Captures classified as inject, replan, or quick-task were marked
"resolved" in CAPTURES.md but their resolution actions were never
executed — tasks were never injected into plans, replan triggers
were never written, and quick-tasks were never dispatched.
This wires up the existing resolution executor functions that were
defined but never called:
- After triage-captures unit completes, executeTriageResolutions()
reads actionable captures and executes their resolutions:
- inject: calls executeInject() to add tasks to the slice plan
- replan: calls executeReplan() to write REPLAN-TRIGGER.md
- quick-task: queues for dispatch as a new unit type
- Quick-task dispatch block dispatches queued captures one at a time
using buildQuickTaskPrompt(), with proper session/timeout handling
- New markCaptureExecuted() and loadActionableCaptures() functions
track execution state, preventing double-execution on retries
- Quick-task unit type excluded from post-unit hooks (lightweight
one-offs don't need hook chains)
Closes#701
buildExecuteTaskPrompt() was missing the verificationBudget variable
that the execute-task.md template expects. The prompt-loader's strict
placeholder validator threw on every auto-mode task dispatch, blocking
all execution entirely.
Compute the budget from the executor's context window using the existing
computeBudgets() engine and pass it as ~NNK chars format string.
Fixes#707
Previously, running `/gsd cleanup` without a subcommand (branches or
snapshots) fell through to the unknown command handler, producing a
warning. Now bare `/gsd cleanup` runs both branch and snapshot cleanup.
The execute-task, plan-slice, and research-slice prompts all include a
passive instruction to 'use GSD Skill Preferences to decide which skills
to load.' In practice, auto-mode agents never act on this — across 30+
execution units in a real milestone, zero skill files were read.
The root cause is that the passive wording ('use it to decide') gets
overridden by the stronger 'don't re-research, just build what the plan
says' directive in execute-task. The agent treats skill loading as
optional and skips it 100% of the time.
This change rewrites the skill instruction in all three prompts from
passive guidance to an explicit action:
- execute-task: 'read its SKILL.md file now — before writing any code'
- plan-slice: 'read any skill files relevant to this slice's technology
stack before decomposing'
- research-slice: 'read any skill files relevant to this slice's
technology stack before exploring code'
The execute-task change also points agents to both the GSD Skill
Preferences block AND the <available_skills> catalog, since both are
present in the system prompt but the old instruction only referenced
the preferences block.
The plan-slice change adds guidance to note relevant skills in task
plans, so executors know which skills to load without rediscovering
them.
Scans activity logs, metrics, crash locks, and doctor diagnostics for
anomalies, generates a structured forensic report, saves it locally,
and hands it to the LLM for interactive root-cause analysis with
optional GitHub issue creation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
getAutoWorktreePath() only checked existsSync() on the worktree
directory, treating any directory under .gsd/worktrees/<MID>/ as a
valid auto-worktree. A stray (non-git) directory would be accepted,
causing auto-mode to derive state from an empty/invalid path and
conclude no milestones exist.
Add git worktree validation to both getAutoWorktreePath() and
enterAutoWorktree(): check that the directory contains a .git file
(not directory) with a 'gitdir:' pointer, which is the hallmark of
a real git worktree checkout. Return null / throw if validation fails.
This ensures stray directories are ignored and auto-mode falls through
to normal worktree creation or root-state derivation.
Closes#695
The mcporter extension only discovered servers that the mcporter CLI
itself knew about (via .vscode/mcp.json, Claude Desktop config, etc.).
Servers configured in the standard .mcp.json at the project root —
used by Claude Code, Cursor, and other AI coding tools — were invisible.
Changes:
1. mcporter extension (index.ts):
- Add readProjectMcpJson() that reads .mcp.json from cwd and returns
servers not already discovered by mcporter
- Merge .mcp.json servers into getServerList() results
- Add getMcpJsonServerUrl() to resolve HTTP URLs for .mcp.json servers
- Update getServerDetail() to pass HTTP URLs directly to mcporter
for servers only known via .mcp.json
- Update mcp_call to use HTTP URL as server reference for .mcp.json
servers
2. discover_configs scanner (scanners.ts):
- Add .mcp.json to the project-level MCP config scan path alongside
.claude/.mcp.json and .claude/mcp.json
Closes#692
Use execFileSync with argument arrays instead of execSync with string
interpolation to prevent shell injection via sinceDays parameter.
Validate sinceDays as a positive integer. Replace string-based path
resolution in file-watcher with path.relative() to prevent traversal
via symlinks or .. segments.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
gpt-5.x models (via Copilot/OpenAI/Azure) don't support 'minimal' as a
reasoning effort level — they only accept 'none', 'low', 'medium',
'high', and 'xhigh'. Setting /thinking minimal with gpt-5.4 causes a
400 error.
The openai-codex-responses provider already had this clamping, but the
openai-responses and azure-openai-responses providers passed the value
through unclamped.
Add clampReasoningForModel() to both providers that maps 'minimal' to
'low' for gpt-5.x models, matching the existing behavior in
openai-codex-responses.
Fixes the bug portion of #688
YAML frontmatter parsers can return Date objects for ISO date strings
instead of plain strings. This caused a TypeError when calling
.localeCompare() on a Date object in the changelog sort.
Wrap completedAt with String() at both assignment and sort to handle
both native and JS parser paths safely.
- Skip E2E --print test when no API key is configured (process hangs
waiting for onboarding wizard input in non-TTY CI environments)
- Skip file-watcher extensions subdirectory test on Windows (chokidar
subdirectory event delivery is unreliable in Windows CI runners)
Warp terminal (both macOS and Windows) does not emit recognized escape
sequences for Ctrl+Alt key combos. This adds Warp to the unsupported
terminals list so users see the /gsd status fallback hint.
Closes#643
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Increase file-watcher extension directory test delay to 1500ms with
500ms settle time (Windows filesystem events are slower)
- Make E2E --print test more permissive on exit code 1: check for
unhandled crash indicators instead of specific error messages
(error text varies by CI environment)
The roadmap parser regex used (\w+) to capture slice/task IDs, which
only matches [a-zA-Z0-9_]. Fractional IDs like S03.5 (created by
/gsd steer) contain a dot, causing the parser to skip the entire line.
The dispatcher then jumps from S03 to S04, finds S04 blocked by
the unparsed S03.5, and gives up with 'earlier slice is not complete'.
Update the ID capture group to ([\w.]+) in both:
- roadmap-slices.ts (primary roadmap parser)
- files.ts (plan task parser, for consistency)
This allows dots in slice/task IDs while preserving all existing
behavior for standard IDs like S01, S02, T01, etc.
Closes#681
Create src/mcp-server.ts with dynamic imports to bypass TypeScript's
static module resolution for @modelcontextprotocol/sdk subpath exports
that use wildcard patterns (./*) without matching type declarations.
The plan-slice.md template declares {{executorContextConstraints}} but
buildPlanSlicePrompt() never passed this variable, causing loadPrompt()
to throw: 'template declares {{executorContextConstraints}} but no value
was provided.'
Add formatExecutorConstraints() that uses the budget engine
(computeBudgets + resolveExecutorContextWindow) to generate the
executor context constraints block with task count ranges and inline
context budgets based on the configured executor model's context window.
Pass the formatted string to loadPrompt() as executorContextConstraints.
Closes#677
When auto-mode runs in a worktree, .gsd/ metadata (STATE.md, roadmap
checkboxes, slice plans, task summaries) only updates inside the
worktree directory. The project root on main retains stale state.
If auto-mode restarts, startAutoMode() calls deriveState(projectRoot)
which reads the stale .gsd/ from main, sees completed units as
incomplete, and re-dispatches them — causing an infinite loop on
already-finished work.
Add syncStateToProjectRoot() that copies STATE.md and the active
milestone directory from worktree → project root after each unit's
rebuildState + autoCommit. This ensures deriveState(projectRoot) on
restart reads current completion state.
The sync is fully non-fatal (try/catch wrapped). Failure falls back
to existing behavior. Uses cpSync with recursive:true for the
milestone directory tree.
When auto-mode restarts after being stopped, the initial deriveState()
reads from the project root which has stale .gsd/ metadata. Completed
units appear incomplete, causing re-dispatch of finished work.
The auto-worktree (if it exists from the previous run) has the current
state. After the initial deriveState(base), check if an auto-worktree
exists for the active milestone and re-derive from there.
This is safe because:
- Only triggers when worktree isolation is enabled
- Only when not already inside a worktree
- Only when an auto-worktree actually exists for the milestone
- The worktree setup at lines 976+ still runs normally after
Fixes#654
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use separate git commands instead of && chains (fails on Windows).
Configure git user.name/email before commit (not set in CI runners).
Mirrors the pattern from worktree-e2e.test.ts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Warp terminal does not correctly re-render inverse video (\x1b[7m)
cursor indicators on arrow key movement, making the cursor appear
invisible when navigating with arrow keys.
Auto-detect Warp via TERM_PROGRAM=WarpTerminal and enable the hardware
cursor by default (same as PI_HARDWARE_CURSOR=1). The hardware cursor
is positioned correctly via CURSOR_MARKER and provides reliable visual
feedback in Warp.
Terminals that render inverse video correctly (iTerm2, Terminal.app)
are unaffected — they continue using the fake cursor by default.
During full redraws, the TUI emitted \x1b[3J (clear scrollback) before
\x1b[2J\x1b[H (clear screen + home). In terminals like Ubuntu Terminal
that honor CSI 3J, this destroyed the scrollback buffer and caused the
scrollbar/view position to jump to the top during phase transitions.
Replace with just \x1b[2J\x1b[H which clears the visible screen and
homes the cursor without touching scrollback history. This preserves
view continuity while still performing a clean redraw.