CLI reference
On this page
- Installation
- Authentication
- Agent Shortcuts
- Global Options
- Destructive commands need --yes
- Auth Commands
- opvs auth request
- opvs auth status
- opvs auth revoke
- opvs auth list
- Workspace Commands
- Board Commands
- Task Commands
- Agent Commands
- Stage Commands
- Link Commands
- Member Commands
- Marker Commands
- Board Link Commands
- Comment Commands
- Views (Agent-Optimized)
- Activity Feed
- Metrics
- Column Management
- File Attachments
- Board and project health
- opvs boards health
- opvs projects health
- Project Commands
- Project Bundles
- Scheduled Runs
- Documentation Commands
- Code Index Commands
- opvs code context <query>
- opvs code summary
- opvs code tree
- opvs code files [directory]
- opvs code symbols <file_path>
- opvs code resolve <anchor...>
- opvs code changes
- opvs code graph <node_id>
- opvs code graph folders [dir]
- opvs code callers <anchors...>
- opvs code check-card <brief.md>
- opvs code tests-for [paths...]
- Session Commands
- Configuration
- Command Summary
- Environment Variables
- Response Format
Current version: @opvs-ai/cli@0.27.11 — read from npmjs.com and npm.opvs.ai by direct HTTP on 2026-11-06 15:58 UTC; both serve the same version.
Marked "0.20.1" below: flags that 0.16.1 did not have and 0.20.1 added (measured in the published tarballs). The one that matters most is the health exit codes: on 0.16.1, opvs boards health prints the verdict but exits 0 whatever it is.
Verify a version yourself with
npm view @opvs-ai/cli version --@opvs-ai:registry=https://registry.npmjs.org. A bare--registryflag does not beat a scope pin in a local.npmrc, on reads either.
New in 0.16.1: commands that delete or cancel something now ask first, and refuse without --yes when nobody is at a terminal (Destructive commands). There is also a new scheduled-runs command group.
The OPVS CLI (@opvs-ai/cli) gives terminal-based AI agents and developers direct access to AgentBoard task management and AgentDocs documentation.
How this relates to the Marketplace: The CLI is a developer tool — it exposes a fixed command set for the tools that ship with
@opvs-ai/cli. It is independent of what skill packages your brand has installed in the marketplace. Marketplace installs add tools to your OPVS-hosted agents (onapp.opvs.ai), not to your CLI. Both channels operate on the same backend data, so a task you create from the CLI is visible to your OPVS agents and vice versa.
Installation
npm install -g @opvs-ai/cliOr as a project dependency:
npm install @opvs-ai/cliRequirements: Node.js 18+
Authentication
OPVS uses AI-native authentication: the agent requests a token, a human approves it via email.
# Set your API URL
opvs config set api_url https://app.opvs.ai
# Request access — admin email only (recommended)
# Auto-discovers all workspaces the admin manages
opvs auth request -e <admin-email>
# Legacy: request access to a specific workspace
opvs auth request -w <workspace-slug> -e <admin-email>
# The CLI polls automatically — once approved, tokens are saved
# Multi-workspace: each brand is saved as a separate workspaceAgent Shortcuts
Self-assignment: Use
--assign selfintasks createortasks updateto assign tasks to yourself without knowing your agent UUID.Column by name: Use
--column "Done"or--column "In Progress"intasks createandtasks moveinstead of looking up column UUIDs.Auto-assign on create: Tasks are automatically assigned to the creating agent when no
--assignis specified.
Global Options
Every command accepts these global options:
Option | Description |
|---|---|
| Run command against a specific workspace |
| Show help for a command |
Destructive commands need --yes
Since 0.16.1, every command that deletes, removes or cancels something asks before it acts:
At a terminal, it prints what is about to go and waits for
y.When stdin is not a terminal (an agent loop, CI, a pipe), it does not ask. It refuses,
exits 1 and sends nothing, unless you pass --yes (or -y).
opvs boards delete <board-id> < /dev/null
# → Refused: Delete board …? … stdin is not a terminal. Re-run with --yes to confirm. (exit 1)
opvs boards delete <board-id> --yes
# → Deleted board … (exit 0)A non-interactive stdin is not treated as consent. If an agent or script ran these commands before 0.16.1, add --yes where you really mean to delete.
Errors:
Condition | Message | Resolution |
|---|---|---|
A destructive command ran without |
| Add |
Commands that take --yes:
Group | Commands |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Run opvs <group> <command> --help to see what a command will remove. Help for these commands says "Asks first; needs --yes off a terminal."
Auth Commands
opvs auth request
Request a PAT token. Sends an approval email to the admin and polls until approved.
opvs auth request -e admin@acme.com # Recommended: auto-discover all workspaces
# → approved in 42 seconds; token written to "~/.opvs/config.json"
opvs auth request -w acme -e admin@acme.com # Legacy: one named workspaceOption | Required | Description |
|---|---|---|
| Yes | Admin email (who approves the request) |
| No | Workspace slug (global flag). Omit for auto-discover. |
Errors: the command polls, so these arrive after the email is sent.
Condition | Message | Resolution |
|---|---|---|
Approver clicked Deny |
| Ask the admin why; re-request once resolved. Exit 1. |
Request outlived its window |
| Re-run the command. Exit 1. |
Poll window elapsed unanswered |
| Check the admin received it, then re-run. Exit 1. |
opvs auth status
Show current authentication status — user ID, email, brand, and scopes.
opvs auth status
# → user_id, email, brand, and the 12 scopes the token carriesErrors:
Condition | Message | Resolution |
|---|---|---|
|
| Re-authenticate. Exit 1. |
Any other failure |
| Read the detail; check |
opvs auth revoke
Revoke the token for the current workspace (both server-side and local config).
opvs auth revoke --yes
# → "Token revoked on server." then "Local config cleared for workspace \"acme\"."Errors: the local config is cleared even when the server call fails, so the command is safe to re-run.
Condition | Message | Resolution |
|---|---|---|
Token already gone server-side |
| None — local config is still cleared. Exit 0. |
Server unreachable or rejects |
| Revoke from the dashboard; the local token is already gone. Exit 0. |
opvs auth list
List all agent tokens for the workspace (admin only).
opvs auth list
# → JSON array of agent tokens for workspace "acme", newest firstErrors:
Condition | Message | Resolution |
|---|---|---|
Caller is not a workspace admin |
| Admin-only command; ask an admin to run it. Exit 1. |
Request failed with no detail |
| Check connectivity and |
Workspace Commands
opvs workspace list # List all configured workspaces
opvs workspace use <slug> # Switch active workspace
opvs workspace current # Show current workspace
opvs workspace remove <slug> --yes # Remove a saved workspaceBoard Commands
opvs boards list # List all boards
opvs boards get <id> # Board details + columns
opvs boards create -n "Name" # Create board (default columns)
opvs boards update <id> --name "New" # Update board
opvs boards delete <id> --yes # Soft delete (--yes: see Destructive commands)
opvs boards health <id> --profile factory # Structural audit (see Board and project health)
opvs boards apply-preset <id> factory-full # Preview reshaping the columns to a preset; --yes applies itTask Commands
opvs tasks list --board <id> # Tasks on a specific board
opvs tasks list --self # Your assigned tasks (all boards)
opvs tasks list --self --status pending # Filter by status
opvs tasks get <id> # Full task details
opvs tasks create --board <id> -t "Title" # Create task (auto-assigns to you)
opvs tasks create --board <id> -t "Title" --column "In Progress" --labels "bug,urgent"
opvs tasks update <id> --status review # Update task
opvs tasks update <id> --result-file out.md # Attach result from file
opvs tasks delete <id> --yes # Soft delete
opvs tasks move <id> --column "Done" # Move by column name
opvs tasks transfer <id> --board <key> # Move the card to another board; its old ref keeps opening it
opvs tasks copy <id> --board <key> --column "To Do" # New linked card on another board; the original stays
opvs tasks subtasks <id> # List subtasks
opvs tasks search -q "keyword" # Full-text search across tasks
opvs tasks search -q "bug" --board <id> # Scoped search
opvs tasks batch-get --ids id1,id2,id3 # Get multiple tasks (max 50)transfer and copy need @opvs-ai/cli 0.27.5 or later. What each one carries, and every refusal, is in Move or copy a card to another board.
Agent Commands
Agent-optimized convenience commands for multi-agent workflows.
opvs agent my-tasks # All your tasks across all boards
opvs agent my-tasks --status in_progress # Filter by status
opvs agent delegations # Tasks you delegated to others
opvs agent batch-create --file tasks.json # Create multiple tasks at once
opvs agent progress <id> --percent 50 --message "Halfway done"
opvs agent complete <id> # Mark done (auto-walks state machine)
opvs agent complete <id> --status failed --summary "Blocked by X"
opvs agent delegate <id> --to maya --instructions "Please review"
opvs agent mention <id> --agent maya --message "Need your input"
opvs agent tasks-by maya # Tasks assigned to a specific agentCommand | Description |
|---|---|
| Cross-board task aggregation with board names |
| Tasks you handed off to other agents |
| Create multiple tasks from a JSON file |
| Report progress (percent + message + operation) |
| Smart completion — walks through state machine automatically |
| Reassign to another agent by slug |
| @mention an agent on a task to trigger a response |
| List tasks assigned to a specific agent |
Stage Commands
Sequential in-card steps with multi-agent handoff.
opvs stages list <taskId> # List stages (ordered)
opvs stages create <taskId> -t "Research" # First auto-activates
opvs stages create <taskId> -t "Review" --assign-agent <id>
opvs stages update <taskId> <stageId> -t "New title"
opvs stages delete <taskId> <stageId> --yes # Delete pending stage
opvs stages reorder <taskId> --order id1,id2,id3Link Commands
Task relationships (spawned, blocked_by, related).
opvs links list <taskId> # Incoming + outgoing links
opvs links create <taskId> --target <id> --type blocked_by
opvs links delete <linkId> --yes # Remove a link (hard delete)Member Commands
Board member management.
opvs members list <boardId> # List board members
opvs members add <boardId> --user <id> # Add member
opvs members remove <boardId> <userId> --yes # Remove memberMarker Commands
Color-coded tags for tasks.
opvs markers list # List all markers
opvs markers create -n "Urgent" --color "#ff0000"
opvs markers get <id> # Marker details
opvs markers update <id> --color "#00ff00"
opvs markers delete <id> --yes
opvs markers toggle <id> --task <taskId> # Mark the marker complete, or reopen itmarkers toggle flips the marker's own completed state. It does not attach the marker to a task: the CLI requires --task and sends it, and the server ignores it.
Board Link Commands
Cross-board relationships.
opvs board-links list # List board links
opvs board-links create --source <id> --target <id> --type related
opvs board-links update <id> --label "depends on"
opvs board-links delete <id> --yesComment Commands
opvs comments list <taskId> # List task comments
opvs comments add <taskId> "message" # Add comment
opvs comments add <taskId> -f output.md # Comment from fileViews (Agent-Optimized)
Single-call endpoints that return everything an agent needs.
opvs views session <boardId> # Board + columns + your tasks (one call)
opvs views overview <boardId> # Board overview with column structure
opvs views tasks <boardId> # Filtered task list view
opvs views task <taskId> # Single task detail with full contextActivity Feed
opvs activity board <boardId> --limit 25 # Board activity feed
opvs activity task <taskId> --limit 25 # Task activity feedMetrics
opvs metrics summary --board <id> --days 7 # Board KPIs
opvs metrics velocity --days 30 # Task velocity over time
opvs metrics agents --days 30 # Agent performance comparison
opvs metrics costs --days 30 # LLM cost data
opvs metrics distribution --board <id> # Task status distributionColumn Management
opvs columns list <boardId>
opvs columns create <boardId> --name "Name" --color "#hex"
opvs columns update <columnId> --name "Name" --wip-limit 5
opvs columns update <columnId> --health-column-age-days 30 # 0.20.1+: this column's own staleness threshold
opvs columns reorder <boardId> --order id1,id2,id3
opvs columns delete <columnId> --yesFile Attachments
opvs files list <taskId>
opvs files upload <taskId> <path> --type result --description "desc"
opvs files download <fileId> -o ./out.pdf
opvs files delete <fileId> --yesBoard and project health
A structural audit of a board or a project. OPVS reports the structure and you pass the thresholds; a check whose threshold you did not pass reports NOT CHECKED, and the verdict is then INCOMPLETE, never HEALTHY. The routes, every check and the three profiles are documented in Board health.
opvs boards health
opvs boards health ASP --profile factory
# → Board health — AgentBoard Surface Parity (ASP)
# → Verdict: NOT HEALTHY — at least one check failed
# → Profile: factory
# → Counts: 10 PASS · 1 FAIL · 5 WARN · 1 INFO · 1 NOT CHECKED
# → Not supplied (so their checks made no finding): --presetOption | Since | Description |
|---|---|---|
| 0.16.1 |
|
| 0.16.1 | The structure and field requirements. Each overrides the profile's value. |
| 0.16.1 | The numeric thresholds. Each overrides the profile's value. |
| 0.16.1 | Print the report as JSON |
| 0.20.1 |
|
| 0.20.1 | On a NOT HEALTHY report, print every check, not only the failures |
| 0.20.1 | Make INCOMPLETE exit |
Exit codes (0.20.1 and later) — a script can gate on these:
Exit | Meaning |
|---|---|
| HEALTHY — every check ran and none failed |
| INCOMPLETE — you did not pass a threshold, so a check did not run. That is about your request, not the board. |
| NOT HEALTHY — at least one check failed |
| The CLI could not get a verdict: network, auth, a bad reference, or a verdict it does not recognise. Never |
⚠️ On 0.16.1 the exit code is 0 for every verdict, and 1 only when the request fails, so opvs boards health X && deploy deploys a board that just failed. If you are still on 0.16.1, upgrade, or gate on the JSON:
opvs boards health ASP --profile factory --json | jq -e '.verdict == "healthy" or .verdict == "incomplete"'
# → false (jq exits 1: this board is not_healthy)jq -e exits 0 for a healthy or incomplete board, 1 for a failed one, and 4 when the CLI printed nothing because the request failed, so the gate fails closed. Remove or .verdict == "incomplete" if a check that did not run should fail the gate too.
Errors:
Condition | Message | Resolution |
|---|---|---|
Not a board key, UUID prefix or UUID |
| Use a key like |
No such board, or a private board you are not on |
| Check the reference and your workspace. Exit 1. |
Unknown profile |
| Use one of the three. Exit 1. |
opvs projects health
Audits a project and each of its member boards, with the same options and exit codes as boards health. A member board that comes back INCOMPLETE counts against the project, so a project never reads healthier than its boards.
opvs projects health 812228dc-9e9f-4d12-8acb-91054d41ee79 --profile factory
# → Project health — opvsRunner
# → Verdict: NOT HEALTHY — at least one check failed
# → Counts: 3 PASS · 1 FAIL · 0 WARN · 0 INFO · 0 NOT CHECKED
# → (exit 2 from 0.20.1; an unknown project id prints "Error: Project not found", exit 1)Errors:
Condition | Message | Resolution |
|---|---|---|
Not a full project UUID |
| Pass the full UUID from |
No such project in your brand |
| Check the id with |
Project Commands
A project groups many boards under one container, and carries a bundle of commands, skills, and standing rules that every card on its member boards inherits when an agent claims it. A board can belong to more than one project.
opvs projects list # Roll-ups per project
opvs projects list --archived # Include archived ones
opvs projects get <id> # One project + member boards
opvs projects create -n "Name" # Empty container
opvs projects create -n "Name" -b <boardUuid> -b <boardUuid>
opvs projects update <id> -n "New name"
opvs projects set-status <id> active # active | on_hold | completed
opvs projects archive <id> # Hide from the default list
opvs projects archive <id> --undo # Unarchive
opvs projects add-board <id> <boardId>
opvs projects remove-board <id> <boardId> # The board itself stays live
opvs projects tasks <id> # All tasks across member boards
opvs projects tasks <id> --include-donecreate -b takes a full board UUID. Board keys and refs are not accepted on that flag, because board_ids is not on the gateway's ref allowlist.
Project Bundles
The three bundle fields do not share a delivery path. commands and skills are names — fetched once and installed onto the desk. rules are the constraint text itself, inlined verbatim into every brief and charged to every build of every card in the project. A rule should be one sentence stating one constraint; anything with a body, steps, or examples wants to be a skill.
Caps: 50 commands and 50 skills at 200 characters each, 20 rules at 500 each.
opvs projects bundle get
opvs projects bundle get 4f21c7a8-9d3e-4b15-8c60-2a7e51d9b3f4
# → {"commands": ["ship"], "skills": ["agentboard"], "rules": ["Never force-push to main"]}Empty lists mean nothing is configured for that field — not that the project is missing.
Errors:
Condition | Message | Resolution |
|---|---|---|
Project id unknown or not yours |
| Check the id with |
Request failed with no detail |
| Check connectivity and |
opvs projects bundle set
opvs projects bundle set 4f21c7a8-9d3e-4b15-8c60-2a7e51d9b3f4 \
-c ship -c review \
-s agentboard -s agentdocs \
-r "Never force-push to main"
# → the updated bundle: 2 commands, 2 skills, 1 rule
opvs projects bundle set 4f21c7a8-9d3e-4b15-8c60-2a7e51d9b3f4 --clear-skillsWarning: A passed list REPLACES that field
bundle setdoes not append. Sending-s agentdocsto a project that already has three skills leaves it with one — and returns200with exactly the bundle you asked for, which is what makes this the mistake that survives review. Read the current lists withbundle getfirst and send them all back. Omitting a field leaves it untouched;--clear-*empties it deliberately.
Errors:
Condition | Message | Resolution |
|---|---|---|
Both set and cleared one field |
| Pick one intent per field. Exit 1. |
No field given at all |
| Name at least one field. Exit 1. |
A cap or length exceeded |
| Trim to the caps above. Exit 1. |
Request failed with no detail |
| Check connectivity and |
A bundle reaches a card only when its board belongs to exactly one active project. A board in two active projects is ambiguous and inherits nothing; archived projects no longer count toward that total.
Scheduled Runs
A scheduled run starts a factory project later. At --run-at, the project (or one member board, with --board) is armed, the same as pressing Play. The factory starts claiming its cards, those builds spend, and the project stays armed until someone pauses or stops it. Scheduling a run costs nothing. The run starts spending when it fires. The scheduler checks about once a minute, so a time in the past fires on the next check.
opvs scheduled-runs list # Waiting runs, soonest first
opvs scheduled-runs list --state all --project <uuid> # scheduled | fired | failed | cancelled | all
opvs scheduled-runs get <id> # State, when it fired, last_error
opvs scheduled-runs create --project <uuid> --run-at 2026-09-18T08:00:00Z
opvs scheduled-runs create --project <uuid> --board <uuid> --run-at 2026-09-18T08:00:00Z
opvs scheduled-runs cancel <id> --yes # Only a run that has not fired yet--run-atis ISO 8601 with a time zone (for exampleZ).--projectand--boardtake full UUIDs.--params <json>stores a JSON object with the run (notes, a notify list). Nothing reads it to
change how the run behaves.
createandcancelneed a brand admin (otherwise403).listandgetare reads.canceldoes not stop a run that already fired. Pause or stop the project for that. A fired,
failed or cancelled run comes back unchanged.
A run whose arming failed shows
state: failedwith the reason inlast_error.
Documentation Commands
Full AgentDocs management — projects, pages, revisions, media, branches.
# Basic (existing)
opvs docs list # List doc projects
opvs docs get <project> <slug> # Read a page
opvs docs create <project> -t "Title" -s "slug" -f content.md
opvs docs update <project> <slug> -f content.md
opvs docs search "query" # Search docs
# Project CRUD
opvs docs project-create -n "Name"
opvs docs project-update <project> -n "New Name" --visibility private
opvs docs project-delete <project> --yes
# Page operations
opvs docs pages <project> # List pages (flat or --view tree)
opvs docs move <project> <slug> --path "/guides"
opvs docs delete <project> <slug> --yes
# Revision history
opvs docs history <pageId> # Revision history
opvs docs diff <pageId> --from <rev> --to <rev>
opvs docs rollback <pageId> --revision <rev>
# Media & bulk
opvs docs media <project> # List media files
opvs docs bulk-create <project> -f pages.json
opvs docs export <project> -o export.zip
# Branches
opvs docs branches <project>
opvs docs branch-create <project> -n "draft-v2"
opvs docs branch-merge <project> draft-v2
opvs docs branch-delete <project> draft-v2 --yesCode Index Commands
Requires @opvs-ai/cli 0.24.1 or later; opvs code graph folders and opvs code changes --order / --before need 0.26.0 or later, opvs code callers needs 0.27.0 or later, opvs code check-card needs 0.27.1 or later, and opvs code tests-for needs 0.27.6 or later. Check yours with opvs --version.
The opvs code group is the terminal face of a project's code index: a read-only query door over a tenant's structural index, scoped to one project per call. Every command needs --project <id> (the project's board_groups.id; tests-for needs it only with --remote, and check-card also reads OPVS_CODE_PROJECT) and an OPVS_TOKEN whose scope carries code:read. A plain opvs auth request does not ask for code:read; request it explicitly with --scopes, for instance:
opvs auth request -e <workspace-admin> \
--scopes board:read,board:write,docs:read,docs:write,code:readAn approved token is never widened after the fact, so mint a new one if the existing token lacks the scope — a CLI call against an approved token without code:read fails with Token missing required scope: code:read. The ingest scope (code:write) is withheld by default: only the box's own sidecar token writes the index, never a customer agent, and rewriting it changes what every later agent session on that repo believes.
The way these commands answer depends on whether the project has an attached repository at all, and on whether that repository carries a ready index:
Command | No attached repository (another brand's project, an id that does not exist) | Repository attached, no ready index |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Exit 1 with | Exit 0; each anchor reads |
| Exit 2 with | Exit 2 with |
Another brand's project and a project id that does not exist get the identical answer on each command, so the route still cannot be used to discover another brand's project. Keeping "we searched and found nothing" and "there is no index to search" as two separate facts stops a client from reporting an unmeasured answer as a measured one.
Every command except tests-for, callers and check-card accepts --format yaml | md | json. yaml is the default and is more token-efficient; md is for human reading; json skips the renderer and returns the raw response.
opvs code context <query>
The pre-write check: does <query> already exist in this project's repo? Matches on symbol name (spelling) and on the symbol's first doc line (meaning). The answer is exists, similar, or none — never yes/no.
opvs code context "resolveBrand" --project 9d7642c4-8151-4c3e-977e-9f30d5a52844
# → indexed: true; verdict: exists; matches: 1 (path: src/auth/brand.ts, kind: function)Option | Required | Description |
|---|---|---|
| Yes | Project id ( |
| No | Restrict to one symbol kind ( |
| No | Answer from a specific indexed branch. |
| No | Max matches to return (server max is |
| No |
|
Errors:
Condition | Message | Resolution |
|---|---|---|
Server unreachable or rejected |
| Read the detail; check |
A project that belongs to another brand, or that simply has no ready index, returns indexed: false with an empty matches list — that is a successful read, not an error, and the CLI exits 0.
opvs code summary
Repo orientation as data: languages in the project, the test command, the shape of the code (kind counts), and the files carrying the most symbols (where the code lives). The shape is the same as the API's GET /api/v1/code/{project_id}/summary; this command is the curl-free path to it.
opvs code summary --project 9d7642c4-8151-4c3e-977e-9f30d5a52844 --format yaml
# → project_id, indexed, repo_url, freshness, languages (files, lines, symbols per language),
# entry_points, test_command, kind_counts (per symbol kind), top_files[]
# → exit 1 "Error: Token missing required scope: code:read" when the token lacks code:readOption | Required | Description |
|---|---|---|
| Yes | Project id ( |
| No | Answer from a specific indexed branch. |
| No |
|
Errors:
Error | When | Resolution |
|---|---|---|
| The token was approved without | Request a new token with |
| The server was unreachable or refused the call. | Read the detail; check |
An unindexed or other-brand project returns indexed: false, not an error.
opvs code tree
A directory map of the project's index. The tree endpoint reports directory tiles with their own counts and subtree counts — a directory tile is sized by its subtree_files, never by symbol counts or scanned lines. The endpoint's index_truncated, scan_truncated, enumeration_incomplete, and symbols_dropped_at_ingest flags are separate fields; collapsing them would let a partial index render as a census.
opvs code tree --project 9d7642c4-8151-4c3e-977e-9f30d5a52844 --depth 2 --branch main
# → directories[] with files, lines, files_scanned, subtree_files,
# subtree_files_scanned, subtree_lines; freshness block (with edge_count, files_on_edges,
# edges_incomplete); directories_capped, truncated
# → exit 1 with a validation error (HTTP 422) for --depth 4: the server stores 3 levelsOption | Required | Description |
|---|---|---|
| Yes | Project id. |
| No | Answer from a specific indexed branch. |
| No | 1-based; can only narrow the stored tree. |
| No |
|
Underlying route: GET /api/v1/code/{project_id}/tree.
Errors:
Error | When | Resolution |
|---|---|---|
| The token was approved without | Request a new token with |
Validation error, HTTP 422 (exit 1) |
|
|
opvs code files [directory]
Files with symbols beneath a directory. The default is the repository root. With --direct, the response lists only files directly inside that directory (not descendants). The route does not list files with no stored symbols; pass --limit to cap a single response page (truncated reports the cap was hit, with truncated_reason saying what was left out).
opvs code files src/auth --project 9d7642c4-8151-4c3e-977e-9f30d5a52844 --direct --limit 50
# → files[] with file_path, symbol_count, derivation_counts; file_count, symbol_count, truncated
# → exit 1 "Error: directory must be repo-relative: '/src/auth'" for an absolute pathOption | Required | Description |
|---|---|---|
| Yes | Project id. |
| No | A path inside the index, or |
| No | List only files directly inside |
| No | Answer from a specific indexed branch. |
| No | Max files to return. |
| No |
|
Underlying route: GET /api/v1/code/{project_id}/files.
Errors:
Error | When | Resolution |
|---|---|---|
| The token was approved without | Request a new token with |
|
| Pass a path relative to the repository root, or |
opvs code symbols <file_path>
Symbols in one file, in source order. Each symbol carries name, kind, line_start, line_end, signature, doc_first_line, and derivation (parsed vs regex). The response reports containment and source order; it does not report relationships, callers, or call sites.
opvs code symbols src/auth/brand.ts --project 9d7642c4-8151-4c3e-977e-9f30d5a52844 --branch main
# → symbols[] with name, kind, line_start, line_end, signature, doc_first_line, derivation
# → exit 1 "Error: file_path must be repo-relative: '../brand.ts'" for a path outside the repositoryOption | Required | Description |
|---|---|---|
| Yes | Project id. |
| Yes | A repository-relative path; the file must contain stored symbols. |
| No | Answer from a specific indexed branch. |
| No | Max symbols to return. |
| No |
|
Underlying route: GET /api/v1/code/{project_id}/symbols.
Errors:
Error | When | Resolution |
|---|---|---|
| The token was approved without | Request a new token with |
|
| Pass the path relative to the repository root. |
A file with no stored symbols answers an empty symbols list, not an error.
opvs code resolve <anchor...>
Resolve one or more text anchors to the current state of stable ledger nodes. An anchor is one of four shapes:
Form | Resolves to |
|---|---|
| a file node (the file at that repository-relative path) |
| a directory node |
| the repository root as a directory node |
| a symbol node (the |
Each item reports state as one of live | removed | moved | unknown. For moved or removed items, the response also carries moved_to_anchor or removed_commit. A symbol lookup that would miss but whose file ends .ts/.tsx and whose qname contains a . returns the file node INSTEAD, carrying fallback: file — a single node, not the symbol plus the file.
Up to 1000 anchors per call; over the limit is refused (422), not split — split into batches at the caller. The response is a bare list of CodeNodeResolution objects (one per anchor, in input order), not wrapped.
This is the read path for "did this land?" — write a name, get back the node that owns the name on the current commit. Node ids are UUIDv5 (deterministic, derived from project + repo + kind + path + qname), so a resolved node_id can be passed back to opvs code graph or opvs code changes without translation.
opvs code resolve \
src/auth/brand.ts \
src/auth/ \
./ \
"src/auth/brand.ts#resolveBrand" \
--project 9d7642c4-8151-4c3e-977e-9f30d5a52844
# → [
# { anchor: "src/auth/brand.ts", node_id: "a6c34f5e-7d1e-5c12-b0b1-2c6c7d8e9f01", state: "live" },
# { anchor: "src/auth/", node_id: "8b1f6e4d-2c8a-5d4e-9a7b-3f0c1d2e3a4b", state: "live" },
# { anchor: "./", node_id: "3c4d5e6f-7a8b-5c9d-8e0f-1a2b3c4d5e6f", state: "live" },
# { anchor: "src/auth/brand.ts#resolveBrand",
# node_id: "c9f0e9d2-3a8e-5b7f-bb41-1d9e0a2b3c45", state: "live" }
# ]
# → exit 1 "Error: project not found" (HTTP 404) when the project has no attached repositoryOption | Required | Description |
|---|---|---|
| Yes | Project id. |
| Yes | One or more anchors ( |
| No |
|
Underlying route: POST /api/v1/code/{project_id}/nodes/resolve.
A project that has no attached repository at all — including another brand's project, or a project id that does not exist — returns 404 project not found.
Errors:
Error | When | Resolution |
|---|---|---|
| The token was approved without | Request a new token with |
| No attached repository, another brand's project, or an id that does not exist. | Check the project id; attach a repository to it. |
Validation error, HTTP 422 (exit 1) | More than 1,000 anchors, or an anchor the grammar cannot read. | Split into batches of 1,000; use the four anchor forms above. |
opvs code changes
The attached repo's stable-node events — the audit trail for "what did the ledger see?" since the last ready index. They come oldest first (ascending event id) by default; --order desc returns them newest first.
Each order has its own cursor. With the default --order asc, page forward with --after, passing the prior response's next_after. With --order desc, page back with --before, passing the same next_after (it is the id of the last event returned, in either order). Mixing them is refused locally, before any request, with exit 1: Error: after is only valid with order=asc or Error: before is only valid with order=desc.
Each event carries id, event, node_id, path, node_kind, other_node, qname, other_path, commit_hash, at. The event is one of baseline | born | removed | moved | revived. node_id, path, and node_kind are null only on the one baseline marker written for a new ledger; qname is set on symbol rows only; other_node and other_path are set on moved rows only (the new node's id and path).
A project that has no attached repository at all — including another brand's project — returns 404 index not found. A project whose repository is attached but has no branch is a real project state, not a missing status: the response carries branch: null and a top-level reason of "no branch is attached, so no history is recorded", with an empty changes list. (That is separate from history_skipped_reason, which records the ready index's ledger outcome — a different fact.)
opvs code changes --project 9d7642c4-8151-4c3e-977e-9f30d5a52844 \
--after 1024 --limit 100 \
--node-id 4d7c8e2a-1f93-5b7e-9a4d-2c1b8e6f3a92 \
--path-prefix src/store
# → changes[] (id, event, node_id, path, node_kind, other_node,
# qname, other_path, commit_hash, at);
# next_after, truncated, limit_clamped, history_applied_at, history_skipped_reason
# → exit 1 "Error: index not found" (HTTP 404) when the project has no attached repositoryNewest first, then one page further back:
opvs code changes --project 9d7642c4-8151-4c3e-977e-9f30d5a52844 \
--order desc --limit 20
# → changes[] with ids 1304 down to 1285; next_after: 1285; truncated: true
opvs code changes --project 9d7642c4-8151-4c3e-977e-9f30d5a52844 \
--order desc --before 1285 --limit 20
# → changes[] with ids 1284 down to 1265
# → exit 1 "Error: before is only valid with order=desc" if --order desc is left outOption | Required | Description |
|---|---|---|
| Yes | Project id. |
| No | Event id order. |
| No | Return events after this event id. Only with |
| No | Return events before this event id. Only with |
| No | Max events to return (server max is |
| No | A stable node UUID. Repeat to select up to 100 nodes; a batch over 100 is refused locally with exit 1. |
| No | A repo-relative path. Matches the path itself and everything beneath it ( |
| No |
|
Underlying route: GET /api/v1/code/{project_id}/nodes/changes.
Errors:
Error | When | Resolution |
|---|---|---|
| The token was approved without | Request a new token with |
|
| Use |
|
| Add |
| More than 100 | Split the nodes into batches of 100. |
| No attached repository, another brand's project, or an id that does not exist. | Check the project id; attach a repository to it. |
opvs code graph <node_id>
Adjacency for one node — what imports it or is imported by it. The graph carries import edges only: file-to-file (TypeScript / JavaScript, via regex) and module-to-module (Python, via the AST). The scanner emits no import edges for Go. Call edges need type resolution and remain a separate, later decision.
node_id is a UUIDv5 (the same id that opvs code resolve returns, or that a brand's own tooling can recompute from the project + repo + kind + path + qname). Direction is in, out, or both; depth is 1 or 2. Each neighbour carries node_id, path, node_kind, state, kind, derivation, hop — hop is 1 for a direct neighbour and 2 for one reached through a direct neighbour (--depth 2 only); state is the ledger state of the neighbour node (live | removed | moved), kind is the edge kind (always import today), and derivation is parsed (Python ast) for files the AST lane walked, or regex for TypeScript / JavaScript files matched by the regex lane.
opvs code graph 4d7c8e2a-1f93-5b7e-9a4d-2c1b8e6f3a92 \
--project 9d7642c4-8151-4c3e-977e-9f30d5a52844 \
--direction both --depth 1 --limit 25 --branch main
# → project_id, node_id, commit_hash, branch, direction, depth;
# neighbours[] each carrying
# node_id, path, node_kind, state (live | removed | moved),
# kind (import), derivation (parsed | regex), hop (1 | 2);
# truncated, limit_clamped, effective_limit
# → exit 1 "Error: index not found" (HTTP 404) for an unknown node or a project with no ready indexOption | Required | Description |
|---|---|---|
| Yes | Project id. |
| Yes | A ledger node id (UUIDv5). |
| No |
|
| No |
|
| No | Max neighbours to return (server default 100, clamps at 500). |
| No | Answer from a specific indexed branch. |
| No |
|
Underlying route: GET /api/v1/code/{project_id}/graph.
A project that has no attached repository at all — including another brand's project, or a project id that does not exist — returns 404 index not found.
Errors:
Error | When | Resolution |
|---|---|---|
| The token was approved without | Request a new token with |
| An unknown node, no ready index, no attached repository, or another brand's project. | Resolve the node with |
Validation error, HTTP 422 (exit 1) |
| Use one of the listed values. |
opvs code graph folders [dir]
Requires @opvs-ai/cli 0.26.0 or later.
The import map of one directory, rolled up to folders: which folders import which, which files are the hubs (most imported, most importing), and how many pairs of files import each other. It is the same read the dashboard's Graph overview draws. Edges are imports only (Python and TypeScript / JavaScript); there are no call edges and no Go import edges.
dir is repo-relative and defaults to ., the repository root. Deeper files fold into their folder at --depth levels below dir.
opvs code graph folders src/payments \
--project 9d7642c4-8151-4c3e-977e-9f30d5a52844 \
--depth 2 --limit 100 --format json
# → indexed: true; dir: src/payments; depth: 2;
# folders[] (path, node_id, files_on_edges, imports_inside, parsed, regex);
# links[] (src, dst, imports, parsed);
# hubs.in_degree[] and hubs.out_degree[] (path, node_id, imports);
# mutual_import_pairs: 2; truncated: false; limit_clamped: false; effective_limit: 100
# → indexed: false with empty lists when the project has no ready index
# → exit 1 "Error: dir must be repo-relative: '../other'" for a path outside the repositoryOption | Required | Description |
|---|---|---|
| Yes | Project id. |
| No | Repo-relative directory to roll up. Default |
| No | Folder levels below |
| No | Max folder rows and cross-folder link rows (server default |
| No | Answer from a specific indexed branch. |
| No |
|
Underlying route: GET /api/v1/code/{project_id}/graph/folders.
Unlike graph, this command never answers 404 for a project: another brand's project, an id that does not exist and a project with no ready index all print indexed: false, so read indexed before reading any count.
Errors
Error | When | Resolution |
|---|---|---|
| The token was approved without | Request a new token with |
|
| Pass a path relative to the repository root, or |
A validation error from the server (HTTP 422, exit 1) |
| Use a value inside the range. |
|
| Pass the project id. |
opvs code callers <anchors...>
Requires @opvs-ai/cli 0.27.0 or later. Use 0.27.9 or later: it separates calls from other files from calls inside the file, and earlier versions can misreport both.
Who calls this function? Name up to 50 files or symbols. The command resolves each anchor, then asks the code index (POST /api/v1/code/{project_id}/callers) for its callers and the test files near it. A leading ./ is fine.
Three limits hold for every answer. The first line says which commit answered, because the index is as old as its last commit. A [heuristic] caller may be wrong. A test hint is not coverage: it is a test file that imports this file, and it does not say that any test runs this function.
Calls come from Python files only. For any other language the answer reads UNKNOWN, not zero.
This command has no yaml or md output: it prints text, or the raw answer with --json or --format json.
opvs code callers src/payments/charge.py#charge_card src/payments/charge.py \
--project 9d7642c4-8151-4c3e-977e-9f30d5a52844
# → index: 92f4a7d, 2 h old
# →
# → src/payments/charge.py#charge_card
# → callers: 2 (showing 2)
# → src/api/checkout.py#create_order → charge_card [exact]
# → src/jobs/retry.py#retry_failed → charge_card [heuristic]
# → test hints (not coverage): tests/payments/test_charge.py
# →
# → src/payments/charge.py
# → callers from other files: 2 calling symbols in other files, 3 calls shown
# → src/api/checkout.py#create_order → charge_card [exact]
# → src/api/refunds.py#create_refund → refund_card [exact]
# → src/jobs/retry.py#retry_failed → charge_card [heuristic]
# → calls inside the file: 4
# → test hints (not coverage): tests/payments/test_charge.py
# → exit 1 "Error: project not found" when the project has no attached repositoryOn a file, calling symbols counts distinct functions and calls shown counts rows, so one function that calls two symbols here adds one to the first and two to the second. — truncated appears only when outside rows are missing from the list; raise --limit to see them.
What it prints | What it means |
|---|---|
| The callers of one symbol. |
| The index holds calls for this file, and none comes from another file. |
| The index stores no call edges at all. |
| No call edges exist for this language. Unknown, not zero. |
| The file sits in a folder the index lists but does not read for calls. |
| An empty file, or one with only imports. |
| The anchor is a folder. |
| The anchor is not a live file or symbol in this index. |
| No index answered. |
Option | Required | Description |
|---|---|---|
| Yes | Up to 50 anchors: |
| Yes | Project id. |
| No | Callers per anchor, 1 to 100. Default 20. |
| No | Print the API answer unchanged, plus |
| No |
|
Underlying routes: POST /api/v1/code/{project_id}/nodes/resolve, then POST /api/v1/code/{project_id}/callers. Up to 10 test hints print per anchor; that number is not an option.
Errors
Error | When | Resolution |
|---|---|---|
| More than 50 anchors. | Split the list. |
|
| Pass a whole number from 1 to 100. |
|
| Pass the project's id. |
| The token was approved without | Request a new token with |
| The project has no attached repository, belongs to another workspace, or does not exist. | Check the id, or attach a repository. |
| The index could not be read. Callers are unknown, not zero. | Read the repository instead, and retry later. |
opvs code check-card <brief.md>
Requires @opvs-ai/cli 0.27.1 or later. Use 0.27.9 or later, for the same reason as opvs code callers.
Are the paths in this draft card real, and what touches them? The command reads a markdown brief, finds the repository paths in it, checks each one against the code index, and prints one ## Index facts block to paste into the card before anyone starts work. A wrong path shows up as a NO row while it is still cheap to fix. The block names the commit it was checked against, because the index is as old as its last commit. Its test hints are not coverage, and a heuristic caller may be wrong.
What it reads:
Backticked paths, with an optional
#symbol,::symbolor:LINE, in the brief as written.Nothing inside a fenced code block, and nothing after the heading
# The gate block — the last section of every builder brief.
At most 50 paths. Each one counts once, and the block says how many were left out.
A path on a line that says
create,NEWornew fileis declared new, so it does not have to
exist yet. Any other wording, such as add, leaves it a NO row.
opvs code check-card ./brief.md --project 9d7642c4-8151-4c3e-977e-9f30d5a52844
# → ## Index facts
# → <!-- index-facts v1 commit=92f4a7d2b1c03a8a43bda19a2cfcda16b3680c5e indexed_at=2026-10-27T09:14:02.511000+00:00 checked_at=2026-10-27T11:44:02.100Z -->
# → index: 92f4a7d, 2 h old · project 9d7642c4-8151-4c3e-977e-9f30d5a52844 · 3 paths checked
# → | Anchor | In the index | Callers | Test hints (not coverage) |
# → | --- | --- | --- | --- |
# → | `src/payments/charge.py#charge_card` | yes | 2 (1 exact, 1 heuristic): `src/api/checkout.py#create_order → charge_card`, `src/jobs/retry.py#retry_failed → charge_card` | `tests/payments/test_charge.py` |
# → | `src/payments/chrage.py` | NO — not in the index and not declared new | n/a | n/a |
# → | `src/payments/refunds.py` | no (declared new) | n/a | n/a |
# → exit 2 "CANNOT: project not found" when the project has no attached repositoryThe block is at most 6,000 bytes, so a long brief may show fewer rows than it checked; the block says so, and --json carries them all.
Option | Required | Description |
|---|---|---|
| Yes | The draft brief to read. |
| Yes, or set | Project id. |
| No | Add |
| No | Exit 1 when a path is not in the index and not declared new. |
| No | Print the same facts as JSON. |
Exit codes: 0 whatever the check finds, 1 only with --strict, 2 when it cannot run. The check is advisory: a NO row does not fail anything unless you ask it to.
Underlying routes: POST /api/v1/code/{project_id}/nodes/resolve, then POST /api/v1/code/{project_id}/callers, and GET /api/v1/code/{project_id}/context for --similar.
Errors
Error | When | Resolution |
|---|---|---|
| No project was named. | Pass |
| The file does not exist or cannot be read. | Check the path. |
| More than five | Drop some. |
| The project has no attached repository, belongs to another workspace, or does not exist. | Check the id, or attach a repository. |
| No index answered. | Wait for the first index, or retry later. |
| The token was approved without | Request a new token with |
opvs code tests-for [paths...]
Requires @opvs-ai/cli 0.27.6 or later.
Which tests can these files break? With --remote, the command asks the code index (POST /api/v1/code/{project_id}/tests-for), which walks the stored graph backwards from each file, through imports and through files a test names by path, to the test files that reach it. It needs no checkout and runs nothing. Use it on your own repository.
Without --remote, the command runs scripts/select_tests.py from the root of the checkout you are in. That script ships in the OPVS repository; in a checkout without it, the command stops and names --remote. The local answer is advisory, and it prints a line that says so.
Unlike the other opvs code commands, this one has no --format: it prints text, or the raw answer with --json. Local mode takes no --project.
opvs code tests-for src/payments/charge.py src/payments/legacy.py \
--remote --project 9d7642c4-8151-4c3e-977e-9f30d5a52844
# → index: 92f4a7d, 2 h old
# → src/payments/charge.py
# → tests/payments/test_charge.py hops 1 import
# → tests/fixtures/test_charge_amounts.py hops 1 reads
# → tests/api/test_checkout.py hops 2 import → import
# → src/payments/legacy.py
# → unknown — not a file in this index
# → exit 1 "Error: project not found" when the project has no attached repositoryEach test line shows the edges walked (hops) and their kinds, from the test towards your file. When the index cannot answer in full, degraded: lines come first; read them before trusting an empty list. A path with too many tests ends with truncated (N total).
Option | Required | Description |
|---|---|---|
| With | Repo-relative files to look up, at most 200. In local mode, files to add to the diff. |
| No | Ask the code index instead of running the local selector. |
| With | Project id. Refused in local mode. |
| No | Diff base for the local selector. Default |
| No | Print the API answer, or the selector's JSON, unchanged. |
Underlying route: POST /api/v1/code/{project_id}/tests-for. The route's depth (default 4) and limit (default 200) are not exposed as options.
Errors
Error | When | Resolution |
|---|---|---|
|
| Pass |
|
| Name at least one file. |
|
| Add |
| Local mode outside a checkout. | Run it inside the checkout, or use |
| A path is absolute or climbs out of the repository. | Pass paths relative to the repository root. |
| The token was approved without | Request a new token with |
| The project has no attached repository, belongs to another workspace, or does not exist. | Check the id, or attach a repository. |
Session Commands
opvs session get --self # Your assigned tasks across all boards
opvs session get --board <id> # Board context
opvs session get # List all boardsConfiguration
opvs config set format yaml # Output format: yaml, json, md
opvs config set api_url <url> # API URL for current workspace
opvs config get # Show current config
opvs config get --all # Show all workspaces
opvs config path # Show config file location
opvs config reset --yes # Reset to defaults
opvs init # Print CLAUDE.md snippetCommand Summary
Group | Commands | Description |
|---|---|---|
|
| PAT token management |
|
| Multi-workspace management |
|
| Board CRUD, structural audit, presets |
|
| Full task management |
|
| Agent-optimized workflows |
|
| Sequential task stages |
|
| Task relationships |
|
| Board member management |
|
| Color-coded task tags |
|
| Cross-board relationships |
|
| Task comments |
|
| Agent-optimized views |
|
| Activity feeds |
|
| Board analytics |
|
| Project containers + inherited bundles |
|
| Start a factory project at a set time |
|
| Column management |
|
| Task file attachments |
|
| AgentDocs documentation |
|
| Session context |
|
| CLI configuration |
| Generate CLAUDE.md snippet |
Environment Variables
Variable | Description | Overrides |
|---|---|---|
| PAT token | Config file token |
| API base URL | Config file api_url |
| Workspace key, |
|
| Output format ( | Config file format |
Response Format
By default, responses use YAML format which is 40-76% more token-efficient than JSON:
opvs config set format json # Switch to JSON
opvs config set format yaml # Switch to YAML (default)
opvs config set format md # Switch to Markdown