Record the person's raw prompts from a provider hook — as a JSON record with a Markdown and a YAML rendering of it — draw the episode boundary from the provider's own lifecycle hooks, install those hooks, and report what each provider actually does in this repository.
Why a hook and not an instruction. A worker cannot be asked to record its own prompts.
It never sees the bytes the person typed, only what the provider assembled from them, and a
record written by a model is missing exactly the prompts that mattered: the first one of a
session, which arrives before any instruction has been read, and every one where the model
was busy doing what it was asked. A line in AGENTS.md is a request, not a mechanism, and
no behavioural test can prove a request was honoured. So capture happens below the model,
in the provider's own hook, where running it is the proof that it works.
capture install writes a shim per event and the matching entries in
.claude/settings.json, and refuses to overwrite any of them: majordomus-capture for
UserPromptSubmit, majordomus-session-start for SessionStart and
majordomus-session-end for SessionEnd. When the configuration exists and does not name
one of them — including a configuration this tool wrote before it knew about the lifecycle
events — the command prints the entries to add, one per missing event, and exits 15 rather
than rewriting a file it did not write. A shim finds the repository from its own location:
the provider substitutes its project directory into the command string textually, so nothing
in the environment names the repository, and the working directory a hook runs in is not
contracted.
capture prompt reads one JSON payload on stdin and writes one record. It never exits
2, because in UserPromptSubmit that exit code rejects the person's prompt, and a broken
archive must never cost someone their input; a payload it cannot read is written to the
archive's own log instead — and doctor fails while that log is non-empty, because a
capture that failed means prompts were lost. That log is the only thing that can catch the
failure that matters: if the provider renames the field the prompt arrives in, the hook
still runs and the self test still passes, since it sends a payload of the tool's own
making. Read the log and delete it; it is a diagnostic, and unlike a record, nothing is lost
by removing it. The prompt text itself is the raw span from the payload, copied through
still escaped, so no decode and re-encode step can lose a character.
One prompt is two files under one stem, .ai/local/prompts/YYYYMMDDHHMMSS-<slug>.json
and .md, where the slug is the opening of the prompt itself:
.ai/local/prompts/
20260905113342-why-arent-prompts-saved-automatically.json
20260905113342-why-arent-prompts-saved-automatically.md
20260905114501-make-it-a-file-per-prompt.json
20260905114501-make-it-a-file-per-prompt.md
Both formats, always, and they are not alternatives. The .json is the record: the
provider's own spans, still escaped, pretty printed one member per line — what everything
else is derived from and what capture must not lose. A record is read by people too, and a
single forty-kilobyte line is not something any editor or diff shows usefully; the
indentation lies outside the string spans, so the prompt's bytes are untouched by it. The .md is that record rendered for a person, in one shape
every rendering has: the fields as YAML front matter, the same fields again as a table, and
the prompt last under ## PROMPT, decoded and fenced.
---
schema: 'majordomus.capture/v1'
ts: '2026-09-05T12:48:55Z'
provider: 'claude-code'
event: 'UserPromptSubmit'
id: 'bb6f8c96'
session: '5b13785c-728f'
source: 'user'
cwd: '~/src/your-repo'
repository: '~/src/your-repo'
branch: 'feature/prompt-capture-markdown'
head: 'a0ccbc9e25d6ec7f6bb754f3515affb9b1cc4014'
record: '20260905124855-make-it-a-file-per-prompt.json'
---
# Prompt — 2026-09-05 12:48:55 UTC
| | |
|---|---|
| **Started** | `2026-09-05T12:48:55Z` |
| **Provider** | `claude-code` · `UserPromptSubmit` |
| **Session** | `5b13785c-728f` |
| **Prompt** | `bb6f8c96` |
| **Source** | `user` |
| **Repository** | `~/src/your-repo` |
| **Branch** | `feature/prompt-capture-markdown` · `a0ccbc9` |
| **Directory** | `~/src/your-repo` |
| **Schema** | `majordomus.capture/v1` · `share/schemas/majordomus/capture/capture.v1.proto` |
| **Record** | `20260905124855-make-it-a-file-per-prompt.json` |