# Command-line interface

The `roscoe` CLI is a single binary that authors, runs, inspects, and serves workflows. Reach for it when you want to script Roscoe into a build step, kick off a quick run from the terminal, or stand up the web UI on a port of your choosing. Every subcommand is also reachable from source via `bun apps/cli/src/index.ts <subcommand>` while developing.

This page is a tour. For the exhaustive flag-by-flag reference, see [/docs/reference/cli-commands](/docs/reference/cli-commands). For the in-browser tour, see [/docs/running/web-ui](/docs/running/web-ui). For driving Roscoe from a Claude Code session, see [/docs/running/mcp](/docs/running/mcp).

## Authoring

### `roscoe init`

One-shot setup: creates the data directory, runs DB migrations, seeds a set of starter workflows (including `demo-1-the-panel`, `demo-2-week-of-dinners`, and `demo-4-second-draft`), and writes a starter `roscoe.yaml`. When run inside a git repo it also scaffolds `<repo>/.roscoe/` and drops a `.roscoe/.gitignore` so its regenerated schema files (and a stray DB) aren't committed. Your repo's root `.gitignore` is left untouched, and `<repo>/.roscoe/workflows/` + `roscoe.yaml` are tracked so you can commit and share them. The database itself lives in the global home (`~/.roscoe/`), never in your repo.

```bash
roscoe init                    # default — touches both global and (if in git repo) project
roscoe init --global-only      # skip repo scaffolding
roscoe init --repo-only        # skip global init
roscoe init --force            # overwrite existing example files
roscoe init --no-examples      # skip seeding the nine example workflows
```

Global init (anything but `--repo-only`) also installs a `/roscoe` slash command
at `~/.claude/commands/roscoe.md`, so Claude Code / Cowork users can launch a
workflow by typing `/roscoe <workflow-name>`. It's best-effort: a failure is
logged but never aborts init.

### `roscoe validate <file>`

Static check against the workflow JSON Schema and integrity rules (unreachable states, dangling transitions, unknown models). Run it in CI or as a pre-commit hook to catch malformed YAML before it ships.

```bash
roscoe validate .roscoe/workflows/release-train.workflow.yaml
```

## Run

### `roscoe run <workflow> [--var key=value ...] [--source global|repo]`

Start a workflow. The first argument is either a workflow id (resolved against project then global, see [/docs/configuration/workflow-sources](/docs/configuration/workflow-sources)) or an explicit path to a `.workflow.yaml` file.

`roscoe run` routes through a running server. Start `roscoe serve` (or `bun run dev` during development) in another terminal first. If nothing is listening, the command exits with a hint pointing at both startup paths.

In a TTY, `roscoe run` mounts a live Ink view: a node-by-node tree, a status line, and an interactive HITL prompt when the workflow pauses on a `human` node. Press `q` to detach (the run keeps executing on the server), `c` to cancel (with confirmation), or `o` to open the run page in the browser.

```bash
# By id — repo wins over global on collision
roscoe run demo-1-the-panel

# Force the global copy when a repo workflow shadows the same id
roscoe run demo-1-the-panel --source global

# Pass workflow inputs (repeatable -i / --input; values are JSON-parsed)
roscoe run demo-2-week-of-dinners --input days=3 --input diet=vegetarian

# By explicit path
roscoe run ./scratch/experiment.workflow.yaml

# Headless / CI / agent: emit JSON and exit
roscoe run demo-1-the-panel --json

# Cap the spend: stop the run once it exceeds $0.50 (or 200k tokens)
roscoe run expensive-flow --spending-cap 0.50
roscoe run expensive-flow --spending-cap-tokens 200000

# Reattach to a run that's still going
roscoe run --attach <runId>
```

When stdout isn't a TTY (piped output, CI), the command degrades to a one-shot start: it waits for the run to settle, then prints a JSON object with the run id, status, and the cost rollup (`totalCostUsd`, `meteredCostUsd`, `subscriptionEquivCostUsd`, `billingMode`, `inputTokens`, `outputTokens`, plus `stopReason` when the run was stopped). AI nodes hit either the Anthropic SDK or the Claude CLI depending on your environment. See [/docs/configuration/llm-backends](/docs/configuration/llm-backends).

#### Spending caps

`--spending-cap <usd>` and `--spending-cap-tokens <n>` set an optional per-run
ceiling. Caps are **off by default**. After each node completes, Roscoe re-totals
the run's cost (metered out-of-pocket **plus** the subscription API-equivalent)
and tokens; if either cap is exceeded, the run stops cleanly: status
`cancelled`, `stopReason: spending_cap_exceeded`. Set both to stop on whichever
is reached first. The cap bounds the whole run, including `consensus` / `map` /
`round_robin` fan-out and nested sub-workflows, and applies to subscription runs
too (it bounds the would-be API cost). A workflow can also declare a default cap
in its YAML (see [/docs/authoring/yaml-structure](/docs/authoring/yaml-structure));
a flag here overrides that default for the run.

