Commit graph

1140 commits

Author SHA1 Message Date
Tom Boucher
188e7a67c4 fix: single ENTER submits slash command argument autocomplete (#944) (#953)
When completing a /gsd subcommand via autocomplete (e.g. selecting 'auto'
after typing '/gsd '), ENTER now submits immediately instead of requiring
a second press.

The selectConfirm handler already fell through to submit when the
autocomplete prefix started with '/' (completing the command name itself).
Now it also falls through when the cursor is in a slash command context
(completing an argument like 'auto', 'status', 'help').

Non-slash completions (@file references, paths) still require explicit
ENTER to submit — only slash command arguments auto-submit.
2026-03-17 15:28:40 -06:00
Tom Boucher
614012f38a fix: add git.manage_gitignore preference to opt out of .gitignore changes (#950) (#952)
When set to false in .gsd/preferences.md, GSD will not modify .gitignore
at all — no baseline patterns added, no self-healing, no untracking.

Usage in preferences.md:
  git:
    manage_gitignore: false

Files changed:
- git-service.ts: Add manage_gitignore to GitPreferences interface
- gitignore.ts: Early return when manageGitignore is false
- auto.ts: Pass manage_gitignore preference to ensureGitignore
- preferences.ts: Parse and validate manage_gitignore in git config
2026-03-17 15:27:34 -06:00
Tom Boucher
aa224b5944 fix: break web search loop with consecutive duplicate guard (#949) (#955)
When the LLM calls the same web search query 4+ times consecutively,
return an error telling it to stop and use existing results instead of
silently returning cached results that the LLM ignores.

Tracks consecutive duplicate searches via a simple counter keyed on
the normalized query + parameters. Resets when a different query is
searched. Threshold is 3 consecutive duplicates before the guard fires.

File changed: search-the-web/tool-search.ts
2026-03-17 15:27:18 -06:00
Tom Boucher
1b1df58749 refactor: encapsulate auto.ts state into AutoSession class (#898) (#948)
* refactor: encapsulate auto.ts state into AutoSession class (#898)

Follow-up to PR #906 (7 module extractions). All ~40 mutable module-level
variables in auto.ts are replaced with properties on a single AutoSession
class instance (s).

Changes:
- auto/session.ts: 200-line AutoSession class with typed properties,
  clearTimers(), resetDispatchCounters(), completeCurrentUnit(), reset(),
  and toJSON() for diagnostics.
- auto.ts: ~700 variable references renamed from bare names to s.xxx.
  All module-level let/const state declarations removed. Constants
  (MAX_UNIT_DISPATCHES, etc.) re-exported from session.ts.
- Tests updated: milestone-transition-worktree.test.ts and
  triage-dispatch.test.ts source-grep patterns updated for s.xxx names.

Benefits:
- 40 scattered declarations → 1 class with typed properties
- Manual reset of 25+ variables in stopAuto → s.reset()
- s.toJSON() for state snapshots and diagnostics
- grep 's.' shows every state access

No behavioral changes. 1224 tests pass.

* fix: import constants locally for tsconfig.extensions.json compatibility

The extensions tsconfig couldn't resolve re-exported constants from
auto/session.js. Fix: import them explicitly in addition to re-exporting.
Also remove leftover DISPATCH_GAP_TIMEOUT_MS local declaration.
2026-03-17 14:59:42 -06:00
Tom Boucher
1e979ff626 fix: retry transient network errors before model fallback (#941) (#945)
Previously, any provider error during auto-mode immediately triggered the
model fallback chain. This meant providers with occasional network flakiness
(e.g. zai-coding-plan) would get abandoned after a single transient error,
barely getting used before the fallback took over.

Now, transient network errors (ECONNRESET, ETIMEDOUT, socket hang up, DNS
failures, etc.) are retried up to 2 times with linear backoff (3s, 6s)
before falling back to the next model. Permanent errors (auth, quota,
billing) still trigger immediate fallback.

Changes:
- index.ts: Add network retry loop before fallback chain in agent_end error
  handler. Track retry counts per model in networkRetryCounters map.
  Clear counters on successful unit completion and model switches.
- preferences.ts: Extract isTransientNetworkError() as testable utility.
  Matches network signals while excluding permanent auth/billing errors.
- network-error-fallback.test.ts: Add 12 tests for transient error detection
  covering all signal patterns and exclusion cases.
2026-03-17 14:59:22 -06:00
deseltrus
5bae521af0 fix: parallel worker PID tracking, spawn-status race, exit persistence (#932)
* fix: parallel worker PID tracking, spawn-status race, exit persistence

Three bugs in parallel-orchestrator.ts that cause workers to appear
permanently stuck in "running" or silently lose state on exit:

1. Worker PID initialized to coordinator's process.pid instead of 0.
   Session status files recorded wrong PID, breaking stale detection
   (isPidAlive returns true for the coordinator, not the dead worker).

2. Session status written with "running" BEFORE spawn attempt. If spawn
   fails, status file stays "running" indefinitely. Now spawns first,
   then writes status with actual state (running or error).

3. Worker exit handler updates session status but didn't call
   persistState(), so orchestrator.json got out of sync. Next
   coordinator restart could adopt already-dead workers.

Closes #672 (partial — worker lifecycle hardening)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: adapt lifecycle tests for spawn-aware session status

Tests now handle both outcomes: when spawnWorker() succeeds (running
state) and when it fails in CI (error state, no GSD binary available).
The lifecycle logic under test — session status writes, stop, pause,
resume — works correctly in both cases.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 14:59:05 -06:00
Tom Boucher
a82092eb63 fix: /gsd discuss now recommends next undiscussed slice (#935) (#939)
Three fixes for the discuss picker loop:

1. Recommend first undiscussed slice instead of always recommending
   the first pending slice (i === 0). The recommended flag now checks
   discussion state via CONTEXT file existence.

2. Exit with a summary notification when all pending slices have been
   discussed, instead of looping back to a picker where everything
   is already done.

3. Invalidate deriveState cache after each discuss session completes
   so subsequent state reads pick up the newly-written CONTEXT files.
2026-03-17 14:10:15 -06:00
Jeremy McSpadden
b44e43b841 refactor: reorder HTML report sections to match visualizer tab order (#937)
Mirror the visualizer's logical grouping in the HTML report:
- Core project views: Summary, Progress, Timeline, Dependencies
- Analytics/monitoring: Metrics, Health
- Content: Changelog, Knowledge, Captures
- Report-only: Artifacts, Planning

Updated sections array, TOC nav, and file header comment.
2026-03-17 14:06:24 -06:00
Tom Boucher
87352c9a4f fix: allow suffix text after '## Slices' heading in roadmap parser (#924) (#936)
The regex required exactly '## Slices' with nothing after. If an agent
renamed it (e.g. '## Slices (generate flow — first batch)'), the parser
returned zero slices, blocking auto-mode.

Changed /^## Slices\s*$/m to /^## Slices\b.*$/m — word boundary ensures
'Slices' is complete, .* allows any trailing text.
2026-03-17 14:06:10 -06:00
Jeremy McSpadden
43776b68c6 refactor: reorder visualizer tabs into logical groupings (#934)
Reorganize the 10-tab TUI visualizer for better workflow:
- Core project views: Progress, Timeline, Deps
- Analytics/monitoring: Metrics, Health, Agent
- Content: Changes, Knowledge, Captures
- Utility: Export (moved to last position)

Updated all tab index references (switch cases, export key
handling, export status display) and corrected help text
from "7-tab" to "10-tab" with accurate tab listing.
2026-03-17 14:05:38 -06:00
deseltrus
9fe805b1d3 test: parallel merge reconciliation + budget atomicity (G5/G6) (#933)
* test: parallel merge reconciliation + budget atomicity coverage (G5/G6)

27 new tests covering two gaps identified in #672:

G5 — Merge Reconciliation (parallel-merge.test.ts, 17 tests):
- determineMergeOrder: sequential, by-completion, filtering, defaults
- formatMergeResults: success, conflict, empty, mixed output
- mergeCompletedMilestone: clean merge with session cleanup, missing
  roadmap error, conflict detection with structured file list
- mergeAllCompleted: sequential order, stop-on-first-conflict,
  by-completion order (integration tests with real git repos)

G6 — Budget Atomicity (parallel-budget-atomicity.test.ts, 10 tests):
- Ceiling enforcement: exceeded, not exceeded, exact boundary
- Cost aggregation: correct sum, incremental updates
- No double-counting: 5 rapid refreshes produce correct total
- Budget reset: resetOrchestrator clears all state
- No ceiling: unlimited spending when budget_ceiling unset
- Worker state sync: refreshWorkerStatuses picks up disk changes

All tests use node:test + node:assert/strict. No production code changes.

Relates to #672

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use double quotes in git commit messages for Windows compatibility

Single-quoted commit messages in test helpers fail on Windows CMD
(pathspec errors). Switch to double quotes which work cross-platform.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 14:05:16 -06:00
Jeremy McSpadden
1ea653b5fc refactor: TUI dashboard cleanup, dedup, and feature improvements (#931)
* refactor: TUI dashboard cleanup, dedup, and feature improvements

- Extract shared format-utils.ts: formatDuration, padRight, joinColumns,
  centerLine, fitColumns, sparkline, stripAnsi — eliminating 3× duplication
  across dashboard-overlay, visualizer-views, and auto-dashboard
- Use shared STATUS_GLYPH/STATUS_COLOR from ui.ts consistently across all
  overlay and view files instead of hardcoded Unicode glyphs
- Fix redundant dynamic import('node:fs') in visualizer-data.ts (statSync
  already imported at top level)
- Replace (entry as any) casts with proper SessionMessageEntry type narrowing
- Add mtime-based file content cache for visualizer data loader to avoid
  re-parsing unchanged roadmap/plan files on every refresh
- Increase visualizer refresh interval from 2s to 5s (with mtime cache,
  unchanged files are effectively free)
- Fix sparkline to use loop-based max instead of Math.max(...values) to
  avoid stack overflow on large arrays
- Add ETA/time-remaining estimate to progress widget and dashboard overlay
  based on average unit duration from metrics ledger
- Show warning glyph for budget-pressured units in completed units list
  (continueHereFired units now show ⚠ instead of ✓)
- Add terminal resize (SIGWINCH) handling to both overlays — invalidates
  cache and re-renders on window size change
- Fix dispose race in dashboard overlay close path — now calls dispose()
  before onClose() to prevent timer callbacks firing after teardown
- Add 23 unit tests for format-utils.ts (including 100k-element sparkline)
- Add 2 tests for estimateTimeRemaining
- Add source-contract tests for resize handler and shared imports

* fix: use STATUS_GLYPH.warning instead of STATUS_GLYPH.statusWarning

STATUS_GLYPH is keyed by ProgressStatus ("warning"), not by GLYPH
property name ("statusWarning"). Fixes typecheck failure in CI.
2026-03-17 14:02:26 -06:00
Jeremy McSpadden
58fd9cf0c1 docs: update README for v2.25-v2.26 features (#929)
Add documentation for HTML report generator, verification enforcement,
parallel orchestrator crash recovery, headless multi-session orchestration,
milestone validation gate, require_slice_discussion option, meaningful
commit messages, and new keyboard shortcuts. Update comparison table,
preferences reference, suggested .gitignore, and commands table.
2026-03-17 14:02:06 -06:00
Tom Boucher
3b18711524 fix: don't overwrite user's model choice when API key is temporarily unavailable (#910) (#928)
The startup model validation overwrote the user's configured model when
it was 'not available' (API key missing, OAuth token expired, rate
limited). This silently changed the model to a fallback like
google/gemini-1.5-flash or openai/gpt-5.4.

Fix: Only trigger the fallback when the configured model doesn't exist
in the registry at all (removed/unknown). A model that exists but is
temporarily unavailable (credential issue) keeps its setting — the
session-level fallback resolver handles it at prompt time.
2026-03-17 14:01:51 -06:00
Tom Boucher
2cba5bc072 fix: break reassess-roadmap skip loop by preventing re-persistence of evicted keys (#912) (#927)
After the skip-loop breaker evicts a completion key, the fallback path
at the bottom of dispatchNextUnit re-persists it because the expected
artifact exists on disk. This recreates the exact loop the breaker was
trying to break:

  evict key → dispatch → verifyArtifact(true) → re-persist key → skip → evict → repeat

Fix: Track recently-evicted keys in a Set. The fallback artifact-check
path skips re-persistence for keys that were just evicted by the
skip-loop breaker. Set is cleared on stopAuto.
2026-03-17 14:01:36 -06:00
Tom Boucher
2306e6bb34 fix: LSP command resolution and ENOENT crash on Windows/MSYS (#901) (#925)
Two fixes:

1. lsp/config.ts: Use `where.exe` instead of `which` on Windows.
   MSYS's `which` returns POSIX paths (/c/Users/...) that Node's
   spawn() can't execute. `where.exe` returns native Windows paths.

2. lsp/client.ts: Handle spawn ENOENT error gracefully. When the LSP
   server binary doesn't exist, the error event now triggers a clean
   exit instead of bubbling up and crashing auto-mode.
2026-03-17 14:01:16 -06:00
Tom Boucher
7869312769 fix: dispatch plan-slice when task plan files are missing (#909) (#923)
When a slice plan (S03-PLAN.md) was pre-created during roadmapping
but plan-slice never ran to generate per-task files (tasks/T01-PLAN.md),
deriveState returned 'executing' phase. execute-task then failed because
the task plan didn't exist, creating an infinite restart loop.

Fix: In deriveState, when the tasks directory exists but has zero .md
files and the slice plan references tasks, return 'planning' phase
instead of 'executing'. This causes plan-slice to dispatch and generate
the missing task plans.

Tests updated: 6 test files that create synthetic state fixtures now
include a stub task plan file so their 'executing' phase assertions
remain valid.
2026-03-17 13:59:34 -06:00
Jeremy McSpadden
e0420f5981 fix: reduce CPU usage on long auto-mode sessions (#921)
* fix: reduce CPU usage on long auto-mode sessions

Seven targeted fixes for compounding process/timer/I/O issues that cause
high CPU during multi-hour /gsd auto sessions:

1. Wrap idle watchdog and hard timeout async callbacks in try-catch to
   prevent unhandled rejections from orphaning intervals
2. Cache nativeHasChanges fallback (10s TTL) to avoid spawning a new
   git process every 15 seconds when native module is unavailable
3. Call clearUnitTimeout() before dispatchNextUnit() in all recovery
   paths to prevent stale idle watchdog from firing alongside new timers
4. Add 10-second timeout to subagent worktree cleanup to prevent hangs
   when git worktree remove blocks indefinitely
5. Prune dead bg-shell processes after each unit completion to free
   retained output buffers (~500KB-1MB per dead process)
6. Throttle STATE.md rebuilds to at most once per 30 seconds (was every
   unit completion at 100-400ms each)
7. Increase progress widget refresh interval from 5s to 15s to reduce
   synchronous file I/O on the hot path

* fix: reset nativeHasChanges cache in worktree test

The 10s TTL cache on nativeHasChanges was causing the worktree test
to return stale "no changes" when checking a freshly dirtied repo
within the cache window. Reset the cache before the dirty-repo
assertion so the test correctly detects new changes.
2026-03-17 13:58:14 -06:00
Lex Christopherson
6becff186e Merge pull request #906 from jeremymcs/issue-898-auto-refactor
refactor: extract 8 focused modules from auto.ts
2026-03-17 13:20:39 -06:00
Lex Christopherson
2e013d70b5 merge: resolve 12 conflicts with main — integrate continueHere feature into refactored closeoutUnit
Conflicts arose because main added continueHereHandle cleanup and
buildSnapshotOpts (with continueHereFired) while the PR extracted
inline closeout code into closeoutUnit(). Resolution: use closeoutUnit()
with buildSnapshotOpts() to pass all fields including continueHereFired.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 13:20:20 -06:00
Colin Johnson
7733e12413 fix: reap orphan-prone child processes across session churn (#920)
* fix: reap orphan-prone child processes across session churn

* test: make bg-shell cleanup test shell-safe
2026-03-17 13:14:51 -06:00
Jeremy McSpadden
3167e9fbf4 feat: HTML report generator with progression index (#876) 2026-03-17 11:51:54 -06:00
Tom Boucher
dc7f5e8da5 fix: resolve symlinked skill directories in always_use_skills (#911) (#919) 2026-03-17 11:49:11 -06:00
Goran Cabarkapa
6ed5e23603 fix: resolve symlinked skill directories in preferences (#913) 2026-03-17 11:48:46 -06:00
deseltrus
10200c43f3 feat: crash recovery for parallel orchestrator (#873) 2026-03-17 11:47:26 -06:00
Tom Boucher
5d86159ea8 fix: replan-slice infinite loop, non-standard finish_reason crash, fork-resilient test (#866) 2026-03-17 11:47:07 -06:00
Jeremy McSpadden
2dd7e256f0 fix: skip slice plan commit when commit_docs is false (#784) (#802) 2026-03-17 11:46:53 -06:00
Jeremy McSpadden
2ee0e5ee17 fix: dispatch plan-slice when task plans missing instead of hard-stop (#909) (#915) 2026-03-17 11:44:42 -06:00
Jeremy McSpadden
f5e9b00f47 fix: wire continue-here context-pressure monitor to send wrap-up signal at 70% (#916) 2026-03-17 11:44:12 -06:00
Tom Boucher
25292f8840 fix: missing STATE.md in fresh worktree deadlocks pre-dispatch health gate (#889) (#895) 2026-03-17 11:09:25 -06:00
Tom Boucher
2dd8f481a3 fix: clean stale runtime unit files for completed milestones on startup (#887) (#896) 2026-03-17 11:08:58 -06:00
Juan Francisco Lebrero
bdbe739ebc feat: headless orchestration skill + supervised mode (#905) 2026-03-17 11:08:15 -06:00
Jeremy McSpadden
f77ce13944 fix: use generic type on resolveModelId to preserve Model<Api> through resolution
resolveModelId narrowed the return type to { id, provider } which lost
the full Model<Api> shape needed by pi.setModel(). Using a generic <T>
preserves the actual type from the registry's getAvailable() call.
2026-03-17 11:37:59 -05:00
Jeremy McSpadden
5a5cbff64e merge: resolve conflict with main — keep parsePlan import from upstream 2026-03-17 11:22:13 -05:00
Jeremy McSpadden
50117a47b6 refactor: extract worktree sync, resource staleness, stale escape to auto-worktree-sync.ts
Cherry-picks the worktree-sync concept from #902, adapted to the flat
auto-*.ts naming convention with fixes:
- Uses gsdVersion (not syncedAt) for resource staleness detection (#804)
- Uses ESM imports (not require()) for node:fs/node:os
- Includes both sync directions (project→worktree and worktree→project)
- Includes escapeStaleWorktree and cleanStaleRuntimeUnits

auto.ts: 3,256 → 3,099 lines (-157 lines)
2026-03-17 11:17:05 -05:00
Jeremy McSpadden
3c9762d9b7 refactor: extract 7 focused modules from auto.ts (#898)
Break up the 3,975-line auto.ts into focused, single-concern modules:

- auto-budget.ts: Pure budget alert level and enforcement functions
- auto-tool-tracking.ts: In-flight tool call tracking for idle detection
- auto-observability.ts: Pre-dispatch observability validation and repair
- auto-unit-closeout.ts: Consolidated metrics/activity/memory closeout helper
- auto-direct-dispatch.ts: Manual /gsd dispatch phase command handling
- auto-timeout-recovery.ts: Idle and hard timeout recovery with escalation
- auto-model-selection.ts: Model routing, complexity classification, fallback chains

auto.ts retains orchestration (start/stop/pause, handleAgentEnd, dispatchNextUnit)
and drops from 3,975 to 3,256 lines (-719 lines, -18%).

All extractions are pure moves with re-exports — no behavior changes.
All 1,092 unit tests and 30 integration tests pass.
2026-03-17 11:03:01 -05:00
TÂCHES
1dd32c635f feat(M001): Verification Enforcement (#891)
* docs: project plan — 4 milestones

* chore(M001): record integration branch

* chore(M001/S01): auto-commit after research-slice

* docs(S01): add slice plan

* chore: update state to S01 execution

* chore(M001/S01/T01): auto-commit after execute-task

* chore(M001/S01/T02): auto-commit after execute-task

* chore(M001/S01/T03): auto-commit after execute-task

* chore(M001/S01): auto-commit after complete-slice

* chore(M001/S01): auto-commit after reassess-roadmap

* chore(M001/S02): auto-commit after research-slice

* docs(S02): add slice plan

* chore(M001/S02/T01): auto-commit after execute-task

* chore(M001/S02/T02): auto-commit after execute-task

* test(S02/T03): Added evidence_block_missing and evidence_block_placehol…

- src/resources/extensions/gsd/observability-validator.ts
- src/resources/extensions/gsd/tests/verification-evidence.test.ts

* chore(M001/S02): auto-commit after complete-slice

* chore(M001/S02): auto-commit after reassess-roadmap

* chore(M001/S03): auto-commit after research-slice

* docs(S03): add slice plan

* fix(S03/T01): Added `formatFailureContext` pure function and retry meta…

- src/resources/extensions/gsd/verification-gate.ts
- src/resources/extensions/gsd/verification-evidence.ts
- src/resources/extensions/gsd/tests/verification-gate.test.ts
- src/resources/extensions/gsd/tests/verification-evidence.test.ts

* fix(S03/T02): Wired verification gate auto-fix retry loop into auto.ts…

- src/resources/extensions/gsd/auto.ts

* chore(M001/S03): auto-commit after complete-slice

* chore(M001/S03): auto-commit after reassess-roadmap

* chore(M001/S04): auto-commit after research-slice

* docs(S04): add slice plan

* test(S04/T01): Added RuntimeError interface and captureRuntimeErrors()…

- src/resources/extensions/gsd/types.ts
- src/resources/extensions/gsd/verification-gate.ts
- src/resources/extensions/gsd/tests/verification-gate.test.ts

* test(S04/T02): Integrated captureRuntimeErrors() into auto.ts gate bloc…

- src/resources/extensions/gsd/auto.ts
- src/resources/extensions/gsd/verification-evidence.ts
- src/resources/extensions/gsd/tests/verification-evidence.test.ts

* chore(M001/S04): auto-commit after complete-slice

* chore(M001/S04): auto-commit after reassess-roadmap

* chore(M001/S05): auto-commit after research-slice

* docs(S05): add slice plan

* test(S05/T01): Added AuditWarning type, runDependencyAudit() with git d…

- "src/resources/extensions/gsd/types.ts"
- "src/resources/extensions/gsd/verification-gate.ts"
- "src/resources/extensions/gsd/tests/verification-gate.test.ts"

* feat(S05/T02): Wired runDependencyAudit() into the verification gate pi…

- src/resources/extensions/gsd/verification-evidence.ts
- src/resources/extensions/gsd/auto.ts
- src/resources/extensions/gsd/tests/verification-evidence.test.ts

* chore(M001/S05): auto-commit after complete-slice

* chore(M001): auto-commit after validate-milestone

* chore(M001): auto-commit after complete-milestone

* feat(M001): Verification Enforcement

Completed slices:
- S01: Built-in Verification Gate
- S02: Structured Evidence Format
- S03: Auto-Fix Retry Loop
- S04: Runtime Error Capture
- S05: Dependency Security Scan

Branch: milestone/M001

* chore(M002): record integration branch

* chore(M003): record integration branch

* chore(M004): record integration branch

* fix(M001): Address verification gate review feedback

1. Add 120s default timeout to spawnSync in runVerificationGate (configurable
   via commandTimeoutMs) — prevents hanging commands from deadlocking the system
2. Sanitize taskPlanVerify commands — reject strings containing ;, |, backticks,
   or $() shell injection patterns
3. Clear verificationRetryCount in pauseAuto — previously only
   pendingVerificationRetry was cleared, leaving stale retry state on resume

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ci): remove .gsd/ and .audits/ from tracking

These directories were accidentally included via M001 milestone
auto-commits. Both are already in .gitignore. The no-gsd-dir CI
check correctly catches this.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 09:47:12 -06:00
Jeremy McSpadden
23b89c64c9 fix: detect broken install and add Windows symlink fallback (#890)
Fixes #882 — npm install -g gsd-pi installing a broken version where
@gsd/pi-coding-agent cannot be resolved, causing ERR_MODULE_NOT_FOUND.

Root causes addressed:
1. On Windows without Developer Mode or admin rights, symlinkSync fails
   even for NTFS junctions, leaving node_modules/@gsd/ empty and causing
   a cryptic ERR_MODULE_NOT_FOUND instead of a usable error message.
2. If npm latest dist-tag is stale (pointing to an old version that
   predates the packages/ directory), users get the same failure.

Changes:
- src/loader.ts: after symlinking, validate @gsd/pi-coding-agent exists;
  emit a clear actionable error with reinstall instructions instead of
  letting Node throw ERR_MODULE_NOT_FOUND deep inside cli.js. Also adds
  cpSync fallback when symlinkSync fails (Windows without elevated perms).
- scripts/link-workspace-packages.cjs: same cpSync fallback — ensures
  postinstall succeeds on restricted Windows environments.
- scripts/validate-pack.js: verify @gsd/* packages are resolvable after
  the isolated install test, and run `gsd -v` to confirm end-to-end
  resolution before declaring the pack valid.
- .github/workflows/build-native.yml: add post-publish dist-tag
  verification step that confirms npm dist-tags.latest matches the
  published version for stable releases, catching stale-tag regressions
  in CI before users encounter them.
2026-03-17 09:35:57 -06:00
Tom Boucher
01a6294b23 feat: auto-restart headless mode on crash with exponential backoff (#886) (#897)
When the headless child process crashes or errors out, auto-restart
with exponential backoff (5s, 10s, 15s... up to 30s) instead of
exiting immediately. This enables overnight 'fire and forget' runs.

- --max-restarts N (default 3, 0 to disable): controls restart budget
- Only restarts on crashes (exit code !== 0), not on success or blocked
- SIGINT/SIGTERM bypasses restart (user intent to stop)
- Restart count shown in summary output
- Backoff prevents rapid crash loops from burning API credits

The inner loop function (runHeadlessOnce) returns exit status instead
of calling process.exit, letting the outer loop decide whether to
restart or terminate.

This is the first step toward the 'absolute autonomy' goal described
in #886 — process-level resilience for long-running sessions.
2026-03-17 09:35:33 -06:00
Lex Christopherson
d804c759bd 2.26.0 2026-03-17 09:15:58 -06:00
Lex Christopherson
8a566f85c8 docs: update changelog for v2.26.0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 09:15:43 -06:00
Tom Boucher
ea9c5f7ee2 fix: headless mode exits early on progress notifications containing 'complete' (#879) (#888)
isTerminalNotification() used broad substring matching against
['complete', 'stopped', 'blocked']. Any notification containing these
words triggered early exit — including progress messages like:
  'All slices are complete — nothing to discuss.'
  'Override(s) resolved — rewrite-docs completed.'
  'Skipped 5+ completed units. Yielding to UI before continuing.'

Fix: Replace substring matching with prefix matching against the actual
stop signals emitted by stopAuto():
  'Auto-mode stopped...'
  'Step-mode stopped...'

These are the ONLY notifications that indicate auto-mode has genuinely
terminated. All other notifications (slice completion, override
resolution, skip yielding) are progress events and must not trigger
exit.

Also tighten isBlockedNotification to match 'blocked:' (with colon)
instead of bare 'blocked' to avoid false positives from unrelated
messages.

Added 15 regression tests covering:
- All real terminal notification variants
- 6 false-positive cases from the issue report
- Non-notify event rejection
- Blocked detection with and without colon
2026-03-17 09:11:34 -06:00
Tom Boucher
3542b17c97 fix: normalize Windows paths in LLM-visible text to prevent bash failures (#874) (#884)
On Windows, process.cwd() returns backslash paths (C:\Users\name\...).
When these paths are injected into system prompts, worktree context
blocks, or tool results, the model copies them into bash commands.
Bash interprets backslashes as escape characters, silently stripping
them — producing invalid paths like 'C:Usersnamedevelopmentapp-name'.

This is not a regex hack — it's a proper cross-platform boundary:
- Filesystem operations (fs, path.join, spawn cwd) use native paths
  unchanged. Node handles both separators correctly for I/O.
- LLM-visible text (prompts, tool results, extension messages) uses
  toPosixPath() to normalize to forward slashes. C:/Users/name/...
  is valid in Git Bash, WSL bash, PowerShell, and Node.js.

Changes:

- utils/path-display.ts: New toPosixPath() utility in pi-coding-agent
  package (for system prompt) and shared extension module (for
  extensions that can't import from the compiled package at dev time)

- system-prompt.ts: Normalize resolvedCwd before injecting into the
  'Current working directory' line

- gsd/index.ts: Normalize all process.cwd() and originalBase paths in
  worktree context blocks injected into the system prompt

- bg-shell/index.ts: Normalize cwd in tool result text (start, env
  actions) that the model reads and may reference in commands

- path-display.test.ts: 9 regression tests covering toPosixPath
  behavior and system prompt output verification. Includes a scanner
  that fails if any Windows absolute paths with backslashes appear in
  buildSystemPrompt() output.

Audit scope: Checked all process.cwd() usage across pi-coding-agent
and all bundled extensions. Filesystem-only paths (join, readFile,
spawn cwd, existsSync) are correct and left unchanged. Only paths
entering LLM text are normalized.
2026-03-17 09:02:23 -06:00
Tom Boucher
7a26d27e94 feat: add model type, provider, and API docs fields to bug report template (#877) 2026-03-17 08:42:25 -06:00
Tom Boucher
7e1fcd3549 fix: don't trigger LLM turns on async_bash job completion (#875) (#880) 2026-03-17 08:30:13 -06:00
Tom Boucher
62bbaa8e8e feat: integrate hashline edit mode into active workflow (#870) (#872) 2026-03-17 08:23:53 -06:00
Jeremy McSpadden
a8eb66b8b3 feat: group /model selector by provider (#871) 2026-03-17 08:23:29 -06:00
Tom Boucher
ae8b8eeca3 fix: limit native web_search to max_uses:5 per response (#817) (#869) 2026-03-17 08:23:05 -06:00
Tom Boucher
4c7c64f7f5 fix: completed milestone with summary re-entered as active on resume (#864) (#868) 2026-03-17 08:22:16 -06:00
Gary Trakhman
9fe046d3fa Revert PR #744 - symlink-based development workflow (#867) 2026-03-17 08:21:55 -06:00