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.
Overview: how the CLI is structured
There are 11 MCP tools total (8 read, 3 write). The local stdio server registers 10 of them: 7 read plus 3 write, because list_projects is remote-only and is also excluded from the CLI autogen allowlist.
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.
- The CLI is a Typer application named
nauro, defined inpackages/nauro/src/nauro/cli/main.py. Its help string is: "Human-approved project judgment and current state for connected AI agents, surfaced before 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,link,note,sync,graph,log,import,serve,render-plugin(hidden), andstatus; 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 link
nauro note nauro sync nauro log nauro import
nauro serve nauro status nauro graph
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 four workflow roles into ~/.claude/agents/ as Markdown and ~/.codex/agents/ as TOML), --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 and Codex workflow. Plain adopt installs only the core nauro-adopt skill. After restart, invoke it as /nauro-adopt in Claude Code or $nauro-adopt in Codex; Cursor uses the installed 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<repo>/.cursor/mcp.jsonfor each repo (samemcpServers["nauro"]entry shape). Flags:--project,--remove.nauro setup codexwrites the user-global~/.codex/config.tomlunder[mcp_servers.nauro](same command and args entry). 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 and Codex TOML 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. 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. Flag:--repo PATH(default cwd). 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.
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_, _configure_json_mcp, _configure_codex, SUBAGENTS_CONNECTOR_NAME_NOTICE); auth.py (login, status, logout; DEFAULT_API_URL, DEFAULT_AUTH0_AUDIENCE, REDIRECT_PORT, REDIRECT_URI); attach.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. 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.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 (required string); 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(defaultmedium)--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 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. Pass exactly one of --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).
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); 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]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.--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.nauro log [--limit/-n 10] [--full] [--all] [--decisions] [--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. Default--limitis 10.nauro status [--project] [--no-probe]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.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.
$ 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 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; import_cmd.py (import_cmd, _import_memory_bank, _import_adrs); questions.py (migrate); serve.py.
Visualize the store
nauro graph [--project NAME] [--output PATH] [--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.
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. 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, _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 only values whose KEY contains "key" (case-insensitive) and is longer than 8 chars, showingfirst4...last4via_mask. Non-"key" secrets (for example tokens stored underauth) are not masked by this function, and non-string 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 (the 11 tools)
- 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 local stdio server registers 10 of the 11 (7 read, 3 write);
list_projectsis remote-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. - 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). A--json/--no-jsonflag (default True) exists as a parity no-op, since JSON is the only output mode. - 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).
| 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, remote-only |
propose_decision | nauro propose-decision | write |
flag_question | nauro flag-question | write |
update_state | nauro update-state | write |
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, _READ_ANNOTATIONS, _WRITE_ANNOTATIONS, LIST_PROJECTS comment); 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 or block, 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).