Skip to content

CLI reference ​

This page documents every user-facing planwerk-agent subcommand and flag. A PR/issue/repo reference can be a full URL or the short form (owner/repo#123, owner/repo).

The hidden gen-man-pages helper (used by release tooling) is intentionally omitted. Shell completions and man pages are produced by the built-in completion command and packaging — see Install completions & man pages.

Drafting and splitting are skills, not subcommands

draft and meta are no longer planwerk-agent subcommands. They are Claude Code Skills — /planwerk:draft and /planwerk:meta — because both turn on decisions only a human can make mid-run. elaborate and fix exist both ways: as the commands documented below, and as the /planwerk:elaborate and /planwerk:fix skills. See Use the skills.

Global flags ​

These persistent flags apply to every command (review, propose, audit, glossary, gap-analysis, review-prepared, elaborate, prompt, fix, rebase, address, implement, ship, cache, schema).

FlagDescriptionDefault
--verbose, -vEnable debug-level logging (also shows verbose build info with --version)false
--log-formatLog output format: text (human-friendly) or json (one JSON object per record, CI-friendly)text
--remote-patterns-ttlRefresh interval for remote pattern sources (env: PLANWERK_REMOTE_PATTERNS_TTL; <=0 disables refresh once cached). See Remote pattern sources.24h
--claude-timeoutMaximum duration for a single Claude Code invocation, applied to every Claude call across all subcommands. Accepts any time.ParseDuration value (e.g. 20m, 1h30m); must be > 0. Env: PLANWERK_CLAUDE_TIMEOUT.60m
--show-claude-outputStream Claude Code's live output to stderr while a run is in flight, instead of only the periodic heartbeat. Env: PLANWERK_SHOW_CLAUDE_OUTPUT (truthy: 1, true, yes, on).false
--claude-modelModel passed to Claude Code via --model for every Claude call. Accepts a short alias (opus, fable, sonnet) or a full model ID (claude-fable-5-1). Env: PLANWERK_CLAUDE_MODEL.opus
--claude-effortReasoning effort passed to Claude Code via --effort: one of low, medium, high, xhigh, max. Env: PLANWERK_CLAUDE_EFFORT.xhigh
--structure-modelModel for the mechanical JSON-structuring passes — the secondary calls that cast an upstream reasoning call's prose into its artifact's JSON schema (propose, elaborate, gap-analysis, sync, capture, review-prepared). Also governs every JSON-repair and schema-repair recovery call, including those behind the passes that emit findings, and the dedup fallback. Independent of --claude-model: a cheap tier for bounded transcription. These passes run in an empty working directory with every built-in tool disabled, so they load no project settings or memory and cannot read the checkout — a transcription needs neither. Accepts a short alias (sonnet, opus, fable) or full model ID. Env: PLANWERK_STRUCTURE_MODEL.sonnet
--structure-effortReasoning effort for the JSON-structuring passes: one of low, medium, high, xhigh, max. The model swap is the primary cost lever; this is the secondary tunable (medium is enough to transcribe). Env: PLANWERK_STRUCTURE_EFFORT.xhigh
--finder-modelModel for the read-only finder passes — the adversarial pass, each domain specialist, the coverage map, the feature-compliance check, the simplify finder, and claim verification. Independent of --claude-model, and empty by default, which inherits it. These passes are the fan-out: six specialists run concurrently on every review --specialists and on the first round of implement's review loop, so they are where a cheaper tier saves most — and, because their findings drive an editing session, where it can cost recall. Measure with make eval before lowering it. Env: PLANWERK_FINDER_MODEL.(inherits --claude-model)
--finder-effortReasoning effort for the finder passes: one of low, medium, high, xhigh, max. Empty inherits --claude-effort. Thinking tokens are output tokens, so this is the cheaper half of the experiment above: try high before swapping the model. Env: PLANWERK_FINDER_EFFORT.(inherits --claude-effort)
--claude-inherit-user-configLet orchestrated Claude sessions inherit your user-global ~/.claude settings and MCP servers. Off by default: every session runs hermetically (--setting-sources project --strict-mcp-config) so a review is reproducible across machines. Enable only if your claude authentication lives in a user-global setting (e.g. apiKeyHelper). Env: PLANWERK_CLAUDE_INHERIT_USER_CONFIG (truthy: 1, true, yes, on). See design decisions #45–#46 for the reproducibility rationale.false

Logs are written to stderr; when stderr is not a terminal, Claude-invocation heartbeats are still emitted at INFO level so long-running runs are visible in CI log streams.

review (default command) ​

The root command reviews a single GitHub pull request.

Every pass that emits findings (the review, the audit, the adversarial pass, the domain specialists, the feature-compliance check, the simplify finder and the implementation verifier) constrains its output with Claude Code's --json-schema flag, so review, audit and implement require Claude Code v2.1.83+ (the same minimum the implement auto mode needs). The review and the audit run on --claude-model; the other five run on the --finder-model tier.

bash
# Simple invocation with PR URL
planwerk-agent https://github.com/owner/repo/pull/123

# Short form with owner/repo#number
planwerk-agent owner/repo#123

# Post review as inline comments on the PR
planwerk-agent --inline owner/repo#123

# Write output to file
planwerk-agent owner/repo#123 > review.md
FlagDescriptionDefault
--patternsAdditional pattern source: local directory, github:owner/repo[/sub][@ref], or git+https://…[#ref[:sub]] (see Remote pattern sources)-
--min-severityMinimum severity level for output (info, warning, critical, blocking)info
--min-confidenceMinimum confidence shown in the main report (verified, likely, uncertain); findings below the threshold are filtered out, and uncertain low-severity findings otherwise move to an Unverified section-
--no-repo-patternsIgnore repo-specific patternsfalse
--no-local-patternsIgnore local patterns from the toolfalse
--no-cacheIgnore cache, force a fresh reviewfalse
--wikiUse the target repo's GitHub Wiki as a knowledge source (off by default — enabling trusts the wiki's unreviewed editors; review patterns + project memory; env: PLANWERK_WIKI). See GitHub Wiki.false
--no-wikiDo not use the target repo's GitHub Wiki (overrides --wiki)false
--wiki-refPin the wiki to a branch, tag, or commit (env: PLANWERK_WIKI_REF)-
--brainLet the read-only session search the local mirror of the repository's issues, pull requests, and wiki with brain search (off by default: everyone who can comment on the repository wrote the mirrored text; env: PLANWERK_BRAIN). See Sessions that search the mirror.false
--no-brainDo not let the sessions search the local mirror (overrides --brain)false
--clear-cacheClear cached reviews and exit (honors --clear-cache-scope)false
--clear-cache-scopeRestrict --clear-cache to a single command (review, propose, audit, glossary, elaborate, gap-analysis, review-prepared)-
--cache-statsShow cache size, age distribution, and per-command breakdown, then exitfalse
--cache-inspectPrint the metadata and payload for the given cache key, then exit-
--cache-max-ageReject cached entries older than this duration (0 disables the TTL)720h
--formatOutput format (markdown, json)markdown
--post-reviewPost the review as a comment on the PR (updates existing if found)false
--inlinePost review with inline comments using the GitHub Review API (implies --post-review)false
--thoroughRun an additional adversarial review pass for security and failure modesfalse
--specialistsRun the domain-specialist review fan-out (security, data-migration, testing, performance, api-contract, maintainability) concurrently and merge their findings. Specialists are adaptively gated (see Adaptive specialist gating).false
--coverage-mapGenerate a test coverage map for changed functionsfalse
--max-patternsMax review patterns injected into the prompt (<=0 disables truncation; env: PLANWERK_MAX_PATTERNS; see Configuration file for precedence)0 (unlimited)
--max-findingsCap on findings returned (<=0 disables cap)0
--localOperate on the current working directory instead of cloning into a temp dir (see Use local mode). The PR reference may be omitted — it is inferred from the current branch.false
--forceWith --local, skip the confirmation prompt when the working tree is dirtyfalse
--no-captureSkip the read-only capture pass that proposes new wiki review patterns from the review findings (only runs with --wiki; writes nothing)false
--versionShow version information and exitfalse

Sessions that search the mirror ​

With --brain, the session that plans or judges may run brain search for the repository of the run: the review session of review, the audit session of audit, the analysis session of propose, the elaboration session of elaborate with its refinement turns, and the planning session of implement and of every implement run that ship drives. The other sessions of these commands, and the fix and address sessions, get no search. The run needs a mirror that a brain sync finished, and it does not sync the mirror itself. Without one the run logs the warning the brain is enabled, but this repository has no finished mirror; the sessions get no search with the command to run, and continues as a run without --brain. A run also continues without the search, with a warning of its own, when the path of the planwerk-agent binary holds a character other than letters, digits, and _ . / + @ - (every Windows path does), and when the search index cannot be built.

When the review uses --wiki, a read-only capture pass then proposes new project knowledge for the wiki: generalizable review findings become candidate review_patterns/ pages, deduplicated against the wiki's existing entries and the bundled pattern catalog. It is always propose-only — the suggestions surface on stdout, and (only with --post-review) as a PR comment; nothing is ever written to the wiki. Unlike implement and audit, review has no --capture-wiki flag and never pushes the accepted pages: it analyzes an untrusted pull request and the proposal pass reads attacker-controlled source, so auto-pushing its free-form pages would let an external contributor poison the shared knowledge base. A standalone review has no plan or implementation report, so it proposes patterns only, never memory/ pages. The pass runs on a cache miss only, is non-fatal, is a clean no-op when nothing clears the bar, and is skipped without a resolved wiki. Disable it with --no-capture. To grow the wiki from captured patterns, run the write-back from a trusted source — implement or audit. See Use the GitHub Wiki.

propose ​

Analyze a GitHub repository in depth and generate feature proposals.

bash
planwerk-agent propose owner/repo
planwerk-agent propose --format issues owner/repo
planwerk-agent propose --create-issues owner/repo
FlagDescriptionDefault
--patternsAdditional pattern source (see Remote pattern sources)-
--no-repo-patternsIgnore repo-specific patternsfalse
--no-local-patternsIgnore local patterns from the toolfalse
--no-cacheIgnore cache, force a fresh analysisfalse
--wikiUse the target repo's GitHub Wiki as a knowledge source (off by default — enabling trusts the wiki's unreviewed editors; review patterns + project memory; env: PLANWERK_WIKI). See GitHub Wiki.false
--no-wikiDo not use the target repo's GitHub Wiki (overrides --wiki)false
--wiki-refPin the wiki to a branch, tag, or commit (env: PLANWERK_WIKI_REF)-
--brainLet the read-only session search the local mirror of the repository's issues, pull requests, and wiki with brain search (off by default: everyone who can comment on the repository wrote the mirrored text; env: PLANWERK_BRAIN). See Sessions that search the mirror.false
--no-brainDo not let the sessions search the local mirror (overrides --brain)false
--cache-max-ageReject cached entries older than this duration (0 disables the TTL)720h
--formatOutput format (markdown, json, issues)markdown
--max-patternsMax review patterns injected into the prompt (<=0 disables truncation; env: PLANWERK_MAX_PATTERNS)0 (unlimited)
--create-issuesInteractively create GitHub issues from proposalsfalse
--no-issue-dedupeDo not filter proposals whose title matches an existing GitHub issuefalse
--localOperate on the current working directory instead of cloning into a temp dir (see Use local mode). The repository reference may be omitted — it is inferred from the origin remote.false
--forceWith --local, skip the confirmation prompt when the working tree is dirtyfalse

