Skip to content
Nauro

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 in packages/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 / -V flag prints nauro <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: running nauro with no command prints help.
  • _register_commands() registers the hand-written surface: top-level commands init, adopt, attach, link, note, sync, graph, log, import, serve, render-plugin (hidden), and status; plus the Typer sub-apps projects, questions, hook, setup, config, validate, auth, and telemetry.
  • 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-plugin is registered with hidden=True, and the entire hook sub-app is invoked by an AI agent's hook runner, not by a human at a prompt.
command surface
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
shell
$ 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-code registers the MCP server in each repo's project-scope .mcp.json under mcpServers["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 advisory UserPromptSubmit hook into each repo's .claude/settings.local.json).
  • nauro setup cursor writes <repo>/.cursor/mcp.json for each repo (same mcpServers["nauro"] entry shape). Flags: --project, --remove.
  • nauro setup codex writes the user-global ~/.codex/config.toml under [mcp_servers.nauro] (same command and args entry). Flags: --remove, --with-hooks. Codex hooks use each repo's project-scoped .codex/hooks.json for SessionStart and SubagentStart; review new or changed hooks through /hooks. Standalone --remove preserves the global entry while any Nauro project is registered; use nauro setup all --remove from the last project to clear it.
  • nauro setup all configures Claude Code, Cursor, and Codex in one call. Setup and adopt preserve an unmanaged AGENTS.md and warn; incidental regeneration does the same. nauro sync is the sole explicit overwrite path, and a # Manual section survives replacement. Flags: --project, --remove, --with-subagents, --force-overwrite, --with-skills, --with-hooks. --with-subagents installs Claude Code Markdown and Codex TOML definitions; --force-overwrite refreshes Nauro-owned skills and agents without their usual .bak. Its --remove path also strips the generated AGENTS.md in each associated repo (a # Manual section is preserved); the per-surface --remove flags un-wire only that surface's config.
  • Commit .nauro/config.json as the portable project identity. Repo-local MCP and hook files are machine-local because they contain an absolute executable path. Setup manages their .gitignore entries and refuses to write such wiring into a tracked file.
  • At the shared AGENTS.md regeneration seam, Nauro writes a two-line owned CLAUDE.md bridge with @AGENTS.md only 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_app has no invoke_without_command callback, so running nauro setup with no subcommand prints help. (This is unlike nauro projects, which lists on a bare invocation.)
  • Setup probes candidate executables with --version and prefers a durable absolute pipx, uv tool, system, or equivalent path. A working project-venv sibling is recorded only with a fragility warning. Bare nauro is 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 name Nauro (capitalized) so their bundled tools resolve. Codex does not share that connector-name constraint.
  • Run nauro status to diagnose integration wiring and nauro doctor to 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 login runs Auth0 Authorization Code + PKCE (S256) via a localhost redirect (port 18457, redirect URI http://localhost:18457/callback) and stores tokens (sub, sanitized_sub, user_id, access_token, refresh_token) in ~/.nauro/config.json. nauro auth status shows auth state and exits 1 if not authenticated; nauro auth logout clears 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 add mcp_oauth_callback_port = 8765 to ~/.codex/config.toml for the remote connector.
  • nauro attach PROJECT_ID is the cloud equivalent of init --add-repo: it associates the current repo with an existing cloud project, verifying membership against GET /projects first. Flag: --repo PATH (default cwd). PROJECT_ID is a ULID argument.
  • nauro link --cloud promotes the current repo's local-only project to cloud: it calls create_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 (run nauro auth login first) and a local-mode project. The promotion is one-way; there is no inverse unlink. nauro link with no target errors and suggests nauro 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.

shell · local quickstart
$ 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
shell · zero-account demo (macOS/Linux)
$ mkdir -p /tmp/nauro-demo && cd /tmp/nauro-demo
$ nauro init --demo
$ nauro check-decision "Store dollar amounts as decimal numbers"
PowerShell · zero-account demo
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"
shell · complete adoption
$ nauro adopt --with-skills --with-subagents
$ nauro status
# Restart, then use /nauro-adopt in Claude Code or $nauro-adopt in Codex
shell · promote to cloud
$ 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 the check_decision kernel 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 --limit is 10; include_superseded defaults to False.
  • nauro list-decisions [--limit 20] [--include-superseded/--no-include-superseded] [--project] browses decision history. Default --limit is 20; include_superseded defaults to False.
  • nauro get-decision NUMBER [--mode header|full] [--project] returns one decision by number. Default mode full returns complete markdown; header returns 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 --days is omitted, or finds the nearest snapshot to N days ago and diffs against the latest when --days is provided. days is an optional integer with no default.
  • nauro get-raw-file PATH [--project] returns the raw markdown of any store file. Valid paths include project.md, state.md, stack.md, open-questions.md, and decisions/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.

shell
$ 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 for add and supersede, where Tier 1 rejects a blank title; rejected on update)
  • --operation add|update|supersede (default add)
  • --affected-decision-id ID (required when operation is update or supersede)
  • --rejected JSON (a JSON array of {alternative, reason} objects)
  • --confidence high|medium|low (default medium)
  • --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.

