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-browser |
dist/entry/browser-cli.js |
Drive the session's agent browser |
cortex-evidence-export |
dist/entry/production-evidence-export-cli.js |
Production benchmark evidence v2 export |
cortex-hook |
dist/entry/hook-cli.js |
Hook inspection, switching, test runs, and blocking user asks |
cortex-signal |
dist/entry/signal-cli.js |
Resolve a waitpoint from an external program |
All five accept --help (or -h) to print their usage.
cortex¶
Server lifecycle and initialization CLI. cortex --help lists every command.
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 the mcp-config*.json files, machines.json,
preferences.json and agent-state.json. It also seeds schedules.json,
creates a git repository in CORTEX_HOME when git is available, and can
enable the local Web UI and install a system service when those answers ask
for it.
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 asks for a default model and favorites from
detected endpoints. The recommended default is preselected and included among
favorites. 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, agent-state.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 selects Claude.
- --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.
Model answers use this shape (the selected pair must be among detected models):
{
"defaultChoice": { "mode": "openai-codex", "model": "gpt-6.1-sol" },
"favorites": [{ "mode": "openai-codex", "model": "gpt-6.1-sol" }]
}
An explicit defaultChoice replaces the saved default; otherwise it is preserved.
Favorites are merged and include the selected default. The positional stdin
protocol ends with two model lines: default mode:model, then comma-separated
favorite mode:model pairs.
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. Its output ends by pointing at cortex ui pair.
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. Without it, a CORTEX_UI_PORT already in .env is kept, and 3004 is used when there is none; an invalid port exits 1
- --home <path> — set CORTEX_HOME (default: $CORTEX_HOME or ~/.cortex)
- --json — print { ok, home, url, token, port, changed }
cortex ui pair [--home <path>] [--json]
Print a one-time pairing code (XXXX-XXXX) for a new app or browser. The code
is valid for 10 minutes and works once; enter it with the server's address in
the desktop or Android app, or on the browser sign-in page. The code is written
to $CORTEX_HOME/data/ui-pairing.json, which the running server reads, so no
restart is needed. Signed-in devices can create the same codes under
Settings → Machines → Connected apps (see
Browser access).
Options:
- --home <path> — set CORTEX_HOME (default: $CORTEX_HOME or ~/.cortex)
- --json — print { ok, code, expiresAt }
cortex client remove --device <name> [--keep-remote] [--dry-run] [--home <path>]
Retire a remote machine. The command deletes the machine's entry from
config/machines.json, connects to the machine over SSH, stops every process
running its client bundle, and deletes the bundle (client/),
config/cortex-client.json, and the logs/client-*.log files from the
client's CORTEX_HOME. It then drops the machine from data/client-pids.json
and data/client-routes.json. The registry entry goes first, so a running
server releases the machine's tunnel and restart timer on hot-reload instead of
relaunching the client that is being stopped.
The client's CORTEX_HOME comes from the bundle path in clientCommand and
defaults to ~/.cortex (%USERPROFILE%\.cortex on Windows). A home that also
contains config/machines.json belongs to a Cortex server; there the client
process is stopped and every file stays in place. A machine that cannot be
reached is still removed locally, with remote.status: "unreachable" in the
result and a warning on stderr. The server's local machine (an entry without
ssh) is refused.
Output is JSON with the device, removed_at, the removed machine_entry,
the runtime pid and route that were dropped, and remote — cleaned (with
the killed pids and files: removed | kept-server-home), unreachable, or
skipped. --dry-run prints the plan under would_remove and changes nothing.
Options:
- --device <name> — machine name in machines.json (required)
- --keep-remote — only unregister locally; leave the remote process and files alone
- --dry-run — print what would be removed and change nothing
- --home <path> — set CORTEX_HOME
cortex path <install|remove|status> [--home <path>] [--dry-run] [--json]
Make this install's commands available in new terminals. install writes a
launcher for each binary above into $CORTEX_HOME/runtime/bin. The launcher
runs the Node.js that executed the command against this install's entry file,
so the terminal does not need Node.js on PATH. It then registers that
directory on PATH. On Linux and macOS it uses a block marked
# >>> cortex >>>. The block goes at the end of ~/.profile, and of
~/.bash_profile and ~/.zshenv (under $ZDOTDIR when set) where those
shells are in use. It goes at the top of ~/.bashrc, so that non-interactive
SSH commands see it. On Windows the directory goes at the front of the user
Path, which keeps its registry type and announces the change to new
processes. Running install again rewrites only what differs. New terminals
use this install even when another cortex is on PATH; the result lists
those under shadowed. fish is not configured.
At startup the server re-points existing launchers when its Node.js or entry
path has changed. A server that the desktop app installed in
$CORTEX_HOME/runtime/prefix also runs install itself when no launchers
exist, unless remove was the last choice. install and remove record that
choice in $CORTEX_HOME/runtime/terminal-cli.json. remove deletes only the files this
command wrote and takes the directory back off PATH, which leaves the
profiles and the user Path as they were before install. status reports
whether the launchers exist, which are out of date, and where the directory is
registered.
Options:
- --home <path> — set CORTEX_HOME, which also becomes the launchers' default CORTEX_HOME (default: $CORTEX_HOME or ~/.cortex)
- --dry-run — for install and remove, report the changes and write nothing
- --json — print { ok, action, dry_run, dir, … }: written and shadowed for install, removed for remove, and path_targets with each profile file or HKCU\Environment\Path and whether it changed
cortex start
Run the server in the foreground: fork dist/entry/app.js with inherited
stdio and $CORTEX_HOME as its working directory, and exit with its exit
code. The process runs the platform adapters, the Web UI host, the webhook
server, and all agent orchestration, with no supervisor to restart it.
cortex daemon
Start the supervisor in the background and return at once. The command forks
dist/entry/daemon.js detached from the terminal, with no stdio (the daemon
writes its own logs under logs/), and exits 0. The daemon runs app.js as
its child, restarts it after a crash with exponential backoff, and drains and
respawns it when $STORE_DIR/.restart is touched. cortex daemon --version
prints the version.
cortex daemon stop
Send the daemon SIGTERM and wait up to 10 seconds for it to exit; a daemon
still alive after that is killed with SIGKILL and the command exits 1. The
daemon is stopped together with the client it launched on this machine (the
local client), so no Cortex process keeps running or keeps files of the
runtime in use; a local client that survives also exits 1. Clients on SSH and
custom-command machines keep running, and the next cortex daemon launches the
local client again. A daemon that is not running, including a stale PID file,
which is cleaned up, exits 0.
cortex daemon status
Print the daemon's PID and uptime and the state of its app.js child (PID and
uptime, starting..., or none).
cortex daemon restart [--hard | --force]
Without a flag, touch the restart trigger so the daemon drains and respawns
app.js. --hard sends SIGTERM straight to app.js and --force sends
SIGKILL; either way the daemon starts a new child. All three exit 1 when no
daemon is running; the flags also exit 1 when no app.js child is alive.
cortex daemon restart-self
Stop the daemon as cortex daemon stop does, then start a new detached daemon.
cortex restart
Touch the restart trigger at $STORE_DIR/.restart and print its path. A
running daemon drains and respawns app.js; with no daemon running the command
only creates the file. It exits 1 when the file cannot be written.
cortex tui [--resume] [--project <id>] [--port <n>]
Open the terminal chat client against a running server. The command checks
that the TUI gateway is listening on 127.0.0.1:<port> and exits 1 with a hint
to start the daemon when it is not; otherwise it runs the client in the
terminal and exits with its exit code.
Options:
- --resume — open the resume-session picker on connect
- --project <id> — start a fresh session in that project
- --port <n> — the gateway port (default: $CORTEX_TUI_PORT, else 3003)
cortex install latest
Update the server from npm. The command asks npm for the latest
@cortex-agent/server version, prints Already on the latest version when the
running install is current, and otherwise runs npm install -g for that version
into CORTEX_NPM_PREFIX, else the running install's own prefix. Restart the daemon
afterwards with cortex daemon restart. A failed lookup or install exits 1.
cortex config
Print resolved paths and initialization status. Shows INSTALL_ROOT,
all data directories, and whether .env, mcp-config.json, and
agent-state.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 (a failure on Windows, a warning elsewhere), theclaudebinary on PATH (a warning when missing; PI always passes, since it runs in-process from the bundled SDK), and whether the daemon is running. - Backend install / login — data directories exist and are writable,
.envis present, the client and webhook tokens are set,ANTHROPIC_API_KEYstatus, and thatagent-state.json,models.json, andmcp-config.jsonare present and valid. A missingmodels.jsonis a warning; invalid JSON, an invalid default selection, or a missing or invalid favorites array is a failure. 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 — the configured gateway YAML's presence and a health probe of
http://127.0.0.1:<CORTEX_GATEWAY_PORT>/status(default port9880). Its directory followsCORTEX_GATEWAY_HOME, defaulting to~/.aistatus/. 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; warnings do not fail the run.
Options:
- --fix — apply safe, idempotent repairs only: create missing data
directories, create a missing .env, generate missing auth tokens, and
rebuild mcp-config.json. The JSON report lists them under fixes.
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/model 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 the gateway YAML (with backup of existing),
and write $CORTEX_HOME/config/models.json. Run this whenever you add
a new API key or change models. The default output is
~/.aistatus/gateway.yaml; CORTEX_GATEWAY_HOME selects
<CORTEX_GATEWAY_HOME>/.aistatus/gateway.yaml, and CORTEX_GATEWAY_PORT
sets its port. Both settings may be saved in $CORTEX_HOME/config/.env; see
Local model gateway for multi-instance setup.
Options:
- --dry-run — print the generated gateway.yaml to stdout without writing
- --output-dir <path> — write gateway.yaml and models.json under <path> instead of defaults; this exports files and does not change the running gateway location
Setup preserves an existing default selection and merges favorites. Its summary
shows the default model and favorites; use Settings → Models to change the
default. Provider-login sync also preserves existing choices, supplies a missing
default, and adds a recommended favorite for each newly connected backend/provider
group without one. Its result includes defaultSet and favorites.
cortex schedule <list|get|add|edit|pause|resume|remove> [<id>] [options]
Manage scheduled tasks — the same records !schedule and the Web UI manage.
Each fire opens a new session in the schedule's project
and sends it the message; see Scheduling. Changes are written
to schedules.json, and a running daemon picks them up within a second.
Agents use this CLI through the schedule skill. For a session-project request,
resolve the project from session context or cortex_context and pass --project
explicitly; if the requested current project is missing, ask which one to use.
The CLI does not infer session scope from Bash environment variables. Daily/weekly
times use the daemon's local timezone (including TZ overrides); convert the
user's timezone and weekday explicitly and report the next run in their timezone.
There is no per-schedule timezone or automatic cross-timezone daylight-saving adjustment.
Every command prints JSON with ok: true and the schedule (schedule, or
schedules and count for list); timestamps are ISO 8601. Invalid input
exits 1 with the valid values on stderr.
list [--project <id>]— all schedules, or one project'sget <id>— one schedule; an unknown id exits 1add --type <type> …— create a schedule; printscreated: falseand the existing schedule when--idempotency-keymatches one. A timing flag that does not belong to the type is rejectededit <id> …— change the message, project, model selection, pre-check, or the timing fields of the schedule's type; the type itself is fixedpause <id>/resume <id>— pause or resume a recurring schedule; repeating either changes nothingremove <id> [--dry-run]— delete a schedule (removed: falsewhen it does not exist);--dry-runprintswould_removeand deletes nothing
Options:
- --type interval|daily|weekly|once — schedule type (add only)
- --interval <duration> — interval: repeat period, e.g. 30m, 2h, 1d
- --time <HH:MM> — daily/weekly: 24-hour time, server local time
- --day <day> — weekly: sun..sat or 0-6
- --delay <duration> — once: fire this long from now; on edit, moves the fire time
- --message <text|-> — the prompt for the fired session; - reads it from stdin
- --project <id> — project the fired session belongs to (default: general)
- --model <[provider/]model> — model to run; PI accepts an explicit provider/model. Creation without model flags stores the configured default selection, which may differ from the active session
- --thinking <level> — backend-native thinking level. With no model flag, applies to a copy of the configured default on creation or the schedule’s stored selection on edit; omitted on a model change, uses that model’s remembered level
- --pre-check <cmd> — shell gate run before each fire; a non-zero exit skips that fire
- --idempotency-key <key> — add: a retry with the same key returns the first schedule
- --help, -h — show schedule help
cortex schedule add --type interval --interval 30m --project nimbus \
--message "Check GPU usage on lab2 and report a one-line summary"
cortex schedule add --type weekly --day mon --time 21:00 --message "Weekly review"
echo "Check training run r42 on lab2" | cortex schedule add --type once --delay 2h --message -
cortex schedule list --project nimbus
cortex schedule edit a1b2c3d4 --time 10:30
cortex schedule remove a1b2c3d4 --dry-run
cortex memory <regen|consolidate> [--project <name>]... [--text]
Maintain project memory under $CORTEX_HOME/context/projects/*/ (its
experiments/, knowledge/ and patterns/ directories; see
Memory). --project limits the run to one project and can be
repeated; without it every project with memory directories is processed, and an
unknown project exits 1 with the valid names.
regen— rebuild the generatedindex.mdfiles from entry frontmatter and the access log, never touching theEXP/K/PATentries themselves. Prints{ ok: true, projects: [{ project, indexes }] }.consolidate— report contradictions, stale entries, anti-pattern signals and consolidation candidates as{ ok: true, reports };--textprints a readable summary instead.
cortex feishu <login|status|logout> [--scope <scopes>] [--manual] [--redirect-uri <url>]
Manage the Feishu user identity that Feishu document tools act as.
login— authorize a Feishu account with the OAuth device flow: the command prints a URL and a code to open in any browser. It needsFEISHU_APP_IDandFEISHU_APP_SECRET. On success it stores the user token and writesFEISHU_AUTH_MODE=userto.env; restart the daemon to apply it.status— show the auth mode (userorbot) and the stored token's validity and expiry.logout— delete the stored user token.
Options:
- --scope <scopes> — the scopes to request (default: $FEISHU_USER_SCOPE, else a document, sheet, base and wiki set that needs no app review; offline_access is always added)
- --manual — use the authorize-URL and paste-the-code flow instead of the device flow
- --redirect-uri <url> — redirect URI for --manual (default: $FEISHU_REDIRECT_URI)
Exit codes¶
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Error (invalid command, missing config, runtime failure) |
| 130 | init or auth login cancelled, or the Claude Code install declined |
cortex start and cortex tui exit with the code of the process they run.
cortex-browser¶
Drive the calling session's tab in the agent browser. Agents run it from
their shell; every argument except --device is passed to the pinned
agent-browser, attached to the session's own tab. Its output and exit
code are agent-browser's. cortex-browser skills get core prints agent-browser's command guide for
the shipped version, and --full adds every command and option.
Options:
- --device <name> — whose Chrome: server or a connected device. The choice sticks for the session (default: the session's last device, else server)
- --help, -h — show usage; cortex-browser <command> --help shows one agent-browser command
Each call asks the daemon for the session's browser over the loopback webhook
(http://127.0.0.1:$WEBHOOK_PORT/webhook/browser, authenticated with CORTEX_WEBHOOK_TOKEN). The
session comes from CORTEX_SESSION_ID (or CORTEX_EXECUTION_ID). Agent shells carry all three,
so the command needs no setup there. skills, help and --version run without a browser.
Relative paths given to file commands (screenshot, pdf, download, upload, record,
trace, har, state, …) and to --screenshot-dir, --download-path and --baseline resolve
against the caller's working directory.
cortex-browser sets --cdp, --session and the tab binding itself, and refuses flags that would
leave the session's tab: --cdp, --session, --no-pin-tab, --auto-connect, --provider/-p,
--namespace, --engine and --executable-path. It also refuses connect, install,
upgrade, dashboard, mcp and close --all, each with the reason and what to use instead.
When the session's tab is gone (closed after 10 idle minutes, or Chrome restarted), the call opens a new blank tab, prints a note on stderr, and runs the command there.
Exit codes¶
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | The agent-browser command failed, or no browser could be used: unknown or offline device, Chrome failed to start, the user is controlling the browser, the daemon is unreachable or rejected the token. The reason and a hint are on stderr |
| 2 | Usage error (a refused flag or command, --device without a name) or no session in the environment |
Once the browser is attached, the exit code is agent-browser's own.
Examples¶
# Open a page and list what can be clicked
cortex-browser open http://127.0.0.1:5173 && cortex-browser snapshot -i
# Click a ref from the snapshot, then capture the result
cortex-browser click @e2 && cortex-browser screenshot ./after-click.png
# Use the Chrome on the user's machine (its localhost, its logins)
cortex-browser --device my-pc open https://example.com
# Read the version-matched command reference
cortex-browser skills get core --full
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, reads attributed costs, then delegates validation and atomic publication to
exportProductionBenchmarkEvidence. Cortex benchmark arms always run through the production
server, as one direct session per trial; this command records that runtime's stores and does not
define a second orchestration path.
Evidence is keyed by session and execution: every run the trial's session opened is one attempt,
identified by its session_id and execution_id, and each later run of the session is a spawn
child of the run before it.
--input-file contains one JSON-encoded ProductionEvidenceExportInput; use - to read it from
stdin. The arguments must be exactly --input-file <value>; anything else is a usage error. The launcher owns every field: outputDirectory, trialId, rootRunId, armName,
armCanonicalSha256, bundleManifestHash, proxyExport, and optional evaluatedChecks. Hashes,
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-hook¶
Inspect and operate the hooks mounted from the built-in registry and
$CORTEX_HOME/config/hooks/. Every command prints JSON on stdout and errors on
stderr. Hooks: the cortex-hook CLI covers the
registry these commands act on.
list— every mounted hook:id,event,enabled,source(builtinoruser), and whether a user file overrides a built-in oneshow --id <id>— one complete declaration; an unknown id exits 1 and lists the valid onesenable --id <id> [--dry-run]/disable --id <id> [--dry-run]— switch a hook, idempotently; the result says whether anythingchanged, and--dry-runreportswould_setwithout writingtest --id <id> --payload <file|->— run the hook once with the payload on stdin and print itsexit_code,stdoutandstderr; the command exits with the hook's own exit codeask (--question <text> | --payload <file|->)— post an ask-user card and block until it is answered, then print{ ok: true, answers }.--header,--options "a|b|c",--multiand--level info|warn|warning|errorshape a single question;--payloadtakes a JSON array of question objects. The card goes to--channel(default:$CORTEX_HOOK_CHANNEL) or the channel of--session-id(default:$CORTEX_HOOK_SESSION_ID), both of which a hook's environment already carries.--dry-runresolves synthetically without posting. See Asking the user from a hook
Exit codes¶
| Code | Meaning |
|---|---|
| 0 | Success, or an answered ask |
| 1 | Usage or runtime error |
| 2 | ask timed out unanswered |
test exits with the code of the hook it ran.
cortex-signal¶
Tell Cortex that something it is waiting for has finished. Run without
arguments, it prints its usage. 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|-|text> — extra payload from a file, stdin, or the literal text; 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, missing value, bad --status or --exit-code, unreadable --data file) |
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) &