CLI reference
Current version: @opvs-ai/cli@0.16.1 (requires @opvs-ai/core ^0.4.8) — verified 2026-09-26 on
both npmjs.com and the OPVS registry (npm.opvs.ai), which serve the same tarball.
Marked "next release" below: flags that are in the CLI's source but not in 0.16.1. They arrive
with the next published version. 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/cli
Or as a project dependency:
npm install @opvs-ai/cli
Requirements: 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 workspace
Agent 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 |
|---|---|
-w, --workspace <slug> |
Run command against a specific workspace |
-h, --help |
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
1and 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 --yes, with stdin not a terminal |
Refused: <what it would do>? … stdin is not a terminal. Re-run with --yes to confirm. |
Add --yes if you mean it. Nothing was sent. Exit 1. |
Commands that take --yes:
| Group | Commands |
|---|---|
boards |
delete, move-to-project (it removes the board from every other project) |
tasks |
delete |
stages |
delete |
links |
delete, delete-doc |
members |
remove |
markers |
delete |
board-links |
delete |
columns |
delete |
files |
delete |
docs |
delete, project-delete, branch-delete |
code |
context, summary, tree, files, symbols, resolve, changes, graph |
scheduled-runs |
cancel |
employees |
hire (spends a plan seat and may bill), cancel |
mail |
draft delete |
tools |
uninstall |
auth |
revoke |
workspace |
remove |
config |
reset |
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 workspace
| Option | Required | Description |
|---|---|---|
-e, --email <email> |
Yes | Admin email (who approves the request) |
-w <slug> |
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 | Access denied by workspace owner. |
Ask the admin why; re-request once resolved. Exit 1. |
| Request outlived its window | Request expired. Try again. |
Re-run the command. Exit 1. |
| Poll window elapsed unanswered | Request timed out. The owner may not have seen the email. |
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 carries
Errors:
| Condition | Message | Resolution |
|---|---|---|
401 from the server |
Token is invalid or expired. Run \opvs auth request` to re-authenticate.` |
Re-authenticate. Exit 1. |
| Any other failure | Error: <detail> |
Read the detail; check opvs config get api_url. Exit 1. |
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 | Token not found on server (may already be revoked). |
None — local config is still cleared. Exit 0. |
| Server unreachable or rejects | Server revoke failed (<detail>), but local config cleared. |
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 first
Errors:
| Condition | Message | Resolution |
|---|---|---|
| Caller is not a workspace admin | Error: <detail> |
Admin-only command; ask an admin to run it. Exit 1. |
| Request failed with no detail | Error: Failed to list agents |
Check connectivity and opvs auth status. Exit 1. |
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 workspace
Board 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 it
Task 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 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)
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 agent
| Command | Description |
|---|---|
my-tasks |
Cross-board task aggregation with board names |
delegations |
Tasks you handed off to other agents |
batch-create |
Create multiple tasks from a JSON file |
progress <id> |
Report progress (percent + message + operation) |
complete <id> |
Smart completion — walks through state machine automatically |
delegate <id> |
Reassign to another agent by slug |
mention <id> |
@mention an agent on a task to trigger a response |
tasks-by <slug> |
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,id3
Link 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 member
Marker 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 it
markers 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> --yes
Comment Commands¶
opvs comments list <taskId> # List task comments
opvs comments add <taskId> "message" # Add comment
opvs comments add <taskId> -f output.md # Comment from file
Views (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 context
Activity Feed¶
opvs activity board <boardId> --limit 25 # Board activity feed
opvs activity task <taskId> --limit 25 # Task activity feed
Metrics¶
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 distribution
Column 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 # next release: this column's own staleness threshold
opvs columns reorder <boardId> --order id1,id2,id3
opvs columns delete <columnId> --yes
File 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> --yes
Board 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): --preset
| Option | Since | Description |
|---|---|---|
--profile <name> |
0.16.1 | planner-orchestration, factory or crm: a named set of the thresholds below |
--preset, --required-column-fields, --required-task-fields |
0.16.1 | The structure and field requirements. Each overrides the profile's value. |
--stale-days, --column-age-days, --star-fanout |
0.16.1 | The numeric thresholds. Each overrides the profile's value. |
--json |
0.16.1 | Print the report as JSON |
--format <fmt> |
next release | text (default), json (the report verbatim), md or yaml |
--all-checks |
next release | On a NOT HEALTHY report, print every check, not only the failures |
--require-complete |
next release | Make INCOMPLETE exit 2 as well |
Exit codes (next release) — a script can gate on these:
| Exit | Meaning |
|---|---|
0 |
HEALTHY — every check ran and none failed |
0 |
INCOMPLETE — you did not pass a threshold, so a check did not run. That is about your request, not the board. --require-complete makes it 2. |
2 |
NOT HEALTHY — at least one check failed |
1 |
The CLI could not get a verdict: network, auth, a bad reference, or a verdict it does not recognise. Never 0. |
⚠️ 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. Until the next release is on
npm, 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 | Error: path.board_id: 'ZZZNOPE9!' is not a valid board reference. … |
Use a key like PC, 8 or more hex characters, or a full UUID. Exit 1. |
| No such board, or a private board you are not on | Error: Board not found |
Check the reference and your workspace. Exit 1. |
| Unknown profile | Error: unknown profile 'nope' — known profiles: crm, factory, planner-orchestration |
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 the next release; an unknown project id prints "Error: Project not found", exit 1)
Errors:
| Condition | Message | Resolution |
|---|---|---|
| Not a full project UUID | Error: path.group_id: '812228dc' is not a valid reference. … |
Pass the full UUID from opvs projects list. A prefix is refused today even though the message suggests one. Exit 1. |
| No such project in your brand | Error: Project not found |
Check the id with opvs projects list. Exit 1. |
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-done
create -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 | Error: <detail> |
Check the id with opvs projects list. Exit 1. |
| Request failed with no detail | Error: Failed to get project bundle |
Check connectivity and opvs auth status. Exit 1. |
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-skills
A passed list REPLACES that field
bundle set does not append. Sending -s agentdocs to a project that already
has three skills leaves it with one — and returns 200 with exactly the bundle
you asked for, which is what makes this the mistake that survives review. Read
the current lists with bundle get first 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 | Cannot both set and clear skills. Drop --clear-skills or the repeated flag. |
Pick one intent per field. Exit 1. |
| No field given at all | Nothing to update. Provide --command / --skill / --rule, or --clear-commands / --clear-skills / --clear-rules |
Name at least one field. Exit 1. |
| A cap or length exceeded | Error: <detail> (422 from the server) |
Trim to the caps above. Exit 1. |
| Request failed with no detail | Error: Failed to set project bundle |
Check connectivity and opvs auth status. Exit 1. |
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 --yes
Code 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. 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) 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:read
An 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 |
|---|---|---|
context, summary, tree, files, symbols |
200 with indexed: false |
200 with indexed: false |
resolve |
404 with project not found |
200; each anchor returns state: unknown (the ledger has the path-derived id but has never seen the node) |
changes |
404 with index not found |
200; if a branch is attached, the events recorded so far (or empty if none yet); if no branch is attached, branch: null with a reason |
graph |
404 with index not found |
404 with index not found (no ready index means no edges to read) |
graph folders |
200 with indexed: false |
200 with indexed: false |
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 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 |
|---|---|---|
--project <id> |
Yes | Project id (board_groups.id where kind=project). |
--kind <kind> |
No | Restrict to one symbol kind (function, class, …). |
--branch <name> |
No | Answer from a specific indexed branch. |
--limit <n> |
No | Max matches to return (server max is 50). |
--format <fmt> |
No | yaml (default) | md | json — overrides the configured default. |
Errors:
| Condition | Message | Resolution |
|---|---|---|
| Server unreachable or rejected | Error: <detail> |
Read the detail; check opvs config get api_url. Exit 1. |
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:read
| Option | Required | Description |
|---|---|---|
--project <id> |
Yes | Project id (board_groups.id where kind=project). |
--branch <name> |
No | Answer from a specific indexed branch. |
--format <fmt> |
No | yaml (default) | md | json — overrides the configured default. |
Errors:
| Error | When | Resolution |
|---|---|---|
Error: Token missing required scope: code:read (exit 1) |
The token was approved without code:read. |
Request a new token with --scopes …,code:read. |
Error: <detail> (exit 1) |
The server was unreachable or refused the call. | Read the detail; check opvs config get api_url. |
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 levels
| Option | Required | Description |
|---|---|---|
--project <id> |
Yes | Project id. |
--branch <name> |
No | Answer from a specific indexed branch. |
--depth <n> |
No | 1-based; can only narrow the stored tree. |
--format <fmt> |
No | yaml (default) | md | json. |
Underlying route: GET /api/v1/code/{project_id}/tree.
Errors:
| Error | When | Resolution |
|---|---|---|
Error: Token missing required scope: code:read (exit 1) |
The token was approved without code:read. |
Request a new token with --scopes …,code:read. |
| Validation error, HTTP 422 (exit 1) | --depth is outside 1 to 3. |
--depth can only narrow the stored tree; use 1, 2 or 3. |
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 path
| Option | Required | Description |
|---|---|---|
--project <id> |
Yes | Project id. |
[directory] |
No | A path inside the index, or . for the repo root. Defaults to root. |
--direct |
No | List only files directly inside [directory], not descendants. |
--branch <name> |
No | Answer from a specific indexed branch. |
--limit <n> |
No | Max files to return. |
--format <fmt> |
No | yaml (default) | md | json. |
Underlying route: GET /api/v1/code/{project_id}/files.
Errors:
| Error | When | Resolution |
|---|---|---|
Error: Token missing required scope: code:read (exit 1) |
The token was approved without code:read. |
Request a new token with --scopes …,code:read. |
Error: directory must be repo-relative (exit 1) |
[directory] starts with / or has a .. segment. |
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 repository
| Option | Required | Description |
|---|---|---|
--project <id> |
Yes | Project id. |
<file_path> |
Yes | A repository-relative path; the file must contain stored symbols. |
--branch <name> |
No | Answer from a specific indexed branch. |
--limit <n> |
No | Max symbols to return. |
--format <fmt> |
No | yaml (default) | md | json. |
Underlying route: GET /api/v1/code/{project_id}/symbols.
Errors:
| Error | When | Resolution |
|---|---|---|
Error: Token missing required scope: code:read (exit 1) |
The token was approved without code:read. |
Request a new token with --scopes …,code:read. |
Error: file_path must be repo-relative (exit 1) |
<file_path> starts with / or has a .. segment. |
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 |
|---|---|
path |
a file node (the file at that repository-relative path) |
dir/ |
a directory node |
./ |
the repository root as a directory node |
path#qname |
a symbol node (the # is the separator; qname is everything after the first #) |
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 repository
| Option | Required | Description |
|---|---|---|
--project <id> |
Yes | Project id. |
<anchor...> |
Yes | One or more anchors (path, dir/, ./, or path#qname). Up to 1000 per call. |
--format <fmt> |
No | yaml (default) | md | json. |
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 |
|---|---|---|
Error: Token missing required scope: code:read (exit 1) |
The token was approved without code:read. |
Request a new token with --scopes …,code:read. |
Error: project not found (HTTP 404, exit 1) |
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 repository
Newest 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 out
| Option | Required | Description |
|---|---|---|
--project <id> |
Yes | Project id. |
--order <asc\|desc> |
No | Event id order. asc (default) is oldest first; desc is newest first. Needs CLI 0.26.0. |
--after <id> |
No | Return events after this event id. Only with --order asc. |
--before <id> |
No | Return events before this event id. Only with --order desc; omit it for the newest page. Needs CLI 0.26.0. |
--limit <n> |
No | Max events to return (server max is 500). |
--node-id <id> |
No | A stable node UUID. Repeat to select up to 100 nodes; a batch over 100 is refused locally with exit 1. |
--path-prefix <path> |
No | A repo-relative path. Matches the path itself and everything beneath it (--path-prefix src/store matches src/store and src/store/..., not src/storex). |
--format <fmt> |
No | yaml (default) | md | json. |
Underlying route: GET /api/v1/code/{project_id}/nodes/changes.
Errors:
| Error | When | Resolution |
|---|---|---|
Error: Token missing required scope: code:read (exit 1) |
The token was approved without code:read. |
Request a new token with --scopes …,code:read. |
Error: after is only valid with order=asc (exit 1, no request sent) |
--after with --order desc. |
Use --before to page a desc read. |
Error: before is only valid with order=desc (exit 1, no request sent) |
--before without --order desc. |
Add --order desc, or page with --after. |
Error: too many --node-id values (exit 1, no request sent) |
More than 100 --node-id values. |
Split the nodes into batches of 100. |
Error: index not found (HTTP 404, exit 1) |
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 index
| Option | Required | Description |
|---|---|---|
--project <id> |
Yes | Project id. |
<node_id> |
Yes | A ledger node id (UUIDv5). |
--direction <dir> |
No | in | out | both. |
--depth <n> |
No | 1 | 2. |
--limit <n> |
No | Max neighbours to return (server default 100, clamps at 500). |
--branch <name> |
No | Answer from a specific indexed branch. |
--format <fmt> |
No | yaml (default) | md | json. |
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 |
|---|---|---|
Error: Token missing required scope: code:read (exit 1) |
The token was approved without code:read. |
Request a new token with --scopes …,code:read. |
Error: index not found (HTTP 404, exit 1) |
An unknown node, no ready index, no attached repository, or another brand's project. | Resolve the node with opvs code resolve first; check opvs code tree for indexed. |
| Validation error, HTTP 422 (exit 1) | --depth is not 1 or 2, or --direction is not in, out or both. |
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 repository
| Option | Required | Description |
|---|---|---|
--project <id> |
Yes | Project id. |
[dir] |
No | Repo-relative directory to roll up. Default .. |
--depth <1-10> |
No | Folder levels below dir (server default 2). |
--limit <n> |
No | Max folder rows and cross-folder link rows (server default 250, clamps at 500). |
--branch <name> |
No | Answer from a specific indexed branch. |
--format <fmt> |
No | yaml (default) | md | json. |
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 |
|---|---|---|
Error: Token missing required scope: code:read (exit 1) |
The token was approved without code:read. |
Request a new token with --scopes …,code:read. |
dir must be repo-relative (exit 1) |
dir starts with / or contains a .. segment. |
Pass a path relative to the repository root, or .. |
| A validation error from the server (HTTP 422, exit 1) | --depth is outside 1 to 10, or --limit is below 1. |
Use a value inside the range. |
error: required option '--project <id>' not specified |
--project was left out. |
Pass the project id. |
Session Commands¶
opvs session get --self # Your assigned tasks across all boards
opvs session get --board <id> # Board context
opvs session get # List all boards
Configuration¶
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 snippet
Command Summary¶
| Group | Commands | Description |
|---|---|---|
auth |
request, status, revoke, list |
PAT token management |
workspace |
list, use, current, remove |
Multi-workspace management |
boards |
list, get, create, update, delete, health, apply-preset |
Board CRUD, structural audit, presets |
tasks |
list, get, create, update, delete, move, subtasks, search, batch-get |
Full task management |
agent |
my-tasks, delegations, batch-create, progress, complete, delegate, mention, tasks-by |
Agent-optimized workflows |
stages |
list, create, update, delete, reorder |
Sequential task stages |
links |
list, create, delete |
Task relationships |
members |
list, add, remove |
Board member management |
markers |
list, create, get, update, delete, toggle |
Color-coded task tags |
board-links |
list, create, update, delete |
Cross-board relationships |
comments |
list, add |
Task comments |
views |
session, overview, tasks, task |
Agent-optimized views |
activity |
board, task |
Activity feeds |
metrics |
summary, velocity, agents, costs, distribution |
Board analytics |
projects |
list, get, create, update, set-status, archive, add-board, remove-board, tasks, bundle get, bundle set, health |
Project containers + inherited bundles |
scheduled-runs |
list, get, create, cancel |
Start a factory project at a set time |
columns |
list, create, update, reorder, delete |
Column management |
files |
list, upload, download, delete |
Task file attachments |
docs |
list, get, create, update, search + 16 more |
AgentDocs documentation |
session |
get |
Session context |
config |
get, set, path, reset |
CLI configuration |
init |
Generate CLAUDE.md snippet |
Environment Variables¶
| Variable | Description | Overrides |
|---|---|---|
OPVS_TOKEN |
PAT token | Config file token |
OPVS_API_URL |
API base URL | Config file api_url |
OPVS_WORKSPACE |
Workspace key, brand_id or brand name — not a client_id. It is a key in your local ~/.opvs/config.json; run opvs workspace list to see them. |
-w flag / current workspace |
OPVS_FORMAT |
Output format (yaml, json, md) |
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