@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
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
scanSessionTokenTotals,
|
|
9
9
|
handleUsage,
|
|
10
10
|
} from "../commands-usage.ts";
|
|
11
|
+
import { assertFullOuterBorder } from "./tui-border-assertions.ts";
|
|
11
12
|
|
|
12
13
|
const TS = 1;
|
|
13
14
|
|
|
@@ -108,3 +109,99 @@ test("handleUsage emits JSON when --json is passed", async () => {
|
|
|
108
109
|
assert.equal(parsed.contextUsage.tokens, 10_000);
|
|
109
110
|
assert.equal(parsed.sessionTotals.input, 0);
|
|
110
111
|
});
|
|
112
|
+
|
|
113
|
+
test("handleUsage renders interactive usage output inside a full border", async () => {
|
|
114
|
+
let renderFn: ((width: number) => string[]) | undefined;
|
|
115
|
+
const messages: string[] = [];
|
|
116
|
+
const ctx = {
|
|
117
|
+
hasUI: true,
|
|
118
|
+
model: { provider: "claude-code", id: "claude-sonnet-4-6", contextWindow: 200_000 },
|
|
119
|
+
getContextUsage: () => ({ tokens: 10_000, contextWindow: 200_000, percent: 5 }),
|
|
120
|
+
sessionManager: { getEntries: () => [] },
|
|
121
|
+
ui: {
|
|
122
|
+
custom: async (factory: any) => {
|
|
123
|
+
const theme = {
|
|
124
|
+
fg: (_color: string, text: string) => text,
|
|
125
|
+
bold: (text: string) => text,
|
|
126
|
+
};
|
|
127
|
+
const component = factory({ requestRender: () => {} }, theme, {}, () => {});
|
|
128
|
+
renderFn = component.render;
|
|
129
|
+
return true;
|
|
130
|
+
},
|
|
131
|
+
notify(message: string) {
|
|
132
|
+
messages.push(message);
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
await handleUsage("", ctx as any);
|
|
138
|
+
|
|
139
|
+
assert.equal(messages.length, 0, "interactive usage should use the dialog instead of notify");
|
|
140
|
+
assert.ok(renderFn, "render function should have been captured");
|
|
141
|
+
const lines = renderFn!(80);
|
|
142
|
+
assertFullOuterBorder(lines, 80);
|
|
143
|
+
assert.match(lines.join("\n"), /claude-code\/claude-sonnet-4-6/);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("handleUsage keeps short-terminal usage dialog scrollable", async () => {
|
|
147
|
+
const originalRowsDescriptor = Object.getOwnPropertyDescriptor(process.stdout, "rows");
|
|
148
|
+
Object.defineProperty(process.stdout, "rows", { value: 10, configurable: true });
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
let component: { render(width: number): string[]; handleInput(data: string): void } | undefined;
|
|
152
|
+
let closed = false;
|
|
153
|
+
const ctx = {
|
|
154
|
+
hasUI: true,
|
|
155
|
+
model: { provider: "claude-code", id: "claude-sonnet-4-6", contextWindow: 200_000 },
|
|
156
|
+
getContextUsage: () => ({ tokens: 10_000, contextWindow: 200_000, percent: 5 }),
|
|
157
|
+
sessionManager: {
|
|
158
|
+
getEntries: () => sessionEntries({
|
|
159
|
+
role: "assistant",
|
|
160
|
+
usage: {
|
|
161
|
+
input: 1000,
|
|
162
|
+
output: 200,
|
|
163
|
+
cacheRead: 500,
|
|
164
|
+
cacheWrite: 100,
|
|
165
|
+
totalTokens: 1800,
|
|
166
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0.05 },
|
|
167
|
+
},
|
|
168
|
+
content: [{ type: "toolCall", id: "tc-1", name: "read", arguments: {} }],
|
|
169
|
+
timestamp: TS,
|
|
170
|
+
}),
|
|
171
|
+
},
|
|
172
|
+
ui: {
|
|
173
|
+
custom: async (factory: any) => {
|
|
174
|
+
const theme = {
|
|
175
|
+
fg: (_color: string, text: string) => text,
|
|
176
|
+
bold: (text: string) => text,
|
|
177
|
+
};
|
|
178
|
+
component = factory({ requestRender: () => {} }, theme, {}, () => {
|
|
179
|
+
closed = true;
|
|
180
|
+
});
|
|
181
|
+
return true;
|
|
182
|
+
},
|
|
183
|
+
notify() {},
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
await handleUsage("", ctx as any);
|
|
188
|
+
|
|
189
|
+
assert.ok(component, "usage dialog should render via custom UI");
|
|
190
|
+
const initialLines = component.render(80);
|
|
191
|
+
assert.ok(initialLines.length <= 8, `usage dialog should fit 80% terminal height, got ${initialLines.length}`);
|
|
192
|
+
|
|
193
|
+
for (let i = 0; i < 10; i++) component.handleInput("\u001b[B");
|
|
194
|
+
|
|
195
|
+
assert.equal(closed, false, "scroll keys should not close a scrollable usage dialog");
|
|
196
|
+
assert.match(component.render(80).join("\n"), /Tool calls: 1/);
|
|
197
|
+
|
|
198
|
+
component.handleInput("x");
|
|
199
|
+
assert.equal(closed, true, "non-scroll keys should close the usage dialog");
|
|
200
|
+
} finally {
|
|
201
|
+
if (originalRowsDescriptor) {
|
|
202
|
+
Object.defineProperty(process.stdout, "rows", originalRowsDescriptor);
|
|
203
|
+
} else {
|
|
204
|
+
delete (process.stdout as { rows?: number }).rows;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import test from "node:test";
|
|
4
4
|
import assert from "node:assert/strict";
|
|
5
|
-
import { mkdirSync } from "node:fs";
|
|
5
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
|
|
8
8
|
import { postUnitPreVerification } from "../auto-post-unit.ts";
|
|
@@ -70,7 +70,9 @@ test("complete-slice with gsd_task_reopen handoff continues instead of artifact-
|
|
|
70
70
|
test("complete-slice with gsd_replan_slice tool result continues instead of artifact-retrying", async () => {
|
|
71
71
|
const base = makeTempRepo("gsd-complete-slice-replan-");
|
|
72
72
|
try {
|
|
73
|
-
|
|
73
|
+
const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
|
|
74
|
+
mkdirSync(sliceDir, { recursive: true });
|
|
75
|
+
writeFileSync(join(sliceDir, "S01-REPLAN.md"), "# Replan\n");
|
|
74
76
|
|
|
75
77
|
const s = new AutoSession();
|
|
76
78
|
s.active = true;
|
|
@@ -101,10 +103,45 @@ test("complete-slice with gsd_replan_slice tool result continues instead of arti
|
|
|
101
103
|
assert.equal(s.pendingVerificationRetry, null);
|
|
102
104
|
assert.equal(s.verificationRetryCount.has(retryKey), false);
|
|
103
105
|
assert.ok(
|
|
104
|
-
notifications.some((message) => message.includes("
|
|
106
|
+
notifications.some((message) => message.includes("valid replan outcome")),
|
|
105
107
|
`expected handoff notification, got: ${notifications.join("\n")}`,
|
|
106
108
|
);
|
|
107
109
|
} finally {
|
|
108
110
|
cleanup(base);
|
|
109
111
|
}
|
|
110
112
|
});
|
|
113
|
+
|
|
114
|
+
test("complete-slice with gsd_replan_slice but no REPLAN artifact retries", async () => {
|
|
115
|
+
const base = makeTempRepo("gsd-complete-slice-replan-missing-artifact-");
|
|
116
|
+
try {
|
|
117
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01"), { recursive: true });
|
|
118
|
+
|
|
119
|
+
const s = new AutoSession();
|
|
120
|
+
s.active = true;
|
|
121
|
+
s.basePath = base;
|
|
122
|
+
s.currentUnit = { type: "complete-slice", id: "M001/S01", startedAt: Date.now() };
|
|
123
|
+
|
|
124
|
+
const notifications: string[] = [];
|
|
125
|
+
const result = await postUnitPreVerification(
|
|
126
|
+
makePostUnitContext(base, s, notifications),
|
|
127
|
+
{
|
|
128
|
+
skipSettleDelay: true,
|
|
129
|
+
skipWorktreeSync: true,
|
|
130
|
+
agentEndMessages: [
|
|
131
|
+
{
|
|
132
|
+
role: "toolResult",
|
|
133
|
+
toolName: "gsd_replan_slice",
|
|
134
|
+
isError: false,
|
|
135
|
+
content: "Slice replanned with reopened task T02.",
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
assert.equal(result, "retry");
|
|
142
|
+
assert.ok(s.pendingVerificationRetry);
|
|
143
|
+
assert.equal(s.pendingVerificationRetry?.unitId, "M001/S01");
|
|
144
|
+
} finally {
|
|
145
|
+
cleanup(base);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
@@ -10,6 +10,7 @@ import type { ContextBreakdownReport } from "../commands-context.ts";
|
|
|
10
10
|
import { buildContextChartHtml, writeContextChartHtml } from "../context-chart-html.ts";
|
|
11
11
|
import { formatContextChartText, getContextChartTotals } from "../context-overlay.ts";
|
|
12
12
|
import { buildContextBreakdown } from "../commands-context.ts";
|
|
13
|
+
import { assertFullOuterBorder } from "./tui-border-assertions.ts";
|
|
13
14
|
|
|
14
15
|
const TS = 1;
|
|
15
16
|
|
|
@@ -103,3 +104,11 @@ test("formatContextChartText includes chart sections", () => {
|
|
|
103
104
|
assert.match(text, /Conversation/);
|
|
104
105
|
assert.match(text, /█/);
|
|
105
106
|
});
|
|
107
|
+
|
|
108
|
+
test("formatContextChartText renders the context dialog inside a full border", () => {
|
|
109
|
+
const lines = formatContextChartText(SAMPLE_REPORT, 80).split("\n");
|
|
110
|
+
assertFullOuterBorder(lines, 80);
|
|
111
|
+
assert.match(lines[0] ?? "", /^╭─ Context Breakdown /);
|
|
112
|
+
assert.ok(lines.some((line) => line.startsWith("│")), "body rows should have side borders");
|
|
113
|
+
assert.match(lines.at(-1) ?? "", /^╰─+╯$/);
|
|
114
|
+
});
|
|
@@ -344,6 +344,70 @@ describe("Custom engine loop integration", () => {
|
|
|
344
344
|
);
|
|
345
345
|
});
|
|
346
346
|
|
|
347
|
+
it("step mode stops after one custom workflow step", async () => {
|
|
348
|
+
_resetPendingResolve();
|
|
349
|
+
|
|
350
|
+
const runDir = makeTmpDir();
|
|
351
|
+
const graph = makeGraph([
|
|
352
|
+
makeStep({ id: "step-a" }),
|
|
353
|
+
makeStep({ id: "step-b", dependsOn: ["step-a"] }),
|
|
354
|
+
makeStep({ id: "step-c", dependsOn: ["step-b"] }),
|
|
355
|
+
], "step-mode-custom");
|
|
356
|
+
writeGraph(runDir, graph);
|
|
357
|
+
writeDefinition(runDir, graph.steps, "step-mode-custom");
|
|
358
|
+
|
|
359
|
+
const ctx = makeMockCtx();
|
|
360
|
+
const pi = makeMockPi();
|
|
361
|
+
const s = makeLoopSession({
|
|
362
|
+
activeEngineId: "custom",
|
|
363
|
+
activeRunDir: runDir,
|
|
364
|
+
basePath: runDir,
|
|
365
|
+
stepMode: true,
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
const deps = makeMockDeps({
|
|
369
|
+
stopAuto: async (_ctx, _pi, reason) => {
|
|
370
|
+
deps.callLog.push(`stopAuto:${reason ?? "no-reason"}`);
|
|
371
|
+
s.active = false;
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
const loopPromise = autoLoop(ctx, pi, s, deps);
|
|
376
|
+
await resolveNextAgentEnd();
|
|
377
|
+
|
|
378
|
+
let timeout: NodeJS.Timeout | undefined;
|
|
379
|
+
try {
|
|
380
|
+
await Promise.race([
|
|
381
|
+
loopPromise,
|
|
382
|
+
new Promise((_, reject) =>
|
|
383
|
+
timeout = setTimeout(() => {
|
|
384
|
+
s.active = false;
|
|
385
|
+
if (_hasPendingResolveForTest()) {
|
|
386
|
+
resolveAgentEnd({ messages: [{ role: "assistant" }] });
|
|
387
|
+
}
|
|
388
|
+
reject(new Error(
|
|
389
|
+
`step mode did not stop after one custom workflow step; calls=${pi.calls.length}; log=${deps.callLog.join(",")}`,
|
|
390
|
+
));
|
|
391
|
+
}, 1_000),
|
|
392
|
+
),
|
|
393
|
+
]);
|
|
394
|
+
} finally {
|
|
395
|
+
if (timeout) clearTimeout(timeout);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const finalGraph = readGraph(runDir);
|
|
399
|
+
assert.equal(pi.calls.length, 1, "step mode should dispatch exactly one custom step");
|
|
400
|
+
assert.equal(finalGraph.steps[0]?.status, "complete", "first step should complete");
|
|
401
|
+
assert.equal(finalGraph.steps[1]?.status, "pending", "second step should wait for the next /gsd next");
|
|
402
|
+
assert.equal(finalGraph.steps[2]?.status, "pending", "third step should wait for a later step");
|
|
403
|
+
assert.equal(
|
|
404
|
+
deps.callLog.some((e: string) => e.startsWith("stopAuto:")),
|
|
405
|
+
false,
|
|
406
|
+
"step-mode pause should not complete or stop the whole workflow",
|
|
407
|
+
);
|
|
408
|
+
assert.equal(s.preserveStepSurfaceAfterLoopExit, true);
|
|
409
|
+
});
|
|
410
|
+
|
|
347
411
|
it("stops when engine reports isComplete on first derive", async () => {
|
|
348
412
|
_resetPendingResolve();
|
|
349
413
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSD dashboard overlay dialog chrome tests.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
import assert from "node:assert/strict";
|
|
7
|
+
|
|
8
|
+
import { GSDDashboardOverlay } from "../dashboard-overlay.ts";
|
|
9
|
+
import { assertFullOuterBorder } from "./tui-border-assertions.ts";
|
|
10
|
+
|
|
11
|
+
const fakeTheme = {
|
|
12
|
+
fg: (_color: string, text: string) => text,
|
|
13
|
+
bold: (text: string) => text,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
test("GSDDashboardOverlay renders inside the shared full border", (t) => {
|
|
17
|
+
const overlay = new GSDDashboardOverlay({ requestRender() {} }, fakeTheme as any, () => {});
|
|
18
|
+
t.after(() => overlay.dispose());
|
|
19
|
+
|
|
20
|
+
const lines = overlay.render(100);
|
|
21
|
+
assertFullOuterBorder(lines, 100);
|
|
22
|
+
assert.match(lines[0] ?? "", /^╭─ GSD Dashboard /);
|
|
23
|
+
assert.ok(lines.some((line) => line.startsWith("│")), "body rows should have side borders");
|
|
24
|
+
assert.match(lines.at(-1) ?? "", /^╰─+╯$/);
|
|
25
|
+
});
|
|
@@ -19,6 +19,7 @@ const VALID_INVITE = "https://discord.gg/8NnkKuepmQ";
|
|
|
19
19
|
const FILES_WITH_INVITE_LINKS: string[] = [
|
|
20
20
|
"README.md",
|
|
21
21
|
"docs/dev/what-is-pi/15-pi-packages-the-ecosystem.md",
|
|
22
|
+
"packages/pi-coding-agent/README.md",
|
|
22
23
|
];
|
|
23
24
|
|
|
24
25
|
describe("Discord invite links (#2699)", () => {
|
|
@@ -9,7 +9,9 @@ const discussPrompt = readFileSync(promptPath, "utf-8");
|
|
|
9
9
|
test("discuss prompt: resilient vision framing", () => {
|
|
10
10
|
const hardenedPattern = /Say exactly:\s*"What's the vision\?"/;
|
|
11
11
|
assert.ok(!hardenedPattern.test(discussPrompt), "prompt no longer uses exact-verbosity lock");
|
|
12
|
-
assert.ok(discussPrompt.includes('Ask
|
|
12
|
+
assert.ok(discussPrompt.includes('Ask exactly this once: "{{visionAsk}}"'), "prompt asks the injected vision opener exactly once");
|
|
13
|
+
assert.ok(discussPrompt.includes("The opener is intentionally variable"), "prompt documents variable opener voice");
|
|
13
14
|
assert.ok(discussPrompt.includes("Special handling"), "prompt documents special handling");
|
|
14
|
-
assert.ok(discussPrompt.includes(
|
|
15
|
+
assert.ok(discussPrompt.includes("instead of repeating the opener"), "prompt forbids repeating the opener");
|
|
16
|
+
assert.ok(!discussPrompt.includes('"What\'s the vision?"'), "prompt no longer freezes the old opener");
|
|
15
17
|
});
|
|
@@ -111,7 +111,7 @@ describe("discuss tool scoping (#2949)", () => {
|
|
|
111
111
|
setCalls.push([...tools]);
|
|
112
112
|
activeTools = [...tools];
|
|
113
113
|
},
|
|
114
|
-
sendMessage: (message: unknown) => {
|
|
114
|
+
sendMessage: async (message: unknown) => {
|
|
115
115
|
sent.push(message);
|
|
116
116
|
sentTools = [...activeTools];
|
|
117
117
|
},
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import test from "node:test";
|
|
5
5
|
import assert from "node:assert/strict";
|
|
6
6
|
import { execFileSync } from "node:child_process";
|
|
7
|
-
import { existsSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { existsSync, mkdirSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { tmpdir } from "node:os";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
|
|
@@ -30,6 +30,17 @@ function makeRepo(): string {
|
|
|
30
30
|
return base;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
function makeRepoWithOnlyGitignoreCommitted(): string {
|
|
34
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-doctor-empty-worktree-"));
|
|
35
|
+
runGit(["init", "-b", "main"], base);
|
|
36
|
+
runGit(["config", "user.name", "Test User"], base);
|
|
37
|
+
runGit(["config", "user.email", "test@example.com"], base);
|
|
38
|
+
writeFileSync(join(base, ".gitignore"), "node_modules\n", "utf-8");
|
|
39
|
+
runGit(["add", "."], base);
|
|
40
|
+
runGit(["commit", "-m", "chore: init gitignore"], base);
|
|
41
|
+
return base;
|
|
42
|
+
}
|
|
43
|
+
|
|
33
44
|
test("doctor fix recreates an empty registered milestone worktree", async (t) => {
|
|
34
45
|
const base = makeRepo();
|
|
35
46
|
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
@@ -63,3 +74,62 @@ test("doctor fix recreates an empty registered milestone worktree", async (t) =>
|
|
|
63
74
|
assert.ok(existsSync(join(wtPath, "package.json")), "worktree content is restored");
|
|
64
75
|
assert.ok(existsSync(join(wtPath, "milestone-note.txt")), "branch content is restored");
|
|
65
76
|
});
|
|
77
|
+
|
|
78
|
+
test("doctor fix recreates an empty worktree when cwd is inside that worktree", async (t) => {
|
|
79
|
+
const base = makeRepo();
|
|
80
|
+
const originalCwd = process.cwd();
|
|
81
|
+
t.after(() => {
|
|
82
|
+
try {
|
|
83
|
+
process.chdir(originalCwd);
|
|
84
|
+
} catch { /* ignore deleted cwd during cleanup */ }
|
|
85
|
+
rmSync(base, { recursive: true, force: true });
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
createWorktree(base, "M001", { branch: "milestone/M001" });
|
|
89
|
+
const wtPath = worktreePath(base, "M001");
|
|
90
|
+
for (const entry of readdirSync(wtPath)) {
|
|
91
|
+
if (entry === ".git") continue;
|
|
92
|
+
rmSync(join(wtPath, entry), { recursive: true, force: true });
|
|
93
|
+
}
|
|
94
|
+
process.chdir(wtPath);
|
|
95
|
+
|
|
96
|
+
const report = await runGSDDoctor(base, {
|
|
97
|
+
fix: true,
|
|
98
|
+
fixLevel: "all",
|
|
99
|
+
isolationMode: "worktree",
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
assert.ok(
|
|
103
|
+
report.fixesApplied.some((fix) => fix.includes("recreated empty worktree")),
|
|
104
|
+
"doctor applies the repair while cwd is in the worktree",
|
|
105
|
+
);
|
|
106
|
+
assert.ok(existsSync(join(wtPath, "package.json")), "worktree content is restored");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("doctor fix imports untracked project-root content when the worktree only has git metadata", async (t) => {
|
|
110
|
+
const base = makeRepoWithOnlyGitignoreCommitted();
|
|
111
|
+
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
112
|
+
|
|
113
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
114
|
+
writeFileSync(join(base, ".gsd", "PREFERENCES.md"), "---\ngit:\n isolation: worktree\n---\n", "utf-8");
|
|
115
|
+
writeFileSync(join(base, "package.json"), "{\"scripts\":{}}\n", "utf-8");
|
|
116
|
+
createWorktree(base, "M001", { branch: "milestone/M001" });
|
|
117
|
+
const wtPath = worktreePath(base, "M001");
|
|
118
|
+
assert.ok(existsSync(join(wtPath, ".gitignore")), "test setup keeps committed non-content metadata");
|
|
119
|
+
assert.equal(existsSync(join(wtPath, "package.json")), false, "test setup leaves project content only at the root");
|
|
120
|
+
|
|
121
|
+
const report = await runGSDDoctor(base, {
|
|
122
|
+
fix: true,
|
|
123
|
+
fixLevel: "all",
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
assert.ok(
|
|
127
|
+
report.issues.some((issue) => issue.code === "worktree_empty_with_project_content"),
|
|
128
|
+
"doctor reports the empty worktree even when .gitignore exists",
|
|
129
|
+
);
|
|
130
|
+
assert.ok(
|
|
131
|
+
report.fixesApplied.some((fix) => fix.includes("copied") && fix.includes("project file")),
|
|
132
|
+
"doctor copies the root project content into the recreated worktree",
|
|
133
|
+
);
|
|
134
|
+
assert.ok(existsSync(join(wtPath, "package.json")), "untracked project content is restored into the worktree");
|
|
135
|
+
});
|
|
@@ -702,6 +702,48 @@ test("runProviderChecks reports ok for claude-code without any API key", () => {
|
|
|
702
702
|
rmSync(tmpHome, { recursive: true, force: true });
|
|
703
703
|
});
|
|
704
704
|
|
|
705
|
+
test("runProviderChecks reports errors for required Google CLI providers missing from PATH", () => {
|
|
706
|
+
const scenarios = [
|
|
707
|
+
{ provider: "google-gemini-cli", label: "Google Gemini CLI", model: "gemini-2.5-pro" },
|
|
708
|
+
{ provider: "google-antigravity", label: "Antigravity", model: "default" },
|
|
709
|
+
];
|
|
710
|
+
|
|
711
|
+
for (const { provider, label, model } of scenarios) {
|
|
712
|
+
const repo = realpathSync(mkdtempSync(join(tmpdir(), `gsd-providers-${provider}-repo-`)));
|
|
713
|
+
mkdirSync(join(repo, ".gsd"), { recursive: true });
|
|
714
|
+
writeFileSync(
|
|
715
|
+
join(repo, ".gsd", "PREFERENCES.md"),
|
|
716
|
+
[
|
|
717
|
+
"---",
|
|
718
|
+
"models:",
|
|
719
|
+
" execution:",
|
|
720
|
+
` model: ${model}`,
|
|
721
|
+
` provider: ${provider}`,
|
|
722
|
+
"---",
|
|
723
|
+
"",
|
|
724
|
+
].join("\n"),
|
|
725
|
+
);
|
|
726
|
+
|
|
727
|
+
const tmpHome = realpathSync(mkdtempSync(join(tmpdir(), `gsd-providers-${provider}-home-`)));
|
|
728
|
+
|
|
729
|
+
withEnv({
|
|
730
|
+
HOME: tmpHome,
|
|
731
|
+
PATH: tmpHome,
|
|
732
|
+
}, () => {
|
|
733
|
+
withCwd(repo, () => {
|
|
734
|
+
const results = runProviderChecks();
|
|
735
|
+
const cli = results.find(r => r.name === provider);
|
|
736
|
+
assert.ok(cli, `${provider} result should exist`);
|
|
737
|
+
assert.equal(cli!.status, "error", `${provider} should error when the CLI binary is missing`);
|
|
738
|
+
assert.ok(cli!.detail?.includes(label), "should explain which CLI must be installed");
|
|
739
|
+
});
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
rmSync(repo, { recursive: true, force: true });
|
|
743
|
+
rmSync(tmpHome, { recursive: true, force: true });
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
|
|
705
747
|
test("runProviderChecks reports ok for Anthropic via claude-code binary in PATH", () => {
|
|
706
748
|
// Simulate a user who has no Anthropic API key but has the claude CLI installed.
|
|
707
749
|
// Their PREFERENCES use a claude model without an explicit provider, so the doctor
|
|
@@ -736,6 +778,69 @@ test("runProviderChecks reports ok for Anthropic via claude-code binary in PATH"
|
|
|
736
778
|
});
|
|
737
779
|
});
|
|
738
780
|
|
|
781
|
+
test("runProviderChecks ignores external CLI auth sentinels when the CLI is missing", () => {
|
|
782
|
+
const scenarios = [
|
|
783
|
+
{
|
|
784
|
+
requiredProvider: "anthropic",
|
|
785
|
+
routeProvider: "claude-code",
|
|
786
|
+
model: "claude-sonnet-4-6",
|
|
787
|
+
env: {
|
|
788
|
+
ANTHROPIC_API_KEY: undefined,
|
|
789
|
+
ANTHROPIC_OAUTH_TOKEN: undefined,
|
|
790
|
+
COPILOT_GITHUB_TOKEN: undefined,
|
|
791
|
+
GH_TOKEN: undefined,
|
|
792
|
+
GITHUB_TOKEN: undefined,
|
|
793
|
+
},
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
requiredProvider: "google",
|
|
797
|
+
routeProvider: "google-gemini-cli",
|
|
798
|
+
model: "gemini-2.5-pro",
|
|
799
|
+
env: {
|
|
800
|
+
GEMINI_API_KEY: undefined,
|
|
801
|
+
GOOGLE_API_KEY: undefined,
|
|
802
|
+
},
|
|
803
|
+
},
|
|
804
|
+
];
|
|
805
|
+
|
|
806
|
+
for (const { requiredProvider, routeProvider, model, env } of scenarios) {
|
|
807
|
+
const repo = realpathSync(mkdtempSync(join(tmpdir(), `gsd-providers-${routeProvider}-sentinel-repo-`)));
|
|
808
|
+
const tmpHome = realpathSync(mkdtempSync(join(tmpdir(), `gsd-providers-${routeProvider}-sentinel-home-`)));
|
|
809
|
+
const agentDir = join(tmpHome, ".gsd", "agent");
|
|
810
|
+
mkdirSync(join(repo, ".gsd"), { recursive: true });
|
|
811
|
+
mkdirSync(agentDir, { recursive: true });
|
|
812
|
+
writeFileSync(
|
|
813
|
+
join(repo, ".gsd", "PREFERENCES.md"),
|
|
814
|
+
[
|
|
815
|
+
"---",
|
|
816
|
+
"models:",
|
|
817
|
+
` execution: ${model}`,
|
|
818
|
+
"---",
|
|
819
|
+
"",
|
|
820
|
+
].join("\n"),
|
|
821
|
+
);
|
|
822
|
+
writeFileSync(join(agentDir, "auth.json"), JSON.stringify({
|
|
823
|
+
[routeProvider]: { type: "api_key", key: "cli" },
|
|
824
|
+
}));
|
|
825
|
+
|
|
826
|
+
withEnv({
|
|
827
|
+
...env,
|
|
828
|
+
HOME: tmpHome,
|
|
829
|
+
PATH: tmpHome,
|
|
830
|
+
}, () => {
|
|
831
|
+
withCwd(repo, () => {
|
|
832
|
+
const results = runProviderChecks();
|
|
833
|
+
const provider = results.find(r => r.name === requiredProvider);
|
|
834
|
+
assert.ok(provider, `${requiredProvider} result should exist`);
|
|
835
|
+
assert.equal(provider!.status, "error", `${routeProvider} sentinel should not satisfy ${requiredProvider}`);
|
|
836
|
+
});
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
rmSync(repo, { recursive: true, force: true });
|
|
840
|
+
rmSync(tmpHome, { recursive: true, force: true });
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
|
|
739
844
|
test("runProviderChecks detects claude.cmd in PATH on Windows (#4503)", { skip: process.platform !== "win32" }, () => {
|
|
740
845
|
const tmpHome = realpathSync(mkdtempSync(join(tmpdir(), "gsd-providers-cc-win-route-home-")));
|
|
741
846
|
const binDir = join(tmpHome, "bin");
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { afterEach, test } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { closeDatabase } from "../gsd-db.ts";
|
|
3
|
+
import { _getAdapter, closeDatabase, insertMilestone, openDatabase } from "../gsd-db.ts";
|
|
4
4
|
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
import { filterDoctorIssues } from "../doctor-format.ts";
|
|
8
8
|
import { checkEngineHealth } from "../doctor-engine-checks.ts";
|
|
9
|
+
import { appendEvent } from "../workflow-events.ts";
|
|
9
10
|
|
|
10
11
|
afterEach(() => {
|
|
11
12
|
closeDatabase();
|
|
@@ -41,3 +42,102 @@ test("checkEngineHealth reports db_unavailable when gsd.db exists but the DB is
|
|
|
41
42
|
assert.equal(dbIssue.unitId, "project");
|
|
42
43
|
assert.equal(dbIssue.file, ".gsd/gsd.db");
|
|
43
44
|
});
|
|
45
|
+
|
|
46
|
+
test("checkEngineHealth reads canonical reopen events from worktree bases", async (t) => {
|
|
47
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-doctor-reopen-worktree-"));
|
|
48
|
+
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
49
|
+
|
|
50
|
+
const gsdDir = join(base, ".gsd");
|
|
51
|
+
const worktree = join(gsdDir, "worktrees", "M001");
|
|
52
|
+
mkdirSync(join(worktree, ".gsd"), { recursive: true });
|
|
53
|
+
writeFileSync(join(worktree, ".git"), "gitdir: ../../../../.git/worktrees/M001\n", "utf-8");
|
|
54
|
+
|
|
55
|
+
openDatabase(join(gsdDir, "gsd.db"));
|
|
56
|
+
insertMilestone({ id: "M001", title: "Reopened", status: "active" });
|
|
57
|
+
const db = _getAdapter()!;
|
|
58
|
+
db.prepare(
|
|
59
|
+
`INSERT INTO workers (
|
|
60
|
+
worker_id, host, pid, started_at, version, last_heartbeat_at, status, project_root_realpath
|
|
61
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
62
|
+
).run("worker-1", "localhost", 1, "2026-01-01T00:00:00.000Z", "test", "2026-01-01T00:00:00.000Z", "stopped", base);
|
|
63
|
+
db.prepare(
|
|
64
|
+
`INSERT INTO unit_dispatches (
|
|
65
|
+
trace_id, worker_id, milestone_lease_token, milestone_id,
|
|
66
|
+
unit_type, unit_id, status, attempt_n, started_at, ended_at
|
|
67
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
68
|
+
).run(
|
|
69
|
+
"trace-1",
|
|
70
|
+
"worker-1",
|
|
71
|
+
1,
|
|
72
|
+
"M001",
|
|
73
|
+
"complete-milestone",
|
|
74
|
+
"M001",
|
|
75
|
+
"completed",
|
|
76
|
+
1,
|
|
77
|
+
"2026-01-01T00:00:00.000Z",
|
|
78
|
+
"2026-01-01T00:00:01.000Z",
|
|
79
|
+
);
|
|
80
|
+
appendEvent(base, {
|
|
81
|
+
cmd: "reopen-milestone",
|
|
82
|
+
params: { milestoneId: "M001" },
|
|
83
|
+
ts: "2026-01-01T00:00:02.000Z",
|
|
84
|
+
actor: "agent",
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const issues: any[] = [];
|
|
88
|
+
await checkEngineHealth(worktree, issues, []);
|
|
89
|
+
|
|
90
|
+
assert.equal(
|
|
91
|
+
issues.some((issue) => issue.code === "completed_milestone_reopened"),
|
|
92
|
+
false,
|
|
93
|
+
"canonical reopen event should exempt the reopened milestone from doctor drift errors",
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("checkEngineHealth treats explicit reopen as authoritative when dispatch timestamps are missing", async (t) => {
|
|
98
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-doctor-reopen-no-dispatch-time-"));
|
|
99
|
+
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
100
|
+
|
|
101
|
+
const gsdDir = join(base, ".gsd");
|
|
102
|
+
mkdirSync(gsdDir, { recursive: true });
|
|
103
|
+
|
|
104
|
+
openDatabase(join(gsdDir, "gsd.db"));
|
|
105
|
+
insertMilestone({ id: "M001", title: "Reopened", status: "active" });
|
|
106
|
+
const db = _getAdapter()!;
|
|
107
|
+
db.prepare(
|
|
108
|
+
`INSERT INTO workers (
|
|
109
|
+
worker_id, host, pid, started_at, version, last_heartbeat_at, status, project_root_realpath
|
|
110
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
111
|
+
).run("worker-1", "localhost", 1, "2026-01-01T00:00:00.000Z", "test", "2026-01-01T00:00:00.000Z", "stopped", base);
|
|
112
|
+
db.exec("PRAGMA writable_schema = ON");
|
|
113
|
+
db.prepare(
|
|
114
|
+
`UPDATE sqlite_schema
|
|
115
|
+
SET sql = replace(sql, 'started_at TEXT NOT NULL', 'started_at TEXT')
|
|
116
|
+
WHERE type = 'table' AND name = 'unit_dispatches'`,
|
|
117
|
+
).run();
|
|
118
|
+
db.exec("PRAGMA writable_schema = OFF");
|
|
119
|
+
closeDatabase();
|
|
120
|
+
openDatabase(join(gsdDir, "gsd.db"));
|
|
121
|
+
const reopenedDb = _getAdapter()!;
|
|
122
|
+
reopenedDb.prepare(
|
|
123
|
+
`INSERT INTO unit_dispatches (
|
|
124
|
+
trace_id, worker_id, milestone_lease_token, milestone_id,
|
|
125
|
+
unit_type, unit_id, status, attempt_n, started_at, ended_at
|
|
126
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
127
|
+
).run("trace-1", "worker-1", 1, "M001", "complete-milestone", "M001", "completed", 1, null, null);
|
|
128
|
+
appendEvent(base, {
|
|
129
|
+
cmd: "reopen-milestone",
|
|
130
|
+
params: { milestoneId: "M001" },
|
|
131
|
+
ts: "2026-01-01T00:00:02.000Z",
|
|
132
|
+
actor: "agent",
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const issues: any[] = [];
|
|
136
|
+
await checkEngineHealth(base, issues, []);
|
|
137
|
+
|
|
138
|
+
assert.equal(
|
|
139
|
+
issues.some((issue) => issue.code === "completed_milestone_reopened"),
|
|
140
|
+
false,
|
|
141
|
+
"explicit reopen should exempt reopened milestone even when completion dispatch timestamps are absent",
|
|
142
|
+
);
|
|
143
|
+
});
|