audit ​

Apply every loaded review pattern to an entire codebase.

bash
planwerk-agent audit owner/repo
planwerk-agent audit --min-severity warning owner/repo
planwerk-agent audit --format json owner/repo
FlagDescriptionDefault
--patternsAdditional pattern source (see Remote pattern sources)-
--min-severityMinimum severity level for output (info, warning, critical, blocking)info
--min-confidenceMinimum confidence shown in the main report (verified, likely, uncertain); findings below the threshold are filtered out, and uncertain low-severity findings otherwise move to an Unverified section-
--no-repo-patternsIgnore repo-specific patternsfalse
--no-local-patternsIgnore local patterns from the toolfalse
--no-cacheIgnore cache, force a fresh auditfalse
--wikiUse the target repo's GitHub Wiki as a knowledge source (off by default — enabling trusts the wiki's unreviewed editors; review patterns + project memory; env: PLANWERK_WIKI). See GitHub Wiki.false
--no-wikiDo not use the target repo's GitHub Wiki (overrides --wiki)false
--wiki-refPin the wiki to a branch, tag, or commit (env: PLANWERK_WIKI_REF)-
--brainLet the read-only session search the local mirror of the repository's issues, pull requests, and wiki with brain search (off by default: everyone who can comment on the repository wrote the mirrored text; env: PLANWERK_BRAIN). See Sessions that search the mirror.false
--no-brainDo not let the sessions search the local mirror (overrides --brain)false
--cache-max-ageReject cached entries older than this duration (0 disables the TTL)720h
--formatOutput format (markdown, json)markdown
--max-patternsMax review patterns injected into the prompt (<=0 disables truncation; env: PLANWERK_MAX_PATTERNS)0 (unlimited)
--max-findingsCap on findings returned (<=0 disables cap)0
--create-issuesInteractively create GitHub issues from audit findingsfalse
--issue-min-severityMinimum severity for issue creationwarning
--no-issue-dedupeDo not filter findings whose title matches an existing GitHub issuefalse
--localOperate on the current working directory instead of cloning into a temp dir (see Use local mode). The repository reference may be omitted — it is inferred from the origin remote.false
--forceWith --local, skip the confirmation prompt when the working tree is dirtyfalse
--no-captureSkip the read-only capture pass that proposes new wiki review patterns from the audit findings (only runs with --wiki; writes nothing)false
--capture-wikiPush the accepted capture pages to the wiki instead of only proposing them (off by default — a normal run is propose-only; confirms first, refuses a non-TTY run without --yes; env: PLANWERK_CAPTURE_WIKI, config: capture.wiki)false
--yesSkip the --capture-wiki write confirmation prompt (for a non-interactive write)false

When the audit uses --wiki, the same read-only capture pass proposes new review_patterns/ pages from the audit findings, deduplicated against the wiki and the catalog. It is propose-only by default — the suggestions go to stdout (an audit has no PR or issue to comment on); nothing is written to the wiki. Like review it proposes patterns only (no plan or report ⇒ no memory/ pages), runs on a cache miss only, is non-fatal, and is skipped without a resolved wiki. Disable it with --no-capture; push the accepted pages with --capture-wiki (--yes to skip the confirmation). See Use the GitHub Wiki.

extract ​

Anchor a target repository's GitHub Wiki review patterns into committed, reproducible files — the path back from a fast-moving, world-editable wiki to a code-coupled knowledge store. The command is mechanical (it never calls Claude): it reads the wiki's review_patterns/ directory, lets you select which entries to anchor, and writes the selected files.

There are three write modes:

  • Default — write the selected patterns into the target repo's .planwerk/review_patterns/ and open a pull request through the existing PR-creation path.
  • --local — write them directly into the current working tree's .planwerk/review_patterns/ instead of opening a PR.
  • --to-catalog — anchor them into this planwerk-agent checkout's bundled review catalog (internal/patterns/patterns/review/), normalizing each pattern's frontmatter to the review category. This is the maintainer/contribution path and must be run from a planwerk-agent checkout.

By default the patterns are selected interactively (y/N/q per pattern). Pass --all to take every pattern, or --pattern <stem> (repeatable) to take specific ones by filename. A non-interactive run (no TTY) requires one of those flags: the wiki is an untrusted, world-editable source, so it refuses to extract (and, in the default mode, push into a PR) every pattern without an explicit choice rather than failing open.

bash
planwerk-agent extract owner/repo                       # interactive, opens a PR
planwerk-agent extract owner/repo --all                 # every pattern, opens a PR
planwerk-agent extract owner/repo --pattern my-rule --local
planwerk-agent extract owner/repo --all --to-catalog    # contribute to the bundled catalog
FlagDescriptionDefault
--patternExtract only the named wiki pattern(s) by filename stem (repeatable)-
--allExtract every wiki review pattern without promptingfalse
--to-catalogAnchor into this checkout's bundled review catalog (internal/patterns/patterns/review/), normalizing frontmatter to the review categoryfalse
--localWrite directly into the current working tree's .planwerk/review_patterns/ instead of opening a PR (see Use local mode). The repository reference may be omitted — it is inferred from the origin remote.false
--forceWith --local, skip the confirmation prompt when the working tree is dirtyfalse
--overwriteWith --local or --to-catalog, replace an existing pattern at the destination instead of refusing the collisionfalse
--wiki-refPin the wiki to a branch, tag, or commit (env: PLANWERK_WIKI_REF)-

--to-catalog and --local are mutually exclusive, as are --all and --pattern. --to-catalog and the default (PR) mode require an explicit <repo-ref>; --local may infer it from the origin remote.

The destination filename is the wiki-controlled pattern stem, so --local and --to-catalog refuse to write when a file of that name already exists (a wiki author cannot silently clobber a trusted repo or catalog pattern); pass --overwrite to replace it deliberately.

sync ​

Reconcile a target repository's GitHub Wiki knowledge — its review patterns and project-memory pages — against the current state of the code. The repo and its wiki are cloned and a read-only Claude pass flags entries that are stale (they reference code that no longer exists) or redundant (duplicated or superseded by another entry), then reports them.

--dry-run is the default and reports only. --prune (or its alias --apply) runs a separate write phase that deletes the flagged entries on the wiki and pushes — never inside the read-only analysis. The write phase asks for confirmation first; pass --yes to confirm a non-interactive prune. It clones the wiki fresh, deletes only the flagged entries that still exist (reporting any that already vanished and noting a wiki that moved since analysis), commits, and pushes to the wiki's default branch.

The wiki is always read — reconciling it is the command's whole purpose — so there is no --wiki/--no-wiki here, only --wiki-ref to pin it. sync is scoped to whole-entry deletion; it does not edit entry contents.

bash
planwerk-agent sync owner/repo                  # dry run: report only
planwerk-agent sync owner/repo --format json    # machine-readable report
planwerk-agent sync owner/repo --prune          # delete flagged entries (confirms first)
planwerk-agent sync owner/repo --prune --yes    # prune without the prompt (CI)
FlagDescriptionDefault
--dry-runReport stale and redundant entries without changing the wiki (the default)true
--pruneDelete the flagged entries on the wiki and push (the write phase)false
--applyAlias of --prunefalse
--yesSkip the write-phase confirmation prompt (for a non-interactive prune)false
--formatOutput format (markdown, json)markdown
--wiki-refPin the wiki to a branch, tag, or commit (env: PLANWERK_WIKI_REF)-

--dry-run and --prune/--apply are mutually exclusive when both are set explicitly. A --prune run without --yes requires a TTY to confirm; in a non-interactive context it refuses rather than pruning unprompted.

See Sync the wiki for the workflow and the GitHub Action auth requirements.

glossary ​

Generate a starter domain glossary (CONTEXT.md) for a codebase and print it to stdout. The glossary captures the repository's own domain vocabulary so that review, elaborate, and propose phrase their output in the repo's terms once it is committed as CONTEXT.md. See Provide a domain glossary for the schema and how the commands read it back.

bash
planwerk-agent glossary owner/repo > CONTEXT.md
planwerk-agent glossary --local

The output is a starter — review and edit it before committing. The command prints to stdout and never writes into the repo.

FlagDescriptionDefault
--no-cacheIgnore cache, force a fresh glossaryfalse
--cache-max-ageReject cached entries older than this duration (0 disables the TTL)720h
--localOperate on the current working directory instead of cloning into a temp dir (see Use local mode). The repository reference may be omitted — it is inferred from the origin remote.false
--forceWith --local, skip the confirmation prompt when the working tree is dirtyfalse

gap-analysis ​

Compare every Planwerk feature file under .planwerk/completed/ in the target repo against the actual codebase and report incomplete implementations.

bash
planwerk-agent gap-analysis owner/repo
planwerk-agent gap-analysis --feature CC-0042 owner/repo
planwerk-agent gap-analysis --file CC-0042-thing.json owner/repo
FlagDescriptionDefault
--patternsAdditional pattern source: local directory, github:owner/repo[/sub][@ref], or git+https://…[#ref[:sub]]-
--no-repo-patternsIgnore repo-specific patternsfalse
--no-local-patternsIgnore local patterns from the toolfalse
--no-cacheIgnore cache, force a fresh gap analysisfalse
--cache-max-ageReject cached entries older than this duration (0 disables the TTL)720h
--formatOutput format (markdown, json)markdown
--max-patternsMax review patterns injected into the prompt (<=0 disables truncation; env: PLANWERK_MAX_PATTERNS)0 (unlimited)
--featureLimit analysis to a single feature by feature_id (e.g. CC-0042)-
--fileLimit analysis to a single feature file under .planwerk/completed/ (path or basename)-
--create-issuesInteractively create GitHub issues from gapsfalse
--no-issue-dedupeDo not filter gaps whose suggested-issue title matches an existing GitHub issuefalse
--localOperate on the current working directory instead of cloning into a temp dir (see Use local mode). The repository reference may be omitted — it is inferred from the origin remote.false
--forceWith --local, skip the confirmation prompt when the working tree is dirtyfalse

