Back to home
[SLC]DOCS

SLC · Guide

Documentation

Groundwork — the SLC terminal: it scrutinizes your idea, then writes a complete, validated spec tree — using your own AI.

00

Welcome

SLC — the Spec Language for Cognition — keeps AI on track by writing your project down as a set of spec files that every session reads first. Groundwork is the SLC terminal: it lays the groundwork (your specs) before a single line of code. It reads your requirements, asks only what it genuinely doesn't know, and writes a complete, validated spec tree — using your own AI.

SLC writes specs, never application code. Then you point any coding agent (Claude Code, Cursor, Copilot…) at those specs and build.

It's built on Ink (React for terminals), so the mission map, every prompt, and every report — doctor, estimate, db, audit — are real components rather than concatenated strings.

Prefer to work by hand, with no CLI at all? Switch to the Manual · Classic edition using the toggle above — it documents the original file-based workflow.

01

Install & first run

No install needed — run it straight from your package manager, inside the folder that has (or will have) your requirement. It doesn't have to be named requirement.md; SLC finds it either way (see 03). Pick the one you use:

npm
npx @wewiselabs/slc
pnpm
pnpm dlx @wewiselabs/slc
bun
bunx @wewiselabs/slc

Prefer the bare slc command? Install it globally once — then slc, slc doctor, and slc --help work anywhere:

bash
npm install -g @wewiselabs/slc
With npx, always use the full scoped name. npx slc (unscoped) is a different, unrelated package on npm.
Requires Node 22 or newer — that's what the Ink terminal UI needs. If the install fails or the prompts behave oddly, check node --version first (Node 18 is past its own end-of-life anyway).
Everything SLC saves for itself (AI config, mission map, run files, backups) lives in .slc/ — created automatically and auto-gitignored.
02

Connect your AI

SLC never ships its own model — you bring the brain, so the specs are 100% yours. There are two ways to connect, and you do NOT need an API key.

Bridge mode — recommended

Already paying for Claude Code, Copilot, or a chat plan that bundles many models? Bridge mode uses exactly that — no separate API key. SLC writes the full generation prompt to a file; you tell your agent to follow it and save the JSON result; SLC reads it back, writes the spec files, and validates them. Your subscription does the thinking; SLC does the structure.

text
"Follow .slc/run/backend.prompt.md and save the JSON to .slc/run/backend.output.json"
The SLC rulebook is staged once under .slc/rulebook/ and referenced from each prompt, so prompt files stay a few KB. Output files are decoded in any encoding — UTF-8, UTF-16, BOM all fine.

API-key mode

Have a key? Pick Anthropic (Claude, Messages API) or OpenAI-compatible and point it anywhere that speaks /chat/completions. The key is verified with a tiny test call, then saved to .slc/config.json (auto-gitignored). ANTHROPIC_API_KEY / OPENAI_API_KEY in your environment are picked up automatically.

  • OpenAI — base URL https://api.openai.com/v1 · e.g. gpt-5
  • Anthropic — pick the Anthropic shape · e.g. claude-opus-4-8
  • OpenRouter — base URL https://openrouter.ai/api/v1 · any listed model
  • Ollama (fully local, free) — base URL http://localhost:11434/v1 · e.g. llama3.3, key: ollama
Use one consistent model for the whole project. Switching mid-way causes conflicting interpretations. Best picks: Claude Opus 4.8 / Sonnet 4.6 for spec generation; GPT-5 and Gemini 3 Pro are solid alternatives.

Either way, your choice is saved to .slc/config.json so the next run is one tap.

03

Your requirement

Everything starts from a requirements document — the single source of truth SLC uses to generate your whole spec. requirement.md at the project root is the convention, but neither the name nor the shape is enforced; what matters is what's inside it. The more detail you give, the fewer guesses the model has to make.

markdown
## Goal
What the app does in 2-3 sentences.

## User Journeys
Step-by-step flows for every type of user.

## Features
Every feature listed explicitly.

## Tech Stack
- Frontend, Backend, Database, third-party services.

## Design
Colors (hex), font, design style.

## Non-Goals
What the app will NOT do.

## Constraints
Hard limits that must never be violated.
Rule of thumb: if you wouldn't trust a junior developer to guess it, write it down. Vague requirements produce hallucinated specs.

