Port the create-agent-skills skill from ~/.claude/skills/ to bundled
resources with GSD-specific adaptations: dual directory support
(~/.gsd/agent/skills/ global, .pi/agent/skills/ project-local),
auto-discovery integration, /reload activation, and telemetry/health
references. 25 files: 1 router SKILL.md, 9 workflows, 13 references,
2 templates.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the flat model list with a two-step selection: pick provider
first, then pick a model within that provider. Models are sorted
alphabetically within each group. Adds a "(type manually)" escape
hatch for arbitrary model IDs.
Background processes spawned during one task (e.g., Vite dev servers
for browser-based verification) were not cleaned up when the unit
completed. The orphaned server kept the port bound, causing the next
unit's dev server launch to fail or conflict. This led to stuck-loop
anomalies, cost spikes from timeout recovery retries, and port conflicts.
Added killSessionProcesses() to bg-shell process-manager — kills all
alive, non-persistent processes using SIGTERM. Called in auto-post-unit
after pruneDeadProcesses(). Processes with persistAcrossSessions: true
are preserved.
Fixes#1209 (orphaned processes part; the subagent bundled-extension-paths
bug is already fixed on main since a2a701b1)
* refactor: replace MCPorter CLI with native MCP client using @modelcontextprotocol/sdk
MCPorter is a third-party global CLI that fails to install on many systems,
producing error noise on every startup. Replace it with a native extension
that uses the already-bundled @modelcontextprotocol/sdk Client class directly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update README extension table from MCPorter to MCP Client
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add .js suffix to MCP SDK subpath imports for NodeNext resolution
The SDK wildcard export (./*) requires .js suffix for TypeScript NodeNext
module resolution. Also add .js-suffixed virtual module keys so jiti
resolves them correctly in compiled Bun binaries.
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(dashboard): two-column layout with task checklist
Redesign the auto-mode progress widget to use the full terminal width
with a two-column layout:
Left column (~55%): task checklist with done/active/pending glyphs
Right column (~45%): progress bar, ETA, next step, token stats, model
Additional changes:
- Merge project name, slice, and action into a single context line
- Tighten spacing (single spaces, compact hint separator)
- Collapse 5 blank separator lines down to 2
- Cache task details (id, title, done) in slice progress cache
- Footer merges pwd and keybinding hints onto one line
* refactor(dashboard): swap columns — stats left, tasks right
Move progress/ETA/tokens/model to the left column (45%) and task
checklist to the right column (55%) for better visual scanning.
* feat(dashboard): fixed-width right column with narrow fallback
Peg the task checklist to a fixed 44-char right column so it stays
readable at any width. The left column (stats/progress) flexes to
fill remaining space. Below 80 cols, falls back to single-column
stacked layout.
Also adds scripts/preview-dashboard.ts — a visual test harness
that renders the widget with mock data at any terminal width:
npx tsx scripts/preview-dashboard.ts [width]
* refactor(dashboard): swap columns — tasks left, stats right
Move task checklist back to the left column (fixed 44 chars) and
progress/ETA/tokens/model to the right column (flexes to fill).
Narrow fallback (<80 cols) stacks tasks then progress inline.
* refactor(dashboard): stats left, tasks pegged right with growing gap
Both columns are fixed width (44 chars each). The gap between them
grows as the terminal widens, keeping the task checklist anchored to
the right edge. At narrow widths (<80), falls back to single-column
with stats then tasks stacked.
* refactor(dashboard): move task column to middle, adjacent to stats
Both columns are now fixed-width and adjacent (44 + 3 + 44 = 91 chars).
Empty space flows to the right instead of between columns. The layout
stays stable regardless of terminal width.
* refactor(dashboard): flex left column, fixed right with gap
Left column now flexes to fill available space — no more truncation
on wide terminals. Right column (task checklist) stays fixed at 44
chars with a 5-char gap before the divider. Min width for two-column
mode raised to 100.
* feat: add workflow templates — named workflow shapes for different types of work
Introduces `/gsd start <template>` and `/gsd templates` commands with 8 built-in
workflow templates: bugfix, small-feature, spike, hotfix, refactor, security-audit,
dep-upgrade, and full-project. Each template defines purpose-specific phases so
work gets the right level of ceremony instead of forcing everything through the
full milestone pipeline or /gsd quick.
Includes auto-detection from natural language, --dry-run preview, state tracking
for resume support, git branch management, and artifact directory organization.
* fix: guard workflow templates against concurrent auto-mode sessions
Block /gsd start when auto-mode is active to prevent git branch conflicts
and competing message dispatch. When auto-mode is paused, allow templates
to run with an informational notice.
* feat: add workflow resume and in-progress detection
- /gsd start resume — resumes the most recent in-progress workflow
- /gsd start (no args) — shows in-progress workflow if one exists
- STATE.json tracks artifactDir and completedAt for lifecycle management
- Scans .gsd/workflows/*/STATE.json to find unfinished workflows
* chore: remove copyright headers per project conventions
Eliminates repeated try/catch JSON file load/save boilerplate across three
modules by introducing loadJsonFile, loadJsonFileOrNull, and saveJsonFile
in a shared json-persistence.ts utility.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Delete thinking-widget.ts and progress-widget.ts (fully implemented
but never imported anywhere) and remove the buildDirName identity
function from paths.ts.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
initResources() only re-synced when the GSD version changed. This meant
same-version content fixes (e.g. the subagent bundled-extension-paths.js
import fix in a2a701b1) never reached ~/.gsd/agent/extensions/ because
the version-only check saw 2.28.0 == 2.28.0 and skipped the sync.
Add a lightweight content fingerprint (sha256 of file paths + sizes) to
the managed-resources.json manifest. On startup, if the version matches
but the fingerprint doesn't, resources are re-synced. This covers:
- npm link dev workflows where source changes without version bumps
- hotfixes within a release that change bundled extension content
- upgrades from manifests without contentHash (treated as stale)
Cost: ~1ms of stat calls on ~100 files — no file reads needed.
The milestone merge dispatcher in dispatchNextUnit had two 'else if'
blocks that matched when !isInAutoWorktree() && getIsolationMode() !== 'none'.
In worktree mode, if isInAutoWorktree() returned false (e.g., after cwd
was changed back to project root), the branch-mode fallback fired and
ran 'git checkout main' — which fails because main is already checked
out at the project root.
Changed the condition from 'getIsolationMode() !== "none"' to
'getIsolationMode() === "branch"' so the branch-mode merge path only
fires when the user explicitly configured branch isolation. Worktree
mode now correctly falls through without attempting an invalid checkout.
Both instances (all-complete path and milestone-transition path) are fixed.
Fixes#1179
When the verification gate auto-discovers commands from package.json
(typecheck, lint, test), failures on pre-existing errors create a doom
loop: execute → fail → auto-fix → still fails → retry exhausted → pause.
The agent can't fix pre-existing lint/test errors it didn't introduce.
Now, when discoverySource is 'package-json', gate failures are logged
as warnings and the task proceeds without triggering the retry loop.
Explicitly configured checks (via preferences or task plan verify field)
still trigger the full retry cycle.
This preserves the safety of user-configured verification while
preventing auto-discovered checks from blocking on inherited tech debt.
Fixes#1186
Node.js's cpSync fails on Windows when the path contains non-ASCII
characters (e.g. C:\Users\Görloff) due to the \\?\ extended-length path
prefix not handling Unicode correctly. This affects both the build
script (copy-assets.cjs) and the runtime resource sync (resource-loader.ts).
Added a try/catch fallback: when cpSync throws, fall back to a manual
recursive copy using copyFileSync which handles non-ASCII paths correctly.
Changed files:
- src/resource-loader.ts: syncResourceDir() catches cpSync failure and
falls back to copyDirRecursive()
- packages/pi-coding-agent/scripts/copy-assets.cjs: all cpSync calls
wrapped in safeCpSync() with the same fallback
Fixes#1178
* fix: make package-json discovered verification commands non-blocking (advisory only)
Auto-discovered commands from package.json scripts (typecheck, lint, test) are
advisory: their failures are logged as warnings but do not block the gate or
trigger retries. Only explicitly configured preference commands and task-plan
verify commands remain blocking.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add missing blocking field to verification-evidence test fixtures
The previous commit added `blocking: boolean` to VerificationCheck but
only updated verification-gate.test.ts. The evidence test file had 26
VerificationCheck literals missing the new required field.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auto-mode crashed with 'Cannot read properties of undefined (reading
filter)' during partial execute-task recovery when derived state was
structurally incomplete.
Added ?? [] fallback guards on all .filter()/.find()/.map() calls
that access state.registry, roadmap.slices, or similar derived arrays
in the dispatch and recovery paths:
- auto.ts: 3 state.registry.filter() calls
- auto-recovery.ts: 1 roadmap.slices.find() call
- auto-start.ts: 1 state.registry.filter() call
These are belt-and-suspenders guards — the parsers always return arrays,
but crash recovery can encounter partially written or corrupt state files
where the parsers return unexpected shapes.
Fixes#1176
syncStateToProjectRoot() copied STATE.md, milestone directories,
completed-units.json, and runtime records — but not the four root-level
living documents. When agents updated these during slice execution in a
worktree, a new session would read stale copies from the project root,
losing decisions, requirement status changes, project descriptions, and
accumulated knowledge.
Added bidirectional sync for DECISIONS.md, REQUIREMENTS.md, PROJECT.md,
and KNOWLEDGE.md:
- Worktree → project root: in syncStateToProjectRoot() after runtime records
- Project root → worktree: in syncProjectRootToWorktree() before milestone sync
Fixes#1168
When a milestone has CONTEXT-DRAFT.md (phase: needs-discussion), the
dispatch table returned 'stop' — which made auto-mode exit. Running
/gsd again would re-enter auto → dispatch → stop → loop indefinitely.
The guided-flow already has a complete interactive handler for
needs-discussion (discuss from draft / start fresh / skip), but it was
never reached from the auto-mode entry path.
Added an early check in dispatchNextUnit: if phase is needs-discussion,
stop auto-mode gracefully and route to showSmartEntry() which handles
the discussion flow correctly.
Fixes#1170
* Initial plan
* Fix validate-milestone skip loop: verify terminal verdict in artifact check
When verifyExpectedArtifact checked validate-milestone units, it only
verified the VALIDATION file existed on disk. But deriveState requires the
verdict to be terminal (pass/needs-attention/needs-remediation) before
advancing past validating-milestone. If the file existed with malformed
frontmatter or an unrecognized verdict, the artifact check passed (causing
skip) while deriveState stayed in validating-milestone, creating a hard
skip loop that hit the lifetime cap.
Now verifyExpectedArtifact reads the VALIDATION file content and calls
isValidationTerminal() to confirm the verdict matches what deriveState
expects. Non-terminal validations are treated as incomplete artifacts,
triggering re-run instead of skip.
Adds 3 new tests for the tightened verification.
Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
* Address review feedback: clarify comments and add unrecognized verdict test
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>
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>
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.
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.