---
title: "CLI Reference — Canopy docs | UniverLab"
source: "https://univerlab.org/canopy/docs/cli-reference/"
---

[← Canopy](/canopy/)Documentation

# CLI Reference

Last updated 2026 · Sol 296 · 14:10 UTC (2026-10-23T14:10Z)

    canopy [command] [options]

Running `canopy` with no command opens the [TUI](/canopy/docs/tui/) (running setup first if needed, and checking for updates (notice only — run `canopy update` to install)).

## Daemon

| Command | Description |
| --- | --- |
| canopy daemon start | Start the daemon (MCP server, scheduler, watchers) |
| canopy daemon stop | Stop the daemon |
| canopy daemon status | Show daemon status |
| canopy daemon restart | Restart the daemon |
| canopy daemon logs | Show the daemon log |
| canopy daemon install-service | Register as a system service |
| canopy daemon uninstall-service | Remove the system service |
| canopy daemon health-check | Run the daily database integrity check and backup now, instead of waiting for the daemon to find an idle moment (see Installation) |

## Updates

| Command | Description |
| --- | --- |
| canopy update | Check the latest stable release, ask Update to <tag>? [y/N] (default no), and install it atomically; prints canopy <current> → <latest> or canopy <current> is up to date |
| canopy update --check | Print the update status without changing anything; exits 0 when up to date, 1 when an update exists, and 2 when the check could not complete (network, DNS, TLS, HTTP error, or unparsable response — one stderr line names the cause) |
| canopy update --yes | Install without the confirmation prompt |

A cargo-installed binary is never overwritten: `canopy update` prints `installed with cargo — run: cargo install harness-canopy --force`. After a successful replacement, a running systemd-managed daemon is restarted with `systemctl --user restart canopy.service`; an unmanaged daemon uses the normal `canopy daemon stop/start` path. If a graph is running, update asks before restarting and otherwise leaves the daemon untouched and prints the command to run later.

Plain `canopy update` exits 2 on the same lookup errors.

## Setup & diagnostics

| Command | Description |
| --- | --- |
| canopy setup | Interactive setup wizard — detects AI CLIs, generates ~/.canopy/config.toml |
| canopy setup --local-registry <path> | Use a local registry directory (for registry development) |
| canopy setup --force-skills | Overwrite local skill files that diverge from the sync source, even when they were edited locally (default: skip them with a warning) |
| canopy mcp | MCP wizard — sync/add/remove canopy’s MCP entry across platforms |
| canopy mcp --local-registry <path> | Run the MCP wizard against a local registry directory instead of GitHub (same flag as canopy setup --local-registry) |
| canopy mcp → Add | Add a server via pasted README JSON, local stdio command (with args + env), or remote URL (with headers); previews entries, marks (replaces existing) overwrites, then asks Install on N platform(s)? |
| canopy uninstall --dry-run | Preview removal: stop daemon, remove service unit + canopy.service.d/ drop-ins, revert platform configs, remove skill symlinks (changes nothing) |
| canopy uninstall --keep-shared-servers | Only remove canopy’s own canopy server entry; leave shared fetch/filesystem entries in place |
| canopy doctor | Full health diagnostics |

## Platform registry

| Command | Description |
| --- | --- |
| canopy registry refresh | Fetch the platform registry and apply it now, ignoring the 24-hour interval the TUI’s background refresh uses; prints one line per affected platform (<platform>: changed <fields>, <platform>: kept local override <fields>, <platform>: added, <platform>: updated, <platform>: removed), or registry already up to date when nothing changed. Rewrites only config.toml and the baseline file — never starts the TUI, wizard or daemon, safe while graphs run |
| canopy registry refresh --json | Print the same report as JSON (changed, kept, added, updated, deleted, up_to_date) |
| canopy registry refresh --local-registry <path> | Apply a local registry directory instead of fetching from GitHub (same flag as canopy setup --local-registry) |

