The early TTY check blocked the resource-skew detection test which
runs gsd with piped stdin. Move exitIfManagedResourcesAreNewer()
before the TTY gate so version mismatch errors surface in non-TTY
environments.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The no-TTY check at the end of cli.ts ran after full session/extension
initialization, which opens handles that prevent process.exit(1) from
completing promptly (15s hang on Node 24). Move the check right after
arg parsing, before any heavy initialization.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The init command requires interactive/TTY mode which isn't available
in CI containers. Skip the test with a clear message when both
stdin is not a TTY and CI env var is set.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When running /gsd export --html, the generated report now automatically
opens in the user's default browser. Uses platform-specific commands
(open/xdg-open/start). Only applies to manual exports — auto-mode
milestone completion reports do not auto-open.
Multiple CI completions on the same commit trigger duplicate Pipeline
runs. The second run fails with E403 because the version was already
published. Fix by checking npm registry before attempting publish, and
enable cancel-in-progress to avoid redundant runs.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The local smoke test runs npx gsd-pi which fails in the container
because the gsd bin isn't on PATH. Point GSD_SMOKE_BINARY at the
built dist/loader.js directly with an absolute path so smoke tests
work from any cwd.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds OS-level exclusive session locking via proper-lockfile to prevent
multiple GSD auto-mode processes from running simultaneously on the
same project. Previously, the advisory JSON lock file had a TOCTOU race
condition where two processes could both read "no lock" before either
wrote one.
Changes:
- New session-lock.ts module with acquireSessionLock/releaseSessionLock/
validateSessionLock using proper-lockfile for OS-level file locking
- Lock acquired at the START of bootstrapAutoSession (before any state
mutation), not after initialization as before
- Periodic lock validation in dispatchNextUnit detects if another
process has taken over, triggering graceful shutdown
- Session lock released on both stop and pause
- Resume path re-acquires lock before reactivating
- DB module tracks owner PID for diagnostic purposes
- 16 new tests covering acquire/release/validate/lifecycle scenarios
When the last task in a slice completes, the doctor detects expected
completion-transition issues (missing slice summary, unchecked roadmap)
that will be resolved by the upcoming complete-slice dispatch. These
were being counted as real errors in the proactive health tracker,
inflating consecutiveErrorUnits and potentially triggering misleading
heal escalation or verification-failure warnings.
Changes:
- Export COMPLETION_TRANSITION_CODES from doctor-types.ts (was local
to doctor.ts)
- doctor.ts uses the shared constant instead of its local copy
- auto-post-unit.ts filters out completion-transition codes from the
error count and health snapshot when fixLevel is 'task'
Existing doctor-fixlevel tests confirm the doctor still detects and
reports (but does not fix) these issues at task level.
Fixes#1155
Adds a new /gsd logs command for browsing and inspecting GSD's existing
logging infrastructure. Users can now discover and review activity logs,
debug logs, and metrics without navigating the filesystem manually.
Subcommands:
/gsd logs — List recent activity + debug logs with metrics summary
/gsd logs <N> — Show summary of activity log #N (tool calls, files, errors)
/gsd logs debug — List debug log files
/gsd logs debug <N> — Show debug log summary (events, duration, errors)
/gsd logs tail [N] — Show last N activity log summaries (default 5)
/gsd logs clear — Remove old activity and debug logs (keeps recent 5)
Addresses #1161 — users needed a way to understand what happened during
auto-mode sessions for debugging.
When git.isolation is set to 'none' in preferences, /gsd quick now
stays on the current branch instead of creating a gsd/quick/<n>-<slug>
branch. The branch creation logic is skipped entirely, matching the
behavior users expect from isolation: none.
The 'branch' and 'worktree' modes continue to create branches as before.
Fixes#1153
* Initial plan
* fix: add text-based fallbacks for RPC mode where TUI widgets produce empty turns
- rpc-mode.ts: Emit placeholder widget event instead of silently dropping factory-based setWidget calls
- commands.ts: handleStatus() falls back to text-based status summary when custom() returns undefined
- commands.ts: handleVisualize() notifies that TUI is required when custom() returns undefined
- auto-dashboard.ts: updateProgressWidget() emits string-array fallback before factory widget
- queue-reorder-ui.ts: showQueueReorder() notifies with current order when custom() returns undefined
- index.ts: Dashboard shortcut handler falls back to text status in RPC mode
Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
headless-query.ts imported extension modules with .js extensions, but
those files only exist as .ts (never compiled). Other code paths work
because they go through the extension loader's jiti setup, but
headless-query bypasses that as a performance optimization.
Fix: use createJiti() to dynamically import the 4 extension modules,
matching the pattern used by the extension loader. The modules are
loaded lazily in handleQuery() so the jiti overhead only applies when
the query command is actually used.
Fixes#1137
Add environment variable overrides for screenshot capture settings so
users can opt into full-resolution output for human review while keeping
the Anthropic vision-optimized defaults:
- SCREENSHOT_MAX_WIDTH (default 1568, set 0 to uncap)
- SCREENSHOT_MAX_HEIGHT (default 8000, set 0 to uncap)
- SCREENSHOT_FORMAT (default jpeg for viewport / png for crops)
- SCREENSHOT_QUALITY (default 80, range 1-100)
Also fixes:
- Integration test viewport/scale mismatch: was 1280x720 scale 1,
now 1280x800 scale 2 to match production browser context
- Unit test height-limit assertion: test expected <= 1568 but
MAX_SCREENSHOT_HEIGHT is 8000 — corrected test image and assertions
* feat: add pre-commit secret scanner and CI secret detection
Add a comprehensive secret scanning system to prevent accidental
credential leaks in commits and pull requests:
- scripts/secret-scan.sh: ERE-based scanner (macOS/Linux compatible)
that detects AWS keys, API tokens, private keys, database URLs,
GitHub/GitLab/Slack/Stripe/Google/npm tokens, and hardcoded passwords
- scripts/install-hooks.sh: one-command git pre-commit hook installer
- .secretscanignore: allowlist for known false positives (test fixtures,
env var references, placeholder values)
- CI job: secret-scan step in ci.yml scans PR diffs against origin/main
- npm scripts: test:secret-scan, secret-scan, secret-scan:install-hook
- 17 tests covering detection, non-detection, binary skipping, CI mode
* fix: exclude secret-scan test file from CI scanning
The test file contains intentional fake secrets as test inputs.
Add it to .secretscanignore so CI doesn't flag them.
* fix: skip secret-scan tests on Windows (requires bash/POSIX grep)
* fix: pause auto-mode instead of blocking when env variables needed (#1146)
When gsd auto encounters pending secrets in the SECRETS.md manifest,
it now pauses the session with a clear notification listing the missing
keys, instead of blocking the entire auto loop with an interactive TUI
prompt. On resume (/gsd auto), secrets are re-collected via the TUI —
if all are skipped, the session re-pauses to prevent broken task runs.
* feat: notify remote channels (Slack/Discord/Telegram) on secrets pause
Sends a one-way notification to the configured remote channel when
auto-mode pauses for missing env variables. The notification directs
the user back to the terminal — secrets are never collected through
remote channels for security reasons.
Two bugs in the Dev Publish job:
1. node -p with escaped double-quotes broke on Node 22's eval mode
(SyntaxError: Invalid or unexpected token). Switched to node -e
with process.stdout.write and unescaped inner quotes.
2. version-stamp updated root package.json but not platform packages.
When npm publish triggered prepublishOnly, sync-platform-versions
dirtied 5 platform package.json files and git diff --exit-code
failed. Added sync-platform-versions to the stamp step so
prepublishOnly finds everything already in sync.
Fixes: https://github.com/gsd-build/gsd-2/actions/runs/23233857718
`remote-questions-config.ts` was extracted in #592 to avoid crossing
the compiled/uncompiled boundary. However, it still imported
`getGlobalGSDPreferencesPath` from `preferences.ts` via a `.js`
extension — which fails at runtime because `preferences.ts` is
loaded via jiti and never compiled to `.js` in dist/.
This caused remote questions setup (Telegram/Slack/Discord) to fail
during `gsd config` with:
Cannot find module '.../preferences.js' imported from
.../remote-questions-config.js
Fix: inline the path constant directly. It's a single `join()` call
with no logic, so duplicating it is cleaner than adding a build step
or creating a separate compiled module just for this one export.
* feat(S01/T01): Scaffolded the `studio` Electron workspace with a workin…
- package.json
- studio/package.json
- studio/electron.vite.config.ts
- studio/src/main/index.ts
- studio/src/preload/index.ts
- studio/src/renderer/src/styles/index.css
- studio/src/renderer/src/App.tsx
* chore: init gsd
* fix(ci): add safe.directory for containerized pipeline job
The Dev Publish job runs inside a Docker container where the checkout
user differs from the container user (root), causing git's dubious
ownership check to reject git operations in version-stamp.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): remove .gsd/.gitignore from tracking
The no-gsd-dir CI check fails when .gsd/ exists as a directory, even
if only .gitignore is tracked inside it.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add park/discard actions for in-progress milestones
Users could not discard, park, or skip milestones once work had begun.
The wizard only offered "Go auto" and "View status" for milestones with
a roadmap, trapping users with stale or deprioritized milestones.
This adds:
- Park mechanism: PARKED.md marker file in milestone directory.
deriveState() transparently skips parked milestones when finding the
active one. Parked milestones do NOT satisfy depends_on for downstream
milestones, preventing accidental unblocking.
- "Milestone actions" submenu in all four active-milestone wizard
branches (roadmap-exists, planning, summarizing, executing). Offers
Park / Discard / Skip / Back with clean navigation.
- /gsd park [id] and /gsd unpark [id] CLI subcommands for direct access.
- New module milestone-actions.ts with parkMilestone(), unparkMilestone(),
discardMilestone(), isParked(), getParkedReason() — keeps guided-flow
and commands thin.
- 14 tests (36 assertions) covering state derivation, dependency
semantics, park/unpark round-trip, discard with queue-order pruning,
and edge cases (all-parked, no-roadmap park, progress counts).
Files changed:
types.ts — Add 'parked' to MilestoneRegistryEntry.status
milestone-actions.ts — NEW: park/unpark/discard core logic
state.ts — Skip parked in getActiveMilestoneId + deriveState
guided-flow.ts — Milestone actions submenu in 4 wizard branches
commands.ts — /gsd park and /gsd unpark subcommands + help
guided-flow-queue.ts — Parked count in queue summary
visualizer-data.ts — Add 'parked' to VisualizerMilestone.status
park-milestone.test.ts — NEW: comprehensive test suite
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add edge case tests for park/discard milestone interactions
Covers 9 critical scenarios (31 assertions):
- Discard breaks depends_on chain → system correctly blocks
- Park blocks depends_on chain
- Queue order survives discards (QUEUE-ORDER.json pruned)
- Park all + discard all → clean pre-planning state
- Mixed states coexist (complete + parked + active + pending)
- Park then discard same milestone
- Discard milestone that has deps on others
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address critical review findings for park/discard feature
Fixes 7 issues found by adversarial code review:
1. CRITICAL: auto-mode crashed with "Unexpected: N incomplete" error when
all milestones were parked. Filter now excludes 'parked' status, and
pre-planning phase is recognized as a valid stop condition.
2. Merge-to-main was skipped when parked milestones existed — same
incomplete filter now excludes parked.
3. Completed milestones could be parked, corrupting depends_on
satisfaction. parkMilestone() now guards against SUMMARY.md existence.
4. Escape during park reason picker silently parked with literal
"not_yet" as reason. Now properly cancels the operation.
5. Parked milestones lost their human-readable title in registry
(showed ID instead). Phase 1 now caches roadmap for parked
milestones too, for title extraction.
6. GSD_MILESTONE_LOCK bypassed parked check — parallel workers locked
to a parked milestone now correctly return null.
7. Parked milestones were eligible for parallel execution, wasting
worker slots. parallel-eligibility now skips parked milestones.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: complete parked status display across all surfaces
- Visualizer: parked milestones show pause glyph (yellow) instead of
pending dot
- Doctor: parked milestones show pause emoji in registry report
- HTML export: add .dot-parked CSS (yellow), parked legend entry,
collapse parked milestone details by default
- Queue reorder: exclude parked milestones from movable list
Closes all remaining cosmetic findings from adversarial review.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(ci): add version stamp script for dev publishes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(ci): add CLI smoke tests for pipeline test stage
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(ci): add FixtureProvider for LLM conversation recording and replay
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(ci): add fixture test runner and sample recordings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(ci): add live test stubs and pipeline npm scripts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(ci): add three-stage promotion pipeline workflow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(ci): add weekly cleanup workflow for stale dev versions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(ci): add fixture recording helper stub
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
.gsd/ contains per-worktree project state (milestones, db, decisions)
that should never be committed. Auto-commits were leaking these files
into the repo, causing the no-gsd-dir CI check to fail.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- token-profile.test.ts: read preferences-types, preferences-models, and
preferences-validation alongside preferences.ts for structural checks
- triage-dispatch.test.ts: search auto-post-unit.ts for triage/dispatch
markers that moved during extraction, update comment markers to match
actual code
- none-mode-gates.test.ts: skip "no prefs default" test when global
preferences file exists (cannot control ~/.gsd/preferences.md)
- preferences.test.ts: skip getIsolationMode default test (same reason)
Reduces test failures from 48 to 3 (all pre-existing: doctor-git,
worktree-e2e, stopAutoRemote).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: strip model variant suffix for all auth methods, not just OAuth (#1097)
The model ID variant suffix (e.g., `[1m]` in `claude-opus-4-6[1m]`) was
only stripped for OAuth token auth. When using an API key, the suffix was
sent to the Anthropic API as-is, causing a 400 "upstream_error" because
`claude-opus-4-6[1m]` is not a valid API model ID.
The default Anthropic model is `claude-opus-4-6[1m]` (1M context variant),
so every API key user hits this on every request.
Fix: strip `[...]` suffix unconditionally for all auth methods.
* fix: update source-reading tests for post-refactor file locations
triage-dispatch.test.ts: read auto-post-unit.ts (dispatch logic moved
from auto.ts) and update comment string matches to reflect renamed
section headers.
token-profile.test.ts: read preferences-types.ts, preferences-validation.ts,
and preferences-models.ts (GSDPreferences interface and validation logic
split from preferences.ts).
* feat: cache-ordered prompt assembly and dashboard cache hit rate
Add prompt section reordering for better Anthropic cache hit rates.
Sections are classified as static/semi-static/dynamic and reordered
so stable content appears first in the prefix.
- prompt-ordering.ts: section extraction, classification, and
reordering by cache stability (static -> semi-static -> dynamic)
- auto.ts: wire reorderForCaching into dispatch with logged warnings
on failure (not silent catch)
- auto-dashboard.ts: show cache hit rate percentage in progress widget
- dashboard-overlay.ts: show aggregate cache hit rate in status overlay
- auto-prompts.ts: respect compression_strategy preference before
compressing carry-forward sections
Includes 12 tests for reorderForCaching and analyzeCacheEfficiency.
Split from #1083 per review feedback.
* fix: update source-reading tests for post-refactor file locations
triage-dispatch.test.ts: read auto-post-unit.ts (dispatch logic moved
from auto.ts) and update comment string matches to reflect renamed
section headers.
token-profile.test.ts: read preferences-types.ts, preferences-validation.ts,
and preferences-models.ts (GSDPreferences interface and validation logic
split from preferences.ts).
* feat: add comprehensive API key manager (/gsd keys)
Add /gsd keys command with 6 subcommands for full API key lifecycle
management: list, add, remove, test, rotate, and doctor.
- list/status: Dashboard grouped by category (LLM, search, tool, remote)
with masked key previews, OAuth expiry, env var source detection
- add: Interactive provider picker with OAuth vs API key choice,
prefix validation, and env var activation
- remove: Multi-key support with individual or bulk removal
- test: Lightweight API validation per provider with latency reporting
and error classification (401/429/5xx/timeout)
- rotate: Remove-and-replace flow with optional pre-save validation
- doctor: Health checks for expired OAuth, empty keys, duplicates,
env var conflicts, file permissions, missing LLM provider
Includes unified provider registry (22 providers), tab completions,
and redirect from /gsd setup keys. 44 unit tests.
* fix: convert key-manager tests from vitest to node:test for CI typecheck
Extension tests use node:test + node:assert/strict (not vitest) since
tsconfig.extensions.json includes test files and vitest types are not
available in the CI typecheck step.
Extract three modules from the 1,348-line doctor.ts god file:
- doctor-types.ts: DoctorSeverity, DoctorIssueCode, DoctorIssue, DoctorReport, DoctorSummary
- doctor-format.ts: summarizeDoctorIssues, filterDoctorIssues, formatDoctorReport, formatDoctorIssuesForPrompt
- doctor-checks.ts: checkGitHealth, checkRuntimeHealth
All public exports are re-exported from doctor.ts so existing imports
from "./doctor.js" continue to work unchanged.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>