Reference

Build alongside an AI agent

Statecraft is built so coding agents — Cursor, Claude Code, Codex — can author states, wire transitions, and refactor prototypes the same way they write application code. The desktop app syncs your workspace to a local folder as plain text files; the agent edits those files; the canvas updates within a couple of seconds.

When you'd want this

Pointing an agent at the synced folder pays off most when:

  • You want to scaffold a prototype quickly. Tell Claude Code "build me an onboarding flow with idle, loading, error, and success states" and it writes the JSX, the transitions, and the state ordering in one pass. Faster than dragging four frames into existence.
  • You want to make a sweeping edit. "Rename every Button variant from primary to solid across this project, including transitions" — tedious on the canvas, trivial for an agent against the source.
  • You're already using an agent for your app code. The same Cursor / Claude Code session can keep editing both — Statecraft projects look like a normal codebase to them.
  • You want prototypes versioned alongside production code. The synced folder is a real folder; an engineer can commit it next to the components it uses.

If you mostly drag and drop on the canvas, you don't need this. The agent surface is a "you can if you want" extra, not a requirement. The rest of this page is the reference for when you do want it.

What the desktop app does

After installing and signing in, the app watches a chosen folder on disk. Every workspace you can access shows up as a subfolder, with projects nested underneath:

synced-folder-structure.png
<sync-folder>/
  AGENTS.md                      # generated: contract for LLM assistants
  CLAUDE.md                      # pointer to AGENTS.md
  <workspace>/
    AGENTS.md                    # generated: design systems, projects, status
    _design-systems/
      _reference.md              # generated: full component catalog
      _status.md                 # generated: did each DS validate?
      <design-system>.yaml
    <project>/
      project.json
      transitions.json
      _status.md                 # generated: did each state render?
      states/
        _order.json
        <state>.jsx
      canvas/                    # scratch / sketch / WIP drafts
        _order.json
        sketch-<short>.jsx
  • One .jsx file per state, holding the state's JSX as-is — no module wrapper, no imports, just the expression the canvas renders. State ordering and display names live in a sibling _order.json; edit that file to rename or reorder states. To create a new state, just drop a fresh <slug>.jsx in states/ — the daemon mints a uuid, Title-Cases the slug as the display name, and writes the matching _order.json entry on its next reconcile.
  • A canvas/ folder of off-graph drafts that mirrors the free-form sketches on the canvas. The natural place for "try a few variants" prompts — drafts render alongside state frames but are not part of the transition graph and don't appear in the public Play viewer.
  • One YAML file per design system. The same YAML shown in the workspace's Design Systems page, in a file editable by any text editor.
  • A project.json per project with its metadata (name, goal, description, design-system id), alongside a separate transitions.json that holds the transition graph.

Edits to the canvas sync to disk in the background; edits on disk sync back up and appear in the canvas. The sync is bi-directional and continuous — there is no explicit push or pull step.

Orientation for LLM assistants

The sync folder looks like a codebase but isn't one — no package.json, no build step, no imports. To keep Cursor, Claude Code, and Codex from going off the rails the first time you point them at it, the desktop app generates a small hierarchy of markdown files alongside the synced data:

agents-md-file-content.png
  • A root AGENTS.md describing the layout, the JSX rules (single expression, no imports, reserved data-pid and data-transition attributes), and the round-trip model. A sibling CLAUDE.md is a one-line pointer to the same file for Claude Code's auto-load convention.
  • A per-workspace AGENTS.md that lists the workspace's active design systems, its projects, and a read-only banner when the owner's plan has lapsed so the agent doesn't waste effort on writes that won't persist.
  • A generated _design-systems/_reference.md catalog of every component in every design system — tag names, props, default values, and live JSX snippets. This is the piece that stops agents from hallucinating component names that aren't in your system.
  • A generated _design-systems/_status.md listing OK / FAIL / unchecked per design system, with the concrete error and a fix hint on failures. An agent that edits a <slug>.yaml should read this file ~3 seconds later to confirm the edit actually landed cleanly — the same schema + esm.sh resolver check the editor runs at render time produces this verdict, so "reported success but broken on the canvas" is no longer silent.
  • A generated <project>/_status.md doing the same job for state JSX: per-state OK / FAIL with parse errors, "did you mean Button?" hints for unresolved component names, and — when the canvas is open — the actual runtime exception streamed back from the live preview. An agent that edits a state's .jsx reads this file to know whether the state actually rendered, not just whether it parsed.
design-systems-status-file.png
project-status-file.png

These files carry a GENERATED header and are idempotently rewritten as the underlying data changes — edit the YAML or state JSX instead of the docs themselves. AGENTS.md is an open convention that Cursor, Claude Code, Codex, and most other assistants auto-load from the working directory, so no per-tool setup is needed.

For agents that prefer exit codes over markdown parsing, the same verdicts are available through structured CLI verbs that block until the daemon reaches a terminal state. After editing a design system or state file, run statecraft live status --slug <s> --wait --json (for design-system builds) or statecraft project status <ws>/<p> --wait --json (for state-render verdicts) — the CLI exits when the daemon has finished revalidating, with exit 0 on clean, 1 on failure, 124 on timeout. The --json output carries the full error payload (title, detail, suggestion, log path) when the build or render fails. No polling loops, no markdown parsing.

Why sync to disk

The canvas covers visual editing directly; a local editor covers anything that involves typing, refactoring, or automation — multi-file search and replace, AST-aware codemods, agent-driven edits, Git branching, diffs, conflict resolution. Syncing lets the two surfaces work in parallel on the same project.

