Docs/CLI reference

CLI reference

Every command, what it does, and when you would reach for it.

Any config key can be overridden per-run by an environment variable: recall.max_pack_tokensROSE_RECALL_MAX_PACK_TOKENS.

All commands accept --agent {claude,codex,mock} and --model where they spawn an agent. The store is found by walking up from the working directory to the nearest .rose/. If ~/.rose also exists it is layered underneath as a global store: lessons from both are recalled, and new lessons are written to the project one. Editing a global lesson writes back to it rather than forking a local copy. ROSE_HOME overrides the lookup entirely — use it to target the global store directly, e.g. ROSE_HOME=~/.rose rose add ... for something that should follow you across every repo.

Setup#

rose init [path]#

Create a store. --force re-creates missing subdirectories, --agent sets the default backend.

rose install#

Wire ROSE into the host agent(s).

FlagMeaning
`--target claude\codex`repeatable; defaults to claude
`--scope project\user`this repo (default) or globally
--dry-runprint what would be written, write nothing

Also puts the rose command on PATH, by symlinking bin/rose into ~/.local/bin. Hooks never need this — they invoke the package by absolute path — so without it ROSE runs fine while rose status reports command not found. --no-link skips it.

Bootstrap it from a clone with ./bin/rose install; every suggestion this command prints uses an absolute path, because advice that begins "run rose" is no use to someone who does not have it.

rose uninstall#

Remove only ROSE-tagged hooks. Lessons are left in place.

rose report [--about "..."] [--expected "..."] [--days N]#

Writes a redacted defect report to .rose/reports/ and prints it, along with the gh issue create command that would file it. It sends nothing. ROSE makes no network calls and that is a guarantee, so the transport is your own gh, run deliberately.

It contains counts, timings and non-default settings — the things that localise a defect to one stage — plus whatever you write in --about. It never contains lesson text, prompts, transcripts or paths from your machine, and everything passes through the redactor on the way out.

Reflectors are told to run this when they find a defect in ROSE itself, and then to *ask* whether you want it filed. They never file it themselves.

rose doctor#

Which backends are on PATH, whether a store exists, whether hooks are wired. Start here when ROSE seems inert.

Inspection#

rose status#

Families, node counts, episode counts, total vs apex-served tokens, and a table of each family's apex with its level, cost and success rate.

rose tree [--family F] [-v] [--recent] [--limit N]#

The tree, indented from apex down to L0. lines are delta manifest entries — what a compression dropped and which node still holds it. -v includes the first lines of each lesson body.

--recent drops the family grouping and lists newest first, which is the view that answers "did what I just taught it actually land?" — the default grouping cannot, since a new lesson sorts wherever its family happens to sit.

Every line carries an age.

rose recall --prompt "..." [--json]#

Exactly what would be injected for that prompt, and the model's stated reason for each. The tool for answering "why did it think that?" — the answer is a sentence, not a score.

Reads stdin if --prompt is omitted.

rose trace --prompt "..." [--after TRANSCRIPT]#

The agent's-eye view. Walks every stage of a recall and prints the result of each: the apex lessons put in front of the model, its verdict and reason for every one (including the branches it judged irrelevant and therefore never opened), the verbatim block that lands in the agent's context, what you see in Claude Code while it happens, and how the model's turn then begins.

--after continues into the other half: the facts parsed out of a finished session, and what the model made of them — outcome, whether you had to steer, and what was worked out by trial.

Use it when you want to know exactly what ROSE is doing to your prompts. It edits what the model sees, and that should never be something you take on trust.

rose conflicts [--family F]#

Lessons that contradict each other, with the question that would settle each. These are also raised inside the recall pack, so you normally meet them while working rather than by running this.

rose resolve <node-id> [--drop]#

Settle a conflict: keep this lesson (default) or archive it. Clears the disputed state on that node.

rose events [--kind K] [--limit N]#

Raw telemetry as JSONL. Useful kinds: inject, observe, rescue, mint, placement, conflict, conflict-resolved, compaction, merge, repair, error.

The loop#

