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
|
@@ -44,6 +44,9 @@ import {
|
|
|
44
44
|
nativeInit,
|
|
45
45
|
nativeAddAll,
|
|
46
46
|
nativeCommit,
|
|
47
|
+
nativeGetCurrentBranch,
|
|
48
|
+
nativeDetectMainBranch,
|
|
49
|
+
nativeCheckoutBranch,
|
|
47
50
|
} from "./native-git-bridge.js";
|
|
48
51
|
import { GitServiceImpl } from "./git-service.js";
|
|
49
52
|
import {
|
|
@@ -99,11 +102,8 @@ export interface BootstrapDeps {
|
|
|
99
102
|
* concurrent session detected). Returns true when ready to dispatch.
|
|
100
103
|
*/
|
|
101
104
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
* bootstrapAutoSession → showSmartEntry → checkAutoStartAfterDiscuss → startAuto
|
|
105
|
-
* cycles indefinitely when the discuss workflow doesn't produce a milestone. */
|
|
106
|
-
let _consecutiveCompleteBootstraps = 0;
|
|
105
|
+
// Guard constant for consecutive bootstrap attempts that found phase === "complete".
|
|
106
|
+
// Counter moved to AutoSession.consecutiveCompleteBootstraps so s.reset() clears it.
|
|
107
107
|
const MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS = 2;
|
|
108
108
|
|
|
109
109
|
export async function openProjectDbIfPresent(basePath: string): Promise<void> {
|
|
@@ -389,9 +389,9 @@ export async function bootstrapAutoSession(
|
|
|
389
389
|
// Guard against recursive dialog loop (#1348):
|
|
390
390
|
// If we've entered this branch multiple times in quick succession,
|
|
391
391
|
// the discuss workflow isn't producing a milestone. Break the cycle.
|
|
392
|
-
|
|
393
|
-
if (
|
|
394
|
-
|
|
392
|
+
s.consecutiveCompleteBootstraps++;
|
|
393
|
+
if (s.consecutiveCompleteBootstraps > MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS) {
|
|
394
|
+
s.consecutiveCompleteBootstraps = 0;
|
|
395
395
|
ctx.ui.notify(
|
|
396
396
|
"All milestones are complete and the discussion didn't produce a new one. " +
|
|
397
397
|
"Run /gsd to start a new milestone manually.",
|
|
@@ -410,7 +410,7 @@ export async function bootstrapAutoSession(
|
|
|
410
410
|
postState.phase !== "complete" &&
|
|
411
411
|
postState.phase !== "pre-planning"
|
|
412
412
|
) {
|
|
413
|
-
|
|
413
|
+
s.consecutiveCompleteBootstraps = 0; // Successfully advanced past "complete"
|
|
414
414
|
state = postState;
|
|
415
415
|
} else if (
|
|
416
416
|
postState.activeMilestone &&
|
|
@@ -489,7 +489,7 @@ export async function bootstrapAutoSession(
|
|
|
489
489
|
}
|
|
490
490
|
|
|
491
491
|
// Successfully resolved an active milestone — reset the re-entry guard
|
|
492
|
-
|
|
492
|
+
s.consecutiveCompleteBootstraps = 0;
|
|
493
493
|
|
|
494
494
|
// ── Initialize session state ──
|
|
495
495
|
s.active = true;
|
|
@@ -528,6 +528,22 @@ export async function bootstrapAutoSession(
|
|
|
528
528
|
setActiveMilestoneId(base, s.currentMilestoneId);
|
|
529
529
|
}
|
|
530
530
|
|
|
531
|
+
// Guard against stale milestone branch when isolation:none (#3613).
|
|
532
|
+
// A prior session with isolation:branch/worktree may have left HEAD on
|
|
533
|
+
// milestone/<MID>. Auto-checkout back to the integration branch.
|
|
534
|
+
if (getIsolationMode() === "none" && nativeIsRepo(base)) {
|
|
535
|
+
try {
|
|
536
|
+
const currentBranch = nativeGetCurrentBranch(base);
|
|
537
|
+
if (currentBranch.startsWith("milestone/")) {
|
|
538
|
+
const integrationBranch = nativeDetectMainBranch(base);
|
|
539
|
+
nativeCheckoutBranch(base, integrationBranch);
|
|
540
|
+
logWarning("bootstrap", `Returned to "${integrationBranch}" — HEAD was on stale milestone branch "${currentBranch}" (isolation: none does not use milestone branches).`);
|
|
541
|
+
}
|
|
542
|
+
} catch (err) {
|
|
543
|
+
logWarning("bootstrap", `Could not auto-checkout from stale milestone branch: ${err instanceof Error ? err.message : String(err)}`);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
531
547
|
// ── Auto-worktree setup ──
|
|
532
548
|
s.originalBasePath = base;
|
|
533
549
|
|
|
@@ -614,6 +630,25 @@ export async function bootstrapAutoSession(
|
|
|
614
630
|
};
|
|
615
631
|
}
|
|
616
632
|
|
|
633
|
+
// Apply worker model override from parallel orchestrator (#worker-model).
|
|
634
|
+
// GSD_WORKER_MODEL is injected by the coordinator when parallel.worker_model
|
|
635
|
+
// is configured, so parallel milestone workers use a cheaper model than the
|
|
636
|
+
// coordinator session (e.g. Haiku for execution, Sonnet for planning).
|
|
637
|
+
const workerModelOverride = process.env.GSD_WORKER_MODEL;
|
|
638
|
+
if (workerModelOverride && process.env.GSD_PARALLEL_WORKER === "1") {
|
|
639
|
+
const availableModels = ctx.modelRegistry.getAvailable();
|
|
640
|
+
const { resolveModelId } = await import("./auto-model-selection.js");
|
|
641
|
+
const overrideModel = resolveModelId(workerModelOverride, availableModels, ctx.model?.provider);
|
|
642
|
+
if (overrideModel) {
|
|
643
|
+
const ok = await pi.setModel(overrideModel, { persist: false });
|
|
644
|
+
if (ok) {
|
|
645
|
+
// Update start model so all subsequent units use this as the baseline
|
|
646
|
+
s.autoModeStartModel = { provider: overrideModel.provider, id: overrideModel.id };
|
|
647
|
+
ctx.ui.notify(`Worker model override: ${overrideModel.provider}/${overrideModel.id}`, "info");
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
617
652
|
// Snapshot installed skills
|
|
618
653
|
if (resolveSkillDiscoveryMode() !== "off") {
|
|
619
654
|
snapshotSkills();
|
|
@@ -102,3 +102,13 @@ export function isToolInvocationError(errorMsg: string): boolean {
|
|
|
102
102
|
if (!errorMsg) return false;
|
|
103
103
|
return TOOL_INVOCATION_ERROR_RE.test(errorMsg);
|
|
104
104
|
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Returns true if the error message indicates the tool was skipped because
|
|
108
|
+
* a queued user message interrupted the turn (#3595). Retrying will produce
|
|
109
|
+
* the same skip, so the unit should be paused rather than retried.
|
|
110
|
+
*/
|
|
111
|
+
export function isQueuedUserMessageSkip(errorMsg: string): boolean {
|
|
112
|
+
if (!errorMsg) return false;
|
|
113
|
+
return /^Skipped due to queued user message\.?$/i.test(errorMsg.trim());
|
|
114
|
+
}
|
|
@@ -188,8 +188,10 @@ function clearProjectRootStateFiles(basePath: string, milestoneId: string): void
|
|
|
188
188
|
try {
|
|
189
189
|
unlinkSync(file);
|
|
190
190
|
} catch (err) {
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
// ENOENT is expected — file may not exist (#3597)
|
|
192
|
+
if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
193
|
+
logWarning("worktree", `file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
194
|
+
}
|
|
193
195
|
}
|
|
194
196
|
}
|
|
195
197
|
|
|
@@ -218,8 +220,11 @@ function clearProjectRootStateFiles(basePath: string, milestoneId: string): void
|
|
|
218
220
|
try {
|
|
219
221
|
unlinkSync(join(basePath, f));
|
|
220
222
|
} catch (err) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
+
// ENOENT/EISDIR are expected for already-removed or directory entries (#3597)
|
|
224
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
225
|
+
if (code !== "ENOENT" && code !== "EISDIR") {
|
|
226
|
+
logWarning("worktree", `untracked file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
227
|
+
}
|
|
223
228
|
}
|
|
224
229
|
}
|
|
225
230
|
}
|
|
@@ -770,6 +775,9 @@ export function syncWorktreeStateBack(
|
|
|
770
775
|
.map((d) => d.name);
|
|
771
776
|
|
|
772
777
|
for (const mid of wtMilestones) {
|
|
778
|
+
// Skip the current milestone being merged — its files are already in the
|
|
779
|
+
// milestone branch and would conflict with the squash merge (#3641).
|
|
780
|
+
if (mid === milestoneId) continue;
|
|
773
781
|
syncMilestoneDir(wtGsd, mainGsd, mid, synced);
|
|
774
782
|
}
|
|
775
783
|
} catch (err) {
|
|
@@ -1049,12 +1057,20 @@ export function createAutoWorktree(
|
|
|
1049
1057
|
reuseExistingBranch: true,
|
|
1050
1058
|
});
|
|
1051
1059
|
} else {
|
|
1052
|
-
// Fresh start — create branch from integration branch
|
|
1060
|
+
// Fresh start — create branch from integration branch.
|
|
1061
|
+
// Use the same 3-tier fallback as mergeMilestoneToMain (#3461):
|
|
1062
|
+
// 1. META.json integration branch (explicit per-milestone override)
|
|
1063
|
+
// 2. git.main_branch preference (user's configured working branch)
|
|
1064
|
+
// 3. nativeDetectMainBranch (origin/HEAD auto-detection)
|
|
1065
|
+
// Without tier 2, projects with main_branch=dev but origin/HEAD→master
|
|
1066
|
+
// would fork worktrees from the wrong (stale) branch.
|
|
1053
1067
|
const integrationBranch =
|
|
1054
1068
|
readIntegrationBranch(basePath, milestoneId) ?? undefined;
|
|
1069
|
+
const gitPrefs = loadEffectiveGSDPreferences()?.preferences?.git;
|
|
1070
|
+
const startPoint = integrationBranch ?? gitPrefs?.main_branch ?? undefined;
|
|
1055
1071
|
info = createWorktree(basePath, milestoneId, {
|
|
1056
1072
|
branch,
|
|
1057
|
-
startPoint
|
|
1073
|
+
startPoint,
|
|
1058
1074
|
});
|
|
1059
1075
|
}
|
|
1060
1076
|
|
|
@@ -1453,8 +1469,13 @@ export function mergeMilestoneToMain(
|
|
|
1453
1469
|
originalBasePath_,
|
|
1454
1470
|
milestoneId,
|
|
1455
1471
|
);
|
|
1472
|
+
// Validate prefs.main_branch exists before using it — a stale preference
|
|
1473
|
+
// (e.g. "master" when repo uses "main") causes merge failure (#3589).
|
|
1474
|
+
const validatedPrefBranch = prefs.main_branch && nativeBranchExists(originalBasePath_, prefs.main_branch)
|
|
1475
|
+
? prefs.main_branch
|
|
1476
|
+
: undefined;
|
|
1456
1477
|
const mainBranch =
|
|
1457
|
-
integrationBranch ??
|
|
1478
|
+
integrationBranch ?? validatedPrefBranch ?? nativeDetectMainBranch(originalBasePath_);
|
|
1458
1479
|
|
|
1459
1480
|
// Remove transient project-root state files before any branch or merge
|
|
1460
1481
|
// operation. Untracked milestone metadata can otherwise block squash merges.
|
|
@@ -76,6 +76,7 @@ import {
|
|
|
76
76
|
hasInteractiveToolInFlight,
|
|
77
77
|
clearInFlightTools,
|
|
78
78
|
isToolInvocationError,
|
|
79
|
+
isQueuedUserMessageSkip,
|
|
79
80
|
} from "./auto-tool-tracking.js";
|
|
80
81
|
import { closeoutUnit } from "./auto-unit-closeout.js";
|
|
81
82
|
import { recoverTimedOutUnit } from "./auto-timeout-recovery.js";
|
|
@@ -397,7 +398,7 @@ export function markToolEnd(toolCallId: string): void {
|
|
|
397
398
|
*/
|
|
398
399
|
export function recordToolInvocationError(toolName: string, errorMsg: string): void {
|
|
399
400
|
if (!s.active) return;
|
|
400
|
-
if (isToolInvocationError(errorMsg)) {
|
|
401
|
+
if (isToolInvocationError(errorMsg) || isQueuedUserMessageSkip(errorMsg)) {
|
|
401
402
|
s.lastToolInvocationError = `${toolName}: ${errorMsg}`;
|
|
402
403
|
}
|
|
403
404
|
}
|
|
@@ -1140,9 +1141,9 @@ export async function startAuto(
|
|
|
1140
1141
|
s.stepMode = meta.stepMode ?? requestedStepMode;
|
|
1141
1142
|
s.autoStartTime = meta.autoStartTime || Date.now();
|
|
1142
1143
|
s.paused = true;
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1144
|
+
// Don't delete pause file yet — defer until lock is acquired.
|
|
1145
|
+
// If lock fails, the file must survive for retry.
|
|
1146
|
+
s.pausedSessionFile = pausedPath;
|
|
1146
1147
|
ctx.ui.notify(
|
|
1147
1148
|
`Resuming paused custom workflow${meta.activeRunDir ? ` (${meta.activeRunDir})` : ""}.`,
|
|
1148
1149
|
"info",
|
|
@@ -1166,10 +1167,9 @@ export async function startAuto(
|
|
|
1166
1167
|
s.stepMode = meta.stepMode ?? requestedStepMode;
|
|
1167
1168
|
s.autoStartTime = meta.autoStartTime || Date.now();
|
|
1168
1169
|
s.paused = true;
|
|
1169
|
-
//
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
}
|
|
1170
|
+
// Don't delete pause file yet — defer until lock is acquired.
|
|
1171
|
+
// If lock fails, the file must survive for retry.
|
|
1172
|
+
s.pausedSessionFile = pausedPath;
|
|
1173
1173
|
ctx.ui.notify(
|
|
1174
1174
|
`Resuming paused session for ${meta.milestoneId}${meta.worktreePath ? ` (worktree)` : ""}.`,
|
|
1175
1175
|
"info",
|
|
@@ -1186,10 +1186,21 @@ export async function startAuto(
|
|
|
1186
1186
|
if (s.paused) {
|
|
1187
1187
|
const resumeLock = acquireSessionLock(base);
|
|
1188
1188
|
if (!resumeLock.acquired) {
|
|
1189
|
+
// Reset paused state so isAutoPaused() doesn't stick true after lock failure.
|
|
1190
|
+
// Pause file is preserved on disk for retry — not deleted.
|
|
1191
|
+
s.paused = false;
|
|
1189
1192
|
ctx.ui.notify(`Cannot resume: ${resumeLock.reason}`, "error");
|
|
1190
1193
|
return;
|
|
1191
1194
|
}
|
|
1192
1195
|
|
|
1196
|
+
// Lock acquired — now safe to delete the pause file
|
|
1197
|
+
if (s.pausedSessionFile) {
|
|
1198
|
+
try { unlinkSync(s.pausedSessionFile); } catch (err) {
|
|
1199
|
+
logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
|
|
1200
|
+
}
|
|
1201
|
+
s.pausedSessionFile = null;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1193
1204
|
s.paused = false;
|
|
1194
1205
|
s.active = true;
|
|
1195
1206
|
s.verbose = verboseMode;
|
|
@@ -95,12 +95,24 @@ export async function handleAgentEnd(
|
|
|
95
95
|
return;
|
|
96
96
|
}
|
|
97
97
|
if (lastMsg && "stopReason" in lastMsg && lastMsg.stopReason === "error") {
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
// #3588: errorMessage can be useless (e.g. "success") while the real error
|
|
99
|
+
// is in the assistant message text content. Fall back to content when
|
|
100
|
+
// errorMessage looks uninformative.
|
|
101
|
+
const rawErrorMsg = ("errorMessage" in lastMsg && lastMsg.errorMessage) ? String(lastMsg.errorMessage) : "";
|
|
102
|
+
const isUseless = !rawErrorMsg || /^(success|ok|true|error|unknown)$/i.test(rawErrorMsg.trim());
|
|
103
|
+
// #3588: When errorMessage is uninformative, extract the real error from
|
|
104
|
+
// the assistant message text content for display purposes only.
|
|
105
|
+
// Classification still uses rawErrorMsg to avoid false positives from prose.
|
|
106
|
+
let displayMsg = rawErrorMsg;
|
|
107
|
+
if (isUseless && "content" in lastMsg && Array.isArray(lastMsg.content)) {
|
|
108
|
+
const textBlock = lastMsg.content.find((b: any) => b.type === "text" && b.text);
|
|
109
|
+
if (textBlock) displayMsg = (textBlock as any).text.slice(0, 300);
|
|
110
|
+
}
|
|
111
|
+
const errorDetail = displayMsg ? `: ${displayMsg}` : "";
|
|
100
112
|
const explicitRetryAfterMs = ("retryAfterMs" in lastMsg && typeof lastMsg.retryAfterMs === "number") ? lastMsg.retryAfterMs : undefined;
|
|
101
113
|
|
|
102
|
-
// ── 1. Classify
|
|
103
|
-
const cls = classifyError(
|
|
114
|
+
// ── 1. Classify using rawErrorMsg to avoid prose false-positives ────
|
|
115
|
+
const cls = classifyError(rawErrorMsg, explicitRetryAfterMs);
|
|
104
116
|
|
|
105
117
|
// Cap rate-limit backoff for CLI-style providers (openai-codex, google-gemini-cli)
|
|
106
118
|
// which use per-user quotas with shorter windows (#2922).
|
|
@@ -1000,6 +1000,16 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
1000
1000
|
updateSliceStatus(params.milestoneId, params.sliceId, "skipped");
|
|
1001
1001
|
invalidateStateCache();
|
|
1002
1002
|
|
|
1003
|
+
// Rebuild STATE.md so it reflects the skip immediately (#3477).
|
|
1004
|
+
// Without this, /gsd auto reads stale STATE.md and resumes the skipped slice.
|
|
1005
|
+
try {
|
|
1006
|
+
const basePath = process.cwd();
|
|
1007
|
+
const { rebuildState } = await import("../doctor.js");
|
|
1008
|
+
await rebuildState(basePath);
|
|
1009
|
+
} catch (err) {
|
|
1010
|
+
logError("tool", `skip_slice rebuildState failed: ${(err as Error).message}`, { tool: "gsd_skip_slice" });
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1003
1013
|
return {
|
|
1004
1014
|
content: [{ type: "text" as const, text: `Skipped slice ${params.sliceId} (${params.milestoneId}). Reason: ${params.reason ?? "User-directed skip"}. Auto-mode will advance past this slice.` }],
|
|
1005
1015
|
details: {
|
|
@@ -22,17 +22,18 @@ export function registerQueryTools(pi: ExtensionAPI): void {
|
|
|
22
22
|
}),
|
|
23
23
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
24
24
|
try {
|
|
25
|
-
//
|
|
26
|
-
//
|
|
25
|
+
// Open the DB if not already open — safe for read-only use since
|
|
26
|
+
// ensureDbOpen() only creates/migrates when .gsd/ has content (#3644).
|
|
27
|
+
const { ensureDbOpen } = await import("./dynamic-tools.js");
|
|
28
|
+
const dbAvailable = await ensureDbOpen();
|
|
27
29
|
const {
|
|
28
|
-
isDbAvailable,
|
|
29
30
|
getMilestone,
|
|
30
31
|
getSliceStatusSummary,
|
|
31
32
|
getSliceTaskCounts,
|
|
32
33
|
_getAdapter,
|
|
33
34
|
} = await import("../gsd-db.js");
|
|
34
35
|
|
|
35
|
-
if (!
|
|
36
|
+
if (!dbAvailable) {
|
|
36
37
|
return {
|
|
37
38
|
content: [{ type: "text" as const, text: "Error: GSD database is not available." }],
|
|
38
39
|
details: { operation: "milestone_status", error: "db_unavailable" } as any,
|
|
@@ -36,7 +36,10 @@ function installEpipeGuard(): void {
|
|
|
36
36
|
if (handleRecoverableExtensionProcessError(err)) {
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
// Log unhandled errors instead of re-throwing — throwing inside an
|
|
40
|
+
// uncaughtException handler is a fatal double-fault in Node.js (#3163).
|
|
41
|
+
process.stderr.write(`[gsd] uncaught extension error (non-fatal): ${err.message}\n`);
|
|
42
|
+
if (err.stack) process.stderr.write(`${err.stack}\n`);
|
|
40
43
|
};
|
|
41
44
|
process.on("uncaughtException", _gsdEpipeGuard);
|
|
42
45
|
}
|
|
@@ -6,7 +6,7 @@ import { isToolCallEventType } from "@gsd/pi-coding-agent";
|
|
|
6
6
|
import { buildMilestoneFileName, resolveMilestonePath, resolveSliceFile, resolveSlicePath } from "../paths.js";
|
|
7
7
|
import { buildBeforeAgentStartResult } from "./system-context.js";
|
|
8
8
|
import { handleAgentEnd } from "./agent-end-recovery.js";
|
|
9
|
-
import { clearDiscussionFlowState, isDepthVerified, isQueuePhaseActive, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockQueueExecution } from "./write-gate.js";
|
|
9
|
+
import { clearDiscussionFlowState, isDepthVerified, isDepthConfirmationAnswer, isQueuePhaseActive, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockQueueExecution } from "./write-gate.js";
|
|
10
10
|
import { isBlockedStateFile, isBashWriteToStateFile, BLOCKED_WRITE_ERROR } from "../write-intercept.js";
|
|
11
11
|
import { cleanupQuickBranch } from "../quick.js";
|
|
12
12
|
import { getDiscussionMilestoneId } from "../guided-flow.js";
|
|
@@ -108,7 +108,10 @@ export function registerHooks(pi: ExtensionAPI): void {
|
|
|
108
108
|
});
|
|
109
109
|
|
|
110
110
|
pi.on("session_before_compact", async () => {
|
|
111
|
-
|
|
111
|
+
// Only cancel compaction while auto-mode is actively running.
|
|
112
|
+
// Paused auto-mode should allow compaction — the user may be doing
|
|
113
|
+
// interactive work (#3165).
|
|
114
|
+
if (isAutoActive()) {
|
|
112
115
|
return { cancel: true };
|
|
113
116
|
}
|
|
114
117
|
const basePath = process.cwd();
|
|
@@ -246,7 +249,12 @@ export function registerHooks(pi: ExtensionAPI): void {
|
|
|
246
249
|
const questions: any[] = (event.input as any)?.questions ?? [];
|
|
247
250
|
for (const question of questions) {
|
|
248
251
|
if (typeof question.id === "string" && question.id.includes("depth_verification")) {
|
|
249
|
-
|
|
252
|
+
// Only unlock the gate if the user selected the first option (confirmation).
|
|
253
|
+
// Cross-references against the question's defined options to reject free-form "Other" text.
|
|
254
|
+
const answer = details.response?.answers?.[question.id];
|
|
255
|
+
if (isDepthConfirmationAnswer(answer?.selected, question.options)) {
|
|
256
|
+
markDepthVerified();
|
|
257
|
+
}
|
|
250
258
|
break;
|
|
251
259
|
}
|
|
252
260
|
}
|
|
@@ -54,6 +54,35 @@ export function markDepthVerified(): void {
|
|
|
54
54
|
depthVerificationDone = true;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Check whether a depth_verification answer confirms the discussion is complete.
|
|
59
|
+
* Uses structural validation: the selected answer must exactly match the first
|
|
60
|
+
* option label from the question definition (the confirmation option by convention).
|
|
61
|
+
* This rejects free-form "Other" text, decline options, and garbage input without
|
|
62
|
+
* coupling to any specific label substring.
|
|
63
|
+
*
|
|
64
|
+
* @param selected The answer's selected value from details.response.answers[id].selected
|
|
65
|
+
* @param options The question's options array from event.input.questions[n].options
|
|
66
|
+
*/
|
|
67
|
+
export function isDepthConfirmationAnswer(
|
|
68
|
+
selected: unknown,
|
|
69
|
+
options?: Array<{ label?: string }>,
|
|
70
|
+
): boolean {
|
|
71
|
+
const value = Array.isArray(selected) ? selected[0] : selected;
|
|
72
|
+
if (typeof value !== "string" || !value) return false;
|
|
73
|
+
|
|
74
|
+
// If options are available, structurally validate: selected must exactly match
|
|
75
|
+
// the first option (confirmation) label. Rejects free-form "Other" and decline options.
|
|
76
|
+
if (Array.isArray(options) && options.length > 0) {
|
|
77
|
+
const confirmLabel = options[0]?.label;
|
|
78
|
+
return typeof confirmLabel === "string" && value === confirmLabel;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Fallback when options aren't available (e.g., older call sites):
|
|
82
|
+
// accept only if it contains "(Recommended)" — the prompt convention suffix.
|
|
83
|
+
return value.includes("(Recommended)");
|
|
84
|
+
}
|
|
85
|
+
|
|
57
86
|
export function shouldBlockContextWrite(
|
|
58
87
|
toolName: string,
|
|
59
88
|
inputPath: string,
|
|
@@ -71,7 +100,13 @@ export function shouldBlockContextWrite(
|
|
|
71
100
|
|
|
72
101
|
return {
|
|
73
102
|
block: true,
|
|
74
|
-
reason:
|
|
103
|
+
reason: [
|
|
104
|
+
`HARD BLOCK: Cannot write to milestone CONTEXT.md without depth verification.`,
|
|
105
|
+
`This is a mechanical gate — you MUST NOT proceed, retry, or rationalize past this block.`,
|
|
106
|
+
`Required action: call ask_user_questions with question id containing "depth_verification".`,
|
|
107
|
+
`The user MUST select the "(Recommended)" confirmation option to unlock this gate.`,
|
|
108
|
+
`If the user declines, cancels, or the tool fails, you must re-ask — not bypass.`,
|
|
109
|
+
].join(" "),
|
|
75
110
|
};
|
|
76
111
|
}
|
|
77
112
|
|
|
@@ -13,7 +13,13 @@ export interface GsdDispatchContext {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export function projectRoot(): string {
|
|
16
|
-
|
|
16
|
+
let cwd: string;
|
|
17
|
+
try {
|
|
18
|
+
cwd = process.cwd();
|
|
19
|
+
} catch {
|
|
20
|
+
// cwd directory was deleted (e.g. worktree teardown) — fall back to HOME (#3598)
|
|
21
|
+
cwd = process.env.HOME ?? "/";
|
|
22
|
+
}
|
|
17
23
|
const root = resolveProjectRoot(cwd);
|
|
18
24
|
if (root !== cwd) {
|
|
19
25
|
assertSafeDirectory(cwd);
|
|
@@ -52,6 +52,7 @@ export function showHelp(ctx: ExtensionCommandContext): void {
|
|
|
52
52
|
" /gsd cmux Manage cmux integration [status|on|off|notifications|sidebar|splits|browser]",
|
|
53
53
|
" /gsd config Set API keys for external tools",
|
|
54
54
|
" /gsd keys API key manager [list|add|remove|test|rotate|doctor]",
|
|
55
|
+
" /gsd show-config Show effective configuration (models, routing, toggles)",
|
|
55
56
|
" /gsd hooks Show post-unit hook configuration",
|
|
56
57
|
" /gsd extensions Manage extensions [list|enable|disable|info]",
|
|
57
58
|
" /gsd fast Toggle OpenAI service tier [on|off|flex|status]",
|
|
@@ -71,6 +72,9 @@ export function showHelp(ctx: ExtensionCommandContext): void {
|
|
|
71
72
|
|
|
72
73
|
export async function handleStatus(ctx: ExtensionCommandContext): Promise<void> {
|
|
73
74
|
const basePath = projectRoot();
|
|
75
|
+
// Open DB in cold sessions so status uses DB-backed state, not filesystem fallback (#3385)
|
|
76
|
+
const { ensureDbOpen } = await import("../../bootstrap/dynamic-tools.js");
|
|
77
|
+
await ensureDbOpen();
|
|
74
78
|
const state = await deriveState(basePath);
|
|
75
79
|
|
|
76
80
|
if (state.registry.length === 0) {
|
|
@@ -214,6 +218,25 @@ export async function handleCoreCommand(trimmed: string, ctx: ExtensionCommandCo
|
|
|
214
218
|
await handleCmux(trimmed.replace(/^cmux\s*/, "").trim(), ctx);
|
|
215
219
|
return true;
|
|
216
220
|
}
|
|
221
|
+
if (trimmed === "show-config") {
|
|
222
|
+
const { GSDConfigOverlay, formatConfigText } = await import("../../config-overlay.js");
|
|
223
|
+
const result = await ctx.ui.custom<void>(
|
|
224
|
+
(tui, theme, _kb, done) => new GSDConfigOverlay(tui, theme, () => done()),
|
|
225
|
+
{
|
|
226
|
+
overlay: true,
|
|
227
|
+
overlayOptions: {
|
|
228
|
+
width: "65%",
|
|
229
|
+
minWidth: 55,
|
|
230
|
+
maxHeight: "85%",
|
|
231
|
+
anchor: "center",
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
);
|
|
235
|
+
if (result === undefined) {
|
|
236
|
+
ctx.ui.notify(formatConfigText(), "info");
|
|
237
|
+
}
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
217
240
|
if (trimmed === "setup" || trimmed.startsWith("setup ")) {
|
|
218
241
|
await handleSetup(trimmed.replace(/^setup\s*/, "").trim(), ctx);
|
|
219
242
|
return true;
|
|
@@ -105,7 +105,7 @@ function discoverManifests(): Map<string, ExtensionManifest> {
|
|
|
105
105
|
const manifests = new Map<string, ExtensionManifest>();
|
|
106
106
|
if (!existsSync(extDir)) return manifests;
|
|
107
107
|
for (const entry of readdirSync(extDir, { withFileTypes: true })) {
|
|
108
|
-
if (!entry.isDirectory()) continue;
|
|
108
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
109
109
|
const m = readManifest(join(extDir, entry.name));
|
|
110
110
|
if (m) manifests.set(m.id, m);
|
|
111
111
|
}
|