Hook setup
The hook (hook/) is the writer half of the project: a Claude Code plugin that logs every session's events, an end-of-session summary, and the full transcript to CouchDB + an S3-compatible blob store (Garage). The webapi/webui then read that data back.
Prerequisites
- Bun on the machine running Claude Code (the hook scripts are Bun TypeScript).
- A reachable CouchDB and an S3 bucket — either the bundled
deploy/stack or your own. The bucket must already exist (the hook does not create it).
1. Configure
Fill a .env (copy the repo-root .env.example) with your CouchDB credentials and S3 (Garage) key, then generate the hook config:
cd hook
ENV_FILE=../.env bash scripts/setup.sh
This writes ~/.config/claude-transcripts/config.json (mode 600), creates the CouchDB database, and syncs the design docs + Mango index. Re-run with FORCE=1 to regenerate the config.
The config shape (the db/bucket names + features/logging blocks are baked in from the repo-root claude-transcripts.config.json — see configuration.md):
{
"couch": { "url": "http://127.0.0.1:5984", "db": "claude-sessions", "auth": "user:pass" },
"blob": {
"endpoint": "http://127.0.0.1:3900",
"region": "garage",
"accessKey": "...",
"secretKey": "...",
"bucket": "claude-sessions"
},
"features": { "s3Blobs": true },
"logging": { "chunk": { "maxEntriesPerChunk": 200, "flushIntervalMs": 15000 } }
}
Omit blob (or leave accessKey empty) to log event/summary docs to CouchDB only. Note S3 is the transcript's sole home (ADR 0014): without a blob backend, transcript content is not persisted anywhere — only the summary doc's transcript_bytes is recorded.
2. Verify
bun run scripts/smoke-test.ts
Seeds one synthetic session through the whole write path (CouchDB doc, S3 blob round-trip, view queries) and prints PASS/FAIL. It cleans up after itself; pass --keep to leave the seeded session for the UI.
3. Register the hook with Claude Code
The normal route needs neither a plugin nor a checkout — the installed binary registers itself:
claude-transcripts hook install
It merges into ~/.claude/settings.json, so other tools' hooks are untouched and re-running is a no-op. claude-transcripts hook status shows what's registered.
If you'd rather use Claude Code's plugin mechanism, install the hooks/ directory (so ${CLAUDE_PLUGIN_ROOT} resolves):
claude plugin install /absolute/path/to/claude-transcripts/hooks
That form still requires the CLI to be installed: the plugin is a shim that pipes each payload to claude-transcripts hook run.
Architecture
Either route ends in the same place — claude-transcripts hook run (hook.md) — which reads one payload on stdin and runs the actions bound to that event by the app model. One event can drive several actions; they run concurrently and settled, and the process always exits 0.
Registered events: SessionStart, UserPromptSubmit, PostToolUse, PostToolUseFailure, Stop, SubagentStart, SubagentStop, SessionEnd. Live events write as they happen; SessionEnd writes the summary + transcript.
To wire another supported event (PreToolUse, Notification, PreCompact, …), add the binding to the model's BINDINGS and re-run bun run gen:hooks — dispatch and registration are both projections of it, so neither is edited by hand.
4. (Optional) Backfill existing history
Adopting on-disk history is no longer a hook script — it's the CLI's backfill command (cli.md, tools.md), which reconstructs each session at parity with a live recording (summary + per-event docs, and — planned — chunk docs) rather than a thin summary-only record:
claude-transcripts backfill --dry-run # preview
claude-transcripts backfill # adopt ~/.claude/projects/**.jsonl
Backfilled summaries are tagged source: "backfill" (+ backfilled_at) to distinguish them from live (source: "live") recordings, and the transcript's real timestamps are preserved. Existing sessions are skipped, so it's safe to re-run.