--feature and --file may be combined as a sanity check; if the file's feature_id does not match --feature, the run aborts before invoking Claude.

review-prepared ​

Review every Planwerk feature spec under .planwerk/features/ whose status is prepared — surface weaknesses in the spec text itself and, with --create-pr, open a pull request that rewrites the JSON to address every WARNING-or-higher finding. This command reviews the spec only; it does not compare the spec to the codebase (use gap-analysis for that).

bash
planwerk-agent review-prepared owner/repo
planwerk-agent review-prepared --feature PX-0028 owner/repo
planwerk-agent review-prepared --create-pr owner/repo
FlagDescriptionDefault
--patternsAdditional pattern source: local directory, github:owner/repo[/sub][@ref], or git+https://…[#ref[:sub]]-
--no-repo-patternsIgnore repo-specific patternsfalse
--no-local-patternsIgnore local patterns from the toolfalse
--no-cacheIgnore cache, force a fresh reviewfalse
--cache-max-ageReject cached entries older than this duration (0 disables the TTL)720h
--formatOutput format (markdown, json)markdown
--max-patternsMax review patterns injected into the prompt (<=0 disables truncation; env: PLANWERK_MAX_PATTERNS)0 (unlimited)
--min-severityMinimum severity to render (info, warning, critical)info
--featureLimit review to a single feature by feature_id (e.g. PX-0028)-
--fileLimit review to a single feature file under .planwerk/features/ (path or basename)-
--create-prAfter the review, commit improved feature JSON files on a fresh branch and open a pull requestfalse
--pr-branchBranch name for --create-prplanwerk-agent/improve-prepared-features
--pr-baseBase branch for --create-prrepo default branch
--localOperate on the current working directory instead of cloning into a temp dirfalse
--forceWith --local, skip the confirmation prompt when the working tree is dirtyfalse

elaborate ​

Expand a high-level GitHub issue into a detailed engineering plan grounded in the actual repository state.

bash
planwerk-agent elaborate owner/repo#123
planwerk-agent elaborate --update-issue owner/repo#123
planwerk-agent elaborate --post-comment owner/repo#123
planwerk-agent elaborate --wiki owner/repo#123
FlagDescriptionDefault
--patternsAdditional pattern source (see Remote pattern sources)-
--no-repo-patternsIgnore repo-specific patternsfalse
--no-local-patternsIgnore local patterns from the toolfalse
--no-cacheIgnore cache, force a fresh elaborationfalse
--cache-max-ageReject cached entries older than this duration (0 disables the TTL)720h
--formatOutput format (markdown, json)markdown
--max-patternsMax review patterns injected into the prompt (<=0 disables truncation; env: PLANWERK_MAX_PATTERNS)0 (unlimited)
--update-issueReplace the issue body with the elaborated body via gh issue editfalse
--post-commentPost the elaborated body as a new issue comment via gh issue commentfalse
--reviewRun a reviewer pass that checks the draft for executability and refines it to close gaps before outputfalse
--max-review-iterationsCap on reviewer refine iterations when --review is set (<=0 uses the default of 3)0
--localGround the elaboration in the current working directory instead of cloning into a temp dir. The issue reference is still required — only the repository checkout is local.false
--forceWith --local, skip the confirmation prompt when the working tree is dirtyfalse
--wikiUse the target repo's GitHub Wiki as a knowledge source (off by default — enabling trusts the wiki's unreviewed editors; review patterns + project memory; env: PLANWERK_WIKI). See GitHub Wiki.false
--no-wikiDo not use the target repo's GitHub Wiki (overrides --wiki)false
--wiki-refPin the wiki to a branch, tag, or commit (env: PLANWERK_WIKI_REF)-
--brainLet the read-only session search the local mirror of the repository's issues, pull requests, and wiki with brain search (off by default: everyone who can comment on the repository wrote the mirrored text; env: PLANWERK_BRAIN). See Sessions that search the mirror.false
--no-brainDo not let the sessions search the local mirror (overrides --brain)false

--update-issue and --post-comment are mutually exclusive.

With the wiki enabled, the elaboration loads the wiki's review_patterns/ and reads the project memory: the prompt carries the memory index, and the session reads a page from a directory the run writes and removes when it ends. The --review reviewer reads neither. The wiki's commit is part of the cache key, so a wiki that moved re-elaborates. A run whose wiki loaded while its commit could not be resolved logs a warning and neither reads nor writes the cache.

The body is written to a 40,000-character budget: over it, --review refines the draft to size, and a run without it logs a warning. A body over GitHub's 65,536-character cap is not truncated: --update-issue writes it as the body plus continuation comments and --post-comment as a run of comments (refused when the issue body is itself continued), and every command that reads the issue merges the parts back first. See Elaborate an issue.

prompt ​

Deterministically render a copy-paste-ready Claude Code prompt for an existing GitHub issue. No Claude call is involved.

bash
planwerk-agent prompt owner/repo#42
planwerk-agent prompt --mode fix owner/repo#42
planwerk-agent prompt --mode implement owner/repo#42
FlagDescriptionDefault
--modePrompt variant (auto, fix, implement). In auto, issue bodies carrying a **Severity**: marker get the fix prompt; everything else gets the implement prompt.auto

fix ​

Watch a pull request's CI checks and, when one fails, dispatch a fresh Claude Code session to apply a minimal fix and publish it. The loop continues until every check is green or --max-iterations is exhausted.

The loop runs unattended, so it decides alone at every fork the repair turns on — whether the code or the test is the wrong one, whether to reach outside the failure surface. When you are there to answer those, use the /planwerk:fix skill instead.

By default each fix is folded into the branch commit it belongs to (git commit --fixup + git rebase --autosquash) and published with git push --force-with-lease, so the branch history stays clean instead of accumulating "Fix failing CI checks" commits. This is the default in both temp-dir and --local runs. Pass --no-fixup to append the fix as a fresh on-top follow-up commit and push without rewriting history.

bash
planwerk-agent fix owner/repo#123
planwerk-agent fix --dry-run owner/repo#123
planwerk-agent fix --no-fixup owner/repo#123
planwerk-agent fix --local --force
FlagDescriptionDefault
--intervalPolling interval between check-status queries1m
--max-iterationsMaximum number of fix attempts before giving up5
--interactiveAsk before starting each new fix iteration (after the first)false
--dry-runReport failing checks but do not invoke Claude or commitfalse
--print-promptRender the fix prompt for the current failing checks to stdout and exitfalse
--print-bare-promptRender a self-contained fix prompt (no check analysis) to stdout and exitfalse
--no-fix-commentDo not post each iteration's fix report as a comment on the pull requestfalse
--patternsAdditional pattern source: local directory, github:owner/repo[/sub][@ref], or git+https://…[#ref[:sub]]-
--no-repo-patternsIgnore repo-specific patterns under .planwerk/review_patterns/ in the target repofalse
--no-local-patternsIgnore local patterns from the toolfalse
--max-patternsMax review patterns injected into the prompt (<=0 disables truncation; env: PLANWERK_MAX_PATTERNS)0 (unlimited)
--localOperate on the current working directory instead of cloning into a temp dirfalse
--forceWith --local, skip the confirmation prompt when the working tree is dirtyfalse
--no-fixupAppend the fix as a fresh on-top follow-up commit instead of folding it into the commits it belongs to (git commit --fixup + git rebase --autosquash, then push --force-with-lease)false
--wikiUse the target repo's GitHub Wiki as a knowledge source (off by default — enabling trusts the wiki's unreviewed editors; review patterns + project memory; env: PLANWERK_WIKI). See GitHub Wiki.false
--no-wikiDo not use the target repo's GitHub Wiki (overrides --wiki)false
--wiki-refPin the wiki to a branch, tag, or commit (env: PLANWERK_WIKI_REF)-

--dry-run, --print-prompt, and --print-bare-prompt are mutually exclusive.

With the wiki enabled, each fix session loads the wiki's review_patterns/ and reads the project memory from a directory written for that iteration. The wiki is resolved once per run, on the first iteration that dispatches a session, so a run whose checks are green and a --dry-run never clone it. The printed prompts resolve no wiki and carry no memory.

rebase ​

Rebase a pull request's branch onto a base branch (--onto, default main), resolving conflicts semantically with Claude rather than a naive ours/theirs pick, preserving the individual commits. After a clean rebase, analyze each rebased commit against the upstream commits that entered the base since the PR forked and report concrete per-commit adjustments — even where git produced no textual conflict. History is force-pushed only with --push.

bash
planwerk-agent rebase owner/repo#123
planwerk-agent rebase --onto develop owner/repo#123
planwerk-agent rebase --dry-run owner/repo#123
planwerk-agent rebase --local --push
FlagDescriptionDefault
--ontoBase branch to rebase ontomain
--pushForce-push the rebased branch with --force-with-lease (never done implicitly)false
--apply-adjustmentsApply the post-rebase analysis as fixup commits instead of only reportingfalse
--max-iterationsMaximum number of conflict-resolution iterations before aborting10
--no-analysisSkip the post-rebase commit analysisfalse
--no-analysis-commentDo not post the post-rebase analysis as a comment on the pull requestfalse
--dry-runShow the rebase plan and conflicting commit without resolving, committing, or pushingfalse
--print-promptRender the post-rebase analysis prompt to stdout and exit; do not rebase or invoke Claudefalse
--print-bare-promptRender a self-contained rebase prompt (rebase + conflict resolution + analysis) to stdout and exitfalse
--patternsAdditional pattern source: local directory, github:owner/repo[/sub][@ref], or git+https://…[#ref[:sub]]-
--no-repo-patternsIgnore repo-specific patterns under .planwerk/review_patterns/ in the target repofalse
--no-local-patternsIgnore local patterns from the toolfalse
--max-patternsMax review patterns injected into the prompt (<=0 disables truncation; env: PLANWERK_MAX_PATTERNS)0 (unlimited)
--localOperate on the current working directory instead of cloning into a temp dirfalse
--forceWith --local, skip the confirmation prompt when the working tree is dirtyfalse

--dry-run, --print-prompt, and --print-bare-prompt are mutually exclusive. The conflict resolution and the apply step run in Claude Code's auto mode.

implement ​

Take an elaborated GitHub issue, run a read-only Claude Code planning session, then a fresh implement session that executes the plan end to end (code, tests, docs) and commits on a feature branch. The simplify and review passes then run over the committed diff, and a finalize session opens the draft pull request last — so it lands already simplified and self-reviewed. The implementation report is posted back onto the source issue as a comment on every run (use --no-report-comment to skip that), so the course of each implementation is recorded on the issue.

