CLI Reference¶
Cortex ships five binaries, registered in agent-server/package.json:
| Binary | Entry point | Purpose |
|---|---|---|
cortex |
dist/entry/cortex-cli.js |
Server management and initialization |
cortex-evidence-export |
dist/entry/production-evidence-export-cli.js |
Production benchmark evidence v2 export |
cortex-hook |
dist/entry/hook-cli.js |
Hook inspection and blocking user asks |
cortex-task |
dist/domain/tasks/system/task-cli.js |
Task system read and mutation |
cortex-signal |
dist/entry/signal-cli.js |
Resolve a waitpoint from an external program |
All five accept --help (or -h) to print their usage. The cortex task
subcommand delegates directly to cortex-task.
cortex¶
Server lifecycle and initialization CLI.
Commands¶
cortex init [--home <path>] [--gateway-config-dir <path>] [--force] [--answers <file>] [--json]
Initialize CORTEX_HOME. The interactive wizard uses bundled PI by default
without a backend-selection prompt, collects platform (Slack / Feishu),
machine, usage-reporting, and service settings, writes .env, copies default
configs, and generates mcp-config.json and mode.json.
After writing configs, it detects local provider credentials and offers
login, rescan, continue with configured providers, or a warned skip. Login
uses the same flow as cortex auth login; choosing Claude Code reuses an
existing installation or asks permission to install a missing copy. Continue
configures gateway routes and profiles from detected endpoints. Detection
makes no paid inference request and does not verify model access.
--answers and --json runs do not start provider login or wait for input.
They report credential status and can finish with provider setup incomplete.
Options:
- --home <path> — set CORTEX_HOME (default: $CORTEX_HOME or ~/.cortex/)
- --gateway-config-dir <path> — gateway config output directory (default: ~/.aistatus/)
- --force — overwrite existing configs (.env, budget.json, mode.json, etc.)
- --answers <file> — read answers from a JSON document without prompting. Omitted fields use defaults; {} creates a single-machine installation using PI, with provider login still required if no credentials are detected. Used by the desktop installer. An explicit backends array retains its selection and installation semantics (claude, pi, or both); an explicit empty array retains the Claude fallback.
- --json — emit newline-delimited progress events on stdout and move human-readable output to stderr. The final event reports the home directory, the version, the client token, and the local Web UI URL.
cortex ui enable [--port <n>] [--home <path>] [--json]
Make this install reachable by the Cortex desktop app. Generates the client
token if it is missing, sets CORTEX_UI_HTTP=1 and CORTEX_UI_PORT, and adds
the app's origins to uiCorsOrigins. Idempotent — running it twice changes
nothing the second time.
Restart the daemon when it reports that the configuration changed: .env is
read once at process start.
Options:
- --port <n> — the port to listen on (default: 3004)
- --home <path> — set CORTEX_HOME
- --json — print the endpoint, token, port, and whether anything changed as JSON
cortex start
Fork dist/entry/app.js as a child process, inherit stdio. This is the
primary way to run Cortex in the foreground. The child process runs the
Slack bot, webhook server, and all agent orchestration.
cortex daemon
Fork dist/entry/daemon.js as a child process, inherit stdio. The daemon
wraps app.js with file watching and auto-restart on crash. Touching
$STORE_DIR/.restart signals the daemon to drain and respawn app.js.
cortex restart
Signal a running daemon to drain and respawn app.js by touching the
.restart trigger file at $STORE_DIR/.restart. If no daemon is running,
this is a no-op beyond creating the file.
cortex task <subcommand> [options]
Delegate to cortex-task. See the cortex-task section below for all
subcommands.
cortex config
Print resolved paths and initialization status. Shows INSTALL_ROOT,
all data directories, and whether .env, mcp-config.json, and
mode.json exist.
cortex doctor [--fix] [--json]
Health-check the whole installation in one pass and report what is wrong. The default run is read-only and safe at any time. It inspects four areas:
- Runtime & process — Node version,
giton PATH, the configured backend binary (claude/pi), and whether the daemon is running. - Backend install / login — data directories exist and are writable,
.envis present, the WebSocket/webhook auth tokens are set,ANTHROPIC_API_KEYstatus, and thatmode.json,profiles.json, andmcp-config.jsonare present and valid. It also smoke-tests the installed PI runtime and itsloginexport, then summarizes expired or logged-out in-use providers from the credential-free backend auth snapshot. These two checks warn rather than fail. In API mode, a healthy gateway with no local key is reported as gateway-backed information instead of a false logged-out warning. - Messaging platform — resolves
CORTEX_PLATFORMand validates the credentials for each enabled platform (Slackxoxb-/signing/xapp-, Feishu app id/secret). - Gateway —
~/.aistatus/gateway.yamlpresence and a health probe ofhttp://127.0.0.1:9880/status. The probe is reported as a failure only when the gateway is actually in use; otherwise it is skipped.
Each check prints [OK], [WARN], [FAIL], or [--] (skipped) with a
short detail and, for problems, a fix hint. The command exits 1 when any
check fails, else 0.
Options:
- --fix — apply safe, idempotent repairs only: create missing data
directories, generate missing auth tokens, and rebuild mcp-config.json.
It never deletes configuration or overwrites existing credentials. After
fixing, diagnostics re-run so the summary reflects the repaired state.
- --json — emit the full report (sections, checks, counts) as JSON for
scripting.
cortex auth status [--json]
Read the normalized Claude Code and PI authentication snapshot. The default
text output shows a concise status overview, while --json returns the full
AuthStatusSnapshot for scripting. Both forms omit credential values and
fragments.
This subcommand is read-only and reports local credential state, not live
inference validation. Sign in locally with cortex auth login. Remote login
starts from !login in Slack or Feishu, or Settings → Accounts in the Web
UI; mobile users drill in at /m/settings/accounts.
Options:
- --json — print the complete credential-free status snapshot as JSON
- --help, -h — show auth command help
See Backends: Remote login for login commands, provider capability rules, and expiration handling.
cortex auth login [--backend pi|claude] [--provider ID] [--auth-type oauth|api_key]
Sign in to a provider from an interactive terminal without a running daemon. Omitted options are selected interactively from the backend's supported providers and authentication methods. PI is bundled. For Claude Code, an existing installation is reused; a missing installation requires confirmation before Cortex installs it and starts login.
Login requires TTY stdin and stderr, even when all selectors are supplied. API keys and authorization responses are entered through secret prompts; this command accepts no key, token, or authorization-code arguments. Browser links and device instructions appear on stderr. Non-TTY login fails promptly without starting an authentication flow; help works without a TTY.
The result is JSON on stdout, with human prompts and errors on stderr; no
--json flag is needed or accepted. Results omit secrets. A successful login
synchronizes gateway/profile configuration and reports the sync result with
inferenceVerified: false: credentials are configured, not inference-tested,
and no paid model request is sent.
Options:
- --backend pi|claude — choose the backend; omitted means select interactively
- --provider ID — choose a provider supported by that backend's runtime
- --auth-type oauth|api_key — choose a supported authentication method
- --help, -h — show login help without starting login
cortex auth login
cortex auth login --backend pi --provider anthropic --auth-type api_key
cortex auth login --backend claude --provider anthropic --auth-type oauth
cortex auth login --help
cortex auth provider <list|add|remove> [options]
Manage custom PI providers — self-hosted or proxied endpoints that have no
login flow of their own. add is an upsert, so re-running it with a changed
URL or model list edits the stored definition in place.
Options:
- --name <name> — provider name; letters, digits, - and _ only
- --api <api> — request protocol: anthropic-messages, openai-completions, openai-responses, or google-generative-ai
- --url <url> — upstream endpoint, stored in the gateway route
- --key <key|-> — upstream API key; - reads it from stdin so it never appears in shell history
- --model <id> — a model id the endpoint serves; repeat for several
- --dry-run — on remove, report what would be deleted without deleting it
- --json — print the result as JSON
- --help, -h — show provider command help
# define a local endpoint that speaks the Anthropic protocol
cortex auth provider add --name my-vllm --api anthropic-messages \
--url http://127.0.0.1:8100 --model Model-27B
# take the key from a secret store instead of the command line
pass show my-endpoint | cortex auth provider add --name my-proxy \
--api openai-completions --url https://proxy.example.com/v1 --model small --key -
cortex auth provider list --json
cortex auth provider remove --name my-vllm --dry-run
See Backends: Custom providers for what the two writes contain and how a definition reaches an agent.
cortex setup-gateway [--dry-run] [--output-dir <path>]
Auto-detect Claude Code and PI configurations from their local config
files, generate ~/.aistatus/gateway.yaml (with backup of existing),
and write $CORTEX_HOME/config/profiles.json. Run this whenever you add
a new API key or change models.
Options:
- --dry-run — print the generated gateway.yaml to stdout without writing
- --output-dir <path> — write gateway.yaml and profiles.json under <path> instead of defaults
Exit codes¶
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Error (invalid command, missing config, runtime failure) |
cortex-evidence-export¶
Publish immutable terminal/composite benchmark evidence v2 from the production stores under
CORTEX_HOME. The command initializes the production attempt identity and journal stores, loads
data/executions.json and data/threads.json, reads attributed costs, task state, and production
topology, then delegates validation and atomic publication to
exportProductionBenchmarkEvidence. Cortex benchmark arms always run through the production
server; this command records that runtime's stores and does not define a second orchestration path.
--input-file contains one JSON-encoded ProductionEvidenceExportInput; use - to read it from
stdin. The launcher owns every field: outputDirectory, project, trialId, rootRunId,
armName, armCanonicalSha256, bundleManifestHash, mode, expectedRoles, managerQa,
proxyExport, and optional evaluatedChecks. Hashes, modes, role sets, proxy trial identity, and
predicate results are validated before publication. Missing or malformed input,
incomplete durable records, or an existing output directory fail closed with exit code 1.
cortex-evidence-export --input-file production-evidence.json
cat production-evidence.json | cortex-evidence-export --input-file -
Success writes one JSON object to stdout:
{"ok":true,"directory":"/evidence/run-1","composite_path":"/evidence/run-1/composite-manifest.json","composite_sha256":"<sha256>","terminal_paths":["/evidence/run-1/execution-root.terminal.json"]}
Failure writes {"ok":false,"error":"..."} to stderr and does not publish a partial directory.
The public output path appears only after all terminal and composite bytes are written, fsynced,
and verified in a hidden staging directory.
cortex-task¶
Read and mutate TASKS.yaml files across projects. For the full task system
lifecycle, format reference, and dispatch model, see tasks.md. Available both as a
standalone binary and via cortex task <command>.
Read commands¶
These commands do not modify any files. They all support --json for
machine-readable output.
list [--project <name>] [--status <status>] [--priority <level>] [--text <filter>] [--has-deps] [--no-deps] [--json]
Show actionable tasks (default). Filters by project, status, priority, text
substring, or dependency presence. Use --all to include completed tasks.
all [options]
Alias for list --all. Shows all tasks including completed ones.
query [--project <name>] [--status <status>] [--priority <level>] [--has-deps] [--no-deps] [--json]
Filter across all tasks (including completed). Same filter options as list
but always scans the full task set.
show --task-id <id> [--json]
Show detailed information for a single task: text, why, done-when, plan, status, dependencies, and dependent tasks.
deps --task-id <id> [--json]
Show the dependency graph for one task: what it depends on and what depends on it.
lint [--project <name>] [--json]
Validate task structure: missing IDs, dangling dependencies, cycles, and invalid template names.
stats [--json]
Print task supply statistics per project: counts by status and priority.
State commands¶
These commands require --project and either --task-id or --task.
claim --project <name> (--task-id <id> | --task <text>) [--agent <name>]
Mark a task as in-progress (claimed). The --agent flag records which agent
claimed it (default: cortex-local).
unclaim --project <name> (--task-id <id> | --task <text>)
Remove the claimed status from a task, returning it to open.
pause --project <name> (--task-id <id> | --task <text>)
Pause a task (typically an in-progress one). Paused tasks are not dispatched.
resume --project <name> (--task-id <id> | --task <text>)
Resume a paused task.
pending --project <name> (--task-id <id> | --task <text>)
Mark a task as pending — something outside this turn (a long run, a build) has to finish before the task can be completed. Pair it with a waitpoint so the session is woken when that happens.
reopen --project <name> (--task-id <id> | --task <text>)
Restore a stuck pending task back to open so the dispatcher can pick it up
again. Use this to rescue a task that stayed pending because whatever it was
waiting on never reported. It is idempotent on an already-open task and refuses a completed task
(use uncomplete for those).
complete --project <name> (--task-id <id> | --task <text>) [--note <text>] [--skip-verify] [--skip-verify-reason <text>]
Mark a task as complete. Requires a --note describing what was done. By
default, Cortex validates that the done-when criteria were met. Use
--skip-verify with --skip-verify-reason to bypass validation.
uncomplete --project <name> (--task-id <id> | --task <text>)
Reverse a completed task, returning it to its previous state.
Approval commands¶
request-approval --project <name> (--task-id <id> | --task <text>)
Mark a task as needing approval.
approve --project <name> (--task-id <id> | --task <text>)
Approve a task that was waiting for approval.
clear-approval --project <name> (--task-id <id> | --task <text>)
Clear the approval status from a task.
Blocking commands¶
block --project <name> (--task-id <id> | --task <text>) --reason <text>
Block a task with a reason. Blocked tasks are not dispatched.
unblock --project <name> (--task-id <id> | --task <text>)
Unblock a previously blocked task.
Acceptance commands¶
verdict --project <name> --task-id <parent id> --child <child id> --verdict accepted|rejected [--note <text>]
Record a manager's acceptance verdict for a delivered child task into the
parent task node's acceptance ledger (DR-0017). Required: --project,
--task-id (the parent/manager task), --child (the child task id), and
--verdict, which must be exactly accepted or rejected (any other value is
an error). --note optionally records why. An accepted child result never
re-delivers to future manager incarnations; a rejected verdict bumps the
child's rework_round and re-opens the child for another verdict when it
completes again after rework. This command writes a per-node ledger and does
not require a project lock. For the acceptance-ledger data model, see
tasks.md.
Mutation commands¶
These commands require a project lock (cortex-task lock-acquire) before
they can run, to prevent concurrent edits to the same TASKS.yaml.
add --project <name> --text <text> [--why <text>] [--done-when <text>] [--plan <path>] [--priority <level>] [--template <name>] [--depends-on <id...>]
Add a new task. Required: --text. Optional: --why (rationale),
--done-when (success criteria), --plan (reference to design doc),
--priority (high/medium/low, default: medium), --template (thread
template name), --depends-on (space-separated hex IDs).
edit --project <name> (--task-id <id> | --task <text>) [--text <text>] [--why <text>] [--done-when <text>] [--plan <path>] [--priority <level>] [--depends-on <id...>] [--add-depends-on <id>] [--remove-depends-on <id>] [--clear-depends-on]
Edit task fields. At least one field must be specified. Dependencies can be
set (replace), appended (--add-depends-on, repeatable), removed
(--remove-depends-on, repeatable), or cleared (--clear-depends-on).
batch-edit --project <name> --task-ids <ids> [fields...]
Apply the same edit to multiple tasks. --task-ids takes a comma-separated
list of hex IDs. Same field options as edit.
decompose --project <name> (--task-id <id> | --task <text>) --subtasks-file <path> [--dry-run]
Replace a task with subtasks defined in a JSON file. Use - for stdin.
--dry-run previews without executing.
Lock commands¶
The project lock system prevents concurrent edits to TASKS.yaml. Each lock has a fixed 20-minute TTL.
lock-acquire --project <name> [--force] [--note <text>] [--json]
Acquire a project lock. Identifies the owner via git config user.email or
$USER. --force steals the lock from another owner.
lock-release --project <name> [--force] [--json]
Release a project lock. Only the lock owner (or --force) can release.
lock-status [--project <name>] [--json]
Show lock status. Without --project, lists all projects.
lock-force-release --project <name> [--json]
Force-release a project lock regardless of owner.
Maintenance commands¶
assign-ids [--project <name>]
Auto-assign 4-character hex IDs to tasks missing one. Requires project lock.
validate
Validate all task IDs across all projects. Checks for duplicate IDs and malformed entries. Does not modify files.
stop --task-id <id> [--dry-run]
Kill a dispatched task process. --task-id can be a dispatch ID (e.g.
dispatch_abc123) or a task hash. --dry-run shows what would be killed
without executing. The kill is forwarded to the remote client via the
daemon webhook.
Common options¶
| Flag | Description |
|---|---|
--project <name> |
Project name (required for most write commands) |
--task-id <id> |
Task hash ID (4-char hex) |
--task <text> |
Lookup by task text (fuzzy alternative to --task-id) |
--base-dir <path> |
Cortex root directory (default: ~/Cortex) |
--json |
Output as JSON (read commands and lock operations) |
--help |
Show command help |
Task lifecycle states¶
open → claimed → done
↓ ↓
paused pending → open (reopen)
↓
blocked → open (unblock)
approval states: request-approval → approve → clear-approval
Both block/unblock and reopen normalize a task's status back to open, so a
task left as pending by work that never reported returns to a dispatchable
state rather than staying invisible to the dispatcher.
Exit codes¶
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Error (invalid arguments, lock held by another, task not found) |
cortex-signal¶
Tell Cortex that something it is waiting for has finished. The agent arms a
waitpoint with the wait_create MCP tool and ends its turn; this command
resolves that waitpoint and wakes the session. See
waitpoints.md for the full picture.
--id and --secret default to $CORTEX_SIGNAL_ID and
$CORTEX_SIGNAL_SECRET, so the usual integration is two lines: export them
once, then append one command.
Options:
- --id <wp_…> — waitpoint id (default: $CORTEX_SIGNAL_ID)
- --secret <hex> — the capability issued with that waitpoint (default: $CORTEX_SIGNAL_SECRET)
- --status <s> — ok, fail, or progress (default: ok). ok/fail are terminal: they count toward the quorum and wake the session. progress records a heartbeat without resolving anything and without waking anyone — the difference between the two is entirely "is this worth a turn"
- --exit-code <n> — derive the status from a command's exit code (0 = ok, anything else = fail) and default the message to exit=<n>
- --message <text> — one-line summary shown in the wake message
- --member <name> — which member of a multi-job waitpoint this signal is for
- --data <@file|-> — extra payload from a file or stdin; it reaches the agent labelled as data, never as instructions
- --url <url> — daemon signal endpoint (default: http://127.0.0.1:$WEBHOOK_PORT/webhook/signal)
- --no-spool — fail instead of spooling to disk when the daemon is unreachable
- --help, -h — show usage
The endpoint is loopback-only and does not accept CORTEX_WEBHOOK_TOKEN:
the per-waitpoint secret is the only credential, and it can do nothing except
resolve its own waitpoint. To signal from another machine, drop a JSON file in
~/.cortex/tmp/signals/ there instead — see
waitpoints.md.
Spooling¶
If the daemon cannot be reached, the signal is written to
$CORTEX_HOME/tmp/signals/<name>.json (.tmp first, then renamed, so a
half-written file is never picked up) and the command still exits 0. The
daemon drains that directory on its next sweep, so a signal sent during a
restart is delayed, not lost. --no-spool turns this off and exits 1 instead.
Exit codes¶
| Code | Meaning |
|---|---|
| 0 | Accepted, spooled, or the waitpoint was already resolved |
| 1 | Refused (bad secret, unknown id, rate-limited) or unreachable with --no-spool |
| 2 | Usage error (missing id/secret, unknown flag, bad --status) |
An already-resolved waitpoint exits 0 on purpose: a retrying script should not treat "someone already reported this" as a failure.
Examples¶
# Report the exit code of the command that just ran
export CORTEX_SIGNAL_ID=wp_1a2b3c4d5e6f CORTEX_SIGNAL_SECRET=…
python train.py; cortex-signal --exit-code $?
# Report one arm of a multi-job waitpoint
cortex-signal --member arm2 --status ok --message "33,120 steps"
# Attach the tail of a log as data
python train.py; s=$?; tail -c 2000 train.log | cortex-signal --exit-code $s --data -
# Heartbeat without waking anyone
cortex-signal --status progress --message "epoch 12/40"
# Signal when a process that is already running exits
(while kill -0 12345 2>/dev/null; do sleep 5; done; cortex-signal --status ok) &