Three fixes to the dispatch loop:
1. Don't mark a unit complete when the next dispatch is the same unit
(retry scenario) — let the retry mechanism handle it instead of
persisting a false completion.
2. Verify expected artifact exists on disk before marking a unit
complete. Uses resolveExpectedArtifactPath + existsSync to gate
persistCompletedKey calls.
3. Cross-validate idempotency: when skipping a "completed" unit, verify
the artifact actually exists. If missing, remove the stale record
from completed-units.json and re-run the unit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cut 48% of system prompt token cost while preserving all load-bearing content:
- Remove Activity Logs section (agent never interacts with these)
- Remove Investigation escalation ladder (redundant with tool-routing)
- Remove Context economy section (obvious/redundant)
- Remove Web research vs browser execution (compressed into playbooks)
- Compress tool-routing to non-obvious entries only (scout, bg_shell, Context7)
- Compress Ask vs infer to core rule
- Compress Code structure to 5 key principles
- Compress Verification to inline task-type table
- Compress Agent-First Observability (character block carries the why)
- Compress Background processes playbook from 30 to 5 lines
- Compress Web behavior playbook from 25 to 6 lines
- Compress Libraries and Current facts into single section
- Remove BRAVE_API_KEY config (user-facing, not agent-facing)
Three additions to the GSD character block:
- Security/performance/elegance as craft instinct, not checkbox compliance
- Anti-laziness: finish what you start, no stubs, no 80% features, no skipped error handling
- Self-debugging awareness: you write code you will debug later with no memory of writing it
Replace the generic agent intro with a craftsman-engineer character
definition: curious about problems, warm but terse, co-owner during
planning, committed executor during auto-mode. Consolidate the
scattered Communication and Writing Style + Work Narration sections
into a single focused Communication section that preserves all
calibration signals (pushback triggers, narration examples, uncertainty
handling).
- Prevent duplicate Brave tool entries when toggling providers repeatedly
by filtering already-active tools before re-adding (BUG-1)
- Remove single quotes from test glob patterns in package.json so Windows
shell expands them correctly (BUG-2)
- Fix test mock fire() to call all handlers instead of short-circuiting
on first match, matching real framework behavior (BUG-3)
- Suppress "Native Anthropic web search active" notification on session
restore (source: "restore") to reduce UX noise (BUG-4)
- Add regression tests for all 4 bugs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The model_select event doesn't reliably fire on startup, so Brave tools
remained visible to Claude even without a key. Now before_provider_request
filters search-the-web and search_and_read from the payload directly,
ensuring Claude only sees the native web_search tool.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Pi SDK's streaming parser drops server_tool_use and
web_search_tool_result content blocks. When the conversation is replayed,
assistant messages are incomplete, causing the Anthropic API to reject
requests with "thinking blocks cannot be modified."
Fix: stripThinkingFromHistory() removes thinking/redacted_thinking blocks
from all assistant messages before sending, since they're all from stored
history. The model generates fresh thinking for each new turn.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
12 tests covering: tool injection for claude models, non-claude passthrough,
double-injection prevention, tool deactivation/reactivation on model switch,
and session_start diagnostics.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Inject the web_search_20250305 server-side tool into Anthropic API
requests, eliminating the BRAVE_API_KEY requirement for Anthropic models.
When Anthropic + no Brave key, custom search tools are disabled to avoid
confusing the LLM with broken tools. fetch_page (Jina) is unaffected.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Theme proxy throws when accessed in RPC mode since initTheme() is
never called without a TUI. Wrap header rendering in try/catch so
the GSD extension loads cleanly in both TUI and RPC modes.
Closes#121
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a Work Narration section to system.md and per-phase hints to
research, plan, and execute prompts. Instructs the LLM to emit brief
status messages between tool calls covering decisions, discoveries,
phase transitions, and verification results — without narrating
routine reads or trivial commands.
Replace the plain-text API-key-only wizard with a branded, clack-based
onboarding experience that guides first-launch users through LLM provider
authentication (OAuth or API key), optional tool API keys, and a summary.
- Create src/logo.ts as single source of truth for ASCII logo
- Create src/onboarding.ts with shouldRunOnboarding() and runOnboarding()
- Trim src/wizard.ts to env hydration only (loadStoredEnvKeys)
- Wire onboarding into src/cli.ts, add `gsd config` subcommand
- Remove duplicate first-launch banner from src/loader.ts
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Parallel mode was slicing each agent's output to 200 characters before
returning to the parent agent, destroying researcher/scout findings.
Single and chain modes already return full output — this aligns parallel.
Closes#116
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Terminals like macOS Terminal.app and JetBrains IDEs don't support
the Kitty keyboard protocol, so Ctrl+Alt shortcuts silently fail.
Shortcut descriptions now detect unsupported terminals and surface
the equivalent slash command (e.g. /gsd status, /bg, /voice).
Closes#100, closes#104
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: branded postinstall with @clack/prompts
Replace raw ANSI ASCII art dump with structured, branded installer
flow using @clack/prompts and picocolors:
- Branded intro header with product name and version
- Animated spinners during patch and Playwright install steps
- Subprocess output captured (no more raw npm/Playwright noise)
- Boxed summary note with status indicators (✓/⚠)
- Clean outro with next-step instructions
- Graceful fallback to minimal output if clack unavailable
- All output routed to stderr for npm lifecycle visibility
- Async subprocess execution (not execSync) so spinners animate
* fix: restore ASCII banner alongside clack postinstall UI
The branded ASCII art banner is a key differentiator. Keep it as the
first thing users see, then follow with clack spinner steps for the
setup progress. Fallback path also simplified since the banner already
shows the version.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- D1: Move complete-slice dispatch before needsReassess so mergeSliceToMain
cannot be bypassed by early reassessment
- D2: Preserve main's slice-branch-chaining guard (branch from current HEAD
when not on a slice branch, fall back to main otherwise)
- D3: Replace consecutive-repeat stuck detection with per-unit total dispatch
counter that catches A→B→A→B alternating loops
- Atomic closeout: write unit completion to .gsd/completed-units.json before
in-memory update for crash recovery
- Persistent idempotency: completedKeySet loaded from disk on start/resume
- Startup self-heal: scan runtime records and clear stale ones
- Recovery backoff: exponential backoff between cross-invocation recovery attempts
Closes#96Closes#109
Co-Authored-By: omarsharaf96 <omarsharaf96@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ROADMAP.md was the only fatal requirement for .planning → .gsd migration,
but the transformer already had a null-roadmap fallback that infers
milestones from the phases/ directory. Downgrade to warning so partial
v1 projects can migrate successfully.
Closes#93Closes#90
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: worktree branch namespacing and fresh-start flow
- Namespace slice branches by worktree name (gsd/<wt>/<M>/<S>) to prevent
git checkout conflicts when multiple worktrees work on the same milestone
- getMainBranch() returns worktree/<name> inside a worktree so slice merges
target the worktree branch instead of main (which is checked out elsewhere)
- Add continue/fresh-start prompt when creating a worktree with existing milestones
- Restyle all worktree command output with consistent semantic color palette
- Add parseSliceBranch() and SLICE_BRANCH_RE for robust branch name parsing
- Fix duplicate getCurrentBranch import in auto.ts
- Add 40-assertion integration test covering full worktree lifecycle
* fix: branch slice from current branch, not main
ensureSliceBranch always branched from getMainBranch() (main/master),
but planning artifacts (CONTEXT, ROADMAP, etc.) may only exist on the
working branch (e.g. "developer"). The slice branch would lose all
planning artifacts, causing deriveState to see pre-planning and the
rebuildState post-hook to overwrite STATE.md with a blank state.
Now branches from the current branch when it is not itself a slice
branch. Falls back to main when on a slice branch to avoid chaining.
Adds regression tests for both cases.
- Fix auto.ts indentation: properly indent inner if-else chain inside summarizing guard
- Clear unitDispatchCount on resume path (paused → active) to prevent stale counts
- Add parseSummary regression tests for #91: bare scalar "none" coerced to string[]
- Add parseSummary test for missing frontmatter fields yielding empty arrays
- Verify .slice().join() works on coerced arrays (the original crash pattern)
Test results: 273 passed, 0 failed (24 new assertions)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CLI routing (#81, #107):
- Import and route --mode rpc to runRpcMode() instead of silently falling through to runPrintMode
- Add TTY guard before interactive mode — exit with helpful message when stdin is not a TTY
- Add --version and --help flags
Auto-mode infinite loop (#96):
- Move summarizing/complete-slice dispatch before reassessment check (D1) — ensures mergeSliceToMain always runs
- Add per-unit dispatch counter to detect alternating loops like A→B→A→B (D3)
Windows shell escaping (#106, #98):
- Platform-aware escapeShellArg() in mcporter extension — double quotes on Windows, single quotes on Unix
CRASH: parseSummary (#91):
- Add asStringArray() helper to safely coerce YAML bare scalars (e.g. "none") to string arrays
- Applied to all 7 frontmatter fields that expect string[]
Google Search model (#99):
- Replace hardcoded gemini-3-flash-preview with env var GEMINI_SEARCH_MODEL (default: gemini-2.5-flash)
Worktree branch collision (#84):
- Check git worktree list before checkout to detect branches already in use by another worktree
Migration UX (#90, #93):
- Improve error messages to distinguish migration from new project setup, suggest /gsd:new-project
Keyboard shortcuts (#100, #104):
- Document terminal protocol requirement in shortcut descriptions — Ctrl+Alt combos need Kitty/modifyOtherKeys
Closes#81, #84, #91, #96, #99, #106, #107
Addresses #90, #93, #95, #98, #100, #104
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three defects in auto.ts + worktree.ts combined to produce an infinite
alternating loop and unreliable unit closeout in GSD auto-mode.
**D1 (auto.ts)** — `state.phase === "summarizing"` is now the first
branch in the dispatch if-else chain, evaluated before `needsRunUat`
and `needsReassess`. Previously, if an execute-task agent wrote
slice-level artifacts early, `needsReassess` fired instead and
`mergeSliceToMain` was permanently skipped.
**D2 (worktree.ts)** — New slice branches are now created from the
current HEAD instead of `main`. When a prior slice merge was skipped,
the new branch would inherit a stale ROADMAP from main, creating
divergent state that drove the A→B→A→B alternation.
**D3 (auto.ts)** — Replaced `lastUnit`/`retryCount` consecutive-repeat
detection with a `unitDispatchCount` map that tracks total dispatches
per unit key. The old guard reset to 0 on every ID change; the map
catches alternating-loop patterns and stops after MAX_UNIT_DISPATCHES=3.
**Atomic closeout (auto.ts)** — `persistCompletedKey` writes the unit
key to `.gsd/completed-units.json` before any in-memory update. A crash
mid-closeout is now recoverable: on next start `loadPersistedKeys`
re-populates `completedKeySet` and the idempotency guard skips already-
completed units.
**Persistent idempotency (auto.ts)** — `completedKeySet` is loaded from
disk on `startAuto` and checked before every dispatch, preventing re-
dispatch of units completed in a prior session even after a restart.
**Startup self-heal (auto.ts + unit-runtime.ts)** — `selfHealRuntimeRecords`
runs on start and resume; it scans all on-disk runtime records, checks
whether each unit's expected artifact exists, and clears any orphaned
records. Added `listUnitRuntimeRecords` to unit-runtime.ts to support
this scan.
**Recovery backoff (auto.ts)** — `recoverTimedOutUnit` now tracks
cross-invocation recovery attempts per unit in `unitRecoveryCount` and
applies exponential backoff (1s→2s→4s…30s cap) between attempts.
Attempt number is included in all recovery notify messages for
traceability.
Closes#96Closes#109
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add Tavily Search API as an alternative backend for search-the-web and
search_and_read tools. Tavily is selected automatically when TAVILY_API_KEY
is set (preferred over Brave when both keys present). Existing Brave
Search paths are completely unchanged.
Motivation: Brave Search API signup requires Stripe payment which may
not be available in all regions. Tavily offers a free tier and also
provides a Deep Research API for future expansion.
Changes:
- Auth: Tavily API key in wizard, auth.json storage, env hydration
- search-the-web: Tavily POST backend with response normalization
- search_and_read: Tavily advanced search with client-side token budgeting
- /search-provider: slash command for explicit provider switching
- 61 new tests covering all Tavily integration paths
- Zero changes to existing Brave code paths