Continuously capture, distill, and serve your own judgment criteria to Claude Code — so it behaves like a very knowledgeable copy of you.
Named after the Swampman thought experiment: is a perfect copy the same person?
What's worth persisting is not knowledge but judgment. Claude already knows the general best practices; what it doesn't know is how you decide when facing trade-offs — your preferences, your taste.
Humans are bad at self-reporting their own judgment criteria, so swampman harvests them from what you actually did in your Claude Code sessions, not from what you say you do. Three loops keep the data alive instead of growing a CLAUDE.md forever:
Capture SessionEnd hook → extracts "episodes" (rejections / choices / statements)
from the session transcript into a pending queue
Distill /swampman:distill → clusters episodes into principles with you in the loop,
writes them to core.md / domains/*.md, archives episodes
Serve SessionStart hook → injects core.md + a table of contents of domain files
All data lives in ~/.swampman/ as human-readable markdown + JSONL. You can grep it, edit it by hand, and manage it as dotfiles with git.
~/.swampman/
├── core.md # constitution — always injected, line-capped
├── domains/*.md # per-domain principles — TOC injected, read on demand
├── episodes/
│ ├── pending/ # not yet distilled (work queue)
│ └── archive/ # distilled, kept for provenance
├── state.json # processing ledger (local only, gitignored)
└── harvest.log # what the SessionEnd hook did, and when (local only, gitignored)
- Node.js >= 20
- Claude Code installed and logged in — episode extraction runs through
claude -p(headless mode), riding on your existing Claude Code authentication. A Pro/Max subscription is enough; no API key needed. Ifclaudeis not on PATH, harvesting is skipped gracefully (nothing breaks; nothing is captured).
Install both pieces — the CLI does the work, the plugin wires it into Claude Code:
# 1. CLI
$ npm install -g swampman
# 2. Claude Code plugin
$ claude plugin marketplace add koki-develop/swampman
$ claude plugin install swampman@swampman
# 3. Initialize the data store
$ swampman initIf you want to version the store: cd ~/.swampman && git init (the local-only bits — state.json, locks, harvest.log — are gitignored). swampman init is idempotent and adds ignore entries introduced by newer versions to an existing .gitignore, so re-run it after upgrading.
Work with Claude Code as usual. Episodes are harvested automatically when sessions end.
Every once in a while (when swampman status shows pending episodes piling up), run a distillation session inside Claude Code:
/swampman:distill
Claude clusters the pending episodes, checks them against your existing principles, asks you to adjudicate only the conflicts, updates core.md / domains/*.md, and archives the episodes.
$ swampman init # initialize ~/.swampman
$ swampman status # pending episodes, last distillation, harvest history + recent log
$ swampman check # lint the store (line cap, placeholders, frontmatter, JSONL)swampman internal * subcommands are for the plugin's hooks and the distill skill. They are not part of the stable interface — don't call them by hand.
Optional ~/.swampman/config.json:
Environment variables: SWAMPMAN_HOME overrides ~/.swampman, CLAUDE_CONFIG_DIR is respected when locating transcripts.
- Harvesting is per-session, incremental, and idempotent — a cursor in
state.jsontracks how much of each transcript has been processed. - The SessionEnd hook does not run the harvest itself. It detaches it into its own process group and returns in milliseconds, because SessionEnd hooks only get a ~1.5s budget that a plugin-provided
timeoutcannot raise, while an extraction takes tens of seconds. The detached harvest outlives the exiting Claude Code process. - Because that harvest has no terminal to log to, every run appends to
~/.swampman/harvest.log(detach→start→ outcome).swampman statusshows the tail of it. A session that ends with nothing inepisodes/pending/is normal — extraction is deliberately conservative, andrecorded=0in the log tells you it ran and found nothing worth keeping, as opposed to never having run. - Extraction runs as an isolated
claude -psubprocess: no tools exceptrecord_episode(served by swampman itself over stdio MCP), no access to your settings, hooks, or CLAUDE.md, and the extraction session itself is never persisted. Episode data never travels through stdout — it is validated at the tool boundary and written by swampman. - Only three triggers count as episodes —
rejectionandchoice(behavior, strong evidence) andstatement(self-report, weak evidence). Everything else is discarded; precision beats recall because real preferences recur. - Distillation conversations are skipped by a cursor fast-forward mechanism, so swampman never learns from its own output (no echo chamber).
{ "model": "claude-haiku-4-5", // model used for episode extraction "coreMaxLines": 50, // line cap enforced on core.md by `swampman check` "retentionDays": 30, // how long state.json remembers processed sessions "minHumanMessages": 2 // sessions with fewer human messages are skipped }