@coralai/sps-cli
v0.90.1
Published
SPS CLI — AI-driven development pipeline orchestrator
Maintainers
Readme
SPS CLI — AI Agent Harness & Development Pipeline
中文文档:README-CN.md
OpenAI daemon agent: configuration, security, recovery, and operations
v0.59.0
SPS (Smart Pipeline System) drives a Claude Code worker through task cards — code, commit, push, QA, merge, all automated. Three modes:
| Mode | Command | When |
|---|---|---|
| Harness | sps agent | Zero-config — one-shot or multi-turn chat with Claude. No project, no PM. |
| Pipeline | sps tick <project> | Automated card-driven workflow with YAML-configurable stages. |
| Console | sps console | Web UI — voice-first home, kanban, logs, projects, chat, memory, plugins, system (config / processes / doctor / audit). |
The headline of v0.59 is a redesigned Console: a voice-first home (speak or type to a live agent, with an animated intro and a morphing voice-orb ↔ text-input), Doubao (Volcengine) large-model TTS for speech output (remote API, configured via SPS_DOUBAO_* in ~/.coral/env), a unified System page (config / processes / doctor / audit), and a consistent Pastel-Neubrutalism theme with light/dark switching. The Wiki Knowledge Base (opt-in per project, 5-layer retrieval auto-injected into worker prompts) remains — see doc-28 and ATTRIBUTION.md.
Table of contents
- Install & setup
- Deployment (server / from source)
- Harness mode (
sps agent) - Console mode (
sps console) - Pipeline mode (
sps tick) - Card lifecycle
- Memory + Wiki
- Skills
- Command reference
- Project config (conf)
- Project layout
- Architecture
- Troubleshooting
Install & setup
npm install -g @coralai/sps-cli # latest 0.65.x
sps setup # interactive wizard (must run once)sps setup:
- Creates the
~/.coral/directory tree (projects/,memory/, …). - Copies bundled skills →
~/.coral/skills/(the single skills source; the global~/.claude/skillsdir is no longer used — setup also prunes legacy link pollution there). - Asks for
GITLAB_URL/GITLAB_TOKEN/MATRIX_*(optional) → writes~/.coral/env, with a commented reference of optional keys (memory / voice TTS / daemon sandbox & concurrency gate) at the end — the same file the Console System page edits visually. - Pins the workspaces root
SPS_WORKSPACES_ROOT(default~/coral-workspaces).
Re-run safe with sps setup --force (keeps existing values as defaults).
Prerequisites: Node ≥ 18 (≥ 22 recommended — code-graph & memory index use node:sqlite); for the claude backend a logged-in claude CLI (or API key / subscription); for the openai backend codex login (subscription) or endpoints registered in ~/.coral/agent-providers.json.
Upgrading a stale install: clean reinstall recommended
Installs that are many versions behind (especially pre-0.6x / ACP-era) accumulate retired config keys, old-schema state files, and legacy symlinks that cause hard-to-debug oddities. Prefer a full clean reinstall over an in-place upgrade:
# 1) Stop everything (apps first, then the engine)
pm2 delete all 2>/dev/null # if you used pm2
sps console --kill 2>/dev/null
sps agent daemon stop 2>/dev/null
npm rm -g @coralai/sps-cli
# 2) Move the state root aside (backup + wipe in one step)
mv ~/.coral ~/.coral.bak-$(date +%m%d)
rm -rf ~/coral-workspaces # auto-created session workspaces (move out anything you keep first)
# 3) In ~/.claude only remove what sps put there — never the whole dir
rm -rf ~/.claude/skills # legacy full-link pollution from old versions
# 4) Reinstall
npm i -g @coralai/sps-cli && sps setupKeep-list before wiping (copy back from the backup as needed): claude CLI credentials (~/.claude/, don't delete the dir), codex subscription auth (~/.codex/auth.json; re-run codex login if expired), your memory store (memory/ → ~/.coral/memory/), custom skills (bundled ones reinstall automatically), and any hand-filled secrets from the old env — transcribe valid keys into the new template rather than copying the whole file (old templates contain retired keys).
Version discipline after any upgrade: CLI / console / daemon must run the same version — upgrading the npm package does not restart running processes. Run sps agent daemon restart (check for in-flight work first), restart the console, and hard-refresh the browser. Platforms embedding sps (e.g. coral-platform) upgrade their own dependency and restart their own process.
Deployment (server / from source)
For running sps-cli as long-lived services (Console web UI + IM gateway + pipeline) on a server, deploy from source. An AI or operator can follow the steps below end to end.
After installing the npm package — required extras
npm install -g @coralai/sps-cli gives you the sps CLI only. To actually run it, also provide:
- Node ≥ 22 (code-graph uses
node:sqlite; the rest of the CLI runs on ≥ 18). - Claude Code CLI in PATH — the worker backend drives it (
sps doctorcheckswhich claude):npm install -g @anthropic-ai/claude-code - Claude auth (one of): log in with
claude(Pro / Max →~/.claude/.credentials.json), or setANTHROPIC_API_KEY, or point workers at a third-party Anthropic endpoint. - Run
sps setuponce — scaffolds~/.coral/, installs the ACP transport@agentclientprotocol/claude-agent-acpglobally, writes~/.coral/env. - git — pipelines run worktrees/branches inside the target repo.
Model / subscription config lives outside the package (secrets, per machine): the official subscription (claude login) works out of the box; third-party endpoints go in ~/.coral/game-platform/providers.json; the worker's default model in ~/.coral/agents/smartarrange.json (optional).
Verify the box is ready:
sps project init demo
sps doctor demo --fix # checks Node, claude-in-PATH, dirs, config — reports what's missingPer-feature extras (only if used): IM gateway → ~/.coral/im/config.json; remote Console → SPS_CONSOLE_TOKEN.
Dependencies
- Node ≥ 22 and npm (the code-graph feature imports
node:sqlite—DatabaseSync— which requires Node ≥ 22; the rest of the CLI runs on ≥ 18). - git.
- Claude auth for workers: either
ANTHROPIC_API_KEYin the environment, or a logged-inclaudeCLI (Pro / Max subscription).sps setupinstalls the worker transport@agentclientprotocol/claude-agent-acpglobally. - No native/compiled dependencies —
@colbymchenry/codegraphand the rest are pure JS; a plainnpm installis enough.
1. Clone, install, build
git clone <repo-url> sps-cli && cd sps-cli
npm install # root dependencies
npm run build # THE build — see warning below
sps setup # or: node dist/main.js setup — one-time ~/.coral scaffold + skills + claude-agent-acpnpm run build runs both halves and is the only correct build command:
build:cli→rm -rf dist && tsc(compilessrc/→dist/).build:console→cd console && npm install && vite build, then copiesconsole/dist→dist/console-assets. The console has its ownnode_modules; this step installs it for you — you do not run a separate install inconsole/.
⚠️ Never run
npm run build:clialone. Itsrm -rf distwipesdist/console-assets, so the Console web UI breaks with a 500 / missingindex.html. Always use the fullnpm run build.
Expose the binary, or invoke dist/main.js directly:
npm link # puts `sps` on PATH → dist/main.js
# or, without linking:
node dist/main.js <command>2. Run the services
Console (web UI):
sps console # local: 127.0.0.1:4311
# server: bind all interfaces + REQUIRED auth token
SPS_CONSOLE_TOKEN=$(openssl rand -hex 24) sps console --host 0.0.0.0 --port 4321 --no-open
# then open http://<host>:4321/?token=<the-token>SPS_CONSOLE_TOKEN gates access. When binding to 0.0.0.0, always set it — without a token the UI is unauthenticated on the network. The startup banner prints the tokenized URL.
IM gateway (sps im) — inbound + outbound bots for Telegram / DingTalk (Stream) / Feishu-Lark / Slack / Discord / Matrix, with in-chat project switching:
sps im # foreground; reads ~/.coral/im/config.jsonConfigure channels the easy way in the Console → Plugins → Channel page (left = channel list, right = settings; it writes the file for you). Or edit ~/.coral/im/config.json directly:
{
"console": { "url": "http://localhost:4321", "token": "<SPS_CONSOLE_TOKEN>", "agentKind": "claude" },
"adapters": [
{ "kind": "telegram", "token": "<bot-token>", "allowedChatIds": [123456789] }
]
}console.url + token bridge IM plain-text messages to project-agent chat. After changing the config, restart sps im for it to take effect. (Feishu is the only adapter needing a public callback; the rest work without inbound ports.)
Pipeline:
sps tick my-app # foreground tick loop for one project3. Keep the services alive
sps console, sps im, and sps tick are foreground processes. Use nohup or a systemd unit for persistence:
# nohup (simple)
SPS_CONSOLE_TOKEN=<hex> nohup node dist/main.js console --host 0.0.0.0 --port 4321 --no-open > ~/console.log 2>&1 &
nohup node dist/main.js im > ~/im.log 2>&1 &For a systemd --user service, point ExecStart=node /abs/path/sps-cli/dist/main.js console --host 0.0.0.0 --port 4321 --no-open and set Environment=SPS_CONSOLE_TOKEN=....
4. Upgrading
git pull && npm install && npm run build
sps skill sync --force # refresh skill SOPs after an upgrade
# then restart the console + im processes so new code/routes loadNewly added Console API routes (e.g. /api/channels) only take effect after the console process restarts — rebuild alone is not enough.
Harness mode (sps agent)
Direct one-shot or multi-turn chat with Claude. No project, no PM, no Git.
# One-shot
sps agent "Explain this repo"
sps agent --output summary.md "Summarize the architecture"
# Multi-turn (daemon-backed, persistent sessions)
sps agent --chat # interactive REPL
sps agent --chat --name reviewer # named session, resume later
sps agent status # list active sessions
sps agent close --name reviewer
# Profile + context files
sps agent --profile reviewer "Review this module" --context src/auth.ts --context src/auth.test.ts
sps agent --system "You are a release engineer" "Plan the v0.52 cut"
# Verbose
sps agent --verbose "Why did this build fail?"--profile <name>: looks up ~/.coral/skills/dev-worker/references/<name>.md, injects as system prompt. (Different from sps skill add — that's for project-level skill linking.)
Built-in agent: claude only (Codex / Gemini support removed in v0.38). Workers communicate via ACP JSON-RPC over stdio with claude-agent-acp.
Agent skills auto-loaded by Claude Code: ~/.claude/skills/ is scanned by claude itself — including sps-pipeline, sps-memory, wiki-update, and the 24 dev/persona skills. Skill descriptions trigger lazy load; no SPS prompt injection needed for harness mode.
Daemon cwd caveat: sps console and sps agent --chat start a session daemon (~/.coral/sessions/daemon.sock) that captures process.cwd() at startup and uses it as the default working directory for all chat workers. To switch the chat's working directory, restart the daemon: sps agent daemon stop && sps agent daemon start from the desired cwd.
Console mode (sps console)
Local web UI bundled into the binary. Single-instance guard via ~/.coral/console.lock.
sps console # opens http://127.0.0.1:4311
sps console --port 5000
sps console --no-open # don't auto-open browser
sps console --kill # stop running console
sps console --dev # vite dev server (development)Pages:
| Path | Purpose |
|---|---|
| /projects | List all projects with status |
| /projects/new | Create project (Wiki toggle + worker-model picker, v0.58.3+) |
| /projects/<n> | Pipeline editor + conf editor + delete |
| /board | Kanban (per-column scrolling, v0.51.1+) |
| /workers | Aggregate worker dashboard across projects |
| /logs | Live SSE log viewer |
| /skills | User-level skill management |
| /plugins | Model endpoints + IM channels + memory backend config (v0.58+) |
| /system | Global settings + daemon status |
| /chat | Agent chat (multi-session; per-session model picker, v0.58.3+) |
Tech: Hono server on 127.0.0.1:4311, chokidar watchers pushing SSE to React 19 + Vite + Tailwind v4 + shadcn/ui frontend. Design system: Pastel Neubrutalism, locked in console/DESIGN.md.
Model selection (v0.58.3+): the worker/chat model is chosen per project / per session at creation (dropdown of configured endpoints); leaving it unset uses the global default (~/.coral/agents/smartarrange.json). Project picks are written to the repo's .claude/settings.local.json; session picks are injected per-session. sps-cli does no global model env injection — the global config is only the fallback default.
Pipeline mode (sps tick)
Fully automated card-driven workflow. One worker, one card at a time, serial. Each card walks one or more YAML-defined stages (e.g. develop → review → Done); failure halts pipeline until you remove the NEEDS-FIX label.
Create a project
sps project init my-app
# or use Console /projects/new — has a Wiki toggle (v0.51+)Asks for: project dir, merge branch, max workers, ACK timeout, optional GitLab remote, optional Matrix room.
Generates:
~/.coral/projects/my-app/
├── conf # mode 600 — your active config
├── conf.example # full reference (read-only docs)
├── pipelines/
│ ├── project.yaml # default 1-stage pipeline (develop → Done)
│ └── sample.yaml.example # heavily-commented YAML reference
└── pipeline_order.json # active pipeline pointerIn the target repo (PROJECT_DIR):
.claude/CLAUDE.md # worker rules (auto-installed)
.claude/skills/ # symlinked from ~/.coral/skills/
.claude/settings.local.json # Claude Code local config
wiki/ # if WIKI_ENABLED — see doc-28
ATTRIBUTION.md # if WIKI_ENABLEDRun
sps tick my-app # foreground tick loop
sps pipeline start my-app # alias
sps pipeline stop my-app # graceful stop (alias: sps stop my-app)
sps stop --all # stop all running ticks
sps status # all projectsPipeline YAML
~/.coral/projects/<n>/pipelines/project.yaml — single source of truth for stages.
mode: project
git: true # false = non-code project, no git ops
stages:
- name: develop
profile: fullstack
on_complete: "move_card Review"
on_fail: { action: "label NEEDS-FIX", halt: true }
- name: review
profile: reviewer
on_complete: "move_card Done"
on_fail: { action: "label REVIEW-FAILED", halt: true }Critical rules:
mode: projectfor state-machine pipelines;mode: stepsfor one-shot custom (usesps pipeline run <name>).- Each stage's
on_completemust point to the next stage's target state. - Last stage's
on_complete: "move_card Done". - Don't write
agent:field — it's silently ignored (v0.38+ Claude is the only worker). triggerandcard_stateare auto-derived per stage.
Field reference: see ~/.coral/projects/<n>/pipelines/sample.yaml.example (auto-generated, comment-rich) or doc-17.
Card lifecycle
Backlog → Todo → Inprogress → [QA / Review] → Done
↑↓ ↓ fail
Planning NEEDS-FIX (halt)
(manual park, v0.51.9+)v0.51.10: caller-aware default state.
sps card add(CLI / agent / API default) → Backlog(自动跑)- Console "新卡片" 表单(人在 UI 操作) → Planning(暂存,等用户拖到 Backlog)
CLI 用户想暂存:sps card add ... --draft。Console 用户想立即跑:勾"立即派发执行"。
卡片严格按 seq 排序;不再有 pipeline_order.json。
Default states (configurable via YAML pm.card_states).
sps card add <p> "Title" "Description"
sps card add <p> "T" "D" --skills python,backend --labels feature
sps card dashboard <p> # CLI table
# console: /board?project=<n>
sps card mark-started <p> <seq> # called by Claude Code UserPromptSubmit hook
sps card mark-complete <p> <seq> # called by Claude Code Stop hook
sps reset <p> # reset all non-Done cards
sps reset <p> --card 5,6,7
sps reset <p> --all # full reset incl. Done + worktrees + branchesCard label vocabulary
| Label | Meaning | Set by |
|---|---|---|
| AI-PIPELINE | Required to enter pipeline | User on creation |
| STARTED-<stage> | ACK signal — Claude received the prompt | UserPromptSubmit hook |
| COMPLETED-<stage> | Worker finished a stage | Stop hook |
| CLAIMED | StageEngine reserved a worker slot | Engine |
| NEEDS-FIX | Worker failed; pipeline halted | Engine |
| BLOCKED | External dep; pipeline skips | User |
| WAITING-CONFIRMATION | Worker waiting on user input | Engine |
| STALE-RUNTIME | Inprogress > timeout | MonitorEngine |
| ACK-TIMEOUT | Claude never ACK'd within WORKER_ACK_TIMEOUT_S | MonitorEngine |
| skill:<name> | Force-load specific skill | User |
| conflict:<domain> | Serial-with-others-in-same-domain | User |
The active stage writes a per-slot marker file at ~/.coral/projects/<p>/runtime/worker-<slot>-current.json (v0.50.21+). Stop hook reads it to detect which card the worker just finished.
Memory + Wiki
Two complementary persistence systems, both auto-injected into worker prompts.
| | Memory | Wiki (v0.51+) |
|---|---|---|
| Path | ~/.coral/memory/{user,agents,projects/<p>}/ | <repo>/wiki/ (per-project, in repo) |
| Format | Flat markdown + YAML frontmatter | 5 page types with zod-validated frontmatter |
| Cross-link | None (flat index) | [[type/Title]] wikilinks |
| Auto-inject | knowledge section of prompt | wikiContext section (5-layer retrieval) |
| Opt-in | Always on (toggle via ENABLE_MEMORY=false) | Per-project (WIKI_ENABLED=true) |
| Best for | Personal prefs, ad-hoc decisions, gotchas | Structured project knowledge: modules, concepts, decisions, lessons |
Memory CLI
⚠️ Under reconstruction (v2). The old
sps memory list/add/search/context/ingestcommands andread_memory/append_memory/recallMCP tools have been removed. The new local memory system (sps memory recall/save/read/...) is being rebuilt — seedocs/design/memory-v2.md.
Wiki CLI (when WIKI_ENABLED=true)
sps wiki init <p> # scaffold wiki/ (auto on project init if toggled on)
sps wiki update <p> # show source diff
sps wiki update <p> --finalize # flush manifest after worker writes pages
sps wiki check <p> # lint: orphan / dead-link / fm-gap / stale
sps wiki list <p> --type lesson --tag pipeline
sps wiki get <p> lessons/Stop-Hook-Race
sps wiki status <p> # source ↔ manifest ↔ pages diff
sps wiki add <p> ~/notes.md --category transcripts
sps wiki read <p> "<query>" # preview the 5-layer retrievalThe 5-layer retrieval: hot.md / index summary / pinned / skill-tag / BM25F keyword. Type priority: lesson = 3, decision = 3, concept = 2, module = 1, source = 1. Token budget capped at ~2000.
Worker SOP: skills/wiki-update/SKILL.md (300 lines, single source of truth).
Skills
User-level skills live in ~/.coral/skills/ (28 bundled, copied from npm package on sps setup). Symlinked into ~/.claude/skills/ so Claude Code auto-loads them.
sps skill list # what's available + project status
sps skill add <name> --project <p> # symlink into <repo>/.claude/skills/
sps skill remove <name> --project <p>
sps skill freeze <name> --project <p> # symlink → real copy (allow project edits)
sps skill unfreeze <name> --project <p> # back to symlink
sps skill sync # ① bundled (npm pkg) → ~/.coral/skills/
# ② ~/.coral/skills/ → ~/.claude/skills/
sps skill sync --force # ⭐ overwrite existing user skills (after sps-cli upgrade)Bundled skills (v0.51.3):
- Dev (23):
frontend,frontend-developer,backend,backend-architect,typescript,golang,rust,python,java,kotlin,swift,mobile,database,database-optimizer,qa-tester,security-engineer,architecture-decision-records,coding-standards,debugging-workflow,devops,devops-automator,git-workflow,code-reviewer - Worker profiles (3):
dev-worker,tax-worker,reviewer(referenced via--profile) - SPS-specific (5):
sps-pipeline,sps-memory,wiki-update
Agent persona & config (Output Style)
Each agent (chat / worker) gets its persona/role from an Output Style, injected per-agent — separate from the shared base, model, and execution rules. See the design doc.
- Shared base →
CLAUDE.md(root +.claude/: project identity/invariants + codegraph block). Thin, read by every agent. - Persona/role → the role segment in
<repo>/.claude/agent-config.json(worker/chat), oneoutputStyleeach, injected per-agent via_meta(both segments coexist → no cross-talk under concurrency):
The Output Style file lives at{ "worker": { "outputStyle": "gf-worker" }, "chat": { "outputStyle": "sps-orchestrator" } }<repo>/.claude/output-styles/<name>.md(frontmatterkeep-coding-instructions).projectInitseeds the defaultsps-orchestrator(generic orchestrator) for new projects. - Model + endpoint + env →
<repo>/.claude/settings.local.json(read natively by Claude, cross-platform). One model per project, not via process env (ANTHROPIC_BASE_URLvia env doesn't reach claude on macOS). - Worker execution rules (scope / card lifecycle / git) → fixed by sps, delivered only to the worker via
appendSystemPrompt. - Memory → a
<memory>pointer injected at dispatch; the worker pulls withmemory_recallon demand.
Platforms (e.g. gameforge) built on sps-cli follow these rules — they only fill agent-config.json with their own outputStyle after projects.create (and pass skipDefaultAgentConfig: true to skip the generic default).
Command reference
# Setup & projects
sps setup [--force]
sps project init <name>
sps project doctor <name> [--fix] [--json] [--reset-state] [--skip-remote]
sps doctor <name> --fix # alias
# Pipeline
sps tick <project> [--json]
sps pipeline start|stop|status|reset|workers|board|card|logs|list|run|use [project] [args]
sps pipeline run <name> "<prompt>" # for mode: steps pipelines
sps pipeline tick <project> # one-off StageEngine pass
sps scheduler tick <project> # dormant since v0.51.9 (kept for tick orchestrator)
sps qa tick <project> # QA → Done finalization
sps monitor tick <project> # health probe (ACK timeout, stale)
sps pm scan <project> # rebuild card index from disk
# Cards
sps card add <p> "title" ["description"] [--skills a,b] [--labels x,y]
sps card dashboard <p>
sps card mark-started <p> [seq] [--stage <name>]
sps card mark-complete <p> <seq> [--stage <name>]
# Worker
sps worker ps <project>
sps worker dashboard <project>
sps worker kill <project> <seq>
sps worker launch <project> <seq>
# Status / logs
sps status [--json]
sps stop <project> [--all]
sps reset <project> [--all] [--card N,N,N]
sps logs [project] [--err] [--lines N] [--no-follow]
# Memory — under reconstruction (v2); old `sps memory` subcommands removed. See docs/design/memory-v2.md
# Wiki (v0.51+)
sps wiki init <p>
sps wiki update <p> [--finalize] [--json]
sps wiki read <p> "<query>" [--skills a,b] [--pinned id1,id2] [--budget N]
sps wiki check <p> [--json] [--fix]
sps wiki add <p> <file> [--category <name>] [--no-ingest]
sps wiki list <p> [--type T] [--tag T] [--json]
sps wiki get <p> <pageId> [--json]
sps wiki status <p> [--json]
# Skill
sps skill list [--project <p>]
sps skill add <name> [--project <p>]
sps skill remove <name> [--project <p>]
sps skill freeze <name> [--project <p>]
sps skill unfreeze <name> [--project <p>]
sps skill sync [--force]
# Console
sps console [--port N] [--host H] [--no-open] [--dev] [--kill]
# Agent
sps agent "<prompt>" [--profile <p>] [--system "..."] [--context file] [--output file] [--verbose]
sps agent --chat [--name <session>]
sps agent status|close [args]
sps agent daemon start|stop|status
# Hooks (called by Claude Code, not by users)
sps hook stop
sps hook user-prompt-submit
# ACP control (for advanced debugging)
sps acp <ensure|run|prompt|status|stop|pending|respond> <project> [args]Add --help after any command to see its specific usage. Add --json for structured output where supported.
Project config (conf)
Live at ~/.coral/projects/<name>/conf (shell export VAR="value" syntax, mode 600). Full field reference (with comments) auto-generated at ~/.coral/projects/<name>/conf.example.
| Field | Default | Notes |
|---|---|---|
| PROJECT_NAME | (required) | Internal id |
| PROJECT_DIR | (required) | Absolute path to repo |
| GITLAB_PROJECT | — | user/repo (optional, for GitLab API) |
| GITLAB_PROJECT_ID | — | Numeric ID (GitLab only; auto-resolved from path on first MR) |
| GITLAB_MERGE_BRANCH | main | Worker pushes here |
| PM_TOOL | markdown | Only markdown supported as of v0.42. Cards live in ~/.coral/projects/<n>/cards/<state>/<seq>.md |
| PIPELINE_LABEL | AI-PIPELINE | Required label on cards to enter pipeline |
| MR_MODE | none | none (push direct) / create (open MR; needs GITLAB_PROJECT_ID) |
| WORKER_TRANSPORT | acp-sdk | Fixed; do not change |
| MAX_CONCURRENT_WORKERS | 1 | Slot count; cards still serial within a project |
| MAX_ACTIONS_PER_TICK | 3 | New tasks claimable per tick |
| INPROGRESS_TIMEOUT_HOURS | 2 | After this, MonitorEngine flags STALE-RUNTIME |
| WORKER_ACK_TIMEOUT_S | 300 | Wait for STARTED- label after dispatch (5min, raised in v0.50.24) |
| WORKER_ACK_MAX_RETRIES | 1 | ACK timeout retry count |
| MONITOR_AUTO_QA | true | Auto-advance to QA on stale runtime |
| CONFLICT_DEFAULT | serial | Fallback for cards without conflict: label |
| MATRIX_ROOM_ID | — | Project-level Matrix override |
| WORKTREE_DIR | ~/.coral/worktrees/<p> | Worker scratch space |
| DEFAULT_WORKER_SKILLS | — | Comma-separated; fallback when no profile: and no card.skills |
| ENABLE_MEMORY | true | false skips memory write instructions in prompt |
| WIKI_ENABLED | unset (off) | v0.51+: true enables wiki context injection + reminder |
| COMPLETION_SIGNAL | done | Word the Stop hook listens for |
Global credentials at ~/.coral/env: GITLAB_URL, GITLAB_TOKEN, GITLAB_SSH_HOST, GITLAB_SSH_PORT, MATRIX_HOMESERVER, MATRIX_ACCESS_TOKEN, MATRIX_ROOM_ID. Set via sps setup or vim.
Speech output (TTS) — Doubao (Volcengine)
The Console voice home speaks via Doubao (Volcengine) large-model TTS (remote API, zero client dependencies: the browser only hits the Console domain; the backend proxies Volcengine and the key never leaves the server). Configure it in ~/.coral/env on the machine running sps console:
| Variable | Required | Notes |
| --- | --- | --- |
| SPS_DOUBAO_API_KEY | ✅ | Volcengine Speech API key (new console single-header X-Api-Key auth, ark-… form) |
| SPS_DOUBAO_SPEAKER | ✅ | A 2.0 voice id (suffix _uranus_bigtts), e.g. zh_female_vv_uranus_bigtts (female), zh_male_yangguangqingnian_uranus_bigtts (male) |
| SPS_DOUBAO_RESOURCE_ID | no | defaults to seed-tts-2.0 |
| SPS_DOUBAO_ENDPOINT | no | defaults to https://openspeech.bytedance.com/api/v3/plan/tts/unidirectional |
# ~/.coral/env (mode 600, keep out of git)
SPS_DOUBAO_API_KEY=ark-xxxxxxxx-...
SPS_DOUBAO_SPEAKER=zh_female_vv_uranus_bigttsIf unset, speech falls back to the browser Web Speech API. Restart with sps console --stop then start again to apply. Deploying on another machine only needs this block repeated there — no local model to install.
Project layout
~/.coral/ # User-global state
├── env # Global credentials (mode 600)
├── skills/ # User-level skills (synced from npm)
├── memory/{user,agents,projects}/ # 3-layer memory store
├── projects/<name>/ # Per-project state
│ ├── conf # Project config (mode 600)
│ ├── conf.example # Field reference (auto-generated)
│ ├── pipelines/{project,*}.yaml # Pipeline definitions
│ ├── pipeline_order.json # Active pipeline pointer
│ ├── runtime/state.json # Worker slot + active card state
│ ├── runtime/worker-<slot>-current.json # Per-slot card marker (v0.50.21+)
│ ├── runtime/tick.lock # Tick lock
│ ├── runtime/acp-state.json # ACP session state
│ ├── cards/<state>/<seq>.md # Card files (markdown PM backend)
│ ├── cards/seq.txt # Sequence counter
│ ├── logs/ # Per-tick logs
│ └── pm_meta/ # Card index
├── sessions/ # Agent daemon (chat sessions)
│ ├── daemon.sock daemon.pid
│ └── chat-sessions/<id>.json # Persisted chat sessions
├── console.lock # Single-instance guard for console
└── worktrees/<project>/<seq>/ # Worker worktree per active cardIn the target repo (PROJECT_DIR):
.claude/
├── CLAUDE.md # Worker rules (project-specific + SPS-injected)
├── settings.local.json # Claude Code local config
├── skills/ # Symlinked from ~/.coral/skills/
└── hooks/{start,stop}.sh # Lifecycle hooks (call into sps)
wiki/ # If WIKI_ENABLED — see docs/design/28-wiki-system.md
ATTRIBUTION.md # If WIKI_ENABLEDArchitecture
4-layer service architecture (v0.50+):
Delivery (commands/, console/routes/) Thin parameter parsing + I/O orchestration
↓
Service (services/) ProjectService / ChatService / PipelineService /
SkillService / WikiService — Result<T> + DomainEvent
↓
Domain (engines/) SchedulerEngine / StageEngine / MonitorEngine /
CloseoutEngine / EventHandler — pipeline logic
↓
Infrastructure WorkerManager (single worker), ACPWorkerRuntime,
(manager/, providers/, daemon/) sessionDaemon, TaskBackend, RepoBackendEngines:
- SchedulerEngine — dormant since v0.51.9 (cards go directly to Backlog on add; Planning is a manual park). Class kept as a no-op for the tick orchestrator's stable interface.
- StageEngine — drives card through stages; builds prompt (skill + projectRules + memory + wikiContext + task description + wikiUpdateReminder); kicks worker via ACP.
- MonitorEngine — ACK timeout detection, stale runtime, auto-QA promotion.
- CloseoutEngine + EventHandler — finalize completed cards.
Single-worker is intentional: v0.37.2 deleted multi-worker concurrency code. Don't propose "add a parallel mode" — the architecture relies on serial execution for state coherence. For higher throughput, run multiple projects in parallel.
For deep dives:
- doc-27: Service Layer Architecture — current architecture
- doc-26: Console Architecture — console internals
- doc-28: Wiki System — wiki design
- doc-13: Development Guardrails — hard rules for contributors
- doc-17: Pipeline Configuration — YAML field semantics
- docs/design/ — full design tree (most v0.15-v0.32 docs are marked HISTORICAL)
Troubleshooting
sps doctor <project> --fix # ★ first thing to try
sps logs <project> --err # stderr / errors only
sps reset <project> --card <seq> # nuke a stuck card
sps reset <project> --all # full project reset
# Worker / daemon issues
sps worker ps <project>
sps agent daemon status # is the chat daemon up?
sps agent daemon stop && sps agent daemon start # restart (clears stale cwd)
# Wiki issues
sps wiki check <project>
sps wiki status <project>Common issues:
| Symptom | Cause / fix |
|---|---|
| Pipeline halted with NEEDS-FIX | Open the failed card, fix the issue, remove the label. Console makes this 2 clicks. |
| Worker not starting | sps worker ps, then sps logs --err. Often Claude API key missing or claude-agent-acp adapter not installed (sps setup reinstalls). |
| Cards stuck in Planning | Need AI-PIPELINE label. sps card add applies it automatically; if added externally, add manually. |
| ACK timeout on every card | Claude cold-start is slow with many skill / memory files. Raise WORKER_ACK_TIMEOUT_S (default 300s as of v0.50.24). |
| Console shows stale data | SSE may have dropped; reload page; if persistent, sps console --kill && sps console. |
| Wiki context not injecting | Verify WIKI_ENABLED=true in conf and wiki/WIKI.md exists. StageEngine logs a warning if conf says yes but scaffold is missing. |
| New skill SOP not pulling after upgrade | sps skill sync --force (default sync skips existing skills). |
| Daemon chat using wrong cwd | Daemon captures cwd at startup. sps agent daemon stop && cd <repo> && sps agent daemon start. |
License & attribution
MIT, see LICENSE.
The Wiki system (v0.51+) borrows ~70% from claude-obsidian (MIT) — three-layer architecture, manifest delta tracking, hot cache, ingest workflow, contradiction callouts, wikilinks. SPS-specific 30%: 5 page types, sources={card,commit,path}, 5-layer reader, sps wiki check exit gate. Mental model from Karpathy's "LLM Wiki" gist.
Full attribution: ATTRIBUTION.md.
