# Company OS command line

This page is the terminal reference for operators of Company OS. It lists the
commands that exist in the launcher and the operator CLI, their flags, and a few
short recipes. For connecting an AI client (Claude Code, Codex, Cursor, and
others) see [Connect agents](./CONNECT_AGENTS.md).

## Two layers, one command name

Company OS has two command-line layers that share the names `company-os` and
`companyos`.

| Layer | What it is | Where it runs |
|-------|------------|---------------|
| Launcher | A small shell script that starts, stops, installs, and diagnoses the local stack, and hands everything else to the operator CLI. | On the machine that hosts Company OS. |
| Operator CLI | A Node program that signs in to a Company OS host and calls its API: tasks, runs, approvals, receipts, memory, usage, MCP. | Anywhere that can reach the host, local or remote. |

How the names relate, from the source:

- The operator CLI is the `company-os` / `companyos` package binary. Both names
  point at the same file, `dist/company-os-cli.js`, so
  `companyos task list` and `company-os task list` are the same program. The
  help text uses both spellings.
- In a source checkout, `./company-os` is the launcher entrypoint. It runs
  `scripts/company-os`, which handles the lifecycle verbs itself (`up`, `down`,
  `desktop`, `install`, and so on) and runs the operator CLI for the rest. If the
  CLI is not built yet, the launcher builds it first.
- Installed, the command is `company-os`. This page writes `company-os` in
  synopses; from a checkout, type `./company-os`.

> **Older checkouts.** Routing the operator commands (`task`, `approvals`,
> `receipt`, `events`, `ps`, and the rest) through `./company-os` is recent.
> If `./company-os task list` is not recognised, your launcher only forwards
> `login`, `logout`, `connect`, and `mcp`. Run the operator CLI directly with
> `node services/company-os-mcp-server/dist/company-os-cli.js task list`, after
> `npm --prefix services/company-os-mcp-server run build`.

A few words mean different things in each layer. Check which one you are in:

| Word | Launcher (`./company-os`) | Operator CLI (`companyos`) |
|------|---------------------------|-----------------------------|
| `status` | Prints the workspace URL when the stack is healthy. | Shows whether this CLI is signed in. |
| `doctor` | Static host check; requires `--execution-path codex`. | Client preflight plus the server's own checks. |
| `chat` | Opens the host-governed OMP terminal UI. | Plain terminal chat client (`--thread-id`). |
| `run` | `run RUN_ID` prints a read-only JSON dump; `run pause` and `run resume` go to the operator CLI. | Only `run pause` and `run resume`. |

From a checkout, the launcher definitions win for `status`, `doctor`, `chat`, and
`run RUN_ID`. To see CLI sign-in state from a checkout, use `./company-os mcp status`.
The operator CLI's `doctor` is reachable through the installed `companyos` binary.

## Signing in

Operator commands need a signed-in session.

```bash
company-os login                                   # local host, opens a browser
company-os login --console-url https://HOST --device   # remote or headless
company-os logout
```

- `login` uses an authorization-code request with PKCE in your browser. The
  browser is only the authorization handshake.
- `login --device` prints a Console URL and a one-time code to stderr. Open the
  URL on a device where you are signed in and enter the code. Use it on a
  headless terminal.
- The default Console URL is `COMPANY_OS_CONSOLE_URL`, or `http://127.0.0.1:3050`
  when that is unset. A remote URL must be HTTPS and an origin only (no path or
  credentials); plain HTTP is accepted only for loopback.
- If you are already signed in, `login` refuses and tells you to run `logout`
  or `login --force`.
- Only a refresh credential is stored, never a long-lived access key. On macOS it
  is a mode-0600 file, `~/Library/Application Support/Company OS/cli-refresh.json`.
  On Linux it uses `secret-tool`; on Windows it uses the Credential Manager
  vault. `logout` revokes the CLI session on the host, then deletes the local
  credential.
- Credentials supplied in the process environment take precedence over the saved
  session. A saved session takes precedence over an old local host binding.

## Output and safety flags

These apply to the operator CLI.