A plan planwerk-agent already posted on the issue (from an earlier run that planned but was aborted before implementing) is reused by default: the planning session is skipped and no duplicate plan comment is posted. Use --no-plan-reuse to force a fresh planning session when the posted plan has gone stale.

An issue that has not been elaborated (its body carries no Acceptance Criteria heading) gives the plan no definition of done, so before cloning the run asks whether to implement it anyway. Answering no aborts with the elaborate invocation to run first; a non-TTY run refuses instead of asking. Use --allow-unelaborated to implement such an issue without the question. ship never asks: it runs unattended over the draft-depth Sub Issues meta files.

bash
planwerk-agent implement owner/repo#123
planwerk-agent implement --allow-unelaborated owner/repo#123
planwerk-agent implement --no-plan owner/repo#123
planwerk-agent implement --no-plan-reuse owner/repo#123
planwerk-agent implement --verify owner/repo#123
planwerk-agent implement --no-simplify owner/repo#123
planwerk-agent implement --no-review owner/repo#123
planwerk-agent implement --wiki owner/repo#123
planwerk-agent implement --wiki --no-capture owner/repo#123
planwerk-agent implement --wiki --capture-wiki owner/repo#123
planwerk-agent implement --wiki --capture-wiki --yes owner/repo#123
FlagDescriptionDefault
--dry-runReport what would happen but do not clone, invoke Claude, or push anythingfalse
--print-promptRender the implement prompt (with the issue body embedded, without a plan) to stdout and exitfalse
--print-bare-promptRender a self-contained implement prompt (no issue body) to stdout and exitfalse
--print-plan-promptRender the planning prompt (with the issue body embedded) to stdout and exitfalse
--no-planSkip the planning session and implement directly in a single sessionfalse
--no-plan-reuseAlways run a fresh planning session; do not reuse an implementation plan already posted on the issuefalse
--no-plan-commentDo not post the generated implementation plan as a comment on the source issuefalse
--no-report-commentDo not post the implementation report as a comment on the source issuefalse
--plan-modelModel for the planning session passed to Claude Code via --model (e.g. opus, fable; env: PLANWERK_PLAN_MODEL)opus
--plan-effortReasoning effort for the planning session passed via --effort (low, medium, high, xhigh, max; env: PLANWERK_PLAN_EFFORT)xhigh
--implement-modelModel for the implement session only, passed to Claude Code via --model; the simplify/review/finalize passes stay on --claude-model (env: PLANWERK_IMPLEMENT_MODEL)inherits --claude-model
--implement-worker-modelModel for the implementer subagents the implement session delegates its work packages to. Setting it switches the session into orchestrator mode: the session (on --implement-model, e.g. fable) keeps the whole issue in view, delegates each work package to a subagent on this model, and verifies every delivered package against the actual diff before moving on. The subagent is defined inline via Claude Code's --agents flag, so the checkout stays untouched; the report's attribution names this model, and the workers' commit trailers carry their exact model id. Pass an exact model id (e.g. claude-opus-5-5) for exact footer attribution. Empty keeps the single-session behavior (env: PLANWERK_IMPLEMENT_WORKER_MODEL)- (orchestrator mode off)
--implement-worker-effortReasoning effort for the implementer subagents in orchestrator mode (low, medium, high, xhigh, max); ignored without --implement-worker-model (env: PLANWERK_IMPLEMENT_WORKER_EFFORT)xhigh
--verifyAfter implementing, run an independent pass that checks the actual diff against the issue's Acceptance Criteria without trusting the implementer's report; any unmet criteria are then fed into the review applier and fixed on the branch before the PR opensfalse
--no-simplifySkip the automatic simplify pass that folds over-engineering removals into the branch before the review phasefalse
--no-reviewSkip the automatic review-and-fix pass that folds review findings into the branch after the simplify passfalse
--no-specialistsSkip the domain-specialist fan-out on the review pass's first round; the adversarial finder still runsfalse
--max-review-iterationsCap on the review-and-fix loop: each round re-reviews the branch and re-fixes until the finder comes back clean, an apply resolves nothing, or this bound is hit (<=0 uses the default of 3)0
--no-captureSkip the read-only capture pass that proposes new wiki review patterns and memory pages (only runs with --wiki; writes nothing)false
--capture-wikiPush the accepted capture pages to the wiki instead of only proposing them (off by default — a normal run is propose-only; confirms first, refuses a non-TTY run without --yes; env: PLANWERK_CAPTURE_WIKI, config: capture.wiki)false
--yesSkip the --capture-wiki write confirmation prompt (for a non-interactive write)false
--patternsAdditional pattern source: local directory, github:owner/repo[/sub][@ref], or git+https://…[#ref[:sub]]-
--no-repo-patternsIgnore repo-specific patterns under .planwerk/review_patterns/ in the target repofalse
--no-local-patternsIgnore local patterns from the toolfalse
--max-patternsMax review patterns injected into the prompt (<=0 disables truncation; env: PLANWERK_MAX_PATTERNS)0 (unlimited)
--wikiUse the target repo's GitHub Wiki as a knowledge source (off by default — enabling trusts the wiki's unreviewed editors; review patterns flow into the plan step's pattern catalog + project memory into the planning prompt; env: PLANWERK_WIKI). See GitHub Wiki.false
--no-wikiDo not use the target repo's GitHub Wiki (overrides --wiki)false
--wiki-refPin the wiki to a branch, tag, or commit (env: PLANWERK_WIKI_REF)-
--brainLet the read-only session search the local mirror of the repository's issues, pull requests, and wiki with brain search (off by default: everyone who can comment on the repository wrote the mirrored text; env: PLANWERK_BRAIN). See Sessions that search the mirror.false
--no-brainDo not let the sessions search the local mirror (overrides --brain)false
--localOperate on the current working directory instead of cloning into a temp dirfalse
--forceWith --local, skip the confirmation prompt when the working tree is dirtyfalse
--allow-unelaboratedImplement an issue that has not been elaborated (no Acceptance Criteria) without asking first; without it such a run asks, and a non-TTY run refusesfalse
--no-resumeStart a fresh feature branch instead of resuming the commits an earlier aborted run for this issue left on its branch; also disables pushing partial progress and posting the progress note after an abortfalse

--dry-run, --print-prompt, --print-bare-prompt, and --print-plan-prompt are mutually exclusive. The implement session runs in Claude Code's auto mode and requires Claude Code v2.1.83+.

A session that ends without its implementation report (typically it yielded to "wait" for a backgrounded test run whose result can never arrive in a one-shot session) is first resumed in place with a completion nudge — the same Claude session, full context intact, told to finish the outstanding verification and emit the report. Only when that fails does the run abort; the session's final output is then preserved as a ## Progress Note comment on the issue.

The implement session also receives its report's status contract (the STATUS verdict definitions and the unproven Acceptance Criterion status) in its system prompt, through Claude Code's --append-system-prompt, on the first turn and on every completion nudge turn. The implement prompt carries the same definitions, but it arrives as the session's first message, and a session long enough to compact its context can lose them from the summary. Claude Code rebuilds the system prompt after a compaction, so the contract is present when the session writes its report. --print-prompt output is unchanged, since the printed prompt already holds the same definitions.

If an earlier run for this issue aborted mid-implementation (for example the Claude session hit its usage limit), it left commits on a feature branch. By default the next run detects that branch, checks it out, and continues from where it stopped — reconciling the commits already present against the plan's commit sequence — instead of redoing the committed work, and feeds the most recent progress note or partial report from the issue back into the session so already-verified work is not re-derived. In --local mode the branch persists in your checkout; in clone/CI mode the aborted run pushes its partial progress to origin (no PR) so the next clone can fetch and resume it. Pass --no-resume to start a fresh branch and disable pushing partial progress and the progress note.

