VIRGO

CLI specification

Source
docs/design/CLI-SPEC.md
Pinned at
d62e85485903a5537ee2b73c14201597e29bdaa5
sha256
5796137f060f66308bb4bd32195687a1699bf9b250f484513183e66eeb9898c2

This page is the specification itself, rendered from the pinned source above. Its text is reproduced exactly; only its presentation is added.

Virgo CLI specification (v1 draft — greenfield build)

Status: decision consolidation for the v2 greenfield installation (fresh build on a new hub machine; the currently running installation is untouched and no interoperability with it is required). Implementation tracks this document; conflicts resolve through the owner, not silent drift. v0 history: machine/persona verbs consolidated 2026-08-03..05; v1 integrates the owner's 2026-08-09 requirements (wizard UX, agents, adapters) and the seven field-proven operational verbs.

One entrypoint, two interaction modes

virgo is the single user-facing command. Every operation is a subcommand; no companion scripts, no scattered binaries. Daemons (hub services, keepers, watchdogs, advertisers) ship inside the release and are managed by the CLI — users never launch them by hand.

Every subcommand supports both modes with one grammar:

  1. Direct mode — the subcommand is required; everything else is a flag. Any operation is expressible as one non-interactive command line (scriptable, replayable, testable).
  2. Wizard mode — invoked automatically when required inputs are missing. The wizard asks stepwise questions and always shows a computed recommendation as the default answer (accept with Enter). Wizards never ask for what they can measure: every step inspects current state first and already-satisfied steps pass silently.

Wizard answers persist into the machine/installation profile, so a later run can replay unattended (--profile). Interrupted wizards resume where they left off (progress manifest). Human-only steps (credential placement, OS permission grants, engine logins) are guided, awaited, then verified — never automated.

Installation

A shell script installs the release and nothing else:

curl -fsSL https://virgo.codes/install.sh | sh   # installs `virgo` (hash release)

The installer puts a release under ~/.local/lib/virgo/<sha> with a current symlink and virgo on PATH. All configuration happens after installation, through virgo onboard — the installer asks nothing.

Top-level command map

CommandPurpose
virgo onboardMake THIS machine part of an installation: first machine (becomes the hub) or join an existing hub
virgo initScaffold the owner's canon (base, classes, people); once per installation
virgo hub start/stop/statusManage hub services (store, transport, registry, web) — hub machine only
virgo agent list/add/remove/repairManage AI engines available on machines (claude/codex/…)
virgo adapter list/add/remove/doctorManage human-channel adapters (teams; later email/slack/telegram)
virgo workspace add/listWork-domain isolation boundaries (implicit default home)
virgo project add/list/onboardWork units (typically repos) inside a workspace
virgo persona add/up/down/list/status/retireResident persona lifecycle
virgo persona checkpoint <name>Land + ingest a live persona's state without stopping it
virgo persona reincarnate <name>Anti-pollution context renewal: checkpoint, clear the session context, re-brief from clean records, resume
virgo persona launch <project>On-demand lead/deputy activation for a project
virgo render [--scan]Render config artifacts (CLAUDE.md, settings, hooks, AGENTS.md, skills, MCP) from canon; --scan reports drift
virgo record sync/status/source addRecord-graph reconciliation and sources
virgo duty list/addScheduled obligations (morning report, liveness alerts, receipts)
virgo skill list/add/edit/approveWork-skill management (shared skills gated by propose-approve)
virgo statusWhole-system view: machines, personas, fences, queues, channels
virgo operator verify-context / promote-completionAttestation and receipt-bearing promotion
virgo screenOwner-specificity scan before any repo publication
virgo machine profileCanonical machine paths/identity (single source for path questions)
virgo channel doctor [<binding>]Diagnose a silent human channel (see CHANNEL-DIAGNOSTICS.md)

Workspace removal stays deliberately absent (gated destructive semantics, to be specified with deletion semantics — not a casual verb).

virgo onboard — machine enrollment wizard

