API reference: Authoring and Components list every authoring helper and JSX element, their options, and links to source and tests.
Setup
Most projects should usebunx smithers-orchestrator init; it scaffolds everything below.
To embed into an existing codebase:
smithers-orchestrator bundles React and ships the JSX runtime as
smithers-orchestrator/jsx-runtime, so you do not add react or react-dom
yourself. jsxImportSource (below) routes JSX through the workflow reconciler,
never React DOM. The one required dev dep is @types/react (React ships no
bundled types, and the JSX transform resolves its JSX namespace from it). The
browser UI surface (smithers-orchestrator/gateway-react) is the only place that
takes react/react-dom as peers.
Minimal tsconfig.json:
jsxImportSource is the only non-standard line; it routes JSX through smithers-orchestrator/jsx-runtime instead of React DOM.
Optional MDX prompts: add bun add -d @types/mdx and a preload.ts that calls mdxPlugin(), register it in bunfig.toml as preload = ["./preload.ts"].
Verify with bunx tsc --noEmit and bunx smithers-orchestrator --help.
A minimal workflow
createSmithers is a named export. The lowercase smithers wrapper is not a
top-level import; it is the property returned by createSmithers(...).
smithers((ctx) => ...) returns the SmithersWorkflow value to export from the
workflow file. The input schema above types ctx.input.repo; omit input and
ctx.input is unknown, so you must guard or parse it yourself.
outputs.analysis is the typed reference for the Zod schema, so typos are compile errors. The task body is a JSX expression ({...}) whose value is a plain object, with no LLM call here, just a static return. Real tasks pass a run prop or an AI model. See the Task component reference.
Reactivity
The tree re-renders on every frame, so branching is a normal JSX conditional. Inside a workflow function,ctx exposes ctx.input and ctx.outputMaybe(ref, { nodeId }). The latter returns the output of a completed task, or undefined if it hasn’t run yet:
The ctx parameter is optional: it is just a normal function argument, so a workflow that never reads ctx.input or ctx.outputMaybe can drop it entirely and write smithers(() => ( ... )). Add (ctx) only when you actually reference dynamic input or prior task output.
report Task doesn’t exist in the plan until analysis completes. No placeholder, no skipped node. The conditional IS the dependency. Unlike static DAG tools that require you to declare optional nodes upfront, the JSX conditional is evaluated fresh each frame: if analysis is undefined, the report task simply doesn’t exist in that frame’s plan.
Read next
- Tour: six-step worked example with agents, schemas, approvals, resume.
- How It Works: the render → execute → persist loop.
- Components: full prop surface for every JSX element.