When the run that stopped had already finished implementing — its implementation report on the issue says DONE (or DONE_WITH_CONCERNS) and it stopped in one of the passes after the session, typically at a usage limit — the resume continues from that pass instead of running the implement session again: a posted simplification report skips the simplify pass, each posted review report counts as a used round of --max-review-iterations, and the review loop resumes at the next round, scoped to the fixes of the last one (branch-wide when the checkout no longer has that round's pre-fix commit). A PARTIAL report whose Work Breakdown Coverage lists every work package as done counts as a finished implementation: the run that posted it read the verdict as DONE_WITH_CONCERNS and continued past the implement session. Capture, verification, and the finalize session then run as usual. A finalize session that fails persists the branch the same way an abort does, so the next run resumes it and opens the pull request.

--verify is a verification pass that runs over the actual committed diff, not the implementer's self-report, and checks it for acceptance-criteria coverage. It is non-fatal — a finding is reported, it does not fail the run. When --verify finds unmet criteria, those findings are also fed into the same review applier the review-and-fix pass uses, so the gaps are fixed on the local branch before the finalize step opens the PR (this apply is non-fatal too, and a clean pass — or a run with no applier wired — stays render-only).

The simplify pass runs by default once the branch is committed, before the review-and-fix and verification passes, so they assess the leaner diff. A read-only ponytail-style finder reviews the diff through a YAGNI decision ladder for over-engineering; when it finds something, a fresh session folds each removal into the commit it belongs to (git commit --fixup + git rebase --autosquash) on the local branch — no push, since no pull request exists yet, and never touching commits already on the base branch. It never removes validation, error handling, security, or accessibility code and never deletes or weakens tests or assertions; its report is posted as a comment on the source issue. Nothing to simplify is a clean no-op (no commit, no issue comment), and the pass is non-fatal. Disable it with --no-simplify.

The review-and-fix pass runs by default after the simplify pass — a full run is implement → simplify → review → finalize. It runs the same finders and the same finding hygiene the review command runs (package internal/hygiene), then folds each surviving fix into the commit it belongs to (git commit --fixup

  • git rebase --autosquash) on the local branch — no push, since no pull request exists yet. Unlike the simplify pass, it is allowed to add regression tests.

The first round runs the adversarial finder plus the domain-specialist fan-out (security, data-migration, testing, performance, api-contract, maintainability) concurrently, adaptively gated by the files the branch changed (see Adaptive specialist gating) and grounded in the same review-pattern catalog a later review of the diff would apply. The fan-out is on by default because implement runs unattended with nobody present to opt in; --no-specialists turns it off, and later rounds run the cheaper adversarial finder alone regardless, bounding the fan-out's cost to the first round.

The merged findings pass through the shared finding hygiene — multi-pass merge (with its confidence boost and cross-pass provenance), file-less dedup, the quote-or-demote snippet gate, and claim verification — and only findings that survive it are handed to the editing session. Findings that do not survive (an unverifiable snippet, a refuted claim) are reported on stdout and on the source issue but never applied — the restriction is enforced by the harness, not requested in the editing session's prompt.

It runs as a bounded loop: after each apply it re-reviews and, while the finder still reports actionable findings, fixes them again — stopping when the finder comes back clean, when a round yields no findings that survive hygiene, when an apply escalates (STATUS: BLOCKED / NEEDS_CONTEXT), when an apply resolved none of the findings it was handed — the branch is then unchanged, so re-reviewing it can only re-report them — or after --max-review-iterations rounds (default 3), noting any findings still unresolved when the budget runs out. Each round's report is posted as a comment on the source issue (best-effort). Nothing to fix on the first round is a clean no-op (no commit, no issue comment beyond a short stdout note).

Each round re-reviews at a narrower scope than the last. The first round reviews the whole branch diff with the full fan-out; from the second round on, the adversarial finder reviews only what the previous round's fixes changed — git diff <pre-fix commit>, recorded before the editing session ran — since the whole branch was already reviewed and the open question is whether those fixes hold. If the pre-fix commit cannot be recorded the round falls back to the branch-wide scope. The pass is non-fatal — a failed or escalated review never changes the run's exit code. The read-only --verify flag remains available for a report-only run. Disable the whole pass with --no-review, or just the first-round specialist fan-out with --no-specialists.

When the run uses --wiki, a read-only capture pass then proposes new project knowledge for the wiki: generalizable review findings become candidate review_patterns/ pages, durable rationale from the plan and the implementation report becomes candidate memory/ pages, and every candidate is deduplicated against the wiki's existing entries and the bundled pattern catalog. It is propose-only — the suggestions surface in the run report and as a comment on the source issue, and nothing is written to the wiki. The pass is non-fatal, is a clean no-op when nothing clears the bar, and is skipped without a resolved wiki. Disable it with --no-capture. See Use the GitHub Wiki for the memory write convention it follows.

By default the capture pass is propose-only — it writes nothing. Pass --capture-wiki to push the accepted pages to the wiki: a separate, mechanical write phase clones the wiki fresh, writes each page (provenance marker included) under the pinned tool identity, and pushes — creating the wiki's first commit when it is still uninitialized. Claude never pushes; it authored the page bytes in the read-only proposal pass, and this phase performs the push. The write is gated like the rest of the wiki surface: it confirms interactively and refuses a non-TTY run without --yes. The write-back is non-fatal — a refusal or push failure degrades back to propose-only without failing the run. The gate is also settable via PLANWERK_CAPTURE_WIKI or a capture.wiki config key (flag → config → env → off).

Once the simplify and review passes are done, a finalize session opens the draft pull request last: it resolves the base branch from origin/HEAD, pushes the feature branch, and runs gh pr create --draft with a description that walks the reviewer through the commits and links the issue with Closes #N. This is the run's deliverable, so — unlike the passes above — a failure to push or open the PR is fatal. A branch that carries no commits over the base opens no PR and is not an error.

ship ​

Take a Meta Issue — the kind the /planwerk:meta skill produces — and drive every one of its Sub Issues to merged on the default branch, in dependency order, without a human in the loop. Where implement is supervised and deliberately stops at a draft pull request, ship makes those decisions itself: for each Sub Issue it runs the full implement pipeline, marks the opened PR ready, waits for CI, fixes red CI itself (reusing the fix loop), and merges when green, then advances to the next ready Sub Issue.

Sub Issues are processed in the order their dependencies allow. ship reads the native "blocked by" relationships /planwerk:meta records and works them topologically, so a Sub Issue becomes eligible only once every Sub Issue it is blocked by has merged; independent Sub Issues stay independently shippable. When a Sub Issue cannot be finished autonomously — implement reports BLOCKED / NEEDS_CONTEXT, CI stays red past the fix budget, or the PR will not merge — ship skips it and everything transitively blocked by it, then continues with any remaining Sub Issue whose blockers have all merged. The failed Sub Issue's PR is left open with its report for a human to pick up.

ship narrates its progress on the Meta Issue and posts a final summary. Because state lives in GitHub (closed Sub Issues, merged PRs), a re-run resumes naturally — a Sub Issue already merged is recognized and skipped — so an interrupted run can simply be invoked again. When every Sub Issue has merged, the Meta Issue is closed.

bash
planwerk-agent ship owner/repo#123
planwerk-agent ship --dry-run owner/repo#123
planwerk-agent ship --no-merge owner/repo#123
planwerk-agent ship --merge-method squash owner/repo#123
planwerk-agent ship --start-at 456 owner/repo#123
planwerk-agent ship --wiki owner/repo#123
planwerk-agent ship --wiki --capture-wiki --yes owner/repo#123
FlagDescriptionDefault
--dry-runReport the planned order of Sub Issues without cloning, calling Claude, or mergingfalse
--no-mergeRun the whole pipeline but stop at green CI, leaving the merges to a humanfalse
--merge-methodMerge method for each PR (rebase, squash, merge)rebase
--start-atBegin from a specific Sub Issue number (0 = from the top of the dependency order)0
--max-fix-iterationsCI self-heal budget per PR before the Sub Issue is skipped5
--intervalPolling interval between CI check-status queries1m
--no-simplifySkip the automatic simplify pass in each per–Sub Issue implement runfalse
--no-reviewSkip the automatic review-and-fix pass in each per–Sub Issue implement runfalse
--verifyIn each implement run, check the produced diff against the Sub Issue's Acceptance Criteriafalse
--no-planSkip the planning session in each per–Sub Issue implement runfalse
--no-plan-reuseAlways run a fresh planning session; do not reuse a plan already posted on the Sub Issuefalse
--no-plan-commentDo not post the generated implementation plan as a comment on each Sub Issuefalse
--plan-modelModel for the planning session passed to Claude Code via --model (env: PLANWERK_PLAN_MODEL)opus
--plan-effortReasoning effort for the planning session passed via --effort (env: PLANWERK_PLAN_EFFORT)xhigh
--implement-modelModel for the implement session in each per–Sub Issue run; the other sessions stay on --claude-model (env: PLANWERK_IMPLEMENT_MODEL)inherits --claude-model
--implement-worker-modelModel for the implementer subagents in each per–Sub Issue implement run; setting it switches those runs into orchestrator mode, exactly as on implement (env: PLANWERK_IMPLEMENT_WORKER_MODEL)- (orchestrator mode off)
--implement-worker-effortReasoning effort for the implementer subagents in orchestrator mode; ignored without --implement-worker-model (env: PLANWERK_IMPLEMENT_WORKER_EFFORT)xhigh
--patternsAdditional pattern source: local directory, github:owner/repo[/sub][@ref], or git+https://…[#ref[:sub]]-
--no-repo-patternsIgnore repo-specific patterns under .planwerk/review_patterns/ in the target repofalse
--no-local-patternsIgnore local patterns from the toolfalse
--max-patternsMax review patterns injected into the prompt (<=0 disables truncation; env: PLANWERK_MAX_PATTERNS)0 (unlimited)
--wikiUse the target repo's GitHub Wiki as a knowledge source (off by default — enabling trusts the wiki's unreviewed editors; review patterns + project memory; env: PLANWERK_WIKI). See GitHub Wiki.false
--no-wikiDo not use the target repo's GitHub Wiki (overrides --wiki)false
--wiki-refPin the wiki to a branch, tag, or commit (env: PLANWERK_WIKI_REF)-
--brainLet the read-only session search the local mirror of the repository's issues, pull requests, and wiki with brain search (off by default: everyone who can comment on the repository wrote the mirrored text; env: PLANWERK_BRAIN). See Sessions that search the mirror.false
--no-brainDo not let the sessions search the local mirror (overrides --brain)false
--no-captureSkip the read-only capture pass in each per–Sub Issue implement run (only runs with --wiki; writes nothing)false
--capture-wikiPush the accepted capture pages of each per–Sub Issue implement run to the wiki; ship never asks for confirmation, so the push also needs --yes (off by default; env: PLANWERK_CAPTURE_WIKI)false
--yesConfirm the --capture-wiki write for the whole runfalse

Autonomy and merge safety: ship merges to the default branch unattended, so it honors branch protection — it refuses to merge (skipping the Sub Issue) when a required check or review would block, or when the PR has a conflict, and never force-merges past a protection rule. --no-merge is the escape hatch from full autonomy: it stops the pipeline at green CI for every Sub Issue (so nothing merges and, by construction, only the initially-unblocked Sub Issues run). --start-at resumes from a chosen Sub Issue, treating Sub Issues ordered before it as already-handled unless they are still open. The per–Sub Issue implement runs honor the same --no-simplify / --no-review switches as implement, so each diff is cleaned and self-reviewed before CI ever sees it. ship gains no fan-out off-switch of its own: each per–Sub Issue run inherits the default-on first-round specialist fan-out, and --no-review remains the whole-pass switch — so every Sub Issue is checked across the same domains and its self-review findings pass the same hygiene before any fix lands. ship does not create Sub Issues — that stays the job of the /planwerk:meta skill.

Wiki and capture: ship resolves the wiki settings once and hands them to every implement and fix run it drives. With the wiki enabled, each of those runs reads the wiki's review_patterns/ and the project memory, and each implement run ends with the capture pass, which proposes new wiki pages in a comment on its Sub Issue. --no-capture skips that pass. ship never shows the confirmation prompt the capture write phase has on implement: it pushes the accepted pages only when the write-back is enabled (--capture-wiki, the capture.wiki config key, or PLANWERK_CAPTURE_WIKI) and --yes is given. Enabled without --yes, ship logs one warning at start and every run stays propose-only.

address ​

Read a pull request's human review threads, present the unresolved ones as an interactive selection list, and drive a fresh Claude Code session to incorporate the selected ones as follow-up commits on the PR head branch — then, gated, reply to and resolve each addressed thread. This closes the loop the other commands leave open: fix loops on failing CI checks, rebase resolves merge conflicts, and implement works from an issue — none of them consume the inline reviewer feedback on a PR.

Threads GitHub already marks resolved, and the tool's own inline review comments, are skipped by default. The orchestrator pushes the follow-up commits; replies are best-effort and on by default, resolving is best-effort and off by default (it is outward-facing).

