Reference
CLI reference
Complete reference for the nauro CLI, a Typer app whose entry point is packages/nauro/src/nauro/cli/main.py. The CLI has two layers: hand-written commands for setup, connection, project and registry management, sync, and inspection; and auto-generated commands that mirror the MCP read and write tools one-for-one. As of 1.0, the nauro CLI command surface follows semantic versioning: the commands and flags documented here are a stable contract and will not change incompatibly without a major version bump.
On this page
Overview: how the CLI is structured
The shared MCP tool registry (nauro_core.mcp_tools.ALL_TOOLS) holds 11 tools (8 read, 3 write); the hosted server’s registry (HOSTED_TOOLS) adds update_stack, share_context, and create_project for 14. The local stdio server registers 12: the 10 shared tools without list_projects (7 read, 3 write) plus update_stack and share_context, which write only through an attached generation replica, so 7 read and 5 write locally. The CLI autogen allowlist mirrors the 10 shared tools; share-context and update-stack are registered explicitly as hand-written commands, and list_projects and create_project are hosted-only with no CLI command.
Each hand-written command’s flags are taken verbatim from its typer.Option and typer.Argument definitions; each autogen command’s flags are derived from the tool’s JSON input_schema: a required string or integer becomes a positional argument, everything else becomes an option, project_id becomes --project, and cwd is dropped. Every autogen command also takes --format json|text.
- The CLI is a Typer application named
nauro, defined inpackages/nauro/src/nauro/cli/main.py. Its help string is: "What every agent should know. Keep your project’s direction in human hands as agents do more of the work." - A global
--version/-Vflag printsnauro <version>and exits via the eager callback_version_callback. There is no product-analytics consent callback or first-run analytics prompt. no_args_is_help=True: runningnaurowith no command prints help._register_commands()registers the hand-written surface: top-level commandsinit,adopt,attach,reconnect,link,note,sync,graph,journal,log,import,serve,render-plugin(hidden),share-context,update-stack,status,doctor, andrepair; plus the Typer sub-appsprojects,questions,hook,setup,config,validate,auth, andtelemetry.- After the hand-written commands,
register_autogen_commands(app)adds one command per allowlisted MCP tool. - Two registered commands are not user-facing features:
render-pluginis registered withhidden=True, and the entirehooksub-app is invoked by an AI agent’s hook runner, not by a human at a prompt.
nauro init nauro adopt nauro attach nauro reconnect
nauro link nauro note nauro sync nauro log
nauro journal nauro import nauro serve nauro status
nauro doctor nauro repair nauro graph
nauro share-context nauro update-stack # replica-only writes
nauro projects … nauro questions … nauro hook … nauro setup …
nauro config … nauro validate … nauro auth … nauro telemetry …
# auto-generated from MCP tools (see "MCP tool to CLI mapping"):
nauro get-context nauro get-raw-file nauro list-decisions
nauro get-decision nauro diff-since-last-session
nauro search-decisions nauro check-decision
nauro propose-decision nauro flag-question nauro update-state
$ nauro --version
# -> nauro <version>
The version string format beyond nauro <version> is dynamic: it is read from nauro.__version__ at runtime, so the current version number depends on the installed build.
Source: packages/nauro/src/nauro/cli/main.py (app, main callback, _register_commands, register_autogen_commands).
Setup and connect
nauro init [NAME] registers a new project and scaffolds its store. NAME defaults to the directory name (demo-project with --demo). Flags: --add-repo PATH (repeatable; associate existing repo paths with the project, defaults to cwd), --demo (seed a sample project with pre-written decisions), --cloud (create a cloud-scoped project via the remote MCP server’s POST /projects), --force (overwrite the cwd’s .nauro/config.json only).
--demo and --cloud are mutually exclusive and rejected at command entry with typer.BadParameter. Local init writes .nauro/config.json (commit it) and mints a ULID, with no network calls. Starting the demo from another directory reuses the registered demo project. Re-running it in the same directory exits without writing; --force deliberately reseeds it there. --add-repo against a cloud-mode project is rejected; use nauro attach instead.
nauro adopt bootstraps an existing repo in one shot: detect the repo root, register a v2 project (local mode), scaffold the store, wire MCP, and materialize skills across Claude Code, Cursor, and Codex. It preserves an unmanaged AGENTS.md and warns. Flags: --name NAME (default: repo dir basename), --repo PATH (default: cwd), --print-prompt (print the canonical nauro-adopt skill body and exit; mutually exclusive with all other flags), --no-setup-and-skills, --with-subagents (install the five bundled workflow subagents, nauro-planner, nauro-executor, nauro-reviewer, nauro-tech-lead, and nauro-investigator, into ~/.claude/agents/ as Markdown for Claude Code, .cursor/agents/ in each registered repo for Cursor, and ~/.codex/agents/ as TOML for Codex), --force-overwrite, and --with-skills (install opt-in skills alongside the always-installed nauro-adopt). The teardown flags are --remove (un-adopt this repo: remove the MCP/skill/subagent/hook wiring, strip the generated AGENTS.md while preserving a # Manual section, delete .nauro/config.json, and deregister; drops only this repo when the project spans several, and keeps the store), --purge-store (with --remove on a project’s last repo, also delete the on-disk store; irreversible), and --yes (skip the --remove confirmation prompt).
adopt requires a git repo and refuses with a "run git init first" message otherwise. On an already-adopted repo (where a .nauro/config.json exists) it exits 1 unless re-run with --with-subagents, --with-skills, or --force-overwrite (in which case it routes to the materialize step without re-registering) or with --remove (which un-adopts the repo).
Use nauro adopt --with-skills --with-subagents for the complete Claude Code, Cursor, and Codex workflow. Plain adopt installs only the core nauro-adopt skill. After restart, invoke it as /nauro-adopt in Claude Code, $nauro-adopt in Codex, or @nauro-adopt in Cursor Agent chat, which includes the installed .cursor/rules/nauro-adopt.mdc rule.
Wiring the MCP server into each agent
nauro setup claude-coderegisters the MCP server in each repo’s project-scope.mcp.jsonundermcpServers["nauro"]with the entry{"command": "<validated absolute path to nauro>", "args": ["serve", "--stdio"]}when a durable install is available. Flags:--project NAME,--remove, and--with-hooks(wire the advisoryUserPromptSubmithook into each repo’s.claude/settings.local.json).nauro setup cursorwrites the user-global~/.cursor/mcp.json, shared by every registered project, with the entrymcpServers["nauro"] = {"type": "stdio", "command": "<validated absolute path to nauro>", "args": ["serve", "--stdio"]}. It fails when no durable absolutenauroexecutable can be validated, and it leaves an entry it does not own untouched. It then removes an owned legacy per-repo<repo>/.cursor/mcp.jsonentry from each repo (and, once that file holds nothing else and is deleted, its managed.gitignoreline); a git-tracked legacy file is refused rather than rewritten. The per-repo files Cursor still gets, throughnauro setup all --with-skills --with-subagents, are the rules.cursor/rules/nauro-*.mdcand agents.cursor/agents/nauro-*.md, which carry no machine-local path and are committed with the repo. Flags:--project,--remove;--removepreserves the global entry while another Nauro project is registered.nauro setup codexwrites the user-global~/.codex/config.tomlunder[mcp_servers.nauro]withcommand,args = ["serve", "--stdio"], andstartup_timeout_sec = 20; the timeout is a floor, so a higher value you set is kept, and keys you added to the table are never touched. Flags:--remove,--with-hooks. Codex hooks use each repo’s project-scoped.codex/hooks.jsonforSessionStartandSubagentStart; review new or changed hooks through/hooks. Standalone--removepreserves the global entry while any Nauro project is registered; usenauro setup all --removefrom the last project to clear it.nauro setup allconfigures Claude Code, Cursor, and Codex in one call. Setup and adopt preserve an unmanagedAGENTS.mdand warn; incidental regeneration does the same.nauro syncis the sole explicit overwrite path, and a# Manualsection survives replacement. Flags:--project,--remove,--with-subagents,--force-overwrite,--with-skills,--with-hooks.--with-subagentsinstalls Claude Code Markdown (~/.claude/agents/), Cursor Markdown (.cursor/agents/in each registered repo), and Codex TOML (~/.codex/agents/) definitions;--force-overwriterefreshes Nauro-owned skills and agents without their usual.bak. Its--removepath also strips the generatedAGENTS.mdin each associated repo (a# Manualsection is preserved); the per-surface--removeflags un-wire only that surface’s config.- Commit
.nauro/config.jsonas the portable project identity. Repo-local MCP and hook files are machine-local because they contain an absolute executable path. Setup manages their.gitignoreentries and refuses to write such wiring into a tracked file. - At the shared
AGENTS.mdregeneration seam, Nauro writes a two-line ownedCLAUDE.mdbridge with@AGENTS.mdonly when the file is absent. Existing foreign files are never modified. Setup automatically strips only Nauro’s retired marked block, and teardown removes only the validated owned bridge. setup_apphas noinvoke_without_commandcallback, so runningnauro setupwith no subcommand prints help. (This is unlikenauro projects, which lists on a bare invocation.)- Setup probes candidate executables with
--versionand prefers a durable absolute pipx, uv tool, system, or equivalent path. A working project-venv sibling is recorded only with a fragility warning. Barenaurois the warned terminal fallback when no absolute candidate is available. - The local-install MCP server name is
nauro(lowercase) across all three surfaces. Claude Code’s workflow definitions require the remote cloud connector nameNauro(capitalized) so their bundled tools resolve. Codex does not share that connector-name constraint. - Run
nauro statusto diagnose integration wiring andnauro doctorto check store integrity. MCP config is loaded at session start, so the final verification happens in a fresh agent session.
Auth and cloud projects
nauro auth loginruns Auth0 Authorization Code + PKCE (S256) via a localhost redirect (port 18457, redirect URIhttp://localhost:18457/callback) and stores tokens (sub,sanitized_sub,user_id,access_token,refresh_token) in~/.nauro/config.json.nauro auth statusshows auth state and exits 1 if not authenticated;nauro auth logoutclears stored credentials.nauro auth refreshrenews credentials explicitly, without replaying any request; it needs either a migrated (generation-replica) project resolved from the cwd or--reference-profile PATH, and exits 2 with a usage error otherwise. Renewal is otherwise automatic before authenticated generation work, sorefreshis for forcing it. Everyauthsubcommand accepts--reference-profile PATHto act on an explicit reference authentication profile instead of the stored credentials, and on a migrated projectlogin,status, andlogoutoperate on that project’s generation credentials. Default API URL:https://mcp.nauro.ai. Auth0 audience and MCP connector URL:https://mcp.nauro.ai/mcp(enter exactly, no trailing slash). The login success message instructs Codex users to addmcp_oauth_callback_port = 8765to~/.codex/config.tomlfor the remote connector.nauro attach PROJECT_IDis the cloud equivalent ofinit --add-repo: it associates the current repo with an existing cloud project, verifying membership againstGET /projectsfirst. Flags:--repo PATH(default cwd) and--generation(also install an initial generation replica of the project). PROJECT_ID is a ULID argument.nauro link --cloudpromotes the current repo’s local-only project to cloud: it callscreate_project(name)to mint a cloud project_id, re-keys the local store and registry entry (rename_project_id_v2), rewrites the repo config to cloud mode, and does a best-effort initial push. It requires auth (runnauro auth loginfirst) and a local-mode project. The promotion is one-way; there is no inverse unlink.nauro linkwith no target errors and suggestsnauro link --cloud.nauro reconnectconnects or recovers the project already named by this repository’s.nauro/config.json(found by walking up from the cwd; exits 1 when there is none). It takes no flags. When the repo is already connected it prints the project name and store path and, if this machine records no MCP wiring for the repo, suggestsnauro setup all. When the repo is disconnected (a fresh clone, or a store that moved) it prints guidance and prompts for one action:locatebinds an existing local store at an absolute path you enter,restoreverifies cloud membership and restores the latest eligible cloud record (a server-side rename updates the local records), andcontinue(the default) makes no changes. A successful connection refreshes a Nauro-managedAGENTS.md. It is unavailable on a generation replica.
The Codex remote connector uses the fixed callback port 8765 and requires Codex 0.131.0 or newer; the full setup is on the Connect your agent page.
$ nauro init my-project
$ nauro setup all --with-skills --with-subagents
$ nauro setup all --with-hooks # optional Claude Code + Codex hooks
$ nauro status
$ nauro doctor
$ mkdir -p /tmp/nauro-demo && cd /tmp/nauro-demo
$ nauro init --demo
$ nauro check-decision "Store dollar amounts as decimal numbers"
New-Item -ItemType Directory -Force "$env:TEMP\nauro-demo" | Out-Null
Set-Location "$env:TEMP\nauro-demo"
nauro init --demo
nauro check-decision "Store dollar amounts as decimal numbers"
$ nauro adopt --with-skills --with-subagents
$ nauro status
# Restart, then use /nauro-adopt in Claude Code or $nauro-adopt in Codex
$ nauro auth login
$ nauro link --cloud # one-time: promote the local project to cloud
$ nauro sync
Source: init.py; adopt.py; setup.py (claude_code, cursor, codex, all_) with cli/integrations/ (orchestrator.py, json_mcp.py, cursor.py, codex_config.py, SUBAGENTS_CONNECTOR_NAME_NOTICE); auth.py (login, status, logout, refresh; REDIRECT_PORT, REDIRECT_URI) with nauro/auth.py (DEFAULT_API_URL, DEFAULT_AUTH0_AUDIENCE) and cli/generation_auth.py; attach.py; reconnect.py; link.py; README (Quickstart, Connect, Remote connector).
Decisions and context (read)
The read-side MCP tools are exposed as CLI commands by cli/autogen.py. Each prints the tool’s JSON envelope (json.dumps(..., indent=2)) to stdout by default; --format text renders the same envelope for human reading through the shared read-tool renderers, falling back to JSON when no renderer applies. They resolve the project from the cwd’s .nauro/config.json, or via --project NAME.
nauro check-decision PROPOSED_APPROACH [--context TEXT] [--project NAME]runs thecheck_decisionkernel against the local store and returns related decisions (via Tier 1 plus Tier 2 BM25 retrieval) along with a deterministic assessment string. It does not judge conflicts. PROPOSED_APPROACH is a positional argument (required string).nauro get-context [--level L0|L1|L2] [--project]returns project context. Default level L0 is concise (project summary, current state, top open questions, and the last 10 active decisions with titles and dates); L1 (working set) adds full decision bodies for recent decisions; L2 is the full dump of everything in the store.nauro search-decisions QUERY [--limit 10] [--include-superseded/--no-include-superseded] [--project]BM25-ranks decisions against titles and rationale and returns active decisions by default. QUERY is a positional argument (required, non-empty). Default--limitis 10;include_supersededdefaults to False.nauro list-decisions [--limit 20] [--include-superseded/--no-include-superseded] [--project]browses decision history. Default--limitis 20;include_supersededdefaults to False.nauro get-decision NUMBER [--mode header|full] [--project]returns one decision by number. Default modefullreturns complete markdown;headerreturns the triage frontmatter (status, supersession, date, type, confidence) plus the title and a short lede from the rationale. NUMBER is a positional integer argument.nauro diff-since-last-session [--days N] [--project]diffs the two most recent session-scoped snapshots when--daysis omitted, or finds the nearest snapshot to N days ago and diffs against the latest when--daysis provided.daysis an optional integer with no default.nauro get-raw-file PATH [--project]returns the raw markdown of any store file. Valid paths includeproject.md,state_current.md,stack.md,open-questions.md, anddecisions/042-some-decision.md. PATH is a positional argument (required string).
Note: list_projects is the 8th read tool but is remote-only, so it is not in the CLI autogen allowlist and there is no nauro list-projects MCP command. The unrelated nauro projects list lists the local registry, not cloud projects.
$ nauro check-decision "Adopt Redis for the read cache"
$ nauro search-decisions "authentication" --limit 5
$ nauro get-decision 42 --mode header
$ nauro get-context --level L1
Source: cli/autogen.py (AUTOGEN_ALLOWLIST, _schema_to_typer_params, _make_command); nauro_core/mcp_tools.py (GET_CONTEXT, GET_RAW_FILE, LIST_DECISIONS, GET_DECISION, DIFF_SINCE_LAST_SESSION, SEARCH_DECISIONS, CHECK_DECISION input_schema); nauro/mcp/tools.py (tool_* adapters).
Decisions and questions (write)
Three write tools are exposed as CLI commands via autogen: propose-decision, flag-question, and update-state. They run the same validation pipeline as the MCP tools (dispatching to the same tool_<name> adapter) and emit the JSON envelope.
nauro propose-decision RATIONALE [options] records a decision. RATIONALE is the only positional argument; it is optional in the signature so the reference lookup modes can omit it, and a plain write without it exits 2 with Missing argument RATIONALE. The title travels as --title. Options derived from the input_schema:
--title TEXT(required in practice foraddandsupersede, where Tier 1 rejects a blank title; rejected onupdate)--operation add|update|supersede(defaultadd)--affected-decision-id ID(required when operation isupdateorsupersede)--rejected JSON(a JSON array of{alternative, reason}objects)--confidence high|medium|low(no CLI default, so anupdatecan leave it unset; the adapter appliesmediumonaddandsupersedewhen omitted)--decision-type {architecture, api_design, infrastructure, pattern, refactor, data_model}(no default)--reversibility easy|moderate|hard(no default)--files-affected PATH(repeat the flag per path)--resolves-questions ID(repeatable)--project NAME
--rejected (a list[dict] schema property) accepts three input forms via cli/_json_input.parse_json_list_of_dicts: an inline JSON literal, @file.json (read a file), or - (read stdin). Malformed input (bad JSON, non-array, non-object elements, a missing file, or empty stdin) raises typer.BadParameter, which is rendered to stderr with exit code 2; the adapter is never invoked.
--files-affected and --resolves-questions are list[str] schema properties, so Typer aggregates the repeated flags natively (repeat the flag once per value).
With --operation update the server consumes only RATIONALE and --affected-decision-id: --title, --rejected, --files-affected, --decision-type, --reversibility, and --confidence are rejected at the boundary. Use supersede to change any of those. An update bumps the decision’s version and appends a dated paragraph to its rationale.
nauro flag-question [QUESTION] [--question TEXT] [--context TEXT] [--targets ID] [--resolved-by ID] [--project] flags an open question or, with --resolved-by set, stamps the --targets entries as resolved against that decision id. QUESTION may be passed positionally or as --question, not both; pass exactly one of a question or --resolved-by. --targets is a repeatable list; --resolved-by takes a decision id.
nauro update-state DELTA [--project] replaces the current state body (state_current.md) with DELTA, archiving the prior body to state_history.md, and triggers a snapshot. Each call replaces the whole body rather than appending, so compose one delta per session; deltas over 5,000 characters are rejected at the boundary. A keyword-overlap warning returns when DELTA heavily mirrors an existing state bullet, but the write still applies. DELTA is a positional argument (required string).
Two write commands are registered explicitly in cli/main.py rather than through autogen, because their tools are not in the shared registry. nauro share-context [SLUG] [CONTENT] [--pointer-kind brief|resume|selection] [--summary TEXT] [--project/-p NAME] publishes an immutable brief at context/<slug>.md together with its discovery pointer; slugs are permanent, so a slug_conflict_observed result stays unresolved until you recover the saved reference. nauro update-stack [CONTENT] [--expected-revision REV] [--project/-p NAME] replaces the whole stack.md document; an omitted --expected-revision uses the installed replica’s revision, and a mismatch reports revision_conflict_observed and publishes nothing. Both write only through an attached generation replica; on a legacy store they exit 1 with "requires a generation replica".
These two commands, and update-state and flag-question as well (update-state also takes --expected-revision), carry the generation write options --request-mode submit|discover|recover|retry, --operation-id ID, and --payload-digest DIGEST: submit (the default) creates and saves an immutable attempt; discover lists this connection’s local saved attempts; recover only looks up the saved operation id and payload digest; retry looks up first and resends the original payload only when it is absent and still within its original 24-hour horizon. The positional content is optional so the lookup modes can run without it. The result envelope prints as JSON, or as key-value lines with --format text, and the exit code is 1 unless the status is committed or discovered. propose-decision carries its own reference delivery options instead (--reference-profile PATH, --request-mode, --operation-id, --payload-digest, --after CURSOR); --request-mode there also accepts prepare. Those modes run through --reference-profile or, without one, through the generation replica the project resolves to; on a legacy store without a profile they exit 2 with "Reference modes require --reference-profile".
There are also two simpler hand-written write commands rooted at nauro note TEXT: it records a decision by default, or a question if TEXT ends with ? (and --decision is not set) or if --question / -q is passed.
Options: --rationale / -r, --confidence / -c (default medium), --decision / -d (force decision), --question / -q (force question), and --project. On each write it refreshes a Nauro-managed AGENTS.md; an unmanaged file is preserved with a warning.
$ nauro propose-decision "In-memory cache for hot read paths" --title "Adopt Redis" \
--files-affected src/cache.py --files-affected src/api.py \
--rejected '[{"alternative": "Memcached", "reason": "Less feature-rich"}]'
$ nauro propose-decision "..." --title "Adopt Redis" --rejected @rejected.json
$ echo '[{"alternative":"Memcached","reason":"Less feature-rich"}]' | \
nauro propose-decision "..." --title "Adopt Redis" --rejected -
$ nauro note "Switched to Postgres for the audit log" -r "Need transactional writes"
$ nauro note "Should we shard the events table?" # trailing '?' -> question
Source: cli/autogen.py (_optional_param array handling, json_array_names, enum_arg_names); cli/_json_input.py (parse_json_list_of_dicts); nauro_core/mcp_tools.py (PROPOSE_DECISION, FLAG_QUESTION, UPDATE_STATE, UPDATE_STACK, SHARE_CONTEXT); cli/commands/share.py; cli/commands/stack.py; cli/generation_writes.py (with_write_options); cli/decision_reference.py (with_reference_options); cli/commands/note.py; README (MCP tools section, propose-decision example).
Project, registry and sync
nauro projects(ornauro projects list) prints every local v2 registry entry: project_id, name, mode, and repo paths (Repo: (none)when a project has no repos). This is the local registry, distinct from the remote-onlylist_projectsMCP tool.projects_apphasinvoke_without_command=True, so a barenauro projectslists.nauro projects rm PROJECT_ID [--yes]removes a single registry entry but leaves the on-disk store under~/.nauro/projects/<id>/intact (recoverable). It prompts for confirmation (typer.confirm(..., abort=True)) unless--yes, and exits 1 if no project has that id. This is the documented recovery path whennauro initrefuses to mint a second entry for an already-claimed repo. PROJECT_ID is a ULID argument.nauro sync [--message/-m TEXT] [--project NAME] [--status] [--push-only]captures a snapshot and explicitly replacesAGENTS.mdin each associated repo. It is the sole overwrite path, and a# Manualsection survives replacement. With cloud sync configured it pulls from the server first (a git-style pull-then-push:GET /sync/manifest, thenPOST /sync/presign, then S3 GETs) and then pushes;--push-onlyskips the pull.--statusshows sync state instead (server URL, files tracked, last successful sync, pending local changes, conflict backups). The trigger defaults to"manual sync"when no message is given. It does not touch project state (state_current.md); use theupdate_statetool for that. On a generation replica,nauro syncrefreshes the installed generation from the server and pushes no local files (--push-onlyis refused with exit 1), and--statusreports the replica’s refresh state: the generation, the last refresh attempt, and the last successful refresh. Exits 1 on failure and 2 when the pull left files unwritten.nauro log [--limit/-n 10] [--full] [--all] [--decisions] [--json] [--project]lists recent snapshots (version, timestamp, trigger).--decisionsswitches to a decision list (id, status, version, title);--allincludes superseded decisions in that list;--fullprints complete snapshot content;--jsonemits either listing as JSON. Default--limitis 10. On a generation replica only--decisionsserves, from the installed generation (aGeneration:line precedes the table); a plainnauro logrefuses with exit 1 because the snapshot archive stays on the server, and points atnauro diff-since-last-sessionfor recent history.nauro status [--project] [--no-probe] [--json]diagnoses Sync, MCP, Codex hooks, installed skills, workflow agents, and generatedAGENTS.md, then shows local and, when cloud-authenticated, remote decision counts plus last-sync time. By default it probes each recorded MCP and Codex-hook executable;--no-probereports wiring presence without running those liveness checks. Sync is "active" only when an auth token exists and the project is v2 cloud-mode.--jsonemits the capability report as JSON (or an error envelope when no project resolves). On a generation replica it reports the replica’s refresh state instead: the generation, the last refresh attempt, and the last successful refresh, with a pointer atnauro auth statusandnauro syncwhen the last refresh failed; it exits 1 when that state cannot be read.nauro journal [--project NAME]emits the store’s write-path provenance journal as one JSON array on stdout, oldest event first. It is the machine-read the desktop app’s operations log consumes, so it is a hand-written command rather than an MCP tool. A missing or empty journal prints an empty array and exits 0, and a truncated or corrupt final record is skipped rather than failing the whole log; windowing and filtering are left to the reader.nauro doctor [--project NAME]reports deterministic integrity defects in the decision store: unparseable decision files, duplicate decision numbers, duplicate active titles, dangling supersession refs, supersession cycles, and status contradictions. Supersede backref orphans (a decision recordssupersedes: Nwhile N is still active with nosuperseded_by) are counted separately as repairable, with the fix route named, and unknown frontmatter keys are listed as advisory. It exits 0 whether or not it finds defects, because a defect is information rather than a failed command; on a generation replica it diagnoses the installed generation (printing aGeneration:line), reports orphans as unrepairable there, and exits 1 only when that generation cannot be read. It reads only the store: connection and wiring problems belong tonauro status.nauro repair [--project NAME]acts on exactly one shape, the single unambiguous supersede backref orphan, after an interactive confirmation that names both decisions, the exact field change, and each side’s version, date, and status; there is deliberately no flag to skip the prompt. Several orphans, or any other shape, are reported with guidance and nothing is written. The confirmed plan is recomputed under the decision write lock before the one write, so a store that changed in between aborts with exit 1 and no change. After the flip it journals the repair, captures a snapshot, refreshes a Nauro-managedAGENTS.md, and suggestsnauro sync. It is unavailable on a generation replica. The hosted-recovery flags (--judgmentwith--project PROJECT_ID, plus--saga ID,--resume,--abandon,--action ID, or--resend-action ID) inspect or recover a hosted judgment instead;--resumeand--abandonexclude each other,--actionand--resend-actioneach exclude the other modes, and an invalid combination exits 2.nauro importmigrates external context. Flags (fromimport_cmd):--memory-bank PATH(a Cline / Roo Code Memory Bank.context/directory),--adr PATH(Architecture Decision Records, NNN- or NNNN-title.md files), and--project NAME. There is no--dry-runon import. With neither--memory-banknor--adr, it errors and exits 1. Each import path captures a snapshot afterward.nauro questions migrate [--project] [--dry-run]mints sequential Q### ids for legacy[YYYY-MM-DD HH:MM UTC]question entries in open-questions.md.--dry-runprints the rename map and writes nothing; on a real run it refreshes a Nauro-managedAGENTS.mdand preserves an unmanaged file with a warning.nauro servestarts the local MCP server over stdio (the sole supported local transport; the former local HTTP transport was retired). It carries a hidden--stdioflag defaulting to True that is a no-op kept for backward compatibility with installed client configs that spawnnauro serve --stdio. There is no--projectflag; the server resolves project context from the cwd’s.nauro/config.json.--reference-profile PATHinstead starts a decision-only reference connection from an explicit operator profile.
$ nauro projects # list registered projects (id, name, mode, repos)
$ nauro projects rm 01J… --yes # drop a registry entry; store left intact
$ nauro sync -m "after auth refactor"
$ nauro log --decisions --all
$ nauro status
$ nauro doctor # store integrity; exits 0 either way
$ nauro journal # provenance journal as a JSON array
$ nauro import --memory-bank ./.context
$ nauro import --adr ./docs/adr
Source: cli/commands/projects.py (projects_app, list_projects, remove_project); sync.py (sync, _show_status, _pull_via_presign); log.py; status.py; journal.py; doctor.py; repair.py; cli/generation_reads.py (refresh_command, status_command, render_installed, require_legacy_snapshots); import_cmd.py (import_cmd, _import_memory_bank, _import_adrs); questions.py (migrate); serve.py.
Visualize the store
nauro graph [--project NAME] [--output PATH] [--format html|json] [--open/--no-open] [--include-bodies/--no-include-bodies] renders the project’s decision store to one self-contained HTML file (inline CSS, JS, and SVG, no network requests, works offline) and, by default, opens it in a browser. --open defaults on; pass --no-open to write the file without opening it. The project resolves from the cwd’s .nauro/config.json, or via --project NAME. --format json prints the same graph payload to stdout instead: no file is written and no browser opens, and combining it with --output is rejected with exit 2 (redirect stdout to a file instead).
The file carries four views. Graph (the default) is a deterministic node-link map of the whole store, drawing supersession threads as star clusters and standalone decisions as category discs, with insight chips that highlight matching nodes. Lineage draws one supersession DAG per thread, so a consolidation reads as a fan-in. Timeline places decisions on a true date axis in category lanes. Browse lists every decision grouped by category, with superseded ones dimmed. A header strip filters by status, category, and confidence (confidence stands in for priority, since it is the per-decision metadata Nauro records).
With no --output, the file lands at <store>/nauro-graph.html, inside the project store directory rather than the cwd, because the HTML embeds store content and a current-directory default would invite committing that extract into a repo. On a generation replica the graph is built from the installed generation (a Generation: line goes to stderr) and lands at <Nauro home>/nauro-graph-<project_id>.html instead, keeping the replica root free of foreign files. An explicit --output that names an existing directory writes the default filename inside it; otherwise the path is taken as the full file path. The default filename is excluded from sync (the NEVER_SYNC set), since its generation timestamp changes every run and its content hash would never settle; a custom --output path is the user’s explicit choice and is not guarded.
By default the file embeds decision titles and metadata, open-question summaries, and each decision’s full body Markdown behind an expander in the detail panel. Pass --no-include-bodies for a redacted artifact with titles and metadata only. The render is lenient: a malformed or unreadable decision file is skipped with a warning on stderr rather than aborting the whole graph, and the same tolerance applies to an unreadable open-questions file.
$ nauro graph # render to the store dir and open it
$ nauro graph --output ./decisions.html --no-open
$ nauro graph --no-include-bodies # redacted: titles and metadata only
Source: cli/commands/graph.py (graph, _read_decisions_lenient, _read_open_questions, _generation_inputs, _resolve_output_path); graph/__init__.py (DEFAULT_GRAPH_FILENAME, render_html); sync/merge.py (NEVER_SYNC); nauro_core/graph.py (build_graph_payload); README (graph paragraph).
Configuration and telemetry
nauro configis a read-only and cleanup surface; there is no genericset. Subcommands:nauro config get KEY(exits 1 if not set),nauro config list(all top-level config values), andnauro config unset KEY(exits 1 if not set).config listandconfig getmask values whose key containskey,token,secret,password, orcredential(case-insensitive), recursing into nested objects so the tokens underauthnever print in full: via_mask, a sensitive string longer than 8 characters shows asfirst4...last4and a shorter one as***. Other values are printed as-is.nauro telemetry status|enable|disable|resetare deprecated no-network, no-mutation compatibility shims retained for the 1.x command contract. Every subcommand reports that product telemetry has retired. The command group will be removed in 2.0.- Legacy telemetry config is ignored and left untouched. Nauro sends no product analytics.
nauro validate status [--project]shows validation diagnostics: project name, total decisions, active decisions, and the search backend line: "Search: BM25 (bm25s + PyStemmer, built on-the-fly per query)".
$ nauro telemetry status
$ nauro telemetry disable
$ nauro config list
$ nauro validate status
Which keys actually appear in ~/.nauro/config.json depends on which features have written config; _mask applies to whichever of them are present when config list prints.
Source: cli/commands/config.py (config_get, config_list, config_unset, _mask); telemetry.py (status, enable, disable, reset); validate.py (status).
MCP tool to CLI mapping (11 shared, 12 local, 14 hosted)
- The single source of truth for MCP tools is
nauro_core.mcp_tools.ALL_TOOLS: 11 tools, 8 read plus 3 write. Read:get_context,get_raw_file,list_decisions,get_decision,diff_since_last_session,search_decisions,check_decision,list_projects. Write:propose_decision,flag_question,update_state. The hosted server’s registry,HOSTED_TOOLS, isALL_TOOLSplus the typed operationsupdate_stack,share_context, andcreate_project: 14 tools. - The local stdio server registers 12 (7 read, 5 write): the 10 shared tools without
list_projects, plusupdate_stackandshare_context, which write only through an attached generation replica and otherwise answer with ablockedstatus whoseerror_codeisgeneration_required.list_projectsandcreate_projectare hosted-only. - The CLI autogen layer (
cli/autogen.pyAUTOGEN_ALLOWLIST) exposes exactly 10 tools as commands;list_projectsis excluded by name, since local installs auto-resolve to a single project and need no discovery entry point. The allowlist is verified againstALL_TOOLSat import time and fails loudly on a typo.share-contextandupdate-stackare not autogen commands:cli/main.pyregisters them explicitly fromcli/commands/share.pyandstack.py, built on the sameSHARE_CONTEXTandUPDATE_STACKspecs. - Tool name to CLI command name is snake_case to kebab-case (
_command_namereplaces_with-):get_contextbecomesnauro get-context,propose_decisionbecomesnauro propose-decision,diff_since_last_sessionbecomesnauro diff-since-last-session, and so on. - Schema-to-flag rules (
cli/autogen.py): a requiredstringorintegerproperty becomes a positional argument; every other property becomes an option--kebab-name; abooleanbecomes--flag/--no-flag; astringwith anenumbecomes a native Choice (a bad value exits 2);array of stringbecomes a repeatable--flag;array of objectbecomes a single JSON-valued--flag(literal, @file, or -).project_idis dropped and replaced by--project NAME;cwdis dropped (implicit). Every autogen command also takes--format json|text(json, the default, prints the machine-readable envelope exactly as before;textrenders it for human reading) and a--json/--no-jsonflag (default True) kept as a compatibility no-op. - Each CLI command dispatches to the matching
tool_<name>adapter innauro/mcp/tools.py(for exampletool_propose_decision), the same adapter the local stdio server uses, so the CLI and MCP stay in lockstep. Enum members are converted back to their wire.valuebefore dispatch. - Read tools are annotated
readOnlyHint=True,openWorldHint=False,idempotentHint=True; write tools arereadOnlyHint=False,destructiveHint=False,openWorldHint=False,idempotentHint=False. Annotations come from_READ_ANNOTATIONSand_WRITE_ANNOTATIONS(every tool is closed-world; writes are additive, so nothing is deleted).update_stackandshare_contextcarry the write annotations;create_projectis the one write annotatedidempotentHint=True, since itsoperation_idmakes a resend replay the original outcome.
| MCP tool | CLI command | Kind |
|---|---|---|
get_context | nauro get-context | read |
get_raw_file | nauro get-raw-file | read |
list_decisions | nauro list-decisions | read |
get_decision | nauro get-decision | read |
diff_since_last_session | nauro diff-since-last-session | read |
search_decisions | nauro search-decisions | read |
check_decision | nauro check-decision | read |
list_projects | (no CLI command) | read, hosted-only |
propose_decision | nauro propose-decision | write |
flag_question | nauro flag-question | write |
update_state | nauro update-state | write |
update_stack | nauro update-stack | write, replica-only; registered explicitly |
share_context | nauro share-context | write, replica-only; registered explicitly |
create_project | (no CLI command) | write, hosted-only |
AUTOGEN_ALLOWLIST: frozenset[str] = frozenset(
{
"get_context",
"get_raw_file",
"list_decisions",
"get_decision",
"diff_since_last_session",
"search_decisions",
"check_decision",
"update_state",
"flag_question",
"propose_decision",
}
)
Source: nauro_core/mcp_tools.py (ALL_TOOLS, HOSTED_TOOLS, UPDATE_STACK, SHARE_CONTEXT, CREATE_PROJECT, _READ_ANNOTATIONS, _WRITE_ANNOTATIONS, LIST_PROJECTS comment); mcp/stdio_server.py (_spec_kwargs, the registered tools); cli/main.py (_register_commands); cli/autogen.py (AUTOGEN_ALLOWLIST, _DROPPED_PROPERTIES, _command_name, _schema_to_typer_params, _make_command, register_autogen_commands); nauro/mcp/tools.py (tool_* adapters); README (MCP tools section).
Hidden and agent-only commands
Two commands exist in code but are not user-facing, so you would not type them at a prompt.
render-pluginis registered withhidden=Trueand is a cross-repo CI helper: it materializes the canonicalnauro-*subagent bodies into<dir>/agents/so a separate plugin repo can commit byte-identical copies, and--checkbyte-verifies the committed copies against the live render (the plugin repo’s CI gate). It is not a user-facing feature.- The
hooksub-app is invoked by agent hook runners, not by a human.nauro hook user-prompt-submithandles Claude Code turns: it resolves the project, runscheck_decision, and returns an advisory block when a decision clears the BM25 relevance floor.nauro hook codex-bootstraphandles CodexSessionStartandSubagentStart: it injects the canonical Nauro protocol plus current L0 project context. Both commands read JSON from stdin, never write to the project store or block the turn (user-prompt-submitkeeps only a per-session dedup file under the Nauro home), swallow failures, and always exit 0.
Source: cli/main.py (render-plugin hidden=True); cli/commands/render_plugin.py; hook.py (user_prompt_submit, codex_bootstrap).