@opengsd/gsd-pi 1.0.2-dev.e9a1b49 → 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 +36 -55
- 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-post-create-hook.js +117 -0
- 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/dist/worktree-cli.d.ts +0 -2
- package/dist/worktree-cli.js +21 -9
- package/package.json +5 -2
- package/packages/cloud-mcp-gateway/bin/gsd-cloud-mcp-gateway.js +14 -0
- package/packages/cloud-mcp-gateway/package.json +4 -3
- 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/bin/gsd-mcp-server.js +14 -0
- 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 +5 -4
- 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/bin/pi-ai.js +14 -0
- 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 +73 -226
- 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 +3 -2
- 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/deps.js +10 -0
- 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 +47 -57
- 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-post-create-hook.test.ts +141 -1
- 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-post-create-hook.ts +127 -0
- 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/tsconfig.extensions.tsbuildinfo +0 -1
- /package/dist/web/standalone/.next/static/{BEjZM0MLHLibeMFbjtMol → tH1tnDYt1E0hK9Ien73Z0}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{BEjZM0MLHLibeMFbjtMol → tH1tnDYt1E0hK9Ien73Z0}/_ssgManifest.js +0 -0
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import test from "node:test";
|
|
4
4
|
import assert from "node:assert/strict";
|
|
5
|
-
import { mkdtempSync, mkdirSync, rmSync } from "node:fs";
|
|
5
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
import { join } from "node:path";
|
|
8
|
+
import { execFileSync } from "node:child_process";
|
|
8
9
|
import { createRepositoryRegistryFromPreferences, defaultRepositoryTargets } from "../repository-registry.ts";
|
|
9
10
|
|
|
10
11
|
test("repository registry includes implicit project root and declared child repos", (t) => {
|
|
@@ -99,3 +100,31 @@ test("defaultRepositoryTargets returns [project] for a parent-mode registry", (t
|
|
|
99
100
|
|
|
100
101
|
assert.deepEqual(defaultRepositoryTargets(registry), ["project"]);
|
|
101
102
|
});
|
|
103
|
+
|
|
104
|
+
test("repository registry keeps project root anchored to .gsd project in monorepo subdirectory", (t) => {
|
|
105
|
+
const monorepo = mkdtempSync(join(tmpdir(), "gsd-repo-registry-mono-"));
|
|
106
|
+
t.after(() => rmSync(monorepo, { recursive: true, force: true }));
|
|
107
|
+
execFileSync("git", ["init"], { cwd: monorepo, stdio: "ignore" });
|
|
108
|
+
|
|
109
|
+
const subproject = join(monorepo, "fieldkit-tools");
|
|
110
|
+
mkdirSync(join(subproject, ".gsd"), { recursive: true });
|
|
111
|
+
writeFileSync(join(subproject, ".gsd", "PREFERENCES.md"), "---\nversion: 1\n---\n");
|
|
112
|
+
|
|
113
|
+
const registry = createRepositoryRegistryFromPreferences(subproject, undefined);
|
|
114
|
+
|
|
115
|
+
assert.equal(registry.projectRoot, subproject);
|
|
116
|
+
assert.equal(registry.byId.get("project")?.root, subproject);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("repository registry uses external-state worktree checkout as project root", (t) => {
|
|
120
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-repo-registry-external-"));
|
|
121
|
+
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
122
|
+
const worktree = join(base, ".gsd", "projects", "abc123", "worktrees", "M001");
|
|
123
|
+
mkdirSync(worktree, { recursive: true });
|
|
124
|
+
execFileSync("git", ["init"], { cwd: worktree, stdio: "ignore" });
|
|
125
|
+
|
|
126
|
+
const registry = createRepositoryRegistryFromPreferences(worktree, undefined);
|
|
127
|
+
|
|
128
|
+
assert.equal(registry.projectRoot, worktree);
|
|
129
|
+
assert.equal(registry.byId.get("project")?.root, worktree);
|
|
130
|
+
});
|
|
@@ -7,6 +7,7 @@ import assert from "node:assert/strict";
|
|
|
7
7
|
|
|
8
8
|
import { GSDConfigOverlay, formatConfigText } from "../config-overlay.ts";
|
|
9
9
|
import { handleCoreCommand } from "../commands/handlers/core.ts";
|
|
10
|
+
import { assertFullOuterBorder } from "./tui-border-assertions.ts";
|
|
10
11
|
|
|
11
12
|
const theme = {
|
|
12
13
|
bold: (s: string) => s,
|
|
@@ -24,6 +25,9 @@ test("GSDConfigOverlay renders and responds to input", () => {
|
|
|
24
25
|
|
|
25
26
|
const lines = overlay.render(60);
|
|
26
27
|
assert.ok(lines.some((line) => line.includes("GSD Configuration")));
|
|
28
|
+
assertFullOuterBorder(lines, 60);
|
|
29
|
+
assert.match(lines[0] ?? "", /^╭─ GSD Configuration /);
|
|
30
|
+
assert.match(lines.at(-1) ?? "", /^╰─+╯$/);
|
|
27
31
|
|
|
28
32
|
overlay.handleInput("j");
|
|
29
33
|
assert.equal(renderRequests, 1);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import {
|
|
7
|
+
appendDiscoveredSkillsFallback,
|
|
8
|
+
clearSkillSnapshot,
|
|
9
|
+
detectNewSkills,
|
|
10
|
+
refreshCatalogForNewSkills,
|
|
11
|
+
snapshotSkills,
|
|
12
|
+
} from "../skill-discovery.js";
|
|
13
|
+
|
|
14
|
+
function makeTempHome(): string {
|
|
15
|
+
return mkdtempSync(join(tmpdir(), "gsd-skill-discovery-"));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function withTempSkillHome<T>(fn: (home: string) => T | Promise<T>): Promise<T> {
|
|
19
|
+
const previousHome = process.env.HOME;
|
|
20
|
+
const previousGsdHome = process.env.GSD_HOME;
|
|
21
|
+
const home = makeTempHome();
|
|
22
|
+
process.env.HOME = home;
|
|
23
|
+
process.env.GSD_HOME = join(home, ".gsd");
|
|
24
|
+
try {
|
|
25
|
+
return await fn(home);
|
|
26
|
+
} finally {
|
|
27
|
+
clearSkillSnapshot();
|
|
28
|
+
if (previousHome === undefined) {
|
|
29
|
+
delete process.env.HOME;
|
|
30
|
+
} else {
|
|
31
|
+
process.env.HOME = previousHome;
|
|
32
|
+
}
|
|
33
|
+
if (previousGsdHome === undefined) {
|
|
34
|
+
delete process.env.GSD_HOME;
|
|
35
|
+
} else {
|
|
36
|
+
process.env.GSD_HOME = previousGsdHome;
|
|
37
|
+
}
|
|
38
|
+
rmSync(home, { recursive: true, force: true });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function writeDiskSkill(root: string, name: string, description = `Use for ${name}.`): void {
|
|
43
|
+
const dir = join(root, name);
|
|
44
|
+
mkdirSync(dir, { recursive: true });
|
|
45
|
+
writeFileSync(join(dir, "SKILL.md"), `---\nname: ${name}\ndescription: ${description}\n---\n\n# ${name}\n`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
test("detectNewSkills detects skills added on disk after the baseline snapshot", async () => {
|
|
49
|
+
await withTempSkillHome((home) => {
|
|
50
|
+
const skillsRoot = join(home, ".agents", "skills");
|
|
51
|
+
writeDiskSkill(skillsRoot, "existing-skill");
|
|
52
|
+
snapshotSkills();
|
|
53
|
+
|
|
54
|
+
writeDiskSkill(skillsRoot, "new-disk-skill", "New disk skill.");
|
|
55
|
+
const detected = detectNewSkills();
|
|
56
|
+
|
|
57
|
+
assert.deepEqual(detected.map(skill => skill.name), ["new-disk-skill"]);
|
|
58
|
+
assert.equal(detected[0].description, "New disk skill.");
|
|
59
|
+
assert.equal(detected[0].location, join(skillsRoot, "new-disk-skill", "SKILL.md"));
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("refreshCatalogForNewSkills retries discovery after reload failure", async () => {
|
|
64
|
+
await withTempSkillHome(async (home) => {
|
|
65
|
+
const skillsRoot = join(home, ".agents", "skills");
|
|
66
|
+
snapshotSkills();
|
|
67
|
+
writeDiskSkill(skillsRoot, "reload-retry-skill");
|
|
68
|
+
|
|
69
|
+
const messages: Array<{ message: string; level: "info" | "warning" }> = [];
|
|
70
|
+
const failed = await refreshCatalogForNewSkills({
|
|
71
|
+
reload: async () => { throw new Error("reload failed"); },
|
|
72
|
+
notify: (message, level) => messages.push({ message, level }),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
assert.deepEqual(failed.map(skill => skill.name), ["reload-retry-skill"]);
|
|
76
|
+
assert.deepEqual(detectNewSkills().map(skill => skill.name), ["reload-retry-skill"]);
|
|
77
|
+
assert.equal(messages[0]?.level, "warning");
|
|
78
|
+
|
|
79
|
+
const loaded = await refreshCatalogForNewSkills({
|
|
80
|
+
reload: async () => {},
|
|
81
|
+
notify: (message, level) => messages.push({ message, level }),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
assert.deepEqual(loaded.map(skill => skill.name), ["reload-retry-skill"]);
|
|
85
|
+
assert.deepEqual(detectNewSkills(), []);
|
|
86
|
+
assert.ok(messages.some(({ level, message }) => level === "info" && message.includes("reload-retry-skill")));
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("appendDiscoveredSkillsFallback exposes newly detected skills missing from the prompt", () => {
|
|
91
|
+
const prompt = appendDiscoveredSkillsFallback("base system prompt", [{
|
|
92
|
+
name: "fallback-skill",
|
|
93
|
+
description: "Use when reload fails & skill is needed.",
|
|
94
|
+
location: "/tmp/fallback-skill/SKILL.md",
|
|
95
|
+
}]);
|
|
96
|
+
|
|
97
|
+
assert.match(prompt, /<newly_discovered_skills>/);
|
|
98
|
+
assert.match(prompt, /fallback-skill/);
|
|
99
|
+
assert.match(prompt, /Use when reload fails & skill is needed\./);
|
|
100
|
+
assert.match(prompt, /\/tmp\/fallback-skill\/SKILL.md/);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("appendDiscoveredSkillsFallback does not duplicate skills already in the prompt", () => {
|
|
104
|
+
const prompt = "base system prompt\n/tmp/already-loaded/SKILL.md";
|
|
105
|
+
|
|
106
|
+
assert.equal(appendDiscoveredSkillsFallback(prompt, [{
|
|
107
|
+
name: "already-loaded",
|
|
108
|
+
description: "Already present.",
|
|
109
|
+
location: "/tmp/already-loaded/SKILL.md",
|
|
110
|
+
}]), prompt);
|
|
111
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
applyUnitSkillVisibility,
|
|
6
|
+
effectiveSkillNamesForUnit,
|
|
7
|
+
resolveVisibleSkillNames,
|
|
8
|
+
unitHasSkillManifest,
|
|
9
|
+
} from "../skill-scope.ts";
|
|
10
|
+
|
|
11
|
+
test("resolveVisibleSkillNames: none policy suppresses catalog", () => {
|
|
12
|
+
assert.deepEqual(resolveVisibleSkillNames("workflow-preferences"), []);
|
|
13
|
+
assert.equal(unitHasSkillManifest("workflow-preferences"), true);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("resolveVisibleSkillNames: wildcard units use full catalog", () => {
|
|
17
|
+
assert.equal(resolveVisibleSkillNames("execute-task"), undefined);
|
|
18
|
+
assert.equal(unitHasSkillManifest("execute-task"), false);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("unitHasSkillManifest: manifest unit types return true", () => {
|
|
22
|
+
assert.equal(unitHasSkillManifest("research-milestone"), true);
|
|
23
|
+
assert.equal(unitHasSkillManifest("plan-slice"), true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("unitHasSkillManifest: wildcard unit types return false", () => {
|
|
27
|
+
assert.equal(unitHasSkillManifest("execute-task"), false);
|
|
28
|
+
assert.equal(unitHasSkillManifest(undefined), false);
|
|
29
|
+
assert.equal(unitHasSkillManifest("unknown-unit"), false);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("applyUnitSkillVisibility: sets manifest names for scoped units", () => {
|
|
33
|
+
let visible: string[] | undefined;
|
|
34
|
+
applyUnitSkillVisibility({
|
|
35
|
+
setVisibleSkills: (names) => {
|
|
36
|
+
visible = names;
|
|
37
|
+
},
|
|
38
|
+
}, "research-milestone");
|
|
39
|
+
|
|
40
|
+
assert.ok(Array.isArray(visible));
|
|
41
|
+
assert.ok(visible!.includes("write-docs"));
|
|
42
|
+
assert.ok(visible!.length < 15);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("applyUnitSkillVisibility: restores full catalog for wildcard units", () => {
|
|
46
|
+
let visible: string[] | undefined = ["stale"];
|
|
47
|
+
applyUnitSkillVisibility({
|
|
48
|
+
setVisibleSkills: (names) => {
|
|
49
|
+
visible = names;
|
|
50
|
+
},
|
|
51
|
+
}, "execute-task");
|
|
52
|
+
|
|
53
|
+
assert.equal(visible, undefined);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("effectiveSkillNamesForUnit: filters installed names by manifest", () => {
|
|
57
|
+
const installed = ["write-docs", "review", "frontend-design", "tdd"];
|
|
58
|
+
const scoped = effectiveSkillNamesForUnit("research-milestone", installed);
|
|
59
|
+
assert.ok(scoped.includes("write-docs"));
|
|
60
|
+
assert.ok(!scoped.includes("review"));
|
|
61
|
+
assert.ok(scoped.length < installed.length);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("effectiveSkillNamesForUnit: pass-through for wildcard units", () => {
|
|
65
|
+
const installed = ["write-docs", "review"];
|
|
66
|
+
assert.deepEqual(effectiveSkillNamesForUnit("execute-task", installed), installed);
|
|
67
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import type { Skill } from "@gsd/pi-coding-agent";
|
|
4
|
+
import {
|
|
5
|
+
detectNewlyInstalledSkills,
|
|
6
|
+
getInstalledSkillNames,
|
|
7
|
+
getInstalledSkills,
|
|
8
|
+
normalizeSkillName,
|
|
9
|
+
resolveInstalledSkill,
|
|
10
|
+
snapshotInstalledSkillNames,
|
|
11
|
+
} from "../skills.js";
|
|
12
|
+
|
|
13
|
+
function makeSkill(name: string, filePath = `/tmp/${name}/SKILL.md`): Skill {
|
|
14
|
+
return {
|
|
15
|
+
name,
|
|
16
|
+
description: `Use for ${name}.`,
|
|
17
|
+
filePath,
|
|
18
|
+
baseDir: `/tmp/${name}`,
|
|
19
|
+
source: "user",
|
|
20
|
+
sourceInfo: {
|
|
21
|
+
path: filePath,
|
|
22
|
+
source: "local",
|
|
23
|
+
scope: "user",
|
|
24
|
+
origin: "top-level",
|
|
25
|
+
baseDir: `/tmp/${name}`,
|
|
26
|
+
},
|
|
27
|
+
disableModelInvocation: false,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
test("normalizeSkillName lowercases and trims", () => {
|
|
32
|
+
assert.equal(normalizeSkillName(" React-Best "), "react-best");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("getInstalledSkills returns explicit override", () => {
|
|
36
|
+
const skills = [makeSkill("alpha"), makeSkill("beta")];
|
|
37
|
+
assert.deepEqual(getInstalledSkills(skills).map((s) => s.name), ["alpha", "beta"]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("snapshotInstalledSkillNames and detectNewlyInstalledSkills diff normalized names", () => {
|
|
41
|
+
const baseline = snapshotInstalledSkillNames([makeSkill("alpha")]);
|
|
42
|
+
const added = detectNewlyInstalledSkills(baseline, [makeSkill("alpha"), makeSkill("Beta")]);
|
|
43
|
+
assert.deepEqual(added, ["beta"]);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("getInstalledSkillNames maps skill names", () => {
|
|
47
|
+
assert.deepEqual(getInstalledSkillNames([makeSkill("tdd")]), ["tdd"]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("resolveInstalledSkill matches installed catalog by name", () => {
|
|
51
|
+
const skills = [makeSkill("react", "/home/user/.gsd/agent/skills/react/SKILL.md")];
|
|
52
|
+
const result = resolveInstalledSkill("react", "/project", skills);
|
|
53
|
+
assert.equal(result.skill?.name, "react");
|
|
54
|
+
assert.equal(result.resolvedPath, "/home/user/.gsd/agent/skills/react/SKILL.md");
|
|
55
|
+
});
|
|
@@ -15,6 +15,13 @@ function readGsdFile(relativePath: string): string {
|
|
|
15
15
|
return readFileSync(resolve(gsdDir, relativePath), "utf-8");
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
function firstIndexOfAny(source: string, needles: string[]): number {
|
|
19
|
+
const indexes = needles
|
|
20
|
+
.map((needle) => source.indexOf(needle))
|
|
21
|
+
.filter((index) => index > -1);
|
|
22
|
+
return indexes.length > 0 ? Math.min(...indexes) : -1;
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
test("command entrypoints use startAutoDetached instead of awaiting startAuto (#3733)", () => {
|
|
19
26
|
const autoHandlerSrc = readGsdFile("commands/handlers/auto.ts");
|
|
20
27
|
const workflowHandlerSrc = readGsdFile("commands/handlers/workflow.ts");
|
|
@@ -145,7 +152,11 @@ test("fresh start registers the auto worker before bootstrap enters worktree flo
|
|
|
145
152
|
const resumeEnterMilestoneIdx = resumeBody.indexOf("buildLifecycle().enterMilestone");
|
|
146
153
|
const dbOpenIdx = bootstrapBody.indexOf("await openProjectDbIfPresent(base);");
|
|
147
154
|
const bootstrapRegisterIdx = bootstrapBody.indexOf("registerAutoWorkerForSession(base);");
|
|
148
|
-
const enterMilestoneIdx = bootstrapBody
|
|
155
|
+
const enterMilestoneIdx = firstIndexOfAny(bootstrapBody, [
|
|
156
|
+
"buildLifecycle().enterMilestone",
|
|
157
|
+
"lifecycle.enterMilestone",
|
|
158
|
+
"lifecycle.adoptStrandedMilestone",
|
|
159
|
+
]);
|
|
149
160
|
|
|
150
161
|
assert.ok(startAutoIdx > -1, "startAuto should exist");
|
|
151
162
|
assert.ok(preBootstrapRegisterIdx > -1, "startAuto should register worker before bootstrap");
|
|
@@ -158,7 +169,7 @@ test("fresh start registers the auto worker before bootstrap enters worktree flo
|
|
|
158
169
|
assert.ok(bootstrapIdx > -1, "bootstrapAutoSession should exist");
|
|
159
170
|
assert.ok(dbOpenIdx > -1, "bootstrap should open the project DB");
|
|
160
171
|
assert.ok(bootstrapRegisterIdx > -1, "bootstrap should register worker after DB open");
|
|
161
|
-
assert.ok(enterMilestoneIdx > -1, "bootstrap should enter milestones through lifecycle");
|
|
172
|
+
assert.ok(enterMilestoneIdx > -1, "bootstrap should enter or adopt milestones through lifecycle");
|
|
162
173
|
assert.ok(
|
|
163
174
|
preBootstrapRegisterIdx < bootstrapCallIdx,
|
|
164
175
|
"worker registration must happen before bootstrap so enterMilestone can claim milestone leases on first entry",
|
|
@@ -17,6 +17,8 @@ import { randomUUID } from "node:crypto";
|
|
|
17
17
|
import {
|
|
18
18
|
openDatabase,
|
|
19
19
|
closeDatabase,
|
|
20
|
+
_getAdapter,
|
|
21
|
+
insertArtifact,
|
|
20
22
|
insertMilestone,
|
|
21
23
|
insertSlice,
|
|
22
24
|
insertTask,
|
|
@@ -229,6 +231,175 @@ test("ADR-017 (#5700): classifyFailure recognizes ReconciliationFailedError", ()
|
|
|
229
231
|
assert.match(result.remediation, /persistent or repair-failed drift kinds/);
|
|
230
232
|
});
|
|
231
233
|
|
|
234
|
+
test("ADR-017: terminal drift blockers return blockers instead of repair exceptions", async () => {
|
|
235
|
+
const record: DriftRecord = { kind: "stale-sketch-flag", mid: "M001", sid: "S02" };
|
|
236
|
+
const handler: DriftHandler = {
|
|
237
|
+
kind: "stale-sketch-flag",
|
|
238
|
+
detect: () => [record],
|
|
239
|
+
blocker: () => "manual drift review required",
|
|
240
|
+
repair: () => {
|
|
241
|
+
throw new Error("repair should not run for terminal blockers");
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const result = await reconcileBeforeDispatch("/project", {
|
|
246
|
+
invalidateStateCache: () => {},
|
|
247
|
+
deriveState: async () => makeState(),
|
|
248
|
+
registry: [handler],
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
assert.equal(result.ok, true);
|
|
252
|
+
assert.deepEqual(result.blockers, ["manual drift review required"]);
|
|
253
|
+
assert.equal(result.repaired.length, 0);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("ADR-017: terminal blockers return a state snapshot refreshed after co-occurring repairs", async () => {
|
|
257
|
+
const repairDrift: DriftRecord = { kind: "stale-sketch-flag", mid: "M001", sid: "S02" };
|
|
258
|
+
const terminalDrift: DriftRecord = {
|
|
259
|
+
kind: "completed-milestone-reopened",
|
|
260
|
+
milestoneId: "M001",
|
|
261
|
+
dbStatus: "active",
|
|
262
|
+
};
|
|
263
|
+
let repaired = false;
|
|
264
|
+
const repairHandler: DriftHandler = {
|
|
265
|
+
kind: "stale-sketch-flag",
|
|
266
|
+
detect: (state) => (state.nextAction === "before repair" ? [repairDrift] : []),
|
|
267
|
+
repair: () => {
|
|
268
|
+
repaired = true;
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
const terminalHandler: DriftHandler = {
|
|
272
|
+
kind: "completed-milestone-reopened",
|
|
273
|
+
detect: (state) => (state.nextAction === "before repair" ? [terminalDrift] : []),
|
|
274
|
+
blocker: () => "manual completed-milestone review required",
|
|
275
|
+
repair: () => {
|
|
276
|
+
throw new Error("repair should not run for terminal blockers");
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const result = await reconcileBeforeDispatch("/project", {
|
|
281
|
+
invalidateStateCache: () => {},
|
|
282
|
+
deriveState: async () =>
|
|
283
|
+
makeState({ nextAction: repaired ? "after repair" : "before repair" }),
|
|
284
|
+
registry: [repairHandler, terminalHandler],
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
assert.equal(result.ok, true);
|
|
288
|
+
assert.equal(result.stateSnapshot.nextAction, "after repair");
|
|
289
|
+
assert.equal(result.repaired.length, 1);
|
|
290
|
+
assert.deepEqual(result.blockers, ["manual completed-milestone review required"]);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test("ADR-017: terminal drift blockers take precedence over co-occurring repair failures", async () => {
|
|
294
|
+
const terminalDrift: DriftRecord = {
|
|
295
|
+
kind: "completed-milestone-reopened",
|
|
296
|
+
milestoneId: "M001",
|
|
297
|
+
dbStatus: "active",
|
|
298
|
+
};
|
|
299
|
+
const repairDrift: DriftRecord = { kind: "stale-sketch-flag", mid: "M001", sid: "S02" };
|
|
300
|
+
const terminalHandler: DriftHandler = {
|
|
301
|
+
kind: "completed-milestone-reopened",
|
|
302
|
+
detect: () => [terminalDrift],
|
|
303
|
+
blocker: () => "manual completed-milestone review required",
|
|
304
|
+
repair: () => {
|
|
305
|
+
throw new Error("repair should not run for terminal blockers");
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
const repairHandler: DriftHandler = {
|
|
309
|
+
kind: "stale-sketch-flag",
|
|
310
|
+
detect: () => [repairDrift],
|
|
311
|
+
repair: () => {
|
|
312
|
+
throw new Error("simulated repair failure");
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const result = await reconcileBeforeDispatch("/project", {
|
|
317
|
+
invalidateStateCache: () => {},
|
|
318
|
+
deriveState: async () => makeState(),
|
|
319
|
+
registry: [repairHandler, terminalHandler],
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
assert.equal(result.ok, true);
|
|
323
|
+
assert.deepEqual(result.blockers, ["manual completed-milestone review required"]);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test("ADR-017: terminal drift found after repair cap returns blockers", async () => {
|
|
327
|
+
const repairDrift: DriftRecord = { kind: "stale-sketch-flag", mid: "M001", sid: "S02" };
|
|
328
|
+
const terminalDrift: DriftRecord = {
|
|
329
|
+
kind: "completed-milestone-reopened",
|
|
330
|
+
milestoneId: "M001",
|
|
331
|
+
dbStatus: "active",
|
|
332
|
+
};
|
|
333
|
+
let repairCount = 0;
|
|
334
|
+
const repairHandler: DriftHandler = {
|
|
335
|
+
kind: "stale-sketch-flag",
|
|
336
|
+
detect: () => (repairCount < 2 ? [repairDrift] : []),
|
|
337
|
+
repair: () => {
|
|
338
|
+
repairCount++;
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
const terminalHandler: DriftHandler = {
|
|
342
|
+
kind: "completed-milestone-reopened",
|
|
343
|
+
detect: () => (repairCount >= 2 ? [terminalDrift] : []),
|
|
344
|
+
blocker: () => "manual completed-milestone review required",
|
|
345
|
+
repair: () => {
|
|
346
|
+
throw new Error("repair should not run for terminal blockers");
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const result = await reconcileBeforeDispatch("/project", {
|
|
351
|
+
invalidateStateCache: () => {},
|
|
352
|
+
deriveState: async () => makeState(),
|
|
353
|
+
registry: [repairHandler, terminalHandler],
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
assert.equal(result.ok, true);
|
|
357
|
+
assert.equal(result.repaired.length, 2);
|
|
358
|
+
assert.deepEqual(result.blockers, ["manual completed-milestone review required"]);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
test("ADR-017: final persistent drift mixed with blockers still fails closed", async () => {
|
|
362
|
+
const repairDrift: DriftRecord = { kind: "stale-sketch-flag", mid: "M001", sid: "S02" };
|
|
363
|
+
const terminalDrift: DriftRecord = {
|
|
364
|
+
kind: "completed-milestone-reopened",
|
|
365
|
+
milestoneId: "M001",
|
|
366
|
+
dbStatus: "active",
|
|
367
|
+
};
|
|
368
|
+
let repairCount = 0;
|
|
369
|
+
const repairHandler: DriftHandler = {
|
|
370
|
+
kind: "stale-sketch-flag",
|
|
371
|
+
detect: () => [repairDrift],
|
|
372
|
+
repair: () => {
|
|
373
|
+
repairCount++;
|
|
374
|
+
},
|
|
375
|
+
};
|
|
376
|
+
const terminalHandler: DriftHandler = {
|
|
377
|
+
kind: "completed-milestone-reopened",
|
|
378
|
+
detect: () => (repairCount >= 2 ? [terminalDrift] : []),
|
|
379
|
+
blocker: () => "manual completed-milestone review required",
|
|
380
|
+
repair: () => {
|
|
381
|
+
throw new Error("repair should not run for terminal blockers");
|
|
382
|
+
},
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
await assert.rejects(
|
|
386
|
+
() =>
|
|
387
|
+
reconcileBeforeDispatch("/project", {
|
|
388
|
+
invalidateStateCache: () => {},
|
|
389
|
+
deriveState: async () => makeState(),
|
|
390
|
+
registry: [repairHandler, terminalHandler],
|
|
391
|
+
}),
|
|
392
|
+
(err: unknown) => {
|
|
393
|
+
assert.ok(err instanceof ReconciliationFailedError);
|
|
394
|
+
assert.deepEqual(err.persistentDrift.map((d) => d.kind).sort(), [
|
|
395
|
+
"completed-milestone-reopened",
|
|
396
|
+
"stale-sketch-flag",
|
|
397
|
+
]);
|
|
398
|
+
return true;
|
|
399
|
+
},
|
|
400
|
+
);
|
|
401
|
+
});
|
|
402
|
+
|
|
232
403
|
// ─── #5701: merge-state drift ────────────────────────────────────────────────
|
|
233
404
|
|
|
234
405
|
function makeGitBase(): string {
|
|
@@ -1052,6 +1223,138 @@ test("ADR-017 (#5706): repair is idempotent — re-running preserves the timesta
|
|
|
1052
1223
|
assert.equal(getSlice("M001", "S01")?.completed_at, tsAfterFirst, "timestamp unchanged");
|
|
1053
1224
|
});
|
|
1054
1225
|
|
|
1226
|
+
test("ADR-017: artifact/DB status divergence fails closed instead of importing completion artifacts", async (t) => {
|
|
1227
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-artifact-db-drift-"));
|
|
1228
|
+
t.after(() => cleanup(base));
|
|
1229
|
+
|
|
1230
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01"), { recursive: true });
|
|
1231
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
1232
|
+
insertMilestone({ id: "M001", title: "Milestone", status: "active" });
|
|
1233
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "pending" });
|
|
1234
|
+
writeFileSync(
|
|
1235
|
+
join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-SUMMARY.md"),
|
|
1236
|
+
"# S01 Summary\n\nAlready done on disk.\n",
|
|
1237
|
+
);
|
|
1238
|
+
|
|
1239
|
+
const result = await reconcileBeforeDispatch(base, {
|
|
1240
|
+
invalidateStateCache: () => {},
|
|
1241
|
+
deriveState: async () => makeState({ activeMilestone: { id: "M001", title: "Milestone" } }),
|
|
1242
|
+
});
|
|
1243
|
+
|
|
1244
|
+
assert.equal(result.ok, true);
|
|
1245
|
+
assert.match(result.blockers.join("\n"), /Artifact\/DB status drift/);
|
|
1246
|
+
assert.equal(getSlice("M001", "S01")?.status, "pending", "DB status remains authoritative");
|
|
1247
|
+
});
|
|
1248
|
+
|
|
1249
|
+
test("ADR-017: meaningful disk-only slice drift blocker includes repair guidance", async (t) => {
|
|
1250
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-disk-slice-guidance-"));
|
|
1251
|
+
const diskOnlySliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S99");
|
|
1252
|
+
t.after(() => cleanup(base));
|
|
1253
|
+
|
|
1254
|
+
mkdirSync(diskOnlySliceDir, { recursive: true });
|
|
1255
|
+
writeFileSync(join(diskOnlySliceDir, "S99-PLAN.md"), "# Disk-only plan\n\nWork to review.\n");
|
|
1256
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
1257
|
+
insertMilestone({ id: "M001", title: "Milestone", status: "active" });
|
|
1258
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Known Slice", status: "pending" });
|
|
1259
|
+
|
|
1260
|
+
const result = await reconcileBeforeDispatch(base, {
|
|
1261
|
+
invalidateStateCache: () => {},
|
|
1262
|
+
deriveState: async () => makeState({ activeMilestone: { id: "M001", title: "Milestone" } }),
|
|
1263
|
+
});
|
|
1264
|
+
|
|
1265
|
+
assert.equal(result.ok, true);
|
|
1266
|
+
const message = result.blockers.join("\n");
|
|
1267
|
+
assert.match(message, /Slice ID drift in M001/);
|
|
1268
|
+
assert.match(message, /Review .*S99/);
|
|
1269
|
+
assert.match(message, /move or delete/);
|
|
1270
|
+
assert.match(message, /\.gsd\/quarantine\/milestones\/M001\/slices\/S99-manual-review/);
|
|
1271
|
+
assert.match(message, /copy or merge/);
|
|
1272
|
+
assert.match(message, /\/gsd doctor M001/);
|
|
1273
|
+
assert.match(message, /\/gsd next or \/gsd auto/);
|
|
1274
|
+
});
|
|
1275
|
+
|
|
1276
|
+
test("ADR-017: orphan task completion artifact fails closed", async (t) => {
|
|
1277
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-orphan-task-artifact-drift-"));
|
|
1278
|
+
t.after(() => cleanup(base));
|
|
1279
|
+
|
|
1280
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks", "T99"), { recursive: true });
|
|
1281
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
1282
|
+
insertMilestone({ id: "M001", title: "Milestone", status: "active" });
|
|
1283
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "pending" });
|
|
1284
|
+
insertTask({ id: "T01", sliceId: "S01", milestoneId: "M001", title: "Task", status: "pending" });
|
|
1285
|
+
insertArtifact({
|
|
1286
|
+
path: join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks", "T99", "T99-SUMMARY.md"),
|
|
1287
|
+
artifact_type: "SUMMARY",
|
|
1288
|
+
milestone_id: "M001",
|
|
1289
|
+
slice_id: "S01",
|
|
1290
|
+
task_id: "T99",
|
|
1291
|
+
full_content: "# T99 Summary\n\nStale artifact after replan.\n",
|
|
1292
|
+
});
|
|
1293
|
+
|
|
1294
|
+
const result = await reconcileBeforeDispatch(base, {
|
|
1295
|
+
invalidateStateCache: () => {},
|
|
1296
|
+
deriveState: async () => makeState({ activeMilestone: { id: "M001", title: "Milestone" } }),
|
|
1297
|
+
});
|
|
1298
|
+
|
|
1299
|
+
assert.equal(result.ok, true);
|
|
1300
|
+
assert.match(result.blockers.join("\n"), /Artifact\/DB status drift/);
|
|
1301
|
+
});
|
|
1302
|
+
|
|
1303
|
+
test("ADR-017: completed milestone dispatch history blocks accidental re-planning", async (t) => {
|
|
1304
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-completed-reopened-drift-"));
|
|
1305
|
+
t.after(() => cleanup(base));
|
|
1306
|
+
|
|
1307
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
1308
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
1309
|
+
insertMilestone({ id: "M001", title: "Milestone", status: "active" });
|
|
1310
|
+
|
|
1311
|
+
const adapter = _getAdapter();
|
|
1312
|
+
assert.ok(adapter);
|
|
1313
|
+
adapter.prepare(
|
|
1314
|
+
`INSERT OR REPLACE INTO workers
|
|
1315
|
+
(worker_id, host, pid, started_at, version, last_heartbeat_at, status, project_root_realpath)
|
|
1316
|
+
VALUES ('w1', 'local', 1, '2026-05-30T00:00:00.000Z', 'test', '2026-05-30T00:00:00.000Z', 'stopped', :root)`,
|
|
1317
|
+
).run({ ":root": base });
|
|
1318
|
+
adapter.prepare(
|
|
1319
|
+
`INSERT INTO unit_dispatches
|
|
1320
|
+
(trace_id, worker_id, milestone_lease_token, milestone_id, unit_type, unit_id, status, attempt_n, started_at, ended_at)
|
|
1321
|
+
VALUES
|
|
1322
|
+
('trace', 'w1', 1, 'M001', 'complete-milestone', 'M001', 'completed', 1, '2026-05-30T00:00:00.000Z', '2026-05-30T00:01:00.000Z')`,
|
|
1323
|
+
).run();
|
|
1324
|
+
|
|
1325
|
+
const result = await reconcileBeforeDispatch(base, {
|
|
1326
|
+
invalidateStateCache: () => {},
|
|
1327
|
+
deriveState: async () => makeState({ activeMilestone: { id: "M001", title: "Milestone" } }),
|
|
1328
|
+
});
|
|
1329
|
+
|
|
1330
|
+
assert.equal(result.ok, true);
|
|
1331
|
+
assert.match(result.blockers.join("\n"), /completed complete-milestone dispatch history/);
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1334
|
+
test("ADR-017: synthetic parallel-research slice directory is ignored", async (t) => {
|
|
1335
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-parallel-sentinel-drift-"));
|
|
1336
|
+
t.after(() => cleanup(base));
|
|
1337
|
+
|
|
1338
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01"), { recursive: true });
|
|
1339
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "parallel-research", "tasks"), { recursive: true });
|
|
1340
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
1341
|
+
insertMilestone({ id: "M001", title: "Milestone", status: "active" });
|
|
1342
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "pending" });
|
|
1343
|
+
|
|
1344
|
+
const result = await reconcileBeforeDispatch(base, {
|
|
1345
|
+
invalidateStateCache: () => {},
|
|
1346
|
+
deriveState: async () => makeState({ activeMilestone: { id: "M001", title: "Milestone" } }),
|
|
1347
|
+
});
|
|
1348
|
+
|
|
1349
|
+
assert.equal(result.ok, true);
|
|
1350
|
+
assert.equal(
|
|
1351
|
+
existsSync(join(base, ".gsd", "milestones", "M001", "slices", "parallel-research")),
|
|
1352
|
+
true,
|
|
1353
|
+
"sentinel directory is left alone, not treated as a real disk-only slice",
|
|
1354
|
+
);
|
|
1355
|
+
assert.equal(result.repaired.some((record) => record.kind === "disk-slice-id-divergence"), false);
|
|
1356
|
+
});
|
|
1357
|
+
|
|
1055
1358
|
// ─── #5707: caller closure (reconcileBeforeSpawn) ────────────────────────────
|
|
1056
1359
|
|
|
1057
1360
|
test("ADR-017 (#5707): reconcileBeforeSpawn returns ok=true on clean reconciliation", async () => {
|