bash
planwerk-agent address owner/repo#123
planwerk-agent address --all owner/repo#123
planwerk-agent address --thread PRRT_kwDOAbc123 owner/repo#123
planwerk-agent address --resolve owner/repo#123
planwerk-agent address --dry-run owner/repo#123
planwerk-agent address --local --force
FlagDescriptionDefault
--allAddress every unresolved thread without promptingfalse
--threadAddress only the named review thread(s) (repeatable)-
--include-resolvedAlso offer threads GitHub already marks resolvedfalse
--replyPost a per-thread reply summarizing the changetrue
--no-replyDo not post per-thread replies (overrides --reply)false
--resolveMark addressed threads as resolved (outward-facing)false
--one-commit-per-threadCommit each thread separately instead of one aggregate committrue
--no-address-commentDo not post the aggregate address report as a comment on the pull requestfalse
--max-iterationsMaximum number of per-thread address iterations10
--dry-runList the selected threads and the planned changes without invoking Claude or committingfalse
--print-promptRender the address prompt for the selected threads to stdout and exitfalse
--print-bare-promptRender a self-contained address prompt (no thread fetch) to stdout and exitfalse
--patternsAdditional pattern source: local directory, github:owner/repo[/sub][@ref], or git+https://…[#ref[:sub]]-
--no-repo-patternsIgnore repo-specific patterns under .planwerk/review_patterns/ in the target repofalse
--no-local-patternsIgnore local patterns from the toolfalse
--max-patternsMax review patterns injected into the prompt (<=0 disables truncation; env: PLANWERK_MAX_PATTERNS)0 (unlimited)
--localOperate on the current working directory instead of cloning into a temp dirfalse
--forceWith --local, skip the confirmation prompt when the working tree is dirtyfalse
--wikiUse the target repo's GitHub Wiki as a knowledge source (off by default — enabling trusts the wiki's unreviewed editors; review patterns + project memory; env: PLANWERK_WIKI). See GitHub Wiki.false
--no-wikiDo not use the target repo's GitHub Wiki (overrides --wiki)false
--wiki-refPin the wiki to a branch, tag, or commit (env: PLANWERK_WIKI_REF)-

--dry-run, --print-prompt, and --print-bare-prompt are mutually exclusive. The address session runs in Claude Code's auto mode.

With the wiki enabled, the run loads the wiki's review_patterns/ and writes the project memory to one directory that every per-thread session reads. A run with no thread to address and a --dry-run never clone the wiki. The printed prompts resolve no wiki and carry no memory.

brain ​

Read and build the project memory a repository keeps on its GitHub Wiki, mirror what the repository knows on GitHub to local files, and search that mirror.

SubcommandArgumentsDescription
brain memory<repo-ref>Print the memory index of the repository's wiki
brain memory<repo-ref> <page>Print the memory page with that file name
brain bootstrap<repo-ref>Build the memory from the repository's history
brain sync<repo-ref>Mirror the issues, pull requests, commit list, and wiki to local markdown
brain search<repo-ref> <query>Print the best-matching issues, pull requests, and wiki pages of the mirror
brain search<repo-ref> --show <id>Print one block of the mirror in full

brain memory ​

Print the project memory. The command starts no Claude session and writes no file. The skills call it, so a skill reads the memory through the same opt-in, authentication, and page checks as the commands above.

bash
# Print the memory index: a header, a blank line, one line per page
planwerk-agent brain memory owner/repo

# Print one page, named by its file name in the index
planwerk-agent brain memory owner/repo pin-dependencies.md
FlagDescriptionDefault
--wikiUse the target repo's GitHub Wiki as a knowledge source (off by default — enabling trusts the wiki's unreviewed editors; review patterns + project memory; env: PLANWERK_WIKI). See GitHub Wiki.false
--no-wikiDo not use the target repo's GitHub Wiki (overrides --wiki)false
--wiki-refPin the wiki to a branch, tag, or commit (env: PLANWERK_WIKI_REF)-

The index opens with a header that names the wiki, its commit, and the number of pages. Each following line carries a page's file name, its title, and after a | the page's summary where it states one:

text
Project memory from acme/widgets.wiki @ 1a2b3c4, pages: 2

- conventions.md: conventions
- pin-dependencies.md: Pin every dependency | Dependencies are pinned to exact versions.

The index has no size budget, unlike the index a prompt carries. Both forms see a page only when its file name starts with a letter or a digit and holds nothing but letters, digits, ., _, and -, because a caller passes the name back on a command line. Any other page is skipped: one warning gives the number of skipped pages, and --verbose logs their names. A script that takes the name from the index puts it after a -- (planwerk-agent brain memory owner/repo -- "$page"). A page name is matched by its bytes first and then by its Unicode NFC form, so a name the wiki stores in decomposed form is found when it is typed precomposed, as long as only one page has that name. Control characters other than newline and tab are dropped from what the command prints on stdout.

The wiki is off by default. The flag decides first, then the wiki section of .planwerk/config.yaml in the working directory, then PLANWERK_WIKI. With the wiki off, with a wiki that cannot be resolved, or with a wiki that has no memory pages, the index form prints nothing on stdout and exits 0. A page the memory does not hold is an error: the command prints nothing on stdout and exits non-zero with no project memory page named "<page>" for <owner>/<repo>. Log lines go to stderr.

brain bootstrap ​

Distill the history of a repository into project memory pages and review patterns. The command reads the history unit by unit, runs one analysis session per unit and one review session per unit that proposes a page, and keeps the pages in a state directory. It writes to the wiki only under --write-wiki and after a confirmation at a terminal. See Bootstrap the project memory for the workflow.

bash
planwerk-agent brain bootstrap owner/repo --dry-run        # list the units, no session
planwerk-agent brain bootstrap owner/repo --max-units 5    # process five units and stop
planwerk-agent brain bootstrap owner/repo                  # process every remaining unit
planwerk-agent brain bootstrap owner/repo --write-wiki     # push the pages after a y
FlagDescriptionDefault
--dry-runClone the repository, list the units, and stop: no Claude session, no wiki clone, no state writefalse
--max-unitsStop after this many units in this run; 0 processes every remaining unit0
--write-wikiOnce no unit remains, push the changed pages after a confirmation at a terminalfalse
--wiki-refPin the wiki to a branch, tag, or commit (env: PLANWERK_WIKI_REF)-
--review-modelModel of the page review (env: PLANWERK_BRAIN_REVIEW_MODEL)fable
--review-effortReasoning effort of the page review: one of low, medium, high, xhigh, max (env: PLANWERK_BRAIN_REVIEW_EFFORT)high
--decision-docsRepository-relative paths of the decision documents to read, comma-separated or repeated; replaces discovery-
--no-decision-docsRead no decision documentfalse
--sourceWhere the history is read from: api (the GitHub API) or mirror (the local mirror of brain sync)api

--dry-run and --write-wiki are mutually exclusive, and so are --decision-docs and --no-decision-docs. --max-units must not be negative. An unknown --review-effort and an unknown --source are rejected before any session runs.

The command always reads the wiki, so it has --wiki-ref and neither --wiki nor --no-wiki. It reads wiki.repo and wiki.ref from .planwerk/config.yaml and ignores wiki.enabled. It has no --yes.

Units ​

The history is read from the GitHub API, or from the local mirror with --source mirror, and grouped into units:

KindKeyContent
Issueissue-<n>A closed issue with its comments, the merged pull requests that closed it (comments, reviews, review threads, commits), and the commit that closed it when no pull request did
Pull requestpr-<n>A merged pull request that closed no closed issue. A pull request opened by a bot is skipped and counted
Commit rangecommits-<first12>-<last12>Up to 25 adjacent commits of the default branch that belong to no merged pull request and closed no issue
Documentdoc-<path>@<hash12>One chunk of a decision document, cut at line boundaries into chunks of at most 16 KiB

Units follow the default branch. A unit sorts by the newest commit it holds on the default branch. A unit without such a commit (an issue closed by hand, a pull request whose commits are no longer on the default branch) sorts by the time it was closed or merged, after the units of the commits up to that time. Document units come last, in path order.

With --source mirror the listing and every issue and pull request thread come from the files brain sync wrote, and the run calls no GitHub API for them. The mirror is read as it is: the run does not sync it, so the history ends where the last brain sync ended. The repository is still cloned, and commit messages are read from that clone. A mirror that cannot be used fails the run:

  • Without a mirror, the listing fails with no mirror of <owner/repo> at <dir>; run "planwerk-agent brain sync <owner/repo>" first.
  • With a mirror in which no sync has finished, it fails with the mirror of <owner/repo> at <dir> has never finished a sync; run "planwerk-agent brain sync <owner/repo>" again.
  • With an item file that cannot be read, it fails with reading <file>: <cause>. A file is not skipped, because a skipped file would shorten the history without notice.
  • When a unit names an issue or a pull request the mirror does not hold, the unit fails with <owner/repo>#<n> is not in the mirror at <dir>; run "planwerk-agent brain sync <owner/repo>".

A Markdown file is a decision document when a directory on its path is named adr, adrs, or decisions, or when its name is design-decisions.md, decisions.md, decision-log.md, or adr.md (compared without case). A symlink, a file over 2 MiB, and a path that holds -- or a character outside letters, digits, ., _, /, and - are skipped with a warning. A path given with --decision-docs must be a regular file inside the repository, or the run fails with decision document "<path>" not found in the repository.

Every piece of a unit's text is scrubbed of known secret patterns. A piece over 32 KiB is cut and ends with [truncated: <n> bytes omitted]. A unit carries at most 384 KiB into a session; the pieces past that are left out, and the prompt says how many.

Sessions ​

The analysis runs on --claude-model and --claude-effort. It proposes memory pages and review patterns, or nothing. A new page must be memory/<name>.md or review_patterns/<name>.md with a name of lowercase letters, digits, ., _, and - that starts with a letter or a digit; a proposal for the path of an existing page is an update. Any other path is rejected as invalid path, and a second proposal for one path as duplicate path.

The review runs on --review-model and --review-effort, and only for a unit with at least one valid proposal. It gives each page one verdict: accept keeps the proposed page, revise replaces it with the reviewer's page, and reject drops it. A page without a verdict is rejected as no review verdict, a reject without a reason is recorded as rejected without a reason, and a revise without a page as revise verdict without a page. A page that is over 64 KiB with its provenance marker is rejected as page exceeds 64 KiB.

Both sessions are read-only, run in a clone of the repository at the default branch's HEAD, and may read the pages of the state directory.

State directory ​

The state lives in .planwerk-brain-sync in the directory the command runs in:

text
.planwerk-brain-sync/
├── .gitignore        # one line, "*", so git never tracks the directory
├── state.json        # processed units, one entry per page, usage of all runs
├── pages/
│   ├── memory/<name>.md
│   └── review_patterns/<name>.md
└── orphans/          # files found under pages/ that state.json has no entry for

