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.
Adds git.isolation: "none" so developers can use GSD's full planning/execution
methodology with all work happening directly on their current branch — no worktree,
no milestone branch, no merge step.
- Preference plumbing: "none" accepted/validated, getIsolationMode() helper
- Core bypass: all worktree/branch gates in auto.ts skip in none mode
- Peripheral: doctor skips worktree checks, system prompt omits worktree context
- Docs: git-strategy, configuration, auto-mode, troubleshooting, system prompt updated
- Tests: 11 new tests, 621/621 pass
Three fixes for git worktree usage:
1. Add resolveGitDir() that follows the gitdir: pointer in worktree
.git files. Without this, any code looking for MERGE_HEAD,
SQUASH_MSG, or rebase state checks the wrong path in worktrees.
2. Guard captureIntegrationBranch() with detectWorktreeName() — in a
worktree the base branch is implicit (worktree/<name>), so writing
it to META.json leaves stale metadata that persists after merge-back.
3. Use resolveGitDir() in doctor.ts for corrupt merge state detection.
Previously hardcoded join(basePath, ".git") which misses worktrees.
Includes 7 regression tests covering all three fixes.
Relates to #654 (stale state from main branch instead of worktree)
Relates to #672 (parallel milestone orchestration)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add native MCP server mode (--mode mcp): exposes GSD's tools via
Model Context Protocol over stdin/stdout for Claude Desktop, VS Code,
and other MCP-compatible clients. Uses @modelcontextprotocol/sdk.
- Add /lint skill: auto-detects ESLint, Biome, Prettier, rustfmt,
gofmt, Black, Ruff and runs with structured output
- Add 6 E2E smoke tests: --version, --help, config --help, update
--help, --list-models, and --mode text --print startup
- Fix diff-context.ts stdio type for CI compatibility
- Fix token-counter.ts tiktoken import for extensions typecheck
- Update help text and CLI to include --mode mcp
- Add /review skill: reviews staged/unstaged/commit changes for security,
performance, bugs, and quality with structured findings by severity
- Add /test skill: auto-detects test framework, generates comprehensive
tests for source files, or runs suites with failure analysis
- Add chokidar file watcher: watches ~/.gsd/agent/ for config changes
(settings.json, auth.json, models.json, extensions/) with debounced
events on an EventBus
- Add --help per subcommand: `gsd config --help` and `gsd update --help`
show subcommand-specific usage information
- 8 new file-watcher tests (start/stop, event emission, debouncing,
unrelated file filtering)
- claude-import-tui.test.ts: add proper mock for ExtensionCommandContext
with all required properties using type assertions
- marketplace-discovery.test.ts: add explicit check for result.error
before accessing it to satisfy strict null checking
Fix type compatibility issues introduced in the JS→TS conversion:
- Restore PageEntry.page to `any` (holds Playwright Page instance)
- Use Record<string, any> for session parameters in
buildFailureHypothesis and summarizeBrowserSession (callers pass
rich objects with extra properties)
- Use Record<string, unknown> for formatTimelineEntries options
- Add explicit type annotations to local variables and callbacks
to satisfy noImplicitAny in tsconfig.extensions.json
Capture the session file path on pause and synthesize a recovery briefing
on resume, so the next agent knows what already happened instead of
restarting the unit from scratch.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move @types/mime-types from dependencies to devDependencies in pi-tui
(type declarations are only needed at compile time)
- Align chalk version: upgrade root from ^5.5.0 to ^5.6.2 to match
pi-ai and avoid version skew
- Extract duplicated help text from loader.ts and cli.ts into shared
help-text.ts module (single source of truth)
- Convert validate-pack.sh to Node.js for Windows compatibility
- Fix dev.js using unnecessary npx for tsc (it's a devDependency,
use node_modules/.bin/tsc directly)
When auto-mode loop recovery marks tasks as [x] in the plan file and
auto is restarted, the done count can equal total. The display always
showed done+1 (assuming "currently working on next task"), causing
values like "task 5/4". Clamp to total so it never exceeds the max.
Closes#662
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Convert browser-tools/core.js (1058 lines) to native TypeScript with
full type annotations from the existing .d.ts file. Remove the
separate .d.ts declaration file (types are now inline).
- Add c8 test coverage reporting: `npm run test:coverage` generates
text + lcov reports with 50% statement threshold baseline.
- Add coverage/ to .gitignore
All 712 unit tests, 63 browser-tools tests, and 11 integration tests
pass with zero regressions.
1. Windows: `start` command opens CMD instead of browser during GitHub
Copilot login — pass empty title arg so URL is treated as target
2. Launch banner missing Tavily provider in web search status display
3. MCPorter auto-installs via npm when not found (like ripgrep auto-download)
4. Notification prefs showing [object Object] — guard against non-boolean values
Closes#663
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Agent now cancels in-flight async_bash jobs after editing source files
before re-running them, preventing stale results from polluting context.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix loadStoredEnvKeys divergent provider lists: add telegram_bot and
custom-openai to wizard.ts (the canonical copy used by CLI), remove
dead duplicate from onboarding.ts
- Security: add SAFE_COMMAND_PREFIXES allowlist to resolveConfigValue
to prevent arbitrary RCE via settings.json shell commands
- Security: add TOFU (Trust On First Use) model for project-local
extensions — skip untrusted .pi/extensions/ with stderr warning
- Performance: debounce sql.js MemoryStorage persistence (500ms window)
so rapid mutations coalesce into a single db.export()+writeFileSync
- Fix double lstatSync call in tool-bootstrap.ts isRegularFile
- Add 26 new tests covering all changes
findMilestoneIds() had a bare catch that returned [] on any error,
causing showSmartEntry() to think no milestones exist and restart
the new-project discuss flow in a loop. Add error logging when the
directory exists but scanning fails, and a sanity check that warns
the user instead of looping when the directory has entries that
weren't recognized.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- google-search test: mock getApiKeyForProvider to return JSON string
matching real OAuth provider behavior (token+projectId), instead of
using AuthStorage.inMemory which bypasses the OAuth getApiKey transform
- smoke test: split on /[/\\]/ for Windows path separator compatibility
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>