How It Works
The architecture: the xiv CLI launches Smithers workflows, which run agents against your repo, Linear, and GitHub.
The four layers
- The xiv CLI — runs from this repo's source via
bun link. It parses your command, resolvesSMITHERS_HOME, and either installs the pack (init/update) or launches a workflow. - The managed pack — a copy of this repo's
pack/(workflows, agent definitions, prompts, and its own dependencies) installed intoSMITHERS_HOME. This is what actually executes; the repo copy is only the source. - The Smithers engine —
smithers-orchestrator, installed as a dependency of the pack. Every workflow command boils down to runningsmithers up workflows/<name>.tsx --input '{…}'insideSMITHERS_HOME, using the pack's ownnode_modules/.bin/smithers. Smithers executes the workflow graph durably: steps, retries, resumability, run state. - Agent tasks — workflow steps that drive the
claudeandcodexCLIs. Those agents do the actual work against the target repo (the directory you invoked xiv from, passed asSMITHERS_TARGET_CWD), Linear (over MCP), and GitHub (viagh).
Two consequences of this shape:
- The CLI is always live; the pack is versioned by install. Command and flag changes take effect the moment you pull, because
xivruns from source. Workflow behavior only changes when you runxiv update, because the engine reads the copy inSMITHERS_HOME— never the repo'spack/directly (except underxiv dev— see below). - The engine runs in
SMITHERS_HOMEbut operates on your repo. The working directory of thesmithersprocess isSMITHERS_HOME; the repo being changed is whateverSMITHERS_TARGET_CWDpoints at. That's why you run xiv commands from inside the target repo.
Jujutsu under the stack
The stacked-PR machinery is built on Jujutsu (jj) rather than scripted git rebase — a core design choice, and most of what makes stacks cheap to operate:
xiv stack initrunsjj git init --colocateonce per repo:.jj/lives next to.git/, git stays the interface GitHub and CI see, and jj is purely local machinery. It's reversible — delete.jj/to undo.- Rebasing is jj's native model, not a scripted loop: when
stack amendedits an entry orstack reviewfixes a finding in the branch that owns it, every descendant branch re-flows automatically, and the stack pushes the rewritten branches back out.
This is why fixing a review comment at the bottom of a ten-PR stack is a one-command operation instead of nine manual rebases. Details in Stack map & triage.
The managed pack
init/update treat the pack as a set of managed files: every file under pack/ is hashed (sha256), copied into SMITHERS_HOME, and recorded in SMITHERS_HOME/.xiv/manifest.json. On the next install, any managed file whose on-disk hash no longer matches the manifest — i.e. something you hand-edited in SMITHERS_HOME — is backed up to .xiv/backups/<timestamp>/ before being overwritten.
The point: SMITHERS_HOME is a deployment target, not a workspace. Local edits there don't survive an update (though they're never lost), which keeps every machine's installed workflows identical to some state of the repo. Full details in Managed files.
Engine-owned files are explicitly excluded from management and never touched by an install: smithers.db (and its -shm/-wal companions), accounts.json, and the executions/, runs/, and logs/ directories.
Where state lives
Everything lives under SMITHERS_HOME (default ~/.smithers; override with the env var):
| Path | What it is | Written by |
|---|---|---|
workflows/, agents.ts, prompts/, … | The managed pack (mirrors this repo's pack/) | xiv init / xiv update |
.xiv/manifest.json | Hash manifest of the managed files | xiv init / xiv update |
.xiv/backups/<timestamp>/ | Drifted files saved before overwrite | xiv update |
smithers.db (+ -shm, -wal) | The engine's durable run database | Smithers |
executions/, runs/, logs/ | Per-run output and logs | Smithers |
stacks/<feature>.json | Stack maps — one per feature, keyed by feature alone so every xiv stack command finds it from any directory | xiv stack plan and later stack commands |
review/ | xiv pr review state: clones/ (auto-cloned repos), worktrees/ (per-PR checkouts), and per-PR scratch dirs (findings, draft review body) | xiv pr review |
Nothing xiv-specific is written into the target repo itself, with one exception: xiv stack init creates a colocated .jj/ directory next to .git/ (reversible — delete it to undo).
xiv dev — the authoring loop
Editing a workflow would be painful if every change needed an xiv update round-trip into SMITHERS_HOME. So two commands bypass the managed copy and run straight from the repo's pack/:
cd pack && bun install # one-time: the pack's own deps
cd /path/to/target-repo # the repo the workflow should operate on
xiv check linear-implement --input '{"issueId":"ENG-123"}' # render the graph, no agents (fast, free)
xiv dev linear-implement --input '{"issueId":"ENG-123"}' # run it live against this repocheck maps to smithers graph (build and print the workflow graph without executing it); dev maps to smithers up, exactly like a normal run, but with the in-repo pack/ as the pack root. Both accept a bare workflow name or a path to a .tsx file, and default --cwd to the current directory. Edit pack/workflows/<name>.tsx, re-run, repeat — then xiv update when it's ready to become the installed behavior. See xiv dev & check.
Observing runs
The operational commands (xiv ps, logs, ui, inspect, cancel, down/panic) forward directly to the corresponding smithers command in SMITHERS_HOME, so they see every run regardless of which repo you launched it from. See Operations.