NPC-COMMANDS(1) NPCAgent Manual NPC-COMMANDS(1)

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:

npcagent/plugins/surfaces/cli/main.pylines 117–127
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.

Side effect

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-codeclaude_code, codex, geminigemini_cli, opencodeopencode_cli, kimikimi_cli, kiro, q, copilot. Default None (uses default_cli_provider, itself defaulting to claude).
--routine
Create a recurring Routine instead of a Project. Requires --schedule; omitting it raises ValueError("--schedule is required with --routine"). Default False.
--reaction
Create an event-triggered Reaction instead of a Project. Mutually exclusive with --routine — passing both exits 2. Default False.
--schedule spec
Routine schedule. Accepts a five-field cron expression validated by croniter, or a duration such as every 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. Default external.
--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. Default None.
--workflow id
Override the selected workflow. One of quick-fix, bug-hunt, blueprint-build, full-expedition, or a user/repo workflow JSON. Default None — see the keyword heuristic in planner(7).
--live / --no-live
Render kernel events as they arrive. Default True. With --no-live nothing is subscribed and event_count stays at zero — the run still executes and still persists every event.
--compact / --no-compact
Switch the live renderer to one-line records (phase idle->complete rather than the padded form). Default False; also settable with NPCAGENT_LIVE_COMPACT=1.
--worktree / --no-worktree
Run agents in a per-run git worktree under .npcagent-worktrees/ instead of the main checkout. Default True. 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.
Isolation is the default

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 in completed, cancelled or dead_lettered are filtered out in Python after the query. --limit defaults to 50 and 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). --limit defaults to 200. --follow then polls every 1.0 s, re-querying with since = last_ts - 1µs and 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_error and escalation_reason when set.
npc approve RUN_ID
Resumes a run parked in needs_user. Refuses with exit 1 if 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.

~/acme-api — npc list rich tables
$ 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. --attach defaults to True; with --no-attach the command prints the tmux attach -t <session> line and returns. Failure prints launch failed: and exits 1.
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 /model is 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/yes and off/false/0/no. A missing argument means toggle. Anything else exits 2. 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.

Static entries in _STATIC_MODEL_CHOICES. Codex, OpenCode and Kimi can additionally discover ids at runtime, but only the dashboard opts in.
Provider idExecutableShort aliasesStatic ids offered by the picker
claude_codeclaudesonnet opus haikuthe three aliases
codexcodex6 ids, e.g. gpt-5.5, gpt-5.3-codex
gemini_cligemini10 ids, e.g. gemini-3.1-pro-preview
opencode_cliopencode10 namespaced ids, e.g. google/gemini-2.5-pro
kimi_clikimi7 ids under moonshot-ai/ and kimi-code/
kiro_clikiro-clinone — provider-managed
q_cliqnone — provider-managed
copilot_clicopilotnone — provider-managed
~/acme-api — npc model interactive picker
$ 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. --port defaults to None, which falls back to webhook_port (8765). --log-json writes structured lines to .npcagent/logs/serve.log. Handles SIGINT and SIGTERM by 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, name NPCAgent, send delay 0.75 s. 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:

python -m npcagent.plugins.surfaces.api --repo-root . --port 8787 python -m npcagent.plugins.surfaces.acp # ACP stdio JSON-RPC, no flags

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.

Sub-commands, their arguments and their failure modes.
CommandArgumentsBehaviour
project list--all, -aTable of Name / Goal / Created / Archived / Active. Empty result prints a hint rather than an empty table.
project switchNAMEWrites the sticky active-project file. Unknown slug → exit 1.
project archiveNAMESets archived_at; the project stops appearing without --all.
project renameOLD NEWBoth ProjectNotFoundError and ValueError exit 1.
agent listReads 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 promoteID --as ROLEWrites .npcagent/agents/<role>/<id>.json atomically. Both id and role pass through validate_project_slug and a path-escape check.
plugin list--toolsBuilds a full Runtime, refreshes MCP tools, renders the table, then always shuts the runtime down in a finally.
tool listEvery registered ToolSpec; descriptions truncated to 80 characters with a single-character ellipsis.
Why --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_user or failed still exits 0run reports the terminal status in its summary rather than in the exit code. Only an exception escaping execute_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 --routine and --reaction, malformed UUID, an unrecognised auto mode, or a forbidden stub environment variable being set.

Examples

A one-shot change, no isolation

npc "add a --json flag to the report command" --no-worktree --compact

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

npc "tighten the retry policy" --workflow full-expedition --provider codex

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

npc "audit dependencies for new CVEs" --routine --schedule "0 9 * * 1"

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

npc "triage the incoming issue" --reaction --trigger-type webhook \ --trigger-route github/issues

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

NPCAGENT_FORCE_OFFLINE_PLANNER=1 npc "rename the config module"

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.

See also

npcagent 0.1.0 Apache-2.0 NPC-COMMANDS(1)