@sublang/slc
v0.16.0
Published
SubLang Compiler (slc): runs phase pipelines.
Maintainers
Readme
slc
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. Every intermediate is a file you can read and edit: the normalized text, one testable "shall" item per behavior, the state machine itself. The compiler also emits tests binding its output back to the 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.
- Your agents, per role. Compilation and execution run through the agent CLIs you already use — Claude Code, Codex, Gemini, OpenCode — selectable per role, with an optional second agent reviewing every compile step.
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 registry entry the playbook CLI runs through Captain.
See it run
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 artifacts are included, so you can watch a run — or read every compile stage — without waiting on a compile.
Install
npm install -g @sublang/slc @sublang/playbook
npm install -g @anthropic-ai/claude-agent-sdk @openai/codex-sdk
slc --versionRequirements:
- A POSIX platform — macOS or Linux.
Compiled script steps execute through
sh. - Node.js >= 23.6 (compiled artifacts are imported as native TypeScript).
- One supported coding-agent CLI, installed and authenticated: Claude Code, Codex CLI, Gemini CLI, or OpenCode.
The second install line supplies the agent SDKs for the default Claude and Codex lineup; name the SDKs your own lineup needs instead. A missing or too-old SDK stops the compile before any agent call and names the exact package and 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? Install the same set there and prefix the
commands with npx: a project-local install is authoritative wherever it
resolves, a project manifest declaring @sublang/playbook must install
it itself, and a global SDK is invisible to a project's nested
@sublang/cligent
([release-11] has the full rules).
Quick start
Write a prose workflow as a .md or .txt file —
demo/workflow.txt is a one-paragraph
example — and compile it from any directory:
slc playbook my-workflow.mdArtifacts 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 registry entry. Enable it in
${SPEX_HOME:-$HOME/.spex}/config/playbook.config.yaml, binding every role
listed in its requiredRoleIds to a stable player. The following example
assumes the source declares one worker role:
captain:
adapter: claude
model: claude-opus-5-5
effort: high
permissions:
mode: auto
players:
my.worker:
adapter: claude
model: claude-opus-5-5
effort: high
permissions:
mode: auto
playbooks:
my-workflow:
from: /absolute/path/to/my-workflow.ts
roles:
worker: my.workerMerge these entries into an existing config, or use the complete example for a new one, then confirm the entry and run its command:
playbook --list
playbook run "/my-workflow <your task>"An entry with different roles needs matching bindings. Relative from paths
resolve against the config file, so an absolute path is clearest. The
English and Chinese demos include a ready
configuration for both precompiled entries. Playbook's
configuration guide
explains reusable players, role overrides, and project config fragments.
Compilation runs your configured coding agent at each phase. Elapsed time
depends on the workflow, model, effort and required corrections; the
performance report records measured
settings, failures and validation separately. The reproduction guide
recreates the locally measured 3:17 minimal compilation with explicit compiler,
definition and model versions. slc reports each phase,
each artifact with its elapsed time, and a heartbeat at least every 30
seconds on stderr. An agent call that goes silent for stallTimeout
seconds fails that phase. Success prints the artifact paths and exits 0;
a failure prints diagnostics naming the failing phase and exits non-zero.
If a phase discovers missing, contradictory, or materially ambiguous behavior,
slc stops with actionable questions and exits 2. The report names the
original source to edit, the phase, and the artifact being compiled. Edit that
source and run the same command again. Questions can arise during normalization,
compilation, or linking; slc never reads interactive answers or saves a pending
session. Any draft from the stopped phase stays unaccepted and no successful
build history is published. Standard error also includes one
SLC_CLARIFICATION: {…} line containing schema
sublang.slc.clarification.v1, phase, target, sources, and questions for
tools consuming the diagnostics.
Intermediates are first-class: edit one, re-run a single phase
(slc playbook.gears2fsm …), and it lands in the same place.
After the necessary named phases and direct link have succeeded, finish a
retained canonical bundle without another model call:
# From the same working directory that contains plan.playbook/:
slc playbook.gears2fsm ./plan.playbook/plan.gears.md
slc playbook.link ./plan.playbook/plan.fsm.ts <runtime-target>
slc playbook ./plan.playbook/plan.text.md --complete --link <runtime-target>--complete checks the explicitly selected entry-form Source and its current
GEARS, FSM, and linked module, then emits the verification files and plan.ts.
It requires no agent credentials, seeds no config, leaves the authored files
and build history unchanged, and never falls back to compilation.
Omit --link when the retained bundle targets the installed Playbook runtime.
Raw input, -o, compile flags, and missing or inconsistent artifacts are refused.
The linked module must import the checked .fsm.ts object: a .fsm.js object
edge is refused even when that sibling exists. Review and correct the retained
import before completing; an unused JavaScript sibling is allowed.
These are mechanical checks of the selected current bundle; they do not prove
its earlier compilation or semantic review, or detect every coherently stale
selection. Review manual Source/artifact amendments before using them.
Host registration and actual workflow acceptance remain separate steps.
slc --help shows all invocation forms and flags, including
--no-optimize to skip the optimization passes.
Incremental recompiles
A successful full compile is snapshotted under <artifact-dir>/.slc/,
so a re-run only redoes what changed. Per phase, slc picks:
- Reuse — inputs are byte-identical: no agent call, and the artifact on disk is left exactly as it is, manual refinements included.
- Update — inputs changed: the phase runs ordinarily and also receives its prior input and a diff, as a hint to update the artifact rather than rewrite it from scratch.
- Ordinary — no usable record, a changed link phase, or
--rebuild.
A run that reuses everything prints up to date instead of paths.
History is success-only, so an interrupted or failed run simply leaves
the next one to compile ordinarily. .slc/ holds verbatim copies of
your source and outputs — treat it as no less private, gitignore it if
you commit the bundle, and delete it freely. Excluded invocation forms
and the full rules are in the
incremental spec.
Configuration
The first run seeds ~/.config/slc/config.yaml with
agent: claude-code, so a fresh machine needs no setup. A
slc.config.yaml in the working directory wins over the user config,
and SLC_AGENT, SLC_MODEL, and the other SLC_* variables override
either, per key.
# slc.config.yaml
agent: claude-code # claude-code | codex | gemini | opencode
model: claude-opus-5-5 # optional; omit to use the agent CLI's default
effort: high # optional adapter-scoped reasoning effort
fastMode: true # optional adapter-scoped fast mode; false is a literal request
reviewerAgent: codex # optional; enables reviewed compilation
reviewerModel: gpt-6.1-sol # optional reviewer model
reviewerEffort: xhigh # optional reviewer reasoning effort
reviewerFastMode: true # optional reviewer fast mode
stallTimeout: 2400 # seconds of agent silence before a stalled call fails
pipelinePath: # search roots for <pipeline> references; defaults to the cwd
- ./pipelinesEffort and fast mode are adapter-scoped: Cligent's capability contract
decides which agent CLIs accept them, and a literal on one that does not
refuses the run before any agent call. Discovery order, --config, and
validation rules live in the CLI spec;
slc --help prints the summary.
Mechanical checks run by default at GEARS-producing phases, the GEARS-to-FSM boundary, and Playbook linking. A failing check returns its findings to the same Coder for at most two repairs; a clean unreviewed transformation uses one agent call. Malformed supplied GEARS stops before its consumer runs, and an invalid FSM stops before linking. Unresolved findings fail the phase, and source questions still stop for clarification (DR-033, DR-035).
Reviewed compilation (two agents)
Set reviewerAgent to compile with two independent agents. Your agent
selection is the Coder that writes each artifact; once mechanical checks
pass, the Reviewer inspects that work read-only and reports only material correctness or
spec defects, and the Coder answers every finding with evidence and a
minimal fix. Mechanical and independent review share three rounds — if the
third still reports
findings, the phase fails closed and names them rather than shipping a
questionable artifact.
An independent review adds an agent call for each mechanically valid
transformation it inspects.
Reuse performs no transformation and so makes no calls; Update,
Ordinary, and --rebuild use the loop automatically
(DR-022).
How pipelines work
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 for 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 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.
Ecosystem
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.
Develop
npm ci
npm run build
npm test
npm run lintThe repository build explicitly uses the native TypeScript 7 compiler.
SLC retains TypeScript 6 as a runtime dependency for its programmatic AST,
emission, and strict artifact checks; TypeScript 7 has no stable compiler
API yet. The explicit build path avoids relying on which tsc executable
npm links.
A 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.
Contributing
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.
