Skip to content

Repository files navigation

swampman

npm version License: MIT

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?

Concept

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)

Requirements

  • 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. If claude is not on PATH, harvesting is skipped gracefully (nothing breaks; nothing is captured).

Installation

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 init

If 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.

Usage

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.

Commands

$ 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.

Configuration

Optional ~/.swampman/config.json:

{
  "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
}

Environment variables: SWAMPMAN_HOME overrides ~/.swampman, CLAUDE_CONFIG_DIR is respected when locating transcripts.

How capturing works

  • Harvesting is per-session, incremental, and idempotent — a cursor in state.json tracks 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 timeout cannot 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 (detachstart → outcome). swampman status shows the tail of it. A session that ends with nothing in episodes/pending/ is normal — extraction is deliberately conservative, and recorded=0 in the log tells you it ran and found nothing worth keeping, as opposed to never having run.
  • Extraction runs as an isolated claude -p subprocess: no tools except record_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 — rejection and choice (behavior, strong evidence) and statement (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).

License

MIT

About

WIP

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages