Command Reference

Contents

The shipped vf surface. See USER_GUIDE.md for a verifiable walkthrough.

Start the UI

npx @magicpro97/vibeflow      # AI-first Home on 127.0.0.1:7799
vf ui                         # same stable default
vf ui --port 0                # explicitly request an OS-selected free port

Starts a local server bound to 127.0.0.1, opens the browser, and serves AI-first Home: the searchable session rail, central conversation pane, details inspector, and trace/capabilities drawers. vf and vf ui both default to 7799; --port 0 is the ephemeral option. A busy fixed port offers an interactive free-port fallback and stops in non-interactive use. Flags: --port <n>, --host <addr>, --no-open.

Any non-loopback --host enables the LAN boundary. The owner browser exchanges a single-use bootstrap URL for an HttpOnly LAN page-session cookie; unauthenticated root loads return 401, and replaying the bootstrap fails. With --no-open, the CLI prints that owner-only URL once instead of persisting it. The authorized page receives a separate CSRF token for legacy API headers/non-conversation SSE; neither page authority authenticates Conversation Home.

Check the environment

vf doctor                # presence/auth check (--probe for a live engine round-trip)
vf doctor --probe        # also run a live "reply READY" round-trip per engine
vf doctor --refresh      # invalidate the readiness cache (60s stable / 5s short TTL) and re-probe
vf doctor --fix          # repair only owned records proved to be orphaned

Readiness results are cached (src/probe-cache.ts): stable probe results live 60s, transient probe-failed results live 5s. vf doctor --refresh discards the cache and re-probes immediately. Engines that fail the probe (presence, auth, or quota) degrade to detection-only per HOOKS_AND_GUARDRAILS.md.

Checks node, git (required) and bun, claude, codex, copilot, agy, docker (optional), plus whether the current directory is a git repo. The “Engine readiness” block reports each engine as ready / no-binary / no-auth / probe-failed. Without --probe it stops at presence/auth; with --probe it actually launches each engine with a trivial prompt and requires it to reply READY (a bounded round-trip that proves auth and a working CLI).

Every canonical owned async launch records the supervisor PID, CLI PID, host, operation, attempt, and exact process-start identity. vf doctor reports each record as active, recovered, or uncertain. vf doctor --fix repairs only an owner whose identity is proved dead or mismatched; a live or identity-unprovable owner remains fail-closed for manual inspection. It never steals a lock or kills a process from PID alone.

Initialize a workflow

vf init                  # TTY: ask intake questions, then generate context
vf init --engine claude  # only one engine's files
vf init --no-ask         # skip the TTY questionnaire
vf init --memory         # force the claude-mem install (skip the prompt)
vf init --no-memory      # skip the claude-mem install (skip the prompt)
vf init --dry-run        # print what would be written

Scans the repo and generates the minimal set: CLAUDE.md, AGENTS.md, .github/copilot-instructions.md, and .vibeflow/* (including a seeded WORKFLOW_STATE.json). PROJECT_CONTEXT.md includes a ## Detected stack section.

Memory (claude-mem): on a TTY, init asks Install claude-mem for spec/plan recall? (Y/n) (default yes). On yes it installs claude-mem (non-interactive) and appends a usage guide to WORKFLOW_POLICY.md. The answer is saved to settings.memory. --memory / --no-memory skip the prompt; a non-TTY run with neither flag skips the step entirely. Toggle the stored setting later with vf config memory.

Readiness gate: a real init runs a live preflight (the same probe as vf doctor --probe) and refuses to create a workflow when no engine is ready. Engines that fail the probe are skipped with a note; files are generated only for the ready ones. --dry-run skips the gate (nothing is written), as does the web intake path.

Dispatch

vf run <claude|codex|copilot|opencode|antigravity>   # write .vibeflow/dispatch/<engine>.md (dry)
vf run <engine> --yes           # launch the engine CLI

Owned launches release their terminal record only after process exit/quiescence and a streams-drained barrier for stdout and stderr. Platform proof differs deliberately:

PlatformOwned scopeProof strengthIdentity / containment contract
Windowswindows-jobkernel-containedA kill-on-close Job Object is installed before receipt/spawn; exact creation time comes from PowerShell/CIM.
Linuxposix-process-groupcooperative-lineageIsolated process group plus boot id and /proc start ticks; a descendant can escape the group.
macOSposix-process-groupcooperative-lineageIsolated process group plus exact libproc seconds/microseconds identity; a descendant can escape the group.

The Windows contract is covered by injected platform regression tests. Live Windows evidence is accepted only from a green, exact-SHA windows-latest smoke job; a local macOS/Linux run is not a Windows canary.

Conversations

ask, chat, and brainstorm all enter the same durable conversation runtime. That runtime owns lifecycle state, FIFO queue order, public trace replay, approvals, and artifact projections. User topics, messages, and engine outputs appear in the public trace only after redaction. Internal role/provider prompt templates, the rendered provider prompt, native engine session identifiers, credentials, environment values, and local paths remain private. The home composer keeps queued user messages in order; only the latest queued human message is editable (ArrowUp in an empty composer; Escape cancels). If dispatch wins the edit race, the draft is preserved for an explicit send-as-new action. Add an agent with Agent or the prepared +@participant command; a typed add-participant request creates a proposal that can promote a direct route into coordinate through proposal/review/commit. Remove one with Remove, the participant-details action, or the prepared -@participant command; if that removes the last executor, the route collapses back to direct. Quote chips are ordered, can move earlier or later, and reactions are restricted to 👍, 👎, ❤️, 🎉, 👀, 🤔, ✅, and ❗. Only a transport-ambiguous request, or a typed admission failure with retryable: true and recovery_action: retry, becomes a retryable queue row. Typed failure retries are user-triggered and reuse the exact request plus idempotency key. An in-flight admission interrupted by browser offline remains Reconciling; after an authoritative refresh Home automatically replays that same idempotency key to settle whether the server already admitted it. A non-retryable collision retains its exact payload as Needs action for typed recovery or confirmed dismissal; it never auto-resends or replaces newer composer state. Rejected rows are current Home UI state, not browser-storage persistence.

Engine capability matrix

The runtime names in this tree are Claude, Codex, Copilot, OpenCode, and Antigravity. The matrix below reflects the exact conversation/runtime behavior enforced by the current adapters.

EngineFresh executionTool/sandbox enforcementExact native resumeOwn-history policyPhase/admission
Claudeyesfullyes (-p -r <session_id>)native history when exact; bounded public replay otherwisePhase 1 built-in read-only yes; phase 2+ yes
Codexyespartial: sandbox yes, rendered tools deniedyes (exec resume <thread_id>)native history when exact; bounded public replay otherwisePhase 1 built-in read-only yes; phase 2+ yes
Copilotyesfullunavailable; no proved by-id pathbounded public replayPhase 1 no; phase 2+ yes when ready/admitted
OpenCodeyesno: conversation launches reject requested tools or sandboxyes (run --session <validated ses_...> --format json)native history when exact; bounded public replay otherwisePhase 1 no; phase 2+ only when the binding does not need tool/sandbox enforcement
Antigravityyesno: conversation launches reject requested tools or sandboxunavailable; no evidenced exact bindingbounded public replayPhase 1 no; phase 2+ only when the binding does not need tool/sandbox enforcement

Exact native resume is the by-id conversation/session path. VF-TURN/1 delivers turns with delivery_mode: "exact-delta" when the runtime can prove the prior public cursor and the interaction cursor; in that case it sends only newly applicable public user messages plus concise peer responses and reactions. It does not re-send the recipient’s own earlier response because that response remains in the CLI’s native session history. When proof is unavailable or supported native reconciliation detects compaction, the runtime revokes exact authority, uses delivery_mode: "full-history", re-sends applicable canonical user/peer context, and includes up to eight of the recipient’s own public responses. Each own-history summary is capped at 2 KiB UTF-8 and carries source digest, provenance, and count/truncation metadata. A fresh/full turn may also carry the content-addressed VF-HANDOFF/1 shared handoff. Exact by-id authority exists only for Claude, Codex, and OpenCode; Copilot and Antigravity fail closed from exact mode instead of silently starting fresh. Phase 1 admits only built-in read-only Claude/Codex bindings; phase 2+ requires a verified engine and live canonical isolation for repo overlays.

Private file ranges are not embedded in that public envelope. They use a separate one-shot VF-PRIVATE-FILE-RANGES/1 canonical JSON payload and are cleared after delivery. Claude, Codex, and OpenCode use stdin; Copilot and Antigravity use native prompt argv. For a large Copilot work-unit prompt, VibeFlow writes .vibeflow/dispatch/<unit>.md and passes a short absolute Read <path> and follow it pointer. This is a transport fallback, not provider history or a resumable session. Antigravity rejects a UTF-8 prompt at or above 30 KiB because its print mode has no supported prompt-file/stdin substitute.

Ask (inline code Q&A)

vf ask src/cli.ts:210-267 "what does this switch do?"   # stream an answer about a snippet
vf ask src/dispatch.ts:172 "why the json output format?" --engine claude
vf ask --conversation conversation-123 "ok, and is that thread-safe?"
vf ask --conversation conversation-123 --resume "keep going"
vf ask --conversation conversation-123 src/x.ts:5-12 "revise it" # persisted conversation

Reads the given line range, frames a prompt (file path + language-fenced snippet + your question), picks the first ready engine (or --engine), and streams the answer straight to your terminal. --conversation routes the ask through a persisted VibeFlow conversation id. --resume is a compatibility flag for question-only follow-ups; it requires --conversation and never targets a native latest-session resume path. Bad target/range, missing file, missing question, or no ready engine exits non-zero with an actionable message. Run vf doctor --probe if none are ready.

ask has no --json mode. Fresh and --conversation asks use the conversation exit table below. Compatibility --resume returns the selected engine process’s exit status unchanged; local target, file, readiness, and unsupported-resume errors retain the legacy ask status 2.

Chat

vf chat "Explain why this function is pure"
vf chat --policy plan --max-rounds 2 "Draft a migration plan"
vf chat --policy coordinate "Implement the scoped change"
vf chat --participant direct@codex --participant direct@claude "Compare implementations"
vf chat --resume conversation-123 "Revise the previous answer"
vf chat --no-baseline --json "Compare without a baseline run"

vf chat is the canonical conversational entry. The coordinator selects a policy unless --policy <direct|coordinate|debate|plan|review|verify|orchestrate> overrides it. Repeat --participant <role@engine[:model]> to bind explicit participants. A typed add-participant request in Home can promote a direct route into coordinate through proposal/review/commit, and removing the last executor collapses it back to direct. --max-rounds <n> must be an integer from 1 through 100. --no-baseline disables the independent debate baseline. --resume <conversation-id> injects the topic into an active conversation or creates a child revision from a completed one. Resume accepts only --resume, --json, and the new message: combining it with create-only --policy, --participant, --max-rounds, or --no-baseline is a validation error rather than a silently ignored option.

With --json, stdout contains exactly one JSON document and no streamed deltas. A new conversation returns:

{"ok":true,"conversation_id":"conversation-123","revision_id":"revision-1","status":"completed","artifact_refs":["artifact-1"],"output":"..."}

A resumed conversation returns conversation_id, nullable child_conversation_id, status, and output. Validation errors are also single documents, for example {"ok":false,"code":"missing_topic"} or {"ok":false,"code":"unknown_flags","flags":["--wat"]}. Runtime failures use the error codes in the exit-code table below.

A plan that pauses for approval returns ok: true, status: "awaiting_approval", its current artifact_refs, and exit 0. Resolve the pending approval in the web workspace or HTTP API; exit 4 remains reserved for a conversation that reaches FAILED. Result and approval artifact_refs are stable public catalog ids, not artifact-download capabilities.

Brainstorm

vf brainstorm "Compare three API designs"                # dry-run only
vf brainstorm --participant brainstorm-participant@codex \
  --participant brainstorm-skeptic@claude "Compare designs"
vf brainstorm --yes --max-rounds 3 "Find the safest rollout plan"
vf brainstorm --resume conversation-123 "Run another round"
vf brainstorm --yes --no-baseline --json "Compare designs"

vf brainstorm is a compatibility facade over the shared debate policy. It requires at least two non-evaluator participants and exactly one evaluator; the runtime adds the evaluator when it is omitted. A new invocation is a read-only dry run unless --yes is present. --resume continues the persisted VibeFlow conversation immediately and therefore does not require --yes.

--max-rounds accepts integers from 1 through 100. With --resume, the create-only --participant, --max-rounds, and --no-baseline flags are rejected; they are never silently ignored. --yes is unnecessary on resume because resume is already an explicit mutation.

--json always writes exactly one document. Dry-run output has exactly these top-level fields: status ("dry_run"), dry_run (true), participants, evaluator_auto_added, engines_available, and models_valid. Executed output has version ("1.0"), conversation_id, terminal status, dry_run (false), rounds, consensus_score, consensus_average, decision_matrix, baseline_comparison, transcript_path, and nullable error. transcript_path, when present, is an authenticated opaque artifact URL built from the ref in the public artifact_created trace event; it is fetchable through the artifact route and is never a filesystem path or a catalog artifact_id. Pre-execution JSON errors use {"status":"error","error":{"error_kind":"validation|engine_start|transport","code":"...","message":"..."}}.

Chat, brainstorm, and persisted-ask exit codes

CodeMeaning
0Dry run, accepted/completed conversation (including explicit awaiting_approval), or a user-stopped conversation
1Invalid flags, participant syntax, topic, range, or other validation input
2An admitted engine could not start or no requested engine was available
3Conversation persistence or transport failed
4The conversation reached FAILED
5The conversation reached ABORTED

Conversation HTTP API and SSE

The web workspace uses the same public DTOs as the CLI:

MethodRouteResult
POST/api/conversations202; conversation id, scoped stream token, expiry, and Location
GET/api/conversations/:id/snapshotCurrent public snapshot
GET/api/conversations/:id/events?stream_token=...&since=NOrdered SSE replay, snapshot barrier, then live events
POST/api/conversations/:id/messages202; accepted message and optional child id/Location
POST/api/conversations/:id/pause, /resume, /stop202; typed lifecycle result
POST/api/conversations/:id/approvals/:approvalId/resolveTyped 202, 404, or 409 result
POST/api/conversations/:id/operations/:operationId/cancelTyped 202, 404, or 409 result
POST/api/conversations/:id/stream-token202; renewed conversation-scoped token
GET/api/conversations/:id/artifacts/:opaqueIdAuthenticated bytes; no client path parameter

Normal JSON and artifact requests require the process-local, HttpOnly, SameSite=Strict vf_conversation_session cookie. Loopback writes additionally require the page’s per-process CSRF token. The SSE endpoint accepts only its 256-bit, single-conversation stream token; it does not accept the session cookie as an SSE credential. Tokens expire after 15 minutes and are renewed through the session-authenticated endpoint. The conversation workspace is intentionally unavailable on every non-loopback bind: LAN page loads receive no conversation session cookie, so conversation routes fail closed with 401 even after LAN page bootstrap.

Resume SSE with either Last-Event-ID: N or ?since=N. If both are supplied they must match. The server replays records with seq > N in ascending order, emits a snapshot at the replay barrier, buffers concurrently arriving records, and then switches to live delivery. Clients must deduplicate by seq. Invalid or conflicting cursors return 400; unknown conversations return 404; lifecycle or route/body conflicts return 409; missing session or stream authority returns 401, and a loopback write with a session but no valid CSRF token returns 403.

Orchestrate

vf orchestrate                         # plan + dispatch work units (dry: prompts only)
vf orchestrate --engine codex          # choose the engine
vf orchestrate --concurrency 4         # bound the parallel pool (default 3)
vf orchestrate --review-engine codex           # optional: reviewer engine (ADR-001)
vf orchestrate --allow-unverified-evidence     # skip evidence format gate (ADR-004 escape hatch)
vf orchestrate --spec-first           # phase 2: generate spec-first tests before dispatch (ADR-002)
                                       # current: flag accepted but no-op until phase 2 wiring
                                       # default: same engine, fresh session, isolated context
vf orchestrate --yes                   # real dispatch via the engine CLI

—auto-pilot require_approval hooks: dispatch independent LLM call to evaluate false positive (confidence ≥ 0.9 → allow, else block). Writes audit entry to .vibeflow/knowledge/hook-audit.log. —yolo Auto-allow ALL require_approval hooks (blind). Audit logged. —allow-all Alias for —yolo. —goal-eval (opt-in, phase 2) Behavioral goal-eval gate: after toolchain passes, an LLM checks whether is covered by the changes. Stub wired in ADR-003; real LLM integration in a future release.

Modes: --yes → CLI, else $VIBEFLOW_AI → bridge, else dry. Dispatches units in parallel, runs an independent reviewer (pass only at confidence 1.0 with evidence), then prints the goal-eval verdict (met | partial | blocked).

When vf orchestrate is used for an explicit multi-participant route, the coordinator is the sole authority and the executor is a different admitted engine. Clarifications route back to the coordinator first; the coordinator resolves ambiguity by checking the task spec, then conversation context, then repo evidence, then a safe default, and asks the user only as a last resort. Exact native session resume stays engine-local; when supported native reconciliation detects compaction or exact proof is unavailable, VibeFlow revokes exact authority and falls back to bounded replay. Bare coordinate routes currently admit Claude and Codex because both can enforce the resolved role sandbox and return authenticated structured coordination output. Copilot, OpenCode, and Antigravity remain available on workflow transports and fail closed for coordinate authority until their adapters can prove both contracts. --isolate keeps each unit in its own linked git worktree so clarification and recovery stay in the same filesystem state. On a proved exact resume, the coordinator sends only fresh user and peer-agent context; after detected compaction or missing exact proof, it also sends bounded public history from the receiving CLI. Executors must commit in their worktree, and the host only fast-forwards a clean, quiescent HEAD after verification; failure and divergence stay preserved for recovery.

Work units (ledger)

vf units status            # board: status, gates, owner, confidence
vf units show <name>       # one unit as JSON
vf units resources         # token / cost / wall-time totals
vf units evidence <name>   # recorded evidence paths

Settings (config)

vf config memory status     # print the current memory setting (default: false/off)
vf config memory builtin    # enable built-in BM25/FTS5 recall (zero deps)
vf config memory claude-mem # enable claude-mem recall (requires claude-mem installed)
vf config memory off        # disable memory recall

vf config env-policy status          # print the env-scrub policy for spawned engines (#556)
vf config env-policy deny 'MY_APP_*' # drop a glob from the env handed to spawned agent CLIs
vf config env-policy allow 'MY_*'    # allowlist a glob (non-empty allow = strict pass-only mode)
vf config env-policy reset           # clear the policy, back to the conservative default

Reads/toggles memory in .vibeflow/SETTINGS.json. Default is false (off). Three modes:

ModeBehaviour
false / offNo recall injected
builtinbun:sqlite FTS5 index of .vibeflow/knowledge/decisions.md; generates .vibeflow/knowledge/memory.db (gitignored)
claude-memShells claude-mem search; requires separate claude-mem install

The setting does not gate the vf init prompt (init always asks on a TTY). It is the switch dispatchPrompt and buildPlanPrompt honour for recall injection.

Skills (demand-driven)

vf skills list             # skills discovered under .vibeflow/.claude/.agents/.github skills dirs
vf skills show <name>      # show detailed info for one skill (owners, changelog, deprecation)
vf skills search <term>    # rank local skills against a task term
vf skills resolve          # derive NEEDS from scan + intake; satisfied vs must-acquire
vf skills telemetry        # print aggregate skill-usage summary from local JSONL telemetry
vf skills validate         # validate every canonical skill against the Anthropic standard
vf skills sync             # sync .vibeflow/skills → engine mirrors (default mode: pointer)
vf skills sync --mode pointer|full   # pointer = stub SKILL.md pointing at canonical; full = copy
vf skills sync --from-registry       # also mirror registry-pinned skills from the lock file
vf skills verify             # promote a canonical skill to status:verified (or --undo)
vf skills verify-sync        # verify each mirror has a SKILL.md for every canonical skill
vf skills verify-sync --from-registry # also verify registry-pinned skills have mirrors
vf skills verify-freshness   # check sourceAnchors against current disk content (SHA-256)
vf skills impact <fact-or-path> # list affected skills and required evals from domain facts
vf skills audit-duplicates # find duplicate fact ownership, triggers, and procedures
vf skills semantic-filter [--max-reviews N] [--reviewer ID]  # find overlapping trigger/fact skill pairs (Jaccard > 0.7 or duplicate owns); reviews execute only when BOTH --reviewer ID and --max-reviews N with N>0 are explicit
vf skills draft [--new] <name> # resolve existing domain before creating a draft
vf skills propose-merge <a> <b> # print non-destructive merged skill proposal
vf skills propose-split <skill> # print non-destructive section split proposal
vf skills crystallize <run-id>  # extract recurring patterns; if they match an existing skill, print PATCH PROPOSAL (stdout only), else write crystallized-run-* draft
vf skills curator scan [--scope=local|repo] [--sync] [--yes]  # local default is private/offline; repo anchors clean HEAD; --sync previews Git-notes sharing; --yes fetches/pushes origin refs/notes/vibeflow-curator
vf skills import <dir>     # import a local skill dir into .vibeflow/skills/
vf skills import context7:<query>  # import a Context7 skill (approval-gated) into the canonical store

Skill registries (git-backed)

vf skills registry add <git-url> --name <id> --ref <tag-or-commit>   # dry-run: show plan, no network
vf skills registry add <git-url> --name <id> --ref <tag-or-commit> --yes  # clone + checkout + pin commit
vf skills registry add obra/superpowers --ref <tag-or-commit> --yes  # owner/repo shorthand → https://github.com/obra/superpowers.git, name defaults to "superpowers"
vf skills registry list                # show pinned registries with commit OID
vf skills registry update              # re-fetch and re-pin all registries (dry-run)
vf skills registry update --yes        # re-fetch and re-pin all registries
vf skills registry update <id> --yes   # re-fetch and re-pin a single registry
vf skills registry install <registry-id>/<skill-name> [--version <v>] [--on-collision skip|replace|rename] [--yes]
                                       # install a verified skill from a cached registry

The owner/repo shorthand expands to https://github.com/owner/repo.git and defaults the registry name to the repo slug; --ref stays required so the pin is deterministic. An explicit git URL is passed through unchanged (supply --name yourself). Adding a source pins its commit in .vibeflow/SKILL_REGISTRY.lock.json; it does not install any skill (installation is separate).

Collision policies:

skip — leave existing untouched (default)

replace — backup existing to .backup//, then overwrite

rename — copy with a new slug, rewrite SKILL.md name: frontmatter

Registries are remote git repos containing skill definitions. The lock file (.vibeflow/SKILL_REGISTRY.lock.json) records each pinned commit so updates are deterministic. On update failure, the prior valid commit is preserved in the lock.

Install native Superpowers plugins

vf superpowers sync            # dry run: inspect installed engine CLIs and exact pinned actions
vf superpowers sync --dry-run  # explicit read-only preview
vf superpowers sync --yes      # install/take over through each engine's native plugin mechanism

The command requires one superpowers registry lock entry for https://github.com/obra/superpowers.git, a full 40-character commit OID, and a matching local registry-cache checkout. Engine eligibility is binary presence only; sync never runs a model probe. Claude, Codex, and OpenCode failures are isolated and reported separately.

--yes persists SUPERPOWERS_DISABLE_TELEMETRY=1 only when the engine has no explicit value, preserves unrelated config (including OpenCode JSON/JSONC semantics), installs the exact locked commit before removing foreign Superpowers selectors, and records a receipt only after native state verification succeeds.

Registry releases

vf skills registry release-propose <registry-id> --from <oid> --to <oid> --version <v> [--changelog <text>] [--dry-run]
vf skills registry release list
vf skills registry release show <proposal-id>
vf skills registry release reject <proposal-id>
vf skills registry release approve <proposal-id> --yes

Proposal creation is local and does not execute targets. Approval is the only fanout execution path. Per-target failures continue; partial failure exits 1.

Security scan on registry install

vf skills registry install runs an optional static security scan (NVIDIA SkillSpector) after frontmatter/path validation, before catalog copy and lock update:

scanner statusgate action
absentinstall proceeds, scan_summary: {scanned:false} in lock
HIGH / CRITICALblocked — exit 1, finding rule_id/message shown, no catalog/lock mutation
MEDIUMwarns, install continues
LOW / NONEpasses

--no-llm is hard-coded: static analysis only, no network egress. See docs/SKILL_SECURITY_SCAN.md for details.

VibeFlow does not pre-install skills. Needs are reported with a suggested on-demand acquisition command. Imported skills start experimental and must be validated + approved before promotion to verified.

Skill acquisition before agent dispatch

vf run and vf orchestrate inspect configured pinned registry caches before agents start. An exact verified match becomes a read-only approval card with name, version, immutable registry commit, and security scan status; proposal creation performs no network call or catalog/lock write. --yes auto-approves installable cards, an interactive TTY asks explicitly, and the Web UI exposes Approve/Reject. HIGH/CRITICAL remains blocked. Approved skills use the normal install pipeline and are scanned again; approval creates no review proof and grants no verified trust. Rejection, ambiguity, missing candidates, and install failure preserve a skill gap and do not cancel agent dispatch. Browser decisions only resolve the waiting run; no direct browser install API exists.

The canonical store is .vibeflow/skills/<name>/ (one SKILL.md plus optional scripts/, references/, assets/), layered on top of the shared machine-wide catalog at ~/.vibeflow/skills/. The four engine mirrors (.claude/skills/, .agents/skills/, .github/skills/, .opencode/skills/) are kept in sync by src/skills/sync.ts: pointer mode writes a stub SKILL.md that points at the canonical file (default; cheap, no duplication); full mode copies the whole skill tree. vf skills verify-sync checks every canonical skill has a matching SKILL.md in every mirror. --from-registry extends both commands to also include skills pinned in the registry lock file (SKILL_REGISTRY.lock.json).

Optional tools (code navigation)

vf tools status                  # enabled/installed/priority per tool + detected languages
vf tools enable <codegraph|lsp>  # turn a tool on and (re)write engine MCP config
vf tools disable <codegraph|lsp> # turn it off and remove its MCP servers
vf tools install <codegraph|lsp> # print the install plan (add --yes to execute)

Two opt-in tools give engines better code navigation, both off by default:

  • codegraph — a 100% local code-graph MCP server (tree-sitter + SQLite), installed via npm i -g @colbymchenry/codegraph.
  • lsp — an MCP↔language-server bridge (mcp-language-server), one server per detected language (TypeScript, Python, Go, Rust).

enable/disable flip the flag in .vibeflow/SETTINGS.json and wire MCP config per engine: merge .mcp.json (Claude), write .codex/config.toml with disabled_tools gating (Codex), and print the exact copilot mcp add commands for you to run (VibeFlow never touches Copilot’s secret config). The priority ladder codegraph > lsp > native is injected into CLAUDE.md/AGENTS.md/copilot-instructions.md, and on Codex the lower-priority LSP tools are structurally disabled when codegraph is on. install only runs commands when you pass --yes; otherwise it just prints the plan. Re-run vf init after changing tools to regenerate the instructions.

Discovery (Context7, approval-gated)

vf discover docs <library>          # prints "approval required"
vf discover docs <library> --yes    # Context7 docs lookup over HTTP
vf discover skills <query> --yes    # Context7 skill search (imports are experimental)

Discovery calls the Context7 HTTP API (https://context7.com/api/v2) with the built-in fetch — no external ctx7 binary is needed. The network is touched only with --yes, every request is bounded by a timeout, and offline/error responses fail gracefully. An optional CONTEXT7_API_KEY env var raises the rate limit (keyless is allowed).

Hooks (guardrails)

vf hooks status     # show core.hooksPath and live guardrail status
vf hooks install    # install fail-closed pre-commit + pre-push; preserve user-owned hooks
vf hooks emit       # write engine configs plus managed git hooks
echo '<json-event>' | vf hook       # → {"decision":"allow|warn|require_approval|block",...}

The generated .githooks/pre-push validates the exact pushed HEAD, derives the pushed range base, then runs vf review check --base <full-SHA>. Missing, stale, malformed, unreadable, or failed evidence blocks with a repair command; docs-only ranges with no applicable checklist need no reviewer record. The hook makes no LLM, network, GitHub API, or Copilot call. git push --no-verify bypasses only this local fast-feedback gate; required remote review-thread-gate remains authoritative. Existing user-owned git hooks are preserved and must be integrated manually.

require_approval in web UI context

When VF_HOOK_MODE=default and .vibeflow/.ui-port exists, require_approval pauses the engine until the user resolves the inline Home approval action. On a LAN bind, the discovery record points hook CLI traffic to a separate loopback-only listener and stores no bearer.

VF_HOOK_MODE env var

Set automatically by vf orchestrate based on flags:

  • default: ask the user through an inline Home action
  • auto-pilot: independent LLM false-positive evaluation
  • yolo: blind allow-all

PR queue & merge

vf pr merge-when-green                 # claim head of queue, poll CI, merge on green
vf pr merge-when-green --head <branch> # target a specific queued branch
vf pr merge-when-green --no-notify     # suppress the desktop notification for this run

Claims the head of the PR queue, polls CI every 30s (up to 5 min), then merges on green, requeues on red, or releases the claim on timeout. Because the poll can run unattended, VibeFlow fires a best-effort OS desktop notification when the poll settles — merged, CI red (requeued), CI timed out, merge failed, or ship-tamper — so you can walk away and get pinged with the outcome (macOS osascript, Linux notify-send; a silent no-op when neither is on PATH).

Suppression precedence (any one silences the ping):

  • --no-notify — suppress for a single run.
  • VF_NO_NOTIFY=1 — env override for a single run (or a whole shell/CI session).
  • Settings → Desktop notifications (notifications in .vibeflow/SETTINGS.json, default true) — the persistent toggle, also editable in the web UI Settings panel.

The notifier is best-effort and never changes the command’s exit code: a missing or failing notifier is swallowed so it can’t break the merge flow.

Verification

vf verify
vf verify --require-review-evidence
vf verify --require-review-evidence --review-base <full-SHA>
vf verify --allow-unverified-evidence  # skip ADR-004 evidence format gate (migration escape hatch)
vf verify --sandbox docker \
  --sandbox-image registry.example/vf@sha256:<digest> \
  --sandbox-volume vf-deps-<lock-sha256>
vf review evidence --base <full-SHA> --result <review-result.json>

Review evidence stays local at .vibeflow/review-evidence/v1/<headSha>.json. vf verify performs no LLM/network call and fails closed by default when current-HEAD review evidence is missing or invalid; --require-review-evidence remains accepted for compatibility. --allow-unverified-evidence does not bypass this gate. --review-base is the full ancestor SHA used only to classify a missing record as a no-checklist range; it never weakens malformed or present-record validation.

Runs typecheck/lint/test (when declared) plus the policy gates: confidence below its risk threshold, missing or unverifiable evidence, a done unit without gates.test: "pass", missing current-HEAD review evidence, and overlapping work-unit scopes all fail.

--sandbox docker runs synchronous CLI toolchain and waiver gates offline in a Docker container. It mounts a disposable source copy, never the active worktree, and passes no host environment. The image must already exist locally and be pinned by OCI digest; vf never pulls/builds it. The named dependency volume must carry label vibeflow.lock-sha256=<SHA-256 of the single supported lockfile>. Missing Docker daemon, image, matching volume, or host UID/GID fails closed without running a host gate. The web verify API and orchestrate per-unit gates remain host-only in this first version.

Eval (Telemetry Success-Rate Gate)

vf eval                                # report only
vf eval --min-pass-rate 0.9            # exit 1 if verdict pass-rate < 90% (enough samples)
vf eval --min-samples 20               # raise the thin-sample floor (default 10)
vf eval --json                         # emit the report as JSON to stdout
vf eval --json --out eval-report.json  # also write the JSON report to a file

vf eval is a passive regression gate: it reads the telemetry vf already writes during normal use — verdict events on the logbus (.vibeflow/logs/current.log, from #542) and verify pass/fail entries in .vibeflow/knowledge/log.md — and aggregates a real success-rate, gate-failure breakdown, average goal score, and cost/token totals. No LLM, no network, no fixtures to maintain: it measures whether vf is doing well on the tasks you actually ran, not a fixed benchmark.

With a threshold (--min-pass-rate, or eval.minPassRate in .vibeflow/SETTINGS.json) it becomes a one-job-two-outcomes gate you can wire into pre-push/CI:

  • exit 0 — pass-rate at/above the threshold, no threshold set, or too few samples (below --min-samples it warns instead of failing, so a handful of hard tasks never trips a false regression).
  • exit 1 — pass-rate below the threshold with enough samples.

Empty telemetry prints a friendly note and exits 0.

Help / Version

vf help
vf --version

Related: User Guide · npm CLI Design Edit this page on GitHub