Fix issues fast, with any model.

Point slag at an issue. It writes a regression test, fixes the code, and proves the fix with your own tests. You review one commit on a branch, or a draft PR with --pr. One flag picks the provider. Hand it a sentence instead and it forges a whole project in proof-gated ingots.

🧱 ore → 🔥 forge → ✅ done → ❌ fail? → ♻️ re-smelt ↩

Quick Start

# install curl -sSf https://slag.dev/install.sh | sh
# fix an issue in the repo you are in slag fix "export drops the last day of the range"
# a GitHub issue, fetched with gh, on another provider, then a draft PR slag fix 123 --provider cerebras --pr
# or forge a whole project from one sentence slag "Build a REST API with auth and rate limiting"
# watch and steer it live in the dashboard slag --tui "Build a REST API with auth and rate limiting"

slag needs one key from one provider. Choose the provider with --provider NAME, SLAG_PROVIDER, or provider = "NAME" in ~/.config/slag/config.toml: openrouter (the default), openai, anthropic, gemini, groq, cerebras, xai, mistral, deepseek, together, fireworks, ollama, lmstudio, or custom. Each preset brings its base URL and a default model. slag reads the provider's key variable, such as CEREBRAS_API_KEY, or the key that slag key --provider NAME stores. ollama and lmstudio need no key.

slag fix takes free text, an issue number, a GitHub issue URL, or --issue-file. It runs your tests before any edit, writes a regression test, fixes the code, and reruns the tests itself. Then it reverts the fix to show the new test fails without it. The fix lands as one commit on a slag/fix-<slug> branch (slag/fix-<n>-<slug> for a GitHub issue). --pr pushes it and opens a draft PR; --no-commit leaves it in the working tree.

slag runs shell commands and edits files in the directory you start it in. Its sandbox keeps shell writes inside that directory and temp space, while reads and the network stay open. Point it at a fresh directory or a repo you can revert.

Releases ship for macOS (Apple Silicon and Intel) and Linux (x86_64 and aarch64, glibc 2.17 or newer). Elsewhere, clone the repo and run cargo install --path slag-rs.

What the Forge Ships

⚔Twin-Cast Duels

A solo ingot can forge as two or three casts in separate git worktrees, each steered by its own direction prompt, so even one model explores different solutions. A judge model assays the casts and merges the winner. SLAG_DUEL=auto (the default) picks 1 to 3 casts by grade and work; on, off, or :duel t per ingot override it.

▣Ratatui Dashboard + Steering

--tui opens a full-screen dashboard: crucible state, live event feed, token totals. Type a message and press Enter to steer the smith mid-forge without stopping the run.

✎Plain-English Narrator

A lightweight interpreter turns raw tool calls into one-line narration on the feed, so you read "wiring the auth routes" instead of a wall of diffs and greps.

📖Recipes

Drop recipes/<name>/RECIPE.md files (frontmatter + markdown) in your repo or ~/.config/slag/recipes/. slag indexes them into every ingot prompt and the smith loads them by name.

$Spend Estimates and Caps

Spend lines show the cost OpenRouter reports, or an estimate from a dated price list (≈ $0.0031 est.). An unknown price shows token counts, and a [pricing] table overrides any rate. Dollar ceilings per ingot session (SLAG_MAX_COST_INGOT) and per run (SLAG_MAX_COST_RUN) are uncapped by default.

□Shell Sandbox

The smith's shell commands write only inside the workspace and temp directories: Seatbelt on macOS, bubblewrap or Landlock on Linux. Package caches and the repo's git hooks and config stay read-only. Proofs and slag fix test runs use the same sandbox. --no-sandbox or SLAG_SANDBOX=off turns it off.

▶Replay

Every forge records a tape in .slag/logs/, and slag replay plays it through the same renderers, in the stream view or the dashboard, at any speed, and can hold a frame at a named moment. It needs no key and no network.

✓Proof-Gated Ingots

Every ingot carries a :proof shell command. Exit 0 forges the ingot and commits; anything else feeds the error back and retries. No commit lands without its proof passing.

↻Any Provider

--provider picks one of 14 presets, from openrouter and openai to cerebras, groq, and a local ollama. Each brings its base URL and a default model, so a fresh key forges with no model to choose. Write provider::model to run one role elsewhere: --provider cerebras --plan-model openai::gpt-4.1-mini.

⚒Issue Fixes

slag fix skips the survey and the plan. One agent session adds a regression test and fixes the bug. slag reruns your tests itself and reverts the fix once to prove the new test catches the bug. The verdict comes from exit codes, not from the model's summary.

Pipeline

  PRD.md                                          PROGRESS.md
  (ore)                                             (ledger)
    |                                                 ^
    v                                                 |
 +-----------+    +--------------+    +--------+    +-------+
 | SURVEYOR  |--->| FOUNDER      |--->| FORGE  |--->| ASSAY |
 | analyze   |    | cast ingots  |    | strike |    | report|
 +-----------+    +--------------+    +--------+    +-------+
    |                |        ^             |
    v                v        |             v --temper N
 BLUEPRINT.md     PLAN.md     |         +---------+
 (analysis)   (s-expr ingots) |         | TEMPER  |
                              +-- gap --| warden  |
                                        +---------+

 New commissions keep these files in .slag/.

