@opengsd/gsd-pi 1.0.2-dev.867e002 → 1.0.2-dev.cce3612
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/dist/onboarding.js +22 -3
- package/dist/resources/.managed-resources-content-hash +1 -1
- 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 +62 -1
- package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -2
- package/dist/resources/extensions/gsd/auto/phases.js +37 -0
- 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 +18 -2
- 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.js +6 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -3
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +39 -5
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +17 -7
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +3 -27
- 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-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-providers.js +54 -24
- package/dist/resources/extensions/gsd/doctor.js +6 -1
- 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/milestone-reopen-events.js +28 -0
- package/dist/resources/extensions/gsd/notification-overlay.js +8 -9
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +15 -13
- 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/guided-discuss-milestone.md +2 -0
- package/dist/resources/extensions/gsd/prompts/system.md +1 -3
- package/dist/resources/extensions/gsd/queue-reorder-ui.js +28 -18
- package/dist/resources/extensions/gsd/repository-registry.js +3 -1
- 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.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/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/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/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
- package/dist/web/standalone/.next/server/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/package.json +1 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/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/transcript-design.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts +3 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +144 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js +2 -14
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/workflow-tools.js +1 -1
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/harness/skills.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/harness/skills.js +6 -0
- package/packages/pi-agent-core/dist/harness/skills.js.map +1 -1
- package/packages/pi-agent-core/dist/harness/system-prompt.d.ts +7 -0
- package/packages/pi-agent-core/dist/harness/system-prompt.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/harness/system-prompt.js +7 -0
- package/packages/pi-agent-core/dist/harness/system-prompt.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +8 -59
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +21 -72
- 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/package.json +1 -1
- package/packages/pi-coding-agent/README.md +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +8 -2
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +3 -0
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- 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 +74 -1
- package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -2
- package/src/resources/extensions/gsd/auto/phases.ts +46 -0
- 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 +37 -2
- 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.ts +6 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -3
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +42 -5
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +18 -6
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +3 -28
- 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-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-providers.ts +55 -27
- package/src/resources/extensions/gsd/doctor-types.ts +2 -0
- package/src/resources/extensions/gsd/doctor.ts +6 -1
- 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/milestone-reopen-events.ts +28 -0
- package/src/resources/extensions/gsd/notification-overlay.ts +12 -11
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +16 -12
- 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/guided-discuss-milestone.md +2 -0
- package/src/resources/extensions/gsd/prompts/system.md +1 -3
- package/src/resources/extensions/gsd/queue-reorder-ui.ts +29 -20
- package/src/resources/extensions/gsd/repository-registry.ts +3 -1
- 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.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 +21 -0
- package/src/resources/extensions/gsd/tests/auto-post-unit-artifact-diagnostic.test.ts +28 -2
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +41 -0
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +436 -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/context-chart.test.ts +9 -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-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/guided-discuss-milestone-prompt-rendering.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +23 -4
- 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/post-unit-retry-on-orchestrator-bridge.test.ts +93 -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/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 +19 -0
- 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 +74 -1
- 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/zombie-gsd-state.test.ts +45 -1
- package/src/resources/extensions/gsd/tools/complete-task.ts +9 -0
- 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/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/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
- /package/dist/web/standalone/.next/static/{praHP_OATcjBkvAVejjGK → orfEoZqDIo6Be_Z9ZFipD}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{praHP_OATcjBkvAVejjGK → orfEoZqDIo6Be_Z9ZFipD}/_ssgManifest.js +0 -0
|
@@ -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 () => {
|
|
@@ -6,6 +6,7 @@ import test from "node:test";
|
|
|
6
6
|
|
|
7
7
|
import { DISCUSS_TOOLS_ALLOWLIST } from "../constants.ts";
|
|
8
8
|
import { buildMinimalAutoGsdToolSet, buildMinimalGsdToolSet, buildMinimalGsdWorkflowToolSet, buildRequestScopedGsdToolSet, MINIMAL_AUTO_BASE_TOOL_NAMES, MINIMAL_GSD_TOOL_NAMES, restoreGsdWorkflowTools, scopeGsdWorkflowToolsForDispatch } from "../bootstrap/register-hooks.ts";
|
|
9
|
+
import { applyUnitSkillVisibility } from "../skill-scope.ts";
|
|
9
10
|
|
|
10
11
|
test("buildMinimalGsdToolSet preserves non-GSD tools and replaces broad GSD surface", () => {
|
|
11
12
|
const result = buildMinimalGsdToolSet([
|
|
@@ -435,3 +436,21 @@ test("scopeGsdWorkflowToolsForDispatch applies and restores per-unit skill visib
|
|
|
435
436
|
assert.deepEqual(visibleSkills, ["previous-skill"]);
|
|
436
437
|
assert.equal(calls.filter((call) => call.kind === "skills").length, 2);
|
|
437
438
|
});
|
|
439
|
+
|
|
440
|
+
test("applyUnitSkillVisibility sets manifest or clears for wildcard", () => {
|
|
441
|
+
const calls: Array<string[] | undefined> = [];
|
|
442
|
+
applyUnitSkillVisibility({
|
|
443
|
+
setVisibleSkills: (names) => {
|
|
444
|
+
calls.push(names);
|
|
445
|
+
},
|
|
446
|
+
}, "plan-milestone");
|
|
447
|
+
assert.ok(Array.isArray(calls[0]));
|
|
448
|
+
assert.ok(calls[0]!.includes("tdd"));
|
|
449
|
+
|
|
450
|
+
applyUnitSkillVisibility({
|
|
451
|
+
setVisibleSkills: (names) => {
|
|
452
|
+
calls.push(names);
|
|
453
|
+
},
|
|
454
|
+
}, "execute-task");
|
|
455
|
+
assert.equal(calls[1], undefined);
|
|
456
|
+
});
|
|
@@ -235,12 +235,16 @@ test("gsd_task_complete — enrichment arrays are optional", () => {
|
|
|
235
235
|
"milestoneId",
|
|
236
236
|
"oneLiner",
|
|
237
237
|
"narrative",
|
|
238
|
-
"verification",
|
|
239
238
|
];
|
|
240
239
|
for (const field of coreRequired) {
|
|
241
240
|
assert.ok(required.has(field), `core field "${field}" must be required`);
|
|
242
241
|
}
|
|
243
242
|
|
|
243
|
+
assert.ok(
|
|
244
|
+
!required.has("verification"),
|
|
245
|
+
"verification must be optional at the schema layer so step-mode can recover when verificationEvidence is present",
|
|
246
|
+
);
|
|
247
|
+
|
|
244
248
|
// Enrichment fields must be optional
|
|
245
249
|
const enrichmentFields = [
|
|
246
250
|
"keyFiles",
|
|
@@ -272,6 +276,25 @@ test("gsd_task_complete — validates with only core params", () => {
|
|
|
272
276
|
assert.strictEqual(errors.length, 0, `Minimal params should validate but got errors: ${errors.join(", ")}`);
|
|
273
277
|
});
|
|
274
278
|
|
|
279
|
+
test("gsd_task_complete — accepts evidence-only verification at schema layer", () => {
|
|
280
|
+
const tool = getTool("gsd_task_complete");
|
|
281
|
+
assert.ok(tool, "gsd_task_complete must be registered");
|
|
282
|
+
|
|
283
|
+
const params = {
|
|
284
|
+
taskId: "T01",
|
|
285
|
+
sliceId: "S01",
|
|
286
|
+
milestoneId: "M001",
|
|
287
|
+
oneLiner: "Implemented the feature",
|
|
288
|
+
narrative: "Created the module and wired it up.",
|
|
289
|
+
verificationEvidence: [
|
|
290
|
+
{ command: "npm test", exitCode: 0, verdict: "pass", durationMs: 1234 },
|
|
291
|
+
],
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const errors = validateSchema(tool, params);
|
|
295
|
+
assert.strictEqual(errors.length, 0, `Evidence-only params should validate but got errors: ${errors.join(", ")}`);
|
|
296
|
+
});
|
|
297
|
+
|
|
275
298
|
// ─── gsd_complete_milestone: enrichment arrays must be optional ──────────────
|
|
276
299
|
|
|
277
300
|
test("gsd_complete_milestone — enrichment arrays are optional", () => {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
|
|
3
|
+
import { visibleWidth } from "@gsd/pi-tui";
|
|
4
|
+
|
|
5
|
+
const ANSI_PATTERN = /\x1b\[[0-9;?]*[ -/]*[@-~]/g;
|
|
6
|
+
|
|
7
|
+
function stripAnsi(text: string): string {
|
|
8
|
+
return text.replace(ANSI_PATTERN, "");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function assertFullOuterBorder(lines: string[], width: number): void {
|
|
12
|
+
assert.ok(lines.length >= 2, "dialog must include top and bottom borders");
|
|
13
|
+
|
|
14
|
+
for (const [index, line] of lines.entries()) {
|
|
15
|
+
assert.equal(visibleWidth(line), width, `line ${index} must fill dialog width`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const top = stripAnsi(lines[0] ?? "");
|
|
19
|
+
const bottom = stripAnsi(lines.at(-1) ?? "");
|
|
20
|
+
assert.match(top, /^[╭┌].*[╮┐]$/, `top border missing full corners: ${top}`);
|
|
21
|
+
assert.match(bottom, /^[╰└].*[╯┘]$/, `bottom border missing full corners: ${bottom}`);
|
|
22
|
+
|
|
23
|
+
for (let index = 1; index < lines.length - 1; index++) {
|
|
24
|
+
const line = stripAnsi(lines[index] ?? "");
|
|
25
|
+
assert.match(line, /^[│┃├]/, `line ${index} missing left border: ${line}`);
|
|
26
|
+
assert.match(line, /[│┃┤]$/, `line ${index} missing right border: ${line}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -5,8 +5,10 @@ import { describe, test } from "node:test";
|
|
|
5
5
|
import assert from "node:assert/strict";
|
|
6
6
|
|
|
7
7
|
import { visibleWidth } from "@gsd/pi-tui";
|
|
8
|
+
import { assertFullOuterBorder } from "./tui-border-assertions.ts";
|
|
8
9
|
import {
|
|
9
10
|
padRightVisible,
|
|
11
|
+
renderDialogFrame,
|
|
10
12
|
renderFrame,
|
|
11
13
|
renderKeyHints,
|
|
12
14
|
renderPanel,
|
|
@@ -60,6 +62,18 @@ describe("tui render kit", () => {
|
|
|
60
62
|
}
|
|
61
63
|
});
|
|
62
64
|
|
|
65
|
+
test("renderDialogFrame draws a full titled modal border with footer", () => {
|
|
66
|
+
const lines = renderDialogFrame(theme, "Dialog", ["row", "long ".repeat(40)], 40, {
|
|
67
|
+
footer: renderKeyHints(theme, ["esc close"], 36),
|
|
68
|
+
});
|
|
69
|
+
assertWidth(lines, 40);
|
|
70
|
+
assertFullOuterBorder(lines, 40);
|
|
71
|
+
assert.match(lines[0] ?? "", /^╭─ Dialog ─+╮$/);
|
|
72
|
+
assert.ok(lines.some((line) => line.startsWith("│") && line.endsWith("│")));
|
|
73
|
+
assert.ok(lines.some((line) => line.startsWith("├") && line.endsWith("┤")));
|
|
74
|
+
assert.match(lines.at(-1) ?? "", /^╰─+╯$/);
|
|
75
|
+
});
|
|
76
|
+
|
|
63
77
|
test("renderPanel stays within width and draws no vertical borders", () => {
|
|
64
78
|
for (const width of [3, 40, 80]) {
|
|
65
79
|
const lines = renderPanel(theme, "Title", ["row", "long ".repeat(40)], width);
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
type SkillsPolicy,
|
|
15
15
|
type UnitContextManifest,
|
|
16
16
|
} from "../unit-context-manifest.ts";
|
|
17
|
+
import { resolveSkillManifest } from "../skill-manifest.ts";
|
|
17
18
|
import {
|
|
18
19
|
ALLOWED_PLANNING_DISPATCH_AGENTS,
|
|
19
20
|
shouldBlockPlanningUnit,
|
|
@@ -148,6 +149,23 @@ test("#4782 phase 1: skills policy shapes are valid discriminated-union members"
|
|
|
148
149
|
}
|
|
149
150
|
});
|
|
150
151
|
|
|
152
|
+
test("#4782 phase 1b: allowlist skills policy matches skill-manifest resolver", () => {
|
|
153
|
+
for (const unitType of KNOWN_UNIT_TYPES) {
|
|
154
|
+
const manifest = UNIT_MANIFESTS[unitType];
|
|
155
|
+
const allowlist = resolveSkillManifest(unitType);
|
|
156
|
+
if (allowlist) {
|
|
157
|
+
assert.strictEqual(manifest.skills.mode, "allowlist", unitType);
|
|
158
|
+
assert.deepEqual(
|
|
159
|
+
[...(manifest.skills as Extract<SkillsPolicy, { mode: "allowlist" }>).skills].sort(),
|
|
160
|
+
[...allowlist].sort(),
|
|
161
|
+
unitType,
|
|
162
|
+
);
|
|
163
|
+
} else {
|
|
164
|
+
assert.notStrictEqual(manifest.skills.mode, "allowlist", unitType);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
151
169
|
// ─── Lookup helper ────────────────────────────────────────────────────────
|
|
152
170
|
|
|
153
171
|
test("#4782 phase 1: resolveManifest returns null for an unknown unit type", () => {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import * as userInputBoundary from "../user-input-boundary.ts";
|
|
5
|
+
|
|
6
|
+
test("lastAssistantText extracts the latest assistant text block content", () => {
|
|
7
|
+
const lastAssistantText = (userInputBoundary as {
|
|
8
|
+
lastAssistantText?: (messages: unknown[] | null | undefined) => string;
|
|
9
|
+
}).lastAssistantText;
|
|
10
|
+
|
|
11
|
+
assert.equal(typeof lastAssistantText, "function");
|
|
12
|
+
assert.equal(
|
|
13
|
+
lastAssistantText?.([
|
|
14
|
+
{ role: "assistant", content: "Older message" },
|
|
15
|
+
{
|
|
16
|
+
role: "assistant",
|
|
17
|
+
content: [
|
|
18
|
+
{ type: "text", text: "First line" },
|
|
19
|
+
{ type: "text", text: "Second line" },
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
]),
|
|
23
|
+
"First line\nSecond line",
|
|
24
|
+
);
|
|
25
|
+
assert.equal(lastAssistantText?.(null), "");
|
|
26
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { chooseVisionAskVariant, VISION_ASK_VARIANTS } from "../vision-ask.ts";
|
|
4
|
+
|
|
5
|
+
test("vision ask variants stay varied and conversational", () => {
|
|
6
|
+
assert.ok(VISION_ASK_VARIANTS.length >= 6, "keep enough openers to avoid repetition");
|
|
7
|
+
assert.equal(new Set(VISION_ASK_VARIANTS).size, VISION_ASK_VARIANTS.length, "openers should be unique");
|
|
8
|
+
|
|
9
|
+
for (const opener of VISION_ASK_VARIANTS) {
|
|
10
|
+
assert.ok(opener.length <= 72, `opener should stay short: ${opener}`);
|
|
11
|
+
assert.doesNotMatch(opener, /\n/, "opener should be a single line");
|
|
12
|
+
assert.doesNotMatch(opener, /\bstakeholders?|key success metrics?|business objectives?\b/i, "avoid corporate wording");
|
|
13
|
+
assert.notEqual(opener, "What's the vision?", "do not keep the old fixed opener in rotation");
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("chooseVisionAskVariant picks from the configured opener list", () => {
|
|
18
|
+
assert.equal(chooseVisionAskVariant(() => 0), VISION_ASK_VARIANTS[0]);
|
|
19
|
+
assert.equal(
|
|
20
|
+
chooseVisionAskVariant((exclusiveMax) => exclusiveMax - 1),
|
|
21
|
+
VISION_ASK_VARIANTS[VISION_ASK_VARIANTS.length - 1],
|
|
22
|
+
);
|
|
23
|
+
});
|
|
@@ -14,6 +14,7 @@ import { test } from "node:test";
|
|
|
14
14
|
import assert from "node:assert/strict";
|
|
15
15
|
|
|
16
16
|
import { GSDVisualizerOverlay, TAB_COUNT } from "../visualizer-overlay.ts";
|
|
17
|
+
import { assertFullOuterBorder } from "./tui-border-assertions.ts";
|
|
17
18
|
|
|
18
19
|
function makeTui() {
|
|
19
20
|
const renders: number[] = [];
|
|
@@ -50,7 +51,11 @@ test("overlay renders 10 tabs (Progress, Timeline, Deps, Metrics, Health, Agent,
|
|
|
50
51
|
overlay.loading = true; // body shows loading text, but tab bar renders regardless
|
|
51
52
|
|
|
52
53
|
// Use a very wide terminal so the tab bar is not truncated.
|
|
53
|
-
const
|
|
54
|
+
const rawLines = overlay.render(200);
|
|
55
|
+
assertFullOuterBorder(rawLines, 200);
|
|
56
|
+
const lines = rawLines.map(stripAnsi);
|
|
57
|
+
assert.match(lines[0] ?? "", /^╭─ GSD Visualizer /);
|
|
58
|
+
assert.match(lines.at(-1) ?? "", /^╰─+╯$/);
|
|
54
59
|
const tabBar = lines.find((l) => l.includes("Progress") && l.includes("Export"));
|
|
55
60
|
assert.ok(tabBar, `expected a tab-bar line containing all labels, got:\n${lines.slice(0, 5).join("\n")}`);
|
|
56
61
|
for (const label of ["Progress", "Timeline", "Deps", "Metrics", "Health", "Agent", "Changes", "Knowledge", "Captures", "Export"]) {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
|
|
7
7
|
import { registerHooks } from "../bootstrap/register-hooks.ts";
|
|
8
8
|
import { GSD_WORKFLOW_MCP_SERVER_NAME } from "../mcp-project-config.ts";
|
|
9
|
+
import { clearSkillSnapshot, snapshotSkills } from "../skill-discovery.js";
|
|
9
10
|
import { prepareWorkflowMcpForProject, shouldAutoPrepareWorkflowMcp } from "../workflow-mcp-auto-prep.ts";
|
|
10
11
|
|
|
11
12
|
test("shouldAutoPrepareWorkflowMcp enables prep for externalCli local transport", () => {
|
|
@@ -134,3 +135,75 @@ test("before_agent_start auto-prepares project workflow MCP for Claude Code CLI"
|
|
|
134
135
|
assert.ok(parsed.mcpServers?.[GSD_WORKFLOW_MCP_SERVER_NAME]);
|
|
135
136
|
assert.match(notifications.join("\n"), /Claude Code MCP prepared/);
|
|
136
137
|
});
|
|
138
|
+
|
|
139
|
+
test("before_agent_start returns discovered skill fallback without project .gsd", async (t) => {
|
|
140
|
+
const projectRoot = mkdtempSync(join(tmpdir(), "gsd-skill-before-agent-"));
|
|
141
|
+
const skillHome = mkdtempSync(join(tmpdir(), "gsd-skill-home-"));
|
|
142
|
+
const originalCwd = process.cwd();
|
|
143
|
+
const originalHome = process.env.HOME;
|
|
144
|
+
const originalGsdHome = process.env.GSD_HOME;
|
|
145
|
+
const handlers = new Map<string, Array<(event: any, ctx?: any) => Promise<any> | any>>();
|
|
146
|
+
const pi = {
|
|
147
|
+
on(event: string, handler: (event: any, ctx?: any) => Promise<any> | any) {
|
|
148
|
+
const existing = handlers.get(event) ?? [];
|
|
149
|
+
existing.push(handler);
|
|
150
|
+
handlers.set(event, existing);
|
|
151
|
+
},
|
|
152
|
+
getActiveTools: () => [],
|
|
153
|
+
getAllTools: () => [],
|
|
154
|
+
setActiveTools() {},
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
t.after(() => {
|
|
158
|
+
process.chdir(originalCwd);
|
|
159
|
+
clearSkillSnapshot();
|
|
160
|
+
if (originalHome === undefined) {
|
|
161
|
+
delete process.env.HOME;
|
|
162
|
+
} else {
|
|
163
|
+
process.env.HOME = originalHome;
|
|
164
|
+
}
|
|
165
|
+
if (originalGsdHome === undefined) {
|
|
166
|
+
delete process.env.GSD_HOME;
|
|
167
|
+
} else {
|
|
168
|
+
process.env.GSD_HOME = originalGsdHome;
|
|
169
|
+
}
|
|
170
|
+
rmSync(projectRoot, { recursive: true, force: true });
|
|
171
|
+
rmSync(skillHome, { recursive: true, force: true });
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
process.env.HOME = skillHome;
|
|
175
|
+
process.env.GSD_HOME = join(skillHome, ".gsd");
|
|
176
|
+
process.chdir(projectRoot);
|
|
177
|
+
snapshotSkills();
|
|
178
|
+
|
|
179
|
+
const skillDir = join(skillHome, ".agents", "skills", "late-skill");
|
|
180
|
+
mkdirSync(skillDir, { recursive: true });
|
|
181
|
+
const skillPath = join(skillDir, "SKILL.md");
|
|
182
|
+
writeFileSync(skillPath, "---\nname: late-skill\ndescription: Use for late skill.\n---\n\n# late-skill\n");
|
|
183
|
+
|
|
184
|
+
registerHooks(pi as any, []);
|
|
185
|
+
const beforeAgentStart = handlers.get("before_agent_start")?.[0];
|
|
186
|
+
assert.ok(beforeAgentStart, "before_agent_start hook should be registered");
|
|
187
|
+
|
|
188
|
+
const result = await beforeAgentStart(
|
|
189
|
+
{ prompt: "hello", systemPrompt: "event system prompt" },
|
|
190
|
+
{
|
|
191
|
+
cwd: projectRoot,
|
|
192
|
+
model: { provider: "openai", baseUrl: "https://api.openai.com" },
|
|
193
|
+
modelRegistry: {
|
|
194
|
+
getProviderAuthMode: () => "apiKey",
|
|
195
|
+
isProviderRequestReady: () => false,
|
|
196
|
+
},
|
|
197
|
+
getSystemPrompt: () => "context system prompt",
|
|
198
|
+
reload: async () => {},
|
|
199
|
+
ui: {
|
|
200
|
+
notify() {},
|
|
201
|
+
setWidget() {},
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
assert.match(result?.systemPrompt ?? "", /<newly_discovered_skills>/);
|
|
207
|
+
assert.match(result?.systemPrompt ?? "", /late-skill/);
|
|
208
|
+
assert.equal(result?.systemPrompt?.includes(skillPath), true);
|
|
209
|
+
});
|