A page file holds the page without its provenance marker. state.json records for each page the source its marker names (source: the unit that last wrote the page, or the source the wiki page's marker named), the hash of the wiki version it is based on (base_sha256), and whether it diverged. A page is dirty when its file differs from that wiki version; dirty pages are what --write-wiki pushes.

A file under pages/ that state.json has no entry for is not a page of the working set. Every run except a dry run moves it to orphans/, under the same relative path, and logs a warning that names both paths. When orphans/ already holds something under that path, the file is moved to the first free name <path>.1, <path>.2, and so on, and what was there stays. A directory of the path whose name orphans/ holds as a file takes the first free name the same way. A state.json that names a page outside memory/ and review_patterns/ fails the run with <file> names the page "<path>", which is not a file in memory/ or review_patterns/; delete the directory to start over.

Every run except a dry run starts by cloning the wiki and refreshing the pages:

Local pageWiki pageResult
AbsentPresentThe page is added
UnchangedChangedThe page takes the wiki's text
ChangedUnchangedNothing changes
ChangedChanged to the same textThe page counts as unchanged
ChangedChanged to another textThe page is marked diverged
UnchangedRemovedThe page is deleted
ChangedRemovedThe page becomes a new page

A wiki file in memory/ or review_patterns/ whose name is only .md is not added: the run logs a warning that names it and skips it.

A diverged page is never pushed. Every run reports it until the local file and the wiki page hold the same text. When the wiki cannot be cloned (a wiki without a first page cannot), the run logs a warning and continues with the pages it has.

Output ​

The run prints to stdout:

text
Units: 196 total, 2 processed, 194 remaining
  103 issues, 66 pull requests, 16 commit ranges, 11 decision document chunks; 18 bot-authored pull requests skipped
[3/196] issue-6: 2 proposed, 1 accepted, 1 rejected
Pages: 4 new, 1 updated, 3 unchanged, 0 diverged
- `memory/pin-dependencies.md` (new) from owner/repo#6
- `memory/one-off.md` (issue-6): a one-off, not a decision
Models: analysis claude-opus-5-5, review claude-fable-5-1
Usage this run: 48211 input tokens, 9120 output tokens, 4 calls, est. $1.84
Usage all runs: 131004 input tokens, 26377 output tokens, 12 calls, est. $5.02
Propose-only: nothing was written to the wiki. The pages are under .planwerk-brain-sync/pages; run again with --write-wiki to push them.
  • The two Units: lines count the units by state and by kind. A dry run then prints one line per remaining unit, <key> <title>, and stops.
  • One [<position>/<total>] line per processed unit.
  • The Pages: line, then one line per dirty page (new or update, and the source of the page, or a local edit for a hand-edited page that had no provenance marker on the wiki), one line per proposal rejected in this run with its unit and the reason, and one line per diverged page.
  • The Models: line names the model each kind of session reported, - for a kind that did not run.
  • The two Usage lines, for this run and summed over all runs.

Control characters other than newline and tab are dropped from what the command prints. Log lines and the usage summary go to stderr.

Stop and resume ​

The state is saved after every unit. A unit that fails ends the run: the run prints Stopped at unit <key>. The state is saved in <dir>; run the same command again to continue. and exits non-zero with unit <key>: <step>: <cause>, where the step is content, analysis, review, or apply. A Claude usage limit, a session timeout, and a GitHub rate limit all arrive this way.

The next run lists the history again and skips every unit in the state. It continues with the first unit that is not processed, and a later run processes only what was closed, merged, or committed since. A commit range that grew gets a new key and is processed again. An issue keeps its key: its unit is processed again when it holds a pull request or a closer commit that its record in state.json does not name, for example after the issue was reopened and closed by a later pull request. Deleting .planwerk-brain-sync starts over.

Wiki write ​

With --write-wiki, and once no unit remains, the run lists every dirty page that is not diverged and asks Write <n> pages to the <owner/repo> wiki and push? (y/N):. On y it clones the wiki fresh and pushes the pages as one commit. Each page carries the provenance marker of its unit. A page that came from the wiki and that you edited by hand keeps the marker it had there, and is pushed without one when it had none.

  • With units remaining, the run prints <n> units remain; the wiki write runs once every unit is processed. and pushes nothing.
  • With no dirty page, it prints Nothing to write: every page matches the wiki.
  • When stdin is not a terminal, it fails with refusing to write to the wiki: brain bootstrap pushes only after a confirmation at a terminal, and stdin is not a TTY.
  • When the wiki moved since the refresh, updates are skipped and new pages are still written.
  • A new page whose path the wiki already holds is skipped.
  • When state.json was last refreshed from another wiki than the one the run writes to, it fails with the pages in <dir> were last refreshed from the "<owner/repo>" wiki, and this run writes to the <owner/repo> wiki; run again once that wiki can be cloned, or delete the directory to start over.
  • When the wiki holds memory or review_patterns as a symbolic link, it fails with the <owner/repo> wiki holds <directory> as a symbolic link; refusing to write through it.

The command never deletes a wiki page; sync --prune does.

brain sync ​

Keep a local mirror of a repository's knowledge on GitHub: one markdown file per issue and per pull request with the whole conversation, the commit list of the default branch, and a clone of the wiki. The command starts no Claude session, and no session is given the mirror directory. With --brain, a read-only session reaches the mirrored text only through brain search, redacted. brain sync is not the sync command, which reconciles the wiki's pages against the code. See Mirror a repository for the workflow.

bash
planwerk-agent brain sync owner/repo           # fetch what changed since the last run
planwerk-agent brain sync owner/repo --full    # delete the mirror and build it again
FlagDescriptionDefault
--fullDelete this repository's mirror first, then syncfalse
--wiki-refPin the wiki to a branch, tag, or commit (env: PLANWERK_WIKI_REF)-

The command always mirrors the wiki, so it has --wiki-ref and neither --wiki nor --no-wiki. It reads wiki.repo and wiki.ref from .planwerk/config.yaml and ignores wiki.enabled.

Mirror directory ​

The mirror lives in the user cache directory, keyed by owner and repository in lowercase: <user cache dir>/planwerk-agent/brain/<owner>/<name> (on Linux ~/.cache/planwerk-agent/brain/<owner>/<name>). The first output line prints the path. No flag, config key, or environment variable moves it. A run that cannot resolve a user cache directory (neither HOME nor XDG_CACHE_HOME is set) stops with an error: the mirror is never written to the temp directory.

text
<owner>/<name>/
├── state.json          # the items cursor, the mirrored wiki and its commit, the end of the last finished sync
├── issues/<number>.md
├── pulls/<number>.md
├── history.jsonl       # one line per commit of the default branch, oldest first
├── index.sqlite        # the search index of brain search, created by its first run
└── wiki/               # a full git clone of <repo>.wiki.git

The mirror directory, issues/, and pulls/ have mode 0700, and the files the command writes have mode 0600. The wiki clone keeps git's own modes inside that directory. Every file is written through a temporary file and a rename. A run that is interrupted can leave its temporary file (*.tmp) in the mirror directory, issues/, or pulls/. A later run removes it once it is more than one hour old. The files hold the text as GitHub returns it, with any secret someone pasted into a comment.

The mirror is a copy: deleting it loses nothing, and the next run builds it again. --clear-cache leaves it in place. index.sqlite belongs to brain search. brain sync does not write it, and --full removes it with the rest of the directory.

state.json has this form:

json
{
  "version": 1,
  "repo": "owner/name",
  "synced_at": "2026-10-02T09:00:00Z",
  "items": { "cursor": "2026-10-02T08:17:02Z" },
  "wiki": { "repo": "owner/name", "commit": "<40 hex>" }
}

synced_at is the end of the last run in which every part succeeded, in UTC. items.cursor is the newest update time a run has handled. A line of history.jsonl is {"sha":"<40 hex>","committed_at":"2026-03-01T10:00:00Z","pr":9}, where pr is the merged pull request GitHub associates with the commit, or 0.

Item file ​

A file is a YAML frontmatter between two --- lines, a blank line, the title as a # heading, a blank line, and the blocks. The frontmatter always holds these keys, in this order:

KeyValue
format1
kindissue or pull
repo<owner>/<name>
number, id, url, titleThe item's number, GraphQL node id, URL, and title
stateopen, closed, or merged
state_reasonAn issue's close reason in lowercase (completed, not_planned), or ""
author, author_is_bot, author_associationThe author's login ("" for a deleted account), whether GitHub types the author as a bot, and the author's relation to the repository
labelsThe label names
created_at, updated_at, closed_at, merged_atTimestamps as GitHub prints them, "" where there is none
base_branchA pull request's base branch
closed_by_prsAn issue's merged closing pull requests of the same repository
closer_commitThe commit that closed an issue
closes_issuesThe issues of the same repository a pull request closes

A block is a marker line, the lines of its text, and one blank line:

markdown
<!-- planwerk-agent:mirror comment {"id":"IC_x","url":"https://github.com/acme/widgets/issues/7#issuecomment-1","author":"octocat","association":"MEMBER","created":"2026-03-01T10:00:00Z","updated":"2026-03-01T10:05:00Z","lines":2} -->
First line of the comment.
Second line.

The marker is <!-- planwerk-agent:mirror <kind> <json> -->. lines is the number of lines of the text, 0 for an empty text. The text is written unchanged. A reader takes lines lines after a marker and never scans a text for markers, so a comment that holds a marker line or a --- line opens no block. In the JSON, <, >, and & are written as \u003c, \u003e, and \u0026, so no value closes the HTML comment. The blocks come in this order:

KindJSON keys, in orderText
bodylinesThe item's body
commentid, url, author, association, created, updated, linesA conversation comment, in GitHub's order
reviewid, url, author, association, state, submitted, updated, linesA review's summary (pull requests); state as GitHub prints it, for example APPROVED
threadid, path, line, resolved, outdated, linesThe diff hunk of a review thread
thread-commentThe keys of commentOne comment of the thread opened by the last thread block
commitsha, committed, headline, linesThe commit message body

A review thread with more than 100 comments keeps the first 100, and the run logs a warning that names the pull request and the thread.

Output of a sync ​

The run prints to stdout:

text
Mirror of acme/widgets at /home/u/.cache/planwerk-agent/brain/acme/widgets
items: 262 listed, 262 fetched, 262 in the mirror (116 issues, 146 pull requests)
history: 563 new commits, 563 in the mirror
wiki: acme/widgets.wiki at 1a2b3c4
  • The items: line counts the issues and pull requests GitHub listed, the ones the run fetched, and the files the mirror holds.
  • The history: line counts the commits the run added. When the default branch no longer holds the last mirrored commit (after a force push), the run replaces the file and prints history: replaced, <n> commits in the mirror. GitHub lists the history by commit date, so a merge can place commits behind the last mirrored one. The run reads past that commit until the commits it read and the mirrored ones add up to the commit count of the branch, and counts those commits as new.
  • The wiki: line names the wiki and its commit, with , unchanged when the commit is the one the last run stored. When the wiki cannot be cloned (a repository without a wiki cannot), the run logs a warning, keeps the clone it has, and prints wiki: <owner/repo>.wiki not mirrored.

Log lines go to stderr. A run logs mirroring items after every 50 fetched items.

What a run does ​

A run asks GitHub for the issues and pull requests updated since items.cursor, oldest update first, and fetches each one whose file is missing or older than the listing says. Issues and pull requests share the cursor: an item's update time rises when a comment on it is edited and when a review is submitted. The listing includes the cursor's own time, so a run right after another one lists the newest item again and prints items: 1 listed, 0 fetched.

The three parts (items, history, wiki) run in that order, and the state is saved after each. A part that fails does not stop the next one. The run then exits non-zero with every failure under its part's name, for example items: fetching acme/widgets#42: <cause>, and leaves synced_at as it was. A failed wiki clone is a warning, not a failure. After a failed fetch the cursor stays at the last item the run handled, so the next run continues there. A GitHub rate limit arrives this way: the run does not wait it out.

A run does not remove what was deleted on GitHub, because no listing reports a deletion. A deleted comment leaves the mirror when its issue or pull request is updated and fetched again. A deleted or transferred issue or pull request leaves it on --full.

--full deletes the mirror directory of this repository, and no other, before it syncs. It asks GitHub for one listing first, so a run that cannot reach GitHub or authenticate deletes nothing. Two full runs over the same state of GitHub write the same issues/, pulls/, and history.jsonl, byte for byte.

A state.json of another version stops the run with unsupported mirror state version <n> in <file>; run brain sync --full to rebuild. An item file of another format stops its reader with unsupported mirror format <n>; run brain sync --full to rebuild.

Search the local mirror that brain sync keeps: the issues and pull requests with their comments, reviews, review threads, and commit messages, and the pages of the wiki. The command ranks by keyword (BM25), prints the best matches first, starts no Claude session, and calls no GitHub API. See Search the mirror for the workflow.

bash
planwerk-agent brain search owner/repo one cursor
planwerk-agent brain search owner/repo '"items cursor" curs*' --type pull --state merged
planwerk-agent brain search owner/repo --show issues/186.md:4

The first argument is the repository. Every argument after it is part of the query, joined with one space. An argument that starts with - is read as a flag. To search for such a word, put -- before the query and every flag before the --: brain search owner/repo --type pull -- --no-cache.

FlagDescriptionDefault
--typeKeep only hits of this type: issue, pull, or wiki. Repeatable-
--stateKeep only items in this state: open, closed, or merged-
--labelKeep only items that carry this label, compared without case. Repeatable; every given label must match-
--limitThe largest number of hits, 1 to 10010
--jsonPrint JSONfalse
--showPrint the block with this id in full, in place of a search-

A wiki page has no state and no label, so --state and --label leave every wiki page out. The command has no wiki flag and does not load .planwerk/config.yaml: it runs the same beside a file with a key this release does not know, as it does for a session in the checkout under review.

The command checks its arguments before it reads the mirror:

ArgumentsError
--show with a query--show takes no query
--show with --type, --state, --label, or --limit--show takes no filter
Neither a query nor --showa query is required, or --show <id>
Another --type--type must be one of issue, pull, wiki
Another --state--state must be one of open, closed, merged
A --limit below 1 or above 100--limit must be between 1 and 100
An unknown flag, or a flag value of the wrong kindinvalid flag or flag value; see brain search --help

No error of the command repeats an argument. A session runs the command through a shell, which can expand a variable or a file name into an argument, and the session reads the error.

Query ​

FormMatches
cursorThe word, whole, without regard to case and to diacritics: anderungen finds Änderungen
curs*Every word that begins with curs
"items cursor"The two words next to each other, in this order

A block matches when it holds at least one of the terms. A block that holds more of them, and rarer ones, ranks higher. A term without a letter or a digit is dropped, and a query without a term stops with the query holds no word to search for. Every other character is searched for as text: the query has no operators, so AND, NOT, and parentheses are words like any other. Words are not stemmed (cursor does not find cursors; use cursor*), and ß is not folded to ss.

Blocks ​

The index holds one block per text of a mirrored file. A hit is one issue, pull request, or wiki page with the block of it that ranks best, so a long thread appears once.

KindTextAuthor, association, URL, time
titleThe item's titleThe item's
bodyThe item's bodyThe item's
commentA conversation commentThe comment's
reviewA review's summaryThe review's, with the time it was submitted
threadThe path and the diff hunk of a review threadNone
thread-commentOne comment of a review threadThe comment's
commitThe headline and the body of a commit of a pull requestOnly the commit time
sectionOne section of a wiki page, cut before every line that starts with # or ## None

An empty text is no block. The blocks of a file are numbered from 1 in the order of the file, and the id of a block is <file>:<number>, with the file relative to the mirror directory: issues/186.md:4, wiki/memory/one-cursor.md:2. The title of a wiki page is its first # heading, or its path in the clone when it has none. history.jsonl is not indexed: the commit messages of a pull request are indexed with the pull request.

text
Search of acme/widgets, mirror synced 2026-10-02T09:00:00Z: 2 hits

1. issue #186 [closed] Add a brain sync subcommand
   labels: brain, feature
   comment by alice (MEMBER) on 2026-10-01T08:00:00Z, block 4 of 17
   https://github.com/acme/widgets/issues/186#issuecomment-1
   id: issues/186.md:4
   ...one cursor for issues and pull requests, because updated_at rises...

2. wiki memory/one-cursor.md: One cursor
   section, block 2 of 3
   id: wiki/memory/one-cursor.md:2
   Items share one cursor...
  • The first line names the repository, the end of the last finished sync, and the number of hits (1 hit, 0 hits). A search without a hit prints only this line and exits 0.
  • The head of an issue or a pull request is its type, number, state, and title. The head of a wiki page is its path in the clone and its title.
  • The labels: line is printed for an item with labels, in lowercase.
  • The next line is the kind of the block, its author, the author's association, and its time where the block has them, and its position among the blocks of its file.
  • The URL is the block's, or the item's when the block has none. A wiki page has none.
  • The last line is an excerpt of at most 40 words on one line, with ... where the block's text goes on.

The output never holds the query, and control characters other than newline and tab are dropped from it.

With --json, the command prints one object on one line:

json
{"repo":"acme/widgets","synced_at":"2026-10-02T09:00:00Z","hits":[{"id":"issues/186.md:4","file":"issues/186.md","type":"issue","number":186,"title":"Add a brain sync subcommand","state":"closed","labels":["brain","feature"],"url":"https://github.com/acme/widgets/issues/186","updated_at":"2026-10-01T09:00:00Z","block":{"ordinal":4,"count":17,"kind":"comment","author":"alice","association":"MEMBER","url":"https://github.com/acme/widgets/issues/186#issuecomment-1","created_at":"2026-10-01T08:00:00Z"},"excerpt":"...one cursor for issues and pull requests, because updated_at rises...","score":7.4}]}

hits is an empty list for a search without a hit, and labels an empty list for an item without labels. score is higher for a better hit. It compares the hits of one search and nothing else.

Output of --show ​

--show <id> prints one block: the head of its item, the line that describes the block, its URL, a blank line, a line that counts the lines of the text, and the whole text with | before every line.

text
issue #186 [closed] Add a brain sync subcommand
comment by alice (MEMBER) on 2026-10-01T08:00:00Z, block 4 of 17
https://github.com/acme/widgets/issues/186#issuecomment-1

text, 2 lines, each after "| ":
| We keep one cursor for issues and pull requests, because updated_at rises
| when a comment is edited and when a review is submitted.

The text is what its author wrote, and it can hold lines that read like the head of another block. A line that starts with | is the block's text, and every other line is the command's. Line feeds at the end of the text are not printed.

With --json, the object holds block in place of hits: the fields of a hit with text, unchanged and without the prefix, and without excerpt and score. An id of another form stops with invalid block id: want <file>:<number>, as a hit prints it, and an id that names no block with no such block in the mirror.

Redaction ​

The mirror files hold GitHub's text unchanged. The index holds the text after redaction, with the secret patterns review applies to a pull request's title, body, and commit log. An excerpt, a block, and a title print a marker such as [REDACTED:github-token] in place of a recognized secret, and a search for the secret finds nothing. The text is still what everyone who can comment on the repository wrote.

Index file ​

The index is index.sqlite in the mirror directory, a SQLite database with an FTS5 table, created with mode 0600 by the first search or the first run with --brain. Every run compares the files of the mirror with the index by size and modification time before it searches, indexes the new and changed ones, and removes the ones that are gone. A wiki page is indexed when it is a regular file whose name ends in .md, outside .git, of at most 1 MiB, with no control character in its path. A file that cannot be parsed is skipped with a warning and tried again by the next run.

The index is rebuilt from the files when it is missing, when it is no SQLite database or a damaged one, when a release changed the index format, and when a release changed the redaction patterns. Deleting the file loses nothing, and brain sync --full removes it with the mirror.

Two runs can use the index at once. A run waits up to 10 seconds for a run that is writing it, and then stops with an error that starts with opening the search index or updating the search index. It leaves the file in place.

When the command stops ​

The command never syncs. It reads the mirror as the last brain sync left it, and it stops when there is none to read:

ConditionError
The repository has no mirrorno mirror of <repo> at <dir>; run "planwerk-agent brain sync <repo>" first
No sync of the mirror has finishedthe mirror of <repo> at <dir> has never finished a sync; run "planwerk-agent brain sync <repo>" again

cache ​

Inspect the on-disk cache shared by review, propose, audit, glossary, elaborate, and gap-analysis. See Caching model for background.

bash
# Show total entries, size, age distribution, and per-command breakdown
planwerk-agent cache stats

# Dump metadata and pretty-printed payload for one key
planwerk-agent cache inspect <key>
SubcommandArgumentsDescription
cache statsnoneShow cache size, age distribution, and per-command breakdown
cache inspect<key>Print metadata and the pretty-printed payload for a single cache key (keys come from cache stats)

schema ​

Print the JSON Schema (draft 2020-12) that describes a command's --format json output to stdout. Downstream tooling can validate piped JSON against the same contract the renderers follow. See Output format for the field-level contract.

bash
# Print the schema for review/audit JSON output
planwerk-agent schema review

# Validate piped JSON against the schema (example with check-jsonschema)
planwerk-agent propose --format json owner/repo > proposals.json
planwerk-agent schema propose > proposal.schema.json
check-jsonschema --schemafile proposal.schema.json proposals.json
ArgumentDescription
reviewSchema for review --format json output (report-result.schema.json)
auditSchema for audit --format json output — identical to review, because audit reuses the review result shape
proposeSchema for propose --format json output (proposal.schema.json, the proposal-result envelope)
rebaseSchema for the rebase post-rebase analysis output (rebase-analysis.schema.json)

Built-in commands ​

completion and help are provided by Cobra. completion <shell> emits shell completion scripts for bash, zsh, fish, and powershell — see Install completions & man pages. help [command] prints help for any command.