@opengsd/gsd-pi 1.0.2-dev.e70300c → 1.0.2-dev.fb7ddf1
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 +63 -12
- package/dist/headless-answers.js +2 -1
- package/dist/headless-events.d.ts +1 -0
- package/dist/headless-events.js +8 -1
- package/dist/onboarding.js +22 -3
- package/dist/resource-loader.d.ts +7 -0
- package/dist/resource-loader.js +44 -9
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +34 -11
- package/dist/resources/extensions/context7/index.js +12 -2
- package/dist/resources/extensions/get-secrets-from-user.js +16 -16
- package/dist/resources/extensions/google-cli/index.js +30 -0
- package/dist/resources/extensions/google-cli/models.js +55 -0
- package/dist/resources/extensions/google-cli/package.json +11 -0
- package/dist/resources/extensions/google-cli/readiness.js +12 -0
- package/dist/resources/extensions/google-cli/stream-adapter.js +191 -0
- package/dist/resources/extensions/gsd/auto/loop.js +81 -1
- package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -2
- package/dist/resources/extensions/gsd/auto/phases.js +38 -1
- package/dist/resources/extensions/gsd/auto/run-unit.js +8 -0
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +17 -7
- package/dist/resources/extensions/gsd/auto-post-unit.js +65 -16
- package/dist/resources/extensions/gsd/auto-prompts.js +5 -236
- package/dist/resources/extensions/gsd/auto-recovery.js +10 -5
- package/dist/resources/extensions/gsd/auto-start.js +232 -49
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +2 -1
- package/dist/resources/extensions/gsd/auto-verification.js +14 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +34 -1
- package/dist/resources/extensions/gsd/auto.js +40 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -3
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -2
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +39 -5
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +107 -27
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +3 -27
- package/dist/resources/extensions/gsd/bootstrap/tool-search-shim.js +4 -4
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +1 -1
- package/dist/resources/extensions/gsd/closeout-recovery.js +7 -1
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +9 -1
- package/dist/resources/extensions/gsd/commands-handlers.js +3 -0
- package/dist/resources/extensions/gsd/commands-usage.js +105 -1
- package/dist/resources/extensions/gsd/config-overlay.js +20 -14
- package/dist/resources/extensions/gsd/context-overlay.js +22 -16
- package/dist/resources/extensions/gsd/dashboard-overlay.js +10 -23
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +87 -0
- package/dist/resources/extensions/gsd/doctor-git-checks.js +70 -5
- package/dist/resources/extensions/gsd/doctor-providers.js +54 -24
- package/dist/resources/extensions/gsd/doctor.js +7 -2
- package/dist/resources/extensions/gsd/git-conflict-state.js +26 -1
- package/dist/resources/extensions/gsd/guided-flow.js +5 -6
- package/dist/resources/extensions/gsd/key-manager.js +45 -13
- package/dist/resources/extensions/gsd/mcp-filter.js +57 -18
- package/dist/resources/extensions/gsd/mcp-project-config.js +15 -9
- package/dist/resources/extensions/gsd/migration-auto-check.js +5 -1
- package/dist/resources/extensions/gsd/milestone-actions.js +3 -0
- package/dist/resources/extensions/gsd/milestone-reopen-events.js +28 -0
- package/dist/resources/extensions/gsd/notification-overlay.js +8 -9
- package/dist/resources/extensions/gsd/parallel-merge.js +6 -4
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +15 -13
- package/dist/resources/extensions/gsd/post-execution-checks.js +5 -4
- package/dist/resources/extensions/gsd/preferences-skills.js +11 -4
- package/dist/resources/extensions/gsd/preferences.js +14 -2
- package/dist/resources/extensions/gsd/prompt-loader.js +2 -0
- package/dist/resources/extensions/gsd/prompts/discuss.md +4 -2
- package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
- package/dist/resources/extensions/gsd/prompts/system.md +3 -20
- package/dist/resources/extensions/gsd/queue-reorder-ui.js +28 -18
- package/dist/resources/extensions/gsd/repo-identity.js +36 -6
- package/dist/resources/extensions/gsd/repository-registry.js +3 -1
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +13 -6
- package/dist/resources/extensions/gsd/skill-activation.js +233 -0
- package/dist/resources/extensions/gsd/skill-catalog.data.js +820 -0
- package/dist/resources/extensions/gsd/skill-catalog.install.js +179 -0
- package/dist/resources/extensions/gsd/skill-catalog.js +5 -1028
- package/dist/resources/extensions/gsd/skill-discovery.js +121 -79
- package/dist/resources/extensions/gsd/skill-scope.js +52 -0
- package/dist/resources/extensions/gsd/skill-telemetry.js +6 -39
- package/dist/resources/extensions/gsd/skills/gsd-headless/SKILL.md +1 -1
- package/dist/resources/extensions/gsd/skills.js +60 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +351 -0
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +41 -0
- package/dist/resources/extensions/gsd/state-reconciliation/registry.js +4 -0
- package/dist/resources/extensions/gsd/tools/complete-task.js +9 -0
- package/dist/resources/extensions/gsd/tools/exec-tool.js +42 -8
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +63 -2
- package/dist/resources/extensions/gsd/tui/render-kit.js +51 -0
- package/dist/resources/extensions/gsd/unit-context-manifest.js +35 -26
- package/dist/resources/extensions/gsd/user-input-boundary.js +1 -1
- package/dist/resources/extensions/gsd/vision-ask.js +22 -0
- package/dist/resources/extensions/gsd/visualizer-overlay.js +8 -36
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -3
- package/dist/resources/extensions/gsd/worktree-state-projection.js +29 -0
- package/dist/resources/extensions/search-the-web/native-search.js +57 -8
- package/dist/resources/extensions/shared/confirm-ui.js +9 -6
- package/dist/resources/extensions/shared/dialog-frame.js +42 -0
- package/dist/resources/extensions/shared/interview-ui.js +42 -30
- package/dist/resources/extensions/shared/next-action-ui.js +6 -6
- package/dist/resources/extensions/subagent/index.js +8 -15
- package/dist/resources/shared/package-manager-detection.js +36 -0
- package/dist/resources/skills/agent-browser/SKILL.md +1 -1
- package/dist/resources/skills/api-design/SKILL.md +1 -1
- package/dist/resources/skills/code-optimizer/SKILL.md +6 -11
- package/dist/resources/skills/create-gsd-extension/SKILL.md +1 -1
- package/dist/resources/skills/create-mcp-server/SKILL.md +1 -1
- package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
- package/dist/resources/skills/create-skill/workflows/verify-skill.md +2 -10
- package/dist/resources/skills/debug-like-expert/references/when-to-research.md +1 -5
- package/dist/resources/skills/decompose-into-slices/SKILL.md +3 -3
- package/dist/resources/skills/dependency-upgrade/SKILL.md +1 -1
- package/dist/resources/skills/forensics/SKILL.md +2 -2
- package/dist/resources/skills/grill-me/SKILL.md +1 -1
- package/dist/resources/skills/handoff/SKILL.md +1 -1
- package/dist/resources/skills/make-interfaces-feel-better/SKILL.md +1 -1
- package/dist/resources/skills/observability/SKILL.md +1 -1
- package/dist/resources/skills/security-review/SKILL.md +1 -1
- package/dist/resources/skills/spike-wrap-up/SKILL.md +1 -1
- package/dist/resources/skills/tdd/SKILL.md +1 -1
- package/dist/resources/skills/write-docs/SKILL.md +1 -1
- package/dist/resources/skills/write-milestone-brief/SKILL.md +1 -1
- package/dist/update-check.d.ts +6 -2
- package/dist/update-check.js +7 -3
- 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/api/update/route.js +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/chunks/1834.js +2 -2
- 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/node-pty/build/Makefile +1 -1
- package/package.json +5 -2
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/rpc.test.js +5 -0
- package/packages/contracts/dist/rpc.test.js.map +1 -1
- package/packages/contracts/dist/workflow.d.ts +15 -0
- package/packages/contracts/dist/workflow.d.ts.map +1 -1
- package/packages/contracts/dist/workflow.js +16 -0
- package/packages/contracts/dist/workflow.js.map +1 -1
- package/packages/contracts/dist/workflow.test.js +1 -0
- package/packages/contracts/dist/workflow.test.js.map +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +1 -0
- package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +22 -8
- package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts +12 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js +45 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts +3 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js +11 -11
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts +3 -3
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js +13 -11
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts +3 -3
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js +12 -10
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js +2 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts +6 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js +9 -6
- package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +3 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +2 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts +3 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +144 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js +2 -14
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +7 -1
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +13 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +47 -8
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +16 -14
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/harness/skills.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/harness/skills.js +6 -0
- package/packages/pi-agent-core/dist/harness/skills.js.map +1 -1
- package/packages/pi-agent-core/dist/harness/system-prompt.d.ts +7 -0
- package/packages/pi-agent-core/dist/harness/system-prompt.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/harness/system-prompt.js +7 -0
- package/packages/pi-agent-core/dist/harness/system-prompt.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +48 -206
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +67 -220
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +50 -0
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses-shared.js +28 -4
- package/packages/pi-ai/dist/providers/openai-responses-shared.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +2 -0
- 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/dist/utils/tests/tool-search-shim.test.js +29 -1
- package/packages/pi-ai/dist/utils/tests/tool-search-shim.test.js.map +1 -1
- package/packages/pi-ai/dist/utils/tool-search-shim.d.ts +4 -1
- package/packages/pi-ai/dist/utils/tool-search-shim.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/tool-search-shim.js +58 -10
- package/packages/pi-ai/dist/utils/tool-search-shim.js.map +1 -1
- package/packages/pi-ai/dist/utils/tool-shims.d.ts +1 -1
- package/packages/pi-ai/dist/utils/tool-shims.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/tool-shims.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/README.md +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +8 -2
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +3 -0
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.js +5 -7
- package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/read.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/read.js +5 -4
- package/packages/pi-coding-agent/dist/core/tools/read.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.js +0 -1
- package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
- package/packages/pi-coding-agent/package.json +8 -8
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/scripts/install/detect-existing.js +17 -3
- package/scripts/install/npm-global.js +103 -33
- package/scripts/install.js +1 -0
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +36 -11
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +86 -19
- package/src/resources/extensions/context7/index.ts +15 -2
- package/src/resources/extensions/get-secrets-from-user.ts +17 -16
- package/src/resources/extensions/google-cli/index.ts +34 -0
- package/src/resources/extensions/google-cli/models.ts +57 -0
- package/src/resources/extensions/google-cli/package.json +11 -0
- package/src/resources/extensions/google-cli/readiness.ts +15 -0
- package/src/resources/extensions/google-cli/stream-adapter.ts +245 -0
- package/src/resources/extensions/gsd/auto/loop.ts +96 -1
- package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -2
- package/src/resources/extensions/gsd/auto/phases.ts +47 -1
- package/src/resources/extensions/gsd/auto/run-unit.ts +10 -0
- package/src/resources/extensions/gsd/auto/session.ts +3 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +31 -11
- package/src/resources/extensions/gsd/auto-post-unit.ts +101 -18
- package/src/resources/extensions/gsd/auto-prompts.ts +4 -284
- package/src/resources/extensions/gsd/auto-recovery.ts +10 -7
- package/src/resources/extensions/gsd/auto-start.ts +307 -56
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +3 -1
- package/src/resources/extensions/gsd/auto-verification.ts +18 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +45 -1
- package/src/resources/extensions/gsd/auto.ts +50 -2
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -3
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +9 -4
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +42 -5
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +124 -25
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +3 -28
- package/src/resources/extensions/gsd/bootstrap/tool-search-shim.ts +4 -4
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +1 -1
- package/src/resources/extensions/gsd/closeout-recovery.ts +6 -1
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -1
- package/src/resources/extensions/gsd/commands-handlers.ts +2 -0
- package/src/resources/extensions/gsd/commands-usage.ts +110 -5
- package/src/resources/extensions/gsd/config-overlay.ts +19 -16
- package/src/resources/extensions/gsd/context-overlay.ts +24 -19
- package/src/resources/extensions/gsd/dashboard-overlay.ts +14 -27
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +99 -0
- package/src/resources/extensions/gsd/doctor-git-checks.ts +72 -5
- package/src/resources/extensions/gsd/doctor-providers.ts +55 -27
- package/src/resources/extensions/gsd/doctor-types.ts +2 -0
- package/src/resources/extensions/gsd/doctor.ts +7 -2
- package/src/resources/extensions/gsd/git-conflict-state.ts +25 -1
- package/src/resources/extensions/gsd/guided-flow.ts +5 -6
- package/src/resources/extensions/gsd/key-manager.ts +57 -14
- package/src/resources/extensions/gsd/mcp-filter.ts +64 -17
- package/src/resources/extensions/gsd/mcp-project-config.ts +24 -9
- package/src/resources/extensions/gsd/migration-auto-check.ts +6 -0
- package/src/resources/extensions/gsd/milestone-actions.ts +2 -0
- package/src/resources/extensions/gsd/milestone-reopen-events.ts +28 -0
- package/src/resources/extensions/gsd/notification-overlay.ts +12 -11
- package/src/resources/extensions/gsd/parallel-merge.ts +6 -4
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +16 -12
- package/src/resources/extensions/gsd/post-execution-checks.ts +7 -4
- package/src/resources/extensions/gsd/preferences-skills.ts +11 -4
- package/src/resources/extensions/gsd/preferences.ts +17 -2
- package/src/resources/extensions/gsd/prompt-loader.ts +2 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +4 -2
- package/src/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
- package/src/resources/extensions/gsd/prompts/system.md +3 -20
- package/src/resources/extensions/gsd/queue-reorder-ui.ts +29 -20
- package/src/resources/extensions/gsd/repo-identity.ts +35 -7
- package/src/resources/extensions/gsd/repository-registry.ts +3 -1
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +13 -6
- package/src/resources/extensions/gsd/skill-activation.ts +292 -0
- package/src/resources/extensions/gsd/skill-catalog.data.ts +858 -0
- package/src/resources/extensions/gsd/skill-catalog.install.ts +205 -0
- package/src/resources/extensions/gsd/skill-catalog.ts +16 -1087
- package/src/resources/extensions/gsd/skill-discovery.ts +134 -78
- package/src/resources/extensions/gsd/skill-scope.ts +63 -0
- package/src/resources/extensions/gsd/skill-telemetry.ts +6 -40
- package/src/resources/extensions/gsd/skills/gsd-headless/SKILL.md +1 -1
- package/src/resources/extensions/gsd/skills.ts +75 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +499 -0
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +40 -0
- package/src/resources/extensions/gsd/state-reconciliation/registry.ts +8 -0
- package/src/resources/extensions/gsd/state-reconciliation/types.ts +30 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +328 -2
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/auto-post-unit-artifact-diagnostic.test.ts +28 -2
- package/src/resources/extensions/gsd/tests/auto-post-unit-evidence-crossref-4909.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +41 -0
- package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +436 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-untracked-content.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/closeout-recovery.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/commands-context.test.ts +5 -3
- package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +15 -2
- package/src/resources/extensions/gsd/tests/commands-usage.test.ts +97 -0
- package/src/resources/extensions/gsd/tests/complete-slice-reopen-handoff.test.ts +40 -3
- package/src/resources/extensions/gsd/tests/context-chart.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/discord-invite-links.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/doctor-empty-worktree.test.ts +71 -1
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +105 -0
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +101 -1
- package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +22 -3
- package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/interactive-tool-idle-exemption.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +23 -4
- package/src/resources/extensions/gsd/tests/mcp-filter.test.ts +19 -1
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +56 -1
- package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +70 -10
- package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +127 -10
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/project-relocation-recovery.test.ts +101 -0
- package/src/resources/extensions/gsd/tests/queue-reorder-ui.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/register-extension-guard.test.ts +116 -11
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/repository-registry.test.ts +30 -1
- package/src/resources/extensions/gsd/tests/show-config-command.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +111 -0
- package/src/resources/extensions/gsd/tests/skill-scope-auto.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/skills.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +13 -2
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +303 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +67 -1
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +24 -1
- package/src/resources/extensions/gsd/tests/tui-border-assertions.ts +28 -0
- package/src/resources/extensions/gsd/tests/tui-render-kit.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/vision-ask.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +133 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +82 -0
- package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +16 -1
- package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +28 -0
- package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +38 -1
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +45 -1
- package/src/resources/extensions/gsd/tools/complete-task.ts +9 -0
- package/src/resources/extensions/gsd/tools/exec-tool.ts +42 -10
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +82 -5
- package/src/resources/extensions/gsd/tui/render-kit.ts +82 -0
- package/src/resources/extensions/gsd/unit-context-manifest.ts +37 -26
- package/src/resources/extensions/gsd/user-input-boundary.ts +1 -1
- package/src/resources/extensions/gsd/vision-ask.ts +28 -0
- package/src/resources/extensions/gsd/visualizer-overlay.ts +12 -40
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +37 -2
- package/src/resources/extensions/gsd/worktree-state-projection.ts +33 -0
- package/src/resources/extensions/search-the-web/native-search.ts +60 -8
- package/src/resources/extensions/shared/confirm-ui.ts +8 -12
- package/src/resources/extensions/shared/dialog-frame.ts +71 -0
- package/src/resources/extensions/shared/interview-ui.ts +43 -42
- package/src/resources/extensions/shared/next-action-ui.ts +6 -6
- package/src/resources/extensions/shared/tests/confirm-ui.test.ts +57 -0
- package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +163 -0
- package/src/resources/extensions/shared/tests/next-action-ui-hasui.test.ts +55 -0
- package/src/resources/extensions/subagent/index.ts +8 -15
- package/src/resources/shared/package-manager-detection.ts +39 -0
- package/src/resources/skills/agent-browser/SKILL.md +1 -1
- package/src/resources/skills/api-design/SKILL.md +1 -1
- package/src/resources/skills/code-optimizer/SKILL.md +6 -11
- package/src/resources/skills/create-gsd-extension/SKILL.md +1 -1
- package/src/resources/skills/create-mcp-server/SKILL.md +1 -1
- package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
- package/src/resources/skills/create-skill/workflows/verify-skill.md +2 -10
- package/src/resources/skills/debug-like-expert/references/when-to-research.md +1 -5
- package/src/resources/skills/decompose-into-slices/SKILL.md +3 -3
- package/src/resources/skills/dependency-upgrade/SKILL.md +1 -1
- package/src/resources/skills/forensics/SKILL.md +2 -2
- package/src/resources/skills/grill-me/SKILL.md +1 -1
- package/src/resources/skills/handoff/SKILL.md +1 -1
- package/src/resources/skills/make-interfaces-feel-better/SKILL.md +1 -1
- package/src/resources/skills/observability/SKILL.md +1 -1
- package/src/resources/skills/security-review/SKILL.md +1 -1
- package/src/resources/skills/spike-wrap-up/SKILL.md +1 -1
- package/src/resources/skills/tdd/SKILL.md +1 -1
- package/src/resources/skills/write-docs/SKILL.md +1 -1
- package/src/resources/skills/write-milestone-brief/SKILL.md +1 -1
- /package/dist/web/standalone/.next/static/{szb-HAt0IoSx3docUZO-E → tH1tnDYt1E0hK9Ien73Z0}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{szb-HAt0IoSx3docUZO-E → tH1tnDYt1E0hK9Ien73Z0}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Fail-closed reconciliation guards for DB/artifact and slice-id drift.
|
|
3
|
+
import { existsSync, lstatSync, mkdirSync, readdirSync, renameSync, rmSync, } from "node:fs";
|
|
4
|
+
import { basename, join } from "node:path";
|
|
5
|
+
import { _getAdapter, getAllMilestones, getMilestoneSlices, getSliceTasks, isDbAvailable, } from "../../gsd-db.js";
|
|
6
|
+
import { clearParseCache } from "../../files.js";
|
|
7
|
+
import { clearPathCache, gsdProjectionRoot, resolveMilestonePath, resolveSliceFile, resolveTaskFile, } from "../../paths.js";
|
|
8
|
+
import { isClosedStatus } from "../../status-guards.js";
|
|
9
|
+
import { invalidateStateCache } from "../../state.js";
|
|
10
|
+
import { isAfter, latestExplicitReopenAt } from "../../milestone-reopen-events.js";
|
|
11
|
+
function safeListArtifactRows(milestoneId) {
|
|
12
|
+
const adapter = _getAdapter();
|
|
13
|
+
if (!adapter)
|
|
14
|
+
return [];
|
|
15
|
+
try {
|
|
16
|
+
return adapter
|
|
17
|
+
.prepare(`SELECT path, artifact_type, milestone_id, slice_id, task_id, imported_at
|
|
18
|
+
FROM artifacts
|
|
19
|
+
WHERE milestone_id = :mid
|
|
20
|
+
ORDER BY imported_at, path`)
|
|
21
|
+
.all({ ":mid": milestoneId });
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function latestCompletedMilestoneDispatch(milestoneId) {
|
|
28
|
+
const adapter = _getAdapter();
|
|
29
|
+
if (!adapter)
|
|
30
|
+
return null;
|
|
31
|
+
try {
|
|
32
|
+
const row = adapter
|
|
33
|
+
.prepare(`SELECT started_at, ended_at
|
|
34
|
+
FROM unit_dispatches
|
|
35
|
+
WHERE milestone_id = :mid
|
|
36
|
+
AND unit_type = 'complete-milestone'
|
|
37
|
+
AND unit_id = :mid
|
|
38
|
+
AND status = 'completed'
|
|
39
|
+
ORDER BY COALESCE(ended_at, started_at) DESC, id DESC
|
|
40
|
+
LIMIT 1`)
|
|
41
|
+
.get({ ":mid": milestoneId });
|
|
42
|
+
return row ?? null;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function hasExplicitReopenAfter(basePath, milestoneId, completedDispatchAt) {
|
|
49
|
+
const reopenAt = latestExplicitReopenAt(basePath, milestoneId);
|
|
50
|
+
if (!reopenAt)
|
|
51
|
+
return false;
|
|
52
|
+
if (!completedDispatchAt)
|
|
53
|
+
return true;
|
|
54
|
+
return Date.parse(reopenAt) > Date.parse(completedDispatchAt);
|
|
55
|
+
}
|
|
56
|
+
function addUniqueDrift(drifts, seen, drift) {
|
|
57
|
+
const key = [
|
|
58
|
+
drift.milestoneId,
|
|
59
|
+
drift.sliceId ?? "",
|
|
60
|
+
drift.taskId ?? "",
|
|
61
|
+
drift.artifactType,
|
|
62
|
+
drift.artifactPath ?? "",
|
|
63
|
+
drift.reason,
|
|
64
|
+
].join("|");
|
|
65
|
+
if (seen.has(key))
|
|
66
|
+
return;
|
|
67
|
+
seen.add(key);
|
|
68
|
+
drifts.push(drift);
|
|
69
|
+
}
|
|
70
|
+
function detectArtifactDbStatusDriftForMilestone(basePath, milestoneId) {
|
|
71
|
+
const milestone = getAllMilestones().find((m) => m.id === milestoneId);
|
|
72
|
+
if (!milestone || isClosedStatus(milestone.status))
|
|
73
|
+
return [];
|
|
74
|
+
const latestReopen = latestExplicitReopenAt(basePath, milestoneId);
|
|
75
|
+
const artifacts = safeListArtifactRows(milestoneId).filter((row) => isAfter(row.imported_at, latestReopen));
|
|
76
|
+
const bySlice = new Map(getMilestoneSlices(milestoneId).map((slice) => [slice.id, slice]));
|
|
77
|
+
const drifts = [];
|
|
78
|
+
const seen = new Set();
|
|
79
|
+
for (const slice of bySlice.values()) {
|
|
80
|
+
if (!isClosedStatus(slice.status)) {
|
|
81
|
+
const diskSummary = resolveSliceFile(basePath, milestoneId, slice.id, "SUMMARY");
|
|
82
|
+
if (diskSummary && existsSync(diskSummary)) {
|
|
83
|
+
addUniqueDrift(drifts, seen, {
|
|
84
|
+
kind: "artifact-db-status-divergence",
|
|
85
|
+
milestoneId,
|
|
86
|
+
sliceId: slice.id,
|
|
87
|
+
artifactType: "SUMMARY",
|
|
88
|
+
artifactPath: diskSummary,
|
|
89
|
+
dbStatus: slice.status,
|
|
90
|
+
reason: `slice ${slice.id} has SUMMARY on disk while DB status is ${slice.status}`,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const tasks = getSliceTasks(milestoneId, slice.id);
|
|
95
|
+
const taskById = new Map(tasks.map((task) => [task.id, task]));
|
|
96
|
+
const summaryRows = artifacts.filter((row) => row.artifact_type === "SUMMARY" &&
|
|
97
|
+
row.slice_id === slice.id &&
|
|
98
|
+
row.task_id);
|
|
99
|
+
if (tasks.length === 0 && summaryRows.length > 0) {
|
|
100
|
+
addUniqueDrift(drifts, seen, {
|
|
101
|
+
kind: "artifact-db-status-divergence",
|
|
102
|
+
milestoneId,
|
|
103
|
+
sliceId: slice.id,
|
|
104
|
+
artifactType: "SUMMARY",
|
|
105
|
+
artifactPath: summaryRows[0]?.path,
|
|
106
|
+
dbStatus: "no-db-tasks",
|
|
107
|
+
reason: `slice ${slice.id} has task SUMMARY artifacts but no DB tasks`,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
for (const row of summaryRows) {
|
|
111
|
+
const task = row.task_id ? taskById.get(row.task_id) : null;
|
|
112
|
+
if (!task) {
|
|
113
|
+
if (tasks.length > 0) {
|
|
114
|
+
addUniqueDrift(drifts, seen, {
|
|
115
|
+
kind: "artifact-db-status-divergence",
|
|
116
|
+
milestoneId,
|
|
117
|
+
sliceId: slice.id,
|
|
118
|
+
taskId: row.task_id ?? undefined,
|
|
119
|
+
artifactType: "SUMMARY",
|
|
120
|
+
artifactPath: row.path,
|
|
121
|
+
dbStatus: "missing-db-task",
|
|
122
|
+
reason: `task ${slice.id}/${row.task_id} has SUMMARY artifact but no DB task`,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (isClosedStatus(task.status))
|
|
128
|
+
continue;
|
|
129
|
+
addUniqueDrift(drifts, seen, {
|
|
130
|
+
kind: "artifact-db-status-divergence",
|
|
131
|
+
milestoneId,
|
|
132
|
+
sliceId: slice.id,
|
|
133
|
+
taskId: row.task_id ?? undefined,
|
|
134
|
+
artifactType: "SUMMARY",
|
|
135
|
+
artifactPath: row.path,
|
|
136
|
+
dbStatus: task.status,
|
|
137
|
+
reason: `task ${slice.id}/${row.task_id} has SUMMARY artifact while DB status is ${task.status}`,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
for (const task of tasks) {
|
|
141
|
+
if (isClosedStatus(task.status))
|
|
142
|
+
continue;
|
|
143
|
+
const diskTaskSummary = resolveTaskFile(basePath, milestoneId, slice.id, task.id, "SUMMARY");
|
|
144
|
+
if (!diskTaskSummary || !existsSync(diskTaskSummary))
|
|
145
|
+
continue;
|
|
146
|
+
addUniqueDrift(drifts, seen, {
|
|
147
|
+
kind: "artifact-db-status-divergence",
|
|
148
|
+
milestoneId,
|
|
149
|
+
sliceId: slice.id,
|
|
150
|
+
taskId: task.id,
|
|
151
|
+
artifactType: "SUMMARY",
|
|
152
|
+
artifactPath: diskTaskSummary,
|
|
153
|
+
dbStatus: task.status,
|
|
154
|
+
reason: `task ${slice.id}/${task.id} has SUMMARY on disk while DB status is ${task.status}`,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
for (const row of artifacts) {
|
|
159
|
+
if (row.artifact_type !== "SUMMARY" || !row.slice_id || row.task_id)
|
|
160
|
+
continue;
|
|
161
|
+
const slice = bySlice.get(row.slice_id);
|
|
162
|
+
if (!slice || isClosedStatus(slice.status))
|
|
163
|
+
continue;
|
|
164
|
+
addUniqueDrift(drifts, seen, {
|
|
165
|
+
kind: "artifact-db-status-divergence",
|
|
166
|
+
milestoneId,
|
|
167
|
+
sliceId: row.slice_id,
|
|
168
|
+
artifactType: "SUMMARY",
|
|
169
|
+
artifactPath: row.path,
|
|
170
|
+
dbStatus: slice.status,
|
|
171
|
+
reason: `slice ${row.slice_id} has SUMMARY artifact while DB status is ${slice.status}`,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return drifts;
|
|
175
|
+
}
|
|
176
|
+
function classifyDiskOnlySliceDir(sliceDir) {
|
|
177
|
+
let sawScaffold = false;
|
|
178
|
+
const stack = [sliceDir];
|
|
179
|
+
while (stack.length > 0) {
|
|
180
|
+
const dir = stack.pop();
|
|
181
|
+
let entries;
|
|
182
|
+
try {
|
|
183
|
+
entries = readdirSync(dir);
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return "block-meaningful";
|
|
187
|
+
}
|
|
188
|
+
if (entries.length === 0 && dir !== sliceDir) {
|
|
189
|
+
sawScaffold = true;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
for (const entry of entries) {
|
|
193
|
+
if (entry === ".DS_Store")
|
|
194
|
+
continue;
|
|
195
|
+
const full = join(dir, entry);
|
|
196
|
+
let stat;
|
|
197
|
+
try {
|
|
198
|
+
stat = lstatSync(full);
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return "block-meaningful";
|
|
202
|
+
}
|
|
203
|
+
if (stat.isDirectory()) {
|
|
204
|
+
sawScaffold = true;
|
|
205
|
+
stack.push(full);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (stat.isFile() && stat.size === 0) {
|
|
209
|
+
sawScaffold = true;
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
return "block-meaningful";
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return sawScaffold ? "quarantine-scaffold" : "delete-empty";
|
|
216
|
+
}
|
|
217
|
+
function detectDiskSliceIdDivergenceForMilestone(basePath, milestoneId) {
|
|
218
|
+
const milestonePath = resolveMilestonePath(basePath, milestoneId);
|
|
219
|
+
if (!milestonePath)
|
|
220
|
+
return [];
|
|
221
|
+
const slicesDir = join(milestonePath, "slices");
|
|
222
|
+
if (!existsSync(slicesDir))
|
|
223
|
+
return [];
|
|
224
|
+
const knownSliceIds = new Set(getMilestoneSlices(milestoneId).map((slice) => slice.id));
|
|
225
|
+
const drifts = [];
|
|
226
|
+
for (const entry of readdirSync(slicesDir)) {
|
|
227
|
+
const sliceDir = join(slicesDir, entry);
|
|
228
|
+
try {
|
|
229
|
+
if (!lstatSync(sliceDir).isDirectory())
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (entry === "parallel-research")
|
|
236
|
+
continue;
|
|
237
|
+
if (knownSliceIds.has(entry))
|
|
238
|
+
continue;
|
|
239
|
+
const disposition = classifyDiskOnlySliceDir(sliceDir);
|
|
240
|
+
drifts.push({
|
|
241
|
+
kind: "disk-slice-id-divergence",
|
|
242
|
+
milestoneId,
|
|
243
|
+
sliceId: entry,
|
|
244
|
+
sliceDir,
|
|
245
|
+
disposition,
|
|
246
|
+
reason: disposition === "block-meaningful"
|
|
247
|
+
? `disk-only slice directory ${entry} contains meaningful files and is not in the DB`
|
|
248
|
+
: `disk-only slice directory ${entry} is not in the DB`,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
return drifts;
|
|
252
|
+
}
|
|
253
|
+
export function detectArtifactDbDrift(_state, ctx) {
|
|
254
|
+
if (!isDbAvailable())
|
|
255
|
+
return [];
|
|
256
|
+
const drifts = [];
|
|
257
|
+
for (const milestone of getAllMilestones()) {
|
|
258
|
+
if (isClosedStatus(milestone.status))
|
|
259
|
+
continue;
|
|
260
|
+
const completedDispatch = latestCompletedMilestoneDispatch(milestone.id);
|
|
261
|
+
const completedAt = completedDispatch?.ended_at ?? completedDispatch?.started_at ?? null;
|
|
262
|
+
if (completedDispatch &&
|
|
263
|
+
!hasExplicitReopenAfter(ctx.basePath, milestone.id, completedAt)) {
|
|
264
|
+
drifts.push({
|
|
265
|
+
kind: "completed-milestone-reopened",
|
|
266
|
+
milestoneId: milestone.id,
|
|
267
|
+
dbStatus: milestone.status,
|
|
268
|
+
completedDispatchAt: completedAt,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
drifts.push(...detectArtifactDbStatusDriftForMilestone(ctx.basePath, milestone.id));
|
|
272
|
+
drifts.push(...detectDiskSliceIdDivergenceForMilestone(ctx.basePath, milestone.id));
|
|
273
|
+
}
|
|
274
|
+
return drifts;
|
|
275
|
+
}
|
|
276
|
+
function quarantineSliceDir(record, basePath) {
|
|
277
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
278
|
+
const quarantineDir = join(gsdProjectionRoot(basePath), "quarantine", "milestones", record.milestoneId, "slices");
|
|
279
|
+
mkdirSync(quarantineDir, { recursive: true });
|
|
280
|
+
const target = join(quarantineDir, `${basename(record.sliceDir)}-${stamp}`);
|
|
281
|
+
renameSync(record.sliceDir, target);
|
|
282
|
+
}
|
|
283
|
+
function diskSliceIdDivergenceGuidance(record) {
|
|
284
|
+
const quarantineExample = `.gsd/quarantine/milestones/${record.milestoneId}/slices/${record.sliceId}-manual-review`;
|
|
285
|
+
return (`Slice ID drift in ${record.milestoneId}: ${record.reason}. ` +
|
|
286
|
+
"Runtime will not import disk-only slice IDs into the DB. " +
|
|
287
|
+
`Review ${record.sliceDir}. ` +
|
|
288
|
+
`If ${record.sliceId} is stale, move or delete that directory; to preserve it, move it under ${quarantineExample}. ` +
|
|
289
|
+
"If it contains work to keep, copy or merge that content into a DB-backed slice, or explicitly recreate the slice through GSD planning, then remove the disk-only directory. " +
|
|
290
|
+
`After repair, run /gsd doctor ${record.milestoneId}, then resume with /gsd next or /gsd auto.`);
|
|
291
|
+
}
|
|
292
|
+
export function repairArtifactDbDrift(record, ctx) {
|
|
293
|
+
if (record.kind === "disk-slice-id-divergence") {
|
|
294
|
+
if (record.disposition === "delete-empty") {
|
|
295
|
+
rmSync(record.sliceDir, { recursive: true, force: true });
|
|
296
|
+
}
|
|
297
|
+
else if (record.disposition === "quarantine-scaffold") {
|
|
298
|
+
quarantineSliceDir(record, ctx.basePath);
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
throw new Error(diskSliceIdDivergenceGuidance(record));
|
|
302
|
+
}
|
|
303
|
+
clearPathCache();
|
|
304
|
+
clearParseCache();
|
|
305
|
+
invalidateStateCache();
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (record.kind === "completed-milestone-reopened") {
|
|
309
|
+
throw new Error(`Milestone ${record.milestoneId} has completed complete-milestone dispatch history` +
|
|
310
|
+
` (${record.completedDispatchAt ?? "time unknown"}) but the DB status is ${record.dbStatus}. ` +
|
|
311
|
+
"Refusing to plan it again without an explicit reopen or recovery.");
|
|
312
|
+
}
|
|
313
|
+
throw new Error(`Artifact/DB status drift in ${record.milestoneId}` +
|
|
314
|
+
`${record.sliceId ? `/${record.sliceId}` : ""}` +
|
|
315
|
+
`${record.taskId ? `/${record.taskId}` : ""}: ${record.reason}. ` +
|
|
316
|
+
"Runtime will not silently import completion artifacts into DB state; run explicit recovery/repair after review.");
|
|
317
|
+
}
|
|
318
|
+
export function describeArtifactDbDriftBlocker(record) {
|
|
319
|
+
if (record.kind === "disk-slice-id-divergence") {
|
|
320
|
+
if (record.disposition !== "block-meaningful")
|
|
321
|
+
return null;
|
|
322
|
+
return diskSliceIdDivergenceGuidance(record);
|
|
323
|
+
}
|
|
324
|
+
if (record.kind === "completed-milestone-reopened") {
|
|
325
|
+
return (`Milestone ${record.milestoneId} has completed complete-milestone dispatch history` +
|
|
326
|
+
` (${record.completedDispatchAt ?? "time unknown"}) but the DB status is ${record.dbStatus}. ` +
|
|
327
|
+
"Refusing to plan it again without an explicit reopen or recovery.");
|
|
328
|
+
}
|
|
329
|
+
return (`Artifact/DB status drift in ${record.milestoneId}` +
|
|
330
|
+
`${record.sliceId ? `/${record.sliceId}` : ""}` +
|
|
331
|
+
`${record.taskId ? `/${record.taskId}` : ""}: ${record.reason}. ` +
|
|
332
|
+
"Runtime will not silently import completion artifacts into DB state; run explicit recovery/repair after review.");
|
|
333
|
+
}
|
|
334
|
+
export const diskSliceIdDivergenceHandler = {
|
|
335
|
+
kind: "disk-slice-id-divergence",
|
|
336
|
+
detect: (state, ctx) => detectArtifactDbDrift(state, ctx).filter((record) => record.kind === "disk-slice-id-divergence"),
|
|
337
|
+
blocker: describeArtifactDbDriftBlocker,
|
|
338
|
+
repair: repairArtifactDbDrift,
|
|
339
|
+
};
|
|
340
|
+
export const artifactDbStatusDivergenceHandler = {
|
|
341
|
+
kind: "artifact-db-status-divergence",
|
|
342
|
+
detect: (state, ctx) => detectArtifactDbDrift(state, ctx).filter((record) => record.kind === "artifact-db-status-divergence"),
|
|
343
|
+
blocker: describeArtifactDbDriftBlocker,
|
|
344
|
+
repair: repairArtifactDbDrift,
|
|
345
|
+
};
|
|
346
|
+
export const completedMilestoneReopenedHandler = {
|
|
347
|
+
kind: "completed-milestone-reopened",
|
|
348
|
+
detect: (state, ctx) => detectArtifactDbDrift(state, ctx).filter((record) => record.kind === "completed-milestone-reopened"),
|
|
349
|
+
blocker: describeArtifactDbDriftBlocker,
|
|
350
|
+
repair: repairArtifactDbDrift,
|
|
351
|
+
};
|
|
@@ -42,6 +42,8 @@ export async function reconcileBeforeDispatch(basePath, deps = defaultDeps) {
|
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
const failures = [];
|
|
45
|
+
const blockers = [];
|
|
46
|
+
let repairedThisPass = false;
|
|
45
47
|
for (const record of drift) {
|
|
46
48
|
const handler = registry.find((h) => h.kind === record.kind);
|
|
47
49
|
if (!handler) {
|
|
@@ -51,14 +53,33 @@ export async function reconcileBeforeDispatch(basePath, deps = defaultDeps) {
|
|
|
51
53
|
});
|
|
52
54
|
continue;
|
|
53
55
|
}
|
|
56
|
+
const blocker = handler.blocker ? await handler.blocker(record, ctx) : null;
|
|
57
|
+
if (blocker) {
|
|
58
|
+
blockers.push(blocker);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
54
61
|
try {
|
|
55
62
|
await handler.repair(record, ctx);
|
|
56
63
|
repaired.push(record);
|
|
64
|
+
repairedThisPass = true;
|
|
57
65
|
}
|
|
58
66
|
catch (cause) {
|
|
59
67
|
failures.push({ drift: record, cause });
|
|
60
68
|
}
|
|
61
69
|
}
|
|
70
|
+
if (blockers.length > 0) {
|
|
71
|
+
let blockerState = stateSnapshot;
|
|
72
|
+
if (repairedThisPass) {
|
|
73
|
+
deps.invalidateStateCache();
|
|
74
|
+
blockerState = await deps.deriveState(basePath, deps.deriveStateOptions);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
ok: true,
|
|
78
|
+
stateSnapshot: blockerState,
|
|
79
|
+
repaired,
|
|
80
|
+
blockers: [...new Set([...(blockerState.blockers ?? []), ...blockers])],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
62
83
|
if (failures.length > 0) {
|
|
63
84
|
throw new ReconciliationFailedError({ failures, pass });
|
|
64
85
|
}
|
|
@@ -70,6 +91,26 @@ export async function reconcileBeforeDispatch(basePath, deps = defaultDeps) {
|
|
|
70
91
|
const finalCtx = { basePath, state: finalState };
|
|
71
92
|
const persistent = await detectAllDrift(finalState, finalCtx, registry);
|
|
72
93
|
if (persistent.length > 0) {
|
|
94
|
+
const blockers = [];
|
|
95
|
+
const unblockedPersistent = [];
|
|
96
|
+
for (const record of persistent) {
|
|
97
|
+
const handler = registry.find((h) => h.kind === record.kind);
|
|
98
|
+
const blocker = handler?.blocker ? await handler.blocker(record, finalCtx) : null;
|
|
99
|
+
if (blocker) {
|
|
100
|
+
blockers.push(blocker);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
unblockedPersistent.push(record);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (blockers.length > 0 && unblockedPersistent.length === 0) {
|
|
107
|
+
return {
|
|
108
|
+
ok: true,
|
|
109
|
+
stateSnapshot: finalState,
|
|
110
|
+
repaired,
|
|
111
|
+
blockers: [...new Set([...(finalState.blockers ?? []), ...blockers])],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
73
114
|
throw new ReconciliationFailedError({ persistentDrift: persistent });
|
|
74
115
|
}
|
|
75
116
|
return {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// File Purpose: ADR-017 drift handler registry. Single source of truth for
|
|
3
3
|
// the catalog. Tests can override per-call via ReconciliationDeps.registry.
|
|
4
4
|
import { completionTimestampHandler } from "./drift/completion.js";
|
|
5
|
+
import { artifactDbStatusDivergenceHandler, completedMilestoneReopenedHandler, diskSliceIdDivergenceHandler, } from "./drift/artifact-db.js";
|
|
5
6
|
import { mergeStateHandler } from "./drift/merge-state.js";
|
|
6
7
|
import { unregisteredMilestoneHandler } from "./drift/project-md.js";
|
|
7
8
|
import { roadmapDivergenceHandler } from "./drift/roadmap.js";
|
|
@@ -19,6 +20,9 @@ export const DRIFT_REGISTRY = [
|
|
|
19
20
|
staleRenderHandler,
|
|
20
21
|
staleWorkerHandler,
|
|
21
22
|
unregisteredMilestoneHandler,
|
|
23
|
+
diskSliceIdDivergenceHandler,
|
|
22
24
|
roadmapDivergenceHandler,
|
|
25
|
+
completedMilestoneReopenedHandler,
|
|
26
|
+
artifactDbStatusDivergenceHandler,
|
|
23
27
|
completionTimestampHandler,
|
|
24
28
|
];
|
|
@@ -116,6 +116,15 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
116
116
|
if (!params.milestoneId || typeof params.milestoneId !== "string" || params.milestoneId.trim() === "") {
|
|
117
117
|
return { error: "milestoneId is required and must be a non-empty string" };
|
|
118
118
|
}
|
|
119
|
+
if (!params.oneLiner || typeof params.oneLiner !== "string" || params.oneLiner.trim() === "") {
|
|
120
|
+
return { error: "oneLiner is required and must be a non-empty string" };
|
|
121
|
+
}
|
|
122
|
+
if (!params.narrative || typeof params.narrative !== "string" || params.narrative.trim() === "") {
|
|
123
|
+
return { error: "narrative is required and must be a non-empty string" };
|
|
124
|
+
}
|
|
125
|
+
if (!params.verification || typeof params.verification !== "string" || params.verification.trim() === "") {
|
|
126
|
+
return { error: "verification is required and must be a non-empty string" };
|
|
127
|
+
}
|
|
119
128
|
const artifactBasePath = resolveCanonicalMilestoneRoot(basePath, params.milestoneId);
|
|
120
129
|
// ── Ownership check (opt-in: only enforced when claim file exists) ──────
|
|
121
130
|
const ownershipErr = checkOwnership(artifactBasePath, taskUnitKey(params.milestoneId, params.sliceId, params.taskId), params.actorName);
|
|
@@ -40,6 +40,42 @@ function paramError(message) {
|
|
|
40
40
|
isError: true,
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
+
function normalizeRuntime(value) {
|
|
44
|
+
if (value === undefined || value === null || value === "")
|
|
45
|
+
return "bash";
|
|
46
|
+
if (typeof value !== "string") {
|
|
47
|
+
return paramError(`invalid runtime "${String(value)}" — must be bash | node | python`);
|
|
48
|
+
}
|
|
49
|
+
const normalized = value.trim().toLowerCase();
|
|
50
|
+
if (normalized === "" || normalized === "bash" || normalized === "sh" || normalized === "shell")
|
|
51
|
+
return "bash";
|
|
52
|
+
if (normalized === "node" || normalized === "nodejs" || normalized === "js" || normalized === "javascript")
|
|
53
|
+
return "node";
|
|
54
|
+
if (normalized === "python" || normalized === "python3" || normalized === "py")
|
|
55
|
+
return "python";
|
|
56
|
+
return paramError(`invalid runtime "${value}" — must be bash | node | python`);
|
|
57
|
+
}
|
|
58
|
+
function normalizeScript(params) {
|
|
59
|
+
const candidates = [params.script, params.command, params.cmd, params.code];
|
|
60
|
+
let sawNonString = false;
|
|
61
|
+
for (const candidate of candidates) {
|
|
62
|
+
if (candidate === undefined || candidate === null)
|
|
63
|
+
continue;
|
|
64
|
+
if (typeof candidate !== "string") {
|
|
65
|
+
sawNonString = true;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (candidate.trim().length > 0)
|
|
69
|
+
return candidate;
|
|
70
|
+
}
|
|
71
|
+
if (sawNonString) {
|
|
72
|
+
return paramError("script/command must be a non-empty string");
|
|
73
|
+
}
|
|
74
|
+
return paramError("script is required and must be a non-empty string");
|
|
75
|
+
}
|
|
76
|
+
function isToolExecutionResult(value) {
|
|
77
|
+
return typeof value === "object" && value !== null && Array.isArray(value.content);
|
|
78
|
+
}
|
|
43
79
|
function escapeRegExp(value) {
|
|
44
80
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
45
81
|
}
|
|
@@ -151,14 +187,12 @@ function scriptReferencesOriginalRootFromWorktree(script, baseDir) {
|
|
|
151
187
|
export async function executeGsdExec(params, deps) {
|
|
152
188
|
if (!isEnabled(deps.preferences))
|
|
153
189
|
return contextModeDisabledResult("gsd_exec");
|
|
154
|
-
const runtime = params.runtime;
|
|
155
|
-
if (runtime
|
|
156
|
-
return
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return paramError("script is required and must be a non-empty string");
|
|
161
|
-
}
|
|
190
|
+
const runtime = normalizeRuntime(params.runtime);
|
|
191
|
+
if (isToolExecutionResult(runtime))
|
|
192
|
+
return runtime;
|
|
193
|
+
const script = normalizeScript(params);
|
|
194
|
+
if (isToolExecutionResult(script))
|
|
195
|
+
return script;
|
|
162
196
|
if (Buffer.byteLength(script, "utf8") > 200_000) {
|
|
163
197
|
return paramError("script exceeds the 200 KB length limit");
|
|
164
198
|
}
|
|
@@ -6,8 +6,10 @@ import { loadWriteGateSnapshot, shouldBlockContextArtifactSaveInSnapshot, should
|
|
|
6
6
|
import { getActiveRequirements, insertMilestone, getMilestone, getSliceStatusSummary, getSliceTaskCounts, readTransaction, saveGateResult, } from "../gsd-db.js";
|
|
7
7
|
import { GATE_REGISTRY } from "../gate-registry.js";
|
|
8
8
|
import { generateRequirementsMd, saveArtifactToDb } from "../db-writer.js";
|
|
9
|
-
import { resolveMilestoneFile, resolveSliceFile } from "../paths.js";
|
|
9
|
+
import { clearPathCache, resolveGsdPathContract, resolveMilestoneFile, resolveSliceFile } from "../paths.js";
|
|
10
|
+
import { saveFile, clearParseCache } from "../files.js";
|
|
10
11
|
import { unlinkSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
11
13
|
import { handleCompleteMilestone } from "./complete-milestone.js";
|
|
12
14
|
import { handleCompleteTask } from "./complete-task.js";
|
|
13
15
|
import { handleCompleteSlice } from "./complete-slice.js";
|
|
@@ -56,6 +58,25 @@ function registerProjectMilestoneSequence(content) {
|
|
|
56
58
|
}
|
|
57
59
|
return registered;
|
|
58
60
|
}
|
|
61
|
+
async function mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, content) {
|
|
62
|
+
const contract = resolveGsdPathContract(basePath);
|
|
63
|
+
if (!contract.worktreeGsd)
|
|
64
|
+
return;
|
|
65
|
+
if (contract.worktreeGsd === contract.projectGsd)
|
|
66
|
+
return;
|
|
67
|
+
const fullPath = join(contract.worktreeGsd, relativePath);
|
|
68
|
+
try {
|
|
69
|
+
await saveFile(fullPath, content);
|
|
70
|
+
clearPathCache();
|
|
71
|
+
clearParseCache();
|
|
72
|
+
invalidateStateCache();
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
logWarning("tool", `gsd_summary_save worktree projection mirror failed: ${err.message}`, {
|
|
76
|
+
path: relativePath,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
59
80
|
export async function executeSummarySave(params, basePath = process.cwd()) {
|
|
60
81
|
const dbAvailable = await ensureDbOpen(basePath);
|
|
61
82
|
if (!dbAvailable) {
|
|
@@ -145,6 +166,7 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
|
|
|
145
166
|
slice_id: isRootArtifact ? undefined : params.slice_id,
|
|
146
167
|
task_id: isRootArtifact ? undefined : params.task_id,
|
|
147
168
|
}, basePath);
|
|
169
|
+
await mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, contentToSave);
|
|
148
170
|
let registeredMilestones = [];
|
|
149
171
|
if (params.artifact_type === "PROJECT") {
|
|
150
172
|
try {
|
|
@@ -238,6 +260,24 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
|
|
|
238
260
|
};
|
|
239
261
|
}
|
|
240
262
|
}
|
|
263
|
+
function normalizeVerificationEvidence(evidence) {
|
|
264
|
+
return (evidence ?? []).map((entry) => typeof entry === "string"
|
|
265
|
+
? { command: entry, exitCode: -1, verdict: "unknown (coerced from string)", durationMs: 0 }
|
|
266
|
+
: entry);
|
|
267
|
+
}
|
|
268
|
+
function deriveVerificationSummary(evidence) {
|
|
269
|
+
if (evidence.length === 0)
|
|
270
|
+
return null;
|
|
271
|
+
const rendered = evidence.slice(0, 3).map((entry) => {
|
|
272
|
+
const command = entry.command.trim() || "(unspecified command)";
|
|
273
|
+
const verdict = entry.verdict.trim() || "recorded";
|
|
274
|
+
return `\`${command}\` exited ${entry.exitCode} (${verdict})`;
|
|
275
|
+
});
|
|
276
|
+
const suffix = evidence.length > rendered.length
|
|
277
|
+
? `; ${evidence.length - rendered.length} more check(s) recorded`
|
|
278
|
+
: "";
|
|
279
|
+
return `Verification evidence recorded: ${rendered.join("; ")}${suffix}.`;
|
|
280
|
+
}
|
|
241
281
|
export async function executeTaskComplete(params, basePath = process.cwd()) {
|
|
242
282
|
const dbAvailable = await ensureDbOpen(basePath);
|
|
243
283
|
if (!dbAvailable) {
|
|
@@ -249,7 +289,28 @@ export async function executeTaskComplete(params, basePath = process.cwd()) {
|
|
|
249
289
|
}
|
|
250
290
|
try {
|
|
251
291
|
const coerced = { ...params };
|
|
252
|
-
|
|
292
|
+
const verificationEvidence = normalizeVerificationEvidence(params.verificationEvidence);
|
|
293
|
+
coerced.verificationEvidence = verificationEvidence;
|
|
294
|
+
const verification = typeof params.verification === "string" ? params.verification.trim() : "";
|
|
295
|
+
if (verification.length === 0) {
|
|
296
|
+
const derived = deriveVerificationSummary(verificationEvidence);
|
|
297
|
+
if (derived) {
|
|
298
|
+
coerced.verification = derived;
|
|
299
|
+
}
|
|
300
|
+
else if (params.blockerDiscovered === true) {
|
|
301
|
+
coerced.verification = "Not run: blocker discovered before verification.";
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
return {
|
|
305
|
+
content: [{
|
|
306
|
+
type: "text",
|
|
307
|
+
text: "Error completing task: verification is required unless verificationEvidence is provided or blockerDiscovered is true.",
|
|
308
|
+
}],
|
|
309
|
+
details: { operation: "complete_task", error: "verification_required" },
|
|
310
|
+
isError: true,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
}
|
|
253
314
|
const result = await handleCompleteTask(coerced, basePath);
|
|
254
315
|
if ("error" in result) {
|
|
255
316
|
return {
|
|
@@ -105,3 +105,54 @@ export function renderFrame(theme, inner, width, options = {}) {
|
|
|
105
105
|
lines.push(border("╰" + "─".repeat(width - 2) + "╯"));
|
|
106
106
|
return lines.map((line) => safeLine(line, width, ""));
|
|
107
107
|
}
|
|
108
|
+
function renderTitledTopBorder(theme, title, width, border) {
|
|
109
|
+
const trimmedTitle = title.trim();
|
|
110
|
+
if (!trimmedTitle || width < 10) {
|
|
111
|
+
return border("╭" + "─".repeat(width - 2) + "╮");
|
|
112
|
+
}
|
|
113
|
+
const maxTitleWidth = Math.max(0, width - 7);
|
|
114
|
+
const safeTitle = safeLine(trimmedTitle, maxTitleWidth);
|
|
115
|
+
const fill = Math.max(0, width - visibleWidth(safeTitle) - 5);
|
|
116
|
+
return border("╭─ ") + theme.bold(theme.fg("accent", safeTitle)) + border(" " + "─".repeat(fill) + "╮");
|
|
117
|
+
}
|
|
118
|
+
export function renderDialogFrame(theme, title, inner, width, options = {}) {
|
|
119
|
+
if (width < 4)
|
|
120
|
+
return inner.map((line) => safeLine(line, width));
|
|
121
|
+
const borderColor = options.borderColor ?? "borderAccent";
|
|
122
|
+
const paddingX = Math.max(0, options.paddingX ?? 1);
|
|
123
|
+
const contentWidth = Math.max(0, width - 2 - paddingX * 2);
|
|
124
|
+
const border = (text) => theme.fg(borderColor, text);
|
|
125
|
+
const pad = " ".repeat(paddingX);
|
|
126
|
+
const lines = [renderTitledTopBorder(theme, title, width, border)];
|
|
127
|
+
const scroll = options.scroll;
|
|
128
|
+
const bodyRows = inner.length;
|
|
129
|
+
const trackOffset = Math.max(0, Math.min(scroll?.trackOffset ?? 0, bodyRows));
|
|
130
|
+
const trackRows = Math.max(0, Math.min(scroll?.trackRows ?? bodyRows, bodyRows - trackOffset));
|
|
131
|
+
const scrollable = !!scroll && scroll.totalRows > scroll.visibleRows && trackRows > 0;
|
|
132
|
+
const thumbLen = scrollable
|
|
133
|
+
? Math.max(1, Math.round((scroll.visibleRows / scroll.totalRows) * trackRows))
|
|
134
|
+
: 0;
|
|
135
|
+
const maxThumbStart = Math.max(0, trackRows - thumbLen);
|
|
136
|
+
const maxScrollOffset = scrollable ? Math.max(1, scroll.totalRows - scroll.visibleRows) : 1;
|
|
137
|
+
const thumbStart = scrollable
|
|
138
|
+
? trackOffset + Math.min(maxThumbStart, Math.round((scroll.offset / maxScrollOffset) * maxThumbStart))
|
|
139
|
+
: -1;
|
|
140
|
+
for (let i = 0; i < inner.length; i++) {
|
|
141
|
+
const line = inner[i] ?? "";
|
|
142
|
+
const rightBorder = scrollable && i >= thumbStart && i < thumbStart + thumbLen ? "┃" : "│";
|
|
143
|
+
lines.push(border("│") + pad + padRightVisible(line, contentWidth) + pad + border(rightBorder));
|
|
144
|
+
}
|
|
145
|
+
const footer = Array.isArray(options.footer)
|
|
146
|
+
? options.footer
|
|
147
|
+
: options.footer
|
|
148
|
+
? [options.footer]
|
|
149
|
+
: [];
|
|
150
|
+
if (footer.length > 0) {
|
|
151
|
+
lines.push(border("├" + "─".repeat(width - 2) + "┤"));
|
|
152
|
+
for (const line of footer) {
|
|
153
|
+
lines.push(border("│") + pad + padRightVisible(line, contentWidth) + pad + border("│"));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
lines.push(border("╰" + "─".repeat(width - 2) + "╯"));
|
|
157
|
+
return lines.map((line) => safeLine(line, width, ""));
|
|
158
|
+
}
|