Forge Loop

 PICK ORE                       PARALLEL ANVILS
    |                           +---------+---------+
    v                           |         |         |
 :solo t? ----yes-----> ANVIL 1   ANVIL 2   ANVIL 3
    |                      |         |         |
    no                     v         v         v
    |                   (each anvil runs its own smith session)
    v
 SELECT SMITH by :grade
    |
    |  grade >= 3   --> plan model
    |  default      --> work model
    |  unset        --> the provider's default
    v
 STRIKE (agent session over your provider)
    |
    v
 TOOLS (read, write, edit, grep, bash)
    |
    v
 PROOF (run :proof shell command)
    |
    +----- pass ----> :forged  + git commit
    |
    +----- fail ----> :heat++ retry with slag feedback
    |
    +----- max -----> :cracked + on to the next ingot
                        (the run keeps going; ASSAY reports the cracks
                         and the run exits 1 once nothing is pending)

Four Phases and the Warden

1 SURVEYOR

Deep analysis on the plan model. Reads .slag/PRD.md (ore), produces .slag/BLUEPRINT.md with architecture, dependency graph, risk assessment, and forging sequence. Self-iterates to resolve any ambiguity.

2 FOUNDER

Casts S-expression ingots from the blueprint. Each ingot has an ID, complexity grade, skill tag, proof command, and work description. Outputs .slag/PLAN.md as the crucible.

3 FORGE

Strikes each ingot with an agent session over your provider. Solo ingots run on parallel anvils (up to 3). Grade picks the model. Retries with slag feedback on failure. Commits on success.

+ TEMPER

Opt in with --temper N. A warden on the plan model, a critic that built nothing, writes the acceptance bar to .slag/BAR.md and checks the blueprint and the plan against it first. After the forge it opens the built project, runs its checks in a read-only sandbox, and compares the result with the bar. On a loss, the gap it names becomes new ingots and the forge runs again, up to N rounds.

4 ASSAY

Final quality report. Shows forged/cracked counts, temperature bar, and identifies any cracked ingots. It prints on failed runs too, not only clean ones. Exits 0 on full forge, 1 if any ingot cracked.

Design Decisions

Why Any Provider?

slag speaks the OpenAI-compatible chat-completions API, so any provider that serves it works, local ones included. OpenAI GPT-5.4 and later go through the Responses API instead. A preset brings the base URL and a default model for every role, so a fresh key forges without you choosing a model. OpenRouter stays the default. Override any role when you have an opinion: a flag, an environment variable, or a line in ~/.config/slag/config.toml. Write provider::model to send one role to another provider; the :: keeps OpenRouter ids such as openai/gpt-5 working. The file wants the lowercase key; the uppercase name is the environment variable and is ignored inside the file.

Role Env var config.toml key Flag
work SLAG_MODEL_BASE model_base --model
plan SLAG_MODEL_PLAN model_plan --plan-model
duel SLAG_MODEL_ALT model_alt
judge SLAG_MODEL_JUDGE model_judge --judge-model
editor SLAG_MODEL_EDITOR model_editor --editor-model

The editor role writes harness edits for slag fix --evolve and defaults to the plan model. The provider, its key, the duel knobs, and the spend caps follow the same rule: provider, <provider>_api_key (such as openai_api_key), duel, duel_rounds, screenshot_cmd, max_cost_per_ingot, max_cost_per_run in the file; SLAG_PROVIDER, the provider's key variable (such as OPENAI_API_KEY), SLAG_DUEL, SLAG_DUEL_ROUNDS, SLAG_SCREENSHOT_CMD, SLAG_MAX_COST_INGOT, SLAG_MAX_COST_RUN in the environment.

Why S-Expressions?

S-expressions are single-line and grep-parseable, so PLAN.md stays readable by you and by a one-line shell command. Every ingot is one line. Fields are keyword-prefixed (:id, :status), which makes them unambiguous to extract with pattern matching and cheap for a model to emit correctly.

Why Parallel Anvils?

Independent ingots (:solo t) run concurrently, up to MAX_ANVILS=3. This gives 3x throughput for foundation tasks that have no dependencies. Each anvil gets its own smith session. Sequential ingots (:solo nil) run one at a time after parallel work completes.

Why Proof-Based Verification?

Every ingot carries a :proof field containing a shell command. Exit code 0 means pass, anything else means fail. No human review needed. Proofs are concrete: "test -f file", "npm test", "grep -q pattern file". This enables fully autonomous forging with machine-verifiable quality gates.

Why Self-Iteration?

When a surveyor or founder output contains questions, slag detects them and feeds the output back with instructions to resolve autonomously. Up to 3 rounds. This prevents the forge from stalling on ambiguity. The AI is instructed to make expert decisions rather than ask for clarification.

Why Metallurgical Metaphor?