| Flag | Meaning |
|------|---------|
| (default) | The canonical Company OS response as pretty-printed JSON. Errors go to stderr as JSON with an `error` field (`approval_required`, `company_os_api_error`, or `company_os_cli_error`) and the exit code is 1. |
| `--table` | Render list payloads as an aligned table. Falls back to JSON when the payload is not a list. |
| `--quiet` | Print primary ids only, one per line. Cannot be combined with `--table`. |
| `--company-id ID` | Act on a specific company instead of the one bound to your session. Most commands accept it. |
| `--dry-run` | Mutating commands only. Runs the real handler, including reads, then prints the planned request instead of sending it. No `--confirm` is needed. |
| `--confirm` | Required on mutating commands in the default strict mode. See below. |
| `--idempotency-key KEY` | Sent as the `Idempotency-Key` header so a retry of the same request is safe. |

`--json` is not a general flag, and the operator CLI rejects it as an unknown
option on most commands. JSON is already the default. `--json` exists on the
`mcp` subcommands (except `serve`), on `doctor --execution-path codex`, and on
`run RUN_ID` (the launcher dump), where it is a no-op because JSON is the only
format.

### What `--confirm` means

`--confirm` acknowledges that you asked for a mutation. It is not a human
approval and does not grant authority. The host still applies its own principal,
idempotency, spend, settlement, and Gate checks, and can refuse. Without
`--confirm`, a mutating command in strict mode (the default) fails with
`approval_required`. Use `--dry-run` to see what would be sent.

