@opengsd/gsd-pi 1.3.0-dev.65546769 → 1.3.0-dev.965d1788
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/README.md +1 -1
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +11 -2
- package/dist/resources/extensions/google-cli/stream-adapter.js +82 -15
- package/dist/resources/extensions/gsd/artifact-verification.js +427 -0
- package/dist/resources/extensions/gsd/auto/orchestrator.js +12 -3
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto/verification-retry-policy.js +1 -1
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +28 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +20 -19
- package/dist/resources/extensions/gsd/auto-post-unit.js +1 -1
- package/dist/resources/extensions/gsd/auto-prompts.js +26 -11
- package/dist/resources/extensions/gsd/auto-recovery.js +6 -507
- package/dist/resources/extensions/gsd/auto-runtime-state.js +4 -5
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +3 -3
- package/dist/resources/extensions/gsd/auto-verification.js +5 -2
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +103 -13
- package/dist/resources/extensions/gsd/bootstrap/core-session-tools.js +38 -0
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +6 -1
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +16 -19
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -19
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +68 -10
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +1 -1
- package/dist/resources/extensions/gsd/closeout-consistency-gate.js +3 -1
- package/dist/resources/extensions/gsd/commands-context.js +19 -1
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +16 -10
- package/dist/resources/extensions/gsd/commands-worktree.js +12 -10
- package/dist/resources/extensions/gsd/dashboard-overlay.js +32 -3
- package/dist/resources/extensions/gsd/db/queries.js +60 -0
- package/dist/resources/extensions/gsd/db-workspace.js +55 -3
- package/dist/resources/extensions/gsd/doctor-providers.js +92 -8
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +3 -0
- package/dist/resources/extensions/gsd/exec-sandbox.js +45 -9
- package/dist/resources/extensions/gsd/flat-phase-migration.js +45 -0
- package/dist/resources/extensions/gsd/forensics.js +2 -32
- package/dist/resources/extensions/gsd/git-service.js +5 -4
- package/dist/resources/extensions/gsd/gitignore.js +3 -0
- package/dist/resources/extensions/gsd/guided-flow-queue.js +66 -5
- package/dist/resources/extensions/gsd/health-widget.js +55 -29
- package/dist/resources/extensions/gsd/json-persistence.js +20 -0
- package/dist/resources/extensions/gsd/knowledge-backfill.js +18 -11
- package/dist/resources/extensions/gsd/layout-policy.js +3 -1
- package/dist/resources/extensions/gsd/markdown-renderer.js +8 -9
- package/dist/resources/extensions/gsd/memory-backfill.js +28 -21
- package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +44 -21
- package/dist/resources/extensions/gsd/metrics.js +4 -4
- package/dist/resources/extensions/gsd/migration-auto-check.js +22 -0
- package/dist/resources/extensions/gsd/milestone-ids.js +32 -2
- package/dist/resources/extensions/gsd/milestone-implementation-evidence.js +26 -20
- package/dist/resources/extensions/gsd/orphan-stash-audit.js +92 -16
- package/dist/resources/extensions/gsd/prompts/code-review.md +6 -4
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/quick.js +45 -2
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +5 -2
- package/dist/resources/extensions/gsd/session-forensics.js +11 -1
- package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
- package/dist/resources/extensions/gsd/state/derive/cache.js +28 -0
- package/dist/resources/extensions/gsd/state/derive/db-open.js +39 -0
- package/dist/resources/extensions/gsd/state/derive/from-db.js +452 -0
- package/dist/resources/extensions/gsd/state/derive/index.js +75 -0
- package/dist/resources/extensions/gsd/state/derive/interrupted-work.js +21 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +45 -2
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +48 -23
- package/dist/resources/extensions/gsd/state-reconciliation/registry.js +32 -28
- package/dist/resources/extensions/gsd/state.js +12 -611
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +4 -2
- package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -2
- package/dist/resources/extensions/gsd/tools/complete-task.js +43 -14
- package/dist/resources/extensions/gsd/tools/exec-tool.js +7 -2
- package/dist/resources/extensions/gsd/undo.js +43 -6
- package/dist/resources/extensions/gsd/unit-context-composer.js +23 -7
- package/dist/resources/extensions/gsd/unit-registry.js +32 -4
- package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +33 -3
- package/dist/resources/extensions/gsd/validation-block-guard.js +9 -4
- package/dist/resources/extensions/gsd/verification-gate.js +7 -0
- package/dist/resources/extensions/gsd/workflow-manifest.js +4 -1
- package/dist/resources/extensions/gsd/workflow-projections.js +19 -14
- package/dist/resources/extensions/gsd/workspace-git-preflight.js +30 -1
- package/dist/resources/extensions/gsd/worktree-manager.js +44 -2
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
- 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 +9 -9
- 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 +1 -1
- 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_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 +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +2 -2
- 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 +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
- 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 +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/{2659.b7b129ee6a769448.js → 2659.58e950899a9bb82f.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/2772.a7c1fcc69a4685ef.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{3616.3c60753b8ffcbd2e.js → 3616.61a2af74bb8833c8.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{4283.8e446784528ed9dc.js → 4283.d0d9e0a955e441cb.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{5826.a46ecdd1cfe8dabc.js → 5826.5421d66c72b9f34e.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{8785.481aa5869991b760.js → 8785.e29b3134cab1d153.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/8937.640dc9c2aaa1dfad.js +10 -0
- package/dist/web/standalone/.next/static/chunks/app/{page-6644fc6ee8ca1247.js → page-72a856634ad14c10.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/webpack-9c401904f87ded16.js +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/package.json +1 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/workflow.d.ts +1 -0
- package/packages/contracts/dist/workflow.d.ts.map +1 -1
- package/packages/contracts/dist/workflow.js +2 -0
- package/packages/contracts/dist/workflow.js.map +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/agent-session.d.ts +1 -0
- package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/agent-session.js +3 -0
- package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts +41 -0
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts.map +1 -0
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.js +62 -0
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.js.map +1 -0
- package/packages/gsd-agent-core/dist/index.d.ts +2 -0
- package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/index.js +2 -0
- package/packages/gsd-agent-core/dist/index.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-events.js +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-events.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +1 -0
- package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts +5 -0
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +60 -3
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
- package/packages/gsd-agent-core/dist/transcript-store.d.ts +58 -0
- package/packages/gsd-agent-core/dist/transcript-store.d.ts.map +1 -0
- package/packages/gsd-agent-core/dist/transcript-store.js +132 -0
- package/packages/gsd-agent-core/dist/transcript-store.js.map +1 -0
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -11
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts +4 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js +7 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +3 -24
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +26 -829
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts +58 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js +312 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts +31 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js +130 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts +15 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +258 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts +13 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js +118 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts +9 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts +54 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js +20 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +4 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +9 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts +56 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js +44 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts +5 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js +77 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +18 -0
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/README.md +1 -1
- package/packages/mcp-server/dist/server.d.ts +1 -1
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +3 -3
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +13 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +34 -20
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/README.md +3 -2
- package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-context.js +94 -0
- package/packages/pi-coding-agent/dist/core/session-manager-context.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.js +244 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts +12 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.js +84 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +135 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.js +2 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts +6 -154
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +22 -459
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts +75 -75
- package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme-schema.js +1 -1
- package/packages/pi-coding-agent/dist/theme/theme-schema.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.js +11 -7
- package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/dist/theme/theme-schema.d.ts +75 -75
- package/pkg/dist/theme/theme-schema.d.ts.map +1 -1
- package/pkg/dist/theme/theme-schema.js +1 -1
- package/pkg/dist/theme/theme-schema.js.map +1 -1
- package/pkg/dist/theme/theme.d.ts.map +1 -1
- package/pkg/dist/theme/theme.js +11 -7
- package/pkg/dist/theme/theme.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +20 -2
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +80 -0
- package/src/resources/extensions/google-cli/stream-adapter.ts +106 -19
- package/src/resources/extensions/gsd/artifact-verification.ts +464 -0
- package/src/resources/extensions/gsd/auto/orchestrator.ts +25 -11
- package/src/resources/extensions/gsd/auto/session.ts +6 -0
- package/src/resources/extensions/gsd/auto/verification-retry-policy.ts +3 -1
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +47 -1
- package/src/resources/extensions/gsd/auto-dispatch.ts +21 -23
- package/src/resources/extensions/gsd/auto-post-unit.ts +1 -1
- package/src/resources/extensions/gsd/auto-prompts.ts +38 -12
- package/src/resources/extensions/gsd/auto-recovery.ts +10 -508
- package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -5
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +3 -2
- package/src/resources/extensions/gsd/auto-verification.ts +5 -1
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +125 -12
- package/src/resources/extensions/gsd/bootstrap/core-session-tools.ts +43 -0
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +6 -1
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +20 -19
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +52 -18
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +74 -10
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +1 -1
- package/src/resources/extensions/gsd/closeout-consistency-gate.ts +2 -1
- package/src/resources/extensions/gsd/commands-context.ts +18 -1
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +14 -9
- package/src/resources/extensions/gsd/commands-worktree.ts +12 -10
- package/src/resources/extensions/gsd/dashboard-overlay.ts +32 -3
- package/src/resources/extensions/gsd/db/queries.ts +79 -0
- package/src/resources/extensions/gsd/db-workspace.ts +61 -3
- package/src/resources/extensions/gsd/doctor-providers.ts +103 -9
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +3 -0
- package/src/resources/extensions/gsd/exec-sandbox.ts +49 -9
- package/src/resources/extensions/gsd/flat-phase-migration.ts +44 -0
- package/src/resources/extensions/gsd/forensics.ts +2 -33
- package/src/resources/extensions/gsd/git-service.ts +6 -5
- package/src/resources/extensions/gsd/gitignore.ts +3 -0
- package/src/resources/extensions/gsd/guided-flow-queue.ts +89 -4
- package/src/resources/extensions/gsd/health-widget.ts +69 -32
- package/src/resources/extensions/gsd/json-persistence.ts +20 -0
- package/src/resources/extensions/gsd/knowledge-backfill.ts +16 -10
- package/src/resources/extensions/gsd/layout-policy.ts +2 -1
- package/src/resources/extensions/gsd/markdown-renderer.ts +8 -11
- package/src/resources/extensions/gsd/memory-backfill.ts +26 -20
- package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +51 -19
- package/src/resources/extensions/gsd/metrics.ts +4 -4
- package/src/resources/extensions/gsd/migration-auto-check.ts +23 -0
- package/src/resources/extensions/gsd/milestone-ids.ts +31 -2
- package/src/resources/extensions/gsd/milestone-implementation-evidence.ts +35 -21
- package/src/resources/extensions/gsd/orphan-stash-audit.ts +108 -20
- package/src/resources/extensions/gsd/prompts/code-review.md +6 -4
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/quick.ts +43 -2
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +8 -2
- package/src/resources/extensions/gsd/session-forensics.ts +11 -1
- package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
- package/src/resources/extensions/gsd/state/derive/cache.ts +46 -0
- package/src/resources/extensions/gsd/state/derive/db-open.ts +45 -0
- package/src/resources/extensions/gsd/state/derive/from-db.ts +561 -0
- package/src/resources/extensions/gsd/state/derive/index.ts +104 -0
- package/src/resources/extensions/gsd/state/derive/interrupted-work.ts +31 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +81 -7
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +50 -24
- package/src/resources/extensions/gsd/state-reconciliation/registry.ts +43 -28
- package/src/resources/extensions/gsd/state.ts +32 -732
- package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +98 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +111 -1
- package/src/resources/extensions/gsd/tests/commands-context.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/commands-gsd-core.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/commands-worktree-clean.test.ts +80 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +28 -0
- package/src/resources/extensions/gsd/tests/complete-slice-reopen-handoff.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +11 -0
- package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +48 -8
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +55 -2
- package/src/resources/extensions/gsd/tests/decisions-projection-from-memories.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +26 -1
- package/src/resources/extensions/gsd/tests/doctor-forensics-db-open-regression.test.ts +70 -2
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/exec-tool.test.ts +45 -1
- package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +37 -2
- package/src/resources/extensions/gsd/tests/forensics-error-filter.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +268 -3
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +130 -3
- package/src/resources/extensions/gsd/tests/integration/queue-active-milestone-context-budget.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/integration/quick-branch-lifecycle.test.ts +56 -9
- package/src/resources/extensions/gsd/tests/json-persistence-atomic.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/knowledge-backfill-projection.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/knowledge-cold-start.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/memory-consolidation-scanner.test.ts +78 -0
- package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +31 -1
- package/src/resources/extensions/gsd/tests/metrics-prune-cache-invalidation.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/orchestrator-logs.test.ts +43 -1
- package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +91 -7
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +54 -1
- package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +50 -14
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +195 -1
- package/src/resources/extensions/gsd/tests/read-uat-gate-verdict.test.ts +185 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +191 -0
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +151 -0
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/undo.test.ts +74 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +193 -14
- package/src/resources/extensions/gsd/tests/unmerged-milestone-guard.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/validation-block-guard.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/verification-retry-policy.test.ts +69 -4
- package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +151 -2
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +39 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -2
- package/src/resources/extensions/gsd/tools/complete-slice.ts +2 -2
- package/src/resources/extensions/gsd/tools/complete-task.ts +53 -15
- package/src/resources/extensions/gsd/tools/exec-tool.ts +7 -3
- package/src/resources/extensions/gsd/undo.ts +47 -6
- package/src/resources/extensions/gsd/unit-context-composer.ts +33 -7
- package/src/resources/extensions/gsd/unit-registry.ts +32 -4
- package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +41 -5
- package/src/resources/extensions/gsd/validation-block-guard.ts +13 -7
- package/src/resources/extensions/gsd/verification-gate.ts +8 -0
- package/src/resources/extensions/gsd/workflow-manifest.ts +4 -1
- package/src/resources/extensions/gsd/workflow-projections.ts +20 -14
- package/src/resources/extensions/gsd/workspace-git-preflight.ts +31 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +41 -1
- package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +0 -1
- package/dist/web/standalone/.next/static/chunks/796.e0bdc932325d7e03.js +0 -10
- package/dist/web/standalone/.next/static/chunks/webpack-f46ea08200a0227e.js +0 -1
- /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → -ji77QJck2IKIwrGza1Bs}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → -ji77QJck2IKIwrGza1Bs}/_ssgManifest.js +0 -0
|
@@ -94,27 +94,48 @@ function getActiveDecisions() {
|
|
|
94
94
|
return [];
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
*/
|
|
103
|
-
function memoryHasSourceMarker(markerKey, value) {
|
|
97
|
+
function emptyMemorySourceMarkers() {
|
|
98
|
+
return { decisionIds: new Set(), knowledgeIds: new Set() };
|
|
99
|
+
}
|
|
100
|
+
function getMemorySourceMarkers() {
|
|
101
|
+
const markers = emptyMemorySourceMarkers();
|
|
104
102
|
if (!isDbAvailable())
|
|
105
|
-
return
|
|
103
|
+
return markers;
|
|
106
104
|
const adapter = _getAdapter();
|
|
107
105
|
if (!adapter)
|
|
108
|
-
return
|
|
106
|
+
return markers;
|
|
109
107
|
try {
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
.
|
|
113
|
-
|
|
114
|
-
|
|
108
|
+
const rows = adapter
|
|
109
|
+
.prepare("SELECT structured_fields FROM memories WHERE structured_fields IS NOT NULL")
|
|
110
|
+
.all();
|
|
111
|
+
for (const row of rows) {
|
|
112
|
+
collectMemorySourceMarker(markers, row["structured_fields"]);
|
|
113
|
+
}
|
|
115
114
|
}
|
|
116
115
|
catch {
|
|
117
|
-
return
|
|
116
|
+
return markers;
|
|
117
|
+
}
|
|
118
|
+
return markers;
|
|
119
|
+
}
|
|
120
|
+
function collectMemorySourceMarker(markers, raw) {
|
|
121
|
+
if (typeof raw !== "string" || raw.length === 0)
|
|
122
|
+
return;
|
|
123
|
+
try {
|
|
124
|
+
const parsed = JSON.parse(raw);
|
|
125
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
126
|
+
return;
|
|
127
|
+
const fields = parsed;
|
|
128
|
+
const decisionId = fields["sourceDecisionId"];
|
|
129
|
+
if (typeof decisionId === "string" && decisionId.length > 0) {
|
|
130
|
+
markers.decisionIds.add(decisionId);
|
|
131
|
+
}
|
|
132
|
+
const knowledgeId = fields["sourceKnowledgeId"];
|
|
133
|
+
if (typeof knowledgeId === "string" && knowledgeId.length > 0) {
|
|
134
|
+
markers.knowledgeIds.add(knowledgeId);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return;
|
|
118
139
|
}
|
|
119
140
|
}
|
|
120
141
|
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
@@ -127,10 +148,14 @@ const SAMPLE_LIMIT = 5;
|
|
|
127
148
|
export function scanConsolidationGaps(basePath) {
|
|
128
149
|
// ── Decisions ────────────────────────────────────────────────────────
|
|
129
150
|
const decisions = getActiveDecisions();
|
|
151
|
+
const knowledgeRows = parseKnowledgeRows(knowledgeMdContent(basePath));
|
|
152
|
+
const memorySourceMarkers = decisions.length > 0 || knowledgeRows.length > 0
|
|
153
|
+
? getMemorySourceMarkers()
|
|
154
|
+
: emptyMemorySourceMarkers();
|
|
130
155
|
const decisionSamples = [];
|
|
131
156
|
let decisionMigrated = 0;
|
|
132
157
|
for (const decision of decisions) {
|
|
133
|
-
if (
|
|
158
|
+
if (memorySourceMarkers.decisionIds.has(decision.id)) {
|
|
134
159
|
decisionMigrated += 1;
|
|
135
160
|
continue;
|
|
136
161
|
}
|
|
@@ -142,16 +167,14 @@ export function scanConsolidationGaps(basePath) {
|
|
|
142
167
|
}
|
|
143
168
|
}
|
|
144
169
|
// ── KNOWLEDGE.md ─────────────────────────────────────────────────────
|
|
145
|
-
const knowledgeRows = parseKnowledgeRows(knowledgeMdContent(basePath));
|
|
146
170
|
const knowledgeByTable = { rules: 0, patterns: 0, lessons: 0 };
|
|
147
171
|
const knowledgeSamples = [];
|
|
148
172
|
let knowledgeMigrated = 0;
|
|
149
173
|
for (const row of knowledgeRows) {
|
|
150
174
|
knowledgeByTable[row.table] += 1;
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
if (memoryHasSourceMarker("sourceKnowledgeId", row.id)) {
|
|
175
|
+
// KNOWLEDGE.md backfill writes `sourceKnowledgeId`; rows without that
|
|
176
|
+
// marker are still reported as consolidation gaps.
|
|
177
|
+
if (memorySourceMarkers.knowledgeIds.has(row.id)) {
|
|
155
178
|
knowledgeMigrated += 1;
|
|
156
179
|
continue;
|
|
157
180
|
}
|
|
@@ -17,7 +17,7 @@ import { join } from "node:path";
|
|
|
17
17
|
import { openSync, closeSync, unlinkSync, statSync, writeFileSync } from "node:fs";
|
|
18
18
|
import { gsdRoot } from "./paths.js";
|
|
19
19
|
import { getAndClearSkills } from "./skill-telemetry.js";
|
|
20
|
-
import { loadJsonFile, loadJsonFileOrNull,
|
|
20
|
+
import { loadJsonFile, loadJsonFileOrNull, saveJsonFileCompact } from "./json-persistence.js";
|
|
21
21
|
import { parseUnitId } from "./unit-id.js";
|
|
22
22
|
import { buildAuditEnvelope, emitUokAuditEvent } from "./uok/audit.js";
|
|
23
23
|
import { isUnifiedAuditEnabled } from "./uok/audit-toggle.js";
|
|
@@ -636,7 +636,7 @@ export function pruneMetricsLedger(base, keepCount) {
|
|
|
636
636
|
return 0;
|
|
637
637
|
const removed = disk.units.length - keepCount;
|
|
638
638
|
disk.units = keepNewestUnits(disk.units, keepCount);
|
|
639
|
-
|
|
639
|
+
saveJsonFileCompact(metricsPath(base), disk);
|
|
640
640
|
// Keep the in-memory ledger in sync if it is loaded for this session.
|
|
641
641
|
if (ledger) {
|
|
642
642
|
ledger.units = keepNewestUnits(ledger.units, keepCount);
|
|
@@ -799,7 +799,7 @@ function saveLedger(base, data) {
|
|
|
799
799
|
: dataUnits;
|
|
800
800
|
merged.sort(compareUnitsByTime);
|
|
801
801
|
data.units = keepNewestUnits(merged);
|
|
802
|
-
|
|
802
|
+
saveJsonFileCompact(path, data);
|
|
803
803
|
}
|
|
804
804
|
finally {
|
|
805
805
|
releaseLock(lockPath);
|
|
@@ -813,6 +813,6 @@ function saveLedger(base, data) {
|
|
|
813
813
|
// read-merge-write sequence without mutual exclusion.
|
|
814
814
|
logWarning("fs", "saveLedger: lock not acquired — falling back to direct write (no merge)");
|
|
815
815
|
data.units = keepNewestUnits(data.units);
|
|
816
|
-
|
|
816
|
+
saveJsonFileCompact(path, data);
|
|
817
817
|
}
|
|
818
818
|
}
|
|
@@ -45,6 +45,27 @@ function scanHasExtraIdentities(a, b) {
|
|
|
45
45
|
hasExtra(a.slices, b.slices) ||
|
|
46
46
|
hasExtra(a.tasks, b.tasks));
|
|
47
47
|
}
|
|
48
|
+
function paddedMilestoneId(id) {
|
|
49
|
+
return /^\d+$/.test(id) ? `M${id.padStart(3, "0")}` : null;
|
|
50
|
+
}
|
|
51
|
+
function replaceSetPrefix(values, from, to) {
|
|
52
|
+
for (const value of [...values]) {
|
|
53
|
+
if (value !== from && !value.startsWith(`${from}/`))
|
|
54
|
+
continue;
|
|
55
|
+
values.delete(value);
|
|
56
|
+
values.add(`${to}${value.slice(from.length)}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function alignNumericMarkdownIdsWithDb(markdownScan, dbScan) {
|
|
60
|
+
for (const dbId of dbScan.milestones) {
|
|
61
|
+
const paddedId = paddedMilestoneId(dbId);
|
|
62
|
+
if (!paddedId || dbScan.milestones.has(paddedId) || !markdownScan.milestones.has(paddedId))
|
|
63
|
+
continue;
|
|
64
|
+
replaceSetPrefix(markdownScan.milestones, paddedId, dbId);
|
|
65
|
+
replaceSetPrefix(markdownScan.slices, paddedId, dbId);
|
|
66
|
+
replaceSetPrefix(markdownScan.tasks, paddedId, dbId);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
48
69
|
/**
|
|
49
70
|
* True when the DB holds any milestone/slice/task identity the markdown lacks —
|
|
50
71
|
* i.e. a `/gsd recover --confirm` (markdown → DB) would DELETE authoritative DB
|
|
@@ -126,6 +147,7 @@ export async function checkMarkdownHierarchyAgainstDb(basePath) {
|
|
|
126
147
|
refreshWorkflowDatabaseFromDisk();
|
|
127
148
|
const dbScan = scanDbHierarchy();
|
|
128
149
|
const beforeDb = dbScan.counts;
|
|
150
|
+
alignNumericMarkdownIdsWithDb(markdownScan, dbScan);
|
|
129
151
|
// Discussion-phase scratch: a milestone dir with no ROADMAP and no DB row is
|
|
130
152
|
// a pre-registration discussion artifact (CONTEXT/CONTEXT-DRAFT only — the
|
|
131
153
|
// queued DB row is inserted only at discussion handoff). Treating it as
|
|
@@ -123,7 +123,8 @@ export function clearReservedMilestoneIds() {
|
|
|
123
123
|
reservedMilestoneIds.clear();
|
|
124
124
|
}
|
|
125
125
|
// ─── Discovery ──────────────────────────────────────────────────────────────
|
|
126
|
-
function scanMilestoneIdsFromDir(dir) {
|
|
126
|
+
function scanMilestoneIdsFromDir(basePath, dir) {
|
|
127
|
+
const legacyNumericIds = idsWithLegacyNumericDirs(basePath);
|
|
127
128
|
return readdirSync(dir, { withFileTypes: true })
|
|
128
129
|
.filter((d) => d.isDirectory())
|
|
129
130
|
.map((d) => {
|
|
@@ -131,6 +132,12 @@ function scanMilestoneIdsFromDir(dir) {
|
|
|
131
132
|
if (MILESTONE_ID_RE.test(d.name)) {
|
|
132
133
|
return d.name;
|
|
133
134
|
}
|
|
135
|
+
// Legacy recovery/imports may contain bare numeric milestone directories
|
|
136
|
+
// (for example `milestones/15/15-ROADMAP.md`). The DB can preserve that
|
|
137
|
+
// id, and rebuild renders it, so drift scans must see it too.
|
|
138
|
+
if (/^\d+$/.test(d.name)) {
|
|
139
|
+
return d.name;
|
|
140
|
+
}
|
|
134
141
|
// Legacy layout: M001-abcdef-slug descriptor directories
|
|
135
142
|
const legacyMatch = d.name.match(/^(M\d{3}(?:-[a-z0-9]{6})?)-/);
|
|
136
143
|
if (legacyMatch) {
|
|
@@ -144,12 +151,35 @@ function scanMilestoneIdsFromDir(dir) {
|
|
|
144
151
|
if (MILESTONE_ID_RE.test(fromSlug)) {
|
|
145
152
|
return fromSlug;
|
|
146
153
|
}
|
|
154
|
+
const numericId = String(phaseNum);
|
|
155
|
+
if (legacyNumericIds.has(numericId)) {
|
|
156
|
+
return numericId;
|
|
157
|
+
}
|
|
147
158
|
return `M${String(phaseNum).padStart(3, "0")}`;
|
|
148
159
|
}
|
|
149
160
|
return null;
|
|
150
161
|
})
|
|
151
162
|
.filter((id) => id !== null);
|
|
152
163
|
}
|
|
164
|
+
function idsWithLegacyNumericDirs(basePath) {
|
|
165
|
+
const legacyDir = join(gsdProjectionRoot(basePath), "milestones");
|
|
166
|
+
const ids = new Set();
|
|
167
|
+
try {
|
|
168
|
+
for (const entry of readdirSync(legacyDir, { withFileTypes: true })) {
|
|
169
|
+
if (entry.isDirectory() && /^\d+$/.test(entry.name))
|
|
170
|
+
ids.add(entry.name);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
// A missing legacy directory is the common, benign case (nothing to
|
|
175
|
+
// consult). Only surface a real failure when the directory exists but
|
|
176
|
+
// could not be read.
|
|
177
|
+
if (existsSync(legacyDir)) {
|
|
178
|
+
logWarning("engine", `idsWithLegacyNumericDirs: ${legacyDir} exists but readdirSync failed — ${getErrorMessage(err)}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return ids;
|
|
182
|
+
}
|
|
153
183
|
/** Scan the milestones directory and return IDs sorted by queue order (or numeric fallback). */
|
|
154
184
|
export function findMilestoneIds(basePath) {
|
|
155
185
|
const root = gsdProjectionRoot(basePath);
|
|
@@ -163,7 +193,7 @@ export function findMilestoneIds(basePath) {
|
|
|
163
193
|
const ids = new Set();
|
|
164
194
|
for (const dir of dirs) {
|
|
165
195
|
try {
|
|
166
|
-
for (const id of scanMilestoneIdsFromDir(dir))
|
|
196
|
+
for (const id of scanMilestoneIdsFromDir(basePath, dir))
|
|
167
197
|
ids.add(id);
|
|
168
198
|
}
|
|
169
199
|
catch (err) {
|
|
@@ -10,6 +10,8 @@ import { logWarning } from "./workflow-logger.js";
|
|
|
10
10
|
import { resolveTasksDir } from "./paths.js";
|
|
11
11
|
/** Large enough for unbounded milestone-history git log scans in big repos. */
|
|
12
12
|
const GIT_LOG_MAX_BUFFER = 16 * 1024 * 1024;
|
|
13
|
+
const LOG_FIELD_SEPARATOR = "\x1f";
|
|
14
|
+
const LOG_RECORD_SEPARATOR = "\x1e";
|
|
13
15
|
/**
|
|
14
16
|
* Check whether a milestone produced implementation artifacts (non-`.gsd/`
|
|
15
17
|
* files) in git history. The primary signal is the branch diff against the
|
|
@@ -179,7 +181,7 @@ function getChangedFilesFromMilestoneTaggedCommits(basePath, milestoneId) {
|
|
|
179
181
|
// Primary: path-scoped log against .gsd/milestones/<id>. Fast and unbounded
|
|
180
182
|
// by depth when .gsd/ is tracked in git.
|
|
181
183
|
const scoped = scanGsdTaggedCommits(basePath, milestoneId, [
|
|
182
|
-
"log", "--format=%H%x1f%B%
|
|
184
|
+
"log", "--full-diff", "--name-only", "--format=%x1e%H%x1f%B%x1f", "HEAD", "--", `.gsd/milestones/${milestoneId}`,
|
|
183
185
|
]);
|
|
184
186
|
if (!scoped.ok)
|
|
185
187
|
return scoped;
|
|
@@ -195,7 +197,7 @@ function getChangedFilesFromMilestoneTaggedCommits(basePath, milestoneId) {
|
|
|
195
197
|
// reintroducing the rolling-depth failure class removed in #4699 where
|
|
196
198
|
// milestone evidence aged out behind unrelated activity.
|
|
197
199
|
const unscoped = scanGsdTaggedCommits(basePath, milestoneId, [
|
|
198
|
-
"log", "--format=%H%x1f%B%
|
|
200
|
+
"log", "--name-only", "--format=%x1e%H%x1f%B%x1f", "HEAD",
|
|
199
201
|
]);
|
|
200
202
|
if (!unscoped.ok)
|
|
201
203
|
return scoped.matched ? scoped : unscoped;
|
|
@@ -280,8 +282,7 @@ function backfillChangedFilesFromUntaggedMilestoneCommits(basePath, milestoneId)
|
|
|
280
282
|
continue;
|
|
281
283
|
if (commitMessageHasGsdTrailer(record.message))
|
|
282
284
|
continue;
|
|
283
|
-
const
|
|
284
|
-
const implementationFiles = commitFiles.map(normalizeRepoPath).filter(isImplementationPath);
|
|
285
|
+
const implementationFiles = record.files.map(normalizeRepoPath).filter(isImplementationPath);
|
|
285
286
|
if (implementationFiles.length === 0)
|
|
286
287
|
continue;
|
|
287
288
|
if (!implementationFiles.some((file) => hintSet.has(file)))
|
|
@@ -306,24 +307,28 @@ function backfillChangedFilesFromUntaggedMilestoneCommits(basePath, milestoneId)
|
|
|
306
307
|
}
|
|
307
308
|
}
|
|
308
309
|
function getCommitRecords(basePath) {
|
|
309
|
-
const logOutput = execFileSync("git", ["log", "--format=%H%x1f%P%x1f%cI%x1f%B%
|
|
310
|
+
const logOutput = execFileSync("git", ["log", "--name-only", "--format=%x1e%H%x1f%P%x1f%cI%x1f%B%x1f", "HEAD"], {
|
|
310
311
|
cwd: basePath,
|
|
311
312
|
stdio: ["ignore", "pipe", "pipe"],
|
|
312
313
|
encoding: "utf-8",
|
|
313
314
|
maxBuffer: GIT_LOG_MAX_BUFFER,
|
|
314
315
|
});
|
|
315
316
|
return logOutput
|
|
316
|
-
.split(
|
|
317
|
-
.map((record) => record.trim())
|
|
317
|
+
.split(LOG_RECORD_SEPARATOR)
|
|
318
318
|
.filter(Boolean)
|
|
319
319
|
.flatMap((record) => {
|
|
320
|
-
const parts = record.split(
|
|
321
|
-
if (parts.length <
|
|
320
|
+
const parts = record.split(LOG_FIELD_SEPARATOR);
|
|
321
|
+
if (parts.length < 5)
|
|
322
322
|
return [];
|
|
323
|
-
const [hash, parents, committedAt
|
|
324
|
-
|
|
323
|
+
const [hash, parents, committedAt] = parts;
|
|
324
|
+
const files = parseNameOnlyFiles(parts.at(-1) ?? "");
|
|
325
|
+
const message = parts.slice(3, -1).join(LOG_FIELD_SEPARATOR);
|
|
326
|
+
return [{ hash: hash.trim(), parents: parents.trim(), committedAt: committedAt.trim(), message, files }];
|
|
325
327
|
});
|
|
326
328
|
}
|
|
329
|
+
function parseNameOnlyFiles(rawFiles) {
|
|
330
|
+
return rawFiles.split(/\r?\n/).map((file) => file.trim()).filter(Boolean);
|
|
331
|
+
}
|
|
327
332
|
function isFullCommitSha(value) {
|
|
328
333
|
return /^[0-9a-f]{40}$/i.test(value);
|
|
329
334
|
}
|
|
@@ -336,23 +341,24 @@ function scanGsdTaggedCommits(basePath, milestoneId, gitArgs) {
|
|
|
336
341
|
maxBuffer: GIT_LOG_MAX_BUFFER,
|
|
337
342
|
});
|
|
338
343
|
const records = logOutput
|
|
339
|
-
.split(
|
|
340
|
-
.map((record) => record.trim())
|
|
344
|
+
.split(LOG_RECORD_SEPARATOR)
|
|
341
345
|
.filter(Boolean)
|
|
342
346
|
.flatMap((record) => {
|
|
343
|
-
const
|
|
344
|
-
if (
|
|
347
|
+
const parts = record.split(LOG_FIELD_SEPARATOR);
|
|
348
|
+
if (parts.length < 3)
|
|
349
|
+
return [];
|
|
350
|
+
const hash = parts[0].trim();
|
|
351
|
+
if (!hash)
|
|
345
352
|
return [];
|
|
346
|
-
const
|
|
347
|
-
const message =
|
|
348
|
-
return [{
|
|
353
|
+
const files = parseNameOnlyFiles(parts.at(-1) ?? "");
|
|
354
|
+
const message = parts.slice(1, -1).join(LOG_FIELD_SEPARATOR);
|
|
355
|
+
return [{ message, files }];
|
|
349
356
|
});
|
|
350
357
|
const files = new Set();
|
|
351
358
|
let matched = false;
|
|
352
|
-
for (const {
|
|
359
|
+
for (const { message, files: commitFiles } of records) {
|
|
353
360
|
if (!commitMessageHasGsdTrailer(message))
|
|
354
361
|
continue;
|
|
355
|
-
const commitFiles = getChangedFilesForCommit(basePath, hash);
|
|
356
362
|
if (!commitMatchesMilestone(basePath, message, milestoneId, commitFiles))
|
|
357
363
|
continue;
|
|
358
364
|
matched = true;
|
|
@@ -26,16 +26,78 @@ function _isAlreadyRestoredApplyError(err) {
|
|
|
26
26
|
return /already exists, no checkout/i.test(message);
|
|
27
27
|
}
|
|
28
28
|
export { _isAlreadyRestoredApplyError };
|
|
29
|
+
function gitOutput(basePath, args) {
|
|
30
|
+
return execFileSync("git", args, {
|
|
31
|
+
cwd: basePath,
|
|
32
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
33
|
+
encoding: "utf-8",
|
|
34
|
+
env: GIT_NO_PROMPT_ENV,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function listStashUntrackedPaths(basePath, stashRef) {
|
|
38
|
+
try {
|
|
39
|
+
return gitOutput(basePath, ["ls-tree", "-r", "-z", "--name-only", `${stashRef}^3`]).split("\0").filter(Boolean);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function listStashTrackedPaths(basePath, stashRef) {
|
|
46
|
+
try {
|
|
47
|
+
return gitOutput(basePath, ["diff", "--name-only", "-z", `${stashRef}^1`, stashRef]).split("\0").filter(Boolean);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function readWorkingTreeStatus(basePath) {
|
|
54
|
+
try {
|
|
55
|
+
return gitOutput(basePath, ["status", "--porcelain", "-z", "--untracked-files=all"])
|
|
56
|
+
.split("\0")
|
|
57
|
+
.filter(Boolean)
|
|
58
|
+
.map((entry) => ({ code: entry.slice(0, 2), path: entry.slice(3) }));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function isAlreadyRestoredUntrackedStatus(basePath, stashRef, statusEntries) {
|
|
65
|
+
if (statusEntries.length === 0 || statusEntries.some((entry) => entry.code !== "??"))
|
|
66
|
+
return false;
|
|
67
|
+
const stashTrackedPaths = listStashTrackedPaths(basePath, stashRef);
|
|
68
|
+
if (!stashTrackedPaths || stashTrackedPaths.length > 0)
|
|
69
|
+
return false;
|
|
70
|
+
const stashUntrackedPaths = listStashUntrackedPaths(basePath, stashRef);
|
|
71
|
+
if (!stashUntrackedPaths || stashUntrackedPaths.length === 0)
|
|
72
|
+
return false;
|
|
73
|
+
const stashUntrackedPathSet = new Set(stashUntrackedPaths);
|
|
74
|
+
const workingTreeUntrackedPathSet = new Set(statusEntries.map((entry) => entry.path));
|
|
75
|
+
// "Already restored" requires the working tree's untracked paths to match the
|
|
76
|
+
// stash's untracked paths exactly, in BOTH directions:
|
|
77
|
+
// 1. every current untracked path comes from the stash (no unexplained user
|
|
78
|
+
// work that an apply would clobber), AND
|
|
79
|
+
// 2. every stash untracked path is already present in the working tree.
|
|
80
|
+
// Without (2), a partial restore (where only some stash files were recovered)
|
|
81
|
+
// would be misread as fully restored: the audit would no-op without applying
|
|
82
|
+
// or warning, leaving the remaining pre-merge files missing.
|
|
83
|
+
return (statusEntries.every((entry) => stashUntrackedPathSet.has(entry.path)) &&
|
|
84
|
+
stashUntrackedPaths.every((path) => workingTreeUntrackedPathSet.has(path)));
|
|
85
|
+
}
|
|
86
|
+
function manualApplyWarning(stashRef, milestoneId, reason) {
|
|
87
|
+
return (`Pre-merge stash ${stashRef} for milestone ${milestoneId} not auto-applied: ${reason}; ` +
|
|
88
|
+
`run \`git stash apply ${stashRef}\` manually to restore your pre-merge changes.`);
|
|
89
|
+
}
|
|
29
90
|
/**
|
|
30
91
|
* Audit `git stash list` for orphaned `gsd-preflight-stash:M00x:*` entries.
|
|
31
92
|
*
|
|
32
93
|
* The matching merge code in `phases.ts` previously skipped the postflight
|
|
33
94
|
* pop whenever `mergeAndExit` threw, leaking the user's pre-merge working
|
|
34
|
-
* tree into the stash list.
|
|
35
|
-
*
|
|
95
|
+
* tree into the stash list. Completed-milestone stashes are only auto-applied
|
|
96
|
+
* when the working tree is clean and the stash does not modify tracked files.
|
|
97
|
+
* Dirty trees and tracked-file stashes are left untouched with a manual
|
|
98
|
+
* recovery warning. When auto-apply is safe, `git stash apply` is invoked —
|
|
36
99
|
* NOT `pop`. The stash entry stays in the list so the user retains a backup
|
|
37
|
-
* if the apply produces unexpected merge results.
|
|
38
|
-
* startup runs.
|
|
100
|
+
* if the apply produces unexpected merge results.
|
|
39
101
|
*
|
|
40
102
|
* Failures are best-effort: a list error (no repo, git unavailable) returns
|
|
41
103
|
* an empty result. An apply error becomes a warning the user sees alongside
|
|
@@ -45,12 +107,7 @@ export function auditOrphanedPreflightStashes(basePath, isMilestoneComplete) {
|
|
|
45
107
|
const result = { applied: [], warnings: [] };
|
|
46
108
|
let listOutput;
|
|
47
109
|
try {
|
|
48
|
-
listOutput =
|
|
49
|
-
cwd: basePath,
|
|
50
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
51
|
-
encoding: "utf-8",
|
|
52
|
-
env: GIT_NO_PROMPT_ENV,
|
|
53
|
-
});
|
|
110
|
+
listOutput = gitOutput(basePath, ["stash", "list", "--format=%gd%x00%s"]);
|
|
54
111
|
}
|
|
55
112
|
catch {
|
|
56
113
|
return result;
|
|
@@ -78,13 +135,32 @@ export function auditOrphanedPreflightStashes(basePath, isMilestoneComplete) {
|
|
|
78
135
|
}
|
|
79
136
|
if (!complete)
|
|
80
137
|
continue;
|
|
138
|
+
const statusEntries = readWorkingTreeStatus(basePath);
|
|
139
|
+
if (!statusEntries) {
|
|
140
|
+
result.warnings.push(manualApplyWarning(ref, milestoneId, "could not verify working tree status"));
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (statusEntries.length > 0) {
|
|
144
|
+
if (isAlreadyRestoredUntrackedStatus(basePath, ref, statusEntries))
|
|
145
|
+
continue;
|
|
146
|
+
result.warnings.push(manualApplyWarning(ref, milestoneId, "working tree not clean"));
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const trackedPaths = listStashTrackedPaths(basePath, ref);
|
|
150
|
+
if (trackedPaths === null) {
|
|
151
|
+
result.warnings.push(manualApplyWarning(ref, milestoneId, "could not inspect tracked paths in stash"));
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (trackedPaths.length > 0) {
|
|
155
|
+
// A retained tracked-file stash has no durable marker showing whether a
|
|
156
|
+
// clean tree needs first recovery or would be re-mutated by an old backup.
|
|
157
|
+
const preview = trackedPaths.slice(0, 5).join(", ");
|
|
158
|
+
const suffix = trackedPaths.length > 5 ? `, and ${trackedPaths.length - 5} more` : "";
|
|
159
|
+
result.warnings.push(manualApplyWarning(ref, milestoneId, `stash modifies tracked files (${preview}${suffix})`));
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
81
162
|
try {
|
|
82
|
-
|
|
83
|
-
cwd: basePath,
|
|
84
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
85
|
-
encoding: "utf-8",
|
|
86
|
-
env: GIT_NO_PROMPT_ENV,
|
|
87
|
-
});
|
|
163
|
+
gitOutput(basePath, ["stash", "apply", "--quiet", ref]);
|
|
88
164
|
result.applied.push({ milestoneId, stashRef: ref });
|
|
89
165
|
}
|
|
90
166
|
catch (err) {
|
|
@@ -16,18 +16,20 @@ You are running the GSD **code-review** workflow — review source files changed
|
|
|
16
16
|
|
|
17
17
|
1. **Determine the file set.** Use the explicit `--files` list if provided. Otherwise derive the changed source files from the active slice's recent commits / SUMMARY, or fall back to the git diff against the base branch. Exclude `.gsd/`, lockfiles, generated/vendored code, and docs unless `--files` names them.
|
|
18
18
|
|
|
19
|
-
2. **
|
|
19
|
+
2. **Gather diff-first context.** Run `git diff` for the file set (or the relevant commit range) and use that as the primary review evidence. Use `read` only when the diff is insufficient — new/untracked files, truncated hunks, or missing surrounding context needed for a finding. Do **not** read every changed file when the diff already covers the edits. If the Tool Call Loop Guard blocks a tool, stop calling tools for the rest of this turn and continue in text.
|
|
20
|
+
|
|
21
|
+
3. **Review at the requested depth:**
|
|
20
22
|
- **quick** — bugs, security, and correctness only.
|
|
21
23
|
- **standard** (default) — quick + maintainability, error handling, edge cases.
|
|
22
24
|
- **deep** — standard + performance, concurrency, API design, test coverage gaps.
|
|
23
25
|
|
|
24
26
|
Ground every finding in the actual code with file path + line reference. Categorize each by severity (critical / warning / nit) and type (bug, security, quality, performance).
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
4. **Write the review.** Produce `.gsd/reviews/{{reviewId}}-REVIEW.md` with the findings table and per-finding detail (location, issue, suggested fix).
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
5. **Present results.** Summarize counts by severity and list the critical findings first.
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
6. **Fix mode** (only when `--fix` is set): for each finding that is safely auto-fixable (deterministic, no behavior change beyond the fix), apply it. After each fix batch, run the project's tests. Commit atomically with the finding IDs referenced in the message. Do not auto-apply findings that are ambiguous or that change public behavior — surface those for a human decision instead.
|
|
31
33
|
|
|
32
34
|
## Success criteria
|
|
33
35
|
|
|
@@ -45,7 +45,7 @@ If slice research is inlined, trust its architectural findings, but verify every
|
|
|
45
45
|
6. Include Threat Surface (Q3), Requirement Impact (Q4), proof level, observability, integration closure, Failure Modes (Q5), Load Profile (Q6), and Negative Tests (Q7) only where applicable.
|
|
46
46
|
7. Right-size tasks. Simple slices can be one task; split only when context, ownership, or verification boundaries justify it.
|
|
47
47
|
8. Task `verify` commands must be safe, simple commands. Do not use shell pipes, redirects, semicolons, backticks, command substitution, output trimming, or grep regex alternation with `|`. If multiple checks are needed, create a small test file and run it with `node --test` or a package test script, or use separate simple commands joined only with `&&`. For absence checks, verify a pattern does not exist with `! grep -q 'pattern' file` or `! rg -q 'pattern' file`; do not use `grep -c` or `rg -c` to assert zero matches because count commands exit 1 when they find zero matches, and the verification gate treats that as failure.
|
|
48
|
-
9. Each task needs the exact `gsd_plan_slice.tasks[]` shape: `taskId`, `title`, `description`, `estimate`, `files`, `verify`, `inputs`, `expectedOutput`, and optional `observabilityImpact`. `description` should contain the Why / Do / Done-when narrative. `files`, `inputs`, and `expectedOutput` must be JSON arrays of strings, even when there is only one path (for example, `"inputs": ["src/index.ts"]`, never `"inputs": "src/index.ts"`). Use paths relative to `{{workingDirectory}}`; do not put absolute paths to the original checkout or any directory outside `{{workingDirectory}}` in `files`, `inputs`, `expectedOutput`, or verification commands. **`expectedOutput` must only list files the task actually creates or overwrites on disk.** Do NOT include files the task merely reads, verifies, tests, or describes — those belong in `inputs`, `verify`, `description`, or slice success criteria. If a task is a pure verification or test task that produces no new files, `expectedOutput` must be `[]`; if it writes a test-result log or assertion output file, list only that concrete file path. A file that does not yet exist on disk and is needed as an `input` must be produced by an earlier task's `expectedOutput` — if no prior task creates it, add a task before this one that does. Never list GSD planning artifacts — anything under `.gsd/`, `.planning/`, or `.audits/`, or artifact names like `M001-CONTEXT.md` / `S01-PLAN.md` — in `inputs`, `files`, or `expectedOutput`: their content is preloaded as context, and they are written by workflow tools, not by tasks.
|
|
48
|
+
9. Each task needs the exact `gsd_plan_slice.tasks[]` shape: `taskId`, `title`, `description`, `estimate`, `files`, `verify`, `inputs`, `expectedOutput`, and optional `observabilityImpact`. `description` should contain the Why / Do / Done-when narrative. Any npm package name in a task description's install/add instructions (`npm install`, `yarn add`, `pnpm add`) or in `require('...')` or `import ... from '...'` code examples is subject to pre-execution registry validation; reference only packages that exist on the public npm registry, and remove or correct any package name that is not real. `files`, `inputs`, and `expectedOutput` must be JSON arrays of strings, even when there is only one path (for example, `"inputs": ["src/index.ts"]`, never `"inputs": "src/index.ts"`). Use paths relative to `{{workingDirectory}}`; do not put absolute paths to the original checkout or any directory outside `{{workingDirectory}}` in `files`, `inputs`, `expectedOutput`, or verification commands. **`expectedOutput` must only list files the task actually creates or overwrites on disk.** Do NOT include files the task merely reads, verifies, tests, or describes — those belong in `inputs`, `verify`, `description`, or slice success criteria. If a task is a pure verification or test task that produces no new files, `expectedOutput` must be `[]`; if it writes a test-result log or assertion output file, list only that concrete file path. A file that does not yet exist on disk and is needed as an `input` must be produced by an earlier task's `expectedOutput` — if no prior task creates it, add a task before this one that does. Never list GSD planning artifacts — anything under `.gsd/`, `.planning/`, or `.audits/`, or artifact names like `M001-CONTEXT.md` / `S01-PLAN.md` — in `inputs`, `files`, or `expectedOutput`: their content is preloaded as context, and they are written by workflow tools, not by tasks.
|
|
49
49
|
10. Persist with `gsd_plan_slice` using `milestoneId`, `sliceId`, `goal`, optional `successCriteria`/`proofLevel`/`integrationClosure`/`observabilityImpact`, and `tasks`. `gsd_plan_slice` handles task persistence transactionally and renders `{{outputPath}}` plus task plans; do not call `gsd_plan_task`. The DB-backed tool is the canonical write path. Do **not** rely on direct `PLAN.md` writes as the source of truth.
|
|
50
50
|
11. Self-audit before finishing: goal/demo closure, requirement coverage, deliverable coverage audit (cross-check every file listed in CONTEXT.md `## Scope` / `### In Scope` against task `files` or `expectedOutput`), locked decisions, concrete paths, dependency order, wiring, scope size, proof truthfulness, feature completeness, and quality gates. Quality gates: non-trivial slices/tasks include specific Q3-Q7 coverage where applicable.
|
|
51
51
|
12. If planning creates structural decisions, call `gsd_decision_save` for each; the tool persists the decision and regenerates `.gsd/DECISIONS.md`.
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* Quick tasks live in `.gsd/quick/` and are tracked in STATE.md's
|
|
9
9
|
* "Quick Tasks Completed" table.
|
|
10
10
|
*/
|
|
11
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
12
|
-
import { isAbsolute, join, relative } from "node:path";
|
|
11
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
13
13
|
import { QUICK_BRANCH_RE } from "./branch-patterns.js";
|
|
14
14
|
import { loadPrompt } from "./prompt-loader.js";
|
|
15
15
|
import { gsdRoot } from "./paths.js";
|
|
@@ -19,6 +19,7 @@ import { nativeBranchExists, nativeDetectMainBranch, nativeDiffNumstat } from ".
|
|
|
19
19
|
import { nativeHasStagedChanges } from "./native-git-bridge.js";
|
|
20
20
|
import { resolveUokFlags } from "./uok/flags.js";
|
|
21
21
|
let pendingQuickReturn = null;
|
|
22
|
+
const pendingQuickReturnMisses = new Map();
|
|
22
23
|
// ─── Quick Task Helpers ───────────────────────────────────────────────────────
|
|
23
24
|
/**
|
|
24
25
|
* Generate a URL-friendly slug from a description.
|
|
@@ -169,11 +170,36 @@ export function buildQuickCommitInstruction(basePath, root) {
|
|
|
169
170
|
" - Quick tasks run outside the auto-mode lifecycle — there is no system auto-commit, so commit directly here.",
|
|
170
171
|
].join("\n");
|
|
171
172
|
}
|
|
173
|
+
function readHeadBranchName(basePath) {
|
|
174
|
+
try {
|
|
175
|
+
const gitPath = join(basePath, ".git");
|
|
176
|
+
if (!existsSync(gitPath))
|
|
177
|
+
return null;
|
|
178
|
+
let headPath;
|
|
179
|
+
if (lstatSync(gitPath).isDirectory()) {
|
|
180
|
+
headPath = join(gitPath, "HEAD");
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
const gitFile = readFileSync(gitPath, "utf-8").trim();
|
|
184
|
+
if (!gitFile.startsWith("gitdir: "))
|
|
185
|
+
return null;
|
|
186
|
+
headPath = join(resolve(basePath, gitFile.slice("gitdir: ".length)), "HEAD");
|
|
187
|
+
}
|
|
188
|
+
const head = readFileSync(headPath, "utf-8").trim();
|
|
189
|
+
if (!head.startsWith("ref: refs/heads/"))
|
|
190
|
+
return null;
|
|
191
|
+
return head.slice("ref: refs/heads/".length);
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
172
197
|
function quickReturnStatePath(basePath) {
|
|
173
198
|
return join(gsdRoot(basePath), "runtime", "quick-return.json");
|
|
174
199
|
}
|
|
175
200
|
function persistPendingReturn(state) {
|
|
176
201
|
pendingQuickReturn = state;
|
|
202
|
+
pendingQuickReturnMisses.delete(state.basePath);
|
|
177
203
|
mkdirSync(join(gsdRoot(state.basePath), "runtime"), { recursive: true });
|
|
178
204
|
writeFileSync(quickReturnStatePath(state.basePath), JSON.stringify(state) + "\n", "utf-8");
|
|
179
205
|
}
|
|
@@ -181,6 +207,13 @@ function readPendingReturn(basePath) {
|
|
|
181
207
|
if (pendingQuickReturn && pendingQuickReturn.basePath === basePath) {
|
|
182
208
|
return pendingQuickReturn;
|
|
183
209
|
}
|
|
210
|
+
if (pendingQuickReturnMisses.has(basePath)) {
|
|
211
|
+
const statePath = quickReturnStatePath(basePath);
|
|
212
|
+
if (!existsSync(statePath) && readHeadBranchName(basePath) === pendingQuickReturnMisses.get(basePath)) {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
pendingQuickReturnMisses.delete(basePath);
|
|
216
|
+
}
|
|
184
217
|
try {
|
|
185
218
|
const raw = readFileSync(quickReturnStatePath(basePath), "utf-8");
|
|
186
219
|
const parsed = JSON.parse(raw);
|
|
@@ -191,6 +224,7 @@ function readPendingReturn(basePath) {
|
|
|
191
224
|
&& typeof parsed.slug === "string"
|
|
192
225
|
&& typeof parsed.description === "string") {
|
|
193
226
|
pendingQuickReturn = parsed;
|
|
227
|
+
pendingQuickReturnMisses.delete(basePath);
|
|
194
228
|
return pendingQuickReturn;
|
|
195
229
|
}
|
|
196
230
|
}
|
|
@@ -200,14 +234,23 @@ function readPendingReturn(basePath) {
|
|
|
200
234
|
const inferred = inferQuickReturnFromBranch(basePath);
|
|
201
235
|
if (inferred) {
|
|
202
236
|
pendingQuickReturn = inferred;
|
|
237
|
+
pendingQuickReturnMisses.delete(basePath);
|
|
203
238
|
return inferred;
|
|
204
239
|
}
|
|
240
|
+
const branchAtMiss = readHeadBranchName(basePath);
|
|
241
|
+
if (branchAtMiss) {
|
|
242
|
+
pendingQuickReturnMisses.set(basePath, branchAtMiss);
|
|
243
|
+
}
|
|
205
244
|
return null;
|
|
206
245
|
}
|
|
207
246
|
function clearPendingReturn(basePath) {
|
|
208
247
|
if (pendingQuickReturn?.basePath === basePath) {
|
|
209
248
|
pendingQuickReturn = null;
|
|
210
249
|
}
|
|
250
|
+
const branchAtMiss = readHeadBranchName(basePath);
|
|
251
|
+
if (branchAtMiss) {
|
|
252
|
+
pendingQuickReturnMisses.set(basePath, branchAtMiss);
|
|
253
|
+
}
|
|
211
254
|
rmSync(quickReturnStatePath(basePath), { force: true });
|
|
212
255
|
}
|
|
213
256
|
function hasStagedChanges(basePath) {
|
|
@@ -44,8 +44,11 @@ export function validateFileChanges(basePath, expectedOutput, plannedFiles, file
|
|
|
44
44
|
const actualFiles = getChangedFilesFromLastCommit(basePath);
|
|
45
45
|
if (!actualFiles)
|
|
46
46
|
return null;
|
|
47
|
-
// Filter out
|
|
48
|
-
const projectFiles = actualFiles.filter(f => !f.startsWith(".gsd/") &&
|
|
47
|
+
// Filter out GSD-internal paths — only validate project source files.
|
|
48
|
+
const projectFiles = actualFiles.filter((f) => !f.startsWith(".gsd/") &&
|
|
49
|
+
!f.startsWith(".gsd\\") &&
|
|
50
|
+
!f.startsWith(".gsd-backups/") &&
|
|
51
|
+
!f.startsWith(".gsd-backups\\"));
|
|
49
52
|
// Normalize expected paths (strip leading ./ or /)
|
|
50
53
|
const normalizedExpected = new Set([...allExpected].map((f) => normalizePlannedFileReference(f).replace(/^\.\//, "").replace(/^\//, "")));
|
|
51
54
|
// Build allowlist matchers once (dot: true so patterns like `**/.hidden` work).
|