Registry-owned CLI fields — binary path, model flag, headless mode, environment variables — are rewritten from the registry. A field you edited yourself is kept as it is; when the registry changed that same field, the refresh reports it as a local override, so a refresh never overwrites your choice. The command needs an existing `~/.canopy/config.toml` — on a machine that has not run setup it fails with ``run `canopy setup` first``. The refresh time is recorded where the TUI’s background refresh records it, so a manual refresh also restarts that 24-hour window.

## Graph inspection

| Command | Description |
| --- | --- |
| canopy graph list | List all graphs with status and spec progress |
| canopy graph list --workdir <path> | Filter graphs by workdir |
| canopy graph info <id-or-name> | Detailed status for a single graph (specs, runs, hooks) |
| canopy graph crew [id-or-name] | List harness/model assignments — agent nodes outside ensembles, ensemble members and agent hooks, each with platform, model, timeout, crew and availability. Omit the graph for every graph |
| canopy graph crew --platform <p> --model <m> | Filter by exact platform and case-insensitive model substring |
| canopy graph crew --all | Include archived graphs |
| canopy graph crew --group-by-pair | Summarise per platform/model pair: graph, ensemble, node and hook counts plus the graph names |
| canopy graph crew --json | Print the MCP graph_crew JSON shape instead of tables |

Graph ids can be specified by exact id, exact name, or unambiguous id prefix. Ambiguous references list the candidates.

## Graph control

Mirrors the `graph_run`/`graph_pause`/`graph_continue`/`graph_reset`/ `graph_schedule_autorun` MCP tools — a second way to reach the same daemon operations when an MCP client can’t. Every command resolves `<id-or-name>` locally (same rule as graph inspection above) and then delegates the actual state change to the daemon over its existing MCP endpoint; it never writes to the database directly. If the daemon isn’t reachable on the resolved port, the error says so explicitly instead of looking like the graph is missing.

| Command | Description |
| --- | --- |
| canopy graph run <id-or-name> | Run a graph in the background, spec by spec |
| canopy graph run <id-or-name> --queue <queue-id> | Run the queue’s pending specs through the graph instead |
| canopy graph run <id-or-name> --workdir <path> | Override the graph’s workdir for this run only |
| canopy graph run <id-or-name> --from <node> | Start the first spec this run executes at a chosen node (id, id prefix, node name, or ensemble name), reusing the recorded outputs of upstream nodes; refused, naming the node, if an upstream node’s latest run failed, errored or was interrupted |
| canopy graph pause <id-or-name> | Pause a running graph after the current node finishes |
| canopy graph continue <id-or-name> --retry-current-node | Resume a paused or failed graph by retrying the current node (also resumes a paused graph whose current spec is pending with a recorded node; clears that spec’s execution count and the reply says so) |
| canopy graph continue <id-or-name> --skip-next-spec | Resume a paused or failed graph by skipping to the next spec |
| canopy graph continue <id-or-name> --from <node> [--spec <spec>] | Start a spec at a chosen node, reusing the recorded outputs of upstream nodes (refused, naming the node, if one failed, errored or was interrupted; defaults to the graph’s current spec: a running/interrupted member, else a failed one, else the next pending one) |
| canopy graph reset <id-or-name> | Reset a completed/failed graph back to pending (prompts for confirmation) |
| canopy graph reset <id-or-name> --specs <id>... | Reset specific spec ids, even if already completed |
| canopy graph reset <id-or-name> --yes | Skip the confirmation prompt |
| canopy graph reset <id-or-name> --clear-notes | Also delete the reset specs’ spec notes (default keeps them) |
| canopy graph remove-spec <id-or-name> <spec-id> | Unbind a spec from the graph, making it a standalone backlog spec; its execution history is kept |
| canopy graph autorun <id-or-name> --at <iso8601> | Schedule a one-shot resume at a future time |
| canopy graph autorun <id-or-name> --quota-reset-message <text> | Schedule a resume from a raw CLI quota-limit message |
| canopy graph autorun <id-or-name> --cancel | Cancel a pending autorun schedule |
| canopy graph log <id-or-name> | Print the graph’s durable event log, one line per event |
| canopy graph log <id-or-name> --since <id\|RFC3339> | Start from an event id or a timestamp |
| canopy graph log <id-or-name> --kind <k> | Only events of this kind (repeatable) |
| canopy graph log <id-or-name> --spec <id> | Only this spec’s events |
| canopy graph log <id-or-name> --follow | Poll every 2 s and print new events until Ctrl+C |

