Commit graph

558 commits

Author SHA1 Message Date
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
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
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
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
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
Dom Stepek
d2917f18b6 fix(gsd): support shift-tab in visualizer 2026-03-16 17:50:50 -04:00
Tom Boucher
ca299db1c6 feat(browser-tools): add 10 new browser tools (#698)
Implement all features from the browser-tools feature additions proposal:

1. browser_extract — structured data extraction with JSON Schema validation
2. browser_save_state / browser_restore_state — session state persistence
3. browser_generate_test — Playwright test code generation from session
4. browser_mock_route / browser_block_urls / browser_clear_routes — network interception
5. browser_emulate_device — device emulation with 143 Playwright device presets
6. browser_visual_diff — visual regression diffing with baseline management
7. browser_save_pdf — PDF generation (Chromium page.pdf)
8. browser_zoom_region — region capture with upscaling via sharp
9. browser_action_cache — intent→selector caching for repeat visits
10. browser_check_injection — prompt injection detection on page content

Total browser tools: 47 → 60. No new dependencies — uses existing
sharp, ajv, @sinclair/typebox, and Playwright core APIs.
2026-03-16 17:45:11 -04:00
Jeremy McSpadden
c46a4ec484 fix: execute capture resolutions after triage instead of just classifying
Captures classified as inject, replan, or quick-task were marked
"resolved" in CAPTURES.md but their resolution actions were never
executed — tasks were never injected into plans, replan triggers
were never written, and quick-tasks were never dispatched.

This wires up the existing resolution executor functions that were
defined but never called:

- After triage-captures unit completes, executeTriageResolutions()
  reads actionable captures and executes their resolutions:
  - inject: calls executeInject() to add tasks to the slice plan
  - replan: calls executeReplan() to write REPLAN-TRIGGER.md
  - quick-task: queues for dispatch as a new unit type

- Quick-task dispatch block dispatches queued captures one at a time
  using buildQuickTaskPrompt(), with proper session/timeout handling

- New markCaptureExecuted() and loadActionableCaptures() functions
  track execution state, preventing double-execution on retries

- Quick-task unit type excluded from post-unit hooks (lightweight
  one-offs don't need hook chains)

Closes #701
2026-03-16 16:28:39 -05:00
sgodoy90
72cef21876 feat: add gsd sessions subcommand for session picker
Add a new `gsd sessions` subcommand that lists all saved sessions for
the current directory and lets the user interactively pick one to resume.

Currently `gsd --continue` only resumes the most recent session, with no
way to access older conversations. This change adds:

- `gsd sessions` subcommand that calls SessionManager.list() to enumerate
  all sessions for the current working directory
- Interactive numbered list showing date, message count, session name (if
  set), and a preview of the first message
- Selection by number to resume any past session via SessionManager.open()
- Subcommand help text (`gsd sessions --help`)
- Help text entry in the main `gsd --help` output

The implementation uses only existing SessionManager APIs (list, open) -
no SDK changes required.
2026-03-16 15:27:10 -06:00
TÂCHES
da25c0b692 Merge pull request #703 from rangoc/fix/auto-mode-skill-loading
fix(prompts): make skill loading an active directive in auto-mode units
2026-03-16 15:22:50 -06:00
TÂCHES
915112ca1f Merge pull request #710 from jeremymcs/fix/707-execute-task-verification-budget
fix: pass verificationBudget to execute-task prompt template
2026-03-16 15:17:31 -06:00
TÂCHES
f550904724 Merge pull request #708 from jeremymcs/fix/gsd-cleanup-command
fix: handle bare /gsd cleanup command
2026-03-16 15:17:10 -06:00
TÂCHES
bbe665ac04 Merge pull request #702 from ryharrin/fix/gsd-bg-shell-stale-cwd
fix: stop bg-shell from persisting into stale auto-worktree paths
2026-03-16 15:16:38 -06:00
Jeremy McSpadden
b8e6294e6b fix: pass verificationBudget to execute-task prompt template
buildExecuteTaskPrompt() was missing the verificationBudget variable
that the execute-task.md template expects. The prompt-loader's strict
placeholder validator threw on every auto-mode task dispatch, blocking
all execution entirely.

Compute the budget from the executor's context window using the existing
computeBudgets() engine and pass it as ~NNK chars format string.

Fixes #707
2026-03-16 16:07:53 -05:00
Jeremy McSpadden
d19e213010 fix: handle bare /gsd cleanup command
Previously, running `/gsd cleanup` without a subcommand (branches or
snapshots) fell through to the unknown command handler, producing a
warning. Now bare `/gsd cleanup` runs both branch and snapshot cleanup.
2026-03-16 16:04:00 -05:00
Ryan Harrington
f87b4938ca fix/gsd-bg-shell-stale-cwd: normalize bg-shell worktree cwd detection 2026-03-16 17:02:58 -04:00
rangoc
b5ee1def82 fix(prompts): make skill loading an active directive in auto-mode units
The execute-task, plan-slice, and research-slice prompts all include a
passive instruction to 'use GSD Skill Preferences to decide which skills
to load.' In practice, auto-mode agents never act on this — across 30+
execution units in a real milestone, zero skill files were read.

The root cause is that the passive wording ('use it to decide') gets
overridden by the stronger 'don't re-research, just build what the plan
says' directive in execute-task. The agent treats skill loading as
optional and skips it 100% of the time.

This change rewrites the skill instruction in all three prompts from
passive guidance to an explicit action:
- execute-task: 'read its SKILL.md file now — before writing any code'
- plan-slice: 'read any skill files relevant to this slice's technology
  stack before decomposing'
- research-slice: 'read any skill files relevant to this slice's
  technology stack before exploring code'

The execute-task change also points agents to both the GSD Skill
Preferences block AND the <available_skills> catalog, since both are
present in the system prompt but the old instruction only referenced
the preferences block.

The plan-slice change adds guidance to note relevant skills in task
plans, so executors know which skills to load without rediscovering
them.
2026-03-16 21:48:31 +01:00
Ryan Harrington
8b8ba0d207 fix/gsd-bg-shell-stale-cwd: resync bg-shell cwd after auto-worktree exit 2026-03-16 16:45:21 -04:00
TÂCHES
2b0c0064cd Merge pull request #697 from gsd-build/feat/forensics 2026-03-16 14:41:03 -06:00
Lex Christopherson
76f73243eb fix: handle undefined return from ctx.ui.input in forensics
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 14:32:21 -06:00
TÂCHES
a5cf0f88b1 Merge pull request #696 from trek-e/fix/695-stray-worktree-detection
fix: validate auto-worktree is a real git worktree before use (#695)
2026-03-16 14:29:40 -06:00
TÂCHES
f73b17f55b Merge pull request #694 from gsd-build/fix/pr-673-security-fixes
fix: command injection and path traversal in PR #673
2026-03-16 14:29:19 -06:00
TÂCHES
966e5e80fb Merge pull request #673 from jeremymcs/feat/v2.20-phase2-3-features
feat: v2.20 Phase 2-4 — skills, integrations, MCP server
2026-03-16 14:29:07 -06:00
TÂCHES
b1b8a1f782 Merge pull request #693 from trek-e/fix/692-mcp-json-project-root
fix: discover MCP servers from project-root .mcp.json (#692)
2026-03-16 14:28:30 -06:00
Lex Christopherson
14f8135972 feat: add /gsd forensics subcommand for auto-mode failure investigation
Scans activity logs, metrics, crash locks, and doctor diagnostics for
anomalies, generates a structured forensic report, saves it locally,
and hands it to the LLM for interactive root-cause analysis with
optional GitHub issue creation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 14:25:23 -06:00
Tom Boucher
e9a2928ce7 fix: validate auto-worktree is a real git worktree before use (#695)
getAutoWorktreePath() only checked existsSync() on the worktree
directory, treating any directory under .gsd/worktrees/<MID>/ as a
valid auto-worktree. A stray (non-git) directory would be accepted,
causing auto-mode to derive state from an empty/invalid path and
conclude no milestones exist.

Add git worktree validation to both getAutoWorktreePath() and
enterAutoWorktree(): check that the directory contains a .git file
(not directory) with a 'gitdir:' pointer, which is the hallmark of
a real git worktree checkout. Return null / throw if validation fails.

This ensures stray directories are ignored and auto-mode falls through
to normal worktree creation or root-state derivation.

Closes #695
2026-03-16 16:24:19 -04:00
Tom Boucher
7b11faa150 fix: discover MCP servers from project-root .mcp.json (#692)
The mcporter extension only discovered servers that the mcporter CLI
itself knew about (via .vscode/mcp.json, Claude Desktop config, etc.).
Servers configured in the standard .mcp.json at the project root —
used by Claude Code, Cursor, and other AI coding tools — were invisible.

Changes:

1. mcporter extension (index.ts):
   - Add readProjectMcpJson() that reads .mcp.json from cwd and returns
     servers not already discovered by mcporter
   - Merge .mcp.json servers into getServerList() results
   - Add getMcpJsonServerUrl() to resolve HTTP URLs for .mcp.json servers
   - Update getServerDetail() to pass HTTP URLs directly to mcporter
     for servers only known via .mcp.json
   - Update mcp_call to use HTTP URL as server reference for .mcp.json
     servers

2. discover_configs scanner (scanners.ts):
   - Add .mcp.json to the project-level MCP config scan path alongside
     .claude/.mcp.json and .claude/mcp.json

Closes #692
2026-03-16 16:15:02 -04:00
Lex Christopherson
d87a4423b0 fix: eliminate command injection surface in diff-context, harden file-watcher path resolution
Use execFileSync with argument arrays instead of execSync with string
interpolation to prevent shell injection via sinceDays parameter.
Validate sinceDays as a positive integer. Replace string-based path
resolution in file-watcher with path.relative() to prevent traversal
via symlinks or .. segments.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 14:10:29 -06:00
Jeremy McSpadden
a01df1f110 fix: coerce completedAt to String in visualizer changelog sort
YAML frontmatter parsers can return Date objects for ISO date strings
instead of plain strings. This caused a TypeError when calling
.localeCompare() on a Date object in the changelog sort.

Wrap completedAt with String() at both assignment and sort to handle
both native and JS parser paths safely.
2026-03-16 14:59:29 -05:00
TÂCHES
72223d0a7a Merge pull request #685 from gsd-build/fix/643-warp-unsupported-shortcuts
fix: add Warp to unsupported Ctrl+Alt shortcut list
2026-03-16 13:43:40 -06:00