Unambiguous vocabulary that maps naturally to the pipeline. Ore (raw input) is surveyed, cast into ingots, heated in a forge, and either becomes forged steel or cracked waste. Every term has exactly one meaning. The temperature gradient (cold → hot → pure) maps to progress from unstarted to complete.

Ingot Examples

Field Reference

Field Values Meaning
:id "i1", "i2", ... Unique ingot identifier
:status ore | molten | forged | cracked Lifecycle state
:solo t | nil Can run in parallel (t) or must be sequential (nil)
:grade 1-5 Complexity level; grade ≥ 3 uses the plan model
:skill web | api | cli | default Work-type tag. Today only web changes anything: Playwright note in the prompt, duel screenshots
:heat 0-N Current retry attempt
:max 5-8+ Max retries before cracking
:smelt 0 | 1 Re-smelt count. A cracked ingot gets one rewrite; at 1 it cracks for good
:proof shell command Acceptance test (exit 0 = pass)
:bar string The ingot's acceptance bar. With --temper, the warden judges it after the proof passes; empty means the proof alone decides
:work string Task description for the smith
:duel t | nil Twin-cast override: t forces a duel, nil blocks one, absent follows SLAG_DUEL
:casts 1 | 2 | 3 Cast-count override; absent follows SLAG_DUEL and the grade

Those thirteen are every field the parser reads. Any other :key value pair survives a rewrite untouched.

CLI Reference

Command Role
slag fix ISSUEFix one issue in this repository and commit it on a new branch. Takes free text, an issue number, or a GitHub issue URL. --pr opens a draft PR. --tries N, --test CMD, --temper, --tui, --model and --plan-model tune the run
slag fix --evolve KExperimental. Evolve the harness on this issue first (Harness², arXiv 2610.14224). It costs several fix attempts, capped at $2 by default. On slag's own bench it has not beaten plain --tries 4. --harness library reuses past lessons with no probing
slag "commission"Write .slag/PRD.md from the commission and run the full pipeline
slagForge the .slag/PRD.md already in this directory
slag key [KEY] [--provider NAME]Show provider setup or store its key
slag costShow session spend, and the provider balance when available
slag status [--json]Show crucible state; --json prints one object for scripts
slag resumeResume an existing forge
slag replay FILEPlay a recorded tape through the renderers. --speed, --tui, --at, --points
slag runsList past runs from the event logs
slag insightsOffline analytics over the logs: forged and cracked ingots, heats, spend, tool errors
slag psList live forges on this machine
slag rewindRestore the workspace from an attempt checkpoint
slag hooks listPrint the hook events and the exit-code contract
slag updateSelf-update to the latest release
Flag Effect
--provider NAMEProvider preset (overrides SLAG_PROVIDER)
--tuiFull-screen dashboard: crucible, live feed, steering input
--temper NJudge the result against the goal with the warden, up to N rounds (default 0, off)
--anvils NMax parallel anvil workers (default 3)
--autoReset all roles to the selected provider's default model, ignoring environment and config model settings
--model MODELWorker model ID or provider::model
--plan-model MODELPlanner model ID or provider::model, also for grade ≥ 3 ingots
--judge-model MODELDuel judge model ID or provider::model
--duelGive every solo ingot at least two casts (same as SLAG_DUEL=on)
--trace FILEWrite a Chrome trace-event file, one lane per anvil
--forceAccept a one-word commission instead of reading it as a mistyped subcommand
--no-sandboxLet shell commands write outside the workspace (same as SLAG_SANDBOX=off)
--worktreeBranch-per-ingot isolation. Not implemented yet: warns and runs in the shared checkout
Environment variable Effect
OPENROUTER_API_KEY, OPENAI_API_KEY, CEREBRAS_API_KEY, …The provider's key. Overrides that provider's stored key
SLAG_PROVIDERDefault provider; --provider takes precedence
SLAG_BASE_URLOverride the selected provider's API root
SLAG_<PROVIDER>_BASE_URLOverride one provider's API root in a mixed run
SLAG_MODEL_BASE / _PLAN / _ALT / _JUDGE / _EDITORPer-role model ids
SLAG_SANDBOXauto (default) / off for the shell sandbox
SLAG_DUELauto (default) / on / off
SLAG_DUEL_ROUNDSOverride the duel round cap
SLAG_MAX_COST_INGOTDollar ceiling for a single ingot session
SLAG_MAX_COST_RUNDollar ceiling for the whole run
SLAG_EVOLVE_BUDGET / SLAG_EVOLVE_TOKENSDollar and token caps for --evolve (defaults 2 and 4000000)
SLAG_HARNESS_LIBRARYHarness library directory for --evolve and --harness
SLAG_DISABLE_HOOKSTurn off every [hooks] entry for this run
SLAG_REASONING_EFFORTlow / medium / high for models that take it
SLAG_SCREENSHOT_CMDShell command producing a screenshot for visual assay of web ingots
SLAG_CHAR_BUDGETTranscript char budget before an agent session compacts
SLAG_CONFIG_DIRMove the config directory off ~/.config/slag
SLAG_OPENROUTER_BASELegacy OpenRouter-only base URL override
NO_COLOR / COLORTERMDrop color entirely / enable truecolor