@opengsd/gsd-pi 1.4.0-dev.c18009cd → 1.5.0-dev.4fb2d460
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 +10 -10
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/ask-user-questions.js +1 -1
- package/dist/resources/extensions/gsd/auto/closeout.js +97 -5
- package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -3
- package/dist/resources/extensions/gsd/auto/pre-dispatch.js +6 -3
- package/dist/resources/extensions/gsd/auto/unit-phase.js +5 -9
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +12 -3
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +11 -7
- package/dist/resources/extensions/gsd/auto-dispatch.js +16 -2
- package/dist/resources/extensions/gsd/auto-recovery.js +50 -6
- package/dist/resources/extensions/gsd/auto-start.js +4 -3
- package/dist/resources/extensions/gsd/auto-worktree-branch-lifecycle.js +189 -0
- package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +133 -0
- package/dist/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.js +35 -0
- package/dist/resources/extensions/gsd/auto-worktree-creation.js +130 -0
- package/dist/resources/extensions/gsd/auto-worktree-entry.js +118 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-already-merged.js +98 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-branch-head.js +52 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-cleanup.js +87 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-code-changes.js +46 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-db-ready.js +68 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-integration-branch.js +51 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-message.js +106 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-pre-teardown.js +64 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-stash.js +168 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge.js +302 -0
- package/dist/resources/extensions/gsd/auto-worktree-milestone-shelter.js +119 -0
- package/dist/resources/extensions/gsd/auto-worktree-path-resolution.js +5 -0
- package/dist/resources/extensions/gsd/auto-worktree-path-utils.js +5 -0
- package/dist/resources/extensions/gsd/auto-worktree-resource-version.js +41 -0
- package/dist/resources/extensions/gsd/auto-worktree-runtime-cleanup.js +99 -0
- package/dist/resources/extensions/gsd/auto-worktree-session-registry.js +57 -0
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +249 -0
- package/dist/resources/extensions/gsd/auto-worktree-teardown.js +120 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +20 -2073
- package/dist/resources/extensions/gsd/auto.js +9 -3
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +20 -2
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +185 -27
- package/dist/resources/extensions/gsd/codebase-generator.js +7 -1
- package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
- package/dist/resources/extensions/gsd/commands-workflow-templates.js +3 -3
- package/dist/resources/extensions/gsd/db/queries.js +9 -9
- package/dist/resources/extensions/gsd/db/writers/cascades.js +20 -2
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +93 -11
- package/dist/resources/extensions/gsd/flat-phase-migration.js +32 -3
- package/dist/resources/extensions/gsd/forensics.js +1 -1
- package/dist/resources/extensions/gsd/git-conflict-resolve.js +2 -2
- package/dist/resources/extensions/gsd/git-service.js +45 -0
- package/dist/resources/extensions/gsd/gsd-db.js +23 -11
- package/dist/resources/extensions/gsd/guided-flow.js +22 -17
- package/dist/resources/extensions/gsd/markdown-renderer.js +15 -3
- package/dist/resources/extensions/gsd/md-importer.js +89 -23
- package/dist/resources/extensions/gsd/migration-auto-check.js +30 -3
- package/dist/resources/extensions/gsd/milestone-merge-transaction.js +11 -0
- package/dist/resources/extensions/gsd/milestone-settlement.js +1 -1
- package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -1
- package/dist/resources/extensions/gsd/paths.js +17 -3
- package/dist/resources/extensions/gsd/preferences-skills.js +10 -16
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +82 -0
- package/dist/resources/extensions/gsd/preferences.js +20 -0
- package/dist/resources/extensions/gsd/prompts/system.md +6 -6
- package/dist/resources/extensions/gsd/quick.js +12 -3
- package/dist/resources/extensions/gsd/skill-discovery.js +36 -23
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +2 -1
- package/dist/resources/extensions/gsd/state/derive/from-db.js +88 -153
- package/dist/resources/extensions/gsd/tool-contract.js +14 -0
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +5 -0
- package/dist/resources/extensions/gsd/tools/reopen-task.js +13 -5
- package/dist/resources/extensions/gsd/unit-registry.js +12 -2
- package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +1 -1
- package/dist/resources/extensions/gsd/validation-block-guard.js +2 -1
- package/dist/resources/extensions/gsd/workspace-git-preflight.js +2 -2
- package/dist/resources/extensions/gsd/worktree-command.js +1 -1
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +14 -9
- package/dist/resources/extensions/gsd/worktree-reentry.js +2 -1
- package/dist/resources/extensions/shared/interview-ui.js +62 -9
- 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 +8 -8
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- 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.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
- package/dist/web/standalone/.next/server/middleware-build-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/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/integrations/hermes/open_gsd_hermes/gsd_client.py +24 -35
- package/integrations/hermes/tests/test_gsd_client_timeout.py +4 -4
- package/package.json +6 -6
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/export-html/template.js +53 -4
- package/packages/gsd-agent-core/package.json +5 -5
- 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 +3 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +14 -9
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/index.d.ts +1 -0
- package/packages/pi-ai/dist/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/index.js +1 -0
- package/packages/pi-ai/dist/index.js.map +1 -1
- package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/transform-messages.js +7 -3
- package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
- package/packages/pi-ai/dist/tool-result-content.d.ts +3 -0
- package/packages/pi-ai/dist/tool-result-content.d.ts.map +1 -0
- package/packages/pi-ai/dist/tool-result-content.js +62 -0
- package/packages/pi-ai/dist/tool-result-content.js.map +1 -0
- package/packages/pi-ai/dist/types.d.ts +8 -20
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.js +74 -53
- package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +30 -2
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skill-directories.d.ts +66 -0
- package/packages/pi-coding-agent/dist/core/skill-directories.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/skill-directories.js +138 -0
- package/packages/pi-coding-agent/dist/core/skill-directories.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts +4 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +4 -1
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -0
- package/packages/pi-coding-agent/dist/index.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/core/export-html/template.js +53 -4
- package/pkg/package.json +1 -1
- package/src/resources/extensions/ask-user-questions.ts +1 -1
- package/src/resources/extensions/gsd/auto/closeout.ts +114 -7
- package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -3
- package/src/resources/extensions/gsd/auto/pre-dispatch.ts +6 -3
- package/src/resources/extensions/gsd/auto/unit-phase.ts +5 -9
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +12 -3
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +13 -7
- package/src/resources/extensions/gsd/auto-dispatch.ts +18 -2
- package/src/resources/extensions/gsd/auto-recovery.ts +50 -6
- package/src/resources/extensions/gsd/auto-start.ts +5 -3
- package/src/resources/extensions/gsd/auto-worktree-branch-lifecycle.ts +237 -0
- package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +166 -0
- package/src/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.ts +37 -0
- package/src/resources/extensions/gsd/auto-worktree-creation.ts +171 -0
- package/src/resources/extensions/gsd/auto-worktree-entry.ts +154 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-already-merged.ts +161 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-branch-head.ts +83 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-cleanup.ts +132 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-code-changes.ts +71 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-db-ready.ts +112 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-integration-branch.ts +99 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-message.ts +166 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-pre-teardown.ts +93 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-stash.ts +213 -0
- package/src/resources/extensions/gsd/auto-worktree-merge.ts +391 -0
- package/src/resources/extensions/gsd/auto-worktree-milestone-shelter.ts +159 -0
- package/src/resources/extensions/gsd/auto-worktree-path-resolution.ts +6 -0
- package/src/resources/extensions/gsd/auto-worktree-path-utils.ts +10 -0
- package/src/resources/extensions/gsd/auto-worktree-resource-version.ts +44 -0
- package/src/resources/extensions/gsd/auto-worktree-runtime-cleanup.ts +119 -0
- package/src/resources/extensions/gsd/auto-worktree-session-registry.ts +71 -0
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +370 -0
- package/src/resources/extensions/gsd/auto-worktree-teardown.ts +161 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +69 -2429
- package/src/resources/extensions/gsd/auto.ts +11 -17
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +20 -2
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +218 -27
- package/src/resources/extensions/gsd/codebase-generator.ts +7 -1
- package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +3 -3
- package/src/resources/extensions/gsd/db/queries.ts +9 -9
- package/src/resources/extensions/gsd/db/writers/cascades.ts +23 -1
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +109 -10
- package/src/resources/extensions/gsd/flat-phase-migration.ts +34 -3
- package/src/resources/extensions/gsd/forensics.ts +1 -1
- package/src/resources/extensions/gsd/git-conflict-resolve.ts +2 -2
- package/src/resources/extensions/gsd/git-service.ts +53 -0
- package/src/resources/extensions/gsd/gsd-db.ts +33 -11
- package/src/resources/extensions/gsd/guided-flow.ts +26 -18
- package/src/resources/extensions/gsd/markdown-renderer.ts +15 -3
- package/src/resources/extensions/gsd/md-importer.ts +93 -22
- package/src/resources/extensions/gsd/migration-auto-check.ts +32 -3
- package/src/resources/extensions/gsd/milestone-merge-transaction.ts +15 -0
- package/src/resources/extensions/gsd/milestone-settlement.ts +1 -1
- package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +3 -1
- package/src/resources/extensions/gsd/paths.ts +21 -3
- package/src/resources/extensions/gsd/preferences-skills.ts +10 -16
- package/src/resources/extensions/gsd/preferences-types.ts +30 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +69 -0
- package/src/resources/extensions/gsd/preferences.ts +20 -0
- package/src/resources/extensions/gsd/prompts/system.md +6 -6
- package/src/resources/extensions/gsd/quick.ts +11 -3
- package/src/resources/extensions/gsd/skill-discovery.ts +47 -23
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +3 -1
- package/src/resources/extensions/gsd/state/derive/from-db.ts +201 -157
- package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +169 -1
- package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +57 -0
- package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-already-merged.test.ts +177 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-branch-head.test.ts +118 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-cleanup.test.ts +115 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-code-changes.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-db-ready.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-integration-branch.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-message.test.ts +114 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-pre-teardown.test.ts +84 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-stash.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-shelter.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/auto-worktree-resource-version.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-untracked-content.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +71 -1
- package/src/resources/extensions/gsd/tests/flat-phase-renderer.test.ts +25 -1
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/import-completion-lost-update.test.ts +196 -0
- package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +4 -6
- package/src/resources/extensions/gsd/tests/integration/auto-stash-merge.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/integration/feature-branch-lifecycle-integration.test.ts +2 -5
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/integration/merge-preserve-worktree-on-preteardown-error.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/integration/merge-strategy-regular.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +2 -4
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -15
- package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +91 -0
- package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +439 -0
- package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/parallel-worker-lock-contention.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +1 -4
- package/src/resources/extensions/gsd/tests/preferences.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +5 -0
- package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +3 -5
- package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/system-flat-phase-layout.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +196 -0
- package/src/resources/extensions/gsd/tests/uat-stuck-loop-orphaned-worktree.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/unit-registry.test.ts +26 -1
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-db-respawn-truncation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +1 -3
- package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +5 -2
- package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +1 -1
- package/src/resources/extensions/gsd/tool-contract.ts +29 -0
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +5 -0
- package/src/resources/extensions/gsd/tools/reopen-task.ts +13 -5
- package/src/resources/extensions/gsd/unit-registry.ts +16 -2
- package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +1 -1
- package/src/resources/extensions/gsd/validation-block-guard.ts +2 -1
- package/src/resources/extensions/gsd/workspace-git-preflight.ts +2 -2
- package/src/resources/extensions/gsd/worktree-command.ts +1 -1
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +19 -15
- package/src/resources/extensions/gsd/worktree-reentry.ts +2 -1
- package/src/resources/extensions/shared/interview-ui.ts +58 -11
- package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +32 -0
- /package/dist/web/standalone/.next/static/{L2UMqXELiDH3oz-7yguXp → gXoWec4UNqcR0tkuHwYCr}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{L2UMqXELiDH3oz-7yguXp → gXoWec4UNqcR0tkuHwYCr}/_ssgManifest.js +0 -0
|
@@ -14,7 +14,15 @@ import {
|
|
|
14
14
|
} from "./gsd-db.js";
|
|
15
15
|
import { MEMORIES_FTS_REBUILT_KEY } from "./db-memory-fts-schema.js";
|
|
16
16
|
import { isAfter, latestExplicitReopenAt } from "./milestone-reopen-events.js";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
buildTaskFileName,
|
|
19
|
+
gsdProjectionRoot,
|
|
20
|
+
gsdRoot,
|
|
21
|
+
resolveGsdPathContract,
|
|
22
|
+
resolveMilestoneFile,
|
|
23
|
+
resolveMilestonePath,
|
|
24
|
+
resolveSliceFile,
|
|
25
|
+
} from "./paths.js";
|
|
18
26
|
import { deriveState } from "./state.js";
|
|
19
27
|
import { isClosedStatus } from "./status-guards.js";
|
|
20
28
|
import { workflowEventLogPath } from "./workflow-event-ledger.js";
|
|
@@ -23,6 +31,7 @@ import { flushWorkflowProjections } from "./projection-flush.js";
|
|
|
23
31
|
import { parseRoadmapSlices } from "./roadmap-slices.js";
|
|
24
32
|
import { parsePlan } from "./parsers-legacy.js";
|
|
25
33
|
import { parseSummary } from "./files.js";
|
|
34
|
+
import { LAYOUT_SEGMENTS } from "./layout-policy.js";
|
|
26
35
|
|
|
27
36
|
const USER_AUTHORED_ARTIFACT_TYPES = new Set(["CONTEXT", "RESEARCH"]);
|
|
28
37
|
|
|
@@ -160,11 +169,11 @@ function isClearedByMilestoneShellProjectionFlush(
|
|
|
160
169
|
return issue.file === relativeFile(basePath, roadmapPath);
|
|
161
170
|
}
|
|
162
171
|
|
|
163
|
-
function artifactExistsOnDisk(basePath: string, artifactPath: string): boolean {
|
|
164
|
-
return resolveArtifactDiskPath(basePath, artifactPath) !== null;
|
|
172
|
+
function artifactExistsOnDisk(basePath: string, artifactPath: string, row?: ArtifactRow): boolean {
|
|
173
|
+
return resolveArtifactDiskPath(basePath, artifactPath, row) !== null;
|
|
165
174
|
}
|
|
166
175
|
|
|
167
|
-
function
|
|
176
|
+
function resolveLiteralArtifactDiskPath(basePath: string, artifactPath: string): string | null {
|
|
168
177
|
const relativeArtifactPath = artifactPathRelativeToGsd(artifactPath);
|
|
169
178
|
if (isAbsolute(relativeArtifactPath)) {
|
|
170
179
|
return existsSync(relativeArtifactPath) ? relativeArtifactPath : null;
|
|
@@ -176,6 +185,50 @@ function resolveArtifactDiskPath(basePath: string, artifactPath: string): string
|
|
|
176
185
|
return null;
|
|
177
186
|
}
|
|
178
187
|
|
|
188
|
+
function relativeToGsdRoot(basePath: string, diskPath: string): string | null {
|
|
189
|
+
for (const root of [gsdProjectionRoot(basePath), gsdRoot(basePath)]) {
|
|
190
|
+
if (!isPathInside(root, diskPath)) continue;
|
|
191
|
+
return relative(root, diskPath).split("\\").join("/");
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function isFlatPhaseDiskPath(basePath: string, diskPath: string): boolean {
|
|
197
|
+
return relativeToGsdRoot(basePath, diskPath)?.startsWith(`${LAYOUT_SEGMENTS.level1}/`) ?? false;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function hasFlatPhaseLayout(basePath: string): boolean {
|
|
201
|
+
return existsSync(join(gsdProjectionRoot(basePath), LAYOUT_SEGMENTS.level1));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function resolveFlatPhaseArtifactDiskPath(basePath: string, row: ArtifactRow): string | null {
|
|
205
|
+
if (!hasFlatPhaseLayout(basePath)) return null;
|
|
206
|
+
if (!row.milestone_id) return null;
|
|
207
|
+
const artifactType = normalizedArtifactType(row.artifact_type);
|
|
208
|
+
if (!artifactType) return null;
|
|
209
|
+
|
|
210
|
+
if (row.slice_id && row.task_id) {
|
|
211
|
+
const phaseDir = resolveMilestonePath(basePath, row.milestone_id);
|
|
212
|
+
if (!phaseDir || !isFlatPhaseDiskPath(basePath, phaseDir)) return null;
|
|
213
|
+
const candidate = join(phaseDir, buildTaskFileName(row.task_id, artifactType));
|
|
214
|
+
return existsSync(candidate) ? candidate : null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const candidate = row.slice_id
|
|
218
|
+
? resolveSliceFile(basePath, row.milestone_id, row.slice_id, artifactType)
|
|
219
|
+
: resolveMilestoneFile(basePath, row.milestone_id, artifactType);
|
|
220
|
+
|
|
221
|
+
return candidate && isFlatPhaseDiskPath(basePath, candidate) ? candidate : null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function resolveArtifactDiskPath(basePath: string, artifactPath: string, row?: ArtifactRow): string | null {
|
|
225
|
+
if (row && isMilestonesArtifactPath(row.path)) {
|
|
226
|
+
const flatPath = resolveFlatPhaseArtifactDiskPath(basePath, row);
|
|
227
|
+
if (flatPath) return flatPath;
|
|
228
|
+
}
|
|
229
|
+
return resolveLiteralArtifactDiskPath(basePath, artifactPath);
|
|
230
|
+
}
|
|
231
|
+
|
|
179
232
|
function taskExistsInPlan(basePath: string, milestoneId: string, sliceId: string, taskId: string): boolean {
|
|
180
233
|
const planPath = resolveSliceFile(basePath, milestoneId, sliceId, "PLAN");
|
|
181
234
|
if (!planPath || !existsSync(planPath)) return false;
|
|
@@ -228,7 +281,7 @@ function readTaskCompletionEvidenceFromSummary(
|
|
|
228
281
|
if (!row.task_status && Number(row.task_count) > 0 && !taskExistsInPlan(basePath, row.milestone_id, row.slice_id, row.task_id)) {
|
|
229
282
|
return null;
|
|
230
283
|
}
|
|
231
|
-
const diskPath = resolveArtifactDiskPath(basePath, row.path);
|
|
284
|
+
const diskPath = resolveArtifactDiskPath(basePath, row.path, { ...row, artifact_type: "SUMMARY" });
|
|
232
285
|
if (!diskPath) return null;
|
|
233
286
|
try {
|
|
234
287
|
const fullSummaryMd = readFileSync(diskPath, "utf-8");
|
|
@@ -262,6 +315,7 @@ function repairTaskArtifactDbStatusDivergence(
|
|
|
262
315
|
basePath: string,
|
|
263
316
|
row: {
|
|
264
317
|
path: string;
|
|
318
|
+
artifact_type?: string;
|
|
265
319
|
milestone_id: string;
|
|
266
320
|
slice_id: string | null;
|
|
267
321
|
task_id: string | null;
|
|
@@ -353,6 +407,49 @@ function hasPresentMilestonesReplacement(basePath: string, row: ArtifactRow, art
|
|
|
353
407
|
);
|
|
354
408
|
}
|
|
355
409
|
|
|
410
|
+
function hasPresentFlatPhaseReplacement(basePath: string, row: ArtifactRow, artifactRows: ArtifactRow[]): boolean {
|
|
411
|
+
if (resolveFlatPhaseArtifactDiskPath(basePath, row)) return true;
|
|
412
|
+
|
|
413
|
+
return artifactRows.some(
|
|
414
|
+
(other) =>
|
|
415
|
+
other.path !== row.path &&
|
|
416
|
+
!isMilestonesArtifactPath(other.path) &&
|
|
417
|
+
sameArtifactIdentity(row, other) &&
|
|
418
|
+
artifactExistsOnDisk(basePath, other.path, other),
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function hasNoFlatPhaseFileEquivalent(row: ArtifactRow): boolean {
|
|
423
|
+
const artifactType = normalizedArtifactType(row.artifact_type);
|
|
424
|
+
if (row.slice_id && row.task_id) {
|
|
425
|
+
return artifactType === "PLAN" || artifactType === "SUMMARY";
|
|
426
|
+
}
|
|
427
|
+
return Boolean(row.slice_id && !row.task_id && artifactType === "SUMMARY");
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function staleMilestonesArtifactRowFixable(basePath: string, row: ArtifactRow, artifactRows: ArtifactRow[]): boolean {
|
|
431
|
+
if (!isMilestonesArtifactPath(row.path)) return false;
|
|
432
|
+
if (!hasFlatPhaseLayout(basePath)) return false;
|
|
433
|
+
if (hasPresentFlatPhaseReplacement(basePath, row, artifactRows)) return true;
|
|
434
|
+
return hasNoFlatPhaseFileEquivalent(row) && !resolveLiteralArtifactDiskPath(basePath, row.path);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function stalePhasesArtifactRowFixable(basePath: string, row: ArtifactRow, artifactRows: ArtifactRow[]): boolean {
|
|
438
|
+
const issuePath = artifactPathRelativeToGsd(row.path);
|
|
439
|
+
return issuePath.startsWith(`${LAYOUT_SEGMENTS.level1}/`) && hasPresentMilestonesReplacement(basePath, row, artifactRows);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function staleArtifactRowFixable(basePath: string, row: ArtifactRow, artifactRows: ArtifactRow[]): boolean {
|
|
443
|
+
return staleMilestonesArtifactRowFixable(basePath, row, artifactRows) ||
|
|
444
|
+
stalePhasesArtifactRowFixable(basePath, row, artifactRows);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function staleArtifactPruneMessage(row: ArtifactRow): string {
|
|
448
|
+
return isMilestonesArtifactPath(row.path)
|
|
449
|
+
? `pruned stale legacy artifact row ${row.path}`
|
|
450
|
+
: `pruned stale flat-phase artifact row ${row.path}`;
|
|
451
|
+
}
|
|
452
|
+
|
|
356
453
|
export async function checkEngineHealth(
|
|
357
454
|
basePath: string,
|
|
358
455
|
issues: DoctorIssue[],
|
|
@@ -579,17 +676,18 @@ export async function checkEngineHealth(
|
|
|
579
676
|
.all() as ArtifactRow[];
|
|
580
677
|
|
|
581
678
|
for (const row of artifactRows) {
|
|
582
|
-
if (artifactExistsOnDisk(basePath, row.path)) continue;
|
|
583
679
|
const unitId = artifactUnitId(row);
|
|
584
680
|
const issuePath = artifactPathRelativeToGsd(row.path);
|
|
585
|
-
if (
|
|
681
|
+
if (artifactExistsOnDisk(basePath, row.path)) continue;
|
|
682
|
+
if (options?.repair && staleArtifactRowFixable(basePath, row, artifactRows)) {
|
|
586
683
|
// Route the write through the Single Writer owner (gsd-db.ts) instead
|
|
587
684
|
// of issuing raw DELETE SQL here — doctor is a read-only consumer and
|
|
588
685
|
// the single-writer invariant forbids write SQL outside the allowlist.
|
|
589
686
|
deleteArtifactByPath(row.path);
|
|
590
|
-
fixesApplied.push(
|
|
687
|
+
fixesApplied.push(staleArtifactPruneMessage(row));
|
|
591
688
|
continue;
|
|
592
689
|
}
|
|
690
|
+
if (artifactExistsOnDisk(basePath, row.path, row)) continue;
|
|
593
691
|
if (isUserAuthoredArtifactType(row.artifact_type)) {
|
|
594
692
|
const artifactType = normalizedArtifactType(row.artifact_type);
|
|
595
693
|
missingUserContentArtifacts.push({ path: issuePath, artifactType });
|
|
@@ -611,7 +709,7 @@ export async function checkEngineHealth(
|
|
|
611
709
|
unitId,
|
|
612
710
|
message: `Artifact ${issuePath} is recorded in the database as ${row.artifact_type || "UNKNOWN"} but no matching file exists on disk`,
|
|
613
711
|
file: issuePath,
|
|
614
|
-
fixable:
|
|
712
|
+
fixable: staleArtifactRowFixable(basePath, row, artifactRows),
|
|
615
713
|
});
|
|
616
714
|
}
|
|
617
715
|
} catch {
|
|
@@ -643,6 +741,7 @@ export async function checkEngineHealth(
|
|
|
643
741
|
)
|
|
644
742
|
.all() as Array<{
|
|
645
743
|
path: string;
|
|
744
|
+
artifact_type: string;
|
|
646
745
|
milestone_id: string;
|
|
647
746
|
slice_id: string | null;
|
|
648
747
|
task_id: string | null;
|
|
@@ -654,7 +753,7 @@ export async function checkEngineHealth(
|
|
|
654
753
|
|
|
655
754
|
const seen = new Set<string>();
|
|
656
755
|
for (const row of rows) {
|
|
657
|
-
if (!artifactExistsOnDisk(basePath, row.path)) continue;
|
|
756
|
+
if (!artifactExistsOnDisk(basePath, row.path, row)) continue;
|
|
658
757
|
const reopenAt = latestExplicitReopenAt(basePath, row.milestone_id);
|
|
659
758
|
if (!isAfter(row.imported_at, reopenAt)) continue;
|
|
660
759
|
const isSliceSummary = row.slice_id && !row.task_id && row.slice_status && !["complete", "done", "skipped", "closed"].includes(row.slice_status);
|
|
@@ -6,7 +6,7 @@ import { cpSync, existsSync, mkdirSync, readdirSync, renameSync, rmSync, statSyn
|
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
|
|
8
8
|
import { renderAllFromDb, renderRoadmapFromDb } from "./markdown-renderer.js";
|
|
9
|
-
import { getAllMilestones, getMilestoneSlices } from "./gsd-db.js";
|
|
9
|
+
import { deleteArtifactsByPathPrefix, getAllMilestones, getMilestoneSlices } from "./gsd-db.js";
|
|
10
10
|
import { migrateFromMarkdown } from "./md-importer.js";
|
|
11
11
|
import { countDbHierarchy } from "./migration-auto-check.js";
|
|
12
12
|
import { logWarning } from "./workflow-logger.js";
|
|
@@ -108,15 +108,35 @@ function rollbackPartialMigration(
|
|
|
108
108
|
`rollback: could not remove partial ${LAYOUT_SEGMENTS.level1}/: ${(removeErr as Error).message}`,
|
|
109
109
|
);
|
|
110
110
|
}
|
|
111
|
+
// Only the standalone .gsd-backups/migrate-<ts>/ copy is disposable once the
|
|
112
|
+
// restore succeeds. When resuming an interrupted migration, backupDir IS the
|
|
113
|
+
// preserved migrating tree (the sole surviving data source) and must never be
|
|
114
|
+
// removed here. Deleting the backup after a successful rollback keeps the gate
|
|
115
|
+
// from leaking one .gsd-backups/migrate-<ts>/ per session_start.
|
|
116
|
+
const isDisposableBackup = Boolean(backupDir) && backupDir !== migratingPath;
|
|
117
|
+
const cleanupBackup = (): void => {
|
|
118
|
+
if (!isDisposableBackup || !existsSync(backupDir)) return;
|
|
119
|
+
try {
|
|
120
|
+
removePathWithRetries(backupDir);
|
|
121
|
+
} catch (cleanupErr) {
|
|
122
|
+
logWarning(
|
|
123
|
+
"migration",
|
|
124
|
+
`rollback: could not clean backup ${backupDir}: ${(cleanupErr as Error).message}`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
111
129
|
// Restore milestones/ — prefer renaming the preserved migrating copy back.
|
|
112
130
|
const milestonesPath = join(basePath, ".gsd", "milestones");
|
|
113
131
|
try {
|
|
114
132
|
if (migratingPath && existsSync(migratingPath) && !existsSync(milestonesPath)) {
|
|
115
133
|
movePathWithCopyDeleteFallback(migratingPath, milestonesPath);
|
|
134
|
+
cleanupBackup();
|
|
116
135
|
return;
|
|
117
136
|
}
|
|
118
137
|
if (existsSync(backupDir)) {
|
|
119
138
|
cpSync(backupDir, milestonesPath, { recursive: true });
|
|
139
|
+
cleanupBackup();
|
|
120
140
|
}
|
|
121
141
|
if (migratingPath && existsSync(migratingPath)) {
|
|
122
142
|
removePathWithRetries(migratingPath);
|
|
@@ -195,7 +215,8 @@ export function pruneStaleFlatPhaseBackups(basePath: string): number {
|
|
|
195
215
|
* 2. Rename .gsd/milestones/ aside so path resolvers target phases/
|
|
196
216
|
* 3. Render flat-phase from the DB (which already has the data)
|
|
197
217
|
* 4. Verify counts match
|
|
198
|
-
* 5.
|
|
218
|
+
* 5. Prune legacy milestones/ artifact rows
|
|
219
|
+
* 6. Remove the renamed legacy tree
|
|
199
220
|
*
|
|
200
221
|
* Idempotent: if .gsd/milestones/ doesn't exist, returns immediately.
|
|
201
222
|
*/
|
|
@@ -313,7 +334,17 @@ export async function migrateToFlatPhase(basePath: string): Promise<void> {
|
|
|
313
334
|
throw new Error("flat-phase migration verification failed: no artifacts rendered");
|
|
314
335
|
}
|
|
315
336
|
|
|
316
|
-
// 6. Verified —
|
|
337
|
+
// 6. Verified — prune legacy artifact rows now that renderAllFromDb has
|
|
338
|
+
// re-inserted flat-phase rows for artifacts that still have files.
|
|
339
|
+
try {
|
|
340
|
+
deleteArtifactsByPathPrefix("milestones/");
|
|
341
|
+
} catch (err) {
|
|
342
|
+
logWarning("migration", `flat-phase migration could not prune legacy artifact rows: ${(err as Error).message}`);
|
|
343
|
+
rollbackPartialMigration(basePath, backupDir, migratingPath);
|
|
344
|
+
throw err;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// 7. Remove the renamed legacy tree (backup already on disk).
|
|
317
348
|
try {
|
|
318
349
|
removePathWithRetries(migratingPath);
|
|
319
350
|
} catch (err) {
|
|
@@ -30,7 +30,7 @@ import { loadPrompt } from "./prompt-loader.js";
|
|
|
30
30
|
import { gsdRoot } from "./paths.js";
|
|
31
31
|
import { isDbAvailable, getHierarchyCompletionCounts } from "./gsd-db.js";
|
|
32
32
|
import { formatDuration } from "../shared/format-utils.js";
|
|
33
|
-
import { getAutoWorktreePath } from "./auto-worktree.js";
|
|
33
|
+
import { getAutoWorktreePath } from "./auto-worktree-path-resolution.js";
|
|
34
34
|
import { clearGSDPreferencesCache, loadEffectiveGSDPreferences, loadGlobalGSDPreferences, getGlobalGSDPreferencesPath } from "./preferences.js";
|
|
35
35
|
import { showNextAction } from "../shared/tui.js";
|
|
36
36
|
import {
|
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
nativeCheckoutTheirs,
|
|
8
8
|
nativeRmForce,
|
|
9
9
|
} from "./native-git-bridge.js";
|
|
10
|
-
import { isSafeToAutoResolve } from "./auto-worktree.js";
|
|
10
|
+
import { isSafeToAutoResolve } from "./auto-worktree-conflict-auto-resolve.js";
|
|
11
11
|
|
|
12
|
-
export { isSafeToAutoResolve } from "./auto-worktree.js";
|
|
12
|
+
export { isSafeToAutoResolve } from "./auto-worktree-conflict-auto-resolve.js";
|
|
13
13
|
|
|
14
14
|
export interface AutoResolveSafePathsResult {
|
|
15
15
|
resolved: string[];
|
|
@@ -704,6 +704,59 @@ export function runGit(basePath: string, args: string[], options: { allowFailure
|
|
|
704
704
|
}
|
|
705
705
|
}
|
|
706
706
|
|
|
707
|
+
/**
|
|
708
|
+
* Arguments for creating a task/workflow branch.
|
|
709
|
+
*
|
|
710
|
+
* When the current branch is a leftover GSD task branch (workflow template,
|
|
711
|
+
* quick task, or slice), the new branch starts from `baseBranch` — the
|
|
712
|
+
* caller's resolved integration branch, normally `GitServiceImpl.getMainBranch()`
|
|
713
|
+
* so `main_branch` preference, milestone integration branch, and worktree base
|
|
714
|
+
* all take priority over repo-default detection. Branching from HEAD there
|
|
715
|
+
* silently stacks consecutive workflow branches on top of each other, so every
|
|
716
|
+
* new gsd/* branch (and its eventual PR) drags along the previous workflow's
|
|
717
|
+
* commits.
|
|
718
|
+
*
|
|
719
|
+
* On any other branch (a user's feature branch, a worktree base, the main
|
|
720
|
+
* branch itself) the legacy branch-from-HEAD behavior is kept: basing new work
|
|
721
|
+
* on the branch the user chose to stand on is intentional there.
|
|
722
|
+
*
|
|
723
|
+
* Falls back to branch-from-HEAD, with a logged warning, when the base branch
|
|
724
|
+
* has no local ref or the working tree is still dirty (a start point that
|
|
725
|
+
* changes the tree would abort the checkout or strand in-flight changes).
|
|
726
|
+
* The base is the local ref — deliberately no fetch, so branch creation stays
|
|
727
|
+
* offline-safe even if the local base is behind its remote.
|
|
728
|
+
*/
|
|
729
|
+
export function taskBranchArgs(basePath: string, branchName: string, currentBranch: string, baseBranch: string): string[] {
|
|
730
|
+
const args = ["checkout", "-b", branchName];
|
|
731
|
+
const onTaskBranch =
|
|
732
|
+
WORKFLOW_BRANCH_RE.test(currentBranch) ||
|
|
733
|
+
QUICK_BRANCH_RE.test(currentBranch) ||
|
|
734
|
+
SLICE_BRANCH_RE.test(currentBranch);
|
|
735
|
+
if (!onTaskBranch || !baseBranch || baseBranch === currentBranch) return args;
|
|
736
|
+
try {
|
|
737
|
+
if (!nativeBranchExists(basePath, baseBranch)) {
|
|
738
|
+
logWarning(
|
|
739
|
+
"engine",
|
|
740
|
+
`taskBranchArgs: base branch "${baseBranch}" has no local ref — branching ${branchName} from HEAD (${currentBranch})`,
|
|
741
|
+
{ file: "git-service.ts" },
|
|
742
|
+
);
|
|
743
|
+
return args;
|
|
744
|
+
}
|
|
745
|
+
if (nativeHasChanges(basePath)) {
|
|
746
|
+
logWarning(
|
|
747
|
+
"engine",
|
|
748
|
+
`taskBranchArgs: working tree still dirty after auto-commit — branching ${branchName} from HEAD (${currentBranch}) to preserve in-flight changes`,
|
|
749
|
+
{ file: "git-service.ts" },
|
|
750
|
+
);
|
|
751
|
+
return args;
|
|
752
|
+
}
|
|
753
|
+
} catch {
|
|
754
|
+
return args;
|
|
755
|
+
}
|
|
756
|
+
args.push(baseBranch);
|
|
757
|
+
return args;
|
|
758
|
+
}
|
|
759
|
+
|
|
707
760
|
// ─── Commit Type Inference ─────────────────────────────────────────────────
|
|
708
761
|
|
|
709
762
|
/**
|
|
@@ -454,6 +454,14 @@ export function insertTask(t: {
|
|
|
454
454
|
fullSummaryMd?: string;
|
|
455
455
|
sequence?: number;
|
|
456
456
|
planning?: Partial<TaskPlanningRecord>;
|
|
457
|
+
// #1222 (metadata half): when re-importing from markdown, the caller only
|
|
458
|
+
// knows the plan/status — not the execution prose that gsd_task_complete
|
|
459
|
+
// wrote to the DB. Setting this preserves the existing row's execution
|
|
460
|
+
// columns (and completed_at) whenever the incoming value is empty/default,
|
|
461
|
+
// so a re-import cannot silently blank a completed task's summary metadata
|
|
462
|
+
// or refresh its completion timestamp. Off by default: callers that own the
|
|
463
|
+
// execution metadata (gsd_task_complete, planning) keep overwrite semantics.
|
|
464
|
+
preserveCompletionMetadata?: boolean;
|
|
457
465
|
}): void {
|
|
458
466
|
if (!getDbOrNull()!) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
459
467
|
getDbOrNull()!.prepare(
|
|
@@ -473,17 +481,17 @@ export function insertTask(t: {
|
|
|
473
481
|
ON CONFLICT(milestone_id, slice_id, id) DO UPDATE SET
|
|
474
482
|
title = CASE WHEN NULLIF(:title, '') IS NOT NULL THEN :title ELSE tasks.title END,
|
|
475
483
|
status = :status,
|
|
476
|
-
one_liner = :one_liner,
|
|
477
|
-
narrative = :narrative,
|
|
478
|
-
verification_result = :verification_result,
|
|
479
|
-
duration = :duration,
|
|
480
|
-
completed_at = :completed_at,
|
|
481
|
-
blocker_discovered = :blocker_discovered,
|
|
482
|
-
deviations = :deviations,
|
|
483
|
-
known_issues = :known_issues,
|
|
484
|
-
key_files = :key_files,
|
|
485
|
-
key_decisions = :key_decisions,
|
|
486
|
-
full_summary_md = :full_summary_md,
|
|
484
|
+
one_liner = CASE WHEN :preserve_completion = 1 AND NULLIF(:one_liner, '') IS NULL THEN tasks.one_liner ELSE :one_liner END,
|
|
485
|
+
narrative = CASE WHEN :preserve_completion = 1 AND NULLIF(:narrative, '') IS NULL THEN tasks.narrative ELSE :narrative END,
|
|
486
|
+
verification_result = CASE WHEN :preserve_completion = 1 AND NULLIF(:verification_result, '') IS NULL THEN tasks.verification_result ELSE :verification_result END,
|
|
487
|
+
duration = CASE WHEN :preserve_completion = 1 AND NULLIF(:duration, '') IS NULL THEN tasks.duration ELSE :duration END,
|
|
488
|
+
completed_at = CASE WHEN :preserve_completion = 1 AND tasks.completed_at IS NOT NULL THEN tasks.completed_at ELSE :completed_at END,
|
|
489
|
+
blocker_discovered = CASE WHEN :preserve_completion = 1 AND :blocker_discovered = 0 THEN tasks.blocker_discovered ELSE :blocker_discovered END,
|
|
490
|
+
deviations = CASE WHEN :preserve_completion = 1 AND NULLIF(:deviations, '') IS NULL THEN tasks.deviations ELSE :deviations END,
|
|
491
|
+
known_issues = CASE WHEN :preserve_completion = 1 AND NULLIF(:known_issues, '') IS NULL THEN tasks.known_issues ELSE :known_issues END,
|
|
492
|
+
key_files = CASE WHEN :preserve_completion = 1 AND NULLIF(:key_files, '[]') IS NULL THEN tasks.key_files ELSE :key_files END,
|
|
493
|
+
key_decisions = CASE WHEN :preserve_completion = 1 AND NULLIF(:key_decisions, '[]') IS NULL THEN tasks.key_decisions ELSE :key_decisions END,
|
|
494
|
+
full_summary_md = CASE WHEN :preserve_completion = 1 AND NULLIF(:full_summary_md, '') IS NULL THEN tasks.full_summary_md ELSE :full_summary_md END,
|
|
487
495
|
description = CASE WHEN NULLIF(:description, '') IS NOT NULL THEN :description ELSE tasks.description END,
|
|
488
496
|
estimate = CASE WHEN NULLIF(:estimate, '') IS NOT NULL THEN :estimate ELSE tasks.estimate END,
|
|
489
497
|
files = CASE WHEN NULLIF(:files, '[]') IS NOT NULL THEN :files ELSE tasks.files END,
|
|
@@ -523,6 +531,7 @@ export function insertTask(t: {
|
|
|
523
531
|
":observability_impact": t.planning?.observabilityImpact ?? "",
|
|
524
532
|
":full_plan_md": t.planning?.fullPlanMd ?? "",
|
|
525
533
|
":sequence": t.sequence ?? 0,
|
|
534
|
+
":preserve_completion": t.preserveCompletionMetadata && (t.status === "complete" || t.status === "done") ? 1 : 0,
|
|
526
535
|
":target_repositories": JSON.stringify(t.planning?.targetRepositories ?? []),
|
|
527
536
|
":raw_target_repositories":
|
|
528
537
|
t.planning && "targetRepositories" in t.planning
|
|
@@ -1397,6 +1406,19 @@ export function deleteArtifactByPath(path: string): void {
|
|
|
1397
1406
|
getDbOrNull()!.prepare("DELETE FROM artifacts WHERE path = :path").run({ ":path": path });
|
|
1398
1407
|
}
|
|
1399
1408
|
|
|
1409
|
+
/** Delete artifact rows whose paths share a DB-relative prefix. */
|
|
1410
|
+
export function deleteArtifactsByPathPrefix(prefix: string): number {
|
|
1411
|
+
if (!getDbOrNull()!) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1412
|
+
return transaction(() => {
|
|
1413
|
+
const likePrefix = `${prefix}%`;
|
|
1414
|
+
const countRow = getDbOrNull()!.prepare(
|
|
1415
|
+
"SELECT COUNT(*) AS count FROM artifacts WHERE path LIKE :prefix",
|
|
1416
|
+
).get({ ":prefix": likePrefix });
|
|
1417
|
+
getDbOrNull()!.prepare("DELETE FROM artifacts WHERE path LIKE :prefix").run({ ":prefix": likePrefix });
|
|
1418
|
+
return Number(countRow?.["count"] ?? 0);
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1400
1422
|
/**
|
|
1401
1423
|
* Drop hierarchy rows in dependency order inside a transaction. Used by
|
|
1402
1424
|
* `gsd recover --confirm` to rebuild engine state from markdown.
|
|
@@ -50,8 +50,8 @@ import { readSessionLockData, isSessionLockProcessAlive } from "./session-lock.j
|
|
|
50
50
|
import { nativeAddAll, nativeCommit, nativeHasCommittedHead, nativeIsRepo, nativeInit } from "./native-git-bridge.js";
|
|
51
51
|
import { isInheritedRepo } from "./repo-identity.js";
|
|
52
52
|
import { ensureGitignore, ensurePreferences, untrackRuntimeFiles } from "./gitignore.js";
|
|
53
|
-
import { getIsolationMode, loadEffectiveGSDPreferences } from "./preferences.js";
|
|
54
|
-
import { getAutoWorktreePath } from "./auto-worktree.js";
|
|
53
|
+
import { getIsolationMode, loadEffectiveGSDPreferences, renderLanguageDirectiveForPrompt } from "./preferences.js";
|
|
54
|
+
import { getAutoWorktreePath } from "./auto-worktree-path-resolution.js";
|
|
55
55
|
import { resolveUokFlags } from "./uok/flags.js";
|
|
56
56
|
import { ensurePlanV2Graph, isMissingFinalizedContextResult } from "./uok/plan-v2.js";
|
|
57
57
|
import { detectProjectState, hasGsdBootstrapArtifacts } from "./detection.js";
|
|
@@ -94,7 +94,7 @@ import {
|
|
|
94
94
|
import { probeCoversRequiredWorkflowTools } from "./tool-surface-readiness.js";
|
|
95
95
|
import { getRequiredWorkflowToolsForUnit } from "./unit-tool-contracts.js";
|
|
96
96
|
import { isWorkflowToolSurfaceName } from "./workflow-tool-surface.js";
|
|
97
|
-
import {
|
|
97
|
+
import { getUnitWorkflowDispatchReadinessErrorForModel } from "./tool-contract.js";
|
|
98
98
|
import {
|
|
99
99
|
runPreparation,
|
|
100
100
|
formatCodebaseBrief,
|
|
@@ -613,7 +613,7 @@ interface DispatchWorkflowOptions {
|
|
|
613
613
|
deps?: {
|
|
614
614
|
loadPreferences?: typeof loadEffectiveGSDPreferences;
|
|
615
615
|
selectModel?: typeof selectAndApplyModel;
|
|
616
|
-
getDispatchReadinessError?: typeof
|
|
616
|
+
getDispatchReadinessError?: typeof getUnitWorkflowDispatchReadinessErrorForModel;
|
|
617
617
|
};
|
|
618
618
|
}
|
|
619
619
|
|
|
@@ -730,7 +730,7 @@ async function dispatchWorkflow(
|
|
|
730
730
|
const loadPreferences = resolvedOptions.deps?.loadPreferences ?? loadEffectiveGSDPreferences;
|
|
731
731
|
const selectModel = resolvedOptions.deps?.selectModel ?? selectAndApplyModel;
|
|
732
732
|
const getDispatchReadinessError = resolvedOptions.deps?.getDispatchReadinessError
|
|
733
|
-
??
|
|
733
|
+
?? getUnitWorkflowDispatchReadinessErrorForModel;
|
|
734
734
|
|
|
735
735
|
// Route through the dynamic routing pipeline (complexity classification,
|
|
736
736
|
// tier downgrade, fallback chains) — same path as auto-mode dispatches (#2958).
|
|
@@ -750,16 +750,12 @@ async function dispatchWorkflow(
|
|
|
750
750
|
}
|
|
751
751
|
|
|
752
752
|
const compatibilityError = getDispatchReadinessError({
|
|
753
|
-
|
|
753
|
+
model: result.appliedModel,
|
|
754
|
+
fallbackModel: ctx.model,
|
|
755
|
+
getProviderAuthMode: (provider) => ctx.modelRegistry.getProviderAuthMode(provider),
|
|
754
756
|
projectRoot,
|
|
755
757
|
surface: "guided flow",
|
|
756
758
|
unitType,
|
|
757
|
-
authMode: result.appliedModel?.provider
|
|
758
|
-
? ctx.modelRegistry.getProviderAuthMode(result.appliedModel.provider)
|
|
759
|
-
: ctx.model?.provider
|
|
760
|
-
? ctx.modelRegistry.getProviderAuthMode(ctx.model.provider)
|
|
761
|
-
: undefined,
|
|
762
|
-
baseUrl: result.appliedModel?.baseUrl ?? ctx.model?.baseUrl,
|
|
763
759
|
// Guided flow starts the MCP workflow server as part of dispatch, so the
|
|
764
760
|
// parent session's activeTools doesn't include MCP tools yet. The MCP
|
|
765
761
|
// launch config check (detectWorkflowMcpLaunchConfig) is the right gate
|
|
@@ -925,7 +921,7 @@ function resolveAvailableModel<T extends { id: string; provider: string }>(
|
|
|
925
921
|
* Build the discuss-and-plan prompt for a new milestone.
|
|
926
922
|
* Used by all three "new milestone" paths (first ever, no active, all complete).
|
|
927
923
|
*/
|
|
928
|
-
function buildDiscussPrompt(nextId: string, preamble: string,
|
|
924
|
+
function buildDiscussPrompt(nextId: string, preamble: string, basePath: string, pi: ExtensionAPI, ctx: ExtensionCommandContext, preparationContext?: string): string {
|
|
929
925
|
const milestoneRel = `.gsd/milestones/${nextId}`;
|
|
930
926
|
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
931
927
|
const inlinedTemplates = [
|
|
@@ -935,7 +931,7 @@ function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string,
|
|
|
935
931
|
inlineTemplate("roadmap", "Roadmap"),
|
|
936
932
|
inlineTemplate("decisions", "Decisions"),
|
|
937
933
|
].join("\n\n---\n\n");
|
|
938
|
-
return loadPrompt("discuss", {
|
|
934
|
+
return prependLanguageDirective(basePath, loadPrompt("discuss", {
|
|
939
935
|
milestoneId: nextId,
|
|
940
936
|
preamble,
|
|
941
937
|
preparationContext: preparationContext ?? "",
|
|
@@ -945,14 +941,26 @@ function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string,
|
|
|
945
941
|
inlinedTemplates,
|
|
946
942
|
commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): context, requirements, and roadmap`),
|
|
947
943
|
multiMilestoneCommitInstruction: buildDocsCommitInstruction("docs: project plan — N milestones"),
|
|
948
|
-
});
|
|
944
|
+
}));
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* Prepend the configured response-language directive to a dispatched discuss
|
|
949
|
+
* prompt so the discuss/QA conversation is held in the language set in
|
|
950
|
+
* PREFERENCES.md (#1210). No-op when no language is configured.
|
|
951
|
+
*/
|
|
952
|
+
function prependLanguageDirective(basePath: string, prompt: string): string {
|
|
953
|
+
const directive = renderLanguageDirectiveForPrompt(
|
|
954
|
+
loadEffectiveGSDPreferences(basePath)?.preferences,
|
|
955
|
+
);
|
|
956
|
+
return directive ? `${directive}\n\n${prompt}` : prompt;
|
|
949
957
|
}
|
|
950
958
|
|
|
951
959
|
/**
|
|
952
960
|
* Build the discuss prompt for headless milestone creation.
|
|
953
961
|
* Uses the discuss-headless prompt template with seed context injected.
|
|
954
962
|
*/
|
|
955
|
-
function buildHeadlessDiscussPrompt(nextId: string, seedContext: string,
|
|
963
|
+
function buildHeadlessDiscussPrompt(nextId: string, seedContext: string, basePath: string): string {
|
|
956
964
|
const milestoneRel = `.gsd/milestones/${nextId}`;
|
|
957
965
|
const inlinedTemplates = [
|
|
958
966
|
inlineTemplate("project", "Project"),
|
|
@@ -961,7 +969,7 @@ function buildHeadlessDiscussPrompt(nextId: string, seedContext: string, _basePa
|
|
|
961
969
|
inlineTemplate("roadmap", "Roadmap"),
|
|
962
970
|
inlineTemplate("decisions", "Decisions"),
|
|
963
971
|
].join("\n\n---\n\n");
|
|
964
|
-
return loadPrompt("discuss-headless", {
|
|
972
|
+
return prependLanguageDirective(basePath, loadPrompt("discuss-headless", {
|
|
965
973
|
milestoneId: nextId,
|
|
966
974
|
seedContext,
|
|
967
975
|
contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
|
|
@@ -969,7 +977,7 @@ function buildHeadlessDiscussPrompt(nextId: string, seedContext: string, _basePa
|
|
|
969
977
|
inlinedTemplates,
|
|
970
978
|
commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): context, requirements, and roadmap`),
|
|
971
979
|
multiMilestoneCommitInstruction: buildDocsCommitInstruction("docs: project plan — N milestones"),
|
|
972
|
-
});
|
|
980
|
+
}));
|
|
973
981
|
}
|
|
974
982
|
|
|
975
983
|
/**
|
|
@@ -1299,9 +1299,21 @@ export async function renderAssessmentFromDb(
|
|
|
1299
1299
|
sliceId: string,
|
|
1300
1300
|
assessmentData: AssessmentData,
|
|
1301
1301
|
): Promise<{ assessmentPath: string; content: string }> {
|
|
1302
|
-
// Flat-phase: assessment file lives in the phase dir
|
|
1303
|
-
|
|
1304
|
-
|
|
1302
|
+
// Flat-phase: the assessment file lives in the phase dir, NOT in a slices/SID/
|
|
1303
|
+
// subdir. resolveSlicePath() detects a slices/SID/ dir unconditionally, so a
|
|
1304
|
+
// stray slices/SID/ created by an earlier planning step would send this write
|
|
1305
|
+
// to a hybrid path (wrong dir, flat filename) that verification cannot find.
|
|
1306
|
+
// Guard with the same legacy-base check relSlicePath() uses so flat-phase
|
|
1307
|
+
// milestones always target the phase dir.
|
|
1308
|
+
const mDir = resolveMilestonePath(basePath, milestoneId);
|
|
1309
|
+
const legacyBase = legacyMilestonesDir(basePath);
|
|
1310
|
+
const isLegacyLayout = mDir
|
|
1311
|
+
? mDir.startsWith(legacyBase + "/") || mDir.startsWith(legacyBase + "\\")
|
|
1312
|
+
: false;
|
|
1313
|
+
const fallbackDir = join(milestonesDir(basePath), canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title));
|
|
1314
|
+
const slicePath = isLegacyLayout
|
|
1315
|
+
? (resolveSlicePath(basePath, milestoneId, sliceId) ?? fallbackDir)
|
|
1316
|
+
: (mDir ?? fallbackDir);
|
|
1305
1317
|
const phaseNum = milestoneIdToPhaseNum(milestoneId);
|
|
1306
1318
|
const planNum = sliceIdToPlanNum(sliceId);
|
|
1307
1319
|
const absPath = join(slicePath, planFileName(phaseNum, planNum, "ASSESSMENT"));
|