CLI & Scriptability API
Updated July 1, 2026
The crystl CLI lets you control crystl from any terminal. List gems, approve permissions, read terminal output, and script multi-agent workflows, all without touching the GUI.
Free vs Guild. The rule is simple: any read is free, any mutation needs Guild (filing feedback and
copyare the exceptions, free on every tier). Free on any crystl:status,gems,shards,screen,history,fs,pending,events,wait,docs,token,doctor,render,copy,hero list,party list,resurrect list,workbench list,sidequest status, thelistforms offacetandaskuser, andreport. Guild-only (they change something, so without a membership they return403 guild_required):open,close,shard create,shard close,shard rename,gauntlet,resurrect,hero summon,merge,send,approve,deny,quest start/clear/end,sidequest start/end,party create/delete, workbench edits and panel controls (aliasbacklog),facet add/slot/remove,ssh bridge-address, andaskuser answer. This is why/fanout, which spawns and merges workers, is Guild-only.
Setup
The CLI ships inside Crystl.app. Open crystl and choose Help → install command line tool…, which symlinks /usr/local/bin/crystl to the binary inside the app bundle (Crystl.app/Contents/Helpers/crystl). You’ll see one macOS admin prompt the first time, then crystl is available in any new shell.
Prefer to do it by hand:
sudo ln -sfh /Applications/Crystl.app/Contents/Helpers/crystl /usr/local/bin/crystl
The CLI automatically reads the auth token from ~/.crystl-bridge-token. No configuration needed.
Requires Crystl.app to be running. The CLI talks to crystl’s local HTTP bridge.
For agents running inside crystl
When the tell agents about the crystl CLI toggle is on (default), crystl upserts a short Crystl CLI section into each gem’s CLAUDE.md and AGENTS.md as the project is opened, so any agent in a crystl shard discovers it can coordinate with sibling gems and shards. The section is bracketed with <!-- crystl-cli:begin --> / <!-- crystl-cli:end --> so re-running is idempotent and removal is one delete. If a project has no agent instruction file yet, crystl can seed AGENTS.md and a tiny CLAUDE.md import entrypoint unless that creation toggle is disabled.
Commands
status
Overview of all gems, shards, and running agents. This is the default command. A worker parked on an in-terminal approval prompt shows ⏸ awaiting input next to its gem, so a stalled fan-out is visible at a glance instead of looking idle.
Each running agent is annotated with how long it has been in its current state and when it last produced output (e.g. idle 5s · last output 2s ago). crystl computes these durations itself so the numbers are consistent whether you read them here, on a paired phone, or off the agent_status_changed event — agents themselves are weak at time math, so the app owns the clock rather than asking the agent.
crystl status
crystl status --json
gems
List all open gems with their shards and agent status.
crystl gems
crystl gems --json
open
Open a project directory as a new gem.
crystl open ~/Projects/myapp
crystl open .
close
Close a gem by name or UUID. Quote names that contain spaces. Reopening a path that is already a gem focuses the existing one instead of stacking a duplicate.
crystl close myapp
crystl close "Competitor research"
fs
Browse directories so you know what paths you can open. Defaults to your projects directory. Read-only.
crystl fs # list your projects directory
crystl fs ~/Code # list a specific path
crystl fs --json
shards
List shards in a gem. A shard parked on an in-terminal approval prompt (the agent’s own “Do you want to proceed?” menu, which is not a pending request) is flagged ⏸ awaiting input, so you can spot a fanned-out worker that is silently waiting on you. The same flag streams over crystl events as agent_status_changed.awaitingInput.
crystl shards
crystl shards --gem myapp
send
Send text to a terminal shard. Like typing into the terminal.
crystl send "git status"
crystl send --gem myapp "claude 'fix the tests'"
crystl send --gem myapp --shard opal "ls -la"
notify
Notify an orchestrator shard with an attributed worker ping. Unlike send — which injects raw text into the target’s input line as if the human typed it — notify carries the sending shard’s identity, so the lead’s chat dock attributes the message to the worker while still waking the orchestrator agent. This is the push half of a fan-out: workers can stay silent and report only when they finish or get blocked, instead of the lead polling them.
crystl notify --gem myapp --shard diamond "DONE opal: refactor complete"
crystl notify --shard diamond "BLOCKED opal: needs the API key"
crystl notify --shard diamond --from opal "DONE: tests green"
The sender is auto-detected from the CRYSTL_SHARD env a Crystl shell exports; pass --from to override it. Like send, it’s a control command (Guild-gated over the desktop CLI, free over a direct-LAN phone connection).
| Flag | Purpose |
|---|---|
--shard <name> | Target lead/orchestrator shard (defaults to the gem’s first shard) |
--gem <name> | Gem name or ID (defaults to the selected gem) |
--from <name> | Sending shard name (defaults to $CRYSTL_SHARD) |
copy
Hand the user something to copy. crystl copy surfaces text in the copy bar — an editable strip docked at the bottom of the shard with a one-click Copy button — so they don’t have to drag-select it out of wrapped terminal lines. Reach for it whenever your output contains something the user needs out of the conversation (a token, a URL, a generated snippet), and especially for commands they’ll run elsewhere, where a hand-retyped flag or path is easy to get wrong. Free on every tier.
crystl copy "npx some-cli --flag value" # text goes to the bar
some-command | crystl copy # or pipe it in
crystl copy "deploy step 1" --label "deploy" # name the tab
crystl copy "second snippet" # appends a 2nd tab (up to 10)
crystl copy "fixed" --tab 1 # overwrite tab 1 instead of appending
crystl copy --gem myapp --shard opal "..." # target a specific shard
The bar holds up to 10 numbered tabs, so an agent emitting several snippets can stage them all — a plain copy appends a tab, --tab <N> overwrites one. The user can edit the text before clicking Copy. The window grows taller to make room, so the terminal keeps its size.
| Flag | Purpose |
|---|---|
--label <title> | Name the tab’s chip |
--tab <N> | Overwrite tab N instead of appending |
--gem <name> / --shard <name> | Target a specific shard (defaults to the selected one) |
screen
Dump the terminal screen content. See what an agent is doing without switching tabs.
crystl screen
crystl screen --gem myapp
crystl screen --gem myapp --shard opal
history
Print a shard’s structured Claude transcript (past turns and tool calls) so an agent can recover context after a compaction or in a fresh shard. Claude Code shards only; see the History Navigator for the GUI.
Each turn is annotated with its model, token usage, and duration, and a session summary (total tokens, tool calls, models used, wall-clock active time) prints on top — useful for judging a worker’s cost and context before acting on its output.
crystl history # active shard
crystl history --gem myapp --shard opal
crystl history --tools-only # only tool calls and results
crystl history --limit 20 # last 20 rows
crystl history --json # full metadata: per-turn usage/model/stopReason/cwd/gitBranch
pending
List pending permission requests.
crystl pending
approve / deny
Approve or deny a pending permission request by ID.
crystl pending # see request IDs
crystl approve 1 # approve request #1
crystl deny 2 # deny request #2
askuser
List and answer the questions agents raise through AskUserQuestion, the same prompts the floating question panel shows, answerable from any shell. Listing is free; answer resolves the prompt, so it’s Guild-gated.
crystl askuser # list open questions (default subcommand)
crystl askuser list --json
crystl askuser answer <id> "2 weeks" # answer by text, or by an option label
For a multi-question prompt, the answer applies to every question in the series.
shard create
Create a new shard inside an existing gem. Useful for orchestrator agents that want to fan work out into parallel shards.
crystl shard create --gem myapp # shared cwd
crystl shard create --gem myapp --isolated # git worktree
crystl shard create --gem myapp --crystal-name opal # pick the name
crystl shard create --gem myapp --prompt "add tests" # launch an agent on a task
crystl shard create --gem myapp --agent codex --prompt "add tests" # spawn a different agent
crystl shard create --gem myapp -c "npm test" # run a raw shell command
crystl shard create --gem myapp --hero wizard --isolated # summon a solo hero
crystl shard create --gem myapp --hero seeker --prompt "audit auth" # hero with an opening task
To spawn a worker on a task, use --prompt (and optionally --agent/--model). crystl launches the agent and sends the task as its first message. This is agent-neutral: a Claude orchestrator can spawn a Codex worker, or vice versa. Reach for -c only when you want a raw shell command rather than an agent; the raw command path is mutually exclusive with agent and hero launches.
| Flag | Purpose |
|---|---|
--isolated | Back the shard with a git worktree so it can’t clobber sibling shards |
--crystal-name <name> | Pick the shard’s crystal name (auto-assigned otherwise) |
--prompt, --task <text> | Task sent to the launched agent as its first message |
--agent <name> | Agent to launch: claude, codex, or antigravity (the sunset gemini still resolves for back-compat). Omit to auto-resolve (see below) |
--model <id> | Pin the agent’s model/version (e.g. gpt-5.1-codex-max) |
-c, --command <cmd> | Run a raw shell command once the shard starts (mutually exclusive with --agent, --prompt, and --hero) |
--approval <mode> | Pre-set the shard’s approval mode: manual, smart, all, or default |
--quest | Install the fanout comms channel so a worker can report back over quest_msg (see Fanout) |
--hero <name> | Summon a solo hero shard by catalog name (e.g. wizard) — applies its agent, model, and persona. May pair with --prompt/--task for the hero’s opening task and --model for a per-summon model override; mutually exclusive with --agent and -c. Requires Quest enabled. See hero below for discovery |
--json | Emit the created shard as JSON |
Agent resolution. When --agent is omitted, crystl resolves the spawned agent in order: the gem’s per-gem Default Agent (set in gem settings) → the agent already detected running in the gem → the global Default Agent (Settings → General, a typed agent [model] field like codex gpt-5.1-codex-max). If none of those resolve, the command errors rather than assuming an agent: pass --agent, run an agent in the gem first, or set a default.
shard close
Close a shard in a gem by name. This is a mutation, so it’s Guild-gated like shard create, merge, and send.
crystl shard close --gem myapp --shard opal
crystl shard close --shard opal # --gem defaults to the selected gem
crystl shard close --gem myapp --shard opal --json
Closing an isolated shard is non-destructive to committed work: crystl removes the git worktree but preserves any commits or uncommitted changes on the shard’s branch, so you can reopen the branch later from the Isolation panel. It refuses to close the last remaining shard in a gem, since a gem always needs at least one. Eligible local shards are also recorded in the resurrect list before they close; remote shards and Quest party members are skipped.
shard rename
Give a shard a short display label — a friendly name shown on its pill, in the window title, and in crystl shards / status (and on the phone). Useful for labelling the workers you fan out (parser, tests, api). This is a mutation, so it’s Guild-gated like shard create.
crystl shard rename --gem myapp --shard opal --name "auth-refactor" # set a label
crystl shard rename --shard auth-refactor --name "backend" # address by the current label
crystl shard rename --gem myapp --shard opal --name "" # clear it → back to the crystal name
The label is display-only — it never changes the shard’s crystal name, git branch, or worktree, so a rename can’t break identity or addressing. It’s normalized to a short canonical form (lowercased, ≤14 characters, control characters stripped) so it reads the same everywhere it’s shown or typed. After renaming, you can address the shard by its new label, its crystal name, or its id — all case-insensitive; --shard matches identity (name/id) first, then a label, and errors if a label is ambiguous. An empty --name clears the label.
Rename is refused for shards whose name isn’t yours to change: Quest party members (their name comes from the hero/role) and side-quest participants (the name is the live message routing key). Rename a solo hero instead, or edit the hero in Settings → Heroes.
resurrect
Undo-close for recently closed local shards — bring back a shard you just closed in a gem. resurrect list is read-only and free; the resurrect action itself is a mutation, so it’s Guild-gated like shard create, merge, and send.
crystl resurrect list --gem myapp # what can be brought back
crystl resurrect list --gem myapp --json
crystl resurrect --gem myapp # bring back the most recently closed shard
crystl resurrect --gem myapp --shard opal # bring back a specific one by name
crystl resurrect --gem myapp --shard opal --json
Without --shard, resurrect restores the most recent entry. It restores the shard’s name and color, and makes a best-effort attempt to restore its isolation (git worktree) and agent session. Quest shards are excluded — they aren’t tracked for resurrection.
| Command | Free? | Purpose |
|---|---|---|
resurrect list [--gem <name|id>] | Free | List recently closed shards that can be brought back |
resurrect [--gem <name|id>] [--shard <name>] | Guild | Bring back a closed shard (most recent if --shard omitted) |
merge
Merge an isolated shard’s git worktree branch back into the gem’s main branch. This is the integration step /fanout runs when a worker finishes. It routes through crystl’s own merge machinery, so worker agents never merge their own branches.
crystl merge --gem myapp --shard opal
crystl merge --gem myapp --shard opal --json
Returns a structured result: success (with a commit count), nothingToMerge, dirty, conflicts, failed, or notIsolated. Conflicts and dirty trees are left for you to resolve in the Isolation panel; merge never forces.
hero
Discover and summon solo hero shards — a single catalog specialist (its own name, role, persona, agent, and model) started outside a Quest party. list is free; summon is a mutation, so it’s Guild-gated, and (like the desktop swords button) it requires Quest enabled.
crystl hero list # built-in catalog + this gem's local heroes
crystl hero list --gem myapp # local heroes for a specific gem
crystl hero list --json
crystl hero summon wizard # summon into the selected gem
crystl hero summon wizard --gem myapp --isolated # in a git worktree
crystl hero summon seeker --gem myapp --prompt "review the auth diff"
crystl hero summon ranger --gem myapp --model gpt-5.1-codex-max
hero list shows the names you can pass to summon (built-ins like wizard, warrior, ranger, plus any project-local heroes in .crystl/heroes/). Party-only roles such as healer are excluded from solo summon. crystl hero summon <name> is a focused alias for crystl shard create --hero <name> — same behaviour, same gating. Add --prompt / --task to give the hero its opening assignment immediately, and --model to override the hero’s configured model for that summon only.
If you’ve customized a built-in hero in Settings (model, persona, stats, agent), solo summon uses your edited version — the same customizations a Quest party would apply. Project-local heroes are taken as-is from their .crystl/heroes/ definition.
| Command | Free? | Purpose |
|---|---|---|
hero list [--gem <name>] | Free | List summonable heroes (name · role · agent) |
hero summon <name> [--gem <name>] [--isolated] [--prompt <task>] [--model <id>] | Guild | Summon a solo hero shard |
gauntlet
Launch a release-readiness crew as solo hero shards: two Seekers, one Monk, and one Scribe. The default mode is review-only and isolated, so each hero gets its own git worktree and reports findings without editing files. Pass --fix when you want the isolated crew to make focused fixes.
crystl gauntlet "final release readiness"
crystl gauntlet --check "swift test" --check "npm test"
crystl gauntlet --fix --prompt "stabilize the xterm release"
crystl gauntlet --profile release --dry-run --json
The Scribe is instructed to watch the other gauntlet shards, consolidate a PASS/WARN/FAIL/BLOCKED verdict, and add concrete follow-ups to the Workbench when useful.
| Flag | Purpose |
|---|---|
--gem <name> | Target gem (defaults to the selected one) |
--prompt, --task <text> | Goal or scope for this gauntlet |
--check <cmd> | Check command to ask the Monk to run; repeat for multiple checks |
--fix | Allow isolated shards to make focused fixes instead of only reporting findings |
--shared | Use shared shards instead of isolated worktrees |
--profile <name> | Load defaults from .crystl/gauntlet.json |
--dry-run | Print the planned crew and prompts without creating shards |
Optional project profiles live in .crystl/gauntlet.json:
{
"profiles": {
"release": {
"prompt": "Final release readiness pass",
"checks": ["swift test", "npm test"],
"party": ["seeker", "seeker", "monk", "scribe"]
}
}
}
party
Manage Quest parties — a named roster of heroes you launch together. Build a party here, then launch it with crystl quest start --party <name>. list is free; create and delete are mutations, so they’re Guild-gated.
crystl party list # parties available to the selected gem
crystl party list --gem myapp --json
crystl party create "Design & Dev" --heroes wizard,ranger,warrior
crystl party create "Ship It" --heroes wizard,ranger --gem myapp --local
crystl party delete "Design & Dev" --gem myapp
party create seats the named heroes by identity (precedence project-local > custom > built-in, the same resolution a Quest party uses). Pass --local to commit the party to <gem>/.crystl/parties/*.json so it ships with the repo — everyone who clones the gem gets it. Without --local the party is global to your machine.
| Command | Free? | Purpose |
|---|---|---|
party list [--gem <name>] | Free | List parties available to a gem |
party create <name> --heroes <a,b,c> [--gem <name>] [--local] | Guild | Seat a new party (--local commits it to the repo) |
party delete <name> [--gem <name>] | Guild | Remove a party |
quest
Launch, clear, or end a crystl Quest party from the command line. quest start is the CLI parity for the desktop “begin quest” panel: it summons the gem’s party so a fleet of role-played agents start working in a shared chat room. quest clear archives the chat. quest end stops the active quest and turns heroes back into normal solo shards. These are mutations, so they’re Guild-gated.
crystl quest start # launch the gem's active party
crystl quest start --gem myapp --party "Design & Dev"
crystl quest start --sealed # each hero gets its own git worktree
crystl quest start --fresh # archive prior chat, then launch
crystl quest clear # archive the gem's quest chat history
crystl quest clear --gem myapp
crystl quest end --gem myapp
| Flag | Purpose |
|---|---|
--gem <name> | Target gem (defaults to the selected one) |
--party <name> | Which party to summon (defaults to the gem’s active/first party) |
--sealed | Sealed quest, where each hero runs in its own git worktree |
--fresh | Archive any prior quest chat before launching |
quest clear archives the chat aside (recoverably) and leaves the root quest docs (QUEST-LOG.md, DECISIONS.md, HANDOFF.md) untouched. quest end ends the active quest without deleting the shards.
sidequest
CLI parity for a side quest: bridge two shards you already have open into an ad-hoc two-agent chat, without summoning a party. start pairs two shards by name; status shows the active pairing; end tears it down. The two shards can live in different gems and even on different hosts. start/end are mutations (Guild-gated over the desktop CLI); status is free.
crystl sidequest status # show the active pairing (or "none")
crystl sidequest start --gem myapp --a diamond --b jade # same-gem
crystl sidequest start --a crystl-web/garnet --b crystl/citrine # fully cross-gem
crystl sidequest start --gem crystl-web --a garnet --b crystl/diamond # --gem as default scope
crystl sidequest end # end the active side quest
crystl sidequest end --gem myapp
--gem is optional. It sets a default scope for bare shard names, while --a and --b each accept a qualified <gem>/<shard> form so an endpoint can name its own gem, that is how a side quest pairs shards across gems. A bare shard name with no --gem resolves globally across every open gem, and must be unique or crystl returns an ambiguous; pass gem error.
| Flag | Purpose |
|---|---|
--gem <name> | Default gem scope (name or ID) for bare shard names. Optional; endpoints can override it with a qualified <gem>/<shard> |
--a <shard> | First shard to pair, bare or <gem>/<shard> (start only) |
--b <shard> | Second shard to pair, bare or <gem>/<shard> (start only) |
events
Stream live bridge events as JSON lines to stdout. One event per line, pipe-friendly:
crystl events # every type
crystl events --type pending_changed # filter
crystl events --type pending_changed,askuser_changed,notification,agent_status_changed,backlog_changed --limit 5
Frame envelope: {"type": "<event>", "data": <payload>}. Common event types include pending_changed, askuser_changed, notification, agent_status_changed, backlog_changed, quest_changed, and shard_update. Ctrl-C exits cleanly.
wait
Block until a condition is met, then exit. Built on events, so agents can coordinate without polling.
crystl wait pending # block until a permission request exists
crystl wait pending --timeout 30 # give up after 30s (exits with status 1)
crystl wait pending --json # emit the matching request as JSON
crystl wait askuser --timeout 60 # block until an AskUserQuestion opens
crystl wait notification --type Stop # block until a matching hook notification arrives
crystl wait awaiting --gem myapp # block until a worker parks on an in-terminal prompt
crystl wait idle --shard opal # block until a worker finishes a turn
wait pending and wait askuser have a fast path: if a matching item already exists when the command starts, it prints and exits without subscribing. wait notification listens for fresh notifications. wait awaiting catches in-terminal prompts that are not structured permission requests; wait idle exits on a working-to-idle edge.
token
Print the current bearer token for debugging.
crystl token
facet
Manage facet inserts, the snippet library behind the facet bar, from the CLI. list is free; add, slot, and remove change the library, so they’re Guild-gated.
crystl facet list # show the library (ids, labels, slots)
crystl facet list --json
crystl facet add "review" "review this branch for bugs"
crystl facet add "deploy" "npm run deploy" --kind command --slot 1
crystl facet slot <id> 2 # bind an existing insert to bar slot 2
crystl facet remove <id>
facet add flag | Purpose |
|---|---|
--scope <all|shared|isolated> | Which shards the insert is offered in (default: all) |
--action <insert|submit> | Insert the text, or insert-and-submit (default: submit) |
--kind <prompt|command> | Categorize the insert (default: prompt) |
--slot <1|2|3> | Also bind it to a facet-bar slot |
--json | Emit the created insert as JSON |
workbench
Show or hide the Workbench panel, or read and edit the WORKBENCH.md / BACKLOG.md task list from the CLI. Panel controls and edits are mutations, so they’re Guild-gated. workbench list is read-only and free.
crystl workbench # slide the panel into view (default subcommand)
crystl workbench open --gem myapp
crystl workbench close
crystl workbench toggle # flip open/closed
crystl workbench list --gem myapp
crystl workbench add "fix login redirect" --section "Next"
crystl workbench start 12 # mark task 12 [~]
crystl workbench check 12 # mark task 12 [x]
crystl workbench uncheck 12 # mark task 12 [ ]
crystl workbench cycle 12 # todo -> in progress -> done -> todo
crystl workbench delete 12
crystl workbench section "Later"
crystl backlog … is kept as an alias (the panel was renamed from “backlog”), so older scripts and habits keep working. Task indexes come from crystl workbench list.
docs
Search and read crystl’s documentation from the terminal. Handy for an agent answering “how do I X?” or “is this a bug?” without bloating its context. Pulls the docs index live from crystl.dev (cached locally for 6h). Read-only.
crystl docs # list every doc page
crystl docs notifications # exact page id → prints the full page
crystl docs "isolated shards" # otherwise searches; prints ranked matches
crystl docs isolated --full # print full bodies of all matches
crystl docs --fresh # bypass the cache and refetch
doctor
Check local CLI, bridge, and hook wiring. Useful before debugging approval/notification issues.
crystl doctor
crystl doctor --json
render
Render terminal bytes offline through crystl’s terminal grid. This does not require a running bridge; it’s for debugging terminal parser/rendering behavior.
crystl render --feed transcript.bin --cols 120 --rows 40
crystl render --feed transcript.bin --dump-cells
crystl render --feed transcript.bin --json
ssh
Configure the direct bridge address used by SSH sessions when the automatic tunnel is not enough.
crystl ssh bridge-address 100.64.0.1:19280
crystl ssh bridge-address "" # clear override
report
Send a bug report, idea, or praise to the crystl team. Free on every tier. Write the report in plain language and never include terminal output, file paths, or secrets.
crystl report bug "approval panel hangs after Ctrl-C"
crystl report idea "let me pin a shard to the top"
crystl report praise "the rail is gorgeous"
crystl report other "..." --email [email protected] # optional reply address
Scripting examples
Monitor an agent
# What is the agent doing right now?
crystl screen --gem myapp
# Poll every 5 seconds
watch -n5 crystl screen --gem myapp
Approve from another terminal
crystl pending # see what's waiting
crystl approve 1 # approve it
Launch parallel agents
crystl open ~/Projects/frontend
crystl open ~/Projects/backend
crystl send --gem frontend "claude 'update the API client'"
crystl send --gem backend "claude 'add the new endpoint'"
JSON for scripting
crystl gems --json | jq '.gems[].name'
crystl pending --json | jq '.pending | length'
HTTP API
The CLI wraps a local HTTP API at http://127.0.0.1:19280/api/v1/. You can call it directly with curl:
TOKEN=$(head -1 ~/.crystl-bridge-token)
# List gems
curl -s -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:19280/api/v1/gems | jq
# Send a command
GEM_ID="..." # from gems response
SHARD_ID="..." # from gems response
curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"text":"ls\r"}' \
"http://127.0.0.1:19280/api/v1/gems/$GEM_ID/shards/$SHARD_ID/send"
# Subscribe to real-time events (SSE)
curl -N -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:19280/api/v1/events
Endpoints
| Endpoint | Method | Purpose |
|---|---|---|
/api/v1/gems | GET | List open gems |
/api/v1/gems | POST | Open a gem |
/api/v1/gems/:id | GET | Get gem details |
/api/v1/gems/:id | DELETE | Close a gem |
/api/v1/gems/:id/shards | POST | Create a shard (incl. hero) |
/api/v1/gems/:id/shards/:sid | GET | Get shard details |
/api/v1/gems/:id/shards/:sid | DELETE | Close a shard |
/api/v1/heroes | GET | List summonable heroes (?gem= for local) |
/api/v1/gems/:id/shards/:sid/send | POST | Send text to terminal |
/api/v1/gems/:id/shards/:sid/notify | POST | Send an attributed worker notification |
/api/v1/gems/:id/shards/:sid/rename | POST | Set/clear the shard’s display label |
/api/v1/gems/:id/shards/:sid/copy | POST | Push text to the copy bar (free) |
/api/v1/gems/:id/shards/:sid/screen | GET | Get screen content |
/api/v1/gems/:id/shards/:sid/merge | POST | Merge an isolated shard’s branch |
/api/v1/gems/:id/shards/:sid/transcript | GET | Get structured shard history |
/api/v1/gems/:id/resurrect | GET | List resurrectable closed shards → { gem_id, entries } (free) |
/api/v1/gems/:id/resurrect | POST | Resurrect a closed shard (optional body { "crystalName": "opal" }) → ShardResponse |
/api/v1/pending | GET | List pending permissions |
/api/v1/pending/:id/decide | POST | Approve or deny |
/api/v1/history | GET | Recent decisions |
/api/v1/notifications | GET | Recent notifications |
/api/v1/events | GET | SSE event stream |
/api/v1/askuser | GET | List open agent questions |
/api/v1/askuser/:id/answer | POST | Answer an agent question |
/api/v1/gems/:id/quest/start | POST | Launch a quest party |
/api/v1/gems/:id/quest/clear | POST | Archive the quest chat |
/api/v1/gems/:id/quest/end | POST | End the active quest |
/api/v1/gems/:id/quest | GET | Quest state (active, messages, heroes) |
/api/v1/gems/:id/quest/message | POST | Post a message into the quest bus |
/api/v1/parties | GET/POST | List or create parties |
/api/v1/parties/:id | DELETE | Delete a party |
/api/v1/gems/:id/backlog | GET | Read the workbench/backlog |
/api/v1/gems/:id/backlog | POST | Open, close, or toggle the workbench panel |
/api/v1/gems/:id/backlog/toggle | POST | Toggle a task checkbox |
/api/v1/gems/:id/backlog/cycle | POST | Cycle a task through todo / in progress / done |
/api/v1/gems/:id/backlog/task | POST | Add a task |
/api/v1/gems/:id/backlog/task/:index | POST/DELETE | Edit or delete a task |
/api/v1/gems/:id/backlog/section | POST | Add a section |
/api/v1/gems/:id/backlog/section/:index | POST/DELETE | Rename or delete a section |
/api/v1/facets | GET/POST | List or add facet inserts |
/api/v1/settings/ssh | POST | Set or clear the SSH bridge address |
/api/v1/feedback | POST | File a bug / idea (free on every tier) |
All endpoints require Authorization: Bearer <token> header.
Security
- Auth: Every request requires a bearer token read from
~/.crystl-bridge-token(0600 permissions, regenerated each launch) - Remote mutation gate: Local loopback callers can use the CLI directly. Non-loopback callers can mutate terminals only when remote access is enabled; otherwise control routes return 403.
- Rate limited: 100 requests per second per client IP