The SubLang Compiler: describe a workflow in a paragraph of prose, get a deterministic multi-agent program you can inspect, verify, and run.
You write what should happen — in English or Chinese, no DSL, no
orchestration framework. slc compiles that paragraph into a spec, a
state machine, and a runnable playbook that drives AI coding agents
through it. Why compile prose instead of prompting with it:
- Deterministic where it matters. Who acts, in what order, when to stop — the control flow becomes an inspectable XState machine, not prompt improvisation. LLM judgment is confined to the work inside each state.
- Auditable at every stage. The intermediates are first-class files you can read and edit: the normalized text, one testable "shall" item per behavior (in the GEARS spec grammar the SubLang stack shares), and the state machine itself. The compiler also emits verification tests binding its output to the source spec.
- Cheaper and safer by optimization. Steps that need no judgment are rewritten at compile time into plain shell commands — no LLM call, no hallucination, verifiable before anything runs.
- Your agents, per role. Compilation and execution run through the agent CLIs you already use — Claude Code, Codex, Gemini, OpenCode — selectable per role.
The flagship pipeline is playbook (the name of both the pipeline and
the sibling playbook project
that executes its output): prose → GEARS spec items → XState machine →
a linked module the playbook CLI runs.
The demo compiles a one-paragraph description into a two-agent code-review loop, then lets it loose on a buggy C file: the coder and reviewer commit, review, and debate inside a real Git repository until the review comes back clean. Precompiled reference artifacts are included, so you can watch a run — or just read every compile stage — without waiting on a compile.
npm install -g @sublang/slc @sublang/playbook
npm install -g @anthropic-ai/claude-agent-sdk @openai/codex-sdk
slc --versionThe second line supplies the agent SDKs for the default Claude and Codex
lineup. Playbook 4 installs none itself — which versions work is
@sublang/cligent's to enforce at load — so name the SDKs your own
configuration needs. If one is missing, the compile stops before any
agent call and names the package to install; if one is too old, it
names the installed and required versions along with the exact version
to install.
Compiled artifacts import the Playbook engine from their own directory;
when that import does not resolve, playbook run (3.1+) links its own
engine beside the artifact and says so (--no-provision opts out).
Working inside an npm project instead? Install the same set there — the
compiler, the engine, and the SDKs your lineup uses — and prefix the
commands with npx. A project's own install is always authoritative,
and a globally installed SDK is invisible to a project's nested
@sublang/cligent, so the SDKs belong in the same tree
(RELEASE-11 has the full rules).
Requirements:
- A POSIX platform — macOS or Linux; on Windows, use WSL (or Git
Bash). Compiled script steps execute through
sh, so native Windows is not supported. - Node.js >= 23.6 (compiled phase artifacts are imported as native TypeScript at runtime).
- One supported coding-agent CLI, installed and authenticated: Claude Code, Codex CLI, Gemini CLI, or OpenCode.
In any directory, write a prose workflow as a .md or plain .txt
file — demo/workflow.txt is a complete
one-paragraph example — and compile it:
slc playbook my-workflow.mdslc finds the playbook pipeline inside its own @sublang/playbook
dependency, so it compiles in any directory — no clone, no project
setup. Compilation drives your configured coding agent — the first run
seeds ~/.config/slc/config.yaml with agent: claude-code; set
SLC_AGENT (or edit that file) to use another agent CLI. Expect it to
take a while: duration is agent- and workload-dependent, and measured
compiles of a five-line workflow have run from tens of minutes to more
than two hours, with the first intermediate typically landing within
about five minutes. Plain-text input (.txt) works too; it is
normalized first. The pipeline's optimization pass, which rewrites
judgment-free steps into plain script, runs by default
(--no-optimize skips it).
Artifacts land in your working directory: my-workflow.playbook/ holds
the intermediates — my-workflow.gears.md, the XState machine
my-workflow.fsm.ts, the linked runtime module, and its verification
tests — and my-workflow.ts is the runnable entry. Run it:
playbook run ./my-workflow.ts "<your task>"Intermediates are first-class: edit one and re-run a single phase
(slc playbook.gears2fsm …) and it lands in the same place.
slc --help shows all invocation forms.
While a compile runs, slc reports progress on stderr: each phase as it
starts, each artifact as it lands with the elapsed time, the compiled
runtime's own state transitions, and a heartbeat so the terminal is
never silent for more than 30 seconds. An agent call that goes quiet for
stallTimeout seconds (default 600, 0 disables) is aborted and
reported as a failed phase rather than hanging indefinitely.
Success prints the written artifact paths to stdout and exits 0; a failure prints diagnostics to stderr — naming the failing phase when one is at fault — and exits non-zero.
slc reads its agent and pipeline settings from an optional YAML config file,
overridden per key by environment variables. A blank or unset environment value
falls through to the file. When no config file exists anywhere, the first run
seeds ~/.config/slc/config.yaml with agent: claude-code, so a fresh
machine needs no setup; model falls through to the agent CLI's own default
and pipelinePath to the working directory.
# slc.config.yaml
agent: claude-code # claude-code | codex | gemini | opencode
model: claude-opus-4-8 # optional; omit to use the agent CLI's default
stallTimeout: 600 # seconds of agent silence before a stalled call fails
pipelinePath: # search roots for <pipeline> references; defaults to the cwd
- ./pipelinesA slc.config.yaml in the working directory wins over the user config;
SLC_AGENT, SLC_MODEL, SLC_STALL_TIMEOUT, and SLC_PIPELINE_PATH
override either per key. Discovery order, --config, and validation
rules live in the CLI spec; slc --help prints the
summary.
A pipeline is a directory of phase definitions named
<source-format>2<target-format>.md, each declaring its formats in a
## Formats table, plus an optional link.md defining the terminal
link phase. slc infers phase order by chaining formats — no
manifest — and refuses incomplete, branching, or cyclic chains. Adding
a phase means writing a definition, never changing the compiler: slc
itself performs only the generic mechanics of chaining, validation, and
artifact placement. The bundled playbook pipeline chains text2gears
and gears2fsm, with link emitting the runnable runtime.
Every phase runs through a coding agent, one of two ways:
- Interpreted — the configured agent reads the definition and
performs it. This is how an npm-installed
slcruns theplaybookpipeline, using the definitions shipped inside@sublang/playbook. - Compiled — the phase's own compiled playbook artifact drives the
agent through audited state-machine steps. This repository's checkout
runs its bundled phases this way:
slcis self-hosting, its phase definitions compiled, reviewed, and sha256-pinned underpipelines/playbook/, failing closed on drift (self-hosting spec).
Specs are the source of truth — start at the spec map.
slc is part of the SubLang stack (all Apache-2.0,
github.com/sublang-ai):
- cligent — the unified
coding-agent SDK
slcexecutes phases through. - playbook — authors the
playbookpipeline's phase specs and runs the compiled output. - spex — the spec tool that owns
the shared GEARS grammar and invokes
slcfor its in-app playbook compile flow.
npm ci
npm run build
npm test
npm run lintA checkout's own slc.config.yaml routes the
playbook pipeline to the bundled copy under pipelines/, so repo
compiles exercise the pinned artifacts. CI additionally re-verifies the
compiled meta-phase bundles and checks that pin regeneration is
byte-identical to the committed index.
We welcome contributions of all kinds.
- 🌟 Star our repo if you find slc useful.
- Open an issue for bugs or feature requests.
- Open a PR for fixes or improvements.
- Discuss on Discord for support or new ideas.