gsd-pi 2.60.0-dev.d9052f5 → 2.61.0
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/resources/extensions/ask-user-questions.js +7 -4
- package/dist/resources/extensions/gsd/auto/phases.js +15 -7
- package/dist/resources/extensions/gsd/auto-dashboard.js +21 -8
- package/dist/resources/extensions/gsd/auto-dispatch.js +6 -3
- package/dist/resources/extensions/gsd/auto-model-selection.js +58 -9
- package/dist/resources/extensions/gsd/auto-post-unit.js +3 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +36 -20
- package/dist/resources/extensions/gsd/auto-recovery.js +37 -18
- package/dist/resources/extensions/gsd/auto-start.js +9 -5
- package/dist/resources/extensions/gsd/auto-timers.js +11 -5
- package/dist/resources/extensions/gsd/auto-unit-closeout.js +5 -3
- package/dist/resources/extensions/gsd/auto-verification.js +3 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +120 -55
- package/dist/resources/extensions/gsd/auto.js +39 -17
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +6 -3
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +2 -2
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +4 -10
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +2 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +11 -10
- package/dist/resources/extensions/gsd/commands/catalog.js +2 -0
- package/dist/resources/extensions/gsd/commands-codebase.js +48 -21
- package/dist/resources/extensions/gsd/commands-inspect.js +2 -1
- package/dist/resources/extensions/gsd/commands-maintenance.js +32 -19
- package/dist/resources/extensions/gsd/complexity-classifier.js +8 -4
- package/dist/resources/extensions/gsd/custom-verification.js +3 -2
- package/dist/resources/extensions/gsd/gsd-db.js +33 -13
- package/dist/resources/extensions/gsd/guided-flow.js +19 -9
- package/dist/resources/extensions/gsd/init-wizard.js +12 -0
- package/dist/resources/extensions/gsd/markdown-renderer.js +11 -9
- package/dist/resources/extensions/gsd/md-importer.js +5 -4
- package/dist/resources/extensions/gsd/milestone-actions.js +3 -2
- package/dist/resources/extensions/gsd/milestone-ids.js +2 -1
- package/dist/resources/extensions/gsd/model-router.js +156 -121
- package/dist/resources/extensions/gsd/parallel-merge.js +5 -3
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +26 -14
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +45 -0
- package/dist/resources/extensions/gsd/preferences.js +15 -3
- package/dist/resources/extensions/gsd/prompt-loader.js +3 -2
- package/dist/resources/extensions/gsd/prompts/rethink.md +1 -1
- package/dist/resources/extensions/gsd/rule-registry.js +7 -6
- package/dist/resources/extensions/gsd/safe-fs.js +6 -8
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -2
- package/dist/resources/extensions/gsd/tools/complete-slice.js +3 -2
- package/dist/resources/extensions/gsd/tools/complete-task.js +3 -2
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +3 -2
- package/dist/resources/extensions/gsd/tools/plan-slice.js +3 -2
- package/dist/resources/extensions/gsd/tools/plan-task.js +2 -1
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +4 -4
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +2 -1
- package/dist/resources/extensions/gsd/tools/reopen-task.js +2 -1
- package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -1
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +2 -1
- package/dist/resources/extensions/gsd/triage-resolution.js +11 -4
- package/dist/resources/extensions/gsd/workflow-events.js +2 -1
- package/dist/resources/extensions/gsd/workflow-logger.js +37 -4
- package/dist/resources/extensions/gsd/workflow-migration.js +14 -12
- package/dist/resources/extensions/gsd/workflow-projections.js +2 -2
- package/dist/resources/extensions/gsd/workflow-reconcile.js +2 -2
- package/dist/resources/extensions/gsd/worktree-manager.js +26 -14
- package/dist/resources/extensions/shared/interview-ui.js +3 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
- 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/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +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/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- 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/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
- package/dist/web/standalone/.next/server/chunks/2229.js +1 -1
- package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +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/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +5 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +16 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +26 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js +6 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/defaults.json +2 -2
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js +47 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js.map +1 -0
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +6 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +19 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +26 -0
- package/packages/pi-coding-agent/src/core/lsp/config.ts +7 -1
- package/packages/pi-coding-agent/src/core/lsp/defaults.json +2 -2
- package/packages/pi-coding-agent/src/core/lsp/lsp-legacy-alias.test.ts +70 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/ask-user-questions.ts +7 -3
- package/src/resources/extensions/gsd/auto/phases.ts +17 -7
- package/src/resources/extensions/gsd/auto-dashboard.ts +22 -8
- package/src/resources/extensions/gsd/auto-dispatch.ts +7 -3
- package/src/resources/extensions/gsd/auto-model-selection.ts +77 -15
- package/src/resources/extensions/gsd/auto-post-unit.ts +4 -4
- package/src/resources/extensions/gsd/auto-prompts.ts +37 -20
- package/src/resources/extensions/gsd/auto-recovery.ts +38 -18
- package/src/resources/extensions/gsd/auto-start.ts +10 -9
- package/src/resources/extensions/gsd/auto-timers.ts +12 -5
- package/src/resources/extensions/gsd/auto-unit-closeout.ts +6 -2
- package/src/resources/extensions/gsd/auto-verification.ts +3 -6
- package/src/resources/extensions/gsd/auto-worktree.ts +121 -55
- package/src/resources/extensions/gsd/auto.ts +40 -17
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +4 -3
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +2 -2
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +4 -16
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +2 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +11 -10
- package/src/resources/extensions/gsd/commands/catalog.ts +2 -0
- package/src/resources/extensions/gsd/commands-codebase.ts +52 -20
- package/src/resources/extensions/gsd/commands-inspect.ts +2 -1
- package/src/resources/extensions/gsd/commands-maintenance.ts +28 -19
- package/src/resources/extensions/gsd/complexity-classifier.ts +9 -4
- package/src/resources/extensions/gsd/custom-verification.ts +3 -2
- package/src/resources/extensions/gsd/gsd-db.ts +12 -14
- package/src/resources/extensions/gsd/guided-flow.ts +9 -8
- package/src/resources/extensions/gsd/init-wizard.ts +12 -0
- package/src/resources/extensions/gsd/markdown-renderer.ts +11 -17
- package/src/resources/extensions/gsd/md-importer.ts +5 -4
- package/src/resources/extensions/gsd/milestone-actions.ts +3 -2
- package/src/resources/extensions/gsd/milestone-ids.ts +2 -1
- package/src/resources/extensions/gsd/model-router.ts +199 -173
- package/src/resources/extensions/gsd/parallel-merge.ts +5 -3
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +18 -14
- package/src/resources/extensions/gsd/preferences-types.ts +13 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +45 -0
- package/src/resources/extensions/gsd/preferences.ts +16 -3
- package/src/resources/extensions/gsd/prompt-loader.ts +3 -2
- package/src/resources/extensions/gsd/prompts/rethink.md +1 -1
- package/src/resources/extensions/gsd/rule-registry.ts +7 -6
- package/src/resources/extensions/gsd/safe-fs.ts +6 -5
- package/src/resources/extensions/gsd/tests/capability-router.test.ts +347 -0
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +27 -2
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +1188 -0
- package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +841 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +403 -3
- package/src/resources/extensions/gsd/tests/preferences.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +284 -0
- package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +6 -6
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -6
- package/src/resources/extensions/gsd/tools/complete-slice.ts +3 -6
- package/src/resources/extensions/gsd/tools/complete-task.ts +3 -6
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +3 -6
- package/src/resources/extensions/gsd/tools/plan-slice.ts +3 -6
- package/src/resources/extensions/gsd/tools/plan-task.ts +2 -3
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +4 -6
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +2 -3
- package/src/resources/extensions/gsd/tools/reopen-task.ts +2 -3
- package/src/resources/extensions/gsd/tools/replan-slice.ts +2 -3
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +2 -3
- package/src/resources/extensions/gsd/triage-resolution.ts +11 -4
- package/src/resources/extensions/gsd/types.ts +1 -0
- package/src/resources/extensions/gsd/workflow-events.ts +2 -1
- package/src/resources/extensions/gsd/workflow-logger.ts +52 -5
- package/src/resources/extensions/gsd/workflow-migration.ts +14 -12
- package/src/resources/extensions/gsd/workflow-projections.ts +2 -2
- package/src/resources/extensions/gsd/workflow-reconcile.ts +2 -2
- package/src/resources/extensions/gsd/worktree-manager.ts +16 -14
- package/src/resources/extensions/shared/interview-ui.ts +3 -1
- package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +144 -0
- package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
- /package/dist/web/standalone/.next/static/{JVkoVYumy0cDhOQISEYdG → 72_sVF0fdrMZX707jm10G}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{JVkoVYumy0cDhOQISEYdG → 72_sVF0fdrMZX707jm10G}/_ssgManifest.js +0 -0
|
@@ -22,6 +22,7 @@ import { isClosedStatus } from "../status-guards.js";
|
|
|
22
22
|
import { renderAllProjections } from "../workflow-projections.js";
|
|
23
23
|
import { writeManifest } from "../workflow-manifest.js";
|
|
24
24
|
import { appendEvent } from "../workflow-events.js";
|
|
25
|
+
import { logWarning } from "../workflow-logger.js";
|
|
25
26
|
|
|
26
27
|
export interface ReopenTaskParams {
|
|
27
28
|
milestoneId: string;
|
|
@@ -117,9 +118,7 @@ export async function handleReopenTask(
|
|
|
117
118
|
trigger_reason: params.triggerReason,
|
|
118
119
|
});
|
|
119
120
|
} catch (hookErr) {
|
|
120
|
-
|
|
121
|
-
`gsd: reopen-task post-mutation hook warning: ${(hookErr as Error).message}\n`,
|
|
122
|
-
);
|
|
121
|
+
logWarning("tool", `reopen-task post-mutation hook warning: ${(hookErr as Error).message}`);
|
|
123
122
|
}
|
|
124
123
|
|
|
125
124
|
return {
|
|
@@ -16,6 +16,7 @@ import { renderPlanFromDb, renderReplanFromDb } from "../markdown-renderer.js";
|
|
|
16
16
|
import { renderAllProjections } from "../workflow-projections.js";
|
|
17
17
|
import { writeManifest } from "../workflow-manifest.js";
|
|
18
18
|
import { appendEvent } from "../workflow-events.js";
|
|
19
|
+
import { logWarning } from "../workflow-logger.js";
|
|
19
20
|
|
|
20
21
|
export interface ReplanSliceTaskInput {
|
|
21
22
|
taskId: string;
|
|
@@ -226,9 +227,7 @@ export async function handleReplanSlice(
|
|
|
226
227
|
trigger_reason: params.triggerReason,
|
|
227
228
|
});
|
|
228
229
|
} catch (hookErr) {
|
|
229
|
-
|
|
230
|
-
`gsd: replan-slice post-mutation hook warning: ${(hookErr as Error).message}\n`,
|
|
231
|
-
);
|
|
230
|
+
logWarning("tool", `replan-slice post-mutation hook warning: ${(hookErr as Error).message}`);
|
|
232
231
|
}
|
|
233
232
|
|
|
234
233
|
return {
|
|
@@ -22,6 +22,7 @@ import { saveFile, clearParseCache } from "../files.js";
|
|
|
22
22
|
import { invalidateStateCache } from "../state.js";
|
|
23
23
|
import { VALIDATION_VERDICTS, isValidMilestoneVerdict } from "../verdict-parser.js";
|
|
24
24
|
import { insertMilestoneValidationGates } from "../milestone-validation-gates.js";
|
|
25
|
+
import { logWarning } from "../workflow-logger.js";
|
|
25
26
|
|
|
26
27
|
export interface ValidateMilestoneParams {
|
|
27
28
|
milestoneId: string;
|
|
@@ -137,9 +138,7 @@ export async function handleValidateMilestone(
|
|
|
137
138
|
try {
|
|
138
139
|
await saveFile(validationPath, validationMd);
|
|
139
140
|
} catch (renderErr) {
|
|
140
|
-
|
|
141
|
-
`gsd-db: validate_milestone — disk render failed, rolling back DB row: ${(renderErr as Error).message}\n`,
|
|
142
|
-
);
|
|
141
|
+
logWarning("tool", `validate_milestone — disk render failed, rolling back DB row: ${(renderErr as Error).message}`);
|
|
143
142
|
deleteAssessmentByScope(params.milestoneId, 'milestone-validation');
|
|
144
143
|
return { error: `disk render failed: ${(renderErr as Error).message}` };
|
|
145
144
|
}
|
|
@@ -148,10 +148,17 @@ export function executeBacktrack(
|
|
|
148
148
|
capture: CaptureEntry,
|
|
149
149
|
): string | null {
|
|
150
150
|
try {
|
|
151
|
-
// Extract target milestone from capture text or resolution
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const
|
|
151
|
+
// Extract target milestone from capture text or resolution.
|
|
152
|
+
// Filter out the current milestone ID to avoid picking it as the backtrack target
|
|
153
|
+
// when the text mentions both current and target milestones (e.g. "backtrack from M004 to M003").
|
|
154
|
+
const sourceText = capture.resolution ?? capture.text;
|
|
155
|
+
const allMatches = [...sourceText.matchAll(/\b(M\d{3}(?:-[a-z0-9]{6})?)\b/g)]
|
|
156
|
+
.map(m => m[1])
|
|
157
|
+
.filter(id => id !== currentMilestoneId);
|
|
158
|
+
// Reject ambiguous multi-target strings — if more than one distinct target remains,
|
|
159
|
+
// don't guess; let the user clarify.
|
|
160
|
+
const uniqueTargets = [...new Set(allMatches)];
|
|
161
|
+
const targetMilestoneId = uniqueTargets.length === 1 ? uniqueTargets[0] : null;
|
|
155
162
|
|
|
156
163
|
const ts = new Date().toISOString();
|
|
157
164
|
const triggerPath = join(gsdRoot(basePath), "BACKTRACK-TRIGGER.md");
|
|
@@ -2,6 +2,7 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
2
2
|
import { appendFileSync, readFileSync, existsSync, mkdirSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
5
|
+
import { logWarning } from "./workflow-logger.js";
|
|
5
6
|
|
|
6
7
|
// ─── Session ID ───────────────────────────────────────────────────────────
|
|
7
8
|
|
|
@@ -74,7 +75,7 @@ export function readEvents(logPath: string): WorkflowEvent[] {
|
|
|
74
75
|
try {
|
|
75
76
|
events.push(JSON.parse(line) as WorkflowEvent);
|
|
76
77
|
} catch {
|
|
77
|
-
|
|
78
|
+
logWarning("event-log", `skipping corrupted event line (${line.length} bytes)`);
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
// Centralized warning/error accumulator for the workflow engine pipeline.
|
|
3
3
|
// Captures structured entries that the auto-loop can drain after each unit
|
|
4
4
|
// to surface root causes for stuck loops, silent degradation, and blocked writes.
|
|
5
|
-
//
|
|
5
|
+
// Error-severity entries are persisted to .gsd/audit-log.jsonl (sanitized) for
|
|
6
|
+
// post-mortem analysis. Warnings are ephemeral (stderr + buffer only) to avoid
|
|
7
|
+
// log amplification from expected-control-flow catch paths.
|
|
6
8
|
//
|
|
7
9
|
// Stderr policy: every logWarning/logError call writes immediately to stderr
|
|
8
10
|
// for terminal visibility. This is intentional — unlike debug-logger (which is
|
|
@@ -33,7 +35,20 @@ export type LogComponent =
|
|
|
33
35
|
| "compaction" // Event compaction
|
|
34
36
|
| "reconcile" // Worktree reconciliation
|
|
35
37
|
| "db" // Database operations (gsd-db)
|
|
36
|
-
| "dispatch"
|
|
38
|
+
| "dispatch" // Auto-dispatch rule evaluation
|
|
39
|
+
| "recovery" // Auto-recovery and timeout recovery
|
|
40
|
+
| "session" // Session lock and session state I/O
|
|
41
|
+
| "prompt" // Prompt construction and context injection
|
|
42
|
+
| "dashboard" // Auto-dashboard rendering
|
|
43
|
+
| "timer" // Auto-timers (idle watchdog, hard timeout)
|
|
44
|
+
| "worktree" // Worktree lifecycle (create, sync, merge)
|
|
45
|
+
| "command" // Slash command execution and maintenance
|
|
46
|
+
| "parallel" // Parallel orchestrator and merge
|
|
47
|
+
| "fs" // Safe filesystem operations
|
|
48
|
+
| "bootstrap" // Extension bootstrap (system-context, agent-end)
|
|
49
|
+
| "guided" // Guided flow (discuss, plan wizards)
|
|
50
|
+
| "registry" // Rule registry hook state
|
|
51
|
+
| "renderer"; // Markdown renderer and projections
|
|
37
52
|
|
|
38
53
|
export interface LogEntry {
|
|
39
54
|
ts: string;
|
|
@@ -230,15 +245,47 @@ function _push(
|
|
|
230
245
|
_buffer.shift();
|
|
231
246
|
}
|
|
232
247
|
|
|
233
|
-
// Persist to .gsd/audit-log.jsonl so
|
|
234
|
-
|
|
248
|
+
// Persist errors to .gsd/audit-log.jsonl so they survive context resets.
|
|
249
|
+
// Only error-severity entries are persisted — warnings are ephemeral (stderr + buffer)
|
|
250
|
+
// to avoid log amplification from expected-control-flow catch paths.
|
|
251
|
+
if (_auditBasePath && severity === "error") {
|
|
235
252
|
try {
|
|
236
253
|
const auditDir = join(_auditBasePath, ".gsd");
|
|
237
254
|
mkdirSync(auditDir, { recursive: true });
|
|
238
|
-
|
|
255
|
+
const sanitized = _sanitizeForAudit(entry);
|
|
256
|
+
appendFileSync(join(auditDir, "audit-log.jsonl"), JSON.stringify(sanitized) + "\n", "utf-8");
|
|
239
257
|
} catch (auditErr) {
|
|
240
258
|
// Best-effort — never let audit write failures bubble up
|
|
241
259
|
process.stderr.write(`[gsd:audit] failed to persist log entry: ${(auditErr as Error).message}\n`);
|
|
242
260
|
}
|
|
243
261
|
}
|
|
244
262
|
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Sanitize a log entry before persisting to the audit JSONL file.
|
|
266
|
+
* Strips potentially sensitive context (raw paths, cwd, full error text)
|
|
267
|
+
* to avoid leaking local environment details into durable telemetry.
|
|
268
|
+
*/
|
|
269
|
+
function _sanitizeForAudit(entry: LogEntry): LogEntry {
|
|
270
|
+
const sanitized: LogEntry = {
|
|
271
|
+
ts: entry.ts,
|
|
272
|
+
severity: entry.severity,
|
|
273
|
+
component: entry.component,
|
|
274
|
+
// Truncate message to avoid persisting oversized raw error dumps
|
|
275
|
+
message: entry.message.length > 200 ? entry.message.slice(0, 200) + "…[truncated]" : entry.message,
|
|
276
|
+
};
|
|
277
|
+
if (entry.context) {
|
|
278
|
+
// Allowlist: only persist known-safe structured keys
|
|
279
|
+
const SAFE_KEYS = new Set(["fn", "tool", "mid", "sid", "tid", "worktree"]);
|
|
280
|
+
const filtered: Record<string, string> = {};
|
|
281
|
+
for (const [k, v] of Object.entries(entry.context)) {
|
|
282
|
+
if (SAFE_KEYS.has(k)) {
|
|
283
|
+
filtered[k] = v;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (Object.keys(filtered).length > 0) {
|
|
287
|
+
sanitized.context = filtered;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return sanitized;
|
|
291
|
+
}
|
|
@@ -7,6 +7,7 @@ import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import { _getAdapter, transaction } from "./gsd-db.js";
|
|
9
9
|
import { parseRoadmap, parsePlan } from "./parsers-legacy.js";
|
|
10
|
+
import { logWarning } from "./workflow-logger.js";
|
|
10
11
|
|
|
11
12
|
// ─── needsAutoMigration ───────────────────────────────────────────────────
|
|
12
13
|
|
|
@@ -23,8 +24,8 @@ export function needsAutoMigration(basePath: string): boolean {
|
|
|
23
24
|
try {
|
|
24
25
|
const row = db.prepare("SELECT COUNT(*) as cnt FROM milestones").get();
|
|
25
26
|
if (row && (row["cnt"] as number) > 0) return false;
|
|
26
|
-
} catch {
|
|
27
|
-
|
|
27
|
+
} catch (e) {
|
|
28
|
+
logWarning("migration", `DB probe failed: ${(e as Error).message}`);
|
|
28
29
|
return false;
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -71,7 +72,7 @@ export function migrateFromMarkdown(basePath: string): void {
|
|
|
71
72
|
.filter(e => e.isDirectory())
|
|
72
73
|
.map(e => e.name);
|
|
73
74
|
} catch {
|
|
74
|
-
|
|
75
|
+
logWarning("migration", "failed to read milestones directory");
|
|
75
76
|
return;
|
|
76
77
|
}
|
|
77
78
|
|
|
@@ -141,7 +142,7 @@ export function migrateFromMarkdown(basePath: string): void {
|
|
|
141
142
|
risk: s.risk || "low",
|
|
142
143
|
}));
|
|
143
144
|
} catch (err) {
|
|
144
|
-
|
|
145
|
+
logWarning("migration", `failed to parse ROADMAP.md for ${mId}: ${(err as Error).message}`);
|
|
145
146
|
// Still add milestone with ID as title
|
|
146
147
|
milestoneInserts.push({ id: mId, title: mId, status: milestoneStatus });
|
|
147
148
|
}
|
|
@@ -191,7 +192,7 @@ export function migrateFromMarkdown(basePath: string): void {
|
|
|
191
192
|
});
|
|
192
193
|
}
|
|
193
194
|
} catch (err) {
|
|
194
|
-
|
|
195
|
+
logWarning("migration", `failed to parse ${slice.id}-PLAN.md for ${mId}: ${(err as Error).message}`);
|
|
195
196
|
}
|
|
196
197
|
}
|
|
197
198
|
}
|
|
@@ -206,8 +207,8 @@ export function migrateFromMarkdown(basePath: string): void {
|
|
|
206
207
|
process.stderr.write(`workflow-migration: orphaned summary file ${summaryFile} in ${mId} (slice not found in ROADMAP.md), skipping\n`);
|
|
207
208
|
}
|
|
208
209
|
}
|
|
209
|
-
} catch {
|
|
210
|
-
|
|
210
|
+
} catch (e) {
|
|
211
|
+
logWarning("migration", `Orphaned summary check failed for ${mId}: ${(e as Error).message}`);
|
|
211
212
|
}
|
|
212
213
|
}
|
|
213
214
|
|
|
@@ -308,17 +309,18 @@ export function validateMigration(basePath: string): { discrepancies: string[] }
|
|
|
308
309
|
const planContent = readFileSync(planPath, "utf-8");
|
|
309
310
|
const plan = parsePlan(planContent);
|
|
310
311
|
mdTaskCount += plan.tasks.length;
|
|
311
|
-
} catch {
|
|
312
|
-
|
|
312
|
+
} catch (e) {
|
|
313
|
+
logWarning("migration", `Failed to read plan ${slice.id}-PLAN.md: ${(e as Error).message}`);
|
|
313
314
|
}
|
|
314
315
|
}
|
|
315
316
|
}
|
|
316
|
-
} catch {
|
|
317
|
-
|
|
317
|
+
} catch (e) {
|
|
318
|
+
logWarning("migration", `Failed to read roadmap for ${mId}: ${(e as Error).message}`);
|
|
318
319
|
}
|
|
319
320
|
}
|
|
320
321
|
}
|
|
321
|
-
} catch {
|
|
322
|
+
} catch (e) {
|
|
323
|
+
logWarning("migration", `Validation failed to read markdown: ${(e as Error).message}`);
|
|
322
324
|
return { discrepancies: ["Failed to read markdown for validation"] };
|
|
323
325
|
}
|
|
324
326
|
|
|
@@ -423,7 +423,7 @@ export function regenerateIfMissing(
|
|
|
423
423
|
renderSummaryProjection(basePath, milestoneId, sliceId, task.id);
|
|
424
424
|
regenerated++;
|
|
425
425
|
} catch (err) {
|
|
426
|
-
|
|
426
|
+
logWarning("projection", `regenerateIfMissing SUMMARY failed for ${task.id}: ${(err as Error).message}`);
|
|
427
427
|
}
|
|
428
428
|
}
|
|
429
429
|
}
|
|
@@ -452,7 +452,7 @@ export function regenerateIfMissing(
|
|
|
452
452
|
}
|
|
453
453
|
return true;
|
|
454
454
|
} catch (err) {
|
|
455
|
-
|
|
455
|
+
logWarning("projection", `regenerateIfMissing ${fileType} failed: ${(err as Error).message}`);
|
|
456
456
|
return false;
|
|
457
457
|
}
|
|
458
458
|
}
|
|
@@ -455,8 +455,8 @@ function parseEventBlock(block: string): WorkflowEvent[] {
|
|
|
455
455
|
if (paramsMatch) {
|
|
456
456
|
try {
|
|
457
457
|
params = JSON.parse(paramsMatch[1]!) as Record<string, unknown>;
|
|
458
|
-
} catch {
|
|
459
|
-
|
|
458
|
+
} catch (e) {
|
|
459
|
+
logWarning("reconcile", `tool call params parse failed: ${(e as Error).message}`);
|
|
460
460
|
}
|
|
461
461
|
i++; // consume params line
|
|
462
462
|
}
|
|
@@ -95,8 +95,8 @@ export function resolveGitDir(basePath: string): string {
|
|
|
95
95
|
if (content.startsWith("gitdir: ")) {
|
|
96
96
|
return resolve(basePath, content.slice(8));
|
|
97
97
|
}
|
|
98
|
-
} catch {
|
|
99
|
-
|
|
98
|
+
} catch (e) {
|
|
99
|
+
logWarning("worktree", `.git file read failed: ${(e as Error).message}`);
|
|
100
100
|
}
|
|
101
101
|
return join(basePath, ".git");
|
|
102
102
|
}
|
|
@@ -308,8 +308,9 @@ export function findNestedGitDirs(rootPath: string): string[] {
|
|
|
308
308
|
let entries: string[];
|
|
309
309
|
try {
|
|
310
310
|
entries = readdirSync(dir);
|
|
311
|
-
} catch {
|
|
312
|
-
|
|
311
|
+
} catch (e) {
|
|
312
|
+
logWarning("worktree", `readdirSync failed: ${(e as Error).message}`);
|
|
313
|
+
return;
|
|
313
314
|
}
|
|
314
315
|
|
|
315
316
|
for (const entry of entries) {
|
|
@@ -321,7 +322,8 @@ export function findNestedGitDirs(rootPath: string): string[] {
|
|
|
321
322
|
let stat;
|
|
322
323
|
try {
|
|
323
324
|
stat = lstatSync(fullPath);
|
|
324
|
-
} catch {
|
|
325
|
+
} catch (e) {
|
|
326
|
+
logWarning("worktree", `lstatSync failed for ${fullPath}: ${(e as Error).message}`);
|
|
325
327
|
continue;
|
|
326
328
|
}
|
|
327
329
|
if (!stat.isDirectory()) continue;
|
|
@@ -337,8 +339,8 @@ export function findNestedGitDirs(rootPath: string): string[] {
|
|
|
337
339
|
// Don't recurse into the nested repo — we found what we need
|
|
338
340
|
continue;
|
|
339
341
|
}
|
|
340
|
-
} catch {
|
|
341
|
-
|
|
342
|
+
} catch (e) {
|
|
343
|
+
logWarning("worktree", `existsSync/.git check failed for ${fullPath}: ${(e as Error).message}`);
|
|
342
344
|
}
|
|
343
345
|
|
|
344
346
|
walk(fullPath, depth + 1);
|
|
@@ -374,7 +376,7 @@ export function removeWorktree(
|
|
|
374
376
|
if (entry?.path) {
|
|
375
377
|
wtPath = entry.path;
|
|
376
378
|
}
|
|
377
|
-
} catch {
|
|
379
|
+
} catch (e) { logWarning("worktree", `nativeWorktreeList parse failed: ${(e as Error).message}`); }
|
|
378
380
|
|
|
379
381
|
const resolvedWtPath = existsSync(wtPath) ? realpathSync(wtPath) : wtPath;
|
|
380
382
|
|
|
@@ -388,7 +390,7 @@ export function removeWorktree(
|
|
|
388
390
|
if (!existsSync(wtPath)) {
|
|
389
391
|
nativeWorktreePrune(basePath);
|
|
390
392
|
if (deleteBranch) {
|
|
391
|
-
try { nativeBranchDelete(basePath, branch, true); } catch {
|
|
393
|
+
try { nativeBranchDelete(basePath, branch, true); } catch (e) { logWarning("worktree", `nativeBranchDelete failed: ${(e as Error).message}`); }
|
|
392
394
|
}
|
|
393
395
|
return;
|
|
394
396
|
}
|
|
@@ -422,8 +424,8 @@ export function removeWorktree(
|
|
|
422
424
|
logWarning("reconcile", `Submodule changes detected — stash failed, changes may be lost during force removal`, { worktree: name, path: resolvedWtPath });
|
|
423
425
|
}
|
|
424
426
|
}
|
|
425
|
-
} catch {
|
|
426
|
-
|
|
427
|
+
} catch (e) {
|
|
428
|
+
logWarning("worktree", `submodule status check failed: ${(e as Error).message}`);
|
|
427
429
|
}
|
|
428
430
|
}
|
|
429
431
|
|
|
@@ -454,11 +456,11 @@ export function removeWorktree(
|
|
|
454
456
|
// Remove worktree: try non-force first when submodules have changes,
|
|
455
457
|
// falling back to force only after submodule state has been preserved.
|
|
456
458
|
const useForce = hasSubmoduleChanges ? false : force;
|
|
457
|
-
try { nativeWorktreeRemove(basePath, resolvedWtPath, useForce); } catch {
|
|
459
|
+
try { nativeWorktreeRemove(basePath, resolvedWtPath, useForce); } catch (e) { logWarning("worktree", `nativeWorktreeRemove failed: ${(e as Error).message}`); }
|
|
458
460
|
|
|
459
461
|
// If the directory is still there (e.g. locked), try harder with force
|
|
460
462
|
if (existsSync(resolvedWtPath)) {
|
|
461
|
-
try { nativeWorktreeRemove(basePath, resolvedWtPath, true); } catch {
|
|
463
|
+
try { nativeWorktreeRemove(basePath, resolvedWtPath, true); } catch (e) { logWarning("worktree", `nativeWorktreeRemove (force) failed: ${(e as Error).message}`); }
|
|
462
464
|
}
|
|
463
465
|
|
|
464
466
|
// (#2821) If the worktree directory STILL exists after both native removal
|
|
@@ -488,7 +490,7 @@ export function removeWorktree(
|
|
|
488
490
|
nativeWorktreePrune(basePath);
|
|
489
491
|
|
|
490
492
|
if (deleteBranch) {
|
|
491
|
-
try { nativeBranchDelete(basePath, branch, true); } catch {
|
|
493
|
+
try { nativeBranchDelete(basePath, branch, true); } catch (e) { logWarning("worktree", `final branch delete failed: ${(e as Error).message}`); }
|
|
492
494
|
}
|
|
493
495
|
}
|
|
494
496
|
|
|
@@ -298,7 +298,9 @@ export async function showInterviewRound(
|
|
|
298
298
|
// Auto-open the notes field when "None of the above" is selected
|
|
299
299
|
// so the user can immediately provide a free-text explanation
|
|
300
300
|
// instead of being trapped in a re-asking loop (bug #2715).
|
|
301
|
-
if
|
|
301
|
+
// Only auto-open if the user hasn't already provided notes —
|
|
302
|
+
// otherwise Enter from notes mode loops back here endlessly.
|
|
303
|
+
if (!isMultiSelect(currentIdx) && states[currentIdx].cursorIndex === noneOrDoneIdx(currentIdx) && !states[currentIdx].notes) {
|
|
302
304
|
states[currentIdx].notesVisible = true;
|
|
303
305
|
focusNotes = true;
|
|
304
306
|
loadStateToEditor();
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// GSD2 — Regression test for interview-ui "None of the above" notes loop
|
|
2
|
+
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Regression test for bug #3502:
|
|
6
|
+
*
|
|
7
|
+
* Selecting "None of the above" opens the notes field, but pressing Enter
|
|
8
|
+
* after typing a note called goNextOrSubmit() which saw the cursor still
|
|
9
|
+
* on the "None of the above" slot and re-opened notes — trapping the user
|
|
10
|
+
* in an infinite loop.
|
|
11
|
+
*
|
|
12
|
+
* The fix adds a `!states[currentIdx].notes` guard so auto-open only fires
|
|
13
|
+
* when notes are still empty.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { describe, it } from "node:test";
|
|
17
|
+
import assert from "node:assert/strict";
|
|
18
|
+
import { showInterviewRound, type Question, type RoundResult } from "../interview-ui.js";
|
|
19
|
+
|
|
20
|
+
// Raw terminal sequences that matchesKey() recognises
|
|
21
|
+
const ENTER = "\r";
|
|
22
|
+
const DOWN = "\x1b[B";
|
|
23
|
+
const TAB = "\t";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Drive showInterviewRound with a scripted sequence of key inputs.
|
|
27
|
+
* We mock ctx.ui.custom() to capture the widget, feed it inputs, and
|
|
28
|
+
* resolve when done() is called.
|
|
29
|
+
*/
|
|
30
|
+
function runWithInputs(
|
|
31
|
+
questions: Question[],
|
|
32
|
+
inputs: string[],
|
|
33
|
+
): Promise<RoundResult> {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const timeout = setTimeout(() => reject(new Error("Timed out — likely stuck in infinite loop")), 3000);
|
|
36
|
+
|
|
37
|
+
const mockCtx = {
|
|
38
|
+
ui: {
|
|
39
|
+
custom: (factory: any) => {
|
|
40
|
+
const mockTui = {
|
|
41
|
+
requestRender: () => {},
|
|
42
|
+
};
|
|
43
|
+
const mockTheme = {
|
|
44
|
+
// Minimal theme stubs — render output is not asserted
|
|
45
|
+
fg: (_c: string, t: string) => t,
|
|
46
|
+
bold: (t: string) => t,
|
|
47
|
+
dim: (t: string) => t,
|
|
48
|
+
italic: (t: string) => t,
|
|
49
|
+
strikethrough: (t: string) => t,
|
|
50
|
+
accent: (t: string) => t,
|
|
51
|
+
success: (t: string) => t,
|
|
52
|
+
warning: (t: string) => t,
|
|
53
|
+
error: (t: string) => t,
|
|
54
|
+
info: (t: string) => t,
|
|
55
|
+
muted: (t: string) => t,
|
|
56
|
+
dimmed: (t: string) => t,
|
|
57
|
+
};
|
|
58
|
+
const mockKb = {};
|
|
59
|
+
|
|
60
|
+
const widget = factory(mockTui, mockTheme, mockKb, (result: RoundResult) => {
|
|
61
|
+
clearTimeout(timeout);
|
|
62
|
+
resolve(result);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Feed each input sequentially
|
|
66
|
+
for (const input of inputs) {
|
|
67
|
+
widget.handleInput(input);
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
showInterviewRound(questions, {}, mockCtx as any).catch(reject);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
describe("interview-ui notes loop regression (#3502)", () => {
|
|
78
|
+
const questions: Question[] = [
|
|
79
|
+
{
|
|
80
|
+
id: "q1",
|
|
81
|
+
header: "Project Type",
|
|
82
|
+
question: "What type of project?",
|
|
83
|
+
options: [
|
|
84
|
+
{ label: "Web App", description: "Frontend or full-stack" },
|
|
85
|
+
{ label: "CLI Tool", description: "Command-line utility" },
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
it("does not loop when Enter is pressed after typing a note on 'None of the above'", async () => {
|
|
91
|
+
// With 2 options, "None of the above" is index 2 (0-based)
|
|
92
|
+
// Cursor starts at 0, so press Down twice to reach it
|
|
93
|
+
const result = await runWithInputs(questions, [
|
|
94
|
+
DOWN, // cursor → index 1 (CLI Tool)
|
|
95
|
+
DOWN, // cursor → index 2 (None of the above)
|
|
96
|
+
ENTER, // commit → auto-opens notes field
|
|
97
|
+
"u", "n", "s", "u", "r", "e", // type "unsure"
|
|
98
|
+
ENTER, // should advance to review, NOT reopen notes
|
|
99
|
+
ENTER, // submit from review screen
|
|
100
|
+
]);
|
|
101
|
+
|
|
102
|
+
// If we get here, the loop did not occur (timeout would have fired)
|
|
103
|
+
assert.ok(result, "should return a result");
|
|
104
|
+
assert.equal(result.endInterview, false);
|
|
105
|
+
|
|
106
|
+
const answer = result.answers.q1;
|
|
107
|
+
assert.ok(answer, "answer for q1 should exist");
|
|
108
|
+
assert.equal(answer.notes, "unsure", "notes should contain typed text");
|
|
109
|
+
assert.equal(answer.selected, "None of the above");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("still auto-opens notes when selecting 'None of the above' with no prior notes", async () => {
|
|
113
|
+
// Press Down twice to "None of the above", Enter to select
|
|
114
|
+
// Then immediately Enter again (empty notes) — this should re-open notes
|
|
115
|
+
// because the guard only skips when notes are non-empty.
|
|
116
|
+
// Type something on second open, then Enter to proceed.
|
|
117
|
+
const result = await runWithInputs(questions, [
|
|
118
|
+
DOWN, // cursor → 1
|
|
119
|
+
DOWN, // cursor → 2 (None of the above)
|
|
120
|
+
ENTER, // commit → auto-opens notes
|
|
121
|
+
ENTER, // empty notes → goNextOrSubmit → should re-open notes (empty guard)
|
|
122
|
+
"o", "k", // type "ok"
|
|
123
|
+
ENTER, // now notes = "ok" → should advance to review
|
|
124
|
+
ENTER, // submit
|
|
125
|
+
]);
|
|
126
|
+
|
|
127
|
+
assert.ok(result, "should return a result");
|
|
128
|
+
const answer = result.answers.q1;
|
|
129
|
+
assert.ok(answer, "answer for q1 should exist");
|
|
130
|
+
assert.equal(answer.notes, "ok");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("normal option selection is unaffected", async () => {
|
|
134
|
+
const result = await runWithInputs(questions, [
|
|
135
|
+
ENTER, // select first option (Web App) and advance to review
|
|
136
|
+
ENTER, // submit from review screen
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
assert.ok(result, "should return a result");
|
|
140
|
+
const answer = result.answers.q1;
|
|
141
|
+
assert.ok(answer, "answer for q1 should exist");
|
|
142
|
+
assert.equal(answer.selected, "Web App");
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[8974],{2600:(e,t,n)=>{Promise.resolve().then(n.bind(n,66919))},5214:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"workAsyncStorage",{enumerable:!0,get:function(){return r.workAsyncStorageInstance}});let r=n(17828)},17828:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"workAsyncStorageInstance",{enumerable:!0,get:function(){return r}});let r=(0,n(64054).createAsyncLocalStorage)()},21957:(e,t,n)=>{"use strict";function r({moduleIds:e}){return null}Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"PreloadChunks",{enumerable:!0,get:function(){return r}}),n(95155),n(47650),n(5214),n(2451),n(53887)},37206:(e,t,n)=>{"use strict";n.d(t,{default:()=>u.a});var r=n(75707),u=n.n(r)},41112:(e,t,n)=>{"use strict";function r({reason:e,children:t}){return t}Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"BailoutToCSR",{enumerable:!0,get:function(){return r}}),n(1980)},64054:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n={bindSnapshot:function(){return s},createAsyncLocalStorage:function(){return a},createSnapshot:function(){return i}};for(var r in n)Object.defineProperty(t,r,{enumerable:!0,get:n[r]});let u=Object.defineProperty(Error("Invariant: AsyncLocalStorage accessed in runtime where it is not available"),"__NEXT_ERROR_CODE",{value:"E504",enumerable:!1,configurable:!0});class l{disable(){throw u}getStore(){}run(){throw u}exit(){throw u}enterWith(){throw u}static bind(e){return e}}let o="u">typeof globalThis&&globalThis.AsyncLocalStorage;function a(){return o?new o:new l}function s(e){return o?o.bind(e):l.bind(e)}function i(){return o?o.snapshot():function(e,...t){return e(...t)}}},66919:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>l});var r=n(95155);let u=(0,n(37206).default)(()=>Promise.all([n.e(1838),n.e(6079),n.e(4986),n.e(2008),n.e(6502)]).then(n.bind(n,46502)).then(e=>e.GSDAppShell),{loadableGenerated:{webpack:()=>[46502]},ssr:!1,loading:()=>(0,r.jsx)("div",{className:"flex h-screen items-center justify-center bg-background text-sm text-muted-foreground",children:"Loading workspace…"})});function l(){return(0,r.jsx)(u,{})}},68635:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return s}});let r=n(95155),u=n(12115),l=n(41112);function o(e){return{default:e&&"default"in e?e.default:e}}n(21957);let a={loader:()=>Promise.resolve(o(()=>null)),loading:null,ssr:!0},s=function(e){let t={...a,...e},n=(0,u.lazy)(()=>t.loader().then(o)),s=t.loading;function i(e){let o=s?(0,r.jsx)(s,{isLoading:!0,pastDelay:!0,error:null}):null,a=!t.ssr||!!t.loading,i=a?u.Suspense:u.Fragment,c=t.ssr?(0,r.jsxs)(r.Fragment,{children:[null,(0,r.jsx)(n,{...e})]}):(0,r.jsx)(l.BailoutToCSR,{reason:"next/dynamic",children:(0,r.jsx)(n,{...e})});return(0,r.jsx)(i,{...a?{fallback:o}:{},children:c})}return i.displayName="LoadableComponent",i}},75707:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return u}});let r=n(73623)._(n(68635));function u(e,t){let n={};"function"==typeof e&&(n.loader=e);let u={...n,...t};return(0,r.default)({...u,modules:u.loadableGenerated?.modules})}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)}},e=>{e.O(0,[8441,3794,7358],()=>e(e.s=2600)),_N_E=e.O()}]);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[7358],{19393:()=>{},55548:(e,s,n)=>{Promise.resolve().then(n.t.bind(n,27123,23)),Promise.resolve().then(n.t.bind(n,61304,23)),Promise.resolve().then(n.t.bind(n,78616,23)),Promise.resolve().then(n.t.bind(n,64777,23)),Promise.resolve().then(n.t.bind(n,57121,23)),Promise.resolve().then(n.t.bind(n,74581,23)),Promise.resolve().then(n.t.bind(n,90484,23)),Promise.resolve().then(n.bind(n,86869))}},e=>{var s=s=>e(e.s=s);e.O(0,[8441,3794],()=>(s(83861),s(55548))),_N_E=e.O()}]);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[9337],{43946:(e,s,_)=>{Promise.resolve().then(_.t.bind(_,27123,23))}},e=>{e.O(0,[8441,3794,7358],()=>e(e.s=43946)),_N_E=e.O()}]);
|
|
File without changes
|
|
File without changes
|