gsd-pi 2.44.0-dev.d25d507 → 2.45.0-dev.1afbdaa
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/help-text.js +1 -1
- package/dist/loader.js +34 -0
- package/dist/resources/extensions/gsd/activity-log.js +7 -0
- package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
- package/dist/resources/extensions/gsd/auto/phases.js +63 -77
- package/dist/resources/extensions/gsd/auto/run-unit.js +6 -3
- package/dist/resources/extensions/gsd/auto/session.js +0 -11
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +112 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +25 -96
- package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
- package/dist/resources/extensions/gsd/auto-start.js +23 -5
- package/dist/resources/extensions/gsd/auto-timers.js +57 -3
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +14 -10
- package/dist/resources/extensions/gsd/auto.js +42 -60
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +170 -11
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +18 -0
- package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
- package/dist/resources/extensions/gsd/commands/context.js +0 -4
- package/dist/resources/extensions/gsd/commands/handlers/core.js +2 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +10 -0
- package/dist/resources/extensions/gsd/commands/handlers/parallel.js +1 -1
- package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
- package/dist/resources/extensions/gsd/crash-recovery.js +2 -4
- package/dist/resources/extensions/gsd/dashboard-overlay.js +0 -44
- package/dist/resources/extensions/gsd/db-writer.js +40 -22
- package/dist/resources/extensions/gsd/doctor-checks.js +167 -2
- package/dist/resources/extensions/gsd/doctor.js +13 -3
- package/dist/resources/extensions/gsd/git-service.js +8 -3
- package/dist/resources/extensions/gsd/gsd-db.js +28 -4
- package/dist/resources/extensions/gsd/guided-flow.js +1 -2
- package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
- package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +5 -18
- package/dist/resources/extensions/gsd/preferences-types.js +2 -2
- package/dist/resources/extensions/gsd/preferences.js +8 -4
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +21 -10
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -23
- package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
- package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -15
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +5 -3
- package/dist/resources/extensions/gsd/prompts/queue.md +2 -2
- package/dist/resources/extensions/gsd/prompts/quick-task.md +2 -0
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
- package/dist/resources/extensions/gsd/prompts/research-slice.md +3 -3
- package/dist/resources/extensions/gsd/prompts/rethink.md +83 -0
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
- package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
- package/dist/resources/extensions/gsd/repo-identity.js +45 -7
- package/dist/resources/extensions/gsd/rethink.js +115 -0
- package/dist/resources/extensions/gsd/session-lock.js +1 -3
- package/dist/resources/extensions/gsd/state.js +48 -3
- package/dist/resources/extensions/gsd/sync-lock.js +89 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +61 -11
- package/dist/resources/extensions/gsd/tools/complete-slice.js +56 -11
- package/dist/resources/extensions/gsd/tools/complete-task.js +50 -2
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +37 -1
- package/dist/resources/extensions/gsd/tools/plan-slice.js +31 -1
- package/dist/resources/extensions/gsd/tools/plan-task.js +28 -1
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +32 -2
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +86 -0
- package/dist/resources/extensions/gsd/tools/reopen-task.js +90 -0
- package/dist/resources/extensions/gsd/tools/replan-slice.js +34 -2
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
- package/dist/resources/extensions/gsd/unit-ownership.js +85 -0
- package/dist/resources/extensions/gsd/workflow-events.js +102 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +193 -0
- package/dist/resources/extensions/gsd/workflow-manifest.js +244 -0
- package/dist/resources/extensions/gsd/workflow-migration.js +280 -0
- package/dist/resources/extensions/gsd/workflow-projections.js +373 -0
- package/dist/resources/extensions/gsd/workflow-reconcile.js +411 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +34 -3
- package/dist/resources/extensions/gsd/worktree-resolver.js +43 -0
- package/dist/resources/extensions/gsd/write-intercept.js +84 -0
- package/dist/resources/extensions/mcp-client/index.js +14 -0
- package/dist/resources/extensions/voice/index.js +11 -16
- package/dist/resources/extensions/voice/linux-ready.js +67 -0
- 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/react-loadable-manifest.json +2 -2
- 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 +1 -1
- 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 +2 -2
- 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 +2 -2
- 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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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/export-data/route_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.js +1 -1
- 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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.js +1 -1
- 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_client-reference-manifest.js +1 -1
- 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_client-reference-manifest.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_client-reference-manifest.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_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 +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/page.js +1 -1
- 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/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/4024.11ca5c01938e5948.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{3721.bf31263de6d5fa46.js → 485.243af25f0cdf50d6.js} +2 -2
- package/dist/web/standalone/.next/static/chunks/app/{page-b9367c5ae13b99c6.js → page-6654a8cca61a3d1c.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/webpack-0a4cd455ec4197d2.js +1 -0
- package/dist/web/standalone/.next/static/css/dd4ae3f58ac9b600.css +1 -0
- package/package.json +2 -1
- package/packages/native/dist/stream-process/index.js +2 -2
- package/packages/native/src/__tests__/stream-process.test.mjs +34 -0
- package/packages/native/src/stream-process/index.ts +2 -2
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +2 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
- 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/lifecycle-hooks.d.ts +4 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +10 -5
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js +185 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +239 -10
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +13 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +40 -3
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-commands.test.js +206 -195
- package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +17 -0
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +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 +4 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.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 +7 -3
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
- package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +2 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
- package/packages/pi-coding-agent/src/core/lifecycle-hooks.test.ts +227 -0
- package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +11 -5
- package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +297 -11
- package/packages/pi-coding-agent/src/core/model-registry.ts +51 -4
- package/packages/pi-coding-agent/src/core/package-commands.test.ts +227 -205
- package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
- package/packages/pi-coding-agent/src/main.ts +19 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/activity-log.ts +1 -0
- package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -19
- package/src/resources/extensions/gsd/auto/phases.ts +69 -91
- package/src/resources/extensions/gsd/auto/run-unit.ts +6 -3
- package/src/resources/extensions/gsd/auto/session.ts +0 -18
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +131 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +0 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +25 -106
- package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
- package/src/resources/extensions/gsd/auto-start.ts +26 -5
- package/src/resources/extensions/gsd/auto-timers.ts +64 -3
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +17 -11
- package/src/resources/extensions/gsd/auto.ts +44 -86
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +162 -11
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -0
- package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
- package/src/resources/extensions/gsd/commands/context.ts +0 -5
- package/src/resources/extensions/gsd/commands/handlers/core.ts +2 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
- package/src/resources/extensions/gsd/commands/handlers/parallel.ts +1 -1
- package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
- package/src/resources/extensions/gsd/crash-recovery.ts +1 -5
- package/src/resources/extensions/gsd/dashboard-overlay.ts +0 -50
- package/src/resources/extensions/gsd/db-writer.ts +41 -27
- package/src/resources/extensions/gsd/doctor-checks.ts +180 -2
- package/src/resources/extensions/gsd/doctor-types.ts +7 -1
- package/src/resources/extensions/gsd/doctor.ts +13 -4
- package/src/resources/extensions/gsd/git-service.ts +6 -2
- package/src/resources/extensions/gsd/gsd-db.ts +32 -4
- package/src/resources/extensions/gsd/guided-flow.ts +1 -2
- package/src/resources/extensions/gsd/journal.ts +6 -1
- package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
- package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +5 -21
- package/src/resources/extensions/gsd/preferences-types.ts +2 -2
- package/src/resources/extensions/gsd/preferences.ts +7 -3
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +21 -10
- package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -23
- package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
- package/src/resources/extensions/gsd/prompts/execute-task.md +5 -15
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +5 -3
- package/src/resources/extensions/gsd/prompts/queue.md +2 -2
- package/src/resources/extensions/gsd/prompts/quick-task.md +2 -0
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
- package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
- package/src/resources/extensions/gsd/prompts/research-slice.md +3 -3
- package/src/resources/extensions/gsd/prompts/rethink.md +83 -0
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
- package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
- package/src/resources/extensions/gsd/repo-identity.ts +46 -7
- package/src/resources/extensions/gsd/rethink.ts +154 -0
- package/src/resources/extensions/gsd/session-lock.ts +0 -4
- package/src/resources/extensions/gsd/state.ts +49 -1
- package/src/resources/extensions/gsd/sync-lock.ts +94 -0
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +5 -13
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +6 -10
- package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +96 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +264 -228
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +317 -250
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +2 -8
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +121 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +60 -0
- package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
- package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +121 -0
- package/src/resources/extensions/gsd/tests/integration-proof.test.ts +15 -24
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +8 -9
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +42 -3
- package/src/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +0 -7
- package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +20 -24
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +0 -2
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +9 -6
- package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +7 -9
- package/src/resources/extensions/gsd/tests/projection-regression.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +26 -21
- package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +176 -0
- package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/reopen-task.test.ts +165 -0
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +1 -4
- package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +2 -3
- package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +108 -0
- package/src/resources/extensions/gsd/tests/sync-lock.test.ts +122 -0
- package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/unit-ownership.test.ts +175 -0
- package/src/resources/extensions/gsd/tests/workflow-events.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +275 -0
- package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +220 -0
- package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/write-intercept.test.ts +76 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +74 -11
- package/src/resources/extensions/gsd/tools/complete-slice.ts +68 -11
- package/src/resources/extensions/gsd/tools/complete-task.ts +63 -1
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +45 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +40 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +37 -1
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +39 -1
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +125 -0
- package/src/resources/extensions/gsd/tools/reopen-task.ts +129 -0
- package/src/resources/extensions/gsd/tools/replan-slice.ts +41 -1
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
- package/src/resources/extensions/gsd/types.ts +8 -0
- package/src/resources/extensions/gsd/unit-ownership.ts +104 -0
- package/src/resources/extensions/gsd/workflow-events.ts +154 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +243 -0
- package/src/resources/extensions/gsd/workflow-manifest.ts +334 -0
- package/src/resources/extensions/gsd/workflow-migration.ts +345 -0
- package/src/resources/extensions/gsd/workflow-projections.ts +425 -0
- package/src/resources/extensions/gsd/workflow-reconcile.ts +503 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +41 -5
- package/src/resources/extensions/gsd/worktree-resolver.ts +44 -0
- package/src/resources/extensions/gsd/write-intercept.ts +90 -0
- package/src/resources/extensions/mcp-client/index.ts +20 -0
- package/src/resources/extensions/voice/index.ts +11 -21
- package/src/resources/extensions/voice/linux-ready.ts +87 -0
- package/src/resources/extensions/voice/tests/linux-ready.test.ts +124 -0
- package/dist/web/standalone/.next/static/chunks/4024.0de81b543b28b9fe.js +0 -9
- package/dist/web/standalone/.next/static/chunks/webpack-9014b5adb127a98a.js +0 -1
- package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +0 -1
- /package/dist/web/standalone/.next/static/{tokoGmfkYfWf1_Yl_Gz7i → j-BskPs0nxxPeYY-bSrab}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{tokoGmfkYfWf1_Yl_Gz7i → j-BskPs0nxxPeYY-bSrab}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync, rmSync, readFileSync, readdirSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import {
|
|
7
|
+
WorktreeResolver,
|
|
8
|
+
type WorktreeResolverDeps,
|
|
9
|
+
type NotifyCtx,
|
|
10
|
+
} from "../worktree-resolver.js";
|
|
11
|
+
import { AutoSession } from "../auto/session.js";
|
|
12
|
+
import type { JournalEntry } from "../journal.js";
|
|
13
|
+
|
|
14
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
function makeSession(
|
|
17
|
+
overrides?: Partial<{ basePath: string; originalBasePath: string }>,
|
|
18
|
+
): AutoSession {
|
|
19
|
+
const s = new AutoSession();
|
|
20
|
+
s.basePath = overrides?.basePath ?? "/project";
|
|
21
|
+
s.originalBasePath = overrides?.originalBasePath ?? "/project";
|
|
22
|
+
return s;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function makeDeps(
|
|
26
|
+
overrides?: Partial<WorktreeResolverDeps>,
|
|
27
|
+
): WorktreeResolverDeps {
|
|
28
|
+
const deps: WorktreeResolverDeps = {
|
|
29
|
+
isInAutoWorktree: () => false,
|
|
30
|
+
shouldUseWorktreeIsolation: () => true,
|
|
31
|
+
getIsolationMode: () => "worktree",
|
|
32
|
+
mergeMilestoneToMain: () => ({ pushed: false, codeFilesChanged: true }),
|
|
33
|
+
syncWorktreeStateBack: () => ({ synced: [] }),
|
|
34
|
+
teardownAutoWorktree: () => {},
|
|
35
|
+
createAutoWorktree: (_basePath: string, milestoneId: string) =>
|
|
36
|
+
`/project/.gsd/worktrees/${milestoneId}`,
|
|
37
|
+
enterAutoWorktree: (_basePath: string, milestoneId: string) =>
|
|
38
|
+
`/project/.gsd/worktrees/${milestoneId}`,
|
|
39
|
+
getAutoWorktreePath: () => null,
|
|
40
|
+
autoCommitCurrentBranch: () => {},
|
|
41
|
+
getCurrentBranch: () => "main",
|
|
42
|
+
autoWorktreeBranch: (milestoneId: string) => `milestone/${milestoneId}`,
|
|
43
|
+
resolveMilestoneFile: (_basePath: string, milestoneId: string) =>
|
|
44
|
+
`/project/.gsd/milestones/${milestoneId}/${milestoneId}-ROADMAP.md`,
|
|
45
|
+
readFileSync: () => "# Roadmap\n- [x] S01: Slice one\n",
|
|
46
|
+
GitServiceImpl: class {
|
|
47
|
+
constructor() {}
|
|
48
|
+
} as unknown as WorktreeResolverDeps["GitServiceImpl"],
|
|
49
|
+
loadEffectiveGSDPreferences: () => ({ preferences: { git: {} } }),
|
|
50
|
+
invalidateAllCaches: () => {},
|
|
51
|
+
captureIntegrationBranch: () => {},
|
|
52
|
+
...overrides,
|
|
53
|
+
};
|
|
54
|
+
return deps;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function makeNotifyCtx(): NotifyCtx {
|
|
58
|
+
return {
|
|
59
|
+
notify: () => {},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Read all journal entries from a temp .gsd/journal directory. */
|
|
64
|
+
function readJournalEntries(basePath: string): JournalEntry[] {
|
|
65
|
+
const journalDir = join(basePath, ".gsd", "journal");
|
|
66
|
+
try {
|
|
67
|
+
const files = readdirSync(journalDir).filter(f => f.endsWith(".jsonl")).sort();
|
|
68
|
+
const entries: JournalEntry[] = [];
|
|
69
|
+
for (const file of files) {
|
|
70
|
+
const raw = readFileSync(join(journalDir, file), "utf-8");
|
|
71
|
+
for (const line of raw.split("\n")) {
|
|
72
|
+
if (!line.trim()) continue;
|
|
73
|
+
entries.push(JSON.parse(line) as JournalEntry);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return entries;
|
|
77
|
+
} catch {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ─── Tests ───────────────────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
describe("worktree journal events", () => {
|
|
85
|
+
let tmp: string;
|
|
86
|
+
const originalCwd = process.cwd();
|
|
87
|
+
|
|
88
|
+
beforeEach(() => {
|
|
89
|
+
tmp = mkdtempSync(join(tmpdir(), "wt-journal-"));
|
|
90
|
+
});
|
|
91
|
+
afterEach(() => {
|
|
92
|
+
// Restore cwd before cleanup — on Windows, rmSync fails with EPERM
|
|
93
|
+
// if the process cwd is inside the directory being deleted.
|
|
94
|
+
try { process.chdir(originalCwd); } catch { /* best-effort */ }
|
|
95
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("enterMilestone emits worktree-enter on success (new worktree)", () => {
|
|
99
|
+
const s = makeSession({ basePath: tmp, originalBasePath: tmp });
|
|
100
|
+
const deps = makeDeps({ getAutoWorktreePath: () => null });
|
|
101
|
+
const resolver = new WorktreeResolver(s, deps);
|
|
102
|
+
|
|
103
|
+
resolver.enterMilestone("M001", makeNotifyCtx());
|
|
104
|
+
|
|
105
|
+
const entries = readJournalEntries(tmp);
|
|
106
|
+
const enter = entries.find(e => e.eventType === "worktree-enter");
|
|
107
|
+
assert.ok(enter, "worktree-enter event should be emitted");
|
|
108
|
+
assert.equal(enter!.data?.milestoneId, "M001");
|
|
109
|
+
assert.equal(enter!.data?.created, true);
|
|
110
|
+
assert.ok(enter!.data?.wtPath);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("enterMilestone emits worktree-enter with created=false for existing worktree", () => {
|
|
114
|
+
const s = makeSession({ basePath: tmp, originalBasePath: tmp });
|
|
115
|
+
const deps = makeDeps({
|
|
116
|
+
getAutoWorktreePath: () => "/project/.gsd/worktrees/M001",
|
|
117
|
+
});
|
|
118
|
+
const resolver = new WorktreeResolver(s, deps);
|
|
119
|
+
|
|
120
|
+
resolver.enterMilestone("M001", makeNotifyCtx());
|
|
121
|
+
|
|
122
|
+
const entries = readJournalEntries(tmp);
|
|
123
|
+
const enter = entries.find(e => e.eventType === "worktree-enter");
|
|
124
|
+
assert.ok(enter, "worktree-enter event should be emitted");
|
|
125
|
+
assert.equal(enter!.data?.created, false);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("enterMilestone emits worktree-skip when isolation disabled", () => {
|
|
129
|
+
const s = makeSession({ basePath: tmp, originalBasePath: tmp });
|
|
130
|
+
const deps = makeDeps({ shouldUseWorktreeIsolation: () => false });
|
|
131
|
+
const resolver = new WorktreeResolver(s, deps);
|
|
132
|
+
|
|
133
|
+
resolver.enterMilestone("M001", makeNotifyCtx());
|
|
134
|
+
|
|
135
|
+
const entries = readJournalEntries(tmp);
|
|
136
|
+
const skip = entries.find(e => e.eventType === "worktree-skip");
|
|
137
|
+
assert.ok(skip, "worktree-skip event should be emitted");
|
|
138
|
+
assert.equal(skip!.data?.milestoneId, "M001");
|
|
139
|
+
assert.equal(skip!.data?.reason, "isolation-disabled");
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("enterMilestone emits worktree-create-failed on error", () => {
|
|
143
|
+
const s = makeSession({ basePath: tmp, originalBasePath: tmp });
|
|
144
|
+
const deps = makeDeps({
|
|
145
|
+
getAutoWorktreePath: () => null,
|
|
146
|
+
createAutoWorktree: () => { throw new Error("disk full"); },
|
|
147
|
+
});
|
|
148
|
+
const resolver = new WorktreeResolver(s, deps);
|
|
149
|
+
|
|
150
|
+
resolver.enterMilestone("M001", makeNotifyCtx());
|
|
151
|
+
|
|
152
|
+
const entries = readJournalEntries(tmp);
|
|
153
|
+
const failed = entries.find(e => e.eventType === "worktree-create-failed");
|
|
154
|
+
assert.ok(failed, "worktree-create-failed event should be emitted");
|
|
155
|
+
assert.equal(failed!.data?.milestoneId, "M001");
|
|
156
|
+
assert.equal(failed!.data?.error, "disk full");
|
|
157
|
+
assert.equal(failed!.data?.fallback, "project-root");
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("mergeAndExit emits worktree-merge-start", () => {
|
|
161
|
+
const s = makeSession({
|
|
162
|
+
basePath: join(tmp, "worktree"),
|
|
163
|
+
originalBasePath: tmp,
|
|
164
|
+
});
|
|
165
|
+
const deps = makeDeps({
|
|
166
|
+
isInAutoWorktree: () => true,
|
|
167
|
+
getIsolationMode: () => "worktree",
|
|
168
|
+
});
|
|
169
|
+
const resolver = new WorktreeResolver(s, deps);
|
|
170
|
+
|
|
171
|
+
resolver.mergeAndExit("M001", makeNotifyCtx());
|
|
172
|
+
|
|
173
|
+
const entries = readJournalEntries(tmp);
|
|
174
|
+
const start = entries.find(e => e.eventType === "worktree-merge-start");
|
|
175
|
+
assert.ok(start, "worktree-merge-start event should be emitted");
|
|
176
|
+
assert.equal(start!.data?.milestoneId, "M001");
|
|
177
|
+
assert.equal(start!.data?.mode, "worktree");
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("mergeAndExit emits worktree-merge-failed on error", () => {
|
|
181
|
+
const s = makeSession({
|
|
182
|
+
basePath: join(tmp, "worktree"),
|
|
183
|
+
originalBasePath: tmp,
|
|
184
|
+
});
|
|
185
|
+
const deps = makeDeps({
|
|
186
|
+
isInAutoWorktree: () => true,
|
|
187
|
+
getIsolationMode: () => "worktree",
|
|
188
|
+
mergeMilestoneToMain: () => { throw new Error("conflict in main"); },
|
|
189
|
+
});
|
|
190
|
+
const resolver = new WorktreeResolver(s, deps);
|
|
191
|
+
|
|
192
|
+
resolver.mergeAndExit("M001", makeNotifyCtx());
|
|
193
|
+
|
|
194
|
+
const entries = readJournalEntries(tmp);
|
|
195
|
+
const failed = entries.find(e => e.eventType === "worktree-merge-failed");
|
|
196
|
+
assert.ok(failed, "worktree-merge-failed event should be emitted");
|
|
197
|
+
assert.equal(failed!.data?.milestoneId, "M001");
|
|
198
|
+
assert.equal(failed!.data?.error, "conflict in main");
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("journal entries have valid flowId, seq, and ts fields", () => {
|
|
202
|
+
const s = makeSession({ basePath: tmp, originalBasePath: tmp });
|
|
203
|
+
const deps = makeDeps({ shouldUseWorktreeIsolation: () => false });
|
|
204
|
+
const resolver = new WorktreeResolver(s, deps);
|
|
205
|
+
|
|
206
|
+
resolver.enterMilestone("M001", makeNotifyCtx());
|
|
207
|
+
|
|
208
|
+
const entries = readJournalEntries(tmp);
|
|
209
|
+
assert.ok(entries.length > 0, "at least one entry should exist");
|
|
210
|
+
const entry = entries[0];
|
|
211
|
+
assert.ok(entry.flowId, "flowId should be set");
|
|
212
|
+
assert.ok(
|
|
213
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(entry.flowId),
|
|
214
|
+
"flowId should be a valid UUID",
|
|
215
|
+
);
|
|
216
|
+
assert.equal(entry.seq, 0);
|
|
217
|
+
assert.ok(entry.ts, "ts should be set");
|
|
218
|
+
assert.ok(!isNaN(Date.parse(entry.ts)), "ts should be a valid ISO date");
|
|
219
|
+
});
|
|
220
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* worktree-submodule-safety.test.ts — #2337
|
|
3
|
+
*
|
|
4
|
+
* Worktree teardown (removeWorktree) uses --force which destroys
|
|
5
|
+
* uncommitted changes in submodule directories. This test verifies
|
|
6
|
+
* that the removal logic detects submodules and preserves their state.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { readFileSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { createTestContext } from "./test-helpers.ts";
|
|
12
|
+
|
|
13
|
+
const { assertTrue, report } = createTestContext();
|
|
14
|
+
|
|
15
|
+
const srcPath = join(import.meta.dirname, "..", "worktree-manager.ts");
|
|
16
|
+
const src = readFileSync(srcPath, "utf-8");
|
|
17
|
+
|
|
18
|
+
console.log("\n=== #2337: Worktree teardown preserves submodule state ===");
|
|
19
|
+
|
|
20
|
+
// ── Test 1: removeWorktree function exists ──────────────────────────────
|
|
21
|
+
|
|
22
|
+
const removeWorktreeIdx = src.indexOf("export function removeWorktree");
|
|
23
|
+
assertTrue(removeWorktreeIdx > 0, "worktree-manager.ts exports removeWorktree");
|
|
24
|
+
|
|
25
|
+
const fnBody = src.slice(removeWorktreeIdx, removeWorktreeIdx + 3000);
|
|
26
|
+
|
|
27
|
+
// ── Test 2: The function checks for submodules before force removal ─────
|
|
28
|
+
|
|
29
|
+
const checksSubmodules =
|
|
30
|
+
fnBody.includes("submodule") ||
|
|
31
|
+
fnBody.includes(".gitmodules");
|
|
32
|
+
|
|
33
|
+
assertTrue(
|
|
34
|
+
checksSubmodules,
|
|
35
|
+
"removeWorktree checks for submodules before force removal (#2337)",
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// ── Test 3: Submodule changes are stashed or warned about ───────────────
|
|
39
|
+
|
|
40
|
+
const preservesSubmoduleState =
|
|
41
|
+
fnBody.includes("stash") ||
|
|
42
|
+
fnBody.includes("uncommitted") ||
|
|
43
|
+
fnBody.includes("dirty") ||
|
|
44
|
+
fnBody.includes("submodule") && (fnBody.includes("warn") || fnBody.includes("preserv"));
|
|
45
|
+
|
|
46
|
+
assertTrue(
|
|
47
|
+
preservesSubmoduleState,
|
|
48
|
+
"removeWorktree preserves or warns about submodule uncommitted changes (#2337)",
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// ── Test 4: Force removal is skipped when submodules have changes ───────
|
|
52
|
+
|
|
53
|
+
// The key fix: when submodules have dirty state, we should NOT use force
|
|
54
|
+
// removal. Instead, use non-force first and fall back to force only after
|
|
55
|
+
// submodule state is preserved.
|
|
56
|
+
const hasConditionalForce =
|
|
57
|
+
fnBody.includes("submodule") &&
|
|
58
|
+
(fnBody.includes("force") || fnBody.includes("--force"));
|
|
59
|
+
|
|
60
|
+
assertTrue(
|
|
61
|
+
hasConditionalForce,
|
|
62
|
+
"removeWorktree has conditional force logic around submodules (#2337)",
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
report();
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// GSD Extension — write-intercept unit tests
|
|
2
|
+
// Tests isBlockedStateFile() and BLOCKED_WRITE_ERROR constant.
|
|
3
|
+
|
|
4
|
+
import test from 'node:test';
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import { isBlockedStateFile, BLOCKED_WRITE_ERROR } from '../write-intercept.ts';
|
|
7
|
+
|
|
8
|
+
// ─── isBlockedStateFile: blocked paths ───────────────────────────────────
|
|
9
|
+
|
|
10
|
+
test('write-intercept: blocks unix .gsd/STATE.md path', () => {
|
|
11
|
+
assert.strictEqual(isBlockedStateFile('/project/.gsd/STATE.md'), true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('write-intercept: blocks relative path with dir prefix before .gsd/STATE.md', () => {
|
|
15
|
+
assert.strictEqual(isBlockedStateFile('project/.gsd/STATE.md'), true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('write-intercept: blocks bare relative .gsd/STATE.md (no leading separator)', () => {
|
|
19
|
+
// (^|[/\\]) matches paths that start with .gsd/ — covers the case where write
|
|
20
|
+
// tools receive a bare relative path before the file exists (realpathSync fails).
|
|
21
|
+
assert.strictEqual(isBlockedStateFile('.gsd/STATE.md'), true);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('write-intercept: blocks nested project .gsd/STATE.md path', () => {
|
|
25
|
+
assert.strictEqual(isBlockedStateFile('/Users/dev/my-project/.gsd/STATE.md'), true);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('write-intercept: blocks .gsd/projects/<name>/STATE.md (symlinked projects path)', () => {
|
|
29
|
+
assert.strictEqual(isBlockedStateFile('/home/user/.gsd/projects/my-project/STATE.md'), true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// ─── isBlockedStateFile: allowed paths ───────────────────────────────────
|
|
33
|
+
|
|
34
|
+
test('write-intercept: allows .gsd/ROADMAP.md', () => {
|
|
35
|
+
assert.strictEqual(isBlockedStateFile('/project/.gsd/ROADMAP.md'), false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('write-intercept: allows .gsd/PLAN.md', () => {
|
|
39
|
+
assert.strictEqual(isBlockedStateFile('/project/.gsd/PLAN.md'), false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('write-intercept: allows .gsd/REQUIREMENTS.md', () => {
|
|
43
|
+
assert.strictEqual(isBlockedStateFile('/project/.gsd/REQUIREMENTS.md'), false);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('write-intercept: allows .gsd/SUMMARY.md', () => {
|
|
47
|
+
assert.strictEqual(isBlockedStateFile('/project/.gsd/SUMMARY.md'), false);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('write-intercept: allows .gsd/PROJECT.md', () => {
|
|
51
|
+
assert.strictEqual(isBlockedStateFile('/project/.gsd/PROJECT.md'), false);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('write-intercept: allows regular source files', () => {
|
|
55
|
+
assert.strictEqual(isBlockedStateFile('/project/src/index.ts'), false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('write-intercept: allows slice plan files', () => {
|
|
59
|
+
assert.strictEqual(isBlockedStateFile('/project/.gsd/milestones/M001/slices/S01/S01-PLAN.md'), false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('write-intercept: does not block files named STATE.md outside .gsd/', () => {
|
|
63
|
+
assert.strictEqual(isBlockedStateFile('/project/docs/STATE.md'), false);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// ─── BLOCKED_WRITE_ERROR: content ────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
test('write-intercept: BLOCKED_WRITE_ERROR is a non-empty string', () => {
|
|
69
|
+
assert.strictEqual(typeof BLOCKED_WRITE_ERROR, 'string');
|
|
70
|
+
assert.ok(BLOCKED_WRITE_ERROR.length > 0);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('write-intercept: BLOCKED_WRITE_ERROR mentions engine tool calls', () => {
|
|
74
|
+
assert.ok(BLOCKED_WRITE_ERROR.includes('gsd_complete_task'), 'should mention gsd_complete_task');
|
|
75
|
+
assert.ok(BLOCKED_WRITE_ERROR.includes('engine tool calls'), 'should mention engine tool calls');
|
|
76
|
+
});
|
|
@@ -11,12 +11,17 @@ import { mkdirSync } from "node:fs";
|
|
|
11
11
|
|
|
12
12
|
import {
|
|
13
13
|
transaction,
|
|
14
|
+
getMilestone,
|
|
14
15
|
getMilestoneSlices,
|
|
16
|
+
getSliceTasks,
|
|
15
17
|
_getAdapter,
|
|
16
18
|
} from "../gsd-db.js";
|
|
17
19
|
import { resolveMilestonePath, clearPathCache } from "../paths.js";
|
|
18
20
|
import { saveFile, clearParseCache } from "../files.js";
|
|
19
21
|
import { invalidateStateCache } from "../state.js";
|
|
22
|
+
import { renderAllProjections } from "../workflow-projections.js";
|
|
23
|
+
import { writeManifest } from "../workflow-manifest.js";
|
|
24
|
+
import { appendEvent } from "../workflow-events.js";
|
|
20
25
|
|
|
21
26
|
export interface CompleteMilestoneParams {
|
|
22
27
|
milestoneId: string;
|
|
@@ -31,6 +36,11 @@ export interface CompleteMilestoneParams {
|
|
|
31
36
|
lessonsLearned: string[];
|
|
32
37
|
followUps: string;
|
|
33
38
|
deviations: string;
|
|
39
|
+
verificationPassed: boolean;
|
|
40
|
+
/** Optional caller-provided identity for audit trail */
|
|
41
|
+
actorName?: string;
|
|
42
|
+
/** Optional caller-provided reason this action was triggered */
|
|
43
|
+
triggerReason?: string;
|
|
34
44
|
}
|
|
35
45
|
|
|
36
46
|
export interface CompleteMilestoneResult {
|
|
@@ -108,22 +118,53 @@ export async function handleCompleteMilestone(
|
|
|
108
118
|
return { error: "title is required and must be a non-empty string" };
|
|
109
119
|
}
|
|
110
120
|
|
|
111
|
-
// ── Verify
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return { error: `no slices found for milestone ${params.milestoneId}` };
|
|
121
|
+
// ── Verify that verification passed ─────────────────────────────────────
|
|
122
|
+
if (params.verificationPassed !== true) {
|
|
123
|
+
return { error: "verification did not pass — milestone completion blocked. verificationPassed must be explicitly set to true after all verification steps succeed" };
|
|
115
124
|
}
|
|
116
125
|
|
|
117
|
-
|
|
118
|
-
if (incompleteSlices.length > 0) {
|
|
119
|
-
const incompleteIds = incompleteSlices.map(s => `${s.id} (status: ${s.status})`).join(", ");
|
|
120
|
-
return { error: `incomplete slices: ${incompleteIds}` };
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// ── DB writes inside a transaction ──────────────────────────────────────
|
|
126
|
+
// ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
|
|
124
127
|
const completedAt = new Date().toISOString();
|
|
128
|
+
let guardError: string | null = null;
|
|
125
129
|
|
|
126
130
|
transaction(() => {
|
|
131
|
+
// State machine preconditions (inside txn for atomicity)
|
|
132
|
+
const milestone = getMilestone(params.milestoneId);
|
|
133
|
+
if (!milestone) {
|
|
134
|
+
guardError = `milestone not found: ${params.milestoneId}`;
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (milestone.status === "complete" || milestone.status === "done") {
|
|
138
|
+
guardError = `milestone ${params.milestoneId} is already complete`;
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Verify all slices are complete
|
|
143
|
+
const slices = getMilestoneSlices(params.milestoneId);
|
|
144
|
+
if (slices.length === 0) {
|
|
145
|
+
guardError = `no slices found for milestone ${params.milestoneId}`;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const incompleteSlices = slices.filter(s => s.status !== "complete" && s.status !== "done");
|
|
150
|
+
if (incompleteSlices.length > 0) {
|
|
151
|
+
const incompleteIds = incompleteSlices.map(s => `${s.id} (status: ${s.status})`).join(", ");
|
|
152
|
+
guardError = `incomplete slices: ${incompleteIds}`;
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Deep check: verify all tasks in all slices are complete
|
|
157
|
+
for (const slice of slices) {
|
|
158
|
+
const tasks = getSliceTasks(params.milestoneId, slice.id);
|
|
159
|
+
const incompleteTasks = tasks.filter(t => t.status !== "complete" && t.status !== "done");
|
|
160
|
+
if (incompleteTasks.length > 0) {
|
|
161
|
+
const ids = incompleteTasks.map(t => `${t.id} (status: ${t.status})`).join(", ");
|
|
162
|
+
guardError = `slice ${slice.id} has incomplete tasks: ${ids}`;
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// All guards passed — perform write
|
|
127
168
|
const adapter = _getAdapter()!;
|
|
128
169
|
adapter.prepare(
|
|
129
170
|
`UPDATE milestones SET status = 'complete', completed_at = :completed_at WHERE id = :mid`,
|
|
@@ -133,6 +174,10 @@ export async function handleCompleteMilestone(
|
|
|
133
174
|
});
|
|
134
175
|
});
|
|
135
176
|
|
|
177
|
+
if (guardError) {
|
|
178
|
+
return { error: guardError };
|
|
179
|
+
}
|
|
180
|
+
|
|
136
181
|
// ── Filesystem operations (outside transaction) ─────────────────────────
|
|
137
182
|
const summaryMd = renderMilestoneSummaryMarkdown(params);
|
|
138
183
|
|
|
@@ -169,6 +214,24 @@ export async function handleCompleteMilestone(
|
|
|
169
214
|
clearPathCache();
|
|
170
215
|
clearParseCache();
|
|
171
216
|
|
|
217
|
+
// ── Post-mutation hook: projections, manifest, event log ───────────────
|
|
218
|
+
try {
|
|
219
|
+
await renderAllProjections(basePath, params.milestoneId);
|
|
220
|
+
writeManifest(basePath);
|
|
221
|
+
appendEvent(basePath, {
|
|
222
|
+
cmd: "complete-milestone",
|
|
223
|
+
params: { milestoneId: params.milestoneId },
|
|
224
|
+
ts: new Date().toISOString(),
|
|
225
|
+
actor: "agent",
|
|
226
|
+
actor_name: params.actorName,
|
|
227
|
+
trigger_reason: params.triggerReason,
|
|
228
|
+
});
|
|
229
|
+
} catch (hookErr) {
|
|
230
|
+
process.stderr.write(
|
|
231
|
+
`gsd: complete-milestone post-mutation hook warning: ${(hookErr as Error).message}\n`,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
172
235
|
return {
|
|
173
236
|
milestoneId: params.milestoneId,
|
|
174
237
|
summaryPath,
|
|
@@ -15,14 +15,20 @@ import {
|
|
|
15
15
|
transaction,
|
|
16
16
|
insertMilestone,
|
|
17
17
|
insertSlice,
|
|
18
|
+
getSlice,
|
|
18
19
|
getSliceTasks,
|
|
20
|
+
getMilestone,
|
|
19
21
|
updateSliceStatus,
|
|
20
22
|
_getAdapter,
|
|
21
23
|
} from "../gsd-db.js";
|
|
22
24
|
import { resolveSliceFile, resolveSlicePath, clearPathCache } from "../paths.js";
|
|
25
|
+
import { checkOwnership, sliceUnitKey } from "../unit-ownership.js";
|
|
23
26
|
import { saveFile, clearParseCache } from "../files.js";
|
|
24
27
|
import { invalidateStateCache } from "../state.js";
|
|
25
28
|
import { renderRoadmapCheckboxes } from "../markdown-renderer.js";
|
|
29
|
+
import { renderAllProjections } from "../workflow-projections.js";
|
|
30
|
+
import { writeManifest } from "../workflow-manifest.js";
|
|
31
|
+
import { appendEvent } from "../workflow-events.js";
|
|
26
32
|
|
|
27
33
|
export interface CompleteSliceResult {
|
|
28
34
|
sliceId: string;
|
|
@@ -200,27 +206,60 @@ export async function handleCompleteSlice(
|
|
|
200
206
|
return { error: "milestoneId is required and must be a non-empty string" };
|
|
201
207
|
}
|
|
202
208
|
|
|
203
|
-
// ──
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
209
|
+
// ── Ownership check (opt-in: only enforced when claim file exists) ──────
|
|
210
|
+
const ownershipErr = checkOwnership(
|
|
211
|
+
basePath,
|
|
212
|
+
sliceUnitKey(params.milestoneId, params.sliceId),
|
|
213
|
+
params.actorName,
|
|
214
|
+
);
|
|
215
|
+
if (ownershipErr) {
|
|
216
|
+
return { error: ownershipErr };
|
|
207
217
|
}
|
|
208
218
|
|
|
209
|
-
|
|
210
|
-
if (incompleteTasks.length > 0) {
|
|
211
|
-
const incompleteIds = incompleteTasks.map(t => `${t.id} (status: ${t.status})`).join(", ");
|
|
212
|
-
return { error: `incomplete tasks: ${incompleteIds}` };
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// ── DB writes inside a transaction ──────────────────────────────────────
|
|
219
|
+
// ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
|
|
216
220
|
const completedAt = new Date().toISOString();
|
|
221
|
+
let guardError: string | null = null;
|
|
217
222
|
|
|
218
223
|
transaction(() => {
|
|
224
|
+
// State machine preconditions (inside txn for atomicity).
|
|
225
|
+
// Milestone/slice not existing is OK — insertMilestone/insertSlice below will auto-create.
|
|
226
|
+
// Only block if they exist and are closed.
|
|
227
|
+
const milestone = getMilestone(params.milestoneId);
|
|
228
|
+
if (milestone && (milestone.status === "complete" || milestone.status === "done")) {
|
|
229
|
+
guardError = `cannot complete slice in a closed milestone: ${params.milestoneId} (status: ${milestone.status})`;
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const slice = getSlice(params.milestoneId, params.sliceId);
|
|
234
|
+
if (slice && (slice.status === "complete" || slice.status === "done")) {
|
|
235
|
+
guardError = `slice ${params.sliceId} is already complete — use gsd_slice_reopen first if you need to redo it`;
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Verify all tasks are complete
|
|
240
|
+
const tasks = getSliceTasks(params.milestoneId, params.sliceId);
|
|
241
|
+
if (tasks.length === 0) {
|
|
242
|
+
guardError = `no tasks found for slice ${params.sliceId} in milestone ${params.milestoneId}`;
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const incompleteTasks = tasks.filter(t => t.status !== "complete" && t.status !== "done");
|
|
247
|
+
if (incompleteTasks.length > 0) {
|
|
248
|
+
const incompleteIds = incompleteTasks.map(t => `${t.id} (status: ${t.status})`).join(", ");
|
|
249
|
+
guardError = `incomplete tasks: ${incompleteIds}`;
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// All guards passed — perform writes
|
|
219
254
|
insertMilestone({ id: params.milestoneId });
|
|
220
255
|
insertSlice({ id: params.sliceId, milestoneId: params.milestoneId });
|
|
221
256
|
updateSliceStatus(params.milestoneId, params.sliceId, "complete", completedAt);
|
|
222
257
|
});
|
|
223
258
|
|
|
259
|
+
if (guardError) {
|
|
260
|
+
return { error: guardError };
|
|
261
|
+
}
|
|
262
|
+
|
|
224
263
|
// ── Filesystem operations (outside transaction) ─────────────────────────
|
|
225
264
|
// If disk render fails, roll back the DB status so deriveState() and
|
|
226
265
|
// verifyExpectedArtifact() stay consistent (both say "not done").
|
|
@@ -291,6 +330,24 @@ export async function handleCompleteSlice(
|
|
|
291
330
|
clearPathCache();
|
|
292
331
|
clearParseCache();
|
|
293
332
|
|
|
333
|
+
// ── Post-mutation hook: projections, manifest, event log ───────────────
|
|
334
|
+
try {
|
|
335
|
+
await renderAllProjections(basePath, params.milestoneId);
|
|
336
|
+
writeManifest(basePath);
|
|
337
|
+
appendEvent(basePath, {
|
|
338
|
+
cmd: "complete-slice",
|
|
339
|
+
params: { milestoneId: params.milestoneId, sliceId: params.sliceId },
|
|
340
|
+
ts: new Date().toISOString(),
|
|
341
|
+
actor: "agent",
|
|
342
|
+
actor_name: params.actorName,
|
|
343
|
+
trigger_reason: params.triggerReason,
|
|
344
|
+
});
|
|
345
|
+
} catch (hookErr) {
|
|
346
|
+
process.stderr.write(
|
|
347
|
+
`gsd: complete-slice post-mutation hook warning: ${(hookErr as Error).message}\n`,
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
294
351
|
return {
|
|
295
352
|
sliceId: params.sliceId,
|
|
296
353
|
milestoneId: params.milestoneId,
|