Bad vs good

  • Weak: “Users can log in.”
  • Strong: “Users log in with email + password. A JWT is issued on success, stored in an httpOnly cookie named app_token, expiring in 24 hours.”

How SLC finds it

Your file doesn't have to be named requirement.md, and it doesn't have to look like the template above. Discovery is layered, and every layer judges content, never format:

  1. Common names first — requirement.md, requirements.md, PRD.md (any case). Instant, silent, zero setup.
  2. Not found? SLC scans the folder — any root-level .md or .txt file — and scores each one on whether it actually covers the essentials (goal, users, stack, auth, data, non-goals, constraints — checked by keyword, not by header text, so any structure counts). One clear winner is used automatically and SLC tells you which and why; a few plausible files get you a quick picker; nothing plausible falls through to describing the idea or scaffolding a template. It never just gives up.
  3. Know exactly which file? Skip discovery entirely with --file.
bash
npx @wewiselabs/slc --file docs/product-brief.md
Once found, the format is never judged. Scrutiny checks whether the content covers the essentials — never whether it's shaped like SLC's own template. A complete brief in your own structure is left exactly as you wrote it.
No requirement file at all? Type the idea straight into the terminal and scrutiny reviews it and drafts the file for you — or pick “Point me at a file” from the same menu and give it a path to anything, anywhere on disk.
SLC scans your requirement for secret-looking strings and warns before sending anything to your AI. Keep real keys in .slc_secrets or your environment — never in the requirement.
04

Scrutiny — the idea gate

Most AI tools execute whatever you typed. SLC doesn't. Before a single spec is generated, your own LLM audits the brief like a principal engineer reviewing it: clarity (contradictions, missing essentials), feasibility & compatibility (stack choices that fight each other or your hosting), security footguns, scale & cost, and monetization.

One question sets the lens — the posture: hobby (simplest, cheapest, no enterprise nagging), growth (flag scale traps and cost hotspots), or business (security, costs, and billing flows are first-class).

text
┌  Scrutiny report
│  !  Good idea — needs repairs before specs.
│
│  ✗ BLOCK [security]  The admin dashboard has no authentication
│     ↳ Gate every /admin route behind the same login + admin role check
│  !  [compatibility]  SQLite won't survive multi-instance hosting
│     ↳ Use the hosted Postgres your platform offers
│  ·  [cost]  Image uploads on serverless get pricey past ~10k users
└

What you get back

  • A scrutiny report — blockers → warnings → notes, each with a one-line fix. The full report is saved to .slc/scrutiny.json.
  • Every decision genuinely worth making — there is no cap. A real gap SLC doesn't ask about becomes a silent wrong guess later, which is worse than one more question. Each decision ships a suggested answer you can accept by just pressing Enter.
  • Repairs you can see before they land — and only if there is anything to repair.

Scrutiny reviews; it does not rewrite

If your requirement already exists as a file, SLC treats it as yours — your structure, your wording, your section order, even if it's nothing like SLC's own template (it might be better). A brief that's already solid is left completely untouched: no menu, no “repaired” copy, nothing to confirm. SLC says so and moves straight on.

When there genuinely are fixes, SLC shows you a diff — lines added and removed — before anything is written, so “apply” never means “trust me”. Only the flagged spots change, plus one appended ## Assumptions (recorded by SLC) section if anything was left undecided. Your original is backed up to .slc/requirement.backup.md before any real change lands.

Skipped decisions become explicit assumptions recorded in spec/MEMORY.md — never silent inventions.
Disagree with a flag? Pick “Continue with my original text” — the flag is recorded as an assumption, never enforced. You can also go edit the requirement yourself and get re-checked.
05

The guided flow

Groundwork runs on a harness: a persistent mission map saved to .slc/mission.json. It always knows where you are, what's done, and what the end goal is — across pauses, Ctrl+C, and restarts. Every phase gets a numbered section header, so a long run never turns into a wall of text.

text
✦ MISSION — a validated spec tree, then handoff
│
├ ✓ Connect your AI
├ ✓ Read your requirement
├ ✓ Scrutiny — check, repair & confirm the idea
├ ▸ Backend specs   ◄ you are here
│   ├ ✓ generate
│   ├ ✓ health check
│   └ ▸ your review
├ · Checkpoint — project setup
├ · Frontend specs (from the backend contract)
└ · Handoff to your coding agent