shell · inline rejected JSON
$ 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"}]'
shell · rejected from file or stdin
$ nauro propose-decision "..." --title "Adopt Redis" --rejected @rejected.json
$ echo '[{"alternative":"Memcached","reason":"Less feature-rich"}]' | \
  nauro propose-decision "..." --title "Adopt Redis" --rejected -
shell · note shortcut
$ 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 (or nauro 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-only list_projects MCP tool. projects_app has invoke_without_command=True, so a bare nauro projects lists.
  • 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 when nauro init refuses 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 replaces AGENTS.md in each associated repo. It is the sole overwrite path, and a # Manual section survives replacement. With cloud sync configured it pulls from the server first (a git-style pull-then-push: GET /sync/manifest, then POST /sync/presign, then S3 GETs) and then pushes. --status shows 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 the update_state tool for that.
  • nauro log [--limit/-n 10] [--full] [--all] [--decisions] [--project] lists recent snapshots (version, timestamp, trigger). --decisions switches to a decision list (id, status, version, title); --all includes superseded decisions in that list; --full prints complete snapshot content. Default --limit is 10.
  • nauro status [--project] [--no-probe] diagnoses Sync, MCP, Codex hooks, installed skills, workflow agents, and generated AGENTS.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-probe reports wiring presence without running those liveness checks. Sync is "active" only when an auth token exists and the project is v2 cloud-mode.
  • nauro import migrates external context. Flags (from import_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-run on import. With neither --memory-bank nor --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-run prints the rename map and writes nothing; on a real run it refreshes a Nauro-managed AGENTS.md and preserves an unmanaged file with a warning.
  • nauro serve starts the local MCP server over stdio (the sole supported local transport; the former local HTTP transport was retired). It carries a hidden --stdio flag defaulting to True that is a no-op kept for backward compatibility with installed client configs that spawn nauro serve --stdio. There is no --project flag; the server resolves project context from the cwd's .nauro/config.json.
shell · registry
$ nauro projects                 # list registered projects (id, name, mode, repos)
$ nauro projects rm 01J… --yes   # drop a registry entry; store left intact
shell · sync and inspect
$ nauro sync -m "after auth refactor"
$ nauro log --decisions --all
$ nauro status
shell · import external context
$ 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.

shell · render and inspect
$ 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 config is a read-only and cleanup surface; there is no generic set. Subcommands: nauro config get KEY (exits 1 if not set), nauro config list (all top-level config values), and nauro config unset KEY (exits 1 if not set).
  • config list and config get mask only values whose KEY contains "key" (case-insensitive) and is longer than 8 chars, showing first4...last4 via _mask. Non-"key" secrets (for example tokens stored under auth) are not masked by this function, and non-string values are printed as-is.
  • nauro telemetry status|enable|disable|reset are 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)".
shell
$ 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_projects is remote-only.
  • The CLI autogen layer (cli/autogen.py AUTOGEN_ALLOWLIST) exposes exactly 10 tools as commands; list_projects is excluded by name, since local installs auto-resolve to a single project and need no discovery entry point. The allowlist is verified against ALL_TOOLS at import time and fails loudly on a typo.
  • Tool name to CLI command name is snake_case to kebab-case (_command_name replaces _ with -): get_context becomes nauro get-context, propose_decision becomes nauro propose-decision, diff_since_last_session becomes nauro diff-since-last-session, and so on.
  • Schema-to-flag rules (cli/autogen.py): a required string or integer property becomes a positional argument; every other property becomes an option --kebab-name; a boolean becomes --flag/--no-flag; a string with an enum becomes a native Choice (a bad value exits 2); array of string becomes a repeatable --flag; array of object becomes a single JSON-valued --flag (literal, @file, or -). project_id is dropped and replaced by --project NAME; cwd is dropped (implicit). A --json/--no-json flag (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 in nauro/mcp/tools.py (for example tool_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 .value before dispatch.
  • Read tools are annotated readOnlyHint=True, openWorldHint=False, idempotentHint=True; write tools are readOnlyHint=False, destructiveHint=False, openWorldHint=False, idempotentHint=False. Annotations come from _READ_ANNOTATIONS and _WRITE_ANNOTATIONS (every tool is closed-world; writes are additive, so nothing is deleted).
MCP tool CLI command Kind
get_contextnauro get-contextread
get_raw_filenauro get-raw-fileread
list_decisionsnauro list-decisionsread
get_decisionnauro get-decisionread
diff_since_last_sessionnauro diff-since-last-sessionread
search_decisionsnauro search-decisionsread
check_decisionnauro check-decisionread
list_projects(no CLI command)read, remote-only
propose_decisionnauro propose-decisionwrite
flag_questionnauro flag-questionwrite
update_statenauro update-statewrite
python · cli/autogen.py
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-plugin is registered with hidden=True and is a cross-repo CI helper: it materializes the canonical nauro-* subagent bodies into <dir>/agents/ so a separate plugin repo can commit byte-identical copies, and --check byte-verifies the committed copies against the live render (the plugin repo's CI gate). It is not a user-facing feature.
  • The hook sub-app is invoked by agent hook runners, not by a human. nauro hook user-prompt-submit handles Claude Code turns: it resolves the project, runs check_decision, and returns an advisory block when a decision clears the BM25 relevance floor. nauro hook codex-bootstrap handles Codex SessionStart and SubagentStart: 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).