Skip to content

Commands

Terminal window
# From root directory
bun run cli <command>
# Direct execution
bun run apps/cli/index.ts <command>
# Once installed globally (see /cli/install/ — the package is viite-cli, not viite)
viite <command> # or the shorter alias: vt <command>

Global flag: --verbose / -v — enable verbose logging (default false).

Nine top-level commands: branch, read, ingest, worktree, scrape, tasks, agent, local, record. The eight covered here run and exit; agent runs a supervised loop and has its own page.

Terminal window
# Turn free text (e.g. a task title) into a valid git branch name and create it
viite branch create "fix: This is a bug fix"
# Preview the sanitized name without creating the branch
viite branch create "feature: new api" --dry-run

branch create <text> only creates the local branch object — it does not check it out or create a worktree. The sanitizer lowercases and trims the input, replaces punctuation (: \s , ( ) ~ ^ ? * [ ] \ @ { }) with -, collapses repeated -/., strips leading/trailing -/.//, and truncates to 60 characters.

Flags:

  • --dry-run, -d - print the sanitized branch name instead of creating it (default: false)

Two ways to pull from a source:

CommandPatternUse Case
read source gmailDirect (sync)Quick reads, returns raw content immediately
ingest source gmailTask-based (async)Full workflow with analysis/enrichment steps
Terminal window
# Direct Gmail read (synchronous, no workflow/job created)
viite read source gmail --filter=user@example.com --limit-days=7 --with-url
# Gmail read with defaults (7 days, no URL filter, falls back to your session's email)
viite read source gmail
# Trigger the Gmail ingestion workflow (async)
viite ingest source gmail --filter=user@example.com --limit-days=7 --save-to=database

read source gmail flags:

  • --filter, -f - filter by sender email address (falls back to GMAIL_USER_EMAIL, then the authenticated session’s own email, if omitted)
  • --limit-days, -l - limit to emails from the last N days (default: 7)
  • --with-url, -u - only include emails containing URLs (default: false)

ingest source gmail flags: same as above, plus:

  • --save-to, -s - one of console, database, csv, notion (default: console)

Manage git worktrees for parallel development with automatic port offset configuration.

Terminal window
# Create a worktree, auto-assigning the next free port offset
viite worktree create feature-auth
# Create a worktree at an explicit offset (must be a multiple of 100)
viite worktree create bugfix-login 200
# Create without opening terminals / without running bun install
viite worktree create feature-x --no-open
viite worktree create feature-x --no-install
# List all worktrees
viite worktree list
# Remove a worktree (skip prompts with --yes)
viite worktree remove feature-auth --yes
# Remove worktree and delete the branch
viite worktree remove feature-auth --delete-branch

The port-offset argument to worktree create is optional — omit it and the CLI scans existing worktrees’ .env files and auto-assigns the next unused multiple of 100 (printing Auto-assigned port offset: <n>). New branches are created from a freshly-fetched origin/main, not whatever is checked out in the main repo. worktree create also copies gitignored .env files and .claude/settings.local.json / .claude/agents/ into the new worktree.

Port offset scheme (base ports, offset is added to each):

OffsetAPIDashboardDev website
0300050005003
100310051005103
200320052005203

worktree create flags:

  • --open, -o - open terminal sessions running the dev servers after creation (default: true)
  • --install, -i - run git pull --rebase origin main && bun install after creation (default: true)

worktree remove flags:

  • --delete-branch, -d - also delete the git branch (default: false)
  • --force, -f - force removal even with uncommitted changes (default: true)
  • --yes, -y - skip confirmation prompts, for scripted/non-TTY callers (default: false)

Terminal integration: unless --no-open is passed, creating a worktree opens two sessions in your default terminal — one running the API, one running the dashboard, both already cd-ed into the worktree and bound to its port offset. The terminal is resolved from PLATFORM_TERMINAL/TERMINAL, then TERM_PROGRAM, then the OS default (Terminal.app on macOS, x-terminal-emulator on Linux). iTerm2 gets tabs in one window; Terminal.app gets one window per server, because scripting a tab there needs Accessibility permission.

Manage GitHub PRs for worktrees. Requires GitHub CLI (gh) installed and authenticated.

Terminal window
# List PRs with associated worktrees
viite worktree pr list
viite worktree pr list --all # All open PRs (for finding PRs to checkout)
viite worktree pr list --all --author @me # Your open PRs
# Create a PR from the current worktree branch
viite worktree pr create
viite worktree pr create --title "Add feature" --body "Description" --draft
# Checkout an existing PR into a new worktree (port offset is optional/auto-assigned, as with `worktree create`)
viite worktree pr checkout 123
viite worktree pr checkout 123 100
# Show PR status for the current branch
viite worktree pr status
viite worktree pr status feature-branch --checks
# Sync worktree with its base branch
viite worktree pr sync # rebase by default
viite worktree pr sync --merge
viite worktree pr sync --push

PR List flags:

  • --all - show all open PRs, not just ones with a local worktree (default: false)
  • --author, -a - filter by author (@me for your own PRs)
  • --limit, -l - maximum PRs to show (default: 50)

PR Create flags:

  • --title, -t - PR title (prompts if omitted)
  • --body, -b - PR description (prompts if omitted, optional)
  • --draft, -d - create as a draft PR (default: false)
  • --base - base branch (default: main)

PR Checkout flags:

  • --open, -o - open terminal sessions running the dev servers (default: true)
  • --install, -i - run bun install (default: true)

PR Status flags:

  • --checks, -c - show detailed CI check results instead of a one-line summary (default: false)