Backend first, then the review gate

  1. SLC detects the scope (full-stack, backend-only, frontend-only) and asks you to confirm — its guess is only a proposal.
  2. Backend specs are generated first and validated with slc doctor automatically.
  3. The review gate: open spec/backend_specs and look. Lock them in — or pick “Something's off” and describe the change in a sentence (“split auth into its own phase”, “we use Prisma not raw SQL”). The backend regenerates with your note attached, as many rounds as you need. Nothing is frozen until you say so.
The frontend CONTRACT is derived from the backend — never invented. Frontend-only project? The CONTRACT derives from the requirement itself, and every derived endpoint is recorded as an explicit ASSUMPTION in spec/MEMORY.md.

The checkpoint — SLC waits for you

After the backend specs are locked, SLC pauses on purpose. Some things only you can do: create the database, set real secrets, configure hosting. SLC scans your specs for their {PLACEHOLDER} tokens and turns them into your setup checklist. Put real values in .slc_secrets (gitignored) or your environment — then answer “all set”, “give me a minute” (SLC waits), or “skip for now” (the handoff reminds you).

Your brand, your taste

Want it to look like YOUR brand? Drop reference material in an inspo/ folder before the design-taste step: up to 4 images (screenshots, a logo, a moodboard — ≤3MB each), brand/design files (.css, tokens.json, tailwind.config.js — their colors/fonts/radii are treated as law), or links. In API mode images are attached to the model's vision call; in bridge mode your agent is told to open and study the files. Whatever got picked up is printed before the demo renders, and the locked design.json records which references shaped the look.

Pause, resume, crash — all safe

  • Interrupted? Re-run slc — it offers Resume (reopens the mission map exactly where you stopped) or Regenerate. Nothing is silently overwritten.
  • Stopped mid-bridge after your agent wrote the output? SLC detects the prompt is unchanged and offers to reuse the file — the work is never thrown away.
  • State files are written atomically (temp + rename) — a crash can't leave a half-written state behind. A corrupt old state is moved aside and reported, never silently treated as “no state”.
  • An existing spec/ tree is never touched without asking — and regenerating over uncommitted changes demands an explicit extra confirmation.
  • Cloned an already-built project? .slc/state.json is gitignored (it can hold an API key), so a fresh clone never has it — but SLC doesn't call that “start over”. It samples evidence off the spec tree itself (which sides exist, task status counts, a leftover scrutiny report) and offers Resume at the inferred phase, labelled “inferred, not saved state”, with the reasons printed above the menu.
06

Handoff — your agent builds

SLC's job ends at a validated spec tree — it does not write application code; your agent does. The final screen prints the exact line to paste into your coding agent, using the real first task from your task_index (not a guess). It's also saved to .slc/START_HERE.md so it survives the terminal scrolling.

text
Read SPEC.md, follow its read_order, then execute task 1.1 from spec/backend_specs/tasks/task_index.md
  • Bridge mode: you're already in your agent — just send it that line.
  • API mode: open your agent (Claude Code, Cursor, …) in the folder and send it that line.

From there your agent self-drives the SLC execution loop: read SPEC.md → task_index → execute → update status. SLC decides what to build and in what order; your agent writes the code. One task per conversation works best.

07

Working on a built project

Once the spec tree exists, you don't re-run slc for every change — that command is for a new project or a deliberate full regeneration. Four commands cover the life after the first run.

Add one feature — slc feature

bash
slc feature "Add a due-date field to todos, shown on the list and editable per item"

This is a narrow, additive operation, not a re-run of the guided flow. It reads what already exists — CONTEXT.md's non-goals, CONSTRAINTS.md's hard rules, MEMORY.md's frozen decisions, the current backend/frontend CONTRACT and task_index.md — and checks your request against them. A real conflict comes back as a flag (severity block for anything that truly contradicts something frozen) instead of being silently generated around.

  • New tasks always land in a brand-new phase number — never a renumbered slot inside an existing phase — so ids can't collide by construction, not by careful counting.
  • CONTEXT.md, CONSTRAINTS.md and SECURITY.md are never touched. If the feature genuinely needs one of them to change, that's a full slc run — SLC says so instead of guessing.
  • You see the exact file list before anything is written, and slc doctor runs both before (adding to an already-broken tree just compounds the breakage) and immediately after.
  • No description on the command line? SLC asks for one — and you can attach a file instead of typing, which helps when the request is a long bug report or a design note you already wrote somewhere else.

