The CLI is the shell-native way to run and control PMX Canvas. It targets
http://localhost:4313 by default — override with PMX_CANVAS_URL or
PMX_CANVAS_PORT when the server runs elsewhere, or per invocation with the
global --port <n> / --server-url <url> flags (any position; --server-url
wins over --port, and both win over the environment variables). An invalid
value for either flag is a hard error — the CLI never falls back silently to
the default port.
pmx-canvas node list --port 4750 # target a non-default daemon
pmx-canvas --server-url http://127.0.0.1:4750 status # same, by URLpmx-canvas pump turns any CLI agent (Codex, Amp, a script) into a steerable
board citizen: it long-polls the per-consumer delivery claim and runs your
command once per steer, marking per-consumer on success.
On Windows the exec runs through cmd.exe and receives the steer message on
stdin. The {message} and {id} placeholders are refused there: cmd.exe
expands %VAR% and re-parses the result, so a steer containing shell
metacharacters could inject commands. Read stdin, or reference
PMX_STEER_MESSAGE / PMX_STEER_ID inside your own script.
pmx-canvas pump --consumer codex-cli --exec 'codex exec --full-auto {message}'
pmx-canvas pump --consumer amp --exec 'amp -x {message}' --parent claude-codeThe Codex example starts a non-interactive CLI task. It does not steer an open Codex desktop task: desktop owns that thread's writer and requires a host adapter that uses the app's native follow-up channel.
- The steer text reaches the command as
$PMX_STEER_MESSAGE(env) and stdin;{message}in the template expands to the quoted env reference — steer content is never spliced into the shell line. - The delivery envelope rides along as env:
$PMX_STEER_ID,$PMX_STEER_SOURCE(who sent it),$PMX_STEER_TARGET(your consumer key, orALLfor a broadcast), and$PMX_STEER_CREATED_AT— so the host turn can say who steered, whether it was addressed, and how old it is. - Startup backlog is marked silently (pass
--backlog deliverto process it). - A failing command is retried twice, then the pump exits non-zero and leaves the steer pending so a failed host injection cannot look delivered.
--parent <key>rolls the pumped agent up under an orchestrator's chip;--onceprocesses a single steer (useful in scripts and tests).- Survives daemon restarts; backs off against pre-0.4.9 servers that ignore
waitMs.
pmx-canvas # Start canvas, open browser
pmx-canvas --demo # Start with the showcase demo board
pmx-canvas --port=8080 # Custom port
pmx-canvas --no-open # Headless (for agents/CI)
pmx-canvas --theme=light # dark | light | high-contrast | midnight | sepia | arctic | ember | forest | volt
pmx-canvas --mcp # Run as MCP server (stdio)
pmx-canvas --webview-automation # Start headless Bun.WebView session
pmx-canvas open # Open the current workbench in a browserRun detached with pid/log tracking instead of holding a terminal:
pmx-canvas serve --daemon --no-open --wait-ms=20000 # Start detached, wait for health
pmx-canvas serve status # Inspect daemon health + pid
pmx-canvas serve stop # Stop the daemon for this portpmx-canvas node add --type webpage --url https://example.com/docs
pmx-canvas node add --type web-artifact --title "Dashboard" --app-file ./App.tsx
pmx-canvas node add --type graph --graph-type bar --data-file ./metrics.json --x-key label --y-key value
pmx-canvas node add --type graph --graph-type bar --data '[{"x":"a","y":1}]' --x-key x --y-key y
pmx-canvas graph add --graph-type bar --data '[{"x":"a","y":1}]' --x-key x --y-key y # Alias
pmx-canvas html primitive add --kind choice-grid --data-file ./options.json --title "Options"
pmx-canvas html primitive schema --summary
pmx-canvas node add --help --type webpage --json # Schema for one type
pmx-canvas external-app add --kind excalidraw --title "Diagram"
pmx-canvas edge add --from-search "DVT O3 — GitOps" --to-search "deep work trend" --type relation
# Updates return a compact { ok, id, position, size, updatedFields } envelope;
# pass --full for the complete node payload (ext-app payloads can be huge).
pmx-canvas node update <node-id> --width 840 --height 620
pmx-canvas node update <node-id> --title "Renamed" --full--from-search / --to-search must each resolve to exactly one node — broad
queries fail rather than guess. Use the full visible title.
CLI create commands return the created node shape with normalized title, content, and geometry, which makes scripting stacked layouts and batch follow-ups easier.
Graph height flags split by target:
--node-height/--nodeHeight— the canvas node frame--chart-height— the chart content inside the node--height— accepted as a frame-height compatibility alias
For MCP/HTTP payloads, use nodeHeight for the frame and height for chart
content.
pmx-canvas node schema --type json-render --component Table --summary
pmx-canvas validate # Layout validation
pmx-canvas validate spec --type json-render --spec-file ./dashboard.json --summary
pmx-canvas validate spec --type html-primitive --kind choice-grid --data-json '{"items":[{"title":"A"}]}' --summaryThe schema commands surface the running server's data, which is strictly better than guessing flags or payloads.
pmx-canvas batch --file ./canvas-ops.jsonSee HTTP API → batch for the operation schema; the same JSON works for the CLI batch file.
pmx-canvas web-artifact build --title "Dashboard" --app-file ./App.tsx --deps recharts --include-logsFailed or empty CLI bundles print ok: false, exit non-zero, and do not
create a canvas node.
pmx-canvas watch consumes the SSE stream and emits compact semantic deltas
for agents that need low-token updates instead of full layout snapshots. It
filters noise from harmless moves and reports meaningful events such as pins,
node additions/removals, group changes, edge connections, and moves that
change spatial clustering.
pmx-canvas watch --events context-pin,move-end
pmx-canvas watch --json --events context-pin --max-events 1pmx-canvas focus <node-id> # Pan viewport to a node
pmx-canvas focus <node-id> --no-pan # Select/raise without panning
pmx-canvas fit # Fit the viewport to every node
pmx-canvas fit <id-a> <id-b> # Fit to just these nodesfit sizes itself to the connected browser window, so you rarely need to pass
--width / --height. After creating output a human should see, focus a single
node or fit the new ids — auto-placement is board-relative, not camera-relative.
pmx-canvas snapshot save --name "before-refactor"
pmx-canvas snapshot list
pmx-canvas snapshot restore <snapshot-id>
pmx-canvas snapshot delete <snapshot-id>
pmx-canvas snapshot gc --keep 20 # Delete all but the newest N
pmx-canvas diff <snapshot-id> # Compare the canvas against a snapshotAX commands expose the host-agnostic context contract used by adapters. Pins remain the explicit human-curated context set; AX focus is the current attention target and persists with canvas state and snapshots.
pmx-canvas ax status # Persisted AX state
pmx-canvas ax context # Pinned + focused agent context
pmx-canvas ax focus node-1 node-2 # Set AX focus
pmx-canvas ax focus --clear # Clear AX focusHost-agnostic agent-experience primitives. Timeline commands persist for diagnostics (retention-bounded, not snapshotted); work items, approval gates, and review annotations are canvas-bound and ride snapshots/restore.
# Timeline
pmx-canvas ax event add --kind tool-start --summary "ran tests"
pmx-canvas ax steer "focus on the failing test first"
pmx-canvas ax evidence add --kind test-output --title "unit pass"
pmx-canvas ax timeline --limit 50
# Work items (canvas-bound)
pmx-canvas ax work add --title "Wire up auth" --status in-progress node-1
pmx-canvas ax work update <id> --status done
pmx-canvas ax work list
# Approval gates (canvas-bound; pending → approved/rejected)
pmx-canvas ax approval request --title "Deploy to prod" --action deploy.prod
pmx-canvas ax approval resolve <id> --decision approved
pmx-canvas ax approval list
# Review annotations (canvas-bound)
pmx-canvas ax review add --body "off-by-one" --kind finding --severity error --file src/x.ts
pmx-canvas ax review list
# Host capability (own partition; survives clear)
pmx-canvas ax host report --host copilot --canvas --session-messaging
pmx-canvas ax host status# Node interaction — one capability-gated envelope (server re-validates + scopes)
pmx-canvas ax interaction --type ax.work.create --node node-1 --payload '{"title":"Wire auth"}'
# Delivery — claim pending steering for a consumer (loop-safe), then acknowledge
pmx-canvas ax delivery list --consumer copilot --limit 20
pmx-canvas ax delivery mark <steering-id>
# Elicitation — request structured human input, then answer
pmx-canvas ax elicitation request --prompt "Who owns this migration?" --fields owner
pmx-canvas ax elicitation respond <id> --response '{"owner":"alice"}'
pmx-canvas ax elicitation list
# Mode — request a plan/execute/autonomous transition, then resolve
pmx-canvas ax mode request --mode execute --reason "plan approved"
pmx-canvas ax mode resolve <id> --decision approved
pmx-canvas ax mode list
# Commands — list the registry, invoke a registry-gated command
pmx-canvas ax command list
pmx-canvas ax command invoke pmx.plan
pmx-canvas ax command invoke pmx.promote-context --args '{"nodeIds":["n1"]}'
# Policy — read / patch the tool/prompt policy (stored by PMX, enforced by adapters)
pmx-canvas ax policy get
pmx-canvas ax policy set --excluded-tools shell,write --mode concisepmx-canvas smoke # one-command check of a running canvas
pmx-canvas smoke --port 4750 # target a specific daemonVerifies server health + workspace, CLI/server version skew, the MCP initialize handshake, a temporary node create/search/remove round-trip (the temp node is always removed, including on failure), and board validation. Prints a JSON report and exits 1 if any check fails.
Install the bundled GitHub Copilot extension adapter into a repo. The adapter maps onto the same neutral AX surfaces (it never makes the core GitHub-specific).
pmx-canvas copilot install-extension --dry-run # Preview target, writes nothing
pmx-canvas copilot install-extension --yes # Install/overwrite into .github/extensions/pmx-canvas/Refresh the pmx-canvas skill copies already installed in this workspace.
The command discovers them (any <agent-dir>/skills/pmx-canvas[-testing],
whichever agent layout the workspace uses) and compares/replaces whole trees —
references, evals, and fixtures included — so a copy can no longer drift while
its SKILL.md hash matches. It never creates a copy where none is installed
and makes no assumptions about which agents you use.
pmx-canvas skills sync --yes # Refresh every installed copy from the package
pmx-canvas skills sync --check # Report drift only (exit 1 when stale), change nothingReplacing a tree is destructive, so the sync requires --yes; without it the
command only reports drift (like --check) and hints at the flag. A directory
whose SKILL.md identity doesn't match the bundled skill is skipped, never
replaced.
Run skills sync --yes (and copilot install-extension --yes if the repo uses the
Copilot adapter) after every pmx-canvas upgrade so agents operate on the
released guidance.
Drive a headless Bun.WebView (Chromium or WebKit) pointed at the workbench:
pmx-canvas webview status
pmx-canvas webview start --backend chrome --width 1440 --height 900
pmx-canvas webview evaluate --expression "document.title"
pmx-canvas webview resize --width 1280 --height 800
pmx-canvas webview screenshot --output ./canvas.png
pmx-canvas webview stopUse WebView for visual annotation inspection. Agent-readable canvas context only
reports annotation targets and bounds; it does not describe whether the human
drew an arrow, line, circle, or other shape. Inspect .annotation-layer path or
take a screenshot when the drawn form matters.
Humans draw with the rail's Annotate popover (pen / text) and remove marks with its eraser.
If an agent already knows the annotation ID from context, it can remove it through
MCP with canvas_view { action: "remove-annotation", id }.
- Direct terminal control without MCP wiring
- Shell scripts and CI-friendly automation
- Schema-driven discovery from the running server
- Local debugging of canvas, webview, and screenshot flows
- A control surface that covers normal canvas work without MCP wiring