@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
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// Exports: parseDecisionsTable, parseRequirementsSections, migrateFromMarkdown
|
|
6
6
|
|
|
7
7
|
import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';
|
|
8
|
-
import { join, relative, basename } from 'node:path';
|
|
8
|
+
import { join, relative, basename, dirname } from 'node:path';
|
|
9
9
|
import type { Decision, Requirement } from './types.js';
|
|
10
10
|
import {
|
|
11
11
|
upsertDecision,
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
resolveGsdRootFile,
|
|
25
25
|
resolveMilestoneFile,
|
|
26
26
|
resolveSliceFile,
|
|
27
|
+
resolveSlicePath,
|
|
27
28
|
resolveTasksDir,
|
|
28
29
|
legacyMilestonesDir,
|
|
29
30
|
gsdRoot,
|
|
@@ -33,7 +34,7 @@ import {
|
|
|
33
34
|
import { findMilestoneIds } from './guided-flow.js';
|
|
34
35
|
import { milestoneIdToPhaseNum } from './layout-policy.js';
|
|
35
36
|
import { parseRoadmap, parsePlan } from './parsers-legacy.js';
|
|
36
|
-
import { parseContextDependsOn } from './files.js';
|
|
37
|
+
import { parseContextDependsOn, parseSummary } from './files.js';
|
|
37
38
|
import { logWarning } from './workflow-logger.js';
|
|
38
39
|
|
|
39
40
|
// ─── DECISIONS.md Parser ───────────────────────────────────────────────────
|
|
@@ -816,23 +817,68 @@ export function migrateHierarchyToDb(basePath: string): {
|
|
|
816
817
|
// Per K002: use 'complete' not 'done'
|
|
817
818
|
let taskStatus: string = taskEntry.done ? 'complete' : 'pending';
|
|
818
819
|
|
|
819
|
-
//
|
|
820
|
-
//
|
|
821
|
-
//
|
|
822
|
-
//
|
|
823
|
-
//
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
820
|
+
// Resolve the per-task SUMMARY.md. Legacy layout keeps summaries under
|
|
821
|
+
// a tasks/ subdir; flat-phase writes TID-SUMMARY.md directly in the
|
|
822
|
+
// phase dir (execute-task's gsd_task_complete writes it there). A tasks/
|
|
823
|
+
// subdir may still exist in flat-phase for auxiliary artifacts — its
|
|
824
|
+
// mere existence must NOT redirect summary resolution into tasks/ (#1208).
|
|
825
|
+
const slicePath = resolveSlicePath(basePath, milestoneId, sliceEntry.id);
|
|
826
|
+
let taskSummaryFile: string | null = null;
|
|
827
|
+
let isLegacySlice = false;
|
|
828
|
+
// Legacy layout keeps per-task summaries under a tasks/ subdir. Its
|
|
829
|
+
// presence — not merely "is this a legacy slice" — is what makes a
|
|
830
|
+
// missing summary a downgrade signal. resolveTasksDir returns null when
|
|
831
|
+
// no such dir exists, so a legacy slice whose checkbox is the only
|
|
832
|
+
// completion signal (no tasks/ dir at all) is left complete on import.
|
|
833
|
+
let legacyTasksDir: string | null = null;
|
|
834
|
+
if (slicePath) {
|
|
835
|
+
isLegacySlice = basename(dirname(slicePath)) === 'slices';
|
|
836
|
+
legacyTasksDir = isLegacySlice
|
|
837
|
+
? resolveTasksDir(basePath, milestoneId, sliceEntry.id)
|
|
838
|
+
: null;
|
|
839
|
+
const summaryDir = legacyTasksDir ?? slicePath;
|
|
840
|
+
taskSummaryFile = join(summaryDir, `${taskEntry.id}-SUMMARY.md`);
|
|
841
|
+
}
|
|
842
|
+
const hasTaskSummary = taskSummaryFile !== null && existsSync(taskSummaryFile);
|
|
843
|
+
|
|
844
|
+
// Pre-migration consistency (legacy tasks/ layout only): if a task is
|
|
845
|
+
// marked done but its tasks/ dir has no per-task SUMMARY.md, import it
|
|
846
|
+
// as pending so it gets re-executed rather than silently entering the DB
|
|
847
|
+
// as complete. A legacy slice with no tasks/ dir (checkbox-only
|
|
848
|
+
// completion) and flat-phase layout are both left as-is here.
|
|
849
|
+
if (taskStatus === 'complete' && legacyTasksDir !== null && !hasTaskSummary) {
|
|
850
|
+
taskStatus = 'pending';
|
|
851
|
+
process.stderr.write(
|
|
852
|
+
`gsd-migrate: ${milestoneId}/${sliceEntry.id}/${taskEntry.id} marked done but missing summary — importing as pending\n`,
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// Lost-update guard (#1222): a per-task SUMMARY.md is the durable
|
|
857
|
+
// completion attestation written by gsd_task_complete. When the plan
|
|
858
|
+
// checkbox is stale-unchecked but the SUMMARY exists on disk, a
|
|
859
|
+
// re-import must NOT downgrade the completed task back to pending — that
|
|
860
|
+
// silent revert is the lost-update that hard-stopped auto-mode after the
|
|
861
|
+
// first execute-task. The surviving SUMMARY wins over a stale checkbox.
|
|
862
|
+
// reopen-task removes the SUMMARY, so this never re-completes an
|
|
863
|
+
// intentionally reopened task.
|
|
864
|
+
// Flat-phase summaries are phase-scoped (T01-SUMMARY.md); when a milestone
|
|
865
|
+
// has multiple slices, require the summary parent frontmatter to match so
|
|
866
|
+
// one slice's completion does not attest a sibling's same-named task.
|
|
867
|
+
let summaryAttestsCompletion = hasTaskSummary;
|
|
868
|
+
if (summaryAttestsCompletion && !isLegacySlice && roadmap.slices.length > 1 && taskSummaryFile) {
|
|
869
|
+
try {
|
|
870
|
+
const summaryContent = readFileSync(taskSummaryFile, 'utf-8');
|
|
871
|
+
summaryAttestsCompletion = parseSummary(summaryContent).frontmatter.parent === sliceEntry.id;
|
|
872
|
+
} catch {
|
|
873
|
+
summaryAttestsCompletion = false;
|
|
834
874
|
}
|
|
835
875
|
}
|
|
876
|
+
if (taskStatus === 'pending' && summaryAttestsCompletion) {
|
|
877
|
+
taskStatus = 'complete';
|
|
878
|
+
process.stderr.write(
|
|
879
|
+
`gsd-migrate: ${milestoneId}/${sliceEntry.id}/${taskEntry.id} unchecked in plan but SUMMARY.md present — importing as complete (#1222)\n`,
|
|
880
|
+
);
|
|
881
|
+
}
|
|
836
882
|
|
|
837
883
|
insertTask({
|
|
838
884
|
id: taskEntry.id,
|
|
@@ -844,6 +890,12 @@ export function migrateHierarchyToDb(basePath: string): {
|
|
|
844
890
|
files: taskEntry.files ?? [],
|
|
845
891
|
verify: taskEntry.verify ?? '',
|
|
846
892
|
},
|
|
893
|
+
// #1222 (metadata half): re-import derives status from the plan/summary
|
|
894
|
+
// but has none of the execution prose gsd_task_complete wrote to the DB.
|
|
895
|
+
// Preserve the existing row's execution columns and completed_at so a
|
|
896
|
+
// re-import cannot blank a completed task's summary metadata or refresh
|
|
897
|
+
// its completion timestamp — the DB row is strictly richer than the plan.
|
|
898
|
+
preserveCompletionMetadata: true,
|
|
847
899
|
});
|
|
848
900
|
counts.tasks++;
|
|
849
901
|
}
|
|
@@ -857,12 +909,31 @@ export function migrateHierarchyToDb(basePath: string): {
|
|
|
857
909
|
if (!sliceEntry.done) {
|
|
858
910
|
const sliceSummaryPath = resolveSliceFile(basePath, milestoneId, sliceEntry.id, 'SUMMARY');
|
|
859
911
|
const hasSliceSummary = sliceSummaryPath !== null && existsSync(sliceSummaryPath);
|
|
912
|
+
const slicePathForTasks = resolveSlicePath(basePath, milestoneId, sliceEntry.id);
|
|
913
|
+
const isLegacySliceLayout = slicePathForTasks !== null
|
|
914
|
+
&& basename(dirname(slicePathForTasks)) === 'slices';
|
|
915
|
+
const taskSummaryDir = slicePathForTasks
|
|
916
|
+
? (isLegacySliceLayout
|
|
917
|
+
? (resolveTasksDir(basePath, milestoneId, sliceEntry.id) ?? slicePathForTasks)
|
|
918
|
+
: slicePathForTasks)
|
|
919
|
+
: null;
|
|
860
920
|
const allTasksDone = plan.tasks.length > 0 && plan.tasks.every(t => {
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
921
|
+
const taskSummaryFile = taskSummaryDir !== null
|
|
922
|
+
? join(taskSummaryDir, `${t.id}-SUMMARY.md`)
|
|
923
|
+
: null;
|
|
924
|
+
const hasTaskSummary = taskSummaryFile !== null && existsSync(taskSummaryFile);
|
|
925
|
+
let summaryAttestsCompletion = hasTaskSummary;
|
|
926
|
+
if (summaryAttestsCompletion && !isLegacySliceLayout && roadmap.slices.length > 1 && taskSummaryFile) {
|
|
927
|
+
try {
|
|
928
|
+
const summaryContent = readFileSync(taskSummaryFile, 'utf-8');
|
|
929
|
+
summaryAttestsCompletion = parseSummary(summaryContent).frontmatter.parent === sliceEntry.id;
|
|
930
|
+
} catch {
|
|
931
|
+
summaryAttestsCompletion = false;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
// Match per-task import rules above: SUMMARY attestation wins over a stale
|
|
935
|
+
// checkbox (#1222); flat-phase checkboxes are authoritative without summary.
|
|
936
|
+
return summaryAttestsCompletion || (t.done && !isLegacySliceLayout);
|
|
866
937
|
});
|
|
867
938
|
if (allTasksDone && hasSliceSummary) {
|
|
868
939
|
if (_getAdapter()) {
|
|
@@ -102,13 +102,41 @@ function replaceSetPrefix(values: Set<string>, from: string, to: string): void {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Rewrite one milestone identity across EVERY markdown identity set. The
|
|
107
|
+
* roadmapless subset is a view of `milestones`, so it must speak the same
|
|
108
|
+
* aligned vocabulary; otherwise the discussion-phase exclusion below keys off
|
|
109
|
+
* the stale bare id, fails its `dbScan.milestones.has(...)` membership check,
|
|
110
|
+
* and decrements the milestone count even though the aligned identity remains
|
|
111
|
+
* in the set, reporting false drift for a milestone that is actually in sync.
|
|
112
|
+
* Centralising the set list here keeps every alignment path from silently
|
|
113
|
+
* missing a set (the defect that regressed the suffixed roadmapless case).
|
|
114
|
+
*/
|
|
115
|
+
function realignMilestonePrefix(markdownScan: HierarchyScan, from: string, to: string): void {
|
|
116
|
+
replaceSetPrefix(markdownScan.milestones, from, to);
|
|
117
|
+
replaceSetPrefix(markdownScan.slices, from, to);
|
|
118
|
+
replaceSetPrefix(markdownScan.tasks, from, to);
|
|
119
|
+
replaceSetPrefix(markdownScan.milestonesWithoutRoadmap, from, to);
|
|
120
|
+
}
|
|
121
|
+
|
|
105
122
|
function alignNumericMarkdownIdsWithDb(markdownScan: HierarchyScan, dbScan: HierarchyScan): void {
|
|
106
123
|
for (const dbId of dbScan.milestones) {
|
|
107
124
|
const paddedId = paddedMilestoneId(dbId);
|
|
108
125
|
if (!paddedId || dbScan.milestones.has(paddedId) || !markdownScan.milestones.has(paddedId)) continue;
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
126
|
+
realignMilestonePrefix(markdownScan, paddedId, dbId);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function bareMilestoneId(id: string): string | null {
|
|
131
|
+
const match = id.match(/^(M\d{3})-[a-z0-9]{6}$/);
|
|
132
|
+
return match?.[1] ?? null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function alignBareMarkdownIdsWithSuffixedDb(markdownScan: HierarchyScan, dbScan: HierarchyScan): void {
|
|
136
|
+
for (const dbId of dbScan.milestones) {
|
|
137
|
+
const bareId = bareMilestoneId(dbId);
|
|
138
|
+
if (!bareId || dbScan.milestones.has(bareId) || !markdownScan.milestones.has(bareId)) continue;
|
|
139
|
+
realignMilestonePrefix(markdownScan, bareId, dbId);
|
|
112
140
|
}
|
|
113
141
|
}
|
|
114
142
|
|
|
@@ -208,6 +236,7 @@ export async function checkMarkdownHierarchyAgainstDb(
|
|
|
208
236
|
const dbScan = scanDbHierarchy();
|
|
209
237
|
const beforeDb = dbScan.counts;
|
|
210
238
|
alignNumericMarkdownIdsWithDb(markdownScan, dbScan);
|
|
239
|
+
alignBareMarkdownIdsWithSuffixedDb(markdownScan, dbScan);
|
|
211
240
|
|
|
212
241
|
// Discussion-phase scratch: a milestone dir with no ROADMAP and no DB row is
|
|
213
242
|
// a pre-registration discussion artifact (CONTEXT/CONTEXT-DRAFT only — the
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Project/App: gsd-pi
|
|
2
2
|
// File Purpose: Process-level transaction wrapper for milestone merge closeout.
|
|
3
3
|
|
|
4
|
+
import { mergeMilestoneToMain } from "./auto-worktree-merge.js";
|
|
5
|
+
|
|
4
6
|
export interface MilestoneMergeTransactionResult {
|
|
5
7
|
pushed: boolean;
|
|
6
8
|
codeFilesChanged: boolean;
|
|
@@ -45,3 +47,16 @@ export function createMilestoneMergeTransaction(
|
|
|
45
47
|
);
|
|
46
48
|
};
|
|
47
49
|
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Production merge transaction adapter.
|
|
53
|
+
*
|
|
54
|
+
* This is the only production construction point that knows the legacy
|
|
55
|
+
* `auto-worktree.ts` merge primitive. Lifecycle callers depend on the
|
|
56
|
+
* transaction runner interface, not the legacy implementation module.
|
|
57
|
+
*/
|
|
58
|
+
export function createDefaultMilestoneMergeTransaction(
|
|
59
|
+
mergeMilestone: MilestoneMergeTransactionRunner = mergeMilestoneToMain,
|
|
60
|
+
): MilestoneMergeTransactionRunner {
|
|
61
|
+
return createMilestoneMergeTransaction(mergeMilestone);
|
|
62
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Project/App: gsd-pi
|
|
2
2
|
// File Purpose: Milestone closeout settlement state across DB proof, artifacts, merge, and cleanup.
|
|
3
3
|
|
|
4
|
-
import { isInAutoWorktree } from "./auto-worktree.js";
|
|
4
|
+
import { isInAutoWorktree } from "./auto-worktree-entry.js";
|
|
5
5
|
import {
|
|
6
6
|
formatCloseoutProofBlock,
|
|
7
7
|
proveMilestoneCloseout,
|
|
@@ -10,7 +10,7 @@ import { join } from "node:path";
|
|
|
10
10
|
import { spawnSync } from "node:child_process";
|
|
11
11
|
import { resolveGsdPathContract } from "./paths.js";
|
|
12
12
|
import { worktreePathFor, worktreesDirs } from "./worktree-placement.js";
|
|
13
|
-
import { getAutoWorktreePath } from "./auto-worktree.js";
|
|
13
|
+
import { getAutoWorktreePath } from "./auto-worktree-path-resolution.js";
|
|
14
14
|
import { buildWorktreeLifecycleDeps } from "./auto.js";
|
|
15
15
|
import {
|
|
16
16
|
mergeMilestoneStandalone,
|
|
@@ -21,7 +21,9 @@ import { join, dirname } from "node:path";
|
|
|
21
21
|
import { fileURLToPath } from "node:url";
|
|
22
22
|
import { gsdRoot } from "./paths.js";
|
|
23
23
|
import { createWorktree, worktreePath } from "./worktree-manager.js";
|
|
24
|
-
import { autoWorktreeBranch, fastForwardReusedMilestoneBranchIfSafe
|
|
24
|
+
import { autoWorktreeBranch, fastForwardReusedMilestoneBranchIfSafe } from "./auto-worktree-branch-lifecycle.js";
|
|
25
|
+
import { syncGsdStateToWorktreeByScope } from "./auto-worktree-sync.js";
|
|
26
|
+
import { runWorktreePostCreateHook } from "./worktree-post-create-hook.js";
|
|
25
27
|
import { nativeBranchExists } from "./native-git-bridge.js";
|
|
26
28
|
import { readIntegrationBranch } from "./git-service.js";
|
|
27
29
|
import { createWorkspace, scopeMilestone } from "./workspace.js";
|
|
@@ -696,7 +696,16 @@ function pickPreferredPhaseDir(phasesDir: string, matches: string[]): string {
|
|
|
696
696
|
return best;
|
|
697
697
|
}
|
|
698
698
|
|
|
699
|
-
function
|
|
699
|
+
function slugLooksLikeTeamSuffixProjection(slugPart: string): boolean {
|
|
700
|
+
return /^[a-z0-9]{6}(-|$)/.test(slugPart);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function phaseDirMatchesMilestoneId(
|
|
704
|
+
dirName: string,
|
|
705
|
+
milestoneId: string,
|
|
706
|
+
phaseNum: number,
|
|
707
|
+
allowTeamSuffixSlugs = false,
|
|
708
|
+
): boolean {
|
|
700
709
|
const numMatch = dirName.match(/^(\d+)-(.*)$/);
|
|
701
710
|
if (!numMatch || parseInt(numMatch[1]!, 10) !== phaseNum) return false;
|
|
702
711
|
const slugPart = numMatch[2]!;
|
|
@@ -704,8 +713,8 @@ function phaseDirMatchesMilestoneId(dirName: string, milestoneId: string, phaseN
|
|
|
704
713
|
if (suffix) {
|
|
705
714
|
return slugPart === suffix || slugPart.startsWith(`${suffix}-`);
|
|
706
715
|
}
|
|
707
|
-
|
|
708
|
-
return
|
|
716
|
+
if (slugLooksLikeTeamSuffixProjection(slugPart) && !allowTeamSuffixSlugs) return false;
|
|
717
|
+
return true;
|
|
709
718
|
}
|
|
710
719
|
|
|
711
720
|
function resolvePhaseDir(basePath: string, milestoneId: string): string | null {
|
|
@@ -726,6 +735,15 @@ function resolvePhaseDir(basePath: string, milestoneId: string): string | null {
|
|
|
726
735
|
matches.push(entry.name);
|
|
727
736
|
}
|
|
728
737
|
}
|
|
738
|
+
// Bare milestone ids may only have a suffixed team-mode projection on disk.
|
|
739
|
+
if (matches.length === 0 && !milestoneIdUniqueSuffix(milestoneId)) {
|
|
740
|
+
for (const entry of readdirSync(phasesDir, { withFileTypes: true })) {
|
|
741
|
+
if (!entry.isDirectory()) continue;
|
|
742
|
+
if (phaseDirMatchesMilestoneId(entry.name, milestoneId, phaseNum, true)) {
|
|
743
|
+
matches.push(entry.name);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
729
747
|
} catch {
|
|
730
748
|
// unreadable — fall through
|
|
731
749
|
}
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
* to absolute filesystem paths, plus skill discovery and staleness config.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
8
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
10
|
import { isAbsolute, join } from "node:path";
|
|
11
|
+
import { getSkillDirectories } from "@gsd/pi-coding-agent";
|
|
11
12
|
import { gsdHome } from "./gsd-home.js";
|
|
12
|
-
import { statSync } from "node:fs";
|
|
13
13
|
|
|
14
14
|
import type {
|
|
15
15
|
GSDPreferences,
|
|
@@ -25,26 +25,20 @@ export type { GSDSkillRule, SkillDiscoveryMode, SkillResolution, SkillResolution
|
|
|
25
25
|
/**
|
|
26
26
|
* Known skill directories for **preference reference resolution** (bare skill names).
|
|
27
27
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* 4. ~/.claude/skills/ and .claude/skills/ (Claude Code compatibility)
|
|
28
|
+
* Sourced from the shared taxonomy (`@gsd/pi-coding-agent` `getSkillDirectories`)
|
|
29
|
+
* so preference resolution, mid-session discovery, and the PackageManager
|
|
30
|
+
* catalog all agree on which directories exist. The `method` is derived from
|
|
31
|
+
* each entry's scope.
|
|
33
32
|
*
|
|
34
33
|
* Note: the ResourceLoader catalog uses PackageManager precedence instead —
|
|
35
34
|
* project `.gsd/skills` and `.agents/skills` can override bundled GSD skills
|
|
36
35
|
* on name collision. See docs/dev/what-is-pi/09-the-customization-stack.md.
|
|
37
36
|
*/
|
|
38
37
|
export function getSkillSearchDirs(cwd: string): Array<{ dir: string; method: SkillResolution["method"] }> {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// Claude Code official skill directories
|
|
44
|
-
{ dir: join(homedir(), ".claude", "skills"), method: "user-skill" },
|
|
45
|
-
{ dir: join(cwd, ".claude", "skills"), method: "project-skill" },
|
|
46
|
-
];
|
|
47
|
-
return dirs;
|
|
38
|
+
return getSkillDirectories({ cwd, gsdHome: gsdHome() }).map((entry) => ({
|
|
39
|
+
dir: entry.path,
|
|
40
|
+
method: entry.scope === "project" ? "project-skill" : "user-skill",
|
|
41
|
+
}));
|
|
48
42
|
}
|
|
49
43
|
|
|
50
44
|
/**
|
|
@@ -29,6 +29,33 @@ export interface ContextManagementConfig {
|
|
|
29
29
|
tool_result_max_chars?: number; // default: 800, range: 200-10000
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* User-tunable thresholds for the tool-call loop guard (#1198). Keeps the two
|
|
34
|
+
* guards separately configurable: the identical-args guard (catches real
|
|
35
|
+
* infinite loops) and the repeated-tool-name cap (catches improvisation loops,
|
|
36
|
+
* more likely to false-positive on legitimate automation). All fields are
|
|
37
|
+
* optional; omitted fields fall back to the built-in defaults so existing
|
|
38
|
+
* installs keep their current behavior. Also overridable via GSD_TOOL_LOOP_*
|
|
39
|
+
* environment variables.
|
|
40
|
+
*/
|
|
41
|
+
export interface ToolCallLoopGuardConfig {
|
|
42
|
+
/** Master switch for both guards. Default: true. */
|
|
43
|
+
enabled?: boolean;
|
|
44
|
+
/** Identical-args sliding-window guard. */
|
|
45
|
+
identical_args?: {
|
|
46
|
+
enabled?: boolean; // default: true
|
|
47
|
+
max_consecutive_calls?: number; // default: 4, min: 1
|
|
48
|
+
};
|
|
49
|
+
/** Per-tool-name cap, independent of args. */
|
|
50
|
+
repeated_tool?: {
|
|
51
|
+
enabled?: boolean; // default: true
|
|
52
|
+
default_cap?: number; // default: 6, min: 1
|
|
53
|
+
repeatable_cap?: number; // default: 15, min: 1
|
|
54
|
+
/** Additional tool names exempt from the per-tool cap (merged with built-in defaults). */
|
|
55
|
+
exempt_tools?: string[];
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
32
59
|
/**
|
|
33
60
|
* Opt-in tool-output sandboxing for sub-sessions. When enabled, the gsd_exec
|
|
34
61
|
* MCP tool runs scripts in an isolated subprocess and returns only a short
|
|
@@ -139,6 +166,7 @@ export const KNOWN_PREFERENCE_KEYS = new Set<string>([
|
|
|
139
166
|
"min_request_interval_ms",
|
|
140
167
|
"stale_commit_threshold_minutes",
|
|
141
168
|
"context_management",
|
|
169
|
+
"tool_call_loop_guard",
|
|
142
170
|
"experimental",
|
|
143
171
|
"codebase",
|
|
144
172
|
"slice_parallel",
|
|
@@ -440,6 +468,8 @@ export interface GSDPreferences {
|
|
|
440
468
|
*/
|
|
441
469
|
context_window_override?: number;
|
|
442
470
|
context_management?: ContextManagementConfig;
|
|
471
|
+
/** User-tunable tool-call loop guard thresholds (#1198). */
|
|
472
|
+
tool_call_loop_guard?: ToolCallLoopGuardConfig;
|
|
443
473
|
/**
|
|
444
474
|
* Tool-output sandboxing via gsd_exec. Keeps sub-session context windows
|
|
445
475
|
* clean by running scripts in a subprocess and only surfacing a short
|
|
@@ -794,6 +794,75 @@ export function validatePreferences(preferences: GSDPreferences): {
|
|
|
794
794
|
}
|
|
795
795
|
}
|
|
796
796
|
|
|
797
|
+
// ─── Tool-call loop guard (#1198) ────────────────────────────────────────
|
|
798
|
+
if (preferences.tool_call_loop_guard !== undefined) {
|
|
799
|
+
if (typeof preferences.tool_call_loop_guard === "object" && preferences.tool_call_loop_guard !== null) {
|
|
800
|
+
const g = preferences.tool_call_loop_guard as unknown as Record<string, unknown>;
|
|
801
|
+
const validGuard: Record<string, unknown> = {};
|
|
802
|
+
|
|
803
|
+
if (g.enabled !== undefined) {
|
|
804
|
+
if (typeof g.enabled === "boolean") validGuard.enabled = g.enabled;
|
|
805
|
+
else errors.push("tool_call_loop_guard.enabled must be a boolean");
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
if (g.identical_args !== undefined) {
|
|
809
|
+
if (typeof g.identical_args === "object" && g.identical_args !== null) {
|
|
810
|
+
const ia = g.identical_args as Record<string, unknown>;
|
|
811
|
+
const validIa: Record<string, unknown> = {};
|
|
812
|
+
if (ia.enabled !== undefined) {
|
|
813
|
+
if (typeof ia.enabled === "boolean") validIa.enabled = ia.enabled;
|
|
814
|
+
else errors.push("tool_call_loop_guard.identical_args.enabled must be a boolean");
|
|
815
|
+
}
|
|
816
|
+
if (ia.max_consecutive_calls !== undefined) {
|
|
817
|
+
const n = ia.max_consecutive_calls;
|
|
818
|
+
if (typeof n === "number" && Number.isInteger(n) && n >= 1) validIa.max_consecutive_calls = n;
|
|
819
|
+
else errors.push("tool_call_loop_guard.identical_args.max_consecutive_calls must be an integer >= 1");
|
|
820
|
+
}
|
|
821
|
+
if (Object.keys(validIa).length > 0) validGuard.identical_args = validIa;
|
|
822
|
+
} else {
|
|
823
|
+
errors.push("tool_call_loop_guard.identical_args must be an object");
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
if (g.repeated_tool !== undefined) {
|
|
828
|
+
if (typeof g.repeated_tool === "object" && g.repeated_tool !== null) {
|
|
829
|
+
const rt = g.repeated_tool as Record<string, unknown>;
|
|
830
|
+
const validRt: Record<string, unknown> = {};
|
|
831
|
+
if (rt.enabled !== undefined) {
|
|
832
|
+
if (typeof rt.enabled === "boolean") validRt.enabled = rt.enabled;
|
|
833
|
+
else errors.push("tool_call_loop_guard.repeated_tool.enabled must be a boolean");
|
|
834
|
+
}
|
|
835
|
+
if (rt.default_cap !== undefined) {
|
|
836
|
+
const n = rt.default_cap;
|
|
837
|
+
if (typeof n === "number" && Number.isInteger(n) && n >= 1) validRt.default_cap = n;
|
|
838
|
+
else errors.push("tool_call_loop_guard.repeated_tool.default_cap must be an integer >= 1");
|
|
839
|
+
}
|
|
840
|
+
if (rt.repeatable_cap !== undefined) {
|
|
841
|
+
const n = rt.repeatable_cap;
|
|
842
|
+
if (typeof n === "number" && Number.isInteger(n) && n >= 1) validRt.repeatable_cap = n;
|
|
843
|
+
else errors.push("tool_call_loop_guard.repeated_tool.repeatable_cap must be an integer >= 1");
|
|
844
|
+
}
|
|
845
|
+
if (rt.exempt_tools !== undefined) {
|
|
846
|
+
if (Array.isArray(rt.exempt_tools) && rt.exempt_tools.every((t) => typeof t === "string")) {
|
|
847
|
+
validRt.exempt_tools = rt.exempt_tools;
|
|
848
|
+
} else {
|
|
849
|
+
errors.push("tool_call_loop_guard.repeated_tool.exempt_tools must be an array of strings");
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
if (Object.keys(validRt).length > 0) validGuard.repeated_tool = validRt;
|
|
853
|
+
} else {
|
|
854
|
+
errors.push("tool_call_loop_guard.repeated_tool must be an object");
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if (Object.keys(validGuard).length > 0) {
|
|
859
|
+
validated.tool_call_loop_guard = validGuard as any;
|
|
860
|
+
}
|
|
861
|
+
} else {
|
|
862
|
+
errors.push("tool_call_loop_guard must be an object");
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
797
866
|
// ─── Context Mode (gsd_exec sandbox) ────────────────────────────────────
|
|
798
867
|
if (preferences.context_mode !== undefined) {
|
|
799
868
|
if (typeof preferences.context_mode === "object" && preferences.context_mode !== null) {
|
|
@@ -1033,6 +1033,26 @@ export function renderPreferencesForSystemPrompt(
|
|
|
1033
1033
|
return lines.join("\n");
|
|
1034
1034
|
}
|
|
1035
1035
|
|
|
1036
|
+
/**
|
|
1037
|
+
* Render a compact response-language directive for injection into dispatched
|
|
1038
|
+
* prompt *content* (discuss/QA and auto-execution unit prompts).
|
|
1039
|
+
*
|
|
1040
|
+
* The main session's system prompt already carries the full GSD Skill
|
|
1041
|
+
* Preferences block (including language) via `before_agent_start`, but
|
|
1042
|
+
* discuss/QA turns and auto-execution units are dispatched as separate
|
|
1043
|
+
* turns/sessions whose prompts are assembled here — without that block — so the
|
|
1044
|
+
* configured language never reaches them (#1210). Injecting this directive into
|
|
1045
|
+
* the dispatched prompt content makes the `language` preference effective across
|
|
1046
|
+
* both guided and auto workflows.
|
|
1047
|
+
*
|
|
1048
|
+
* Returns "" when no language is configured.
|
|
1049
|
+
*/
|
|
1050
|
+
export function renderLanguageDirectiveForPrompt(preferences: GSDPreferences | undefined): string {
|
|
1051
|
+
if (!preferences?.language) return "";
|
|
1052
|
+
const safeLang = preferences.language.replace(/[\r\n]/g, " ").slice(0, 50);
|
|
1053
|
+
return `## Response Language\n\nAlways respond in ${safeLang} — questions, explanations, and summaries.`;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1036
1056
|
// ─── Hook Resolution ──────────────────────────────────────────────────────────
|
|
1037
1057
|
|
|
1038
1058
|
/**
|
|
@@ -38,7 +38,7 @@ If a `GSD Skill Preferences` block appears below, treat it as durable guidance f
|
|
|
38
38
|
|
|
39
39
|
### Naming Convention
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
GSD projects use a flat-phase layout. Phase directories are `{MM}-{slug}` where `{MM}` is the zero-padded phase number and `{slug}` is derived from the phase title (e.g. `47-real-time-runtime-hardening`). Phase-level files are `{MM}-SUFFIX.md` (e.g. `47-CONTEXT.md`, `47-RESEARCH.md`, `47-ROADMAP.md`, `47-VALIDATION.md`, `47-SUMMARY.md`). Slice/plan files live directly in the phase directory as `{MM}-{SS}-SUFFIX.md` (e.g. `47-01-PLAN.md`, `47-01-SUMMARY.md`, `47-01-UAT.md`). Task plan content lives inside the slice plan file (`{MM}-{SS}-PLAN.md`) as checkboxes; there is no `tasks/` subdirectory and no `tasks/T##-PLAN.md`. Titles live inside content, not names. Prefer GSD-provided layout-aware paths/resolvers over hardcoding artifact paths, since phase slugs are title-derived.
|
|
42
42
|
|
|
43
43
|
### Directory Structure
|
|
44
44
|
|
|
@@ -46,13 +46,13 @@ Directories use bare IDs. Files use ID-SUFFIX format. Milestones: `M001/` or `M{
|
|
|
46
46
|
.gsd/
|
|
47
47
|
PROJECT.md, REQUIREMENTS.md, DECISIONS.md, KNOWLEDGE.md, CODEBASE.md, OVERRIDES.md, QUEUE.md, STATE.md
|
|
48
48
|
runtime/, activity/, worktrees/
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
S01-CONTEXT.md, S01-RESEARCH.md, S01-PLAN.md, S01-SUMMARY.md, S01-UAT.md
|
|
53
|
-
tasks/T01-PLAN.md, tasks/T01-SUMMARY.md
|
|
49
|
+
phases/{MM}-{slug}/
|
|
50
|
+
{MM}-CONTEXT.md, {MM}-RESEARCH.md, {MM}-ROADMAP.md, {MM}-VALIDATION.md, {MM}-SUMMARY.md
|
|
51
|
+
{MM}-{SS}-PLAN.md, {MM}-{SS}-SUMMARY.md, {MM}-{SS}-UAT.md
|
|
54
52
|
```
|
|
55
53
|
|
|
54
|
+
Task plan content is embedded in `{MM}-{SS}-PLAN.md`; do not expect `tasks/T##-PLAN.md`.
|
|
55
|
+
|
|
56
56
|
`runtime/`, `activity/`, `worktrees/`, and `STATE.md` are system-managed. `PROJECT.md` is current state; `REQUIREMENTS.md` is the active capability contract; `DECISIONS.md` and `KNOWLEDGE.md` are append-only; `CODEBASE.md` is an auto-refreshed codebase map.
|
|
57
57
|
|
|
58
58
|
### Isolation Model
|
|
@@ -15,7 +15,7 @@ import { isAbsolute, join, relative, resolve } from "node:path";
|
|
|
15
15
|
import { QUICK_BRANCH_RE } from "./branch-patterns.js";
|
|
16
16
|
import { loadPrompt } from "./prompt-loader.js";
|
|
17
17
|
import { gsdRoot } from "./paths.js";
|
|
18
|
-
import { GitServiceImpl, runGit } from "./git-service.js";
|
|
18
|
+
import { GitServiceImpl, runGit, taskBranchArgs } from "./git-service.js";
|
|
19
19
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
20
20
|
import { nativeBranchExists, nativeDetectMainBranch, nativeDiffNumstat } from "./native-git-bridge.js";
|
|
21
21
|
import { nativeHasStagedChanges } from "./native-git-bridge.js";
|
|
@@ -365,6 +365,7 @@ export async function handleQuick(
|
|
|
365
365
|
const git = new GitServiceImpl(basePath, gitPrefs);
|
|
366
366
|
const branchName = `gsd/quick/${taskNum}-${slug}`;
|
|
367
367
|
let originalBranch = git.getCurrentBranch();
|
|
368
|
+
let returnBranch = originalBranch;
|
|
368
369
|
|
|
369
370
|
const { getIsolationMode } = await import("./preferences.js");
|
|
370
371
|
const usesBranch = getIsolationMode() !== "none" && isGitOpsEnabled();
|
|
@@ -381,7 +382,14 @@ export async function handleQuick(
|
|
|
381
382
|
}
|
|
382
383
|
} catch { /* nothing to commit — fine */ }
|
|
383
384
|
|
|
384
|
-
|
|
385
|
+
const branchArgs = taskBranchArgs(basePath, branchName, current, git.getMainBranch());
|
|
386
|
+
const startPoint = branchArgs[3];
|
|
387
|
+
// When the quick branch is redirected off a stale task branch, the
|
|
388
|
+
// squash-merge on closeout must return to the same base — merging a
|
|
389
|
+
// base-branch-rooted quick branch back into the stale task branch
|
|
390
|
+
// would drag the base's newer history into it.
|
|
391
|
+
if (startPoint) returnBranch = startPoint;
|
|
392
|
+
runGit(basePath, branchArgs);
|
|
385
393
|
branchCreated = true;
|
|
386
394
|
}
|
|
387
395
|
} catch (err) {
|
|
@@ -395,7 +403,7 @@ export async function handleQuick(
|
|
|
395
403
|
if (actualBranch === branchName && originalBranch !== branchName) {
|
|
396
404
|
persistPendingReturn({
|
|
397
405
|
basePath,
|
|
398
|
-
originalBranch,
|
|
406
|
+
originalBranch: returnBranch,
|
|
399
407
|
quickBranch: branchName,
|
|
400
408
|
taskNum,
|
|
401
409
|
slug,
|