### `roscoe resume <runId> [transition]`

Continue a workflow paused at a `human` node. In a TTY, omitting the transition mounts the same interactive picker `roscoe run` uses. With a transition argument the command runs non-interactively (suitable for scripts and CI).

```bash
roscoe resume 01HXYZ... approve
roscoe resume 01HXYZ... reject

# Interactive picker — pick from the valid transitions in a Select
roscoe resume 01HXYZ...
```

The valid transitions are listed in `roscoe monitor` and on the run page in the web UI.

## Inspect

### `roscoe list`

Lists every workflow Roscoe can see across both sources. Project entries appear first; the global entry is marked `[shadowed by repo]` when a project workflow with the same id is masking it.

```bash
roscoe list
```

### `roscoe monitor <runId>`

In a TTY, `roscoe monitor` mounts the same live Ink view as `roscoe run`: a node tree, status line, and HITL prompt, without starting a new run. With `--json` or when piped, it prints a one-shot snapshot (run + node trace) and exits.

```bash
roscoe monitor 01HXYZ...                 # live TUI
roscoe monitor 01HXYZ... --json          # one-shot snapshot for scripts
```

Like `roscoe run`, the live path requires a running server. The static `--json` path reads the local DB directly and works without one.

### `roscoe history`

Recent runs for the current project, newest first.

```bash
roscoe history                 # last 20 runs in this project
roscoe history -n 100          # bump the limit
roscoe history --all           # include runs from every project on this machine
```

## Projects

### `roscoe register` / `roscoe unregister`

Add or remove a project in the local registry that backs the web UI's project
switcher. Both default to the git root of the current directory; pass a path to
target another repo. Registration only records the path. `unregister` deletes
no files; it drops the entry from the switcher.

```bash
roscoe register                # show the current repo in the project switcher
roscoe register --activate     # …and make it the active workspace
roscoe unregister              # remove the current repo from the switcher
roscoe unregister ~/dev/proj   # remove a specific repo
```

You can do the same from the UI: the project switcher's **＋** adds a project
and each row's trash icon removes one.

## Server

### `roscoe serve [--port <port>]`

Boots the REST API and the embedded web UI on a single port. The compiled binary defaults to `:7777`; in dev mode the API and web run as separate processes (see [/docs/configuration/environment](/docs/configuration/environment)).

```bash
roscoe serve                   # http://localhost:7777
roscoe serve --port 4000       # custom port
ROSCOE_PORT=4000 roscoe serve   # equivalent via env
```

If the port is busy the binary auto-falls-back to the next free port and prints the chosen URL. Startup mounts a persistent Ink splash showing the bound URL, mode (standalone vs dev), data directory, and lock path. Press `q` (or send SIGINT/SIGTERM) to shut down: the lock at `<ROSCOE_HOME>/serve.lock` is removed on clean exit so `roscoe run`/`monitor`/`resume` can find the server while it's up.

## MCP

### `roscoe mcp`

Starts the MCP stdio server. Claude Code launches this for you via `.mcp.json`. You rarely run it by hand, but it is useful for debugging the MCP transport.

### `roscoe install-mcp [--project] [--path <file>]`

Registers the `roscoe` entry so Claude Code picks up the binary on its next launch. By default it writes the user-global `~/.claude.json` (top-level `mcpServers`), so the install applies to every project. The entry points at `process.execPath`, meaning the same binary you ran `install-mcp` from.

```bash
roscoe install-mcp            # user-global: writes ~/.claude.json
roscoe install-mcp --project  # project-local: writes ./.mcp.json
```

The default user-global install also drops the `/roscoe` slash command at
`~/.claude/commands/roscoe.md` (same file `roscoe init` writes), so you can launch
a workflow with `/roscoe <workflow-name>` in Claude Code / Cowork. It's skipped
for `--project`, `--client claude-desktop`, and `--path`.

For the full MCP tour, see [/docs/running/mcp](/docs/running/mcp): what the tools do, how the pause/advance loop works, and how Claude Code drives a run end-to-end.

## See also

- [/docs/reference/cli-commands](/docs/reference/cli-commands) — full flag reference for every subcommand.
- [/docs/running/web-ui](/docs/running/web-ui) — the in-browser editor and run viewer.
- [/docs/running/mcp](/docs/running/mcp) — driving Roscoe from Claude Code.
- [/docs/configuration/environment](/docs/configuration/environment) — env vars that change CLI behaviour.
