Skip to content
vxvx
GitHubBlueskydev.toRSS
Blog

Built for the agent at the keyboard

Most vx runs are started by coding agents now. So every verb speaks JSON with a shipped schema, a failure keeps its log and the files it names, the run history is a SQLite file, and the docs and a skill come as plain markdown.

A coding agent cannot read a progress bar. It reads stdout, parses it and decides what to do next. If the answer is buried in a frame meant for human eyes, the agent guesses. vx is built so it never has to.

vx run --format json prints one JSON document when the run ends. The frame and the tasks’ output go to stderr, so a pipe sees only the result.

Terminal window
$ vx run build --all --format json 2>/dev/null
{
"runId": "01a11f0d-014c-700d-a4ad-59e93a00a129",
"ok": true,
"exitCode": 0,
"totalMs": 223.951554,
"savedMs": 1270,
"tasks": [
{
"id": "@demo/api#build",
"status": "cache-hit",
"exitCode": 0,
"durationMs": 3,
"hash": "32b3a0bfa973a7a8",
"storedDurationMs": 326,
...

savedMs is the time the cache saved this run. vx show, vx info, vx why, vx last and vx cache prune take the same --format json.

Each JSON document has a checked-in JSON Schema, shipped in the package at node_modules/@vzn/vx/schemas/: summary.json, plan.json, show.json, info.json, why.json, last.json and cache.json. Every object closes its key set, so a new field is a schema change, never a surprise. A test holds each verb’s output to its schema, so stdout always parses. A refusal under --format json is a JSON line too, with a stable code an agent can branch on: see A refusal an agent can read.

When a task fails, an agent wants two things: what it printed and which files it blames. vx saves both, so the agent does not have to scroll a terminal it never saw.

Terminal window
$ vx last --failed --format json | jq '.tasks[] | select(.output != "") | {status, exitCode, output, locations}'
{
"status": "failed",
"exitCode": 1,
"output": "src/index.ts:3:7 - error TS2322: Type string is not assignable to type number\n",
"locations": [
{
"file": "/work/demo/packages/ui/src/index.ts",
"line": 3,
"col": 7
}
]
}

locations lists only files that exist, with absolute paths. Secrets are masked in the saved output. vx keeps the newest 50 failed runs.

Every task of every run goes into cache.db: time, CPU, peak memory, status and cache result. No export step, no API. The file is the API.

Terminal window
$ sqlite3 .vx/cache/cache.db "
SELECT project, task, status, duration_ms FROM runs
WHERE run_id = (SELECT run_id FROM runs ORDER BY id DESC LIMIT 1)
ORDER BY duration_ms DESC;"
@demo/ui|test|failed|207
@demo/ui|build|cache-hit|1

llms.txt indexes every docs page as markdown, and llms-full.txt is the whole site in one file. @vzn/vx also ships an agent skill, one SKILL.md that teaches an agent to run, debug and query vx:

Terminal window
$ mkdir -p .claude/skills/vx
$ cp node_modules/@vzn/vx/skills/vx/SKILL.md .claude/skills/vx/

For live questions over the Model Context Protocol, see Give your coding agent the build’s memory. The agents guide and machine-readable output have the details.

Turborepo, Nx and other product names are trademarks of their owners. vx is not affiliated with or endorsed by them.