PR Sync flags:

  • --rebase, -r - use rebase strategy (default strategy either way, unless --merge is set)
  • --merge, -m - use merge strategy instead
  • --push, -p - push after a successful sync (rebase + push prompts to confirm a --force-with-lease push)
  • --base, -b - base branch to sync with (auto-detected from the PR, falls back to main)
Terminal window
viite scrape leboncoin --url "https://www.leboncoin.fr/recherche?category=71"
viite scrape leboncoin -u "https://..." --pages 5 --delay 500
viite scrape leboncoin -u "https://..." --save

Scrapes protected sites via a Chrome CDP connection. <source> must be one of leboncoin, autoscout24, malt.

Flags:

  • --url, -u - page URL to scrape (prompted interactively if omitted)
  • --cdp-endpoint - Chrome CDP endpoint (default: http://localhost:9222)
  • --pages, -p - number of pages to scrape (default: 1)
  • --delay, -d - milliseconds to wait between pages (default: 500)
  • --save - save results to the database (default: false)
  • --json - machine-readable output mode (default: false, not yet implemented)
Terminal window
viite tasks # local dev API
viite tasks --target viite-ai # read the API URL from that section of coolify.env
viite tasks --local --json

Lists the workflows on a deployed target, reading the target’s API URL out of deploy/coolify/coolify.env by default.

Flags:

  • --target, -t - coolify.env section name to read the API URL from (e.g. viite-ai)
  • --api-url - explicit API URL override, skips the coolify.env lookup entirely
  • --local - use the local dev API instead (default: false)
  • --json - machine-readable output; also disables interactive login (fails immediately if there’s no valid session, instead of prompting)

URL resolution priority: --api-url > --local > coolify.env scan (override the file path with COOLIFY_ENV_FILE).

viite agent watches a board for cards tagged for the agent and runs a coding session for each one. It is the only command with a security boundary of its own, so it is documented separately — see Agent.

Terminal window
viite local doctor # what can this machine run, and what is missing?
viite local doctor --config <path> # inspect a config other than the default

Lists the recipes declared in ~/.platform-cli/agent.json, whether each one’s tool is actually installed, and what to type to install the ones that aren’t. Offline by design — it reads the local config and probes PATH; no API, no auth, no board. Exits non-zero if anything needs attention, so it works as a setup check in a script.

✓ mov-to-mp4 — ffmpeg found (from:path to:path)
✗ transcribe — "whisper" is not installed
install with: brew install whisper-cpp

Recipes are what a local card is allowed to run here. A card names a recipe and supplies parameters; it never carries a command, a binary, or a directory:

{
"recipes": [
{
"name": "mov-to-mp4",
"command": "ffmpeg",
"args": ["-i", "{from}", "-c:v", "libx264", "-c:a", "aac", "{to}"],
"install": "brew install ffmpeg",
"params": {
"from": { "type": "path", "roots": ["~/Movies"] },
"to": { "type": "path", "roots": ["~/Movies"] }
},
"timeoutMs": 3600000
}
]
}

Same rule as repos: a card can ask, only this file can grant. Four properties follow from it, and each is enforced rather than documented:

  • No shell. The command is spawned with an argv array, so ;, | and $( ) in a value are inert data.
  • A placeholder is a whole argument. "{from}" is one argv element; "--input={from}" is rejected when the config loads, because partial interpolation is how a value smuggles in a second argument. Filter strings (-vf fps=12,scale=800:-1) therefore cannot be parameterised — pass a fixed value or split the argument.
  • Undeclared parameters are refused, not ignored.
  • Path parameters are jailed in their roots, compared after resolving symlinks — so neither ../../.ssh/id_rsa nor a symlink planted inside an allowed directory escapes. An empty roots means nothing is allowed, never everything.

A recipe whose tool is missing does not fail the card as an error: the run ends as missing_tool and the card shows the install line, because the machine that knows is not the machine showing the board.

A recipe this machine does not declare at all is caught earlier still: agent watch tells the board the recipe names it has, so the card is refused at the click and names what is on offer instead. That check only applies to a runner that is actually watching — nothing is precomputed, and nothing here changes the rule that only this file grants what runs.

Terminal window
viite record # open the recorder on DASHBOARD_URL
viite record --local # http://localhost:5000
viite record --url https://… # explicit dashboard URL
viite record --no-autostart # open the page without pressing Start

Opens Business Studio’s screen recorder (/content/record-screen?autostart=1) in your default browser and presses Start for you. Record, stop, and the video uploads straight to a connected YouTube channel.

This command does not record anything itself, and that is the design. The capture runs in the browser (getDisplayMedia + MediaRecorder), so the bytes live in the tab and go directly to Google — nothing is written to your disk, and there is no temp file to clean up. A local ffmpeg recipe would have needed one. What the command does is spare you finding the tab.

Two things follow from that and cannot be worked around:

  • The browser still asks which screen, window or tab to share. getDisplayMedia requires a user gesture and an explicit choice of surface; no URL can consent on your behalf.
  • autostart only clicks the button. If you are not signed in, you land on the sign-in page instead and nothing starts.

The recording is held in memory, so it stops itself after 30 minutes or 500 MB, whichever comes first, and says which. On macOS the machine’s own audio cannot be captured — tick Chrome’s own box in the share dialog for a tab’s or window’s audio, and use the microphone option for narration.

Needs DASHBOARD_URL (or --url / --local): the dashboard is a different origin and port from the API, so PLATFORM_API_URL is not it. Without one, the command says so rather than guessing an origin and opening a page that isn’t there.