When a provider returns a fetch error, the agent_end hook now detects
stopReason === "error" and pauses auto-mode. This prevents the state
machine from silently re-dispatching the same phase until stuck
detection fires.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The "None of the above" option description said "add details in notes
below" without telling users to press TAB to reveal the notes input.
Updated the description to "Press TAB to add optional notes." and made
the footer "tab to add notes" hint always visible in single-select mode
(previously hidden until a selection was committed).
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
When working on a feature branch (e.g. f-123-new-thing), GSD creates
slice branches correctly from the current branch but merges them back
to main instead of the feature branch. This is because getMainBranch()
always resolved to the repo default branch with no concept of where
the user started.
Fix: record the current branch as the "integration branch" in a
per-milestone metadata file (.gsd/milestones/<MID>/<MID>-META.json)
when auto-mode starts. getMainBranch() checks this metadata before
falling back to repo defaults, so switchToMain() and mergeSliceToMain()
target the correct branch.
Key details:
- Integration branch is captured once per milestone (idempotent)
- Committed immediately so it survives branch switches (.gsd/ files
are discarded during checkout)
- main_branch preference still takes highest priority
- Falls back to existing detection if metadata missing (backward compat)
- Per-milestone: different milestones can target different branches
- Validates branch still exists before using it
Tests: 41 new assertions across git-service.test.ts and worktree.test.ts
covering the full lifecycle, multi-slice workflows, resume scenarios,
backward compatibility, and edge cases.
In RPC mode, `ctx.ui.custom()` returns `undefined as never`, causing
`showInterviewRound` to return undefined and `Object.keys(result.answers)`
to throw TypeError.
When `showInterviewRound` returns undefined (RPC mode), fall back to
sequential `ctx.ui.select()` calls for each question, forwarding the
abort signal (#171) and supporting `allowMultiple` (#165).
- Add `allowMultiple` to `ExtensionUIDialogOptions`
- Widen `select()` return type to `string | string[] | undefined`
- Add `allowMultiple` to RPC select request and `values` array to response
- Update RPC `select()` to forward `allowMultiple` and parse array responses
- Guard existing `ctx.ui.select()` callers against the widened return type
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Auto mode's model resolution used `allModels.find(m => m.id === modelId)`
which returns the first match regardless of provider. With 30+ duplicate
model IDs across providers, user preferences silently resolved to the
wrong provider.
Three fixes:
- Use `getAvailable()` instead of `getAll()` so only authenticated
models are considered
- Support `provider/model` format (e.g. "google/gemini-2.5-pro") for
explicit provider targeting
- For bare IDs, prefer the current session's provider, then first
available match, with an ambiguity warning
- Store and restore original model provider instead of hardcoding
"anthropic" when restoring the user's model after auto-mode
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
verifyExpectedArtifact only checked for the task summary file, but
deriveState determines the next task by finding the first unchecked
checkbox in the slice plan. When the agent writes the summary but
doesn't mark the checkbox, the dispatch loop re-sends the same unit
and gets stuck after max retries.
Part 1: verifyExpectedArtifact now also checks that the task checkbox
is marked [x] in the slice plan for execute-task units.
Part 2: At retry time, if the summary exists but the checkbox is
unmarked, the dispatch logic self-repairs by marking the checkbox
programmatically (via skipExecuteTask) and re-derives state instead
of re-dispatching the same unit.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Bug 1: resolveMilestoneFile/resolveSliceFile already check file existence
via readdirSync, so the additional loadFile content check was redundant.
Empty research files (exists on disk but no content) caused a loop where
verifyExpectedArtifact marked research complete while dispatch re-triggered
it because loadFile returned falsy for empty files.
Bug 2: stopAuto now calls rebuildState to synchronize disk state, matching
pauseAuto's pattern and preventing stale state on next resume.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Auto mode exits when mergeSliceToMain() hits conflicts on runtime files
like completed-units.json that were manually committed via `gsd queue`.
Two-part fix:
- Untrack RUNTIME_EXCLUSION_PATHS from the index before merge starts
- If merge conflicts are limited to runtime files, auto-resolve by
taking ours and removing from index instead of aborting
Closes#189
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
After /login, if the current model has no valid API key, auto-switch to
a model from the newly authenticated provider. After /logout, if the
current model belongs to the logged-out provider, auto-switch to a
fallback model from a different provider.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The before_provider_request hook used model.startsWith("claude") to gate
native web search injection. This matched claude-* models served by any
provider (GitHub Copilot, AWS Bedrock, etc.), incorrectly injecting
Anthropic-only web_search_20250305 tool definitions into non-Anthropic
API requests.
The fix checks the isAnthropicProvider flag (set by model_select via the
provider field) instead of sniffing the model name.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Three locations used lastIndexOf("/") or includes("/") for path
manipulation, which fails on Windows where paths use backslashes.
- auto.ts: writeBlockerPlaceholder directory extraction → dirname()
- interactive-mode.ts: parent directory traversal → path.dirname() loop
- path-utils.ts: non-null assertion on MSYS drive letter access
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On Windows, LLMs convert absolute paths like F:\Projects\.gsd\... to
Unix-style /f/Projects/.gsd/... which Node's path.resolve interprets
as drive-root-relative, creating F:\f\Projects\.gsd\... instead.
Replace all *AbsPath template variables in prompt templates with
relative .gsd/... paths that resolve correctly on all platforms.
Add MSYS path normalization in resolveToCwd as defense-in-depth.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Repos that started tracking .gsd/completed-units.json before the
gitignore rule was added continue to see squash-merge conflicts because
.gitignore only prevents new tracking. This adds a bootstrap step that
runs `git rm --cached` on all RUNTIME_EXCLUSION_PATHS, eliminating the
conflict at its source.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GSD bypasses pi-coding-agent's CLI entry point (which sets undici's
EnvHttpProxyAgent as the global dispatcher), so proxy env vars were
ignored — causing 403 errors for users behind HTTPS proxies.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
LLM-generated commands with `> NUL` create undeletable files on Windows
because Git Bash treats NUL as a literal filename. Rewrite NUL redirects
to /dev/null at all three bash spawn sites.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fsevents 2.3.2 lacks prebuilt binaries for Node 25, causing node-gyp
build failures on install. Bumping to 2.3.3 which ships compatible
prebuilds while preserving the optionalDependency hoisting that fixes
Linux installs (#182).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
emitObservabilityWarnings only called ctx.ui.notify — the agent never
saw the warnings and ignored them entirely. Validator caught real issues
(missing observability sections, placeholder diagnostics) but had zero
enforcement.
Rename to collectObservabilityWarnings (returns issues), add
buildObservabilityRepairBlock to format issues as actionable prompt
instructions. Appended to the unit prompt so the agent reads flagged
files and fixes gaps before proceeding with the unit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Browser launch was hardcoded to headless: false, crashing on Linux
servers without a display server ($DISPLAY). Auto-detect headless
environments and also support FORCE_HEADLESS=true override.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Squash merge was hardcoded, causing auto-mode to hard-stop when conflicts
arose from long-lived branches or frequently-changing .gsd/* artifacts.
Add git.merge_strategy preference ("squash" | "merge", default: squash).
"merge" uses --no-ff which preserves branch history and handles conflicts
from divergent branches more gracefully. Users hitting repeated squash
merge failures can set merge_strategy: merge in .gsd/preferences.md.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
complete-slice verification only checked for the SUMMARY file, so when
the LLM skipped writing the UAT, the unit was marked complete and UAT
was never produced. Users saw doctor-created placeholder UATs instead
of real test scripts.
- verifyExpectedArtifact now checks both SUMMARY and UAT for complete-slice
- complete-slice prompt strengthened: step 7 requires concrete test cases,
MUST line lists all three required artifacts with enforcement warning
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three-layer fix for runtime files leaking into git commits:
1. Stage-then-unstage: replace pathspec excludes with git add -A followed
by git reset HEAD for each exclusion. The old approach failed when .gsd/
was in .gitignore — git exited non-zero before evaluating the excludes,
and the catch fallback staged everything unconditionally.
2. Auto-cleanup: on first smartStage call per session, remove any runtime
files that are already tracked in the index (from the fallback bug) via
a dedicated commit. This is a one-time migration that self-heals repos
where runtime files were accidentally committed.
3. Pre-checkout discard: after pre-switch auto-commits that exclude .gsd/,
run git checkout -- .gsd/ to clear dirty runtime files that would
otherwise block git checkout when the target branch has different
tracked versions.
Also adds completed-units.json to RUNTIME_EXCLUSION_PATHS and
BASELINE_PATTERNS (was missing — metrics.json was listed but
completed-units.json was not).
fsevents is a macOS-only native module that comes as a transitive
optional dependency of playwright. On Linux, npm tries to build it
via node-gyp which fails fatally, breaking 'npm install -g gsd-pi'.
Adding fsevents as an explicit optionalDependency at the root level
ensures npm gracefully skips it on non-macOS platforms. This is the
standard pattern used by vite, webpack, and chokidar.
Tasks:
- chore(M002/S06): auto-commit after complete-slice
- chore(M002/S06): auto-commit after complete-slice
- chore(M002/S06/T02): auto-commit after execute-task
- chore(M002/S06/T02): auto-commit after execute-task
- chore(M002/S06/T01): auto-commit after execute-task
- chore(M002/S06/T01): auto-commit after execute-task
- chore(M002/S06): auto-commit after plan-slice
- chore: update state for S06 execution
- docs(S06): add slice plan
Branch: gsd/M002/S06
Tasks:
- chore(M002/S02): auto-commit after complete-slice
- chore(M002/S02): auto-commit after complete-slice
- chore(M002/S02/T02): auto-commit after execute-task
- chore(M002/S02/T02): auto-commit after execute-task
- chore(M002/S02/T01): auto-commit after execute-task
- fix: expand tool outputs by default and pull main before slice merge
- chore(M002/S02): auto-commit after plan-slice
- docs(S02): add slice plan
Branch: gsd/M002/S02