Commit graph

998 commits

Author SHA1 Message Date
Lex Christopherson
d91690bb44 2.23.0 2026-03-16 18:57:13 -06:00
Lex Christopherson
50d6a52a2a docs: update changelog for v2.23.0 2026-03-16 18:57:02 -06:00
TÂCHES
5b3d9fff17 Merge pull request #713 from frizynn/feat/gsd-headless-command
feat: redesign gsd headless for full workflow orchestration
2026-03-16 18:49:05 -06:00
TÂCHES
e0c1cc2f9d Merge branch 'main' into feat/gsd-headless-command 2026-03-16 18:44:18 -06:00
TÂCHES
889a2ee137 Merge pull request #755 from jeremymcs/feat/vscode-marketplace
feat(vscode): marketplace-ready files for VS Code extension publishing
2026-03-16 18:43:31 -06:00
TÂCHES
1e951f9648 Merge pull request #718 from jeremymcs/fix/682-vscode-extension-rebase
feat: VS Code extension — rebased with CI + review fixes (#682)
2026-03-16 18:42:30 -06:00
TÂCHES
4d78620ff1 Merge pull request #754 from jeremymcs/fix/forensics-version-loading
fix(forensics): use GSD_VERSION env var instead of package.json path traversal
2026-03-16 18:42:09 -06:00
TÂCHES
08a34abb08 Merge pull request #753 from jeremymcs/docs/update-all-recent-changes
docs: update documentation for all post-2.22.0 changes
2026-03-16 18:41:51 -06:00
TÂCHES
0f13a8d59c Merge pull request #752 from jeremymcs/feat/688-guided-discuss-milestone
feat(discuss): structured question rounds in guided-discuss-milestone (#688)
2026-03-16 18:41:37 -06:00
TÂCHES
f4f998efc5 Merge pull request #747 from trek-e/fix/733-bash-ampersand-hang
fix: add anti-pattern rule against bash with & to prevent agent hangs (#733)
2026-03-16 18:38:40 -06:00
TÂCHES
8ca9725bb0 Merge pull request #745 from jeremymcs/fix/737-dependency-range-expansion
fix(roadmap): expand range syntax in depends (S01-S04 → S01,S02,S03,S04)
2026-03-16 18:37:53 -06:00
TÂCHES
10f4ac0817 Merge pull request #743 from gtrak/feat/models-json-resolver-v2
feat: Add models.json resolution with fallback to ~/.pi/agent/models.json
2026-03-16 18:37:04 -06:00
TÂCHES
a129e15759 Merge pull request #738 from jeremymcs/fix/733-background-command-hang
fix: prevent indefinite hang when LLM uses bare & to background processes (#733)
2026-03-16 18:36:29 -06:00
TÂCHES
3354f6300c Merge pull request #735 from trek-e/fix/699-plan-slice-empty-scaffold
fix: reject empty scaffold plan files in plan-slice artifact verification (#699)
2026-03-16 18:35:38 -06:00
TÂCHES
4df08ad935 Merge pull request #734 from jeremymcs/fix/728-skip-loop-breaker
fix(auto): break infinite skip loop on repeatedly-skipped completed units
2026-03-16 18:35:04 -06:00
TÂCHES
d8ebe1300a Merge pull request #729 from jeremymcs/fix/724-forensics-worktree-awareness
fix: make forensics worktree-aware to prevent stale root misdiagnosis (#724)
2026-03-16 18:34:46 -06:00
TÂCHES
f9c356bfba Merge pull request #730 from gsd-build/feat/validate-milestone-prompt
feat(gsd): add validate-milestone prompt and template
2026-03-16 18:34:29 -06:00
Jeremy McSpadden
02e3c441cc feat(vscode): enhance chat participant UX
- Auto-start agent when not connected instead of showing error
- Remove noisy tool-completion markdown spam (was printing *Tool X completed* for every call)
- Inject #file references from chat into the prompt automatically
- Add clickable file anchors for files written/edited during the session
- Add follow-up suggestions: /gsd status, /gsd auto, /gsd capture
- Improve tool progress labels (WebSearch, WebFetch, cleaner paths)
- Better error message when agent fails to start
2026-03-16 19:22:15 -05:00
Jeremy McSpadden
8db680c405 feat(vscode): set logo.jpg as extension marketplace icon 2026-03-16 19:16:29 -05:00
Jeremy McSpadden
c465bf7d18 fix(vscode): set publisher to FluxLabs to match marketplace account 2026-03-16 19:14:36 -05:00
Jeremy McSpadden
3f88619fac refactor(vscode): rename extension to GSD-2 2026-03-16 19:12:13 -05:00
Jeremy McSpadden
6e90e8d83b perf: optimize bg-shell hot path, parallel git queries, lazy workspace validation
- bg-shell/types: add compiled union regexes (ERROR/WARNING/READINESS/BUILD/TEST)
  built once at module load; add LINE_DEDUP_MAX constant (500); add
  stdoutLineCount/stderrLineCount tracked fields to BgProcess; export
  PORT_PATTERN_SOURCE string to avoid .source access per line

- bg-shell/output-formatter: analyzeLine uses union regexes instead of
  .some(p => p.test(line)) across 5 pattern arrays; PORT_PATTERN no longer
  reconstructed via new RegExp() on every line; lineDedup Map now has LRU
  eviction at LINE_DEDUP_MAX entries (prevents unbounded memory growth on
  long-running processes); getHighlights also uses union regexes

- bg-shell/process-manager: addOutputLine increments stdoutLineCount/
  stderrLineCount in O(1) as lines arrive; getInfo uses tracked counters
  instead of two O(n) .filter() passes over the output buffer

- gsd/diff-context: replace execFileSync with async execFile wrapper;
  getRecentlyChangedFiles and getChangedFilesWithContext now run all
  independent git queries concurrently via Promise.all (3-5 serial
  subprocess spawns -> 1 parallel batch)

- gsd/workspace-index: per-slice indexing now runs concurrently via
  Promise.all within each milestone; add IndexWorkspaceOptions with
  validate flag (default false) — validatePlanBoundary/validateCompleteBoundary
  skipped by default since they do expensive content analysis and are only
  needed for explicit doctor/audit flows; getSuggestedNextCommands passes
  validate:true as the sole consumer of validationIssues
2026-03-16 19:11:43 -05:00
Jeremy McSpadden
2df27c5179 feat(vscode): add marketplace-ready files for VS Code extension publishing
Adds everything needed to publish the extension to the VS Code Marketplace:

- README.md — full feature documentation with commands table, keyboard
  shortcuts, configuration reference, quick start guide, and @gsd chat
  participant usage
- CHANGELOG.md — initial 0.1.0 release notes
- .vscodeignore — excludes src/, tsconfig, maps from the .vsix package
- .gitignore — excludes dist/ and *.vsix from version control
- LICENSE — MIT license copied from repo root
- package.json — adds repository, homepage, bugs, keywords, galleryBanner
  fields required by the marketplace; adds @vscode/vsce to devDependencies;
  adds publish script

Verified: `npm run package` produces a clean 30KB .vsix with no warnings.
Run `npm run publish` with a VSCE_PAT token to publish.
2026-03-16 19:09:28 -05:00
Jeremy McSpadden
359deb6c23 fix(forensics): use GSD_VERSION env var instead of package.json path traversal
Extensions run from ~/.gsd/agent/extensions/gsd/ at runtime, not from the
package install directory. The previous code traversed 4 levels up from
import.meta.url to find package.json, which resolves to ~/package.json at
runtime — wrong on every system.

The loader already sets process.env.GSD_VERSION at startup, which is how
every other extension reads the version. Use that instead.
2026-03-16 18:54:30 -05:00
Jeremy McSpadden
6fd87c1936 docs: update documentation for all post-2.22.0 changes
- CHANGELOG: fill in [Unreleased] with gsd sessions, 10 new browser
  tools, visualizer shift-tab fix, capture resolution fix, screenshot
  constraint fix, auto.lock fix, and cross-platform test fix
- README: add gsd sessions to CLI reference table; expand Browser Tools
  description to cover the 13 new tools shipped in #698
- docs/commands.md: add gsd sessions to CLI Flags table
- docs/getting-started.md: document gsd sessions in Resume a Session
- docs/proposals/698: mark status as Shipped, update Current State
  section to reflect the 13 implemented tools
2026-03-16 18:48:52 -05:00
Jeremy McSpadden
67847a6547 fix(ci): use pi.getActiveTools() instead of ctx.getActiveTools()
ExtensionContext in the published package does not have getActiveTools —
it lives on ExtensionAPI (pi). The local source has it on both but CI
typechecks against the installed package, which failed with:

  Property 'getActiveTools' does not exist on type 'ExtensionCommandContext'
2026-03-16 18:44:21 -05:00
Jeremy McSpadden
18aa6b1084 feat(discuss): structured ask_user_questions rounds in guided-discuss-milestone (#688)
guided-discuss-milestone.md was a single-paragraph stub — the agent had
no interview protocol, no check-in round, no depth verification, and no
host-conditional behaviour. On Copilot this meant every clarification
burned a separate request with no structure.

Changes:

- guided-discuss-milestone.md: full interview protocol matching
  guided-discuss-slice structure:
  - mandatory investigation pass before first round
  - 1–3 questions per round
  - check-in after each round (wrap up vs keep going)
  - depth verification checklist before wrap-up
  - host-conditional: uses ask_user_questions when available (pi),
    falls back to plain text when not (Copilot, Cursor, Windsurf)
  - depth_verification question ID convention preserved for the
    write-gate in index.ts

- guided-flow.ts: all 5 loadPrompt('guided-discuss-milestone') call
  sites now pass structuredQuestionsAvailable by checking
  ctx.getActiveTools().includes('ask_user_questions') at dispatch time.
  Returns 'true'/'false' string so the prompt can branch conditionally.
2026-03-16 18:39:31 -05:00
Tom Boucher
75a5dd08ad fix: add anti-pattern rule against bash with & to prevent agent hangs (#733)
The bash tool waits for stdout/stderr file descriptors to close. When the
LLM runs 'python -m http.server 8080 &', the backgrounded process inherits
stdout and keeps it open — the bash call hangs indefinitely.

The bg_shell tool exists for exactly this purpose (detached process groups,
readiness detection, lifecycle management). The system prompt already said
to use bg_shell for servers but didn't explicitly warn against bash with &.

Added:
- Explicit anti-pattern: 'Never use bash with & to background a process'
- Expanded background processes section explaining why & hangs
- Both reference bg_shell start as the correct alternative
2026-03-16 19:20:09 -04:00
Jeremy McSpadden
4774a1df22 fix(roadmap): expand range syntax in depends (S01-S04 → S01,S02,S03,S04)
LLMs frequently write depends:[S01-S04] as natural shorthand.
The parser split only on commas, so this produced a single literal
element "S01-S04" that never matched any real slice ID —
permanently blocking the slice with "No slice eligible".

Changes:

roadmap-slices.ts:
- Add expandDependencies() helper — after comma-split, detect dep
  tokens matching /^PrefixN(-|..)PrefixM$/ and expand to individual
  IDs. Handles S01-S04 (dash range) and S01..S04 (dot-range).
  Zero-padding preserved. Mismatched prefixes and reversed ranges
  pass through unchanged.
- Wire into parseRoadmapSlices() after the comma-split step.
- Export for direct testing.

doctor.ts:
- Add "unresolvable_dependency" warning code.
- In the slice audit loop, check each dep against the set of known
  slice IDs in the roadmap. Fires a warning with the bad dep name
  and the correct format hint. Catches leftover range IDs on roadmaps
  that were written before this fix, and catches typos.

plan-milestone.md prompt:
- Add explicit rule: use comma-separated depends:[S01,S02,S03], never
  range syntax. Defense-in-depth so LLMs don't generate the problem.

Tests:
- roadmap-slices.test.ts: 10 new expandDependencies cases + 2
  parseRoadmapSlices integration cases (range + comma round-trip).
- doctor.test.ts: unresolvable_dependency fires for unknown dep S99,
  does not fire for valid S01 dep.

952/952 unit tests pass.
Closes #737
2026-03-16 18:09:06 -05:00
Tom Boucher
2756428e6e fix: reject empty scaffold plan files in plan-slice artifact verification (#699)
verifyExpectedArtifact() for plan-slice units only checked whether the
plan file existed on disk, not whether it contained actual task entries.
When a plan file was created as an empty scaffold during discussion/context
(headings but no tasks), the artifact check considered it 'complete' and
skipped the dispatch. Since deriveState still returned phase:'planning'
(no tasks found), this created an infinite skip loop until auto-mode
exhausted its retry budget and stopped silently.

Added a content check that requires at least one task entry matching
the pattern '- [ ] **T##:' or '- [x] **T##:' before considering a
plan-slice artifact valid. This mirrors the existing content-aware
check used for execute-task (which verifies checkbox state).

Added 3 regression tests covering empty scaffold, valid tasks, and
completed tasks.
2026-03-16 19:07:37 -04:00
Gary Trakhman
49c7c0d540 feat: Add models.json resolution with fallback to ~/.pi/agent/models.json
- Create src/models-resolver.ts with resolveModelsJsonPath() function
- Fallback chain: ~/.gsd/agent/models.json → ~/.pi/agent/models.json → create new
- Integrate into cli.ts ModelRegistry initialization
- Provides smooth migration path for users with existing pi-coding-agent config
2026-03-16 23:05:59 +00:00
TÂCHES
d10412bb1e Merge pull request #727 from jeremymcs/fix/723-auto-lock-creation 2026-03-16 17:04:39 -06:00
Jeremy McSpadden
ae4ae8e8d8 fix(auto): add stalled-tool detection and background process prompt guidance
Two additional layers to address #733 (background command hang):

1. Stalled-tool detection in idle watchdog (auto.ts)
   - Change inFlightTools from Set<string> to Map<string, number> to
     track per-tool start timestamps
   - Idle watchdog now compares the oldest in-flight tool's age to the
     idle timeout. Tools in-flight for < idleTimeoutMs continue to
     suppress recovery as before. Tools running >= idleTimeoutMs are
     treated as stuck and recovery proceeds — preventing infinite hang
     when the bash rewrite is bypassed or a tool hangs for other reasons.
   - Export getOldestInFlightToolAgeMs() for testability

2. Prompt guidance in execute-task.md
   - Add explicit "Background process rule" to step 5 explaining why
     bare `command &` hangs the Bash tool and showing the correct
     `command > /dev/null 2>&1 &` pattern
   - Recommends bg_shell tool as the preferred approach

3. Test updates (in-flight-tool-tracking.test.ts)
   - Import and verify getOldestInFlightToolAgeMs export
   - Update header comment to reflect Map-with-timestamps design
2026-03-16 18:04:27 -05:00
Jeremy McSpadden
a3ff25c668 fix(bash): rewrite background commands to prevent pipe-open hang
Root cause: when the LLM runs `cmd &`, bash forks the process and
exits immediately. The forked process inherits Node's piped stdout/
stderr FDs. Node.js waits for all holders of those FDs to close before
firing the 'close' event — so the tool hangs until the background
process exits (which for a server is never).

Fix: add rewriteBackgroundCommand() in bash.ts. Before exec, detect
commands with a trailing & background operator and inject
>/dev/null 2>&1 before the & when stdout is not already redirected.
This severs the pipe inheritance so Node gets 'close' immediately
when the shell exits.

Guards:
- Commands already redirecting stdout (>, >>, &>, |) are not rewritten
- && (logical AND) is not affected
- & inside single-quoted strings is not affected
- A brief onUpdate advisory is surfaced when rewrite happens so the
  LLM knows to prefer nohup/setsid for robust detachment

Export rewriteBackgroundCommand from pi-coding-agent for testability.

Tests: bash-background.test.ts — 12 cases covering no-op paths,
rewrite paths, compound commands, and already-safe nohup patterns.
Closes #733
2026-03-16 18:03:01 -05:00
Jeremy McSpadden
742cd70c9b fix(auto): break infinite skip loop on repeatedly-skipped completed units
When deriveState() keeps returning the same already-completed unit,
the idempotency skip paths in dispatchNextUnit recursively call
themselves forever. The existing MAX_SKIP_DEPTH (20) breaker yields
to the UI but then re-enters the same loop; the hard lifetime counter
(unitLifetimeDispatches) is never reached because skip paths return
before touching it.

Root cause: no per-unit counter on the skip-only path.

Fix:
- Add unitConsecutiveSkips map + MAX_CONSECUTIVE_SKIPS = 3
- Both skip paths (completedKeySet hit, and fallback artifact-exists)
  increment the counter on each skip of the same idempotencyKey
- When the counter exceeds MAX_CONSECUTIVE_SKIPS, evict the key from
  completedKeySet and persisted storage, invalidate state, and let
  deriveState reconcile on the next real dispatch
- Counter resets to 0 for a given key whenever a real dispatch
  proceeds (i.e., past both skip paths)
- Counter fully cleared at all 4 existing clear sites (stopAuto,
  startAuto, crash recovery, pause/resume)

Export _getUnitConsecutiveSkips / _resetUnitConsecutiveSkips /
MAX_CONSECUTIVE_SKIPS for testability (same pattern as
doctor-proactive.ts resetProactiveHealing).

Tests: auto-skip-loop.test.ts — counter mechanics, threshold bounds,
eviction round-trip, per-key isolation (10 assertions).
Closes #728
2026-03-16 17:48:39 -05:00
frizynn
f56b8c69f0 fix: simplify headless flags, add missing imports, document headless mode
- Remove --verbose flag from headless (use --json for detailed output)
- Remove redundant sawToolExecution state variable
- Remove unused rejectCompletion
- Add missing build*Prompt imports in auto.ts (fixes CI typecheck:extensions)
- Document headless mode in README.md and docs/commands.md
- Simplify help text with examples instead of exhaustive command catalog
2026-03-16 19:46:56 -03:00
frizynn
8ddea154e5 feat: redesign gsd headless for full workflow orchestration
Replace --step flag with positional command routing so any /gsd
subcommand can run headlessly. Add /gsd dispatch <phase> for direct
unit-type dispatch (research, plan, execute, complete, reassess, uat,
replan) with state-aware resolution.

Quick commands (status, queue, doctor, etc.) resolve on first agent_end.
Long-running commands (auto, next, dispatch) use idle timer + terminal
notification detection.
2026-03-16 19:45:39 -03:00
frizynn
93ee6646f1 test: add integration test for gsd headless command
End-to-end test that validates the headless CLI subcommand by:
- Creating a temp dir with a complete .gsd/ project fixture
- Spawning `node dist/loader.js headless --step --json`
- Validating exit code, JSONL stdout, stderr progress, and artifact

Supports --dry-run for fixture validation without running the agent.
2026-03-16 19:45:39 -03:00
frizynn
b09e2a549c feat: add gsd headless CLI subcommand for non-interactive auto-mode
Adds a first-class `gsd headless` command that runs auto-mode without a
TUI by spawning a child process in RPC mode via RpcClient. Useful for
CI/CD pipelines, scripts, and unattended execution.

CLI interface:
  gsd headless                  - Run auto-mode until complete
  gsd headless --step           - Run one unit only (sends /gsd next)
  gsd headless --timeout 300000 - Custom timeout (default 5 min)
  gsd headless --json           - Forward RPC events as JSONL to stdout
  gsd headless --verbose        - Show full agent text and tool results
  gsd headless --model <id>     - Override model

Exit codes: 0 = complete, 1 = error/timeout, 2 = blocked

Features:
- Extension UI auto-responder (handles select, confirm, input, editor,
  notify, setStatus, setWidget, setTitle, set_editor_text)
- Completion detection via terminal notification keywords + idle timeout
- Human-readable progress output to stderr
- SIGINT/SIGTERM forwarding for clean shutdown
- Child process crash detection
- Completion summary with diagnostics on failure
2026-03-16 19:45:39 -03:00
Jeremy McSpadden
1871da1fb3 fix: use process.ppid instead of PID 1 for cross-platform test
PID 1 (init) exists on Unix but not on Windows, causing the
cross-process detection test to fail in CI. Use process.ppid
(parent process) which is guaranteed alive on all platforms.
2026-03-16 17:41:36 -05:00
Lex Christopherson
138a13b620 feat(gsd): add validate-milestone prompt and template
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 16:39:06 -06:00
Jeremy McSpadden
b19357dc84 fix: make forensics worktree-aware to prevent stale root misdiagnosis
When auto-mode runs in an auto-worktree, activity logs are written to
`.gsd/worktrees/<MID>/.gsd/activity/` while forensics only scanned
`.gsd/activity/` at the project root. This caused forensics to report
stale failures from the root while the worktree had already produced
the correct artifacts and advanced to execution.

Changes:

forensics.ts:
- scanActivityLogs() now accepts activeMilestone and scans both the
  worktree activity dir (if an auto-worktree exists) and the root dir
- Results are merged and sorted by mtime so the most recent traces
  from either source appear first
- detectMissingArtifacts() checks both root and worktree paths before
  reporting a missing artifact, preventing false positives
- ForensicReport now includes activeWorktree field for visibility
- Saved report and prompt output include worktree context

session-forensics.ts:
- getDeepDiagnostic() now checks the worktree activity dir first by
  reading the active milestone ID from STATE.md (synchronous, no
  async deriveState dependency)
- Falls back to root activity dir when no worktree is found
- Added readActiveMilestoneId() helper for sync milestone detection

Closes #724
2026-03-16 17:38:07 -05:00
TÂCHES
1a85853fd8 Merge pull request #725 from gsd-build/fix/screenshot-squish-constraint
fix: prevent full-page screenshots from being squished
2026-03-16 16:37:43 -06:00
TÂCHES
b0e28641b9 Merge pull request #721 from sgodoy90/feature/session-picker
feat: add `gsd sessions` subcommand for session picker
2026-03-16 16:37:32 -06:00
Jeremy McSpadden
def96a1b6e fix: write auto.lock at startup and detect remote sessions in dashboard (#723)
Three bugs caused /gsd status to show "No unit running" while auto mode
was actively executing in another terminal:

1. auto.lock was only written during unit dispatch (after newSession()),
   not at auto-mode startup or resume. Any cross-process check between
   startup and first dispatch would find no lock file.

2. The dashboard read only the in-memory `active` flag, which is always
   false in a different process. It never checked auto.lock for
   cross-process detection.

3. The triage dispatch path wrote the lock to `basePath` (worktree)
   instead of `lockBase()` (project root), making it invisible to
   other terminals checking the project root.

Changes:
- Write initial auto.lock immediately in startAuto() and on resume
- Add cross-process detection in getAutoDashboardData() via auto.lock
- Add remoteSession field to AutoDashboardData for cross-process info
- Update dashboard overlay to show remote session status and unit info
- Fix triage dispatch to use lockBase() instead of basePath
- Add 11 tests covering lock creation, cross-process detection, and
  stale lock handling
2026-03-16 17:36:04 -05:00
Jeremy McSpadden
add9e8cf3c fix: address PR review — CSP nonce, dead branch, restart cooldown
1. Webview CSP nonce (security): Added Content-Security-Policy meta tag
   with nonce-based script-src to sidebar.ts. Replaced all inline
   onclick handlers with data-command attributes and a single delegated
   event listener, which CSP requires over inline handlers.

2. Dead branch in chat-participant.ts: Removed the isSlashCommand
   conditional that ran identical code for both paths — slash commands
   and regular messages both call sendPrompt() the same way.

3. Restart loop cooldown in gsd-client.ts: Added a 60-second sliding
   window that tracks crash timestamps. If the process crashes more
   than 3 times within 60 seconds, auto-restart is disabled and an
   error is surfaced to the user via the onError event emitter.
2026-03-16 17:28:32 -05:00
Lex Christopherson
5ae08c4ec5 fix: use independent width/height caps for screenshot constraining
Full-page screenshots were being squished into a 1568x1568 square,
making tall pages unreadable. Now caps width at 1568px and height
at 8000px independently, preserving readability for long pages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 16:23:53 -06:00
TÂCHES
51cf029c96 Merge pull request #717 from domstepek/fix/visualizer-shift-tab
fix(gsd): support Shift+Tab in visualizer
2026-03-16 16:07:40 -06:00
TÂCHES
73b7b0d540 Merge pull request #714 from jeremymcs/fix/701-capture-resolution-execution
fix: execute capture resolutions after triage (#701)
2026-03-16 16:07:08 -06:00
TÂCHES
e185b9e263 Merge pull request #715 from trek-e/docs/698-browser-tools-requirements
feat(browser-tools): add 10 new browser tools (#698)
2026-03-16 16:03:52 -06:00