CLI
Key Ideas
- Reliable Surface: the CLI should be dependable, scriptable, and low-state.
- Daemon Routed: normal product mutations should go through the local daemon so all surfaces share authority.
- JSON Friendly: command output should be inspectable by people and machines.
- Operational Escape Hatch: install, repair, update, diagnostics, lifecycle, and local system commands need a stable terminal surface.
- Agent Compatible: agents should be able to use the CLI when it is the most direct and robust interface.
Purpose
The CLI exists for reliable operation. It should let users and agents start, stop, inspect, repair, install, update, attach projects, manage work, query diagnostics, and automate flows without depending on browser state.
The CLI is not meant to bypass the Model or Application. It should adapt the same Application behavior as other Surfaces.
Expected Role
The CLI should be the most stable surface for automation and system control. Browser state can be refreshed or lost, and future agent surfaces may evolve quickly. The CLI should remain a compact way to operate Refine from the host environment.
Current implementation details that matter to intent:
- command groups include config, project, sync, goal, feature, Todo, workflow, node, fleet, log, agent, and system.
refine configreads ordinary Settings and the Skills domain.refine skillsexposes revision-fenced editing, trigger discovery, cloning, manual launch, run status, history, and cancellation. Each Skill has one trigger; useskills cloneto reuse its instructions at another trigger point. Manual input supports typed parameters, defaults, System context, and interactive prompts for missing required values, independently of Goals. The separate Events, Governance, Quality, and Guidance configuration menus are removed.- normal config calls use the active checkout-owned daemon, API contract version, and mutation idempotency key. Detached apps, unreachable daemons, invalid payloads, missing entries, and stale collection revisions remain structured errors suitable for automation. Explicit target roots exist only as hidden test adapters.
fleet manage "<request>"opens an agent session seeded with the manage-fleet runbook so fleet changes are conversational;fleet "<request>"andfleet distribute "<instructions>"reach the same session. The other fleet commands remain the deterministic primitives the agent acts through.refine commandsemits the supported user-facing command tree as machine-readable JSON, whilerefine nextrecommends commands from current state. Hidden worker entry points and checkout-launcher-only operations are omitted from the catalog. These live surfaces are authoritative instead of a committed generated command snapshot.- Todo commands require an explicit Reporter and route list and item operations through the shared daemon API and
FileTodoService, returning the same machine-usable JSON as other Todo surfaces. goal draftturns Plan text into exactly one reviewable, unpersisted Goal draft through the shared import-extraction API.goal approveis the only CLI command for accepting a Goal after it reaches Review; obsolete verification and merge aliases are not retained.goal resolve-merged <id>is the supported operator recovery for a Quality Goal whose authoritative current-Round candidate was already integrated. It routes through the daemon to the same idempotent, fail-closed resolver used by workflow automation and does not weaken Review-only approval.agent openstarts a general Agent by default.--profile goal <goal-id>attaches the current terminal to the workflow-owned Goal Agent, while--profile planand--profile standaloneopen those role sessions. Ctrl-] detaches without stopping the agent.- normal target-state mutations are routed to the daemon instead of directly writing files in normal operation.
system fetch-email-goals --runtime-root <runtime> --target-root <target>is a bounded one-shot exception for email intake Skills. Its required target identifies the host-local connection authorized by the shared Application capability before secret or request-ledger access. The CLI only adapts that capability: sender filtering, MIME extraction, deterministic Goal identities, durable retries, and ordinary Backlog authoring remain Application-owned. Fetching never approves Goals, changes Review decisions, or sends replies; unrelated direct-target restrictions remain in force.syncis the single top-level state-convergence command; it replacedproject syncand theproject state-recoverysubtree with no aliases. That replacement is per node, not per fleet: a node’s CLI and daemon are one binary, so the retired spellings and the routes behind them disappear together on the node being upgraded while every other node keeps its own. Daemon-routedsync, and the local leg offleet sync, follow their durable operation through success, failure, cancellation, interruption, or timeout. Success prints the terminal structured result; every other terminal outcome is nonzero and retains the structured reconciler error, the stable conflict report id, the node-local report path, and per-path domain-terms summaries instead of returning an initialrunningreceipt.fleet syncis this node’s ownsyncplus one status per other node, asked over each node’s daemon API. A node still on the previous build rejects this build’s API contract version and is reported as that node’spending upgrade, carrying both contract versions; the rest of the fleet still syncs and the command still succeeds. Nodes are upgraded one at a time and in any order, so the condition is normal during a rollout rather than a fleet failure — and it never withholds work from the node, which keeps running its own build. A node whose Git is older than the one the state merge requires is reported the same way, as that node’sunsupported_git: it cannot merge state at all until its Git is upgraded, and that is one node’s condition rather than the fleet’s. Unreachable and error answers stay per-node conditions in the same way: no answer from another node’s daemon changes that node’s recorded health, which is its provisioning verdict and the one thing that withholds work from it. The statuses are this pass’s own observation and stay in this pass’s output rather than being published to the fleet, because whether one node can reach another is a fact about that link and not shared truth. The other nodes’ statuses are reported even when this node’s ownsyncfails: a standing conflict here is this node’s condition, and a rollout check must still be able to read the fleet. What a reached node answers is a receipt — itqueuedits own pass — not a verdict on its reconciliation, which that node runs under its own lock and reports on its own sync surface and state-sync health. Following each node’s pass from here would hold the whole fan-out behind one node’s agent resolution, which takes minutes by design.sync --previewis a read-only divergence summary — classification, both heads and the merge base, per-path sides, a domain-terms summary per contested path, and the recordeddecision_questionwhen a conflict report matches the previewed heads. It writes nothing, exits nonzero on error having written nothing, and is never a token handed to another command.sync --authority live|remoteis terminal recovery — sync with a decision attached: every contested path takes the chosen side inside one merge commit, and repeated--pathexceptions settle named contested paths on the opposite side. Rerunning after success finds converged heads and is a no-op. Bounded races against a moving remote head are retried inside the command; callers never wrap it in retry loops, and every non-race failure surfaces immediately as itself. Escalation is ordered before any of this: the sync attempt resolves contested records with an agent first (syncandfleet syncopt in, subject tostate_sync_agent_resolution), and only a conflict that resolution escalated, could not reach, or is not allowed to touch reaches the daemon’s automatic recovery policy. That policy preserves a proven one-sided Goal transfer, leaves ambiguous ownership for explicit authority or a supported transfer surface, and remains optional throughstate_sync_auto_recovery: off. Ordinary syncing needs no CLI either way.- every stateful command derives its product home from the invoked checkout-owned binary and uses only that checkout’s
run/<port>tree. Running the command from another checkout cannot redirect ownership. Explicit absolute runtime paths fail closed unless they are the exact canonical checkout runtime; isolated tests use explicit test-only adapters rather than production fallbacks. - system commands handle daemon lifecycle, port-scoped OS service registration and removal, repair, rollback, doctor, and API group discovery. They are thin callers of the same port-scoped host lifecycle and installation capabilities used by HTTP/API, update, and maintenance paths.
system service-installandsystem service-uninstallname the service-manager effect directly; the retiredsystem installandsystem uninstallspellings are neither parsed nor advertised. Checkout-only production-binary maintenance remains owned by./r system buildand./r system clean, while source updates remain owned by./r system update; these are launcher operations rather than production-binary subcommands. The shared lifecycle authority selects activated systemd or launchd control versus direct-process fallback, reconciles durable state with fresh post-control reachability, keeps command failures visible without replacing a still-reachable daemon’s healthy state, fails closed on unreachable or ambiguous observations with partial recovery evidence, and preserves restart-specific evidence. It reports stopped only after shutdown is confirmed. Explicit foreground and one-request starts remain direct bootstrap paths. launchd labels are installation-port scoped, while a recorded legacy registration is migrated or controlled only when exact parsed arguments prove that it belongs to the selected installation; adjacent textual ports never count as ownership. - current installation targets are daemon-oriented (
macos_daemon,windows_daemon, andlinux_cli_web). Historical target spellings are deserialize-only migration aliases. When the production binary is missing,./r system service-installbootstraps the locked release from the invoked product home, atomically publishes the stablebin/refineexecutable and deployed marker, and only then performs ordinary service conflict detection and registration. An existing binary is never rebuilt as a side effect of service registration, and the command never fetches or updates source. A bootstrap or publication failure leaves service state untouched../r system service-uninstallstops and removes only the selected port’s service registration. Explicit repair backs up exact legacy registration bytes and parsed identity in a retained port-scoped journal before atomically publishing a checkout-local registration, and restores the original registration if activation or verification fails. External runtime and binary trees are never merged, overwritten, or deleted. - source status reads the same hourly cached identity used by the browser and API; refresh queues one coalesced supervised fetch. Source upgrade launches the configured installed Agent outside Goal claims, and its hidden granular capability/helper commands carry the durable operation and handoff-attempt identifiers. The raw claim nonce is process fencing only and is never returned in command output or public status.
- CLI tests verify daemon routing and shared service behavior.
The CLI should avoid becoming a second implementation of Refine. It should remain a reliable adapter to the same Application and Model, while host execution remains an Infrastructure concern.
refine system status, process diagnostics and refine next expose the same freshly evaluated workflow health. A reachable daemon with stale scheduling evidence reports daemon_healthy=false and remains visible in the running-port list. Reachability and shutdown control do not depend on workflow health.
Next-action guidance reports locally eligible Todo work observed continuously for over thirty seconds with free capacity and no active attempt, including its count, last scheduler tick and supported inspection/remediation commands. Its independent runtime observation distinguishes pause, disabled workflow automation, required Plan/Implement Skill availability and node scope, Feature/priority order, pending outcome handling, live tasks and interactive Goal sessions. A missing, corrupt, stale or mismatched observation is unavailable evidence rather than an all-quiet claim. These observations do not create durable scheduling ownership or change Goal intent.
Workflow inspection and control use workflow show, workflow move, and workflow integrate. Decisions require the expected revision, request id, and reason; optional context accompanies recovery. workflow move --force selects any exact step on the existing Round, stops its current execution, and bypasses automated transition and evidence restrictions. Forced integration remains a separate action requiring concrete Git inputs. goal round-delete ID NUMBER removes a one-based Round and all associated records, then parks the Goal in Backlog; --expected-revision can bind deletion to a prior inspection. API and MCP expose the same capabilities, with zero-based Round indexes on the HTTP route DELETE /work/goals/:id/rounds/:index and a required expected revision. Hub management uses hub commands for sites, collections, JSON records, indexes, bounded queries, streaming JSONL import/export, assets, publication, and sync status. All commands dispatch to the shared daemon capability.
Future Direction
The CLI should become increasingly useful to agents. Future agents may prefer structured CLI calls for discoverability, reproducibility, and low visual overhead.
As AI systems improve, the CLI should expose high-signal operations and machine-readable output without requiring a human to click through the browser. It should remain conservative in surface area: add commands when they express real capabilities, not when they duplicate a page.
AI provider configuration
refine config providers show reads the shared catalog and effective node selection. refine config providers save --file providers.json replaces the catalog with revision checking. refine config providers select ID sets the node override; omit ID to inherit the system default. See Configure AI providers for argument templates, stdin transport, session capabilities, and a custom CLI example.