/gsd headless
What It Does
Section titled “What It Does”gsd headless runs any /gsd command without the interactive TUI. It spawns a child process in RPC mode, automatically responds to interactive prompts, detects when the command completes, and exits with a meaningful exit code.
This is GSD’s entry point for automation — CI pipelines that run auto-mode on push, cron jobs that execute scheduled maintenance, and scripts that need machine-readable output from GSD commands. Progress is written to stderr; use --json to stream structured JSONL events to stdout instead.
gsd headless [flags] [subcommand] [args...]Any /gsd subcommand works as a positional argument. If no subcommand is given, it defaults to auto.
# Run auto mode (default)gsd headless
# Run a single unitgsd headless next
# Check project statusgsd headless status
# Run diagnosticsgsd headless doctor
# Force a specific dispatch phasegsd headless dispatch plan
# Instant JSON snapshot — no LLM, ~50msgsd headless query
# Create a new milestone from a context file and start auto modegsd headless new-milestone --context brief.md --auto
# Create a milestone from inline textgsd headless new-milestone --context-text "Build a REST API with auth"
# Pipe context from stdinecho "Build a CLI tool" | gsd headless new-milestone --context -| Flag | Syntax | Default | Description |
|---|---|---|---|
--timeout | --timeout N | 300000 (5 min) | Overall timeout in milliseconds. The process exits with code 1 if this limit is reached. new-milestone auto-extends this to 600000 (10 min) when the timeout is still the default. |
--max-restarts | --max-restarts N | 3 | Auto-restart on crash with exponential backoff. Set 0 to disable. |
--json | --json | Off | Stream all events as JSONL to stdout. Each line is a self-contained JSON object. |
--events | --events type1,type2 | — | Filter JSONL output to specific event types. Implies --json. |
--verbose | --verbose | Off | Show detailed execution output in stderr progress lines. |
--model | --model ID | Session default | Override the model for the headless session. |
--context | --context <file> | — | Context file for new-milestone. Use - to read from stdin. |
--context-text | --context-text <text> | — | Inline context text for new-milestone. |
--auto | --auto | Off | Chain into auto-mode after milestone creation. |
--answers | --answers <file> | — | JSON file with pre-supplied answers for interactive prompts. See Answer Injection below. |
--supervised | --supervised | Off | Enable supervised mode — forward interactive prompts to the orchestrator via stdin/stdout. Implies --json. Cannot be combined with --context -. |
--response-timeout | --response-timeout N | 30000 | In supervised mode, milliseconds to wait for an orchestrator response before falling back to auto-response. |
Subcommands
Section titled “Subcommands”gsd headless query
Section titled “gsd headless query”Returns a single JSON object with the full project snapshot — no LLM session, no RPC child, instant response (~50ms). This is the recommended way for orchestrators and scripts to inspect GSD state.
gsd headless query | jq '.state.phase'# "executing"
gsd headless query | jq '.next'# {"action":"dispatch","unitType":"execute-task","unitId":"M001/S01/T03"}
gsd headless query | jq '.cost.total'# 4.25Output schema:
{ "state": { "phase": "executing", "activeMilestone": { "id": "M001", "title": "..." }, "activeSlice": { "id": "S01", "title": "..." }, "activeTask": { "id": "T01", "title": "..." }, "nextAction": "Execute T01: Create auth module in slice S01.", "registry": [{ "id": "M001", "title": "...", "status": "active" }], "progress": { "milestones": { "done": 0, "total": 2 }, "slices": { "done": 1, "total": 3 }, "tasks": { "done": 2, "total": 5 } }, "blockers": [], "requirements": { "active": 4, "validated": 2, "deferred": 0, "outOfScope": 0, "blocked": 0, "total": 6 } }, "next": { "action": "dispatch", "unitType": "execute-task", "unitId": "M001/S01/T01", "reason": "Next incomplete task in active slice" }, "cost": { "workers": [{ "milestoneId": "M001", "pid": 12345, "state": "running", "cost": 1.50, "lastHeartbeat": 1716000000000 }], "total": 1.50 }}Fields under state.progress (slices, tasks) are omitted when not applicable (e.g., before slice planning starts). state.requirements is omitted when no REQUIREMENTS.md exists.
gsd headless new-milestone
Section titled “gsd headless new-milestone”Create a new milestone from a context file without the TUI. Combine with --auto to immediately start execution after creation.
# From a filegsd headless new-milestone --context brief.md
# Inline textgsd headless new-milestone --context-text "Build a REST API with auth"
# From stdinecho "Build a CLI tool" | gsd headless new-milestone --context -
# Create and immediately start auto-modegsd headless new-milestone --context brief.md --autoThe timeout is automatically extended to 10 minutes for new-milestone commands when using the default 5-minute timeout, because milestone creation involves codebase investigation and writing multiple planning artifacts. If you pass a custom --timeout, it is used as-is.
How It Works
Section titled “How It Works”RPC Communication
Section titled “RPC Communication”The headless runner spawns GSD with --mode rpc, creating a structured communication channel between the parent (headless controller) and child (GSD session) processes. Events flow from the child to the parent as typed messages — prompts, progress updates, completion signals, and errors.
Auto-Response
Section titled “Auto-Response”When the child process sends an interactive prompt, the headless controller applies built-in defaults to keep execution flowing without human intervention:
| Prompt type | Default behaviour |
|---|---|
select | Picks the first available option |
confirm | Auto-confirms |
input | Submits an empty string |
editor | Returns the prefill text, or empty if none |
Quick commands (status, doctor, export, steer, etc.) resolve on the first agent_end event rather than waiting for a terminal notification. Use --answers to override these defaults with specific answers for predictable decision trees.
Completion Detection
Section titled “Completion Detection”The controller monitors for completion signals — the child process reporting that the dispatched command finished, errored, or hit a blocker. Auto-mode completion is detected by terminal notification messages ("Auto-mode stopped...", "Step-mode stopped..."). An idle timeout fires as a fallback: 15 seconds for most commands, 2 minutes for new-milestone.
Auto-Restart
Section titled “Auto-Restart”When --max-restarts is greater than 0 (default: 3), an error exit triggers an exponential backoff restart — 5 seconds after the first crash, up to a maximum of 30 seconds. SIGINT/SIGTERM interrupts skip the restart and exit immediately.
Exit Summary
Section titled “Exit Summary”On every exit, headless writes a summary to stderr:
[headless] Status: complete[headless] Duration: 42.3s[headless] Events: 187 total, 43 tool callsWhen restarts occurred, a restart count is appended. When --answers was used, an injection stats line is appended:
[headless] Restarts: 2[headless] Answers: 5 answered, 1 defaulted, 2 secretsOn failure, the last 5 events are also printed to aid diagnostics.
Answer Injection
Section titled “Answer Injection”--answers <file> loads a JSON file with pre-supplied answers for interactive selection prompts. This lets you drive headless sessions through predictable decision trees without supervised mode.
Answer file format:
{ "questions": { "question-id": "selected option label", "multi-select-id": ["option A", "option B"] }, "secrets": { "ENV_VAR_NAME": "secret-value" }, "defaults": { "strategy": "first_option" }}questions— Map of question ID to the label of the answer to select. Multi-select questions accept an array.secrets— Environment variables injected into the RPC child process. The child’scheckExistingEnvKeys()detects them inprocess.envand marks them as already configured — no interactive prompt is shown. Secrets are never logged or included in event streams.defaults.strategy— What to do when a question isn’t in the file:"first_option"(default) selects the first available option;"cancel"cancels the prompt.
The injector correlates tool_execution_start events for ask_user_questions with subsequent extension_ui_request events. It handles out-of-order events via a deferred processing queue with a 500ms timeout.
Supervised Mode
Section titled “Supervised Mode”--supervised turns headless into an interactive orchestrator bridge. Rather than auto-responding to every prompt, the headless process forwards extension_ui_request events to the outer process via stdout JSONL, and waits for responses on stdin. If no response arrives within --response-timeout milliseconds, it falls back to auto-response.
Both --answers and --supervised can be active simultaneously: the answer injector tries first, supervised mode handles unmatched prompts next, and the auto-responder catches any remaining ones.
Multi-Session Orchestration
Section titled “Multi-Session Orchestration”Headless supports concurrent parallel workers for multi-milestone execution. Workers coordinate through file-based IPC in .gsd/parallel/ — no sockets or ports.
Spawning workers:
GSD_MILESTONE_LOCK=M001 GSD_PARALLEL_WORKER=1 \ gsd headless --json auto 2>logs/M001.log &Each worker isolates its state via GSD_MILESTONE_LOCK=<MID> — state derivation only sees its assigned milestone. Workers write heartbeat files atomically to .gsd/parallel/<milestoneId>.status.json:
{ "milestoneId": "M001", "pid": 12345, "state": "running", "currentUnit": { "type": "task", "id": "T03", "startedAt": 1710000000000 }, "completedUnits": 7, "cost": 1.23, "lastHeartbeat": 1710000015000, "startedAt": 1710000000000, "worktreePath": ".gsd/worktrees/M001"}Worker states: running, paused, stopped, error
Monitoring workers:
for f in .gsd/parallel/*.status.json; do jq -r '[.milestoneId, .state, (.currentUnit.id // "idle"), "\(.cost)$"] | join("\t")' "$f"doneSending signals: write a signal file — the worker consumes and deletes it on the next dispatch cycle:
echo '{"signal":"pause","sentAt":'$(date +%s000)',"from":"coordinator"}' \ > .gsd/parallel/M001.signal.jsonSignal commands: pause, resume, stop, rebase
Liveness: a session is stale when its PID is dead (kill -0 $pid fails) or its lastHeartbeat is older than 30 seconds. Use gsd headless query for an instant aggregate cost snapshot across all workers.
See /gsd parallel for the built-in parallel orchestration commands.
Exit Codes
Section titled “Exit Codes”| Code | Meaning | When |
|---|---|---|
0 | Success | Command completed successfully |
1 | Error or timeout | Command failed, crashed (and restarts exhausted), or --timeout was exceeded |
2 | Blocked | Execution hit a blocker requiring human input |
What Files It Touches
Section titled “What Files It Touches”Creates
Section titled “Creates”| File | Purpose |
|---|---|
.gsd/runtime/headless-context.md | Temp file written for new-milestone — passes the context spec to the RPC child. Deleted by the child after reading. |
.gsd/ | Bootstrapped if missing during new-milestone |
.gsd/PROJECT.md | Written by new-milestone — full project vision |
.gsd/REQUIREMENTS.md | Written by new-milestone — capability contract |
.gsd/DECISIONS.md | Seeded by new-milestone — initial decisions log |
.gsd/milestones/<MID>/<MID>-CONTEXT.md | Written by new-milestone — milestone scope and assumptions |
.gsd/milestones/<MID>/<MID>-ROADMAP.md | Written by new-milestone — slice breakdown with checkboxes |
.gsd/milestones/<MID>/slices/ | Created by new-milestone for subsequent slice planning |
.gsd/parallel/<MID>.status.json | Written by parallel workers — heartbeat and state |
| File | Purpose |
|---|---|
.gsd/ | Checked for existence before spawning RPC child (except new-milestone) |
--answers <file> | Answer injection JSON file, if provided |
--context <file> | Context spec for new-milestone, or stdin if - |
Writes
Section titled “Writes”| File | Purpose |
|---|---|
.gsd/parallel/<MID>.signal.json | Written by external coordinators to send signals to workers |
Examples
Section titled “Examples”Run auto-mode in CI:
# In a GitHub Actions workflowgsd headless --timeout 600000 autoecho "Exit code: $?"Auto-restart on crash (up to 5 times):
gsd headless --max-restarts 5 autoGet machine-readable status:
gsd headless --json status 2>/dev/null | jq '.type'Stream only specific event types:
gsd headless --events tool_execution_start,agent_end autoAvailable event types: agent_start, agent_end, tool_execution_start, tool_execution_end, tool_execution_update, extension_ui_request, message_start, message_end, message_update, turn_start, turn_end
Run a specific dispatch phase:
gsd headless dispatch executeUse a different model:
gsd headless --model claude-sonnet-4-20250514 autoPre-supply answers for a non-interactive setup wizard:
gsd headless --answers answers.json new-milestone --context brief.md --autoPoll-and-react loop (no LLM cost between checks):
PHASE=$(gsd headless query | jq -r '.state.phase')case "$PHASE" in complete) echo "Done" ;; blocked) echo "Needs intervention" ;; *) gsd headless next ;;esacPrompts Used
Section titled “Prompts Used”discuss-headless— Non-interactive milestone creation prompt
Related Commands
Section titled “Related Commands”/gsd auto— Interactive auto-mode (TUI version)/gsd new-milestone— Interactive milestone creation/gsd parallel— Built-in parallel milestone orchestration- CLI Flags — All command-line flags for GSD
/gsd doctor— Health checks (can be run headless)