The CLI
keelson is the operator CLI. When the server is up, commands route to it over
HTTP and WebSocket; when it is down, chat and workflow run fall back to
in-process execution, and the server-backed commands report exit code 3. This
page is the command tree, the flags, and the two contracts a script depends on:
the --json envelope and the exit codes.
Global flags
Section titled “Global flags”| Flag | Effect |
|---|---|
--json | Emit a machine-readable JSON envelope to stdout. Every entry point, including help and errors, produces a parseable payload. |
-p, --prompt <message> | Sugar for keelson chat <message>; with no message on a TTY it opens interactive chat. Must come before any subcommand. |
-v, --version | Print the CLI, Bun, and contract schema versions and exit. |
-h, --help | Print help. Works at every level (keelson rib --help). |
Exit codes
Section titled “Exit codes”The codes are a stable contract: values never get renumbered, so a script can branch on them.
| Code | Name | Meaning |
|---|---|---|
0 | OK | Success. |
1 | FAIL | The command ran and failed. |
2 | BAD_ARGS | Bad arguments: unknown command or option, missing operand, excess operand, an empty option value. |
3 | NO_SERVER | A server-required command was run while the server is down. |
4 | NOT_FOUND | The named target (a workflow, a rib) does not exist. |
The JSON envelope
Section titled “The JSON envelope”In --json mode, success is { "data": ... } and failure is
{ "error": "...", "code": "..." }. The human-readable output is suppressed, so
the envelope is the only thing on stdout, which is what makes the CLI scriptable
end to end.
keelson --json workflow list | jq '.data.workflows[].name'Server lifecycle
Section titled “Server lifecycle”| Command | Does |
|---|---|
keelson start | Start the server in the background and report its URL. --foreground (-f) runs it attached instead; --db <path> overrides the database. |
keelson stop | Stop the background server (graceful shutdown, kill fallback). |
keelson restart | Stop the background server (if running) and start it again. --db <path> overrides the database. |
keelson status | Report whether the server is running and its URL. Exit 0 up, 3 down. |
The former keelson service group (service start / stop / status, alias
serve) still works as a hidden, deprecated alias. Prefer the top-level verbs.
MCP bridge
Section titled “MCP bridge”| Command | Does |
|---|---|
keelson mcp [--base-url <url>] | Bridge a stdio-only MCP client to the running server’s HTTP MCP endpoint (/api/mcp). Reads JSON-RPC on stdin, forwards it to the server, writes replies to stdout. Exits 3 when the server is down. |
This is for MCP clients that speak stdio only; a client that speaks HTTP points
straight at http://127.0.0.1:7878/api/mcp and skips the bridge. See
Using keelson over MCP.
Connect an agent
Section titled “Connect an agent”| Command | Does |
|---|---|
keelson connect <agent...> | Wire an external agent (claude, copilot, codex, or all) to the MCP endpoint and drop a portable skill into that agent’s skills directory. Machine-global by default; --local writes repo-scoped files (a project .mcp.json for Claude and the skill under the current directory) instead. --no-skill wires the connection only; --url <url> writes a non-default endpoint; --undo reverses; --list shows current connections. |
keelson disconnect <agent...> | Reverse a previous connect for the named agents, removing only Keelson’s own entry and any file the connect created. |
keelson connect records what it wrote under the keelson home, so a disconnect is
exact: it never touches a sibling MCP server or a config file you already had. See
Using keelson over MCP.
Workflows
Section titled “Workflows”| Command | Does |
|---|---|
keelson workflow list [--dir <path>] | List workflows in the home catalog, or in an explicit directory such as a project’s .keelson/workflows. |
keelson workflow validate [name] [--dir <path>] [--live] | Validate one workflow or all of them. --live also checks pinned models and effort against provider catalogs. |
keelson workflow run <name> | Run a workflow. Server-up over HTTP, server-down in-process. |
keelson workflow respond <runId> <nodeId> <text> | Resume a paused run by replying to its approval node. Server-required. |
keelson workflow resume <runId> | Resume an interrupted (failed or cancelled) run from the last completed node. Server-required. |
keelson workflow status [runId] | Show recent runs, or one run’s status. Server-required. |
workflow run options:
| Option | Effect |
|---|---|
--arguments <text> | Primary free-form workflow arguments input. Arrives as inputs.ARGUMENTS ($KEELSON_ARGUMENTS in workflow bodies). |
--inputs <k=v> | Named workflow input; repeat to set several. Legacy ARGUMENTS fallback is --inputs ARGUMENTS=.... |
--watch / --no-watch | Stream node events, or emit one envelope at completion. Watch is the default on a TTY. |
--provider <id> | Provider for in-process runs (default stub). |
--project <name> | Run against a named project; the server resolves its root path. |
--working-dir <path> | Override the working directory directly. |
--worktree / --no-worktree | Force or forbid a git-worktree isolated run, overriding the workflow default. |
--no-preflight | Skip the live model and effort check at run start. |
--base-url <url> | An explicit server URL, skipping the probe. |
Use --arguments for free-form text and reserve --inputs for named key-value inputs.
workflow respond takes --pause-id <id>, the per-pause token from the
approval_awaiting frame, to disambiguate retries against interactive loops.
workflow status takes --workflow <name> to filter to one workflow’s runs.
| Command | Does |
|---|---|
keelson eval run <file> | Run every case in a case-set file against its workflow, grade the outputs, and write a results JSON plus Markdown summary under <home>/evals/<name>/. Server-up over HTTP, server-down in-process. |
keelson eval compare <before.json> <after.json> | Per-split pass-rate delta with an improved / regressed / within-noise verdict from a paired per-case test, the cost delta, and the keep/revert decision. |
keelson eval init <workflow> [--out <file>] | Scaffold a case-set file for a workflow. Refuses to overwrite. |
eval run options:
| Option | Effect |
|---|---|
--reps <n> | Repetitions per case, overriding the file’s reps. |
--split train|test|all | Run one split only. Default all. |
--out <path> | Results JSON path; the summary and outputs directory land beside it. |
--watch / --no-watch | Stream node events per case, or print only per-case verdicts. Watch is the default on a TTY. |
--provider <id> | Provider for prompt nodes, as on workflow run. |
--no-preflight | Skip the live model and effort check at run start. |
--base-url <url> | An explicit server URL, skipping the probe. |
eval run exits 1 when any case errored (a run that produced no gradable
output, as opposed to a graded fail), 2 on a bad case file, 3 when the file
names a project and the server is down, and 4 when the workflow is not
found. See Evaluating workflows for the
file format and graders.
Approvals
Section titled “Approvals”| Command | Does |
|---|---|
keelson approval list | List the policy ASK approvals currently waiting for a decision. Server-required. |
keelson approval resolve <id> accept | Resolve one pending approval; pass reject to deny it. Server-required. |
These resolve the policy engine’s ask gates (for
example the ask_on_shell builtin), distinct from a workflow approval node,
which is resolved with workflow respond. An unanswered approval auto-rejects
after five minutes. The id comes from approval list or the in-app prompt.
Providers
Section titled “Providers”| Command | Does |
|---|---|
keelson provider list | List the built-in providers with their install state (bundled, installed, not installed) and whether each is enabled. |
keelson provider add <id> | Install a provider’s vendor SDK into the home and enable it. claude, codex, or pi — Copilot ships with the harness. |
keelson provider remove <id> | Uninstall a provider’s SDK and disable it in config.json. While KEELSON_PROVIDERS still lists the id it stays enabled — that variable is an exact override — and the command reports it instead of claiming otherwise. |
The SDKs are large and vendor-specific, so only Copilot ships with keelson. A
provider enabled in config.json whose SDK is not installed is left
unregistered rather than offered and broken; keelson doctor reports it. See
Providers.
| Command | Does |
|---|---|
keelson rib list | List discovered ribs with their tools, surfaces, and auth. Server-required, unless --installed. |
keelson rib add <source> | Install a rib from any bun-installable source: a GitHub URL, github:owner/repo, a git URL, an npm name, or a local path. Pins to the source’s newest release; --ref <ref> installs a branch or commit instead. |
keelson rib update [ids...] | Advance ribs to their newest release, or all of them when no id is given. --check reports without applying, --to <version> installs an exact version, --pre considers prerelease tags. |
keelson rib remove <id> | Uninstall a rib from the home. |
keelson rib show <id> | Show one rib’s tools, views, surfaces, and auth. Server-required. |
rib list --installed reads the home directly, so it works whether or not the
server is running, and reports each installed rib’s package version and the ref
it is pinned to. Most other rib commands talk to the running server and report
3 when it is down.
A git-sourced rib is held at an explicit release tag, so nothing moves it until
you ask. rib update resolves each rib’s tags over git ls-remote (no forge
API, no rate limit, any host), rewrites the pin, and reinstalls. A rib whose
repository could not be read is reported by name and exits non-zero rather than
counting as up to date.
Projects
Section titled “Projects”| Command | Does |
|---|---|
keelson project list | List registered projects. Server-required. |
keelson project add <name> <rootPath> | Create or register a project pointing at a local directory. Server-required. |
keelson project remove <nameOrId> | Remove a project by name or id. Server-required. |
project add initializes missing or empty targets with Git and one empty
Initialize project commit using your configured Git identity. Existing
repositories and populated non-Git folders are registered untouched. A non-Git
project cannot host Write agents until it is a Git repository with a commit.
See Creating or registering a project
for initialization failures, root conflicts, and worktree readiness.
Workspaces
Section titled “Workspaces”| Command | Does |
|---|---|
keelson workspace list | List the active workspace leases. Server-required; --base-url <url> points at an explicit server URL. |
Gateways
Section titled “Gateways”| Command | Does |
|---|---|
keelson gateway list | List configured OpenAI-compatible gateways and whether each has a stored key. Server-required. |
keelson gateway add <name> <url> | Register a gateway as a provider. --model <id> sets its default model; --key <key> (or KEELSON_GATEWAY_KEY) stores the key in the keychain; --protocol <p> defaults to openai. |
keelson gateway remove <name> | Remove a gateway and delete its stored key. Server-required. |
A gateway registers as a provider named for it, so it appears in the model picker like a built-in. See Gateways.
keelson chat <message> # one turnkeelson chat # no message on a TTY: interactive chat (server-required)keelson chat <message> runs one turn: server-up it goes over HTTP and shows up
in the SPA; server-down it runs in-process and prints to stdout. A bare keelson chat on a TTY opens the interactive chat instead, which needs the server up and
ignores --json and --project (switch projects with the in-chat /project
command). The options below apply to the one-shot form.
| Option | Effect |
|---|---|
--provider <id> | Provider id. With the server up, defaults to the server’s provider; with the server down, the in-process fallback runs the offline stub. |
--model <id> | Model id passed to the provider. |
--project <name> | Bind the new conversation to a named project. Server-required. |
--conversation <id> | Continue an existing conversation (server-up only). |
--thinking | Enable Claude extended thinking for the turn. |
--reasoning-effort <level> | Copilot reasoning tier. |
--base-url <url> | An explicit server URL. |
Maintenance
Section titled “Maintenance”| Command | Does |
|---|---|
keelson doctor | Probe the toolchain, server, database, auth, workflows, and ribs, including whether the cross-rib grants the running server holds still resolve. --strict exits non-zero on warnings too. |
keelson update | Update the harness and its ribs in place, each to its newest release. --check reports the available harness version and applies nothing (use rib update --check for the ribs); --force re-applies; --no-ribs and --no-notes narrow it. |
keelson backup [output] | Write a consistent snapshot of the database, safe while the server is running. Defaults to <home>/backups/keelson-<timestamp>.db; --db <path> snapshots a different database. |
keelson worktree prune | Remove worktrees and keelson/… branches left by finished isolated runs. Discovery requires a running server, even with --force. --dry-run lists candidates; --force also removes live runs’ worktrees and dirty ones. |
keelson uninstall | Remove the program files, the launcher, the keychain entries keelson wrote, and any agent connections it recorded. --purge also deletes the home; --yes skips the prompt; --keep-credentials leaves the keychain alone; --keep-connections leaves the agent wiring alone; --force proceeds when the server could not be stopped. |
keelson version | Print the CLI, Bun, and schema versions, plus each installed rib’s version. |
The database runs in WAL mode, so copying keelson.db while the server is
running can capture it mid-write. keelson backup opens it read-only and asks
SQLite for a fully checkpointed snapshot instead: consistent, no -wal/-shm
sidecar, and safe to take live. Restore by putting the file back as
keelson.db with the server stopped.
keelson uninstall splits the home in two. The program files (node_modules,
package.json, bun.lock, .npmrc) are what install.sh provisioned, and they
always go. Your data (keelson.db, workflows/, commands/, config.json, and
each rib’s data directory) stays unless you pass --purge.
A plain run removes the CLI itself along with the other program files, so it also
removes the command you would type a second time. Pass --purge on the first run
when the data should go too; after a plain run, delete the home directory
yourself. It leaves a .keelson-uninstalled note behind saying what happened,
which is also how a later --purge recognizes the home: KEELSON_HOME is
whatever you point it at, so a missing package.json on its own is not evidence
of anything. A home whose package.json belongs to some other project is refused
outright, with or without --purge, so pointing the command at a source checkout
cannot cost that project its node_modules.
Related
Section titled “Related”- Operating the server: the service lifecycle as a how-to, including the background server and its shutdown token.
- Your first run: the install,
doctor, and the first chat turn, walked end to end. - Configuration: the
KEELSON_*variables the CLI and server read.