`canopy graph log` prints `<created_at> <KIND> spec=<name> node=<name> <message>` and goes through the daemon like the other `graph` subcommands. See the [Event log](/canopy/docs/graphs/#event-log) section.

## Agents

Read-only inspection of registered agents, served from the local database (like `canopy graph info` — no daemon required).

| Command | Description |
| --- | --- |
| canopy agent show <id> | Show one agent’s full stored definition, prompt last and untruncated |
| canopy agent show <id> --json | Print the same definition as JSON (identical to the agent_get MCP tool’s output) |

## Missions

Read-only listing of the 28 gamification missions (see [TUI](/canopy/docs/tui/#gamification)), served from the local database — no daemon required.

| Command | Description |
| --- | --- |
| canopy missions | List every mission: icon, title, category, unlocked <date> or locked: <challenge>, plus N / 28 unlocked |
| canopy missions --json | Print the same report as one JSON object (unlocked, total, missions with id, title, icon, category, challenge, unlocked_at as RFC 3339 UTC or null) |

## Spec backlog

| Command | Description |
| --- | --- |
| canopy spec create --name <name> --description <text> | Create a standalone spec (a backlog item) |
| canopy spec create ... --workdir <path> | Tag the new spec with a workdir, for backlog filtering |
| canopy spec create ... --spec-type <type> | Set the Conventional Commits type (feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert). Optional — when omitted and the name is a conventional title, the type and scope are parsed from the name |
| canopy spec create ... --spec-scope <scope> | Set the optional scope (e.g. tui). Part of the deterministic commit message |
| canopy spec create ... --queue <queue-id> | Also append the new spec to an existing queue |
| canopy spec create ... --queue <queue-id> --group <group> | Add it to the queue within a context group |
| canopy spec complete <spec-id> --reason <text> | Mark a standalone spec as completed |
| canopy spec skip <spec-id> --reason <text> | Mark a standalone spec as skipped |
| canopy spec reopen <spec-id> --reason <text> | Reopen a completed/skipped spec back to pending |
| canopy spec convert | Migrate legacy heading-format spec bodies to the tagged <spec> format (skips tagged and running specs; prints a report) |
| canopy spec notes <spec-id> | Print every spec note of a spec, newest first, with its owner, last update and body (see Spec notes) |

## Availability roster

| Command | Description |
| --- | --- |
| canopy roster list [--platform <platform>] | List roster entries with their effective state now |
| canopy roster set <platform> [<model_pattern>] --unavailable [--reason <text>] [--until <rfc3339>] | Declare a platform/model pair unavailable; until must be a future RFC 3339 UTC instant |
| canopy roster set <platform> [<model_pattern>] --available | Declare it available |
| canopy roster clear <platform> [<model_pattern>] | Delete one entry (idempotent) |
| canopy roster check <platform> [<model>] | Show the effective availability of one pair |

Omitting `<model_pattern>` addresses every model of the platform; a pattern ending in `*` (e.g. `opencode-go/*`) addresses a prefix. The engine skips effectively-unavailable members at dispatch and blocks a single unavailable agent node or hook — it never edits a graph.

## Crews

| Command | Description |
| --- | --- |
| canopy crew list | List every crew with its version, members and binding counts |
| canopy crew show <name> | Show one crew and every binding of it |
| canopy crew create <name> --from-ensemble <id> [--description <text>] | Create a crew seeded from an ensemble’s current members |
| canopy crew delete <name> | Delete a crew (refused while any ensemble, node or hook binds it) |

A crew is a named, ordered member list ensembles and agent nodes/hooks bind to; member editing is MCP/TUI only (see [MCP tools](/canopy/docs/mcp-tools/)).

## Settings

Every setting in `~/.canopy/config.toml` is declared once, in a typed registry, and read and written through one surface the CLI, MCP and the TUI all use.

| Command | Description |
| --- | --- |
| canopy config list [--group <group>] | List settings with their group, kind, default, effective value, where it came from (default, file, env), when it applies, and status (ok, invalid: <reason>, pending_restart, unknown) |
| canopy config get <key> | Show one setting by its dotted key (e.g. clean.retention_days) |
| canopy config set <key> <value> | Change one setting; validated first, and nothing is written if it is refused |
| canopy config reset <key> | Write the setting’s default back |
| canopy config path | Print the path of config.toml |

Only the daemon writes `config.toml`, and it edits the file with `toml_edit`, so hand-written comments, key order and unknown keys survive every change. `canopy config set` reports what the user must do for the value to take effect: `applied`, `applies on next run`, `restart the daemon`, or `restart the TUI`. A hand edit is picked up by the daemon’s file watcher without a restart, and an invalid value keeps the previous effective value — it is reported by `canopy config list` and as a `canopy doctor` warning rather than being applied. `pending_restart` marks a restart-only setting whose value on disk differs from the one the running daemon loaded, including a key the file no longer holds, and a `config.toml` that will not parse at all is reported as a single `config.toml` row (and doctor warning) instead of being silently ignored. `skills.sources` (a list of tables) must still be edited by hand.

## Spec queues

| Command | Description |
| --- | --- |
| canopy queue group <queue> <spec> <group> | Set the context group of an existing queue member; a spec joining a group with pending members moves next to them |
| canopy queue group <queue> <spec> --clear | Clear the member’s group (it stays in place unless that splits its old group) |

## RAG

| Command | Description |
| --- | --- |
| canopy rag auto-index start | Resume automatic indexing (default state) |
| canopy rag auto-index stop | Pause indexing without losing the queue |
| canopy rag report | Detailed per-file indexing report, store footprint and health |
| canopy rag compact | Merge store fragments and prune old versions (daemon does it, or this process when the daemon is down) |
| canopy rag backfill | Index every configured file that is not in the vector store yet |
| canopy rag purge [--yes] | Delete the entire vector store and reset all RAG state; asks for confirmation unless --yes (-y) is given |
| canopy rag model retry | Clear a failed local embedding-model download so the daemon retries it in the background, without re-running setup |

## Clean

`canopy clean` removes stale data. The default run is a soft cleanup:

| Command | Description |
| --- | --- |
| canopy clean --dry-run | Preview what would be removed; deletes and modifies nothing |
| canopy clean | Soft cleanup: sessions and files older than clean.retention_days, and graph event-log rows older than graph_event_retention_days for graphs that are not running |
| canopy clean --older-than <days> | Override the retention window for this run only |
| canopy clean --hard | Also delete orphaned projects (workdir missing) and their dependent rows; asks for confirmation unless --yes is given. Soft cleanup runs first |
| canopy clean --yes | Skip the confirmation prompt that --hard requires. A typo here can delete a real cascade |
| canopy clean --no-reclaim | Skip reclaiming freed database space (VACUUM and WAL checkpoint) even when this run deleted enough rows to warrant it |
| canopy clean --stop-daemon | Consent to stop a running daemon for a warranted reclaim, then restart it exactly as it was. Without it, an interactive terminal is prompted; a non-interactive run skips reclamation while the daemon is up |

Reclaim runs only when a run deletes at least `clean.reclaim_min_deleted_rows` rows (default 50). It takes an exclusive lock and rewrites the whole database file, which is why it can be skipped or deferred.

## Settings reference

Every key the registry declares, with its default and range. `canopy config list` prints the same keys with the value this machine actually uses and where it came from. **Apply** says when a change takes effect: `applied` (at once), `applies on next run`, `restart the daemon` or `restart the TUI`.

| Key | Default | Range | Apply | Meaning |
| --- | --- | --- | --- | --- |
| mcp_filesystem_root | home directory | — | next run | Root directory the MCP filesystem server exposes |
| temperature_unit | celsius | celsius, fahrenheit | restart the TUI | Temperature unit for the TUI’s system widgets |
| theme | classic | classic, modern | applied | TUI colour theme: bordered or borderless |
| pinned_panel_face | auto | auto, activity, knowledge, graph, crews | applied | Pinned right-panel face; auto lets the panel switch on its own |
| tui.datetime_format | %Y-%m-%d %H:%M | chrono format | applied | Dates and times in the TUI (validated by formatting a fixed instant) |
| tui.time_format | %H:%M | chrono format | applied | Time-only labels in the TUI |
| tui.panel_dwell_seconds | 10 | 0–300 | applied | Seconds a panel event holds the right panel; 0 disables the dwell |
| tui.brians_brain | true | bool | applied | Home-banner animation |
| tui.atmosphere | true | bool | applied | Fireflies and dandelions |
| tui.scroll_step | 3 | 1–20 | applied | Lines per scroll event |
| tui.page_step | 15 | 5–100 | applied | Lines per page-up/down |
| notifications.enabled | true | bool | applied | Desktop notifications |
| update.check_enabled | true | bool | applied | Automatic update checks |
| update.check_interval_hours | 24 | 1–720 | applied | Hours between automatic update checks |
| announcements_enabled | true | bool | next run | Upgrade and tip announcements |
| embeddings_model | empty | — | restart the daemon | Embedding model used by the knowledge layer |
| embeddings_idle_unload_secs | 600 | 1–86400 s | restart the daemon | Idle time before the embedding model is unloaded |
| similarity_threshold | 0.25 | 0.0–1.0 | next run | Lexical-cohesion threshold for semantic chunk merging |
| rag_personal_dirs | [] | list of paths | next run | Personal directories indexed recursively |
| rag_max_file_mb | 10 | 1–ceiling | next run | Per-file indexing size cap, in MB |
| rag_vector_cache_entries | 64 | 1–4096 | restart the daemon | LanceDB index cache ceiling, in entries |
| skills.sources | the UniverLab skills repo | list of tables | restart the daemon | Git sources for the skill store, checked in order (edit by hand) |
| skills.ttl_minutes | 15 | 0–10080 | next run | Minutes a fetched skill is served before its source is re-checked |
| models.catalog_ttl_minutes | 1440 | 1–43200 | next run | Minutes the models.dev catalog cache is served |
| models.native_ttl_minutes | 60 | 1–43200 | next run | Minutes a platform’s native model list is served |
| agents.probe_timeout_seconds | 30 | 5–300 s | applied | Timeout for a probe before the harness gives up |
| scheduler.retry_enabled | true | bool | next run | Retry a failed scheduled run after a delay |
| scheduler.retry_delay_minutes | 60 | 1–1440 | next run | Wait before a scheduled run is retried |
| scheduler.retry_max | 3 | 0–20 | next run | Retries of a failed scheduled run |
| spec_attempt_limit | 3 | 1–20 | restart the daemon | Separate graph executions a spec may fail with a verdict before it is blocked |
| ensemble_concurrency_cap | 4 | 1–64 | restart the daemon | Ensemble members that run at once across every graph |
| graphs.* | see Tuning the engine | see there | see there | Graph engine retry budgets, ensemble member caps and check output |
| clean.retention_days | 7 | 1–3650 days | next run | Days kept before canopy clean removes sessions and files |
| graph_event_retention_days | 90 | 1–3650 days | next run | Days a graph’s event log is kept before canopy clean prunes it |
| clean.reclaim_min_deleted_rows | 50 | 0–100000 | applied | Deleted rows that trigger VACUUM during canopy clean |
| activity.retention_per_workdir | 500 | 50–100000 | applied | Activity-log entries kept per workdir |

`scheduler.retry_*` and `spec_attempt_limit` are not in `graphs.*`, which is why they are listed here.

## Spec queues

## Plumbing

| Command | Description |
| --- | --- |
| canopy stdio | Run the MCP server over stdio |
| canopy bridge --id <session> | Stdio sidecar proxy that injects canopy identity headers |

## Global flags

| Flag | Description |
| --- | --- |
| -p, --port <port> | Override the daemon port (default 7755) |
| --help | Show help for any command |
| --version | Show canopy version |