rose add [body] [--family F] [--title T] [--tags a,b]#

Teach ROSE something now, without waiting for the session to end. Reads stdin if no body is given. The lesson is reconciled against what is already known before being stored, so it may be folded into an existing lesson, set alongside one, or reported as a contradiction with the question that would settle it.

This is the live path. The transcript sweep at session end is a safety net for what nobody noticed in the moment; this is for the moment itself, and the lesson is available to the very next prompt in the same conversation.

--no-reconcile stores it without the consistency check.

Deciding and writing happen under a lock, so concurrent reflectors cannot both conclude "new" about the same lesson. A writer waits for the lock rather than skipping.

rose observe --transcript PATH [--served ids] [--session id]#

Judge a finished session and fold the result into the tree: update node stats, file the episode, and work out which dropped detail any correction was about. Costs one judgement, skipped entirely for sessions too small to teach anything. Normally invoked by the SessionEnd hook.

rose learn --transcript PATH [--session id]#

Ask a model whether the session contained a reusable lesson, and mint a level-0 node if so. Deliberately conservative — "nothing captured" is the common and correct outcome.

rose index [--rebuild] [--gists]#

Write .rose/index.md — one line per lesson, and the only thing the selector looks at before deciding what to open. It is searched, never injected, which is what keeps the per-prompt cost of retrieval independent of how many lessons you have.

FlagMeaning
--rebuildwrite it now, whether or not it looks stale
--gistsfill missing titles and gists first, then rebuild
--limit Nlessons to backfill gists for (default 20)

Rebuilt automatically whenever it falls behind the nodes, so you rarely need this. Reach for it when a lesson is not being found: the first question is whether it is in here, and the second is whether it has a gist. A lesson without one still gets a line, built from the head of its body — but that is prose rather than a statement of when the lesson applies, and the line is what a search has to match.

The index covers the global store as well as this one, with the path on each line, since a selector that cannot see cross-project lessons cannot retrieve them.

rose route#

Selection lessons: what ROSE has learned about where to look, as opposed to what it has learned about your work. Written by the reflection pass, always injected, capped by routing.max_tokens.

FlagMeaning
--listthe rules, their record, and the growth ratio (default)
--when C --then Ateach one by hand; both are required
--forget IDdelete a rule

A rule must be conditioned on a kind of task — "when the task touches the integration tests, read nodes/testing/" — and an unconditioned one is refused. That is not style: annotating candidates with their usage record was measured to drop precision from 48% to 41% and recall from 100% to 81%, because how often a lesson is used says more about the distribution of work than about the lesson. It is also one rule per lesson, which would make this layer a second copy of the store.

The listing ends with the number the whole approach rests on — rules against lessons. It should fall as the store grows.

rose compact#

Compress lessons and regression-test the result.

FlagMeaning
--listshow what is eligible and why, run nothing
--dueprocess the queue (default)
--node IDcompress one specific node
--limit Nhow many to process (default 1)
--dry-rungenerate and validate, but do not write

A node is eligible when it is an active apex, below compaction.max_level, has at least compaction.min_successes successful recalls, is past its cooldown, and has recorded episodes to validate against. A node with no episodes is left alone on purpose: compressing with no way to check the result is worse than not compressing.

Rejection is normal and informative — the failing episodes become preserve: hints for the next attempt.

rose eval-recall [--arm judge|agentic] [--save NAME] [--against NAME] [--limit N]#

Score retrieval against what actually got used. Each recorded episode is re-selected, and the picks are compared with episode.used. Precision and recall are always printed together — precision alone is maximised by serving nothing.

ArmCandidates it chooses from
judge (default)exactly the lessons that episode was served — the baseline, 48% precision at 100% recall
agenticthe whole store, searched cold in a fresh process

The restriction in the judge arm is deliberate: a lesson nobody was shown could not have been used, so counting its absence as evidence would manufacture false positives out of the retrieval decision. The agentic arm cannot honour that restriction, because searching the whole store is the point — so the two face different candidate sets and the report says so rather than presenting them as one scale.