What it saved you — slc estimate

bash
slc estimate

A token report for the spec tree that's already there, split into three kinds of number on purpose — SLC avoids confident-sounding figures it can't back up. MEASURED: file count, size, and a rough token estimate (bytes ÷ 4, labelled rough — not an exact tokenizer count), read straight off disk. DERIVED: task count and total agent-time, surfaced from the total_tasks / total_estimate your specs already carry — not a new guess. ESTIMATED: a token-savings range for spec-driven vs. no spec tree, with the assumption printed next to it, shown as a range and never a single confident number.

No dollar figure is ever built in — model pricing changes too often to bake into a CLI without going stale and misleading you. Give it a $/million-token rate when it asks (Enter to skip) and it converts the range for you.

The data model — slc db

bash
slc db

Reads every data_model entry under backend_specs/ARCH.md (or arch/*.md when split) and prints entities, fields and types, plus the relationships it can infer from *_id-style field names — for example Session.user_id → User.id. Pure and instant, with no LLM call, and it says so plainly when nothing matches the documented shape rather than guessing at a different one.

Risk in the specs — slc audit

bash
slc audit

Scrutiny reviews your requirement before any spec exists. Doctor checks that the generated specs are structurally valid SLC. Neither one reads the actual architecture for security or design risk — audit is that missing pass: an LLM review of ARCH + CONTRACT + SECURITY + CONSTRAINTS for authn/authz gaps, unprotected sensitive fields, boundary violations the ARCH itself declares and then contradicts, and constraint conflicts. Same flag shape as scrutiny: block / warn / note, each with a one-line fix.

audit is read-only — a report, not an auto-fix — because a wrong security “fix” is worse than a wrong structural one. Address the findings with slc feature or by editing the specs yourself.
08

Commands

The guided generator — the whole flow above, resumable. It finds your requirement automatically:

bash
npx @wewiselabs/slc

The same run, but reading the requirement from an exact path — any name, any format:

bash
npx @wewiselabs/slc --file docs/product-brief.md

For a project that already has a spec tree (section 07 covers each of these in full):

bash
slc feature "<description>"   # add one feature — new phase, nothing renumbered
slc estimate                  # token report: measured + derived + estimated
slc db                        # entities, fields, inferred relationships (no LLM call)
slc audit                     # security/architecture review of the specs — report only

Validate an existing spec/ tree — structure, references, contracts, secrets, and post-merge damage. Exit code 1 on errors:

bash
npx @wewiselabs/slc doctor

The same check as machine-readable JSON — headless, CI-friendly. Drop this line into your workflow to guard the spec tree:

yaml
- run: npx @wewiselabs/slc doctor --json

Help and version:

bash
npx @wewiselabs/slc --help
npx @wewiselabs/slc --version
Banner looks garbled on a legacy console? SLC_ASCII=1 slc renders plain-ASCII art, and SLC_ANIM=0 disables the intro animation. slc itself needs a real terminal; doctor --json runs headless.
09

Teams & CI

The spec tree is designed to be merged, but merges break specs in ways no single branch can see. Three habits keep it safe:

  1. Never number things sequentially on a branch. Decisions are dec-<slug> (e.g. dec-auth-jwt-over-sessions), never dec1, dec2 — two branches will both allocate “the next number” and collide, in MEMORY.md and in every code comment that cites it. Slugs derived from content don't collide; when they do, it's literally the same decision.
  2. Run slc doctor after every merge — or gate merges with the CI line above. It catches exactly the post-merge breakage: leftover conflict markers, duplicate block names and task ids, ambiguous references, ARCH.md and arch/ coexisting, orphaned task files, stale total_tasks counts.
  3. Know what's shared and what's local. spec/, requirement.md, and AGENTS.md/CLAUDE.md are shared — commit them. .slc/ is local per developer (your key, your run state, your paper trail) and stays gitignored.
Two branches disagree on a task's status? Take the more advanced one: done beats in-progress beats todo. Counters like total_tasks are derived — the task list is the truth, and doctor tells you when a count went stale.
10

Do & don't

Do

  • Write your requirement like you'd brief a junior dev — explicit features, stack, non-goals. Any filename, any structure; the content is what counts.
  • Use bridge mode if you already pay for Claude Code / Copilot — no API key needed.
  • Take the review gate seriously: the backend contract is frozen after you confirm.
  • Run slc doctor in CI and after every merge so broken specs never land.
  • Use slc feature to add to a built project — it lands in a new phase and leaves the frozen files alone.
  • When the scope itself changes (non-goals, constraints, security), update the requirement first and regenerate — that's bigger than a feature.
  • Run slc audit once the specs are locked — doctor proves they're valid SLC, audit reads them for actual risk.

Don't

  • Don't re-run slc on a built project just to add one thing — that's a full regeneration. Use slc feature.
  • Don't put real secrets, emails, or keys in your requirement or any spec — SLC scans and will stop you.
  • Don't hand-edit task status inside task files — task_index.md is the only status ledger.
  • Don't let your agent invent endpoints — the frontend derives from the backend CONTRACT.
  • Don't switch models mid-project; interpretations drift.
  • Don't number decisions sequentially (dec1, dec2) — use content slugs like dec-auth-jwt-over-sessions.
  • Don't batch many tasks in one agent prompt — one task, one loop.
11

Troubleshooting

Installing / running

  • “'slc' is not recognized” — the bare command only exists after a global install: npm install -g @wewiselabs/slc, then open a new terminal.
  • Still unrecognized after the -g install — npm's global bin folder isn't on PATH. Run npm config get prefix, add that folder (Windows) or its bin/ (macOS/Linux) to PATH, open a new terminal.
  • PowerShell: “slc.ps1 cannot be loaded because running scripts is disabled” — run slc.cmd instead, or once: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned.
  • npx slc installs something weird — unscoped slc is a different, unrelated npm package. Always the full name: npx @wewiselabs/slc.
  • “slc is interactive — run it in a real terminal” — output is piped or the shell has no TTY (some CI shells, Git Bash via MinTTY). Use Windows Terminal / PowerShell / a normal terminal; doctor --json still works piped.
  • Banner/art looks garbled — legacy console without unicode. SLC_ASCII=1 slc gives plain-ASCII art; SLC_ANIM=0 disables the intro animation.
  • Install fails, or prompts hang oddly, on an old Node — SLC's terminal UI runs on Ink, which needs Node 22 or newer. Check node --version and upgrade (Node 18 is past its own end-of-life anyway).

During a run

  • “Could not parse …output.json” — SLC shows what the file actually starts with. Empty → the agent saved to the wrong path. Prose → tell the agent “save the raw JSON only, no commentary”. (UTF-16/BOM encodings are decoded automatically.)
  • “Expected a JSON array of files but found none” — use a more capable model, or in bridge mode make sure the agent wrote real JSON to the output file.
  • “The output looks truncated” — the model hit its output limit. Use a larger-output model, or bridge mode with your agent.
  • slc doctor: N error(s) — in API mode, accept the one-tap auto-fix (it re-prompts your model to repair only the broken files). In bridge mode, fix in your agent and run slc doctor again.
  • Key check failed — check the base URL + key; you can still save and continue. OpenRouter/Ollama need their own base URLs.
  • OpenAI 400 “Unsupported parameter” (gpt-5 / o-series) — handled automatically: SLC adapts max_tokens → max_completion_tokens and drops pinned temperature, then retries. Update SLC if you still see it.
  • Generation dies at exactly 2 minutes — old versions had a 120s network cap; generations now get 5 minutes. Update SLC.
  • “Rate limited (429)” / provider hiccup — wait a moment; API calls retry with backoff automatically.
  • The taste demo didn't open in a browser — open .slc/preview/demo.html yourself; the path is always printed. A blocked opener never stops the run.
  • It generated the wrong thing — answer No at the review gate and describe the change; the backend regenerates.
  • Want a totally clean slate — delete spec/ and .slc/, run slc again.
SLC writes the specs. Your agent writes the code. Keep the two jobs separate and the drift goes away.