gsd-pi 2.65.0-dev.5c8557b → 2.65.0-dev.d0517ff
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mcp-server.js +6 -2
- package/dist/resources/extensions/browser-tools/capture.js +20 -1
- package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
- package/dist/resources/extensions/gsd/auto/run-unit.js +13 -2
- package/dist/resources/extensions/gsd/auto/session.js +4 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +99 -9
- package/dist/resources/extensions/gsd/auto-model-selection.js +7 -5
- package/dist/resources/extensions/gsd/auto-post-unit.js +17 -6
- package/dist/resources/extensions/gsd/auto-prompts.js +24 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +40 -22
- package/dist/resources/extensions/gsd/auto-start.js +42 -11
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +10 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +29 -7
- package/dist/resources/extensions/gsd/auto.js +21 -15
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -4
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +10 -0
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +6 -4
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -3
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +31 -1
- package/dist/resources/extensions/gsd/commands/context.js +8 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +20 -0
- package/dist/resources/extensions/gsd/commands-extensions.js +1 -1
- package/dist/resources/extensions/gsd/config-overlay.js +312 -0
- package/dist/resources/extensions/gsd/db-writer.js +13 -3
- package/dist/resources/extensions/gsd/detection.js +1 -1
- package/dist/resources/extensions/gsd/dispatch-guard.js +2 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -0
- package/dist/resources/extensions/gsd/doctor.js +2 -1
- package/dist/resources/extensions/gsd/gitignore.js +1 -0
- package/dist/resources/extensions/gsd/gsd-db.js +47 -4
- package/dist/resources/extensions/gsd/guided-flow.js +220 -29
- package/dist/resources/extensions/gsd/index.js +1 -1
- package/dist/resources/extensions/gsd/json-persistence.js +5 -2
- package/dist/resources/extensions/gsd/md-importer.js +14 -7
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +17 -11
- package/dist/resources/extensions/gsd/pre-execution-checks.js +12 -5
- package/dist/resources/extensions/gsd/preferences-types.js +3 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +45 -1
- package/dist/resources/extensions/gsd/preferences.js +9 -2
- package/dist/resources/extensions/gsd/preparation.js +1092 -0
- package/dist/resources/extensions/gsd/prompt-validation.js +67 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
- package/dist/resources/extensions/gsd/prompts/discuss.md +2 -0
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
- package/dist/resources/extensions/gsd/prompts/queue.md +2 -0
- package/dist/resources/extensions/gsd/prompts/rethink.md +2 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
- package/dist/resources/extensions/gsd/quick.js +19 -15
- package/dist/resources/extensions/gsd/reactive-graph.js +12 -0
- package/dist/resources/extensions/gsd/roadmap-slices.js +24 -5
- package/dist/resources/extensions/gsd/safety/content-validator.js +3 -3
- package/dist/resources/extensions/gsd/session-lock.js +23 -1
- package/dist/resources/extensions/gsd/state.js +115 -28
- package/dist/resources/extensions/gsd/templates/context-enhanced.md +138 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +15 -3
- package/dist/resources/extensions/gsd/tools/complete-slice.js +27 -6
- package/dist/resources/extensions/gsd/tools/complete-task.js +31 -7
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -5
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +5 -2
- package/dist/resources/extensions/gsd/tools/reopen-milestone.js +119 -0
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +30 -0
- package/dist/resources/extensions/gsd/tools/reopen-task.js +18 -0
- package/dist/resources/extensions/gsd/triage-resolution.js +33 -16
- package/dist/resources/extensions/gsd/undo.js +3 -2
- package/dist/resources/extensions/gsd/workflow-events.js +1 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
- package/dist/resources/extensions/gsd/workflow-projections.js +7 -9
- package/dist/resources/extensions/gsd/workflow-reconcile.js +100 -9
- package/dist/resources/extensions/gsd/workflow-templates.js +11 -2
- package/dist/resources/extensions/gsd/worktree-manager.js +5 -2
- package/dist/resources/extensions/gsd/worktree.js +9 -0
- package/dist/resources/extensions/shared/interview-ui.js +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/6502.8874bcae249c02e1.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-a1c1e452c6b32d04.js → webpack-9fed74684e1c5bb1.js} +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +30 -19
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js +51 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +9 -9
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +10 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +20 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +15 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +18 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/retry-handler.test.ts +80 -0
- package/packages/pi-coding-agent/src/core/retry-handler.ts +37 -25
- package/packages/pi-coding-agent/src/core/sdk.ts +9 -9
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +10 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +20 -4
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +27 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +16 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -0
- package/packages/pi-tui/dist/components/image.d.ts +2 -0
- package/packages/pi-tui/dist/components/image.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/image.js +4 -0
- package/packages/pi-tui/dist/components/image.js.map +1 -1
- package/packages/pi-tui/dist/components/image.test.d.ts +6 -0
- package/packages/pi-tui/dist/components/image.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/image.test.js +32 -0
- package/packages/pi-tui/dist/components/image.test.js.map +1 -0
- package/packages/pi-tui/src/components/image.test.ts +36 -0
- package/packages/pi-tui/src/components/image.ts +5 -0
- package/src/resources/extensions/browser-tools/capture.ts +19 -1
- package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
- package/src/resources/extensions/gsd/auto/run-unit.ts +12 -2
- package/src/resources/extensions/gsd/auto/session.ts +4 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +110 -9
- package/src/resources/extensions/gsd/auto-model-selection.ts +7 -5
- package/src/resources/extensions/gsd/auto-post-unit.ts +16 -6
- package/src/resources/extensions/gsd/auto-prompts.ts +31 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +29 -23
- package/src/resources/extensions/gsd/auto-start.ts +45 -10
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +10 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +28 -7
- package/src/resources/extensions/gsd/auto.ts +19 -8
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -4
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +10 -0
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -4
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +11 -3
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +36 -1
- package/src/resources/extensions/gsd/commands/context.ts +7 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +23 -0
- package/src/resources/extensions/gsd/commands-extensions.ts +1 -1
- package/src/resources/extensions/gsd/config-overlay.ts +331 -0
- package/src/resources/extensions/gsd/db-writer.ts +11 -3
- package/src/resources/extensions/gsd/detection.ts +1 -1
- package/src/resources/extensions/gsd/dispatch-guard.ts +2 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
- package/src/resources/extensions/gsd/doctor.ts +2 -1
- package/src/resources/extensions/gsd/gitignore.ts +1 -0
- package/src/resources/extensions/gsd/gsd-db.ts +46 -4
- package/src/resources/extensions/gsd/guided-flow.ts +254 -30
- package/src/resources/extensions/gsd/index.ts +1 -0
- package/src/resources/extensions/gsd/json-persistence.ts +6 -3
- package/src/resources/extensions/gsd/md-importer.ts +13 -6
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +19 -11
- package/src/resources/extensions/gsd/pre-execution-checks.ts +15 -7
- package/src/resources/extensions/gsd/preferences-types.ts +25 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +45 -1
- package/src/resources/extensions/gsd/preferences.ts +9 -2
- package/src/resources/extensions/gsd/preparation.ts +1419 -0
- package/src/resources/extensions/gsd/prompt-validation.ts +88 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +2 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
- package/src/resources/extensions/gsd/prompts/queue.md +2 -0
- package/src/resources/extensions/gsd/prompts/rethink.md +2 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
- package/src/resources/extensions/gsd/quick.ts +20 -15
- package/src/resources/extensions/gsd/reactive-graph.ts +18 -0
- package/src/resources/extensions/gsd/roadmap-slices.ts +21 -5
- package/src/resources/extensions/gsd/safety/content-validator.ts +3 -3
- package/src/resources/extensions/gsd/session-lock.ts +17 -1
- package/src/resources/extensions/gsd/state.ts +115 -26
- package/src/resources/extensions/gsd/templates/context-enhanced.md +138 -0
- package/src/resources/extensions/gsd/tests/adversarial-review-fixes.test.ts +223 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +33 -2
- package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +41 -0
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +72 -0
- package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/defer-milestone-stamp.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +11 -9
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +28 -30
- package/src/resources/extensions/gsd/tests/integration/test-isolation.ts +53 -0
- package/src/resources/extensions/gsd/tests/integration-prepared-discussion.test.ts +525 -0
- package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +77 -0
- package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +218 -20
- package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/preparation.test.ts +1211 -0
- package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/prompt-builder.test.ts +669 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +7 -4
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +4 -5
- package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/show-config-command.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +147 -0
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +13 -0
- package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +6 -7
- package/src/resources/extensions/gsd/tests/status-db-open.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/symlink-extension-discovery.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +29 -1
- package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +3 -4
- package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +89 -0
- package/src/resources/extensions/gsd/tests/wave1-critical-regressions.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/wave2-events-regressions.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/wave3-session-regressions.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/wave4-write-safety-regressions.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/wave5-consistency-regressions.test.ts +165 -0
- package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +16 -17
- package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +13 -9
- package/src/resources/extensions/gsd/tests/worktree.test.ts +26 -9
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +127 -2
- package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +13 -3
- package/src/resources/extensions/gsd/tools/complete-slice.ts +26 -6
- package/src/resources/extensions/gsd/tools/complete-task.ts +29 -7
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +11 -9
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +5 -2
- package/src/resources/extensions/gsd/tools/reopen-milestone.ts +152 -0
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +27 -0
- package/src/resources/extensions/gsd/tools/reopen-task.ts +17 -0
- package/src/resources/extensions/gsd/triage-resolution.ts +37 -17
- package/src/resources/extensions/gsd/types.ts +4 -0
- package/src/resources/extensions/gsd/undo.ts +3 -2
- package/src/resources/extensions/gsd/workflow-events.ts +5 -3
- package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
- package/src/resources/extensions/gsd/workflow-projections.ts +7 -8
- package/src/resources/extensions/gsd/workflow-reconcile.ts +109 -8
- package/src/resources/extensions/gsd/workflow-templates.ts +11 -2
- package/src/resources/extensions/gsd/worktree-manager.ts +4 -2
- package/src/resources/extensions/gsd/worktree.ts +10 -0
- package/src/resources/extensions/shared/interview-ui.ts +1 -1
- package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +8 -10
- package/dist/web/standalone/.next/static/chunks/6502.7593d7797a4b3999.js +0 -9
- /package/dist/web/standalone/.next/static/{qq3YfHPfyqvh3DIMVmsRH → JwdBI3y1H8vtBKiYvWfEK}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{qq3YfHPfyqvh3DIMVmsRH → JwdBI3y1H8vtBKiYvWfEK}/_ssgManifest.js +0 -0
|
@@ -20,7 +20,7 @@ import { synthesizeCrashRecovery } from "./session-forensics.js";
|
|
|
20
20
|
import { writeLock, clearLock, readCrashLock, formatCrashInfo, isLockProcessAlive, } from "./crash-recovery.js";
|
|
21
21
|
import { acquireSessionLock, releaseSessionLock, updateSessionLock, } from "./session-lock.js";
|
|
22
22
|
import { ensureGitignore, untrackRuntimeFiles } from "./gitignore.js";
|
|
23
|
-
import { nativeInit, nativeAddAll, nativeCommit, } from "./native-git-bridge.js";
|
|
23
|
+
import { nativeIsRepo, nativeInit, nativeAddAll, nativeCommit, nativeGetCurrentBranch, nativeDetectMainBranch, nativeCheckoutBranch, } from "./native-git-bridge.js";
|
|
24
24
|
import { GitServiceImpl } from "./git-service.js";
|
|
25
25
|
import { captureIntegrationBranch, detectWorktreeName, setActiveMilestoneId, } from "./worktree.js";
|
|
26
26
|
import { getAutoWorktreePath } from "./auto-worktree.js";
|
|
@@ -48,11 +48,8 @@ import { resolveDefaultSessionModel } from "./preferences-models.js";
|
|
|
48
48
|
* Returns false if the bootstrap aborted (e.g., guided flow returned,
|
|
49
49
|
* concurrent session detected). Returns true when ready to dispatch.
|
|
50
50
|
*/
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
* bootstrapAutoSession → showSmartEntry → checkAutoStartAfterDiscuss → startAuto
|
|
54
|
-
* cycles indefinitely when the discuss workflow doesn't produce a milestone. */
|
|
55
|
-
let _consecutiveCompleteBootstraps = 0;
|
|
51
|
+
// Guard constant for consecutive bootstrap attempts that found phase === "complete".
|
|
52
|
+
// Counter moved to AutoSession.consecutiveCompleteBootstraps so s.reset() clears it.
|
|
56
53
|
const MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS = 2;
|
|
57
54
|
export async function openProjectDbIfPresent(basePath) {
|
|
58
55
|
const gsdDbPath = resolveProjectRootDbPath(basePath);
|
|
@@ -263,9 +260,9 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
|
|
|
263
260
|
// Guard against recursive dialog loop (#1348):
|
|
264
261
|
// If we've entered this branch multiple times in quick succession,
|
|
265
262
|
// the discuss workflow isn't producing a milestone. Break the cycle.
|
|
266
|
-
|
|
267
|
-
if (
|
|
268
|
-
|
|
263
|
+
s.consecutiveCompleteBootstraps++;
|
|
264
|
+
if (s.consecutiveCompleteBootstraps > MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS) {
|
|
265
|
+
s.consecutiveCompleteBootstraps = 0;
|
|
269
266
|
ctx.ui.notify("All milestones are complete and the discussion didn't produce a new one. " +
|
|
270
267
|
"Run /gsd to start a new milestone manually.", "warning");
|
|
271
268
|
return releaseLockAndReturn();
|
|
@@ -277,7 +274,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
|
|
|
277
274
|
if (postState.activeMilestone &&
|
|
278
275
|
postState.phase !== "complete" &&
|
|
279
276
|
postState.phase !== "pre-planning") {
|
|
280
|
-
|
|
277
|
+
s.consecutiveCompleteBootstraps = 0; // Successfully advanced past "complete"
|
|
281
278
|
state = postState;
|
|
282
279
|
}
|
|
283
280
|
else if (postState.activeMilestone &&
|
|
@@ -338,7 +335,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
|
|
|
338
335
|
return releaseLockAndReturn();
|
|
339
336
|
}
|
|
340
337
|
// Successfully resolved an active milestone — reset the re-entry guard
|
|
341
|
-
|
|
338
|
+
s.consecutiveCompleteBootstraps = 0;
|
|
342
339
|
// ── Initialize session state ──
|
|
343
340
|
s.active = true;
|
|
344
341
|
s.stepMode = requestedStepMode;
|
|
@@ -373,6 +370,22 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
|
|
|
373
370
|
}
|
|
374
371
|
setActiveMilestoneId(base, s.currentMilestoneId);
|
|
375
372
|
}
|
|
373
|
+
// Guard against stale milestone branch when isolation:none (#3613).
|
|
374
|
+
// A prior session with isolation:branch/worktree may have left HEAD on
|
|
375
|
+
// milestone/<MID>. Auto-checkout back to the integration branch.
|
|
376
|
+
if (getIsolationMode() === "none" && nativeIsRepo(base)) {
|
|
377
|
+
try {
|
|
378
|
+
const currentBranch = nativeGetCurrentBranch(base);
|
|
379
|
+
if (currentBranch.startsWith("milestone/")) {
|
|
380
|
+
const integrationBranch = nativeDetectMainBranch(base);
|
|
381
|
+
nativeCheckoutBranch(base, integrationBranch);
|
|
382
|
+
logWarning("bootstrap", `Returned to "${integrationBranch}" — HEAD was on stale milestone branch "${currentBranch}" (isolation: none does not use milestone branches).`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
catch (err) {
|
|
386
|
+
logWarning("bootstrap", `Could not auto-checkout from stale milestone branch: ${err instanceof Error ? err.message : String(err)}`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
376
389
|
// ── Auto-worktree setup ──
|
|
377
390
|
s.originalBasePath = base;
|
|
378
391
|
const isUnderGsdWorktrees = (p) => {
|
|
@@ -448,6 +461,24 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
|
|
|
448
461
|
id: startModelSnapshot.id,
|
|
449
462
|
};
|
|
450
463
|
}
|
|
464
|
+
// Apply worker model override from parallel orchestrator (#worker-model).
|
|
465
|
+
// GSD_WORKER_MODEL is injected by the coordinator when parallel.worker_model
|
|
466
|
+
// is configured, so parallel milestone workers use a cheaper model than the
|
|
467
|
+
// coordinator session (e.g. Haiku for execution, Sonnet for planning).
|
|
468
|
+
const workerModelOverride = process.env.GSD_WORKER_MODEL;
|
|
469
|
+
if (workerModelOverride && process.env.GSD_PARALLEL_WORKER === "1") {
|
|
470
|
+
const availableModels = ctx.modelRegistry.getAvailable();
|
|
471
|
+
const { resolveModelId } = await import("./auto-model-selection.js");
|
|
472
|
+
const overrideModel = resolveModelId(workerModelOverride, availableModels, ctx.model?.provider);
|
|
473
|
+
if (overrideModel) {
|
|
474
|
+
const ok = await pi.setModel(overrideModel, { persist: false });
|
|
475
|
+
if (ok) {
|
|
476
|
+
// Update start model so all subsequent units use this as the baseline
|
|
477
|
+
s.autoModeStartModel = { provider: overrideModel.provider, id: overrideModel.id };
|
|
478
|
+
ctx.ui.notify(`Worker model override: ${overrideModel.provider}/${overrideModel.id}`, "info");
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
451
482
|
// Snapshot installed skills
|
|
452
483
|
if (resolveSkillDiscoveryMode() !== "off") {
|
|
453
484
|
snapshotSkills();
|
|
@@ -92,3 +92,13 @@ export function isToolInvocationError(errorMsg) {
|
|
|
92
92
|
return false;
|
|
93
93
|
return TOOL_INVOCATION_ERROR_RE.test(errorMsg);
|
|
94
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Returns true if the error message indicates the tool was skipped because
|
|
97
|
+
* a queued user message interrupted the turn (#3595). Retrying will produce
|
|
98
|
+
* the same skip, so the unit should be paused rather than retried.
|
|
99
|
+
*/
|
|
100
|
+
export function isQueuedUserMessageSkip(errorMsg) {
|
|
101
|
+
if (!errorMsg)
|
|
102
|
+
return false;
|
|
103
|
+
return /^Skipped due to queued user message\.?$/i.test(errorMsg.trim());
|
|
104
|
+
}
|
|
@@ -135,8 +135,10 @@ function clearProjectRootStateFiles(basePath, milestoneId) {
|
|
|
135
135
|
unlinkSync(file);
|
|
136
136
|
}
|
|
137
137
|
catch (err) {
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
// ENOENT is expected — file may not exist (#3597)
|
|
139
|
+
if (err.code !== "ENOENT") {
|
|
140
|
+
logWarning("worktree", `file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
141
|
+
}
|
|
140
142
|
}
|
|
141
143
|
}
|
|
142
144
|
// Clean up entire synced milestone directory and runtime/units.
|
|
@@ -160,8 +162,11 @@ function clearProjectRootStateFiles(basePath, milestoneId) {
|
|
|
160
162
|
unlinkSync(join(basePath, f));
|
|
161
163
|
}
|
|
162
164
|
catch (err) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
+
// ENOENT/EISDIR are expected for already-removed or directory entries (#3597)
|
|
166
|
+
const code = err.code;
|
|
167
|
+
if (code !== "ENOENT" && code !== "EISDIR") {
|
|
168
|
+
logWarning("worktree", `untracked file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
169
|
+
}
|
|
165
170
|
}
|
|
166
171
|
}
|
|
167
172
|
}
|
|
@@ -656,6 +661,10 @@ export function syncWorktreeStateBack(mainBasePath, worktreePath, milestoneId) {
|
|
|
656
661
|
.filter((d) => d.isDirectory())
|
|
657
662
|
.map((d) => d.name);
|
|
658
663
|
for (const mid of wtMilestones) {
|
|
664
|
+
// Skip the current milestone being merged — its files are already in the
|
|
665
|
+
// milestone branch and would conflict with the squash merge (#3641).
|
|
666
|
+
if (mid === milestoneId)
|
|
667
|
+
continue;
|
|
659
668
|
syncMilestoneDir(wtGsd, mainGsd, mid, synced);
|
|
660
669
|
}
|
|
661
670
|
}
|
|
@@ -901,11 +910,19 @@ export function createAutoWorktree(basePath, milestoneId) {
|
|
|
901
910
|
});
|
|
902
911
|
}
|
|
903
912
|
else {
|
|
904
|
-
// Fresh start — create branch from integration branch
|
|
913
|
+
// Fresh start — create branch from integration branch.
|
|
914
|
+
// Use the same 3-tier fallback as mergeMilestoneToMain (#3461):
|
|
915
|
+
// 1. META.json integration branch (explicit per-milestone override)
|
|
916
|
+
// 2. git.main_branch preference (user's configured working branch)
|
|
917
|
+
// 3. nativeDetectMainBranch (origin/HEAD auto-detection)
|
|
918
|
+
// Without tier 2, projects with main_branch=dev but origin/HEAD→master
|
|
919
|
+
// would fork worktrees from the wrong (stale) branch.
|
|
905
920
|
const integrationBranch = readIntegrationBranch(basePath, milestoneId) ?? undefined;
|
|
921
|
+
const gitPrefs = loadEffectiveGSDPreferences()?.preferences?.git;
|
|
922
|
+
const startPoint = integrationBranch ?? gitPrefs?.main_branch ?? undefined;
|
|
906
923
|
info = createWorktree(basePath, milestoneId, {
|
|
907
924
|
branch,
|
|
908
|
-
startPoint
|
|
925
|
+
startPoint,
|
|
909
926
|
});
|
|
910
927
|
}
|
|
911
928
|
// Copy .gsd/ planning artifacts from the source repo into the new worktree.
|
|
@@ -1235,7 +1252,12 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
|
|
|
1235
1252
|
// checkout and leave the user with a broken merge state (#1668).
|
|
1236
1253
|
const prefs = loadEffectiveGSDPreferences()?.preferences?.git ?? {};
|
|
1237
1254
|
const integrationBranch = readIntegrationBranch(originalBasePath_, milestoneId);
|
|
1238
|
-
|
|
1255
|
+
// Validate prefs.main_branch exists before using it — a stale preference
|
|
1256
|
+
// (e.g. "master" when repo uses "main") causes merge failure (#3589).
|
|
1257
|
+
const validatedPrefBranch = prefs.main_branch && nativeBranchExists(originalBasePath_, prefs.main_branch)
|
|
1258
|
+
? prefs.main_branch
|
|
1259
|
+
: undefined;
|
|
1260
|
+
const mainBranch = integrationBranch ?? validatedPrefBranch ?? nativeDetectMainBranch(originalBasePath_);
|
|
1239
1261
|
// Remove transient project-root state files before any branch or merge
|
|
1240
1262
|
// operation. Untracked milestone metadata can otherwise block squash merges.
|
|
1241
1263
|
clearProjectRootStateFiles(originalBasePath_, milestoneId);
|
|
@@ -23,7 +23,7 @@ import { acquireSessionLock, getSessionLockStatus, releaseSessionLock, updateSes
|
|
|
23
23
|
import { resolveAutoSupervisorConfig, loadEffectiveGSDPreferences, getIsolationMode, } from "./preferences.js";
|
|
24
24
|
import { sendDesktopNotification } from "./notifications.js";
|
|
25
25
|
import { getBudgetAlertLevel, getNewBudgetAlertLevel, getBudgetEnforcementAction, } from "./auto-budget.js";
|
|
26
|
-
import { markToolStart as _markToolStart, markToolEnd as _markToolEnd, getOldestInFlightToolAgeMs as _getOldestInFlightToolAgeMs, clearInFlightTools, isToolInvocationError, } from "./auto-tool-tracking.js";
|
|
26
|
+
import { markToolStart as _markToolStart, markToolEnd as _markToolEnd, getOldestInFlightToolAgeMs as _getOldestInFlightToolAgeMs, clearInFlightTools, isToolInvocationError, isQueuedUserMessageSkip, } from "./auto-tool-tracking.js";
|
|
27
27
|
import { closeoutUnit } from "./auto-unit-closeout.js";
|
|
28
28
|
import { selectAndApplyModel, resolveModelId } from "./auto-model-selection.js";
|
|
29
29
|
import { resetRoutingHistory, recordOutcome } from "./routing-history.js";
|
|
@@ -201,7 +201,7 @@ export function markToolEnd(toolCallId) {
|
|
|
201
201
|
export function recordToolInvocationError(toolName, errorMsg) {
|
|
202
202
|
if (!s.active)
|
|
203
203
|
return;
|
|
204
|
-
if (isToolInvocationError(errorMsg)) {
|
|
204
|
+
if (isToolInvocationError(errorMsg) || isQueuedUserMessageSkip(errorMsg)) {
|
|
205
205
|
s.lastToolInvocationError = `${toolName}: ${errorMsg}`;
|
|
206
206
|
}
|
|
207
207
|
}
|
|
@@ -845,12 +845,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
845
845
|
s.stepMode = meta.stepMode ?? requestedStepMode;
|
|
846
846
|
s.autoStartTime = meta.autoStartTime || Date.now();
|
|
847
847
|
s.paused = true;
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
catch (err) { /* non-fatal */
|
|
852
|
-
logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
|
|
853
|
-
}
|
|
848
|
+
// Don't delete pause file yet — defer until lock is acquired.
|
|
849
|
+
// If lock fails, the file must survive for retry.
|
|
850
|
+
s.pausedSessionFile = pausedPath;
|
|
854
851
|
ctx.ui.notify(`Resuming paused custom workflow${meta.activeRunDir ? ` (${meta.activeRunDir})` : ""}.`, "info");
|
|
855
852
|
}
|
|
856
853
|
else if (meta.milestoneId) {
|
|
@@ -873,13 +870,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
873
870
|
s.stepMode = meta.stepMode ?? requestedStepMode;
|
|
874
871
|
s.autoStartTime = meta.autoStartTime || Date.now();
|
|
875
872
|
s.paused = true;
|
|
876
|
-
//
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
}
|
|
880
|
-
catch (err) { /* non-fatal */
|
|
881
|
-
logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
|
|
882
|
-
}
|
|
873
|
+
// Don't delete pause file yet — defer until lock is acquired.
|
|
874
|
+
// If lock fails, the file must survive for retry.
|
|
875
|
+
s.pausedSessionFile = pausedPath;
|
|
883
876
|
ctx.ui.notify(`Resuming paused session for ${meta.milestoneId}${meta.worktreePath ? ` (worktree)` : ""}.`, "info");
|
|
884
877
|
}
|
|
885
878
|
}
|
|
@@ -893,9 +886,22 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
893
886
|
if (s.paused) {
|
|
894
887
|
const resumeLock = acquireSessionLock(base);
|
|
895
888
|
if (!resumeLock.acquired) {
|
|
889
|
+
// Reset paused state so isAutoPaused() doesn't stick true after lock failure.
|
|
890
|
+
// Pause file is preserved on disk for retry — not deleted.
|
|
891
|
+
s.paused = false;
|
|
896
892
|
ctx.ui.notify(`Cannot resume: ${resumeLock.reason}`, "error");
|
|
897
893
|
return;
|
|
898
894
|
}
|
|
895
|
+
// Lock acquired — now safe to delete the pause file
|
|
896
|
+
if (s.pausedSessionFile) {
|
|
897
|
+
try {
|
|
898
|
+
unlinkSync(s.pausedSessionFile);
|
|
899
|
+
}
|
|
900
|
+
catch (err) {
|
|
901
|
+
logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
|
|
902
|
+
}
|
|
903
|
+
s.pausedSessionFile = null;
|
|
904
|
+
}
|
|
899
905
|
s.paused = false;
|
|
900
906
|
s.active = true;
|
|
901
907
|
s.verbose = verboseMode;
|
|
@@ -79,11 +79,24 @@ export async function handleAgentEnd(pi, event, ctx) {
|
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
if (lastMsg && "stopReason" in lastMsg && lastMsg.stopReason === "error") {
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
// #3588: errorMessage can be useless (e.g. "success") while the real error
|
|
83
|
+
// is in the assistant message text content. Fall back to content when
|
|
84
|
+
// errorMessage looks uninformative.
|
|
85
|
+
const rawErrorMsg = ("errorMessage" in lastMsg && lastMsg.errorMessage) ? String(lastMsg.errorMessage) : "";
|
|
86
|
+
const isUseless = !rawErrorMsg || /^(success|ok|true|error|unknown)$/i.test(rawErrorMsg.trim());
|
|
87
|
+
// #3588: When errorMessage is uninformative, extract the real error from
|
|
88
|
+
// the assistant message text content for display purposes only.
|
|
89
|
+
// Classification still uses rawErrorMsg to avoid false positives from prose.
|
|
90
|
+
let displayMsg = rawErrorMsg;
|
|
91
|
+
if (isUseless && "content" in lastMsg && Array.isArray(lastMsg.content)) {
|
|
92
|
+
const textBlock = lastMsg.content.find((b) => b.type === "text" && b.text);
|
|
93
|
+
if (textBlock)
|
|
94
|
+
displayMsg = textBlock.text.slice(0, 300);
|
|
95
|
+
}
|
|
96
|
+
const errorDetail = displayMsg ? `: ${displayMsg}` : "";
|
|
84
97
|
const explicitRetryAfterMs = ("retryAfterMs" in lastMsg && typeof lastMsg.retryAfterMs === "number") ? lastMsg.retryAfterMs : undefined;
|
|
85
|
-
// ── 1. Classify
|
|
86
|
-
const cls = classifyError(
|
|
98
|
+
// ── 1. Classify using rawErrorMsg to avoid prose false-positives ────
|
|
99
|
+
const cls = classifyError(rawErrorMsg, explicitRetryAfterMs);
|
|
87
100
|
// Cap rate-limit backoff for CLI-style providers (openai-codex, google-gemini-cli)
|
|
88
101
|
// which use per-user quotas with shorter windows (#2922).
|
|
89
102
|
if (cls.kind === "rate-limit") {
|
|
@@ -943,6 +943,16 @@ export function registerDbTools(pi) {
|
|
|
943
943
|
}
|
|
944
944
|
updateSliceStatus(params.milestoneId, params.sliceId, "skipped");
|
|
945
945
|
invalidateStateCache();
|
|
946
|
+
// Rebuild STATE.md so it reflects the skip immediately (#3477).
|
|
947
|
+
// Without this, /gsd auto reads stale STATE.md and resumes the skipped slice.
|
|
948
|
+
try {
|
|
949
|
+
const basePath = process.cwd();
|
|
950
|
+
const { rebuildState } = await import("../doctor.js");
|
|
951
|
+
await rebuildState(basePath);
|
|
952
|
+
}
|
|
953
|
+
catch (err) {
|
|
954
|
+
logError("tool", `skip_slice rebuildState failed: ${err.message}`, { tool: "gsd_skip_slice" });
|
|
955
|
+
}
|
|
946
956
|
return {
|
|
947
957
|
content: [{ type: "text", text: `Skipped slice ${params.sliceId} (${params.milestoneId}). Reason: ${params.reason ?? "User-directed skip"}. Auto-mode will advance past this slice.` }],
|
|
948
958
|
details: {
|
|
@@ -17,10 +17,12 @@ export function registerQueryTools(pi) {
|
|
|
17
17
|
}),
|
|
18
18
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
19
19
|
try {
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
const {
|
|
23
|
-
|
|
20
|
+
// Open the DB if not already open — safe for read-only use since
|
|
21
|
+
// ensureDbOpen() only creates/migrates when .gsd/ has content (#3644).
|
|
22
|
+
const { ensureDbOpen } = await import("./dynamic-tools.js");
|
|
23
|
+
const dbAvailable = await ensureDbOpen();
|
|
24
|
+
const { getMilestone, getSliceStatusSummary, getSliceTaskCounts, _getAdapter, } = await import("../gsd-db.js");
|
|
25
|
+
if (!dbAvailable) {
|
|
24
26
|
return {
|
|
25
27
|
content: [{ type: "text", text: "Error: GSD database is not available." }],
|
|
26
28
|
details: { operation: "milestone_status", error: "db_unavailable" },
|
|
@@ -31,7 +31,11 @@ function installEpipeGuard() {
|
|
|
31
31
|
if (handleRecoverableExtensionProcessError(err)) {
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
// Log unhandled errors instead of re-throwing — throwing inside an
|
|
35
|
+
// uncaughtException handler is a fatal double-fault in Node.js (#3163).
|
|
36
|
+
process.stderr.write(`[gsd] uncaught extension error (non-fatal): ${err.message}\n`);
|
|
37
|
+
if (err.stack)
|
|
38
|
+
process.stderr.write(`${err.stack}\n`);
|
|
35
39
|
};
|
|
36
40
|
process.on("uncaughtException", _gsdEpipeGuard);
|
|
37
41
|
}
|
|
@@ -3,7 +3,7 @@ import { isToolCallEventType } from "@gsd/pi-coding-agent";
|
|
|
3
3
|
import { buildMilestoneFileName, resolveMilestonePath, resolveSliceFile, resolveSlicePath } from "../paths.js";
|
|
4
4
|
import { buildBeforeAgentStartResult } from "./system-context.js";
|
|
5
5
|
import { handleAgentEnd } from "./agent-end-recovery.js";
|
|
6
|
-
import { clearDiscussionFlowState, isDepthVerified, isQueuePhaseActive, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockQueueExecution } from "./write-gate.js";
|
|
6
|
+
import { clearDiscussionFlowState, isDepthVerified, isDepthConfirmationAnswer, isQueuePhaseActive, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockQueueExecution } from "./write-gate.js";
|
|
7
7
|
import { isBlockedStateFile, isBashWriteToStateFile, BLOCKED_WRITE_ERROR } from "../write-intercept.js";
|
|
8
8
|
import { cleanupQuickBranch } from "../quick.js";
|
|
9
9
|
import { getDiscussionMilestoneId } from "../guided-flow.js";
|
|
@@ -98,7 +98,10 @@ export function registerHooks(pi) {
|
|
|
98
98
|
}
|
|
99
99
|
});
|
|
100
100
|
pi.on("session_before_compact", async () => {
|
|
101
|
-
|
|
101
|
+
// Only cancel compaction while auto-mode is actively running.
|
|
102
|
+
// Paused auto-mode should allow compaction — the user may be doing
|
|
103
|
+
// interactive work (#3165).
|
|
104
|
+
if (isAutoActive()) {
|
|
102
105
|
return { cancel: true };
|
|
103
106
|
}
|
|
104
107
|
const basePath = process.cwd();
|
|
@@ -227,7 +230,12 @@ export function registerHooks(pi) {
|
|
|
227
230
|
const questions = event.input?.questions ?? [];
|
|
228
231
|
for (const question of questions) {
|
|
229
232
|
if (typeof question.id === "string" && question.id.includes("depth_verification")) {
|
|
230
|
-
|
|
233
|
+
// Only unlock the gate if the user selected the first option (confirmation).
|
|
234
|
+
// Cross-references against the question's defined options to reject free-form "Other" text.
|
|
235
|
+
const answer = details.response?.answers?.[question.id];
|
|
236
|
+
if (isDepthConfirmationAnswer(answer?.selected, question.options)) {
|
|
237
|
+
markDepthVerified();
|
|
238
|
+
}
|
|
231
239
|
break;
|
|
232
240
|
}
|
|
233
241
|
}
|
|
@@ -43,6 +43,30 @@ export function clearDiscussionFlowState() {
|
|
|
43
43
|
export function markDepthVerified() {
|
|
44
44
|
depthVerificationDone = true;
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Check whether a depth_verification answer confirms the discussion is complete.
|
|
48
|
+
* Uses structural validation: the selected answer must exactly match the first
|
|
49
|
+
* option label from the question definition (the confirmation option by convention).
|
|
50
|
+
* This rejects free-form "Other" text, decline options, and garbage input without
|
|
51
|
+
* coupling to any specific label substring.
|
|
52
|
+
*
|
|
53
|
+
* @param selected The answer's selected value from details.response.answers[id].selected
|
|
54
|
+
* @param options The question's options array from event.input.questions[n].options
|
|
55
|
+
*/
|
|
56
|
+
export function isDepthConfirmationAnswer(selected, options) {
|
|
57
|
+
const value = Array.isArray(selected) ? selected[0] : selected;
|
|
58
|
+
if (typeof value !== "string" || !value)
|
|
59
|
+
return false;
|
|
60
|
+
// If options are available, structurally validate: selected must exactly match
|
|
61
|
+
// the first option (confirmation) label. Rejects free-form "Other" and decline options.
|
|
62
|
+
if (Array.isArray(options) && options.length > 0) {
|
|
63
|
+
const confirmLabel = options[0]?.label;
|
|
64
|
+
return typeof confirmLabel === "string" && value === confirmLabel;
|
|
65
|
+
}
|
|
66
|
+
// Fallback when options aren't available (e.g., older call sites):
|
|
67
|
+
// accept only if it contains "(Recommended)" — the prompt convention suffix.
|
|
68
|
+
return value.includes("(Recommended)");
|
|
69
|
+
}
|
|
46
70
|
export function shouldBlockContextWrite(toolName, inputPath, milestoneId, depthVerified, queuePhaseActive) {
|
|
47
71
|
if (toolName !== "write")
|
|
48
72
|
return { block: false };
|
|
@@ -56,7 +80,13 @@ export function shouldBlockContextWrite(toolName, inputPath, milestoneId, depthV
|
|
|
56
80
|
return { block: false };
|
|
57
81
|
return {
|
|
58
82
|
block: true,
|
|
59
|
-
reason:
|
|
83
|
+
reason: [
|
|
84
|
+
`HARD BLOCK: Cannot write to milestone CONTEXT.md without depth verification.`,
|
|
85
|
+
`This is a mechanical gate — you MUST NOT proceed, retry, or rationalize past this block.`,
|
|
86
|
+
`Required action: call ask_user_questions with question id containing "depth_verification".`,
|
|
87
|
+
`The user MUST select the "(Recommended)" confirmation option to unlock this gate.`,
|
|
88
|
+
`If the user declines, cancels, or the tool fails, you must re-ask — not bypass.`,
|
|
89
|
+
].join(" "),
|
|
60
90
|
};
|
|
61
91
|
}
|
|
62
92
|
/**
|
|
@@ -4,7 +4,14 @@ import { resolveProjectRoot } from "../worktree.js";
|
|
|
4
4
|
import { showNextAction } from "../../shared/tui.js";
|
|
5
5
|
import { handleStatus } from "./handlers/core.js";
|
|
6
6
|
export function projectRoot() {
|
|
7
|
-
|
|
7
|
+
let cwd;
|
|
8
|
+
try {
|
|
9
|
+
cwd = process.cwd();
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
// cwd directory was deleted (e.g. worktree teardown) — fall back to HOME (#3598)
|
|
13
|
+
cwd = process.env.HOME ?? "/";
|
|
14
|
+
}
|
|
8
15
|
const root = resolveProjectRoot(cwd);
|
|
9
16
|
if (root !== cwd) {
|
|
10
17
|
assertSafeDirectory(cwd);
|
|
@@ -48,6 +48,7 @@ export function showHelp(ctx) {
|
|
|
48
48
|
" /gsd cmux Manage cmux integration [status|on|off|notifications|sidebar|splits|browser]",
|
|
49
49
|
" /gsd config Set API keys for external tools",
|
|
50
50
|
" /gsd keys API key manager [list|add|remove|test|rotate|doctor]",
|
|
51
|
+
" /gsd show-config Show effective configuration (models, routing, toggles)",
|
|
51
52
|
" /gsd hooks Show post-unit hook configuration",
|
|
52
53
|
" /gsd extensions Manage extensions [list|enable|disable|info]",
|
|
53
54
|
" /gsd fast Toggle OpenAI service tier [on|off|flex|status]",
|
|
@@ -66,6 +67,9 @@ export function showHelp(ctx) {
|
|
|
66
67
|
}
|
|
67
68
|
export async function handleStatus(ctx) {
|
|
68
69
|
const basePath = projectRoot();
|
|
70
|
+
// Open DB in cold sessions so status uses DB-backed state, not filesystem fallback (#3385)
|
|
71
|
+
const { ensureDbOpen } = await import("../../bootstrap/dynamic-tools.js");
|
|
72
|
+
await ensureDbOpen();
|
|
69
73
|
const state = await deriveState(basePath);
|
|
70
74
|
if (state.registry.length === 0) {
|
|
71
75
|
ctx.ui.notify("No GSD milestones found. Run /gsd to start.", "info");
|
|
@@ -188,6 +192,22 @@ export async function handleCoreCommand(trimmed, ctx) {
|
|
|
188
192
|
await handleCmux(trimmed.replace(/^cmux\s*/, "").trim(), ctx);
|
|
189
193
|
return true;
|
|
190
194
|
}
|
|
195
|
+
if (trimmed === "show-config") {
|
|
196
|
+
const { GSDConfigOverlay, formatConfigText } = await import("../../config-overlay.js");
|
|
197
|
+
const result = await ctx.ui.custom((tui, theme, _kb, done) => new GSDConfigOverlay(tui, theme, () => done()), {
|
|
198
|
+
overlay: true,
|
|
199
|
+
overlayOptions: {
|
|
200
|
+
width: "65%",
|
|
201
|
+
minWidth: 55,
|
|
202
|
+
maxHeight: "85%",
|
|
203
|
+
anchor: "center",
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
if (result === undefined) {
|
|
207
|
+
ctx.ui.notify(formatConfigText(), "info");
|
|
208
|
+
}
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
191
211
|
if (trimmed === "setup" || trimmed.startsWith("setup ")) {
|
|
192
212
|
await handleSetup(trimmed.replace(/^setup\s*/, "").trim(), ctx);
|
|
193
213
|
return true;
|
|
@@ -68,7 +68,7 @@ function discoverManifests() {
|
|
|
68
68
|
if (!existsSync(extDir))
|
|
69
69
|
return manifests;
|
|
70
70
|
for (const entry of readdirSync(extDir, { withFileTypes: true })) {
|
|
71
|
-
if (!entry.isDirectory())
|
|
71
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
72
72
|
continue;
|
|
73
73
|
const m = readManifest(join(extDir, entry.name));
|
|
74
74
|
if (m)
|