The full reference
Every tool in the stack.
The seven headline products are how the stack introduces itself — but nothing is held back. This is the
complete index of 54 command-line tools a person actually invokes, grouped by what they do. Search to jump straight to
one, or click any tool to
expand its captured --help — flags, options, and
examples.
/ to focus ↑↓ to move ↵ to jump
Tasks & Coordination
The coordination layer — where agents get work, message each other, and land it.
JAT - AI Agent Task IDE
USAGE:
jat Launch the IDE (production build, default)
jat --dev Launch the IDE with HMR dev server
jat demo Launch demo environment
jat list Show all projects
jat init Auto-detect projects in ~/code
QUICK START:
jat init Auto-detect and add projects
jat Launch IDE to manage agents
jat --dev Use when editing the IDE itself (hot reload)
PROJECT MANAGEMENT:
init Auto-detect projects in ~/code
add <path> [name] Add a project
join-project <name> --postgres-url <url> [--path PATH]
Join a graduated (Postgres) project
remove <name> Remove project
list Show all projects with status
UPDATES:
update Pull latest JAT updates
update --check Check for updates without installing
update --status Show installation path and version
update --disable Disable automatic update checks
update --enable Enable automatic update checks
DEMO MODE:
demo Launch demo with sample projects
demo setup Fresh setup (cleans & recreates)
demo on/off Toggle demo visibility
SCHEDULER:
scheduler Show scheduler status (default)
scheduler start Start the scheduler daemon
scheduler stop Stop the scheduler daemon
scheduler restart Restart the scheduler daemon
scheduler logs Attach to scheduler session
SESSION MANAGEMENT:
resume <agent> Resume session by agent name (e.g., CrystalTundra)
resume <id> Resume session by ID (partial OK)
resume --list List recent sessions
find-session <agent> Find session ID for an agent
-r Shortcut for resume
-fs Shortcut for find-session
ADVANCED:
cleanup Remove stale agents
config Show configuration
CONFIG: ~/.config/jat/projects.json jt - JAT task CLI
Usage: jt <command> [options]
Commands:
create "title" [--title X] [--type X] [--priority N] [--description "..."]
[--labels "a,b"] [--deps "id1,id2"] [--assignee "X"]
[--files "glob1,glob2"] [--parent EPIC_ID]
[--model low|medium|high|max] [--json]
Run 'jt create --help' for all options
update TASK_ID [--status X] [--priority N] [--assignee X] [--title X]
[--description "..."] [--notes "..."] [--labels "a,b"]
[--files "glob1,glob2"] [--type X] [--reason "..."] [--json]
[--no-reason-comment]
--reason is REQUIRED when --status is waiting/blocked or
--labels adds a blocked:* label (see shared/hold-labels.md).
--no-reason-comment records the reason without posting it
as a comment — only for callers that already wrote it to
the task thread themselves (e.g. jat-signal waiting)
close TASK_ID [--reason "..."] [--json]
delete TASK_ID
show TASK_ID [--json]
list [--status X] [--type X] [--assignee X] [--limit N] [--json]
ready [--json] [--limit N]
search "query" [--status X] [--type X] [--labels "a,b"]
[--updated-after DATE] [--limit N] [--json]
dep add TASK_ID DEPENDS_ON_ID
dep remove TASK_ID DEPENDS_ON_ID
dep tree TASK_ID [--reverse] [--json]
dep cycles
epic-status EPIC_ID [--json] Ground-truth epic state (DB + signals + tmux)
data tables | schema | query | exec | create | drop | columns
bases list | show | create | delete | attach | detach | render | search
sessions search <keyword> [--project NAME] [--limit N] [--json]
Search Claude Code session history by keyword
spawn [TASK_ID] [--remote] [--local] [--model M] [--attach]
agents [--json] [--project X] [--all] List active agents
logs <agent-name> [--remote] [--follow] [--lines N] Stream agent output
ssh [agent-name] Connect to VPS or attach to remote agent session
audit [--json] [--fix] Check project context injection compliance
graduate [PROJECT] --url <postgres-url> [--dry-run] [--yes] [--force]
Migrate project tasks from SQLite to Postgres
downgrade [PROJECT] [--dry-run] [--yes] [--force] [--confirm PHRASE]
Reverse graduation: Postgres → local SQLite
init [--quiet]
Options:
--version Show version
--help Show this help
Status values: open, in_progress, waiting, blocked, submitted, accepted, deployed, closed, dev
Task types: bug, feature, task, epic, chore jat-send — Write a steering message to an agent's mailbox, or post to a channel.
Usage:
jat-send <agent> "text" # kind=steer (default)
jat-send <agent> --complete # kind=complete
jat-send <agent> --answer "x" # kind=answer (label or 0-based index)
jat-send <agent> --nudge # kind=nudge (wake-only, no text payload)
jat-send --channel <name> "text" # post to a channel room (no DM)
Channel mode (--channel):
Posts the message to the named channel in ~/.agent-mail.db instead of an
individual mailbox. The channel must exist (create with `jat-channel create`).
Sender is resolved the same way as DMs (--from > AGENT_NAME > tmux session).
Agent members of the channel receive a mailbox nudge so they drain and see
the post; the nudge text includes the channel name and author.
Mailbox format: ~/.config/jat/mailbox/<agent>.jsonl
Each line is a self-contained JSON object (never split across lines):
{ "id": "<uuid>", "ts": "<iso8601>", "from": "<sender>",
"kind": "steer|complete|answer|nudge",
"text": "...", # steer/answer only
"taskId": "<id>" } # optional; set via --task or TASK_ID env
Drain-and-ack protocol (consumer side):
1. Acquire a drain lock: flock -n ~/.config/jat/mailbox/<agent>.lock
2. Read all lines from <agent>.jsonl into memory.
3. Append those lines to <agent>.consumed.jsonl (O_APPEND atomic write).
4. Truncate <agent>.jsonl to zero bytes (> <agent>.jsonl).
5. Release lock and process the messages in memory order.
Consumed messages are NEVER deleted — <agent>.consumed.jsonl is forensics.
Exactly-once: the drain lock ensures a concurrent jat-send between steps 3
and 4 lands in the live file and is picked up on the next drain cycle.
Concurrency guarantee:
This tool writes exactly one JSON line per invocation using a single
O_APPEND write (via >> redirection after building the full line in a
variable). POSIX guarantees that O_APPEND writes up to PIPE_BUF bytes
are atomic on regular files — a single line stays intact even under
concurrent jat-send calls. Lines are kept ≤ 4095 bytes to stay inside
PIPE_BUF on Linux (4096) and macOS (512 bytes pipe buf, but filesystem
O_APPEND is page-atomic). Text longer than ~3800 chars is accepted but
the caller should be aware this guarantee weakens on extremely long lines
(>PIPE_BUF) — in practice steer messages are well under this limit.
From identity resolution (--from flag > AGENT_NAME env > jat-* tmux session
name > overseer-<pid>). This must never fail an urgent steer on an
unresolvable identity.
Options:
--channel <name> Post to a channel room instead of a DM mailbox
--complete Send a completion trigger (kind=complete, no text)
--answer <x> Send an answer (kind=answer; x = label text or 0-based index)
--nudge Send a minimal wake token (kind=nudge, no text)
--task <id> Attach task ID to the message (default: $TASK_ID env)
--from <name> Override sender identity
--mailbox-dir <d> Override mailbox root (default: ~/.config/jat/mailbox)
--dry-run Print the JSON line without writing it
--no-verify Skip the post-wake drain WAIT (bulk senders). The
mail-deaf preflight still runs — see below.
--help Show this help
Delivery verification:
Writing the mailbox and firing the wake token proves nothing about DELIVERY.
If the target project does not register worker-inbox-trigger.sh on
UserPromptSubmit, the wake token is executed as a shell command, hits
jat-inbox's bare-invocation HOOK MODE, reads empty stdin and exits 0 — the
message is never surfaced and the sender sees a cheerful "woke <Agent>".
So after waking, verify in two stages:
1. PREFLIGHT (always, instant) — does any settings file governing the
target project register worker-inbox-trigger.sh on UserPromptSubmit?
No => the message can never arrive: exit 3 with remediation. This runs
even under --no-verify, so opting out of the wait never buys silence.
2. CONFIRMATION (opt-out, up to 3s) — poll the mailbox for this message
id. Drained => confirmed. Still queued => the agent is busy mid-turn
and its Stop-hook drain will collect it: report and exit 0.
Environment:
JAT_SEND_NO_WAKE=1 Write the mailbox but never send the wake token
JAT_SEND_NO_VERIFY=1 Same as --no-verify
JAT_SEND_VERIFY_TIMEOUT Seconds to wait for the drain (default: 3)
Exit codes:
0 message written / posted (delivery confirmed, or pending on a live agent)
1 usage error
3 message written but UNDELIVERABLE — target project registers no mailbox
drain hook. The message sits in the mailbox until that is fixed. jat-inbox — Drain an agent's mailbox and dispatch owner control verbs.
Called by worker-inbox-trigger.sh (UserPromptSubmit hook) when the "jat-inbox"
trigger token arrives. Can also be invoked standalone for testing.
Usage (hook mode):
echo '<hook-json>' | jat-inbox
stdin: Claude Code hook JSON { "session_id": "...", "prompt": "..." }
stdout: Claude Code hookSpecificOutput JSON
Usage (standalone / test):
AGENT_NAME=TestAgent jat-inbox --drain-only
→ drains mailbox, prints messages to stdout, no hook JSON wrapper
Control verb interception:
Any drained message whose body_md or text starts with (or contains) a
recognised !verb is intercepted by control-verbs.sh BEFORE being delivered
to Claude. Owner-only gate: verb from a non-operator sender is logged and
silently dropped. jat-search - Unified search across tasks, memory, files, and sessions
Usage:
jat-search "query" Meta search (all sources)
jat-search tasks "query" [options] Deep task search (FTS5 + vector)
jat-search memory "query" [options] Memory search (FTS5 + vector)
jat-search files "query" [options] File search (ripgrep + filename)
jat-search sessions "query" [options] Claude session history (ripgrep)
Options:
--project PATH Project path (tasks/memory/files: default cwd;
sessions: default ALL projects, narrows to one)
--limit N Max results (meta: 5/source, individual: 10)
--json JSON output (default for meta search)
--summarize LLM synthesis of meta results
--verbose Debug info to stderr
--help Show this help
Examples:
jat-search "authentication" Search everything
jat-search tasks "OAuth timeout" --json Search tasks only
jat-search memory "browser automation" Search memory
jat-search files "searchTasks" Search file contents
jat-search sessions "isolated-index" Find a Claude session by what
you said, then claude --resume
jat-search "auth" --summarize Meta search with LLM synthesis
Sessions search greps every ~/.claude/projects/**/*.jsonl transcript and
returns matching sessions newest-first with a context snippet and a ready
`claude --resume <id>` command — for recovering a session you crashed out of. jat-skills - Skill catalog, installer, and local management
CATALOG:
jat-skills search <query> Search catalog by keyword
jat-skills list-available List all catalog skills
jat-skills info <skill-id> Show skill details
jat-skills refresh Force refresh the catalog cache
jat-skills sources Show source status
INSTALL & MANAGE:
jat-skills install <name-or-url> Install a skill locally
jat-skills list List installed skills
jat-skills enable <name> Enable an installed skill
jat-skills disable <name> Disable an installed skill
jat-skills uninstall <name> Remove an installed skill
jat-skills update <name> Re-fetch skill from source
jat-skills sync Sync agent links (repair manually)
OPTIONS:
jat-skills --json Output as JSON (combine with other commands)
jat-skills --help Show this help
EXAMPLES:
jat-skills search github # Find GitHub-related skills
jat-skills install git-commit # Install from catalog by name
jat-skills install https://github.com/user/repo # Install from URL
jat-skills list # Show installed + status
jat-skills disable git-commit # Disable without removing
jat-skills update git-commit # Re-fetch latest SKILL.md
INSTALL SOURCES:
- Catalog name Looks up skillMdUrl from catalog
- GitHub repo URL Fetches SKILL.md from repo root or subdirectory
- Raw URL Fetches SKILL.md directly
SKILL DIRECTORIES (searched by agents):
1. Project-level {project}/skills/
2. User-level ~/.config/jat/skills/
3. JAT built-in ~/code/jat/skills/
INSTALLED SKILLS:
Skills stored in ~/.config/jat/skills/{name}/SKILL.md
Registry at ~/.config/jat/skills/installed.json
AGENT LINKS (auto-synced on install/enable/disable/uninstall/update):
Claude Code ~/.claude/commands/{name}.md → SKILL.md symlink
Pi ~/.pi/agent/skills/{id}/ → skill dir symlink
Other agents receive skill info via prompt at spawn time $ am-register --help
Usage: am-register --name NAME [options]
Register or resume an agent identity in Agent Mail.
IMPORTANT: Agent names are globally unique across all projects.
If an agent name already exists, this command will resume that agent
(updating last_active timestamp) instead of creating a duplicate.
NOTE: IDE-spawned agents are pre-registered by the spawn API.
This command is for manual/CLI use only.
Options:
--name NAME Agent name (required, e.g., BlueLake)
--program PROGRAM Program name (default: claude-code)
--model MODEL Model name (default: sonnet-4.5)
--task TASK Task description (default: "")
--project PATH Project path (default: current directory)
--json Output as JSON
--help, -h Show this help
Examples:
am-register --name BlueLake
am-register --name FairBay --program cursor --model gpt-4 --task "Bug fixes"
am-register --name GoldDusk --json
Database: ~/.agent-mail.db
$ am-agents --help
Usage: am-agents [options]
List all agents registered in a project.
Options:
--project PATH Project path (default: current directory)
--json Output as JSON
--help, -h Show this help
Examples:
am-agents
am-agents --json
am-agents --project /path/to/repo
Database: ~/.agent-mail.db
$ am-whoami --help
Usage: am-whoami [options]
Show current agent identity and context information.
Options:
--agent AGENT Agent name (default: $AGENT_NAME environment variable)
--project PATH Project path (default: current directory)
--json Output as JSON
--help, -h Show this help
Environment:
AGENT_NAME Default agent name (overridden by --agent)
PROJECT_KEY Default project path
AGENT_MAIL_DB Database location
Examples:
am-whoami
AGENT_NAME=Alice am-whoami
am-whoami --agent Bob --json
Database: ~/.agent-mail.db jat commands
The jat IDE workflow — the slash commands and skills an agent runs to pull work, land it, verify it, and hand its context off cleanly.
/jat:compact (alias: /jat-compact) — structured work-context compaction for long agent sessions.
A generic /compact summarizes the transcript and hopes the important parts
survive. jat-compact does the opposite: before the window is truncated it
EXTERNALIZES the session's real working state to durable storage, so nothing
load-bearing depends on surviving as prose.
WHAT IT EXTERNALIZES:
- Tasks touched or created, each with the reason it exists
- Knowledge-base blocks written this session
- People, credentials minted, agents still in flight
- The waiting-on-user list and operational lessons learned
- A short transfer note capturing where things stand
THEN it hands back a one-paragraph RELOAD KEY. Paste that after the
compaction (or in a fresh session) and full working context is restored from
the externalized files — not reconstructed from a lossy summary.
WHEN TO REACH FOR IT:
- A session is context-heavy and approaching its limit
- Before processing a large input (a long transcript, a big document)
- Any time you would otherwise say "compact" or "hand this off"
Runs inside the jat IDE session. Nothing leaves your machine. /jat:start — begin working on a task in the jat IDE.
Orients a freshly-spawned agent before it touches any code:
1. Reads the project's context and conventions
2. Pulls the next ready task off the shared queue (or the one it was
spawned for)
3. Claims the task's file lease so no sibling agent can edit the same files
4. Sets up its workspace and confirms what "done" means for the task
The result is an agent that knows what it is doing and cannot collide with
the rest of the fleet. Pairs with /jat:complete, which closes the same task
out through the verification gate. /jat:complete — finish the current task properly, then end the session.
Rather than trusting "looks done", it runs the full verification gate before
anything is marked complete:
- Builds the project and runs its checks / type-checks
- Drives the change in a real browser where the task touched UI, capturing
console + network evidence for review
- Writes a completion summary and closes the task
- Releases the file lease and ends the session cleanly
If the gate fails, the task stays open and the failure is reported instead of
silently passing. Session ends after a successful completion. /jat:verify — escalatory browser verification of a change.
Opens the app in a real browser and actually exercises the change end to end
instead of asserting from the code alone:
- Navigates the affected flow and interacts with it
- Captures console errors and network failures as they happen
- Escalates through progressively deeper checks until it can confirm the
change works, or surface exactly where it breaks
Produces an evidence bundle (screenshots, HAR, console log) that rides along
for a human or overseer to review. Used on its own, or as the browser leg of
/jat:complete. /jat:ask — ask a question about your own projects, answered locally.
The slash-command front door to jat-ask. It gathers context from your own
tasks, notes, memory, and recent activity, answers the question, and shows
the sources it drew from — all on local inference, so no project data leaves
your network.
USAGE:
/jat:ask what open tasks are blocking the voice feature?
/jat:ask summarize what changed in the billing flow this week
Good for reorienting after time away, or pulling a fact out of a large
project without grepping for it by hand. /jat:tasktree — turn a spec into a structured, dependency-wired task tree.
Feed it a PRD, a requirements doc, or a rough spec and it produces a task
tree the fleet can actually execute:
- Breaks the work into concrete tasks and sub-tasks
- Wires dependencies so nothing starts before its prerequisites
- Assigns priorities and, where useful, file partitions so tasks can run
in parallel without conflicting
The output lands in the shared task queue, ready for agents to pull via
/jat:start. This is how a document becomes a plan the fleet can run. /jat:fanout — spawn a wave of agents partitioned by file.
Given a set of tasks, it launches a wave of agents with the work partitioned
by file, so two agents can never edit the same file at the same time — the
core trick that lets a fleet run wide without stepping on itself.
WHAT YOU GET:
- Many tasks running in parallel, each in its own file lease
- No merge conflicts between wave members by construction
- A single notification when the whole wave has landed, so you wait once
instead of babysitting each agent
Best paired with /jat:tasktree, which produces the file-partitioned tasks a
fan-out consumes. /jat:design-campaign — run a best-in-class design skill across every route
of an app, at fleet scale (alias: /route-campaign).
This is the metaskill pattern jat is built for: take an excellent external
skill and drive a whole fleet of agents through it, with adjudication and
verification wrapped around the outside.
HOW IT RUNS:
- Scans the app's routes and spawns a manager agent per route group
- Each manager critiques its route (via the external `impeccable` design
skill), fixes what it finds, gap-lists to a perfect score, then fans out
file-partitioned builder agents to do the work without conflicts
- The invoking session acts as campaign manager — adjudicating findings,
sequencing stages, and holding the quality bar
COMPOSES: impeccable — jat supplies the orchestration, leases, and
verification; the outside skill supplies the design judgment. jat does not
reimplement the skill, it runs it across the whole codebase at once.
USAGE:
/jat:design-campaign all
/jat:design-campaign /dashboard Web & Browser
Drive a browser — your own consented session or the agent's headless one — down to a record-once API client, plus the domains and deploys around a web app.
pilot — drive YOUR browser from the CLI (consent-scoped, tabex-style)
START HERE
setup [--port N] One-time config + browser relaunch instructions (default port 9223)
status Connection + consent summary
tabs [filter] [-v] Scannable tab list (IDX ID EN HOST TITLE); [filter] narrows
by host/title/url; -v adds full URLs
pick [filter] Interactive fuzzy picker (fzf: arrows/mouse/type/Enter) → enables the tab
CONSENT
enable <tab> Enable a tab (index, id prefix, or url substring)
disable <tab> Revoke it
auto-attach add|rm|list [host] Standing rules: tabs on <host> always enabled
ACT (targeting: --tab ID|IDX, --match-url S, --match-title S, --open-url URL [--wait-timeout MS])
nav <url> Navigate the target tab
element list Visible interactive elements
element click --text "Save" Click by visible text / aria-label (preferred)
element fill --label "Email" --value X
page click|text|html [--selector CSS] CSS-level escape hatch
js "code" Page-context JS (return/await ok) — last resort
key "<combo>" Trusted keypress: "?", "Escape", "Control+K", "Control+Shift+Z" [--repeat N]
type "text" Trusted typing [--focus "Label"] [--delay MS]
wait --text S | --selector CSS | --ms N Block until condition [--timeout MS]
assert --text S | --not-text S | --selector CSS [--count N] Exit 3 if DOM doesn't match
reload | back | forward History navigation
shot [--output F] [--full] [--selector CSS] Screenshot (page or one element)
OBSERVE (consent-scoped, in-process)
console [--filter error|warn] [--duration MS] [--limit N] Console + runtime errors (incl. buffered)
net [--url S] [--failures] [--duration MS] [--limit N] Network activity over a window
snapshot [--selector CSS] [--max-depth N] Accessibility tree
monitor <tab> [--har] [--shots N] [--duration MS] [--no-net] [--url S]
LIVE-TAIL a tab (ssh-friendly): nav/console/errors/network as
timestamped lines + JSONL session log under ~/.local/state/jat/pilot-monitor/
replay <events.jsonl> [--realtime] Re-print a monitor session (paced with --realtime)
CAPTURE
record start|stop|status [--output F] Record traffic to HAR — enabled tabs only (via browser-har.js)
client <file.har> Derive a curl API client (via har2client.js)
Every command accepts --json. Acting on a non-enabled tab exits 2. Usage: browser-element.js list|click|fill [options]
list List visible interactive elements
click --text "Save" Click the element matching visible text / aria-label
fill --label "Email" --value "x@y.z" Fill the field matching its label / aria-label / name
Targeting:
--match-url <substr> Target the tab whose URL contains <substr>
--match-title <substr> Target the tab whose title contains <substr>
--open-url <url> Open a new tab at <url> and target it
--wait-timeout <secs> Timeout for --match-*/--open-url (default: 10)
--agent <name> Tag the tab-ownership registry with this agent
--task <id> Tag the tab-ownership registry with this task id
--port <number> Chrome DevTools port (default: $JAT_BROWSER_PORT or 9222)
--json Machine-readable output
Examples:
browser-element.js list
browser-element.js click --text "Save"
browser-element.js fill --label "Email" --value "jw@example.com" --match-url "/settings" Usage: browser-eval.js "<javascript>" [--json]
Execute JavaScript in the active page of the agent browser and print the
result. Supports multi-statement code with an explicit return:
browser-eval.js "const x = 5; const y = 10; return x + y"
browser-eval.js "return document.title"
Options:
--json Machine-readable result
Note: this tool has no --help flag (its first argument is the JS to run);
usage above is from the global-tools reference. Usage: browser-screenshot.js [--port <number>] [--output <path>] [--json]
Options:
--port <number> Chrome DevTools port (default: $JAT_BROWSER_PORT or 9222)
--output <path> Where to save the screenshot (default: a tmp file)
--match-url <substr> Target the tab whose URL contains <substr>
--match-title <substr> Target the tab whose title contains <substr>
--open-url <url> Open a new tab at <url> and target it
--wait-timeout <secs> Timeout for --match-*/--open-url (default: 10)
--agent <name> Tag the tab-ownership registry with this agent
--task <id> Tag the tab-ownership registry with this task id
--json Print {path: '...'} instead of the bare path Usage: browser-snapshot.js [options]
Options:
--selector <css> Snapshot only this element and its children
--max-depth <n> Maximum tree depth (default: 10)
--include-hidden Include hidden elements
Examples:
browser-snapshot.js
browser-snapshot.js --selector 'main'
browser-snapshot.js --max-depth 5 --include-hidden Usage: browser-console.js [options]
Options:
--follow, -f Follow console output (live stream)
--filter <type> Filter by type: log, warn, error, info, debug
--limit <n> Limit output to n messages (default: 100)
Examples:
browser-console.js
browser-console.js --follow --filter error
browser-console.js --limit 50 Usage: browser-network.js [options]
Options:
--follow, -f Follow network requests (live stream)
--type <type> Filter by resource type: xhr, fetch, document, script, stylesheet, image
--url <pattern> Filter by URL pattern (substring match)
--limit <n> Limit output to n requests (default: 100)
Examples:
browser-network.js
browser-network.js --follow --type xhr
browser-network.js --url 'api.example.com' --limit 50 Usage: browser-wait.js --<type> <value> [--timeout <seconds>]
Wait Types:
--text <text> Wait for text to appear on page
--selector <selector> Wait for CSS selector to exist
--url <url> Wait for URL to change to/contain value
--eval <expression> Wait for custom JavaScript expression to be truthy
Options:
--timeout <seconds> Maximum wait time for the condition above (default: 30)
--match-url <substr> Target the tab whose URL contains <substr>
--match-title <substr> Target the tab whose title contains <substr>
--open-url <url> Open a new tab at <url> and target it
--wait-timeout <secs> Timeout for --match-*/--open-url tab targeting (default: 10)
--agent <name> Tag the tab-ownership registry with this agent
--task <id> Tag the tab-ownership registry with this task id
--json Machine-readable output
Examples:
browser-wait.js --text "Login successful"
browser-wait.js --selector ".user-profile"
browser-wait.js --url "/dashboard"
browser-wait.js --eval "document.readyState === 'complete'"
browser-wait.js --text "Welcome" --timeout 60 Usage: browser-tabs.js <list|release|open> [options]
Commands:
list [--port N] [--json] List live tabs + ownership registry
release <targetId> [--port N] Clear ownership metadata for a tab (does not close it)
open <url> [--port N] [--agent NAME] [--task ID] [--wait-timeout SECS]
Open a new tab and record ownership
Options:
--port <number> Chrome DevTools port (default: $JAT_BROWSER_PORT or 9222)
--json Machine-readable output (list only)
The registry is advisory, not enforced — it's the browser twin of jat-lease-claim.
Other browser-*.js tools update it automatically via --match-url/--match-title/--open-url. Usage: browser-har.js <start|stop|status> [options]
Record network traffic from the agent browser (CDP) to a HAR file.
Commands:
start Start a detached recorder attached to all tabs
stop Stop the recorder and finalize the HAR file
status Show whether a recorder is running and where it writes
Options:
--output FILE HAR output path (default: /tmp/jat-har-9222.har)
--filter SUBSTR Only record requests whose URL contains SUBSTR
--port PORT CDP port (default: $JAT_BROWSER_PORT or 9222)
--json Machine-readable output (start/stop/status)
Workflow:
browser-har.js start --output /tmp/ide.har
browser-nav.js http://localhost:3333/tasks # drive the app
browser-har.js stop
har2client.js /tmp/ide.har --output /tmp/ide-client.sh Usage: har2client.js FILE.har [--output client.sh] [--format sh|md|openapi]
Derive a direct HTTP client from a recorded HAR — record one browser session,
then skip the browser and call the API directly.
har2client.js /tmp/site.har --output /tmp/site-client.sh
/tmp/site-client.sh # list derived endpoints
/tmp/site-client.sh get_api_tasks 'status=open'
Options:
--output PATH Where to write the generated client script
--format sh Bash client (default); observed auth headers go to a
chmod-600 <client>.sh.env sidecar (client stays secret-free)
--format md Print just the endpoint map
--format openapi Emit an OpenAPI 3.0 spec (paths, params, inferred schemas)
Filters out static assets and parameterizes ID-like path segments
(/api/tasks/{id}). Note: this tool has no --help flag (its first argument is
the HAR file); usage above is from the global-tools reference. jat-browser - Browser session registry for multi-agent CDP coordination
USAGE:
jat-browser claim <port> --agent NAME --task ID [--pid PID] [--project NAME]
jat-browser release <port>
jat-browser release-agent <name>
jat-browser status [--json]
jat-browser list [--json]
jat-browser available [--json]
jat-browser cleanup
jat-browser flows [--json] [name]
jat-browser help
SUBCOMMANDS:
claim Claim a port for an agent (fails if taken and not stale)
release Release a specific port
release-agent Release all ports owned by an agent
status Show all claims (runs stale cleanup first)
list Probe ports for LIVE CDP browsers (version + tab count)
available Print next free port in 9222-9231 range
cleanup Remove stale entries (dead PID or closed port)
flows Machine-readable recipes for canonical browser workflows
help Show this help
OPTIONS (for claim):
--agent NAME Agent name (required)
--task ID Task ID (required)
--pid PID Chrome process PID (default: auto-detect from port)
--project NAME Project name (optional)
OPTIONS (for status/list/available/flows):
--json Machine-readable output
EXAMPLES:
jat-browser claim 9222 --agent FairBay --task jat-abc
jat-browser release 9222
jat-browser release-agent FairBay
jat-browser status
jat-browser list --json
jat-browser available
jat-browser flows
jat-browser flows record-derive-client --json
PERSISTENT BROWSER:
A systemd user service keeps a persistent agent browser on port 9222.
'jat-browser list' marks it clearly so you don't release/kill it.
REGISTRY:
/tmp/jat-browser-sessions.json (cleared on reboot = clean state)
PORT RANGE:
9222-9231 (10 ports for Chrome DevTools Protocol) Usage: jat-run-workspace.js <start|stop|status|screenshot|list> --task <id> [options]
Per-task evidence workspace: network HAR + console log + screenshots.
Commands:
start Start recording network (browser-har.js) + console for a task
stop Stop recording, finalize the HAR + console log, write summary.json
status Show whether a recording is active for a task
screenshot Save a screenshot into the task's run dir
list List the files captured for a task
Options:
--task <id> Task ID — determines .jat/runs/<id>/ (required)
--label NAME Free-text label recorded in meta.json (start only)
--filter SUBSTR Only record requests whose URL contains SUBSTR (start only)
--port PORT CDP port (default: $JAT_BROWSER_PORT or 9222)
--json Machine-readable output (start/stop/status)
Workflow:
jat-run-workspace.js start --task jat-abc
browser-nav.js http://localhost:3333/tasks # drive the app
jat-run-workspace.js screenshot --task jat-abc --label after-save
jat-run-workspace.js stop --task jat-abc
jat-runtime-verify --task jat-abc # auto-reads the captured HAR jat-deploy - Trigger a Coolify deploy for a JAT project
USAGE:
jat-deploy [PROJECT] [--watch] [--api-url URL]
ARGUMENTS:
PROJECT Project name. Defaults to the current git repo's basename.
OPTIONS:
--watch Poll deployment status until finished/failed, then exit.
--api-url URL Override the Coolify API base URL.
-h, --help Show this help.
SECRETS (via jat-secret):
{project}-coolify-app-uuid Application UUID from Coolify dashboard.
coolify-api-token Coolify API token (Bearer auth).
EXAMPLES:
jat-deploy # deploy current project, fire-and-forget
jat-deploy myapp --watch # deploy myapp and stream build status
jat-deploy --api-url http://other:8000 some-app
The API URL must be set via projects.json (projects.{project}.coolify_api_url) or --api-url. Usage: porkbun <command> [args]
Commands:
ping Test API credentials
check DOMAIN [DOMAIN...] Check availability + price
buy DOMAIN [--yes] [--dry-run] Register a domain (quotes price first;
asks for confirmation unless --yes)
list List all domains in the account
dns list DOMAIN List DNS records
dns add DOMAIN TYPE NAME CONTENT [--ttl N]
Add a record. NAME "" or "@" = apex.
e.g. porkbun dns add example.com A @ 203.0.113.10
dns rm DOMAIN ID Delete a record by id (from dns list)
Environment:
PORKBUN_API_KEY / PORKBUN_SECRET_KEY (auto-loaded from jat-secret
porkbun-api-key / porkbun-secret-key if unset)
Notes:
- Each domain needs "API Access" enabled in the Porkbun dashboard
(or flip "Opt In All Domains" once in Account > API Access).
- buy requires verified account email+phone and payment on file.
- Some TLDs (.us .ca .eu .au) are not API-registerable. Usage: godaddy <command> [args]
Commands:
ping Test API credentials (lists domain count)
list List all domains in the account
dns list DOMAIN [TYPE] List DNS records (optionally filter by TYPE)
dns get DOMAIN TYPE NAME Get specific record(s)
dns set DOMAIN TYPE NAME CONTENT [--ttl N]
Replace record(s) of TYPE+NAME. NAME "@" = apex.
e.g. godaddy dns set example.com TXT @ "v=spf1 ..."
NOTE: GoDaddy PUT replaces ALL records of that TYPE+NAME.
dns add DOMAIN TYPE NAME CONTENT [--ttl N]
Append a record (fetches existing + adds, then PUTs the set).
mail-audit DOMAIN Print SPF / DKIM selectors / DMARC / MX for the domain
export DOMAIN Dump all DNS records as JSON (for migration)
Environment:
GODADDY_API_KEY / GODADDY_API_SECRET (auto-loaded from jat-secret
godaddy-api-key / godaddy-api-secret if unset)
Notes:
- Get keys at developer.godaddy.com > API Keys > Production (needs a
domain in the account; the Production key works on api.godaddy.com).
- GoDaddy's DNS PUT is REPLACE-by-(type,name); `dns add` reads-modifies-writes
to avoid clobbering. Always `dns list` first. Usage: cloudflare <command> [args]
Commands:
ping Verify the API token
list List zones (domains) in the account
dns list DOMAIN List DNS records for a zone
dns add DOMAIN TYPE NAME CONTENT [--ttl N] [--proxied]
Add a record. NAME "" or "@" = apex.
e.g. cloudflare dns add example.com A @ 203.0.113.10 --proxied
dns rm DOMAIN ID Delete a record by id (from dns list)
Environment:
CLOUDFLARE_API_TOKEN (auto-loaded from jat-secret cloudflare-api-token,
then jat-secret cloudflare, if unset)
Notes:
- Token needs Zone:Read for list, Zone:DNS:Edit for dns add/rm.
- Registrar operations (buy/renew) are not in Cloudflare's public API;
use the dashboard for registrations. Media
Generate and edit images, video, and speech — cloud models where they shine, local + unfiltered where privacy matters — plus the first-party ffmpeg engine that assembles them.
$ gemini-image --help
Usage: gemini-image "PROMPT" [OUTPUT] [options]
Generate an image from a text prompt using Gemini.
Arguments:
PROMPT Text description of the image to generate (required)
OUTPUT Output file path (default: ~/Pictures/gemini-TIMESTAMP.png)
Options:
--model MODEL Model to use (default: gemini-3.1-flash-image-preview)
Available: gemini-3.1-flash-image-preview (fast, Nano Banana 2)
gemini-3-pro-image-preview (quality, 4K)
--aspect RATIO Aspect ratio: 1:1, 16:9, 9:16, 4:3, 3:4, etc.
--size SIZE Image size: 1K, 2K, 4K (4K requires pro model)
--help, -h Show this help
Examples:
gemini-image "A sunset over mountains"
gemini-image "Product photo of a coffee mug" product.png --aspect 1:1
gemini-image "Detailed art" art.png --model gemini-3-pro-image-preview --size 2K
Environment:
GEMINI_API_KEY Required. Your Gemini API key.
$ gemini-edit --help
Usage: gemini-edit INPUT "INSTRUCTION" [OUTPUT] [options]
Edit an existing image with a text instruction using Gemini.
Arguments:
INPUT Path to input image file (required)
INSTRUCTION Text instruction for editing (required)
OUTPUT Output file path (default: ~/Pictures/gemini-edit-TIMESTAMP.png)
Options:
--model MODEL Model to use (default: gemini-3.1-flash-image-preview)
--help, -h Show this help
Examples:
gemini-edit photo.png "Remove the background"
gemini-edit portrait.jpg "Make it look like a Van Gogh painting" art.png
gemini-edit product.png "Add a subtle shadow" --model gemini-3-pro-image-preview
Environment:
GEMINI_API_KEY Required. Your Gemini API key.
$ gemini-compose --help
Usage: gemini-compose IMAGE1 IMAGE2 [...] "INSTRUCTION" [options]
Combine multiple images using a text instruction with Gemini.
Arguments:
IMAGE1, IMAGE2, ... Input image files (2-14 images required)
INSTRUCTION Text instruction for composition (required)
Options:
--output PATH Output file path (default: ~/Pictures/gemini-compose-TIMESTAMP.png)
--model MODEL Model to use (default: gemini-3.1-flash-image-preview)
--help, -h Show this help
Detection:
Arguments that exist as files on disk are treated as images.
The first argument that doesn't exist as a file is the instruction.
Examples:
gemini-compose bg.png person.png "Place person on background"
gemini-compose style.jpg photo.jpg "Apply art style" --output styled.png
gemini-compose a.png b.png c.png "Combine into collage"
Environment:
GEMINI_API_KEY Required. Your Gemini API key. $ openai-image --help
Usage: openai-image "PROMPT" [OUTPUT] [options]
Generate an image from a text prompt using OpenAI gpt-image-2.
Arguments:
PROMPT Text description of the image to generate (required)
OUTPUT Output file path (default: ~/Pictures/openai-TIMESTAMP.png)
Options:
--model MODEL Model to use (default: gpt-image-2)
Available: gpt-image-2 (latest), chatgpt-image-latest,
gpt-image-1.5, gpt-image-1, gpt-image-1-mini,
dall-e-3, dall-e-2
--size SIZE Image size (default: 1024x1024)
gpt-image-1: 1024x1024, 1536x1024, 1024x1536, auto
dall-e-3: 1024x1024, 1792x1024, 1024x1792
--quality QUALITY Quality level (default: auto)
gpt-image-1: low, medium, high, auto
dall-e-3: standard, hd
--format FORMAT Output format: png, jpeg, webp (default: png)
--n N Number of images to generate (default: 1, max: 10 for gpt-image-1)
--help, -h Show this help
Examples:
openai-image "A misty Oregon forest at dawn"
openai-image "Brand moodboard card" card.png --size 1536x1024 --quality high
openai-image "Typography specimen" type.png --format webp --quality medium
Environment:
OPENAI_API_KEY Required. Auto-loaded from jat-secret if available.
$ openai-edit --help
Usage: openai-edit INPUT "INSTRUCTION" [OUTPUT] [options]
Edit an existing image with a text instruction using OpenAI gpt-image-1.
Arguments:
INPUT Path to input image (required)
INSTRUCTION Edit instruction (required)
OUTPUT Output file path (default: ~/Pictures/openai-edit-TIMESTAMP.png)
Options:
--model MODEL Model to use (default: gpt-image-2)
--mask PATH Optional mask image (transparent areas = edit region)
--size SIZE Output size: 1024x1024, 1536x1024, 1024x1536 (default: auto)
--quality QUALITY Quality: low, medium, high, auto (default: auto)
--format FORMAT Output format: png, jpeg, webp (default: png)
--n N Number of variations (default: 1)
--help, -h Show this help
Examples:
openai-edit photo.png "Remove the background" clean.png
openai-edit logo.png "Make it look weathered and vintage" vintage.png
openai-edit card.png "Change the background to a misty forest" --quality high
Environment:
OPENAI_API_KEY Required. Auto-loaded from jat-secret if available.
$ openai-brand-image --help
Usage: openai-brand-image "BRIEF" [OUTPUT] [options]
Generate an image grounded in the project's Design Context knowledge base.
The full KB content is prepended to your brief before sending to gpt-image-1.
Arguments:
BRIEF What you want (e.g. "a moodboard card, landscape")
OUTPUT Output file path (default: ~/Pictures/openai-brand-TIMESTAMP.png)
Options:
--kb KB_ID Knowledge base ID to use (default: auto-detect via jt bases list)
--kb-content FILE Use a file as context instead of a knowledge base
--model MODEL Model (default: gpt-image-2)
--size SIZE Size: 1024x1024, 1536x1024, 1024x1536 (default: 1536x1024)
--quality QUALITY Quality: low, medium, high, auto (default: high)
--format FORMAT Output format: png, jpeg, webp (default: png)
--n N Number of images (default: 1)
--dry-run Print the full prompt without generating
--help, -h Show this help
Examples:
openai-brand-image "hero moodboard, landscape, no text" card.png
openai-brand-image "typography specimen card" type.png --size 1024x1024
openai-brand-image "color palette swatch card" --dry-run
Environment:
OPENAI_API_KEY Required. Auto-loaded from jat-secret if available. $ kling-video --help
Usage: kling-video "PROMPT" [OUTPUT] [options]
Generate a video with Kling 2.1 on fal.ai. Submits the task, polls until
complete, downloads the MP4 to OUTPUT.
Arguments:
PROMPT Text description of the video (required)
OUTPUT Output file path (default: ~/Videos/kling-TIMESTAMP.mp4)
Options:
--model MODEL pro (default) | standard | master
--duration SECS 5 or 10 (default: 5)
--image URL Image URL for image-to-video (first frame reference)
--tail-image URL Image URL for the LAST frame (pro/master i2v only)
--aspect RATIO 16:9 | 9:16 | 1:1 (t2v only; i2v follows the image)
--negative TEXT Negative prompt (default: "blur, distort, and low quality")
--cfg N Guidance scale 0-1 (default: 0.5)
--timeout SECS Max seconds to wait for generation (default: 900)
--json Print the final result JSON instead of a summary line
--help, -h Show this help
Examples:
kling-video "model walks toward camera, studio lighting" out.mp4 --image https://cdn.site/ref.png
kling-video "slow dolly-in on product" hero.mp4 --model standard --duration 10
Provider: fal.ai (https://queue.fal.run/fal-ai/kling-video/v2.1/...)
Auth: jat-secret fal-api-key (format: uuid:secret)
Env: FAL_API_KEY overrides jat-secret lookup
Pricing: standard $0.25/5s · pro $0.49/5s (+$0.098/s) · master $1.40/5s
Notes:
- i2v output resolution/aspect follows the reference image (pro = 1080p tier)
- Output moderation is on the lenient end among hosted video models
- Upload a local image to fal storage first:
POST https://rest.alpha.fal.ai/storage/upload/initiate {file_name, content_type}
then PUT the bytes to upload_url and pass file_url as --image
$ hailuo-video --help
Usage: hailuo-video "PROMPT" [OUTPUT] [options]
Generate a video with MiniMax Hailuo 02 on fal.ai. Submits the task, polls
until complete, downloads the MP4 to OUTPUT.
Arguments:
PROMPT Text description of the video (required)
OUTPUT Output file path (default: ~/Videos/hailuo-TIMESTAMP.mp4)
Options:
--model MODEL pro (default, 1080p/6s) | standard (512p/768p, 6s or 10s)
--duration SECS 6 or 10 (standard only; pro is fixed 6s)
--resolution RES 512P | 768P (standard only; default 768P)
--image URL Image URL for image-to-video (first frame reference)
--end-image URL Image URL for the LAST frame (end pose reference; i2v only)
--no-optimize Disable MiniMax's automatic prompt optimizer
--timeout SECS Max seconds to wait for generation (default: 900)
--json Print the final result JSON instead of a summary line
--help, -h Show this help
Examples:
hailuo-video "model settles into an armchair, catalog lighting" out.mp4 --image https://cdn.site/ref.png
hailuo-video "ocean waves at sunset, aerial" waves.mp4 --model standard --duration 10
Provider: fal.ai (https://queue.fal.run/fal-ai/minimax/hailuo-02/...)
Auth: jat-secret fal-api-key (format: uuid:secret)
Env: FAL_API_KEY overrides jat-secret lookup
Pricing: pro $0.08/s ($0.48 per 6s video) · standard $0.045/s ($0.27 per 6s)
Notes:
- Cheapest per second of the three video tools here (vs kling pro $0.098/s,
seedance std $0.30/s) — good default for drafts and volume work
- Generation is slower than Kling (~5-7 min vs ~1 min for a 5-6s clip)
- Upload a local image to fal storage first:
POST https://rest.alpha.fal.ai/storage/upload/initiate {file_name, content_type}
then PUT the bytes to upload_url and pass file_url as --image
$ seedance-video --help
Usage: seedance-video "PROMPT" [OUTPUT] [options]
Generate a video from a text prompt (optionally with a reference image) using
the Seedance 2.0 model on fal.ai. Submits the task, polls until complete,
downloads the MP4 to OUTPUT.
Arguments:
PROMPT Text description of the video (required)
OUTPUT Output file path (default: ~/Videos/seedance-TIMESTAMP.mp4)
Options:
--model MODEL seedance-2-0 (default, quality) | seedance-2-0-fast | seedance-2-0-mini
--duration SECS 5 or 10 (default: 5)
--aspect RATIO 16:9 | 9:16 | 4:3 | 3:4 | 21:9 | 1:1 (default: 16:9 for t2v, auto for i2v)
--resolution RES 480p | 720p (default) | 1080p
--image URL Image URL for image-to-video (first frame reference)
--end-image URL Image URL for the LAST frame (end pose reference; i2v only)
--no-audio Disable audio generation (default: on; use for silent web loops)
--seed N Deterministic seed (default: -1 random)
--timeout SECS Max seconds to wait for generation (default: 600)
--json Print the final result JSON instead of a summary line
--help, -h Show this help
Examples:
seedance-video "slow orbit around a glossy chrome funnel, black studio" hero.mp4 \
--aspect 21:9 --resolution 1080p --duration 10 --no-audio
seedance-video "product spins on turntable" spin.mp4 --image https://site.com/product.png
seedance-video "cut like this reference" out.mp4 --duration 5
Provider: fal.ai (https://queue.fal.run/bytedance/seedance-2.0/...)
Auth: jat-secret fal-api-key (format: uuid:secret)
Env: FAL_API_KEY overrides jat-secret lookup
Pricing: std $0.3034/s@720p · fast $0.2419/s@720p · mini $0.1547/s@720p
("fast" = latency tier, NOT the cheap tier — mini is cheapest)
Notes:
- fal duration buckets: 5 or 10 seconds only (9/10/11+ round to 10; 1-7 round to 5)
- Image-to-video: pass --image with a reachable URL; fal uses it as first-frame reference
- Result URLs are fal CDN — the tool downloads immediately
- Poll interval: 5s jat-video — the single tested source of truth for every ffmpeg primitive in
the jatstack. A fix lands HERE, once; consumers call it, never re-copy
it. Every primitive has exactly one implementation, so a supersample or
encoder fix can't drift across a dozen pasted copies.
Every subcommand DECODE-CHECKS its own output and fails loud (exit != 0) on
any decode error. Heavy renders route through jat-offload (your
jat-appliance) automatically unless --local is passed or the appliance is
unreachable.
Usage:
jat-video <subcommand> [args] · jat-video help · jat-video <sub> --help
Subcommands:
kenburns IN OUT [opts] Ken Burns zoom/pan (supersample-first)
stitch -o OUT c1 c2 … [--xfade S] CFR-normalized concat (hardcut | crossfade)
loop IN OUT [--xfade S] Seamless ping-pong loop (split + reverse)
chain CLIP [--extract-last] [--upload] Actual-frame clip chaining
decode FILE The gate — exit 1 on ANY decode error
cfr IN OUT [--fps N] Constant-frame-rate normalize
poster IN OUT.jpg [--at S] [-q N] Extract a poster frame
encode IN OUT --quality TIER ONE encoder (master | web | archive)
probe FILE --duration Media duration in seconds (the one probe)
scale IN OUT --height H Scale to height, preserve aspect
grade IN OUT [--preset P] Color grade (eq curve presets)
fades IN OUT [--in S] [--out S] Fade-in / fade-out
aspect IN OUT --ratio W:H Aspect-normalize (pad, no crop)
test Fixture: decode-clean + no-jitter
Quality tiers (the ONE place CRF is chosen):
master ≈ crf18 editing master / re-encode source
web ≈ crf23 +faststart delivery; progressive-download ready
archive ≈ crf28 +faststart smallest web-final Usage: jat-image-edit "INSTRUCTION" INPUT [OUTPUT] [options]
Edit a local image on your jat-appliance (the edit runs through ComfyUI).
Uploads INPUT, runs the edit, downloads the result. Fully local — nothing
leaves your network, no content filters.
Arguments:
INSTRUCTION What to do ("remove the hat", "make it watercolor", ...)
INPUT Local image path
OUTPUT Output path (default: INPUT-edited.png)
Options:
--mode MODE kontext (default) | restyle
kontext: Flux Kontext instruction edit — understands
semantic commands, preserves everything else.
Slower but highest quality.
restyle: SDXL img2img — reimagines the whole image
toward the instruction. Fast.
Structure survives, details change.
--denoise N restyle only: 0.3 subtle .. 0.9 heavy (default 0.55)
--steps N sampler steps (default: kontext 20, restyle 30)
--seed N deterministic seed (default random)
--host HOST ssh host running ComfyUI (your jat-appliance's hostname)
--timeout SECS max wait (default 2400)
--help, -h Show this help
Examples:
jat-image-edit "remove the sunglasses" photo.png
jat-image-edit "make the vase a tall straight cylinder" pot.png pot-cyl.png
jat-image-edit "turn this into a watercolor illustration" ref.jpg wc.png --mode restyle --denoise 0.7
Notes:
- Starts the remote ComfyUI server if it isn't running (niced, cache-none).
- Renders queue FIFO with whatever else the appliance is doing.
- For masked/regional edits or multi-image workflows, tunnel the ComfyUI UI
over ssh and open it in a browser (drag any prior output PNG onto the
canvas to reload its workflow). speak — speak text aloud via your jat-appliance's inference relay (Kokoro TTS)
USAGE:
speak [OPTIONS] "text to speak"
speak -f FILE # speak the contents of a text file
echo "text" | speak - # read text from stdin
speak -o out.wav "text" # save WAV instead of playing
OPTIONS:
-f, --file FILE Read the text to speak from FILE ('-' = stdin)
-o, --out FILE Save synthesized WAV to FILE (implies no playback)
-v, --voice NAME Voice (Kokoro: af_heart, af_sky, am_*, bf_*, ...). Default: relay default
-m, --model NAME TTS model (default: tts-1)
-p, --player CMD Force player: paplay | pw-play | ffplay | mpv
--no-play Synthesize only (discard audio unless -o is given)
--stream Chunked mode: split at sentence boundaries, synthesize the
next chunk while the current one plays. First audio in ~1-2s
instead of waiting for the full synthesis (long texts only;
short texts fall back to single-shot automatically)
-q, --quiet Suppress status messages (errors still print)
-h, --help Show this help
ENV:
PRIVATE_INFERENCE_URL / PRIVATE_INFERENCE_KEY override the inference relay
endpoint. Otherwise resolved via 'jat-secret private-inference-url|key'.
EXAMPLES:
speak "Deploy finished successfully"
speak -v af_sky "Switching to a brighter voice"
jt ready --json | jq -r '.[0].title' | speak - Inference & Compute
Push heavy compute and private inference to a dedicated jat-appliance — the local box stays for editing.
jat-appliance — turn a spare machine into a private compute + inference node.
jat-appliance is a repo, not a single command: it converges an always-on box
(a Mac, a Framework, a mini-PC, any capable machine you dedicate) into a
dedicated appliance on
your network that serves LLM, speech-to-text, text-to-speech, and embedding
traffic locally, and accepts offloaded compute jobs from the rest of the
fleet.
The model:
- ROM-model compute. The appliance is stateless: jobs rsync in, results
rsync home, nothing precious lives on the box. Recovery = reinstall from
the repo; backups shrink to config only.
- Profiles, not forks. A profile bundles configs proven together on
specific hardware — runtime choice, context sizes, keep-warm strategy,
memory budgets. A new box is a new profile, not a rewrite.
- Boring-reboot invariant. Auto-login on, power settings pinned, services
self-heal in minutes. A bundled doctor verifies all of it.
How you use it:
- jat-offload / jat-render / jat-typecheck send heavy work to the appliance
(falling back to local if it is unreachable).
- jat-ask / jat-local-llm / speak hit its local inference relay — private
LLM, classification, summarization, and TTS that never leave your network.
- Pool several appliances behind one endpoint with jat.run.
Setup, profiles, and the doctor live in the repo:
https://github.com/jatstack/jat-appliance jat-offload — run a compute command in a working directory on your
jat-appliance instead of burdening the local machine. The general-purpose sibling
of jat-render: rsync a workdir up, run an arbitrary command in it (niced so
inference always wins), rsync the results back. Built for ffmpeg
assembles/stitches/transcodes and other heavy batch compute that competes for
local memory with the agent fleet.
WHY: heavy local ffmpeg under parallel-agent memory pressure can produce
corrupt muxes. A dedicated appliance sits idle by comparison — no pressure,
no corruption.
Usage:
jat-offload [options] <workdir> -- <command...>
Options:
--local Run <command> locally in <workdir> (skip the appliance)
--fast Skip `taskpolicy -b` (nice only) — faster, still yields to inference
--keep-remote Don't delete the remote job dir afterwards
--exclude PAT Extra rsync exclude (repeatable)
-h, --help
Env:
JAT_OFFLOAD_HOST remote appliance host (set to your jat-appliance's hostname)
JAT_OFFLOAD_BASE remote base dir, relative to remote $HOME (default: offload-jobs)
Examples:
jat-offload ./walkthrough -- bash assemble.sh
jat-offload . -- ffmpeg -i in.mp4 -crf 28 -movflags +faststart out.mp4
Falls back to a LOCAL run if the appliance is unreachable OR the remote run
fails; --local forces local. Inference always wins: remote runs are niced
(+ background-QoS unless --fast). jat-render — offload HyperFrames video rendering to your jat-appliance.
Instead of pinning the local dev box for the minutes a HyperFrames render
takes (each Chrome worker is ~256 MB), rsync the project to the appliance,
render it there **niced so inference always wins**, and rsync the resulting
MP4 back.
The appliance typically doubles as your local inference node, so the render
runs under `nice -n 15` AND background QoS — it can never starve the
inference relay's live traffic.
If the appliance is unreachable, jat-render falls back to a LOCAL render — it
never hard-fails on connectivity. `--local` forces the local path.
USAGE:
jat-render <project-dir> [--local] [--keep-remote]
<project-dir> A HyperFrames project (must contain hyperframes.json + index.html)
--local Force a local render (skip the appliance entirely)
--keep-remote Don't delete the remote job dir after rendering
ENV:
JAT_RENDER_HOST ssh host to offload to (set to your jat-appliance's hostname)
JAT_RENDER_BASE remote job base dir, relative to remote $HOME (default: render-jobs)
EXAMPLES:
jat-render ./episodes/1-intro
jat-render ./episodes/1-intro --local
jat-render "./my project" --keep-remote jat-typecheck — run svelte-check/tsc on your jat-appliance instead of
locally.
WHY: svelte-check on a large codebase can need several GB of RSS. With many
agents completing at once, that burst is what pushes the local box into
memory pressure, where a live typecheck can be killed mid-run and an agent
reads it as a spurious "build broken". Typechecks are the ideal thing to
move off-box.
WHY NOT jat-offload: jat-offload rsyncs the workdir BACK (no --delete). For a
typecheck there is nothing to bring home — svelte-check emits diagnostics on
stdout and writes no artifacts — and a sync-back is precisely the mechanism
that makes offloading a *build* dangerous. This tool therefore never syncs
back, and it never runs a caller-supplied command: the remote command is a
fixed, in-tool string (`svelte-kit sync` + `svelte-check`). It is structurally
incapable of reaching `vite build`.
CONTRACT (this is what makes it drop-in for the build gates):
stdout — svelte-check's `--output machine` lines, verbatim and nothing else
stderr — progress, timings, and any infrastructure diagnostics
exit 0 — svelte-check ran, no errors
exit 1 — svelte-check ran, errors found
exit 2 — could not run LOCALLY (the package is not installed here).
UNVERIFIED, not a pass.
exit 3 — could NOT run remotely (unreachable / sync / install failure).
Callers fall back to a local run. This is never "clean".
exit 4 — remote RAN and died on its OWN heap limit (V8 abort / OOM kill).
Callers must NOT fall back to a local run: locally this check can
peak above the per-agent memory cap, so the fallback would
OOM-kill the agent. Raise JAT_CHECK_REMOTE_HEAP_MB.
Usage:
jat-typecheck [options]
Options:
--package DIR package dir, relative to repo root (default: ide)
--repo DIR repo root (default: git rev-parse --show-toplevel)
--local run locally (no appliance); still honours the output contract
--setup sync + install remotely, then exit (warm the cache)
--force-install reinstall remotely even if the dependency stamp matches
--timing print a wall-clock breakdown to stderr
-h, --help
Env:
JAT_TYPECHECK_HOST remote host (default: $JAT_OFFLOAD_HOST)
JAT_TYPECHECK_BASE remote base dir under $HOME (default: jat-typecheck)
JAT_TYPECHECK auto (default) | remote | local | 0 — `0`/`local` skip
the appliance entirely; `remote` disables local fallback
(exit 3 instead), which is what the test suite uses.
REMOTE LAYOUT: everything is placed under
$HOME/$JAT_TYPECHECK_BASE/<repo-name>/
preserving each tree's path relative to the LOCAL repo's parent directory, so
that out-of-repo `file:` workspace dependencies keep resolving remotely
without patching any package.json. jat-ask - Ask a question about your projects using local LLM inference
USAGE:
jat-ask "QUESTION" [OPTIONS]
jat-ask - [OPTIONS] # read question from stdin
OPTIONS:
--sources Show source labels alongside the answer
--projects PROJ,... Comma-separated project keys to filter context
--json Print raw JSON response (answer, sources, durationMs, etc.)
--timeout MS Timeout in milliseconds (default: 60000)
-h, --help Show this help
ENVIRONMENT:
JAT_IDE_URL Override IDE base URL (default: http://localhost:3333)
EXAMPLES:
jat-ask "what is the status of the billing refactor?"
jat-ask "what open tasks are blocking the voice feature?" --sources
jat-ask "summarize recent voice memos" --projects jat
jat-ask - < question.txt
The IDE must be running (jat or jat --dev) for this tool to work.
Answer text is printed to stdout; errors go to stderr. jat-local-llm - Send a raw prompt to your local inference relay
USAGE:
jat-local-llm "PROMPT" [OPTIONS]
jat-local-llm - [OPTIONS] # read prompt from stdin
OPTIONS:
--system TEXT System prompt
--model MODEL Override model (default: gemma4:e2b-voice)
--json Request JSON-formatted output
--stream [stub] Streaming — not implemented in v1; exits 3
--feature FEAT Feature tag for audit log grouping
--timeout MS Timeout in milliseconds (default: 30000)
-h, --help Show this help
ENVIRONMENT:
JAT_IDE_URL Override IDE base URL (default: http://localhost:3333)
EXAMPLES:
jat-local-llm "classify as bug/feature/chore: add dark mode toggle"
jat-local-llm - < notes.txt
echo "summarize: $(cat report.md)" | jat-local-llm - --json
jat-local-llm "Is this PII?" --model gemma4:latest --json
The IDE must be running (jat or jat --dev) for this tool to work.
Response text is printed to stdout; errors go to stderr. Data
Query and inspect connected databases directly.
Usage: db-query "<SQL query>"
Options:
--json Output as JSON (default: table format)
--csv Output as CSV
Examples:
db-query "SELECT * FROM assets LIMIT 5"
db-query "SELECT count(*) FROM users" --json
Safety: Adds LIMIT 100 if no LIMIT specified Usage: db-schema <table_name>
db-schema --tables
db-schema --views
Examples:
db-schema assets # Show assets table schema
db-schema --tables # List all tables
db-schema --views # List all views Usage: db-sessions [options]
Options:
--user <id> Filter by user ID
--brand <id> Filter by brand ID
--recent <n> Show n most recent sessions (default: 10)
--active Only show sessions with activity in last 24h
Examples:
db-sessions --recent 5
db-sessions --user 123e4567-e89b-12d3-a456-426614174000
db-sessions --active Usage: db-connection-test [--latency]
Test postgres database connection
Options:
--latency Measure query latency
Examples:
db-connection-test
db-connection-test --latency Dev, Git & Secrets
Commit safely in a shared checkout, resolve credentials, grow a project's data, and scaffold a new app.
usage: jat-commit -m "<subject>" [-m "<body>"...] -- <path> [<path>...]
jat-commit — atomic, path-explicit commit under a shared commit lock.
In a multi-agent checkout every agent shares ONE .git/index. Two hazards:
1) blanket staging (git add -A / . / -u / <folder>) sweeps siblings' files;
2) files left STAGED while you do something slow get swept into whichever
sibling commits next, under their task id.
jat-commit closes both for the committer:
- refuses blanket/dangerous stage specs (explicit paths only);
- under a flock on the commit lock, stages + commits in ONE step so nothing
sits staged across a slow op;
- verifies the index after staging contains ONLY your paths — if a sibling
already had files staged, it ABORTS instead of committing their work.
It does NOT prevent a sibling editing the SAME file (that is the spawn-time
file-lease allocator's job). This is the hygiene layer.
Signed-agent trailer:
When the committing agent has a secret key on record, jat-commit appends a
trailer line to the commit message:
Signed-agent: AgentName sig=<first-16-hex> sigv=1
covering the staged tree hash + agent identity + timestamp — tamper-evidence
and attribution backed by the key holder.
Example:
jat-commit -m "fix: guard empty input" -- src/lib/parse.ts src/lib/parse.test.ts jat-secret - Retrieve and manage secrets in JAT credentials store
USAGE:
jat-secret <key-name> Get secret value by name
jat-secret --project <proj> <key> Get project secret directly
jat-secret --set <key> <value> [OPTIONS] Set a custom key (pushes to vault)
jat-secret --delete <key-name> [--vault] Delete a custom key
jat-secret --list List keys across all tiers
jat-secret --find <pattern> Search key names + descriptions, all tiers
jat-secret --export Output export statements
jat-secret --env <key-name> Get the env var name for a key
jat-secret --1p Show 1Password integration status
jat-secret --1p-list List items in 1Password JAT vault
jat-secret --vault-push [key] Push key(s) to encrypted vault
jat-secret --vault-pull [key] Pull key(s) from encrypted vault
jat-secret --vault-delete <key> Remove a key from the vault
jat-secret --vault-status Show vault connectivity status
jat-secret --help Show this help
SET OPTIONS:
--env VAR_NAME Environment variable name (default: auto-derived from key)
--desc "text" Description of what the key is for
--no-push Keep this value on this machine only (skip the vault push)
EXAMPLES:
jat-secret stripe # Get a secret
jat-secret myapp-supabase-service-role-key # From project secrets
jat-secret --project myapp supabase_service_role_key # Direct access
jat-secret --set my-api "sk_xxx" # Set + push to vault
jat-secret --set my-api "sk_xxx" --env MY_KEY # Set with explicit env var
jat-secret --set my-api "sk_xxx" --desc "My key" # Set with description
jat-secret --set local-scratch-dir /mnt/fast --no-push # Machine-local only
jat-secret --delete my-api # Delete locally
jat-secret --delete my-api --vault # Delete everywhere
jat-secret --env stripe # Get env var name
jat-secret --find stripe # Search key names + descriptions, all tiers
eval $(jat-secret --export) # Load all as env vars
PROVIDER KEYS:
jat-secret anthropic # Anthropic API key
jat-secret google # Google/Gemini API key
jat-secret openai # OpenAI API key
jat-secret slack # Slack Bot Token
jat-secret telegram # Telegram Bot Token
jat-secret cloudflare # Cloudflare API Token
jat-secret github # GitHub Token
(+ discord, gmail, vercel, fly, convex, linear, sentry,
turso, upstash, neon, pinecone, resend, twilio, openrouter)
PROJECT SECRET FALLBACK:
Names like "project-secret-key" are resolved from projectSecrets:
1. Find longest matching project prefix
2. Strip prefix + dash, convert dashes to underscores
3. Look up in projectSecrets.<project>.<key>.value
VAULT COMMANDS:
jat-secret --vault-push # Push all local secrets to vault
jat-secret --vault-push my-key # Push one secret to vault
jat-secret --vault-pull # Pull all secrets from vault
jat-secret --vault-pull my-key # Pull one secret from vault
jat-secret --vault-delete my-key # Remove one secret from the vault
jat-secret --vault-status # Check vault connectivity
Lookup chain: credentials.json → vault server → 1Password → error
(vault is consulted automatically with 3s timeout when key not found locally)
DISCOVERY (--list reads the SAME chain resolution does):
A key that resolves MUST appear in --list. The listing covers all three
tiers and says which one would serve each entry:
Provider keys (local) built-in provider ids from .apiKeys
Custom keys (local) .customApiKeys — the local tier wins the chain
Project secrets (local) .projectSecrets, shown in the dashed form
that actually resolves
Vault only in the vault, NOT on this machine; these
resolve through the vault fallback
1Password only in 1Password and in neither tier above
Local entries tagged [local only] exist on THIS MACHINE ONLY — they were
never pushed, so no other machine can resolve them. Push with --vault-push.
(jatweb-*/local-* are exempt by design and never tagged.)
If a configured tier cannot be read, --list says so in-band AND on stderr
and exits 3. It never silently lists fewer keys: a short list that looks
complete is how a present credential gets reported as a missing
integration.
--find <pattern> is the same tier walk, filtered: a case-insensitive
substring match against BOTH the key name and its description (where one
exists), across all three tiers, labelled the same way --list labels
them. Use it instead of `jat-secret --list | grep PATTERN` — piping
through grep only ever matched key names, so a key findable only by its
description (or gated behind the vault/1Password network calls) was
invisible to it. Same incomplete-result contract as --list: an
unreachable tier is reported in-band and exits 3, never silently omitted.
NAMING CONVENTION:
Bare names are reserved for the enumerated built-in provider ids
(anthropic, openai, slack, github, ...). Every other secret is:
<service>-<kind> kind = api-key | token | secret | url | password
-api-key is the default kind for an API credential, so a new DeepSeek key
is `deepseek-api-key`, not `deepseek`.
The other shape is ALIASED, not rejected. When a name misses every tier,
an unambiguous conventional alias is resolved and announced on stderr:
jat-secret deepseek -> note: resolved via 'deepseek-api-key'
Ambiguity is never guessed through — if both `foo-key` and `foo-token`
exist, `jat-secret foo` fails and names both. On any other miss, near
matches are suggested. Set JAT_SECRET_NO_SUGGEST=1 to skip both (scripted
callers that probe optional secrets in a hot loop).
ENVIRONMENT:
JAT_SECRET_NO_PUSH=1 --set never pushes to the vault
JAT_SECRET_NO_SUGGEST=1 no alias resolution / suggestions on a miss
JAT_SECRET_VAULT_TIMEOUT seconds for vault writes (default 10)
JAT_SECRET_VAULT_LIST_TIMEOUT seconds for the --list vault fetch (default 8)
JAT_SECRET_OP_TIMEOUT seconds for the 1Password item list (default 6)
SYNC BEHAVIOUR (--set pushes, --delete does not):
--set pushes to the vault by default, so a secret added on one machine is
immediately resolvable from every other one. The push FAILS OPEN: if the
vault is unreachable or mis-keyed, the value is still written locally, the
exit status is still 0, and a loud stderr warning tells you to run
`--vault-push <key>` later. A failed network call must never eat a
just-typed credential.
Skipped automatically (no flag needed):
jatweb-* the vault's own credentials — it cannot bootstrap itself
local-* naming convention for machine-scoped values (paths, ports,
per-box tokens)
Skip it explicitly with `--no-push`, or set JAT_SECRET_NO_PUSH=1 for a
scripted caller that only ever writes machine-local values.
--delete deliberately does NOT propagate. Auto-pushing a set is additive
and recoverable; auto-deleting is irreversible across every machine in the
fleet. So a local delete stays local — but it is not silent: it checks the
vault and warns when a remote copy survives (which the next --vault-pull
would otherwise resurrect). Use `--delete <key> --vault`, or
`--vault-delete <key>`, to retire it everywhere.
VAULT SETUP (once per machine):
# Generate and share with teammates:
jat-secret --set jatweb-url "https://jat.example.com"
jat-secret --set jatweb-enc-key "$(openssl rand -hex 32)" # share out-of-band
jat-secret --set jatweb-api-secret "$(openssl rand -hex 24)" # share out-of-band
# Apply DB migration (once):
psql "$(jat-secret jat-postgres-url)" -f lib/migrations/vault_secrets.sql
# Push all secrets to vault:
jat-secret --vault-push
# New machine: get the 3 meta keys from a teammate, then:
jat-secret --vault-pull
1PASSWORD INTEGRATION:
jat-secret automatically falls back to 1Password when a key isn't found
in credentials.json or vault. Create a vault named "JAT" (or configure a
custom vault name) and add items with names matching your key names.
Setup:
1. Open 1Password > Settings > Developer > "Integrate with 1Password CLI"
2. Create a vault named "JAT" in 1Password
3. Add items with names matching your key names (e.g., "stripe", "openai")
4. The password/credential field is used as the secret value
Custom vault name (in ~/.config/jat/projects.json):
{ "defaults": { "op_vault": "MyVault" } }
Lookup chain: credentials.json > vault > 1Password > error Usage: jat-provision-postgres <project-name>
Creates an isolated database + scoped role on your shared Postgres server and
stashes the connection string in jat-secret, so any machine on your network
can graduate the project to Postgres.
jat-provision-postgres my-project
# -> creates my-project_tasks database + my_project_user role
# -> stashes the connection string as my-project-postgres-url in jat-secret
# Then graduate the project:
jt graduate my-project --url "$(jat-secret my-project-postgres-url)" --yes
Safe to re-run — uses IF NOT EXISTS guards; refreshes the role password if it
already exists. Role names can't contain hyphens, so they are converted to
underscores automatically. Usage: jt graduate [PROJECT] --url <postgres-url> [options]
Migrate a project's task store from local SQLite to shared Postgres.
One-way: exports every task/dep/label/comment from .jat/tasks.db, imports
them atomically into Postgres, flips the projects.json backend flag, and
archives the local db as .jat/tasks.db.sqlite-backup-{timestamp}.
Arguments:
PROJECT Project name or path. Defaults to the current project
(inferred from PROJECT_ROOT or cwd).
Options:
--url URL Postgres connection string (required unless --dry-run)
--dry-run Print the migration preview and exit without writing
--status STATUS Override status for all imported rows (e.g. 'dev', 'submitted')
--target-table TBL Postgres table to import into: 'project_tasks' (default)
or 'tasks' (legacy JAT-owned table)
--mark-internal Deprecated: equivalent to --status dev (kept for compat)
--yes, -y Skip the interactive confirmation prompt
--force Proceed even if the target Postgres db is non-empty
--json Machine-readable JSON output (implies --yes)
--help, -h Show this help
Behaviour:
Idempotent — re-running on an already-graduated project exits 0 with a
message and does not touch either database.
Atomic — on any import error the Postgres transaction rolls back; the
local SQLite db and projects.json are untouched.
Examples:
jt graduate --dry-run
jt graduate --url postgres://jat:pw@host/jat_prod
jt graduate jat --url postgres://jat:pw@host/jat_prod --yes jst — the agent-proven SvelteKit app framework jatstack apps are built with.
A minimal, readable SvelteKit starter: SvelteKit 2 + Svelte 5, Tailwind 4,
DaisyUI 5, jatui, TypeScript, and vitest — with a database (Drizzle + Postgres)
and auth (Better Auth) included but DORMANT. The app boots with an empty
environment; setting DATABASE_URL turns them on.
Small on purpose (~50 files): you can read the whole template in an evening,
and that is the point — build on a stack you actually understand, one that AI
coding agents can navigate end-to-end.
Quick start (use it as a GitHub template, then):
npm install
npm run dev
# visit /notes to see the example module
When your app needs a database (also enables auth + /login):
cp .env.example .env # set DATABASE_URL, BETTER_AUTH_SECRET
npm run db:generate && npm run db:migrate
npm run dev
Source and template: https://github.com/joewinke/jst Want the guided tour instead?
The docs cover the seven
headline products in depth — jat, jst, jat-pilot, jat-secret,
jat-appliance, jatstation, and jat.run — with the story of how they fit
together. This page is the complete lookup for when you already know what
you're after: click a tool, read its real --help.
Every panel is the tool's own --help output.
Source lives under github.com/jatstack.