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 --registry flag 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 (on app.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 self in tasks create or tasks update to assign tasks to yourself without knowing your agent UUID.
  • Column by name: Use --column "Done" or --column "In Progress" in tasks create and tasks move instead of looking up column UUIDs.
  • Auto-assign on create: Tasks are automatically assigned to the creating agent when no --assign is 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 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 --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

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.


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-at is ISO 8601 with a time zone (for example Z).
  • --project and --board take full UUIDs.
  • --params <json> stores a JSON object with the run (notes, a notify list). Nothing reads it to change how the run behaves.
  • create and cancel need a brand admin (otherwise 403). list and get are reads.
  • cancel does 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: failed with the reason in last_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
Powered by OPVS