That arm is also cold: no conversation in front of it. In production the selector forks the live session and has the task's tool calls and reasoning, so this is a floor on agentic selection rather than an estimate of it.

--save stores the run under a name; --against compares the current run to a saved one. That pair is how any change to routing gets an A/B rather than an opinion.

rose tune [--rounds N] [--dry-run] [--history]#

Let ROSE propose an improvement to its own retrieval criteria, measure it, and keep it only if precision and recall are both at least as good. Every attempt is recorded, including the rejected ones, and fed back into the next proposal. See Self-tuning.

rose migrate [--path DIR] [--apply] [--limit N] [--all]#

Copy a Claude or Codex skills library into lessons, verbatim: one skill, one lesson, body byte for byte, no model calls. Plans by default; nothing is written without --apply, and nothing is ever deleted. --all also imports the skill-writing skills that are otherwise reported as superseded. See Migrate from Claude skills.

Configuration#

rose config [key] [value]#

No arguments dumps everything. One argument reads a dotted key. Two arguments set it.

Any key can be overridden per-run by an environment variable: recall.max_pack_tokensROSE_RECALL_MAX_PACK_TOKENS.

KeyDefaultMeaning
agentclaudedefault backend
recall.enabledtrueinject lessons at all
recall.selectoragenticagentic — a fork of your session searches the store; judge — the apex walk, and the eval baseline
recall.selector_timeout_s60bound on the search, since the prompt is blocked meanwhile
recall.selector_max_tool_calls6searches allowed before it must answer with what it has
recall.strategydelta-patchdelta-patch, delta-jump, or stepwise
recall.max_pack_tokens1200ceiling on injected context
recall.max_families3lesson families served per prompt
recall.timeout_s20bound on the routing call, kept under the hook deadline
recall.judge_calls2model calls the relevance walk may spend
recall.max_depth2how far down the tree the walk may look
recall.max_expansions3descents before escalating to L0
selection.w_judge0.60weight on the model's usefulness ranking
selection.w_prior0.28weight on the observed rescue rate
selection.w_cost0.12penalty on token cost
selection.exploreposteriorucb to keep probing rare branches
compaction.min_successes2successful recalls before compressing
compaction.max_ratio0.75candidate must be ≤75% of the original
compaction.threshold1.0required replay pass-rate
compaction.regression_k5episodes replayed per validation
compaction.max_level6deepest compression level
learning.min_tool_calls8below this a session is ignored
learning.nudge_enabledtruereflect after a substantial turn
learning.nudge_modebackgroundbackground — a detached process reflects on the transcript digest; fork — reflect inside a fork of the live session, full context, ~0.1× tokens via cache reads; block — interrupt the agent to reflect in place; off — wait for session end
learning.nudge_after_tool_calls12activity that makes a turn worth a thought
learning.nudge_after_turns3human turns that do the same
learning.min_surprises2failed tool calls that also do
learning.nudge_cooldown_s900minimum gap between asks
learning.nudge_backoff_after3consecutive fruitless nudges before backing off
routing.enabledtruelearn and inject selection lessons
routing.max_tokens800cap on the only layer retrieval still sends per prompt
placement.consulttrueask a model how new knowledge relates to old
placement.judge_calls2model calls the relatedness walk may spend
placement.max_depth2how far down the walk may look
placement.surface_conflictstrueraise unresolved contradictions during recall
signals.min_confidence0.5floor for acting on an outcome
privacy.redacttruescrub secrets before writing
limits.agent_timeout_s180per spawned agent call

Ablations#

recall.strategy exists so the descent policy can be measured rather than asserted:

rose config recall.strategy stepwise      # baseline: walk children, ignore deltas
rose config recall.strategy delta-jump    # replace apex with the holder node
rose config recall.strategy delta-patch   # default: apex + matched claims only

Hooks#

rose hook <event>#

Reads a JSON payload on stdin. Events: user-prompt-submit, stop (per turn), session-end (at teardown). Always exits 0. No-ops when ROSE_CHILD or ROSE_DISABLE is set.

You should not need to call this yourself.