npc-commands — the complete option surface
Every option below is transcribed from the typer.Option and
typer.Argument declarations in
npcagent/plugins/surfaces/cli/main.py. Defaults are the literal defaults in the
source. Nothing here is inferred from help text alone.
Invocation
The Typer application is built with a custom group class. Its only job is to make a bare goal string work as a command:
117 class GoalDefaultGroup(TyperGroup): 118 """Route unknown top-level words to ``run`` for ``npcagent "<goal>"``.""" 119 120 def resolve_command(self, ctx, args): 124 if args and args[0] not in self.commands and not args[0].startswith("-"): 125 args.insert(0, "run") 126 return super().resolve_command(ctx, args)
Consequence: npc "fix the flaky test" and
npc run "fix the flaky test" are the same call. A word that is a
registered command always wins, so a goal beginning with the word "list" must be passed
explicitly through run.
The npc wrapper (cli/npc.py) adds two behaviours before
delegating. With no arguments it computes a deterministic tmux session name —
f"cao-npc-{sha256(repo + provider + model)[:12]}" — attaches to it if it
already exists, and otherwise launches a provider TUI. With the single argument
switch (or /switch) it opens the provider picker and then
replaces the session.
A bare npc also auto-starts the background daemon
(python -m npcagent serve --no-webhook, detached) unless
.npcagent/serve.pid holds a live PID or
NPCAGENT_NO_AUTO_SERVE is set to 1, true or
yes. Webhooks are deliberately excluded from the autostart — nothing binds
a port behind your back.
Goal options
These appear twice: once on the root callback (so they can precede the goal) and once on
run. Values given to run win; unset values fall back to the
root-level value captured in _RootGoalOptions.
- --project slug
- Force the goal into an existing project slug, bypassing the classifier's decision.
Default
None. - --provider, -p id
- Provider for this run. Accepted aliases normalise to canonical ids:
claude/claude-code→claude_code,codex,gemini→gemini_cli,opencode→opencode_cli,kimi→kimi_cli,kiro,q,copilot. DefaultNone(usesdefault_cli_provider, itself defaulting toclaude). - --routine
- Create a recurring Routine instead of a Project. Requires
--schedule; omitting it raisesValueError("--schedule is required with --routine"). DefaultFalse. - --reaction
- Create an event-triggered Reaction instead of a Project. Mutually exclusive with
--routine— passing both exits2. DefaultFalse. - --schedule spec
- Routine schedule. Accepts a five-field cron expression validated by
croniter, or a duration such asevery 1h,30m,2d. Duration parsing is tried first. Units:s/sec/second(s),m/min/minute(s),h/hr/hour(s),d/day(s). - --trigger-type type
- Reaction trigger type. Closed set:
webhook,chat,file_watch,external. Defaultexternal. - --trigger-route route
- Webhook path segment or watched file route. Route paths must match
^[A-Za-z0-9._~-]+(?:/[A-Za-z0-9._~-]+)*$; a route containing..,{,}or a leading slash is rejected. DefaultNone. - --workflow id
- Override the selected workflow. One of
quick-fix,bug-hunt,blueprint-build,full-expedition, or a user/repo workflow JSON. DefaultNone— see the keyword heuristic in planner(7). - --live / --no-live
- Render kernel events as they arrive. Default
True. With--no-livenothing is subscribed andevent_countstays at zero — the run still executes and still persists every event. - --compact / --no-compact
- Switch the live renderer to one-line records
(
phase idle->completerather than the padded form). DefaultFalse; also settable withNPCAGENT_LIVE_COMPACT=1. - --worktree / --no-worktree
- Run agents in a per-run git worktree under
.npcagent-worktrees/instead of the main checkout. DefaultTrue. If worktree creation fails the run aborts with a message telling you to either commit a HEAD or pass--no-worktree— it does not silently fall back to editing your checkout.
Because --worktree defaults to True, the files an agent
writes land in .npcagent-worktrees/<run-id-hex-12>/ on a branch named
npcagent/<workflow>/<first-8-hex>, not in your working tree. What
happens to that worktree afterwards is governed by
worktree_cleanup_mode, whose default is archive — the
directory is renamed with a UTC timestamp suffix and the branch is preserved.
Inspection commands
- npc list [--all | -a] [--limit n]
- Lists runs from the repo-local SQLite database. Without
--all, rows incompleted,cancelledordead_letteredare filtered out in Python after the query.--limitdefaults to50and is clamped to at least 1. Always prints the Needs-You total first. - npc logs RUN_ID [--follow | -f] [--limit n]
- Replays the event log for a run tree via
event_log.replay(root_id=…), ordered by(ts, id).--limitdefaults to200.--followthen polls every 1.0 s, re-querying withsince = last_ts - 1µsand de-duplicating on event id. Ctrl+C prints stopped following logs and exits cleanly. - npc status RUN_ID
- Prints one run row joined to its workflow state: status, primitive, goal, root,
parent, depth, retry_count, workflow_phase, plus
last_errorandescalation_reasonwhen set. - npc approve RUN_ID
- Resumes a run parked in
needs_user. Refuses with exit1if the run is in any other status — the check is explicit, not a transition-table side effect.
All four resolve the run id with UUID(run_id) and exit 2 on a
malformed value. They also require an initialised repo: if .npcagent/ is
missing they print NPCAgent is not initialized. Run npcagent init first. and exit
1.
$ npc list --all --limit 4 Needs-You: 1 Runs ┏━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓ ┃ ID ┃ Status ┃ Type ┃ Goal ┃ Root ┃ ┡━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩ │ 2352067a │ completed │ project │ create a python script named curre… │ 2352067a │ │ 9d14be03 │ running │ project │ add a --json flag to the report co… │ 9d14be03 │ │ c0f7a2b8 │ needs_user │ project │ migrate the billing table │ c0f7a2b8 │ │ 41aa5e6c │ failed │ routine │ nightly dependency audit │ 41aa5e6c │ └──────────┴────────────┴─────────┴──────────────────────────────────────┴──────────┘ $ npc status c0f7a2b8-1e44-4d0a-9a2f-1b7c55d9e310 Run c0f7a2b8-1e44-4d0a-9a2f-1b7c55d9e310 ┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ │ status │ needs_user │ │ primitive │ project │ │ goal │ migrate the billing table │ │ root │ c0f7a2b8-1e44-4d0a-9a2f-1b7c55d9e310 │ │ parent │ │ │ depth │ 0 │ │ retry_count │ 0 │ │ workflow_phase │ executing │ │ escalation_reason│ timeout │ └──────────────────┴───────────────────────────────────────────────────┘ $ npc approve c0f7a2b8-1e44-4d0a-9a2f-1b7c55d9e310 approved run c0f7a2b8-1e44-4d0a-9a2f-1b7c55d9e310
Column sets are exactly those built in list_command and
status_command. Goal text is truncated at 70 characters with a trailing
... by _add_run_row; run ids are shown as the first 8
characters. escalation_reason: timeout is what
DeadlineWatcher writes when a run passes its deadline_at.
Terminal & model
- npc launch [--provider, -p id] [--model, -m id] [--attach / --no-attach]
- Starts a real provider TUI through CAO.
--attachdefaults toTrue; with--no-attachthe command prints thetmux attach -t <session>line and returns. Failure prints launch failed: and exits1. - npc model [ALIAS] [--provider, -p id]
- With no arguments at all, opens the interactive picker. With arguments, validates the
model against the selected provider and writes
~/.npcagent/config.toml.npc /modelis a hidden alias that always opens the picker. - npc auto [on | off | toggle | status]
- Sets or reads the repo auto-approval flag. Accepted synonyms:
on/true/1/yesandoff/false/0/no. A missing argument meanstoggle. Anything else exits2. Creates.npcagent/if absent.
Model aliases are provider-scoped and defined in
npcagent/plugins/clis/model_catalog.py. Only claude_code
exposes short aliases; the other providers take explicit model ids.
| Provider id | Executable | Short aliases | Static ids offered by the picker |
|---|---|---|---|
claude_code | claude | sonnet opus haiku | the three aliases |
codex | codex | — | 6 ids, e.g. gpt-5.5, gpt-5.3-codex |
gemini_cli | gemini | — | 10 ids, e.g. gemini-3.1-pro-preview |
opencode_cli | opencode | — | 10 namespaced ids, e.g. google/gemini-2.5-pro |
kimi_cli | kimi | — | 7 ids under moonshot-ai/ and kimi-code/ |
kiro_cli | kiro-cli | — | none — provider-managed |
q_cli | q | — | none — provider-managed |
copilot_cli | copilot | — | none — provider-managed |
$ npc model Current default — provider claude_code · model sonnet Available providers ┏━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ # ┃ Provider ┃ Installed ┃ Model aliases ┃ ┡━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ │ 1* │ claude_code │ yes │ sonnet*, opus, haiku │ │ 2 │ codex │ yes │ gpt-5.5, gpt-5.4, gpt-5.4-mini, … │ │ 3 │ gemini_cli │ no │ gemini-3.1-pro-preview-customtools, … │ │ 4 │ opencode_cli │ no │ google/gemini-3.1-pro-preview, … │ │ 5 │ kimi_cli │ no │ moonshot-ai/kimi-k2.6, … │ │ 6 │ kiro_cli │ no │ <provider-managed> │ │ 7 │ q_cli │ no │ <provider-managed> │ │ 8 │ copilot_cli │ no │ <provider-managed> │ └────┴──────────────┴───────────┴───────────────────────────────────────┘ Pick provider [1-8 or name, current=claude_code]: 2 Pick model [1-6, name, or explicit id, current=sonnet]: gpt-5.3-codex saved codex · gpt-5.3-codex → /Users/you/.npcagent/config.toml $ npc launch --provider codex --no-attach launched codex cao-npc-1777409265-689083 bec68adc Attach later: tmux attach -t cao-npc-1777409265-689083
The Installed column is
shutil.which(command) is not None. The * markers flag the
current default provider and model. The picker allows two invalid model attempts before
printing aborting: too many invalid attempts. The
launched line format and session id come from the real v0.1.0 acceptance
transcript.
Daemon & web surfaces
- npc serve [--port n] [--webhook/--no-webhook] [--cron/--no-cron] [--file-watch/--no-file-watch] [--log-json]
- Runs the background services: the signal processor, the deadline watcher, the retry
scanner, and the three trigger providers. All three trigger flags default to
True.--portdefaults toNone, which falls back towebhook_port(8765).--log-jsonwrites structured lines to.npcagent/logs/serve.log. HandlesSIGINTandSIGTERMby setting a stop event. - npc dashboard [--host h] [--port n] [--open/--no-open]
- NPCAgent's own web frontend. Defaults
127.0.0.1:8766, browser opened. It sees keystrokes before tmux does, which is why dashboard-level slash commands (/switch,/model,/clear,/kill,/help) can be intercepted rather than forwarded. - npc codeman [--url u] [--start/--no-start] [--session/--no-session] [--name n] [--send text] [--send-delay s] [--open/--no-open]
- Bridge to Codeman, a separate web-terminal project, used as a swappable sidecar
frontend. Defaults: URL
http://localhost:3000, nameNPCAgent, send delay0.75s. It deliberately does not vendor Codeman or couple the runtime to its internals.
The REST API is not a npc subcommand — it is run as a module:
It binds 127.0.0.1 by default and refuses a non-loopback host unless both
NPCAGENT_API_ALLOW_REMOTE is truthy and a bearer token is
configured. See plugins(7) for the route table.
Sub-applications
Four Typer sub-apps, each declared with no_args_is_help=True so bare
npc project prints help rather than erroring.
| Command | Arguments | Behaviour |
|---|---|---|
project list | --all, -a | Table of Name / Goal / Created / Archived / Active. Empty result prints a hint rather than an empty table. |
project switch | NAME | Writes the sticky active-project file. Unknown slug → exit 1. |
project archive | NAME | Sets archived_at; the project stops appearing without --all. |
project rename | OLD NEW | Both ProjectNotFoundError and ValueError exit 1. |
agent list | — | Reads CAO's terminal table directly, filtered to rows whose agent_profile starts with npcagent. If CAO is not importable it prints "No NPCAgent CAO agents found." |
agent promote | ID --as ROLE | Writes .npcagent/agents/<role>/<id>.json atomically. Both id and role pass through validate_project_slug and a path-escape check. |
plugin list | --tools | Builds a full Runtime, refreshes MCP tools, renders the table, then always shuts the runtime down in a finally. |
tool list | — | Every registered ToolSpec; descriptions truncated to 80 characters with a single-character ellipsis. |
--help is fast
The surface module imports only from npcagent.cli at module scope. Every
kernel and runtime import is deferred inside the command function bodies, so
npc --help never opens a database, discovers a plugin, or starts an event
bus. The design note at the top of main.py states this explicitly.
Exit status
- 0
- Success. Note that a run reaching
needs_userorfailedstill exits0—runreports the terminal status in its summary rather than in the exit code. Only an exception escapingexecute_goal()produces a non-zero exit. - 1
- Operation failed: plan rejected by the validator, run not found, run not in
needs_user, project not found, init/launch/serve failure. - 2
- Usage error: empty goal, both
--routineand--reaction, malformed UUID, an unrecognisedautomode, or a forbidden stub environment variable being set.
Examples
A one-shot change, no isolation
Runs in the main checkout. The goal contains none of the routing keywords, so
_select_workflow_id returns quick-fix: a single
execute phase with the developer prompt.
Force a heavier workflow
Bypasses the keyword heuristic. full-expedition is five phases —
research, planning (blocking on spec.md), spec review, execute, code review
— with rejection edges back to planning and executing.
A recurring routine
Creates the Routine and writes a Stage 1 plan artefact, then returns without running
anything. npc serve fires it. The cron provider advances
next_run_at before enqueuing the payload, so a crash mid-fire cannot
produce a duplicate run.
A webhook reaction
Registers POST /webhooks/github/issues on the serve daemon and generates a
32-byte URL-safe secret. The route enforces, in order: a 1 MB body cap (413), an HMAC
check via hmac.compare_digest (401), JSON parsing (400), a token-bucket rate
limit defaulting to 30/min (429), and delivery-id idempotency (200
duplicate). Accepted deliveries return 202.
Offline planning
Swaps Stage1Planner for HeuristicStage1Planner, a fixed
five-phase outline (scope · design · implement · verify · handoff) reported with the model
string heuristic-offline. The same substitution happens automatically when
ANTHROPIC_API_KEY is unset. Execution is unaffected: the work still runs
through a real provider CLI.