A recorded approval is a separate object. See [Approvals](#approvals).

### Idempotency keys

- `task run` requires `--idempotency-key`. The host binds an exact retry to one
  normalized execution intent and refuses conflicting reuse with HTTP 409.
- `task create`, `run pause`, `run resume`, and `mcp connectors enable|disable`
  accept it optionally.
- Pick a key that names the intent, for example `task:TASK_ID:run:1`.

## Start and status

These are launcher verbs.

| Command | Purpose |
|---------|---------|
| `company-os` or `company-os up` | Start or reuse the local workspace and open it. Prints one workspace URL. |
| `company-os up --no-open` | Start or reuse without opening a browser. |
| `company-os up --force-restart` | Stop checkout-owned listeners first, then start. |
| `company-os up --smoke` | Start or reuse, then run the live chat smoke check. |
| `company-os status` | Print the workspace URL if API, runtime, and Workspace are healthy. |
| `company-os down` (alias `stop`) | Stop local services started from this checkout. |
| `company-os desktop` | Start or reuse Company OS through the Zeron desktop app. |
| `company-os chat [--company ID] [--model MODEL] [--prompt TEXT \| --start TEXT]` | Open the host-governed terminal UI. `--prompt` runs one turn and exits; `--start` opens the UI with TEXT as the first message. `--prompt` and `--start` cannot be combined. |
| `company-os tui [--model MODEL] [--company ID]` | Open the Zeron terminal projection. |
| `company-os run RUN_ID [--company ID] [--database-url URL]` | Print everything recorded about one run as JSON. Read-only; RUN_ID must be a UUID. |

The launcher refuses to stop processes it does not own that happen to use the
same ports. Ports are set with `HSM_COMPANY_CONSOLE_PORT` and `HSM_CONSOLE_PORT`.
Defaults and the first-run path are in
[COMPANY_OS_AGENT_CHAT_LAUNCH.md](./COMPANY_OS_AGENT_CHAT_LAUNCH.md).

### Install lifecycle

Launcher verbs that run the installer (`install.sh`):

```bash
company-os install   [options]
company-os upgrade   [options]    # same as install --upgrade
company-os rollback  [options]    # restore the previous stack and desktop
company-os uninstall [options]    # stop the stack, keep data volumes
company-os purge     [options]    # remove stack, data volumes, desktop app, config
```

Installer options: `--dir PATH` (default `~/.hsm-ii`), `--image REF`,
`--profile companion|full|dev`, `--no-start`, `--no-open`, `--desktop MODE`,
`--desktop-manifest URL`, `--credential-gateway`, `--provider-key-file PATH`,
`--telemetry on|off`. `purge` deletes data; `uninstall` does not.

### Other launcher verbs

| Command | Purpose |
|---------|---------|
| `company-os pack validate\|preview\|test\|export PATH [options]` | Offline pack tooling. Example: `pack preview packs/example --task prepare_report`. |
| `company-os registry [args]` | Command-registry policy and validation (runs the `company-os-command-registry` binary through cargo). |
| `company-os doctor --execution-path codex [--json] [--offline]` | Static host check for the native Codex execution path. Needs no running stack. |
| `company-os external-runner claim\|progress\|complete\|reconcile --company-id UUID [--run-id UUID --runner-id ID] [options]` | Drive an external runner lane. Needs `HSM_COMPANY_PRINCIPAL_TOKEN` and a bearer token or a 0600 bearer file. |

## Companies and profiles

| Command | Purpose |
|---------|---------|
| `company-os company list` | List the companies you can reach. |
| `company-os company use ID_OR_SLUG` | Bind the CLI session to another company. Runs a new authorization, so it may open the browser. |
| `company-os company profile show [--company-id ID]` | Show the active document brand profile. |
| `company-os company profile set --file PROFILE.json [--company-id ID] [--confirm \| --dry-run]` | Store a governed profile override. |
| `company-os company invoice-format show [--company-id ID]` | Show the invoice format section. |
| `company-os company invoice-format set --file FILE.json [--company-id ID] [--confirm \| --dry-run]` | Store an invoice format. The file may be a full profile or only the format. |

The file must be a JSON regular file of at most 128 KiB. A full profile has the
schema `company_os.document_brand_profile.v1` with `brand` and `invoice_format`
objects. Preview first:

```bash
company-os company profile set --file PROFILE.json --dry-run
```

## Tasks

| Command | Purpose |
|---------|---------|
| `company-os task list [--state S] [--agent REF] [--limit N] [--offset N]` | List tasks. `--limit` is 1 to 200. |
| `company-os task show TASK_ID` | Show one task. |
| `company-os task create --title TEXT [--specification TEXT] [--owner REF] [--priority N] [--idempotency-key KEY] --confirm` | Create a task. `--priority` is 0 to 100. |
| `company-os task assign TASK_ID --agent REF --confirm` | Assign a task to an agent. |
| `company-os task events TASK_ID [--after-seq N] [--limit N]` | Page through a task's events. |
| `company-os task run TASK_ID --idempotency-key KEY [--prompt TEXT] [--skill SLUG] [--risk LEVEL] [--backend NAME] [--worker-lane NAME] [--resume-if-stale] [--watch] --confirm` | Start a run for a task. `--watch` prints the start response, then follows the run until it ends. |
| `company-os task cancel TASK_ID [--reason TEXT] --confirm` | Set the task state to cancelled. |

```bash
company-os task create --title "Investigate deployment" --confirm
company-os task run TASK_ID --prompt "Collect evidence" \
  --idempotency-key "task:TASK_ID:run:1" --confirm --watch
```

## Runs and events

Read commands:

| Command | Purpose |
|---------|---------|
| `company-os ps [--task-id ID] [--status S] [--limit N] [--offset N]` | List agent runs. |
| `company-os inspect RUN_ID` | The run story: canonical status and recorded steps. |
| `company-os what RUN_ID` / `company-os why RUN_ID` | Aliases of `inspect`. In the current source all three call the same run-story endpoint and return the same payload. |
| `company-os events RUN_ID [--after-seq N] [--through-seq N] [--limit N] [--follow] [--poll-ms N]` | Page through execution events, or stream them. |
| `company-os timeline RUN_ID` | The run's workflow frames. |
| `company-os diff RUN_ID FROM_FRAME_ID TO_FRAME_ID` | Compare two frames. |
| `company-os preview RUN_ID FRAME_ID --operation rewind\|fork\|replay` | Preview a frame operation before proposing it. |

`events` details:

- Without `--follow` it returns one JSON page. `--limit` is 0 to 200.
- With `--follow` it prints one JSON event per line, advances by the server's
  `last_seq`, drains once after the run reaches a terminal status, and ends with
  one summary line (`run_id`, `status`, `last_seq`).
- `--follow` cannot be combined with `--through-seq` or `--limit 0`.
- `--poll-ms` is 100 to 60000 (default 1000).

Governed controls (all need `--confirm` unless noted):

| Command | Purpose |
|---------|---------|
| `company-os run pause RUN_ID [--reason TEXT] [--for 30m\|2h\|3d] [--idempotency-key KEY] --confirm` | Pause a run. `--for` sets a hold of up to 7 days. |
| `company-os run resume RUN_ID [--reason TEXT] [--idempotency-key KEY] --confirm` | Resume a paused run. |
| `company-os steer RUN_ID --text TEXT --confirm` | Send guidance to a live run. |
| `company-os rewind RUN_ID FRAME_ID` | Rewind a run to a frame. Takes no `--confirm` or `--dry-run`; the host decides. Run `preview` first. |
| `company-os replay RUN_ID FRAME_ID` | Replay from a frame. Same rules as `rewind`. |
| `company-os prepare-fork RUN_ID FRAME_ID --source-checkpoint-id ID --branches JSON --confirm` | Prepare a successor fork. |
| `company-os select-winner FORK_ID WINNER_BRANCH_ID --rationale TEXT --confirm` | Pick the winning branch of a fork. |

`--branches` is a JSON array of 2 to 16 objects, each with `label`,
`workspace_slug`, and `budget_scope`:

```bash
company-os prepare-fork RUN_ID FRAME_ID --source-checkpoint-id CKPT \
  --branches '[{"label":"a","workspace_slug":"ws-a","budget_scope":"scope-a"},
               {"label":"b","workspace_slug":"ws-b","budget_scope":"scope-b"}]' \
  --dry-run
```

## Approvals

| Command | Purpose |
|---------|---------|
| `company-os approvals [--status S] [--limit N] [--offset N]` | List the approval inbox. Rows from before the current envelope format are labelled `legacy_unverifiable`. |
| `company-os approval show APPROVAL_ID` | Show one approval. |
| `company-os approval decide APPROVAL_ID --decision approved\|rejected [--reason TEXT] --confirm` | Record a decision. |
| `company-os approval approve APPROVAL_ID [--reason TEXT] --confirm` | Shorthand for `--decision approved`. |
| `company-os approval reject APPROVAL_ID [--reason TEXT] --confirm` | Shorthand for `--decision rejected`. |

`decide` refreshes the host's inbox first and binds your choice to the stored
presentation, subject, and request digests. It never accepts an approval shape
you supply. The host still checks that your principal may decide it. Review with
`approval show` before you decide.

## Receipts and verification

| Command | Purpose |
|---------|---------|
| `company-os receipt list` | List receipts. |
| `company-os receipt show RECEIPT_ID` | Show one receipt. |
| `company-os receipt verify RECEIPT_ID` | Check a receipt against its source. |
| `company-os verify task TASK_ID` | Replay a task's lifecycle history and compare it with the live projection. |
| `company-os verify run RUN_ID` | The same check for a run. |

Receipt ids carry a family prefix: `terminal:`, `run_event:`, `governance:`, or
`spend:`. Use the id exactly as `receipt list` prints it, for example
`terminal:<receipt-uuid>`.

- Terminal receipts are verified by recomputing the stored canonical hash and
  checking chain linkage and the company head.
- Families without a digest contract return `verification_supported=false`. That
  means "not verifiable", never "verified".
- `verify task` and `verify run` return `verified`, `has_drift`, and
  `drift_kind`. Missing history or a replay failure is not reported as verified.

## Memory and context

| Command | Purpose |
|---------|---------|
| `company-os memory search [QUERY] [--scope SCOPE] [--latest-only] [--artifact-type TYPE]` | Search company memory. |
| `company-os context --run-id RUN_ID` | Show the host's context utility output for a run. `--run-id` is required. |

## Usage and cost

| Command | Purpose |
|---------|---------|
| `company-os usage [--limit N]` | Usage report from the host. |
| `company-os cost [--limit N]` | Cost report from the host. |
| `company-os stats [--limit N]` | Operator statistics from the host. |

All three accept `--company-id ID`; `--limit` is 1 to 200. Add `--table` for a
readable view.

## Diagnostics

| Command | Purpose |
|---------|---------|
| `companyos doctor [--company-id ID] [--limit N]` | Client preflight (credentials, active company binding, server health) plus the host's authoritative checks. Prints a JSON report and exits nonzero if any check fails. |
| `company-os operator-status [--company-id ID] [--limit N]` | The host's operator status utility. |
| `company-os utilities` | List the operator utilities the host offers. |
| `company-os vocabulary` | List the host's operator command vocabulary. |
| `company-os completions zsh` | Print a zsh completion script. Only zsh is supported. |

The script registers itself for both `company-os` and `companyos`. Source it, or
save it as `_company-os` in a directory on your `fpath`.

The prototype `scripts/company-os-today.mjs` (a "what needs me" list) is surfaced
in Tern. It is read-only and is not a stable launcher verb.

## MCP

The `mcp` command checks the MCP side of a signed-in session and manages host
connectors. For client setup see [Connect agents](./CONNECT_AGENTS.md).

| Command | Purpose |
|---------|---------|
| `company-os mcp [status]` | Verify the company, host, CLI grant, MCP tools, and connectors. |
| `company-os mcp doctor [--client omp]` | Show each read-only connection check. Exits nonzero on a failed check. |
| `company-os mcp tools [--search QUERY]` | List the tools this session actually exposes. |
| `company-os mcp connect CLIENT` | Configure a client. Same as `company-os connect CLIENT`. |
| `company-os mcp connectors list\|show ID\|resources ID\|prompts ID` | Read host-owned connector state. |
| `company-os mcp connectors enable\|disable ID --confirm [--idempotency-key KEY]` | Request a governed connector change; prints the host's receipt or refusal. |
| `company-os mcp serve --stdio` | Serve this session's read-only tools over MCP stdio. Stdout carries protocol data only. |

Flags: `--company-id UUID` on every subcommand except `serve` and `connect`; `--json` on the rest; `--table` and
`--quiet` on `tools` and `connectors list` (`--quiet` prints tool names or
connector keys). `CLIENT` is `omp`, `codex`, `claude`, `cursor`, `windsurf`,
`gemini`, or `generic`. `mcp connect` also takes `--agent-dir PATH` (omp),
`--project-dir PATH`, `--api-base URL`, `--install-dir PATH`, `--no-start`, and
`--open`.

`company-os help mcp [topic]` and `company-os mcp --help` print per-command help.

## Common workflows

### Check readiness

```bash
./company-os up --no-open        # start or reuse the stack
./company-os status              # prints the workspace URL when healthy
company-os login                 # once per machine, or when the session lapses
company-os mcp status            # host, grant, tools, connectors
companyos doctor                 # client and server checks; nonzero exit on failure
```

### See what needs approval and decide one

```bash
company-os approvals --status pending --table
company-os approval show APPROVAL_ID
company-os approval decide APPROVAL_ID --decision approved \
  --reason "Checked the draft" --dry-run     # preview the request
company-os approval decide APPROVAL_ID --decision approved \
  --reason "Checked the draft" --confirm     # record the decision
```

Read `approval show` before deciding. `--confirm` does not replace your own
review and does not widen what your principal may approve.

### Follow a run

```bash
company-os ps --status running --table
company-os events RUN_ID --follow           # JSONL until the run ends
company-os inspect RUN_ID                   # canonical status and story
company-os run pause RUN_ID --for 2h --reason "Waiting on data" --confirm
```

To start and watch in one step, use `task run ... --watch`.

### Verify a receipt

```bash
company-os receipt list --table
company-os receipt show terminal:RECEIPT_UUID
company-os receipt verify terminal:RECEIPT_UUID
company-os verify run RUN_ID                # replay history against the live run
```

A receipt whose family has no digest contract reports
`verification_supported=false`. Treat that as unverifiable, not as a pass.

## Exit codes and errors

| Situation | Result |
|-----------|--------|
| Operator CLI success | Exit 0. |
| Operator CLI error (bad flag, API error, refused mutation) | Exit 1, JSON error on stderr. |
| `doctor` or `mcp doctor` with a failed check | Nonzero exit. |
| Unknown launcher command | Exit 2, with the launcher log path on stderr. |
| `Not logged in. Run \`companyos login\` first.` | Run `company-os login`. |