Specific workflows that the desktop app enables:

  • Agent-driven authoring. Point Cursor or Claude Code at the synced folder and ask for a new state, a component refactor, or a bulk change across multiple states. The agent edits files; the canvas updates.
  • Version control. Initialise the synced folder as a Git repo. Every canvas edit becomes a file change that can be committed, branched, or diffed. Statecraft does not ship Git integration directly; the desktop app makes it unnecessary.
  • Bulk edits. Find-and-replace across every state in a project — rename a prop, swap a token reference, update an import path — works in any editor that can search a folder.
local-jsx-file-sync.png

Getting it

Install the desktop app from statecraftapp.com/download/tray. It is macOS-only today. Open the app, sign in with the same account used on the web, and choose a folder. Every workspace you can access is synced automatically, each into its own subfolder.

desktop-app-sync-folder.png

The first sync is a full download: every state, design system, and project metadata file is written to disk. Subsequent syncs are incremental.

Headless setups (Linux, CI, ssh) use the CLI directly: statecraft config set sync-folder ~/work writes the same per-machine setting the tray's picker does, and statecraft sync <folder> — the long-running daemon the tray wraps — also persists its folder argument so downstream verbs print resolved paths instead of <sync-folder> placeholders. statecraft config list shows the current value.

Workspaces, projects, and design systems created on the server after the app is running — whether from the browser, the desktop app, or the CLI verbs below — show up on disk automatically within about half a minute, no restart. A refresh icon next to the Sync folder label in Settings forces an immediate check for the impatient case.

Driving the editor from the CLI

Anything an agent can do inside the canvas — adding states, editing JSX, running snapshots — starts to feel awkward if it still has to ask a human to spin up the workspace or project in the browser. The CLI carries the same core CRUD verbs the editor does, so an agent can set up its own scratch space:

statecraft workspace create --name "Agent scratch"
statecraft workspace list
statecraft workspace rename agent-scratch --name "Agent scratch v2"
statecraft workspace rm agent-scratch --yes

statecraft design-system create --workspace agent-scratch \
    --from npm --framework react --name "React Bootstrap" \
    --package react-bootstrap --version 2.10.0
statecraft design-system list --workspace agent-scratch
statecraft design-system rename agent-scratch/react-bootstrap \
    --name "RB"
statecraft design-system rm agent-scratch/react-bootstrap --yes

statecraft project create \
    --workspace agent-scratch \
    --name "Checkout flow" \
    --design-system react-bootstrap
statecraft project list --workspace agent-scratch
statecraft project update agent-scratch/checkout-flow \
    --goal "Reduce abandoned-cart rate"
statecraft project rm agent-scratch/checkout-flow --yes

Destructive verbs (workspace rm, project rm, design-system rm) ask for confirmation interactively; pass --yes to skip the prompt for non-interactive runs. project create always requires --design-system — projects render JSX inside the DS's iframe, so the binding has to be set before the project exists.

design-system create mirrors the three starter templates the editor offers under New design system: --from none (empty stub), --from npm --package <pkg> --version <v> (esm.sh-resolved npm library), and --from css --stylesheet <url> [--component <Name>] (CSS-only framework with named wrappers — synthesised as React components or Vue components matching the project framework). --framework react|vue is required on design-system create, same as statecraft import. For compiling a local component library into a real bundle, use statecraft import … instead.

Waiting for the daemon to finish

Any verb that triggers daemon work — statecraft import, statecraft live build, or simply saving a state file — can be paired with a --wait flag to make the CLI block until the build or revalidation reaches a terminal state. Without --wait, the CLI returns the moment the request is registered and the work happens asynchronously inside the daemon; with it, the CLI behaves like any other build tool and exits when the result is known.

# Block on the first build after importing a new design system.
statecraft import --slug acme-ui --entry src/index.tsx \
    --framework react --repo-root . --wait

# Force a rebuild and wait for it to finish.
statecraft live build --slug acme-ui --wait

# Snapshot the current verdict without blocking.
statecraft live status --slug acme-ui

# Block until a state's daemon-side parse + scope check lands.
statecraft project status workspace-slug/project-slug \
    --state checkout --wait --json

Exit codes: 0 = ok, 1 = failed (build or render error), 3 = slug or project not found, 4 = a foreign publisher (typically CI) published while the wait was running, 124 = timeout. Default wall-clock timeout is 15 minutes; override with --timeout <secs> (or --timeout 0 for unbounded). With --json, the CLI emits a single record on stdout carrying the full lastBuild or state-render payload — useful as input to follow-up agent steps.

Importing a component library

The desktop app also contains the design-system importer — the path described in Bringing your library on. Pointing the importer at a local repo compiles it into an ESM bundle and uploads it as a new design system, without requiring any YAML to be authored by hand.

Conflict handling

The sync treats the most recent edit as authoritative. In practice, concurrent edits to the same state from two sources (the web canvas and a local editor) are rare because the canvas renders changes within a fraction of a second; a conflict window only opens when the desktop app is offline.

If a conflict does occur, the canvas's copy wins on reconnection and the local file is rewritten. To protect in-progress local changes, commit them to Git before bringing the desktop app back online.

When to skip the desktop app

The canvas is self-sufficient for simple projects. The desktop app becomes useful when:

  • A project grows past a handful of states.
  • Multiple states need the same edit applied consistently.
  • Design-system changes are frequent and benefit from agent-driven authoring.
  • The team wants Git-based review for design changes alongside code.