First question (persisted, structural): first machine (this machine becomes the installation's hub) or join existing hub (supply and verify hub address).

First-machine path — steps in order (each: measure → recommend → confirm):

  1. Install location — default recommended (~/.local/lib/virgo, state in ~/.config/virgo); custom allowed, recorded in machine profile.
  2. Machine identity — hostname-derived id recommendation; renameable here and only here (identity is stamped, not guessed later).
  3. AI engines — pick one or more (claude / codex / more later). Per engine the wizard runs the engine's own install+login (human step: guided, awaited, verified). More engines can be added later via virgo agent add.
  4. Adapters — optional now, addable later via virgo adapter add. Currently teams (choose: team channel or personal chatbot); planned: email, slack, telegram. Each adapter runs its own credential wizard (see CHANNEL-ADAPTERS.md) and ends with a verified delivery receipt.
  5. Workspace — default home (implicit, immutable id) auto-accepted; additional workspaces may be added now or later.
  6. Project — default none; may register a first repo now.
  7. First persona — recommended default root/secretary, suggested name the first Virgo-constellation suggestion (star names offered in canonical order; owner free to override). Scope/project/role selectable (see persona grammar below).
  8. Hub start — virgo hub start equivalent; services come up under supervision, acceptance-checked.
  9. Finish — print the web UI URL (hub dashboard) and the one-line summary of what was created. Onboarding ends by handing the owner a working screen, not a wall of text.

Join path: verify hub address + enrollment token, register the machine with the hub registry, install engines (step 3) as needed, done — persona placement on this machine happens later through virgo persona add/up --machine. On completion it prints the hub's web UI link (hub machine remains the root screen).

A legacy/absent profile never guesses: the wizard asks the structural question again and re-stamps the profile (contract v2).

virgo init — owner canon scaffold

Runs once per installation (normally right after the first machine's onboard; re-running is a guided no-op). Scaffolds the owner's private canon: base invariants, class registry (secretary / team-coordinator / project-lead / coder-deputy), people, and the implicit home workspace. Same wizard grammar. The canon lives in the owner's private repo/graph, never inside the product release. (onboard = machine joins the system; init = the OWNER's knowledge base comes into existence. The two names are now disjoint on purpose.)

virgo agent — engines are per-machine capacity, models are bindings

Resolution of the open design questions:

  • An agent = an engine installation on a machine (claude, codex, grok planned). virgo agent add selects engine → runs its install/login on the target machine (--machine, else wizard asks; login is inherently per-machine, which is why agents are machine-scoped records).
  • A model is NOT a separate installable: it is an attribute chosen at binding time. agent add records which models the installed engine offers; personas/roles bind engine+model pairs.
  • Role defaults: each class in the registry declares its recommended engine+model binding (e.g. secretary → claude/fable, coder-deputy → codex/gpt-x). virgo agent add ends with an optional "attach to roles" checklist (pre-checked recommendations); persona add and persona launch read these defaults and may override per persona.
  • agent repair re-runs login/health for an existing agent record (expired logins are a measured state, surfaced by virgo status).

virgo adapter — human channels

adapter add = pick type (teams now; email/slack/telegram planned) → run type-specific credential wizard → bind to a persona address and a direction (team channel vs personal chatbot) → verified end-to-end receipt (a real message round-trip) before the adapter is declared live. adapter doctor wraps CHANNEL-DIAGNOSTICS. Adapter bindings are canon records; the hub renders them into running gateway config (no hand-edited channel files).

virgo persona — lifecycle

persona add [name] --machine <host> --scope <ws> [--project <p>] --role <r> [--character <text>] [--language] [--tone] — creates the first character layer (class template + overlay), suggests constellation names, and defaults the FIRST persona of an installation to root/secretary (with the first constellation-name suggestion). Only the first defaults into the reserved root scope; later personas default to the home workspace, and a bare non-interactive persona add after the first is refused rather than guessed at. --machine records PLACEMENT and starts nothing. It is checked against the installation's machines — the hub's validated device registry — so an unknown machine is refused rather than written, and a CLI that cannot consult a registry refuses to place rather than recording a placement nobody can confirm. Omitting it is fine: an unplaced persona is valid, and the lifecycle refuses it by name later instead of guessing a host. --source-anchor <abs path> overrides that persona's live-tree root; without it the tree is <state_root>/personas/<name>. An anchor requires a machine — a location on no host describes nothing.

virgo persona <up|down> NAME --clear-token PATH [--as PROJECT/AGENT]
              [--operation-id ID] [--recover-operation ID] [--config PATH]
virgo persona checkpoint NAME [--as PROJECT/AGENT] [--operation-id ID] [--config PATH]

up/down restore/checkpoint+stop with acceptance gates (single active copy, fence, CLEAR tokens — see AUDIT-GATED-LIFECYCLE-TOKENS.md); checkpoint lands and ingests without stopping and takes no token, so the gated flags are refused on it by name rather than ignored. Three rules the command line owns:

  • The token is a FILE, never an argument. A CLEAR token on argv sits in the shell history and in every ps for the life of the call. It is read through the same owner-only single-descriptor check as the device token.
  • There is no path flag. Where a persona lives is the hub's answer (see HUB-API.md, Placement); a path here would be one the token binds nothing about.
  • --operation-id is the retry key, and a generated one is PRINTED BEFORE the call, so a command interrupted mid-flight still leaves the caller holding the id its retry must reuse. A new id runs the operation again instead of resuming it.

--as names the seat the call ACTS as. Omitted, it is the installation's configured system seat (root/hub) read from the validated hub API config; an installation without one asks for --as rather than picking a seat. The device token authenticates the machine and never implies an identity.

persona reincarnate NAME [--as PROJECT/AGENT] [--operation-id ID] [--recover-operation ID] prevents context pollution over long residence: checkpoint (land state) → clear the live session context → regenerate the briefing from clean records (deterministic projection first, any narrative compression must cite record hashes) → resume as the same identity. Reincarnation is a scheduled hygiene step at work-unit boundaries, not an incident response; it is the minimum unit of generation lifetime policy.

It takes NO CLEAR token, and that is deliberate: a gate only a human can open is not a hygiene step. Its safety is evidence instead — it drives its own checkpoint and refuses to clear anything unless that checkpoint verifiably landed, so no unsaved work can be lost. The persona's for-life name and seat address never change; its GENERATION advances, which is what fences the session that was just cleared. --recover-operation takes over a reincarnation that cleared a session and never resumed it; that state is stuck, not self-healing, and is never resolved by a timeout or an automatic down.

persona bind NAME --record-source ADDRESS ... states, in PRECEDENCE order, the record sources a persona is briefed from; record source add ADDRESS --governing-document SHA registers a source in canon so a binding can be checked against something real. Canon decides which memories are a persona's — never machine-local config — and an unbound persona refuses to reincarnate by name rather than being briefed from whichever source looks likely. persona launch <project> activates the project's lead/deputy on demand from its class profile. persona status shows fence, host, UUID, health, last-consumed age. persona remove is not a verb; retire preserves lineage (durable, auditable).

Path grammar (unchanged from v0): root reserved scope (secretary/operator only); two segments = workspace-wide (work/coordinator); three = project duty (work/core/lead). The path is the transport address; star names are separate owner-facing metadata.

root/hub is reserved and is NOT a persona address. It is the hub's own structural system seat: canon refuses to register anybody there (a retired row included, so the address never carries a tombstone for somebody who never sat in it), and the address parser answers it as a system address rather than parsing it as a persona. It is the default acting seat for lifecycle calls made on a machine that has one configured.

Skill management

Personal persona skills may be added or edited directly inside that persona's authority. Class and workspace skills are shared behavior: an edit creates a proposed revision that must pass owner/authority approval before activation. Activation writes an owner-private, HMAC-signed root/operator approval record for the exact proposed revision hash, recording approver, receipt, and timestamp; active shared skills revalidate that signed record whenever read, and editing an active shared skill returns it to proposed and clears the prior approval.

Cross-cutting rules (carried, still binding)

  • One release SHA covers CLI, daemons, and web UI assets alike.
  • No owner-specific values in code — machines, workspaces, people, paths come from canon/config/stamp. Public-repo docs are English.
  • Gated verbs refuse without verifiable evidence (CLEAR tokens, context receipts); refusal is loud and machine-readable.
  • Every CLI mutation goes through the Hub API (see HUB-API.md) — the CLI is a client, not a second implementation of the hub's rules.