Commands
Everything persist --help lists, with options, what each command writes, and real output.
Every command that writes files accepts --dry-run (show planned writes
without writing) and --force (overwrite existing files explicitly). Without
--force an existing file is skipped and listed under "Skipped". Every
command prints what it created, where, and a short "Next steps" list.
persist <command> --help prints the options for any command.
| Command | Purpose |
|---|---|
persist init | Initialize repository memory. |
persist adopt | Inspect an existing repository and propose reviewable memory. |
persist feature create <name> | Create feature memory docs. |
persist adr create <title> | Create a proposed ADR. |
persist adr accept <name> | Promote a proposed ADR to accepted repository memory. |
persist adr supersede <old> <new-title> | Record a changed decision. |
persist module create <name> | Create module memory docs. |
persist doctor | Check whether repository memory is healthy. |
persist test-gate | Run the configured test command and require it to pass. |
persist mcp add <server> | Generate proposed, offline memory for an MCP server. |
persist skill create <name> | Generate an agent skill for Claude Code and the portable Agent Skills target. |
persist skill list | List built-in catalog skills. |
persist fence add | Record the human-confirmed reason a path is shaped the way it is. |
persist context | Find the area memory a task needs: cards first, decisions second. |
persist context add | Scaffold a context card for an area of the codebase. |
persist hooks sync | Regenerate the generated hooks from the config. Never touches docs or config. |
persist init
persist init [--ai-tools <list>] [--features] [--modules] [--yes] [--no-enable-hooks] [--dry-run] [--force] [--reinit]
Writes the six memory documents and the decision directory under docs/,
the agent entry points (AGENTS.md, CLAUDE.md, the Cursor
rule), twelve agent skills in both .claude/skills/ and
.agents/skills/ (eleven without --modules:
module-memory is generated only when module memory is enabled),
the pre-commit and pre-push hooks, a GitHub Actions
workflow, the Claude SessionStart hook, and .persist/config.json. It works
in an empty folder. The full file list is in
Repository memory.
On a terminal with no explicit flags it asks five questions, each with a default:
which AI tools, whether to track features, whether to track modules, whether to
enable the test gate (the detected command is printed first), and whether to enable
the Chesterton fence. Any explicit flag, --yes, or a stdin that is not
a terminal skips the questions; a non-TTY run prints a line saying it proceeded with
defaults.
--ai-tools <list>-
Comma-separated tools to generate files for:
claude,codex,cursor,generic. Default is claude, codex, cursor.AGENTS.mdis always written. --yes- Take every default without prompting. For CI and scripted setup.
--features- Also write the opt-in feature memory index,
docs/40-features/README.md. --modules- Also write the opt-in module memory index,
docs/30-modules/README.md. --dry-run- Show planned writes without writing files.
--force- Overwrite existing files explicitly.
--reinit-
Required together with
--forcewhen the directory already has.persist/config.json. Without it,--forcerefuses, so an existing installation is never overwritten by accident. Reinit rewrites every generated file, filled-in docs included, and resets the config to defaults. To refresh hooks only, usepersist hooks sync. --no-enable-hooks-
Write the hooks but leave git config alone. By default init switches the hooks on
for this clone (
git config core.hooksPath .persist/hooks), and never over acore.hooksPathanother tool already owns.
Init also reads package.json. A one-shot test script becomes
testCommand; typecheck and lint scripts become
pre-push gates. When it finds a manifest it also prints what it detected, marked as
proposed:
$ persist init --features
persist repository memory for AI-assisted software work
────────────────────────────────────────
Persist OS init complete.
Test gate: npm run test:run (saved as testCommand in .persist/config.json).
…
Detected in this repository (proposed — review, nothing was accepted):
- Languages: JavaScript
- Package manager: npm (from `package.json`)
- Frameworks: none detected
- Tests: detected via `tests/` directory
If any signal is wrong, correct the source file noted. Run `persist adopt` to record this as proposed memory.
…
See Hooks, test gate, CI for what the gates do with those values.
persist adopt
persist adopt [--dry-run] [--force]
Inspects an existing repository through read-only manifest and marker files, then writes
a proposed adoption report at docs/adopt/ADOPTION_REPORT.md and proposed
framework ADRs under docs/adrs/proposed/ — both follow the configured
docs and ADR directories when memory has moved. It never executes repository code
and never produces accepted memory; every inferred signal names its source and waits
for human review.
$ persist adopt
Persist OS adopt complete.
Inferred signals are proposed and require human review.
Languages: TypeScript
Package manager: pnpm
Frameworks: Next.js
Created:
- docs/adopt/ADOPTION_REPORT.md
- docs/adrs/proposed/ADR-PROPOSED-adopt-nextjs.md
Next steps:
- Review docs/adopt/ADOPTION_REPORT.md — everything in it is proposed.
- Run persist init to establish neutral repository memory if it does not exist yet.
- Accept or reject each proposed ADR under docs/adrs/proposed/.
That run was in a Next.js app with tsconfig.json and a
pnpm-lock.yaml. The report records where each signal came from:
- Languages: TypeScript
- Package manager: pnpm (from `pnpm-lock.yaml`)
- Frameworks: Next.js (from `package.json`)
- Tests: detected via `"test"` script in package.json
- README present: no
- Docs folder present: no
persist feature create
persist feature create <name> [--dry-run] [--force]
Creates a numbered feature folder under the configured features directory (default
docs/40-features/) with a plan and a task list. When a test command is
configured, it also writes a test plan, so the scaffold never outruns the gate that
enforces it.
$ persist feature create checkout
Persist OS feature create complete.
Feature: docs/40-features/F-001-checkout
Created:
- docs/40-features/F-001-checkout/PLAN.md
- docs/40-features/F-001-checkout/TASKS.md
- docs/40-features/F-001-checkout/TEST_PLAN.md
Next steps:
- Start in docs/40-features/F-001-checkout: fill PLAN.md (approach, boundaries, acceptance criteria).
- Then TASKS.md before you implement.
- Run persist doctor to check the memory is complete.
Without a test command the same run creates only PLAN.md and
TASKS.md.
persist adr create
persist adr create <title> [--dry-run] [--force]
Creates a numbered ADR in the configured ADR directory (default docs/adrs/)
with Status set to Proposed and sections for Context, Decision, Alternatives Considered,
Consequences, and Related Documents.
$ persist adr create "Use PostgreSQL for primary storage"
Persist OS ADR create complete.
ADR: docs/adrs/ADR-0001-use-postgresql-for-primary-storage.md
Created:
- docs/adrs/ADR-0001-use-postgresql-for-primary-storage.md
Next steps:
- Open docs/adrs/ADR-0001-use-postgresql-for-primary-storage.md and fill: Context, Decision, Alternatives, Consequences.
- It is Proposed — set Status to Accepted once the team agrees.
- The Chesterton fence is enabled: if this decision revisits why specific source files are shaped the way they are, record the human-confirmed reason in FENCES.md (the chestertons-fence skill walks through it).
persist adr accept
persist adr accept <name> [--dry-run] [--force]
Promotes a proposed ADR to accepted. <name> is the slug, for example
use-postgresql-for-primary-storage or mcp-figma. Two cases:
-
A proposal at
docs/adrs/proposed/ADR-PROPOSED-<slug>.md(as written byadoptormcp add) becomes the next numberedADR-####-<slug>.md, and the proposal file is removed. -
A numbered ADR that is still Proposed (as written by
adr create) is accepted in place.
$ persist adr accept use-postgresql-for-primary-storage
Persist OS ADR accept complete.
Accepted: docs/adrs/ADR-0001-use-postgresql-for-primary-storage.md
Overwritten:
- docs/adrs/ADR-0001-use-postgresql-for-primary-storage.md
Next steps:
- docs/adrs/ADR-0001-use-postgresql-for-primary-storage.md is now Accepted and is repository source of truth.
- Drift checks now treat references to it as resolved.
persist adr supersede
persist adr supersede <old> <new-title> [--dry-run] [--force]
Records a changed decision. <old> must be an accepted ADR (a proposed
one has nothing to supersede; accept it first). The command writes a new, accepted ADR
with a ## Supersedes link back, and rewrites the old ADR's status line to
Accepted — superseded by <new>. Nothing is deleted. The replacement
title must not collide with another live decision or proposal: a colliding title is
refused with the existing file named, because the doctor's
duplicate-titles check would flag it anyway.
$ persist adr supersede use-postgresql-for-primary-storage "Use SQLite for primary storage"
Persist OS ADR supersede complete.
Superseded: docs/adrs/ADR-0001-use-postgresql-for-primary-storage.md (now marked superseded by ADR-0002-use-sqlite-for-primary-storage)
New decision: docs/adrs/ADR-0002-use-sqlite-for-primary-storage.md
Created:
- docs/adrs/ADR-0002-use-sqlite-for-primary-storage.md
Overwritten:
- docs/adrs/ADR-0001-use-postgresql-for-primary-storage.md
Next steps:
- Fill docs/adrs/ADR-0002-use-sqlite-for-primary-storage.md: Context (what changed), Decision, Alternatives, Consequences.
- ADR-0001-use-postgresql-for-primary-storage stays in history as superseded; update any memory that still relies on it.
- Run persist doctor — it flags memory that still references the superseded decision.
- The Chesterton fence is enabled: supersede is the moment a past decision is revisited — if source files shaped by the old decision change, record why in FENCES.md (the chestertons-fence skill walks through it).
persist module create
persist module create <name> [--dry-run] [--force]
Creates a module folder under the configured modules directory (default
docs/30-modules/) for a responsibility boundary: what it owns, what it does
not, its tasks, its test plan, and its decisions.
$ persist module create billing
Persist OS module create complete.
Module: docs/30-modules/billing
Created:
- docs/30-modules/billing/MODULE.md
- docs/30-modules/billing/TASKS.md
- docs/30-modules/billing/TEST_PLAN.md
- docs/30-modules/billing/DECISIONS.md
Next steps:
- Open docs/30-modules/billing/MODULE.md and describe what this module owns and does not own.
- Record durable decisions in DECISIONS.md, or link an ADR.
persist doctor
persist doctor [--json]
Checks that the memory is structurally healthy, that completion claims have evidence,
and that memory references decisions that exist and are accepted. Read-only,
deterministic, no network. Exit code 0 means healthy, 1 warnings only, 2 errors.
--json emits the same report as machine-readable JSON with
schemaVersion: "persist.doctor.v1". Every check and severity is listed in
Doctor.
persist test-gate
persist test-gate
Runs the testCommand from .persist/config.json and exits with
that command's exit code. The command is split on whitespace and executed directly,
without a shell. When testCommand is null the gate does not pretend to
pass; it prints that it was skipped, and why, and exits 0:
$ persist test-gate
Persist OS test gate skipped: the gate is off — set testCommand in .persist/config.json to a one-shot test command (e.g. "pnpm run test:run") or re-run `persist init` to detect one.
The generated pre-push hook runs this command. Details in Hooks, test gate, CI.
persist fence add
persist fence add <path> --why "<reason>" [--by <name>] [--adr <link>] [--dry-run]
persist fence add <path> --no-constraint --by <name> [--dry-run]
Records why a path is shaped the way it is, for reasoning that never rose to an ADR.
The reason comes from a person; the command writes it in the exact shape doctor and the
SessionStart hook read. It refuses a path that does not exist, and refuses to run
outside an initialised repository, because a fence recorded there is never loaded.
When a human confirms nothing in the file is deliberate, --no-constraint
records that answer instead of a reason: --by is required, and
--why and --adr cannot be combined with it. A real reason
later replaces a No constraint entry; --no-constraint never
replaces a real reason.
$ persist fence add src/billing.js --why "Writes four collections in one transaction so the ledger and the audit trail cannot diverge." --by Priya
Persist OS fence add complete.
Fenced: src/billing.js
Created:
- docs/60-engineering/FENCES.md
Fencing a path that is already fenced keeps the standing reason and adds a dated
crossing. A path may carry a symbol — src/billing.js:writeOrder
— which matches changes to the file. The loop end to end is in
Doctor.
persist context
persist context "<task>" [--json] [--limit <n>]
Looks up the area memory a task needs. Context cards
(docs/context/) are scored first with deterministic BM25 over their
Answers, Also Known As, Purpose, title, Rules, Pitfalls, and Start Here fields —
no embeddings, no model calls, no network. The answer is pointers, never whole
files, and every match names the terms it matched on. When no card covers the task,
the closest recorded decisions and fences are listed instead, marked as such.
The matching areas of LESSONS.md come last (at most two, three lessons
each), followed by a More lessons line naming the other areas.
$ persist context "simplify the billing write"
Start here for "simplify the billing write":
Billing (docs/context/billing.md) — matched: simplify, bill, write (via src/billing.js)
src/billing.js — writeOrder: the four-collection write and its reason
Rules: Fence `src/billing.js` — four collections land in one transaction, and why
Pitfall: LESSONS: simplifying the write splits the ledger from the audit trail
The habit around it: run the lookup before starting, read only what it points at,
and when the work is done add the task to the area card's Answers list, phrased the
way it was asked. A prompt hook (persist context --hook) injects the
pointers per prompt in Claude Code and Codex.
persist context add
persist context add <name> --purpose "<one line>" [--dry-run]
Scaffolds a context card with the exact shape the lookup parses and empty sections for the agent to fill by hand. It refuses to overwrite an existing card, and refuses to run outside an initialised repository, because a card recorded there is never loaded.
$ persist context add billing --purpose How billing writes land.
Persist OS context add complete.
Card: docs/context/billing.md
Created:
- docs/context/billing.md
Next steps:
- Fill the sections in docs/context/billing.md: Start Here paths, Rules pointers, and above all the Answers list with the task you were just given, phrased the way it was asked.
- Run persist context "<task>" to check the card is found before calling the work done.
persist hooks sync
persist hooks sync [--dry-run]
Regenerates the git hooks and the Claude SessionStart hook from
.persist/config.json, and nothing else. This is the repair the doctor's
hook-drift warning points at: after an upgrade, or after editing the gates
or testCommand. Hooks that already match are reported as unchanged.
.claude/settings.json is created when missing and otherwise left alone,
because it often holds your own settings. Docs, config and agent files are never
touched.
persist mcp add
persist mcp add <server> [--dry-run] [--force]
Writes proposed, offline memory for an MCP server: docs/ai/mcp/<server>.md
plus a proposed adoption ADR, and a capture-mcp-context skill skeleton
that tells the agent to record durable context from that server into the file. Persist
OS never connects to the server; the agent, which already has the MCP connection, does
the recording, and a human reviews it.
$ persist mcp add figma --dry-run
Persist OS mcp add dry run complete.
MCP memory: docs/ai/mcp/figma.md (proposed — review before adopting)
Capture skill installed so agents record durable MCP context into this memory.
Planned creates:
- docs/ai/mcp/figma.md
- docs/adrs/proposed/ADR-PROPOSED-mcp-figma.md
- .claude/skills/capture-mcp-context/SKILL.md
- .agents/skills/capture-mcp-context/SKILL.md
Files that already exist are skipped and listed as such; nothing is overwritten without
--force.
persist skill create
persist skill create <name> [--dry-run] [--force]
Writes a SKILL.md to both .claude/skills/<name>/ and
.agents/skills/<name>/. A catalog name gets the built-in content; any
other name gets a valid skeleton to fill in. A catalog skill may ship an executable
scripts/ directory (security-review ships a read-only secret
scan); a skill always works with its scripts deleted.
persist skill list
$ persist skill list
Persist OS skill catalog
- implement-task: Implement Task
- write-tests: Write Tests
- create-adr: Create ADR
- drift-review: Drift Review
- completion-report: Completion Report
- module-memory: Module Memory
- plan-feature: Plan Feature
- security-review: Security Review
- conventions-adherence: Conventions Adherence
- chestertons-fence: Chesterton's Fence
- adr-compliance: ADR Compliance
- context: Context Lookup
Skills from earlier releases that are no longer in the catalog are not deleted on
upgrade; persist doctor names each of the four skills still retired
(with the skill that replaced it) and prints the command to remove it. Skills you
create yourself stay silent.