Skip to main content
The repository ships a first-party Claude Code plugin at claude-plugin/. Installing it wires four things into every Claude Code session:
  1. The smithers MCP server (bunx smthrs --mcp): list_workflows, run_workflow, watch_run, resolve_approval, ask_human, and the rest of the semantic tool surface.
  2. The smithers skill: teaches Claude to run durable, multi-step, or background work through Smithers instead of hand-rolled subagent trees, and to consult https://smithers.sh/llms-full.txt before writing any workflow code.
  3. The /workflows mirror: a generic Workflow-tool script, workflows/smithers-run.mjs, that launches or attaches to a Smithers run and mirrors it live into Claude Code’s /workflows progress tree, with zero per-workflow setup.
  4. A background monitor (experimental): smithers claude monitor streams approval, human-input, failure, stalled-heartbeat, and retry-churn notifications into the session even with no mirror running, and emits a periodic run-progress digest whenever a followed run has produced no notification for a full window, so a detached run that stays alive but keeps retrying can never go hours without handing the session a status to relay. It follows only runs this session subscribed to, either automatically (smithers claude tick, or a Claude-launched detached up/workflow run/MCP run_workflow) or explicitly (smithers claude subscribe RUN_ID). The workspace store is shared across sessions, so skipping this scope would wake each session for every other session’s runs and for pre-existing runs at startup (--all-runs restores that unscoped behavior); finished and cancelled runs also stay unbroadcast by default, since a followed run already reports completion through its /workflows mirror (--transitions all streams them anyway).

The /workflows mirror

Claude Code’s /workflows tree can only be populated from inside a session, by a Workflow-tool script; the plugin ships one generic script for this. Claude launches it like this:
By default the script starts the detached run and logs the runId; passing args: { runId: "run-123" } instead attaches to an existing run (after a restart, or for a run started elsewhere). The SessionStart hook announces the script’s absolute path in every .smithers project, with the count of non-terminal runs to re-attach to. New mirror launches automatically persist startedBy.harness = "claude-code" and the native CLAUDE_CODE_SESSION_ID when available. An optional args.startedByPrompt is the sole route for an attribution prompt; workflow input and transcripts are never copied into durable attribution. Attaching by runId leaves the original attribution unchanged. What the mirror renders:
  • Phases from the workflow’s real container tree (Workflow name, Sequence label, Parallel label, loop containers), labeled from the run’s persisted frames, so data-dependent fan-outs and loop iterations appear as they materialize.
  • One row per node: agent, human, approval, and dynamically discovered nodes by default (mirrorAllNodes: true in args adds compute/static rows). A running node gets a live watcher row resolving with its output; a node finished before a slot freed gets a completion row instead.
  • Approval and human-request banners as narrator lines, with the exact resolving command.
  • Continue-as-new: the mirror switches to a run’s new runId automatically.
  • Caps: at most 6 live watcher rows at once (the sync loop must never queue behind its own watchers), a 900-agent budget, and per-phase summary rows above 150 nodes; every cap logs when it engages.
Stopping the mirror workflow never stops the Smithers run: cancelling is explicit, via bunx smthrs cancel RUN_ID (or the gateway cancelRun RPC).

The smithers claude protocol

The mirror’s agents each run one CLI command and relay its JSON; all derivation and blocking happen inside the CLI, against the durable store, under a frozen wire contract ("contract": 1 in every response):
One complete mirror frame: run status, event-log cursor (seq), the latest frame’s phase plan, every node as {nodeId, label, phase, kind, state}, the nodeIds changed after --after-seq, truncated outputs of newly-terminal nodes, pending approvals and human requests, and continuedAs for continued runs. --wait blocks until a mirror-relevant event lands (tool-call and heartbeat chatter is ignored), the run reaches a terminal status, or the timeout expires ("timedOut": true, exit 0); it also waits for a not-yet-existing store or run, so a mirror launched right after workflow run --detach can’t race the engine’s first write. Ticking a non-terminal run also records the session’s subscription for claude monitor.
Blocks until one node reaches a terminal state, then reports {state, output, vanished, timedOut}. A node pruned by a later frame reports state: "skipped", vanished: true. Watchers re-invoke on timedOut: true: how a row outlives the 10-minute Bash cap on multi-hour nodes.
NDJSON follower for the plugin monitor: one line per notable transition (approval-pending, human-request, run-finished, run-failed, run-cancelled, run-continued, run-stalled, node-retrying), each with a summary and the resolving command. Subscriptions are recorded in .smithers/claude-mirror-subscriptions.json, keyed by the CLAUDE_CODE_SESSION_ID each command sees. It skips history replay, surfaces already-pending gates once at startup, and prunes a subscription once its run turns terminal. In a project without a Smithers store, it exits silently. Two of those lines exist because silence is itself a failure mode: an alive run grinding through retries crosses no failed or stalled transition, so nothing would ever wake the session. node-retrying fires when an active attempt reaches --retry-alert-attempt (default 3, re-firing on each later attempt). run-progress fires whenever a followed non-terminal run has written no line for --progress-every-ms (default 30 minutes), reporting elapsed time, the active nodes with their attempt numbers, and the checking command. When one of these lands, relay a short status to the user before deciding whether to intervene.
The explicit subscription path: subscribe points the session’s monitor at a run it didn’t start (validating the run exists); unsubscribe silences one. Both are session-scoped when CLAUDE_CODE_SESSION_ID is set; from a plain terminal, unsubscribe drops the run for every session. These commands are protocol surface for the plugin: keep responses additive. A breaking shape change bumps contract; the shipped script refuses mismatched majors with an actionable message.

Install

From the repository checkout (or a marketplace once published):
Nothing else is required: the MCP server and CLI run through bunx smthrs, and the first invocation bootstraps the CLI from npm. In a project without .smithers/, the skill initializes Smithers before the first run.

Failure modes