gsd-pi 2.77.0-dev.1d17f366c → 2.77.0-dev.2daa994b6
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/headless.js +25 -4
- package/dist/resource-loader.d.ts +40 -0
- package/dist/resource-loader.js +32 -13
- package/dist/resources/extensions/browser-tools/capture.js +9 -0
- package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
- package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
- package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
- package/dist/resources/extensions/browser-tools/tools/forms.js +5 -1
- package/dist/resources/extensions/browser-tools/tools/intent.js +5 -1
- package/dist/resources/extensions/gsd/auto/phases.js +5 -18
- package/dist/resources/extensions/gsd/auto/session.js +6 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +37 -8
- package/dist/resources/extensions/gsd/auto-post-unit.js +79 -0
- package/dist/resources/extensions/gsd/auto-prompts.js +372 -104
- package/dist/resources/extensions/gsd/auto-start.js +75 -24
- package/dist/resources/extensions/gsd/auto.js +34 -0
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -1
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +7 -1
- package/dist/resources/extensions/gsd/component-loader.js +447 -0
- package/dist/resources/extensions/gsd/component-types.js +69 -0
- package/dist/resources/extensions/gsd/context-store.js +23 -7
- package/dist/resources/extensions/gsd/detection.js +49 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/dist/resources/extensions/gsd/forensics.js +106 -0
- package/dist/resources/extensions/gsd/gsd-db.js +1 -1
- package/dist/resources/extensions/gsd/guided-flow.js +2 -4
- package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
- package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
- package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
- package/dist/resources/extensions/gsd/model-router.js +6 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +23 -0
- package/dist/resources/extensions/gsd/prompt-cache-optimizer.js +4 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +5 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
- package/dist/resources/extensions/gsd/service-tier.js +5 -2
- package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
- package/dist/resources/extensions/gsd/slice-cadence.js +238 -0
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -2
- package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
- package/dist/resources/extensions/gsd/unit-context-manifest.js +334 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +51 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +86 -7
- package/dist/resources/extensions/gsd/worktree-telemetry.js +198 -0
- package/dist/resources/extensions/mcp-client/index.js +3 -1
- package/dist/resources/extensions/ollama/index.js +5 -1
- package/dist/resources/extensions/remote-questions/manager.js +11 -5
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
- 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 +17 -17
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- 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 -3
- package/packages/mcp-server/src/mcp-server.test.ts +25 -3
- package/packages/mcp-server/src/readers/graph.test.ts +87 -15
- package/packages/mcp-server/src/workflow-tools.test.ts +80 -39
- package/packages/native/package.json +1 -1
- package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
- package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
- package/packages/native/src/__tests__/ps.test.mjs +14 -8
- package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
- package/packages/native/src/__tests__/truncate.test.mjs +17 -2
- package/packages/pi-agent-core/src/agent-loop.test.ts +5 -15
- package/packages/pi-agent-core/src/agent.test.ts +96 -102
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
- package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
- package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
- package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
- package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
- package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/generated/openai.js +17 -0
- package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.test.js +43 -70
- package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
- package/packages/pi-ai/dist/models.test.js +29 -11
- package/packages/pi-ai/dist/models.test.js.map +1 -1
- package/packages/pi-ai/scripts/generate-models.ts +44 -0
- package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
- package/packages/pi-ai/src/models/generated/openai.ts +17 -0
- package/packages/pi-ai/src/models.generated.test.ts +46 -73
- package/packages/pi-ai/src/models.test.ts +39 -11
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
- package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +61 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +30 -4
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +17 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
- package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
- package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -0
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +16 -2
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -0
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +30 -12
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
- package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
- package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
- package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +58 -0
- package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +35 -4
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +20 -0
- package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
- package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
- package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
- package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
- package/packages/pi-coding-agent/src/core/system-prompt.ts +35 -1
- package/packages/pi-coding-agent/src/index.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +49 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +48 -9
- package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js +18 -8
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +36 -12
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
- package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
- package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
- package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
- package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
- package/packages/pi-tui/dist/components/image.test.js +6 -5
- package/packages/pi-tui/dist/components/image.test.js.map +1 -1
- package/packages/pi-tui/src/__tests__/autocomplete.test.ts +24 -8
- package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
- package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +41 -12
- package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
- package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
- package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
- package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
- package/packages/pi-tui/src/components/image.test.ts +10 -5
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/packages/rpc-client/dist/rpc-client.test.js +101 -51
- package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
- package/packages/rpc-client/src/rpc-client.test.ts +109 -52
- package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
- package/scripts/install.js +15 -1
- package/src/resources/extensions/browser-tools/capture.ts +12 -0
- package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
- package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
- package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
- package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
- package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +80 -72
- package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
- package/src/resources/extensions/github-sync/tests/templates.test.ts +33 -1
- package/src/resources/extensions/gsd/auto/phases.ts +6 -17
- package/src/resources/extensions/gsd/auto/session.ts +7 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +40 -8
- package/src/resources/extensions/gsd/auto-post-unit.ts +81 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +385 -93
- package/src/resources/extensions/gsd/auto-start.ts +97 -4
- package/src/resources/extensions/gsd/auto.ts +37 -0
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +9 -1
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +7 -1
- package/src/resources/extensions/gsd/component-loader.ts +598 -0
- package/src/resources/extensions/gsd/component-types.ts +362 -0
- package/src/resources/extensions/gsd/context-store.ts +25 -8
- package/src/resources/extensions/gsd/detection.ts +58 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/src/resources/extensions/gsd/forensics.ts +118 -1
- package/src/resources/extensions/gsd/git-service.ts +16 -0
- package/src/resources/extensions/gsd/gsd-db.ts +1 -1
- package/src/resources/extensions/gsd/guided-flow.ts +2 -4
- package/src/resources/extensions/gsd/journal.ts +11 -1
- package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
- package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
- package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
- package/src/resources/extensions/gsd/model-router.ts +6 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +21 -0
- package/src/resources/extensions/gsd/prompt-cache-optimizer.ts +4 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +5 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
- package/src/resources/extensions/gsd/service-tier.ts +5 -2
- package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
- package/src/resources/extensions/gsd/slice-cadence.ts +299 -0
- package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +25 -292
- package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +4 -1
- package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -194
- package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
- package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
- package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +108 -0
- package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
- package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +8 -4
- package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
- package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +8 -104
- package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
- package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -56
- package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
- package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +9 -3
- package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +93 -1
- package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +8 -37
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -55
- package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
- package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
- package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -48
- package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +59 -2
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -130
- package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
- package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/prompt-cache-optimizer.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +15 -4
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +22 -16
- package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +4 -5
- package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
- package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
- package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
- package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/sidecar-queue.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
- package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
- package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +242 -0
- package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
- package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +7 -6
- package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
- package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/test-helpers.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/test-helpers.ts +153 -0
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +203 -0
- package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -80
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
- package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
- package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
- package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
- package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
- package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
- package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +210 -0
- package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +8 -2
- package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
- package/src/resources/extensions/gsd/unit-context-manifest.ts +492 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +53 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +96 -9
- package/src/resources/extensions/gsd/worktree-telemetry.ts +322 -0
- package/src/resources/extensions/mcp-client/index.ts +3 -1
- package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
- package/src/resources/extensions/ollama/index.ts +5 -1
- package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
- package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
- package/src/resources/extensions/remote-questions/manager.ts +36 -4
- package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
- package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
- package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
- package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -143
- package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -157
- package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
- package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
- package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
- package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
- package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
- package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -74
- package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
- package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
- package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
- package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -125
- package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -42
- /package/dist/web/standalone/.next/static/{vidAVJkURvTJ0_V2-64ro → gYYky7yfxW8txb9vU2TrJ}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{vidAVJkURvTJ0_V2-64ro → gYYky7yfxW8txb9vU2TrJ}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// GSD-2 — #4782 phase 3: run-uat migrated to compose context via manifest.
|
|
2
|
+
// Regression test: prompt still carries the declared artifacts in the
|
|
3
|
+
// expected shape after the migration.
|
|
4
|
+
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
import assert from "node:assert/strict";
|
|
7
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
|
|
11
|
+
import { buildRunUatPrompt } from "../auto-prompts.ts";
|
|
12
|
+
import { invalidateAllCaches } from "../cache.ts";
|
|
13
|
+
import {
|
|
14
|
+
openDatabase,
|
|
15
|
+
closeDatabase,
|
|
16
|
+
insertMilestone,
|
|
17
|
+
upsertMilestonePlanning,
|
|
18
|
+
insertSlice,
|
|
19
|
+
insertArtifact,
|
|
20
|
+
} from "../gsd-db.ts";
|
|
21
|
+
|
|
22
|
+
function makeBase(): string {
|
|
23
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-runuat-composer-"));
|
|
24
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks"), { recursive: true });
|
|
25
|
+
return base;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function cleanup(base: string): void {
|
|
29
|
+
try { closeDatabase(); } catch { /* noop */ }
|
|
30
|
+
invalidateAllCaches();
|
|
31
|
+
rmSync(base, { recursive: true, force: true });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function seed(base: string, mid: string): void {
|
|
35
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
36
|
+
insertMilestone({ id: mid, title: "Test", status: "active", depends_on: [] });
|
|
37
|
+
upsertMilestonePlanning(mid, {
|
|
38
|
+
title: "Test Milestone",
|
|
39
|
+
status: "active",
|
|
40
|
+
vision: "Demo the composer migration",
|
|
41
|
+
successCriteria: ["Prompt compiles", "UAT passes"],
|
|
42
|
+
keyRisks: [],
|
|
43
|
+
proofStrategy: [],
|
|
44
|
+
verificationContract: "",
|
|
45
|
+
verificationIntegration: "",
|
|
46
|
+
verificationOperational: "",
|
|
47
|
+
verificationUat: "",
|
|
48
|
+
definitionOfDone: [],
|
|
49
|
+
requirementCoverage: "",
|
|
50
|
+
boundaryMapMarkdown: "",
|
|
51
|
+
});
|
|
52
|
+
insertSlice({
|
|
53
|
+
id: "S01",
|
|
54
|
+
milestoneId: mid,
|
|
55
|
+
title: "First",
|
|
56
|
+
status: "complete",
|
|
57
|
+
risk: "low",
|
|
58
|
+
depends: [],
|
|
59
|
+
demo: "",
|
|
60
|
+
sequence: 1,
|
|
61
|
+
});
|
|
62
|
+
// Seed PROJECT.md so inlineProjectFromDb resolves — the run-uat manifest
|
|
63
|
+
// declares "project" as the third inline artifact (#4925 review).
|
|
64
|
+
insertArtifact({
|
|
65
|
+
path: "PROJECT.md",
|
|
66
|
+
artifact_type: "project",
|
|
67
|
+
milestone_id: null,
|
|
68
|
+
slice_id: null,
|
|
69
|
+
task_id: null,
|
|
70
|
+
full_content: "# Project\n\nRun-UAT composer fixture project.\n",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
test("#4782 phase 3: buildRunUatPrompt inlines slice UAT, slice summary, project via composer", async (t) => {
|
|
75
|
+
const base = makeBase();
|
|
76
|
+
t.after(() => cleanup(base));
|
|
77
|
+
invalidateAllCaches();
|
|
78
|
+
|
|
79
|
+
seed(base, "M001");
|
|
80
|
+
|
|
81
|
+
// Write UAT + SUMMARY files. Deliberately diverge the on-disk UAT body
|
|
82
|
+
// from the in-memory uatContent the caller passes — if the resolver
|
|
83
|
+
// ever re-reads disk (the bug fixed in fcf3bfbe), this test fails
|
|
84
|
+
// because the prompt would contain "stale on-disk body" instead of
|
|
85
|
+
// "fresh in-memory snapshot" (#4925 follow-up review).
|
|
86
|
+
const uatRel = ".gsd/milestones/M001/slices/S01/S01-UAT.md";
|
|
87
|
+
writeFileSync(join(base, uatRel), "# S01 UAT\n\n- stale on-disk body\n");
|
|
88
|
+
writeFileSync(
|
|
89
|
+
join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-SUMMARY.md"),
|
|
90
|
+
"---\nid: S01\nparent: M001\n---\n# S01 Summary\n**One-liner**\n\n## What Happened\nShip.\n",
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const uatContent = "# S01 UAT\n\n- Check X\n- Check Y\n (fresh in-memory snapshot)\n";
|
|
94
|
+
const prompt = await buildRunUatPrompt("M001", "S01", uatRel, uatContent, base);
|
|
95
|
+
|
|
96
|
+
// Context wrapper present
|
|
97
|
+
assert.match(prompt, /## Inlined Context \(preloaded — do not re-read these files\)/);
|
|
98
|
+
|
|
99
|
+
// Artifacts from the manifest inline list, in declared order:
|
|
100
|
+
// slice-uat → slice-summary → project (#4925 review).
|
|
101
|
+
const uatIdx = prompt.indexOf("### S01 UAT");
|
|
102
|
+
const summaryIdx = prompt.indexOf("### S01 Summary");
|
|
103
|
+
const projectIdx = prompt.indexOf("### Project");
|
|
104
|
+
assert.ok(uatIdx > -1, "slice UAT block missing");
|
|
105
|
+
assert.ok(summaryIdx > -1, "slice summary block missing");
|
|
106
|
+
assert.ok(projectIdx > -1, "project block missing — manifest declares project as 3rd inline");
|
|
107
|
+
assert.ok(
|
|
108
|
+
uatIdx < summaryIdx && summaryIdx < projectIdx,
|
|
109
|
+
`manifest order violated: uat (${uatIdx}) < summary (${summaryIdx}) < project (${projectIdx})`,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// In-memory uatContent inlined — drift assertion: stale disk content
|
|
113
|
+
// must NOT appear, fresh snapshot MUST appear (#4925 follow-up review).
|
|
114
|
+
assert.match(prompt, /fresh in-memory snapshot/);
|
|
115
|
+
assert.ok(!prompt.includes("stale on-disk body"), "resolver re-read disk instead of using uatContent snapshot");
|
|
116
|
+
|
|
117
|
+
// Summary body content inlined
|
|
118
|
+
assert.match(prompt, /What Happened[\s\S]*Ship/);
|
|
119
|
+
|
|
120
|
+
// Project body content inlined
|
|
121
|
+
assert.match(prompt, /Run-UAT composer fixture project/);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("#4782 phase 3: buildRunUatPrompt omits optional slice summary when file is missing", async (t) => {
|
|
125
|
+
const base = makeBase();
|
|
126
|
+
t.after(() => cleanup(base));
|
|
127
|
+
invalidateAllCaches();
|
|
128
|
+
|
|
129
|
+
seed(base, "M001");
|
|
130
|
+
|
|
131
|
+
const uatRel = ".gsd/milestones/M001/slices/S01/S01-UAT.md";
|
|
132
|
+
writeFileSync(join(base, uatRel), "# S01 UAT\n");
|
|
133
|
+
// No SUMMARY.md written — composer should skip the slice-summary key.
|
|
134
|
+
|
|
135
|
+
const prompt = await buildRunUatPrompt("M001", "S01", uatRel, "# S01 UAT\n", base);
|
|
136
|
+
|
|
137
|
+
// UAT still present
|
|
138
|
+
assert.match(prompt, /### S01 UAT/);
|
|
139
|
+
// No empty "S01 Summary" section — section body would be blank without a file
|
|
140
|
+
assert.ok(!prompt.includes("### S01 Summary"));
|
|
141
|
+
// Project still present (third inline artifact, not optional) and follows
|
|
142
|
+
// UAT directly with the skipped summary collapsed (#4925 review).
|
|
143
|
+
const uatIdx = prompt.indexOf("### S01 UAT");
|
|
144
|
+
const projectIdx = prompt.indexOf("### Project");
|
|
145
|
+
assert.ok(projectIdx > uatIdx, `project must follow UAT when summary is omitted (uat=${uatIdx}, project=${projectIdx})`);
|
|
146
|
+
// No double separator from a skipped block
|
|
147
|
+
assert.ok(!prompt.includes("---\n\n---"));
|
|
148
|
+
});
|
|
@@ -31,6 +31,10 @@ describe("supportsServiceTier", () => {
|
|
|
31
31
|
assert.equal(supportsServiceTier("vibeproxy-openai/gpt-5.4"), true);
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
+
test("returns false for gpt-5.5 until service_tier payload support is verified", () => {
|
|
35
|
+
assert.equal(supportsServiceTier("gpt-5.5"), false);
|
|
36
|
+
});
|
|
37
|
+
|
|
34
38
|
test("returns false for provider-only identifier without gpt-5.4 model suffix", () => {
|
|
35
39
|
assert.equal(supportsServiceTier("vibeproxy-openai"), false);
|
|
36
40
|
});
|
|
@@ -11,6 +11,7 @@ import assert from "node:assert/strict";
|
|
|
11
11
|
import { readFileSync } from "node:fs";
|
|
12
12
|
import { join, dirname } from "node:path";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { extractSourceRegion } from "./test-helpers.ts";
|
|
14
15
|
|
|
15
16
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
17
|
const SESSION_TS_PATH = join(__dirname, "..", "auto", "session.ts");
|
|
@@ -52,7 +53,7 @@ test("SidecarItem type is exported from session.ts", () => {
|
|
|
52
53
|
test("SidecarItem has required kind field with hook/triage/quick-task union", () => {
|
|
53
54
|
const source = getSessionTsSource();
|
|
54
55
|
const ifaceIdx = source.indexOf("export interface SidecarItem");
|
|
55
|
-
const ifaceBlock = source
|
|
56
|
+
const ifaceBlock = extractSourceRegion(source, "export interface SidecarItem");
|
|
56
57
|
assert.ok(
|
|
57
58
|
ifaceBlock.includes('"hook"') && ifaceBlock.includes('"triage"') && ifaceBlock.includes('"quick-task"'),
|
|
58
59
|
"SidecarItem.kind must be a union of 'hook' | 'triage' | 'quick-task'",
|
|
@@ -77,7 +78,7 @@ test("AutoSession resets sidecarQueue in reset()", () => {
|
|
|
77
78
|
const source = getSessionTsSource();
|
|
78
79
|
const resetIdx = source.indexOf("reset(): void");
|
|
79
80
|
assert.ok(resetIdx > -1, "AutoSession must have a reset() method");
|
|
80
|
-
const resetBlock = source
|
|
81
|
+
const resetBlock = extractSourceRegion(source, "reset(): void");
|
|
81
82
|
assert.ok(
|
|
82
83
|
resetBlock.includes("sidecarQueue"),
|
|
83
84
|
"reset() must clear sidecarQueue",
|
|
@@ -6,6 +6,13 @@
|
|
|
6
6
|
* Two tests:
|
|
7
7
|
* 1. Auto-mode files must have zero empty catch blocks (fully migrated).
|
|
8
8
|
* 2. All GSD files must not use raw stderr/console in catch blocks.
|
|
9
|
+
*
|
|
10
|
+
* Implementation note (#4836): the previous implementation walked every
|
|
11
|
+
* `{` / `}` character in the source to infer catch-block boundaries. That
|
|
12
|
+
* ignored string literals, template interpolations, regexes, and comments,
|
|
13
|
+
* producing both false positives and false negatives. The current
|
|
14
|
+
* implementation uses the TypeScript compiler API to walk real
|
|
15
|
+
* `CatchClause` nodes, so lexical accidents cannot flip the verdict.
|
|
9
16
|
*/
|
|
10
17
|
|
|
11
18
|
import { describe, test } from "node:test";
|
|
@@ -13,6 +20,7 @@ import assert from "node:assert/strict";
|
|
|
13
20
|
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
14
21
|
import { join, dirname, relative } from "node:path";
|
|
15
22
|
import { fileURLToPath } from "node:url";
|
|
23
|
+
import ts from "typescript";
|
|
16
24
|
|
|
17
25
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
26
|
const gsdDir = join(__dirname, "..");
|
|
@@ -73,12 +81,6 @@ const MIGRATED_FILES = new Set([
|
|
|
73
81
|
"auto-verification.ts",
|
|
74
82
|
]);
|
|
75
83
|
|
|
76
|
-
/** Patterns that indicate a catch block already uses workflow-logger */
|
|
77
|
-
const LOGGER_PATTERNS = [
|
|
78
|
-
/logWarning\s*\(/,
|
|
79
|
-
/logError\s*\(/,
|
|
80
|
-
];
|
|
81
|
-
|
|
82
84
|
function getAutoModeFiles(): string[] {
|
|
83
85
|
const files: string[] = [];
|
|
84
86
|
|
|
@@ -124,103 +126,61 @@ function getGsdSourceFiles(): string[] {
|
|
|
124
126
|
return files;
|
|
125
127
|
}
|
|
126
128
|
|
|
127
|
-
|
|
128
|
-
* Scan a file for empty catch blocks — catches whose body contains
|
|
129
|
-
* only whitespace and/or comments but no executable statements.
|
|
130
|
-
*/
|
|
131
|
-
function findEmptyCatches(filePath: string): Array<{ line: number; text: string }> {
|
|
129
|
+
function parseSourceFile(filePath: string): ts.SourceFile {
|
|
132
130
|
const content = readFileSync(filePath, "utf-8");
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
for (let i = 0; i < lines.length; i++) {
|
|
137
|
-
const line = lines[i];
|
|
138
|
-
|
|
139
|
-
// Match catch block opening
|
|
140
|
-
if (!/\}\s*catch\s*(\([^)]*\))?\s*\{/.test(line)) continue;
|
|
141
|
-
|
|
142
|
-
// Inline single-line catch: } catch { ... }
|
|
143
|
-
const inlineMatch = line.match(/\}\s*catch\s*(\([^)]*\))?\s*\{(.*)\}\s*;?\s*$/);
|
|
144
|
-
if (inlineMatch) {
|
|
145
|
-
const body = inlineMatch[2].trim();
|
|
146
|
-
const stripped = body.replace(/\/\*.*?\*\//g, "").replace(/\/\/.*/g, "").trim();
|
|
147
|
-
if (!stripped) {
|
|
148
|
-
results.push({ line: i + 1, text: line.trim() });
|
|
149
|
-
}
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Multi-line catch — scan until matching }
|
|
154
|
-
let j = i + 1;
|
|
155
|
-
let depth = 1;
|
|
156
|
-
const bodyLines: string[] = [];
|
|
157
|
-
while (j < lines.length && depth > 0) {
|
|
158
|
-
for (const ch of lines[j]) {
|
|
159
|
-
if (ch === "{") depth++;
|
|
160
|
-
else if (ch === "}") depth--;
|
|
161
|
-
}
|
|
162
|
-
bodyLines.push(lines[j].trim());
|
|
163
|
-
j++;
|
|
164
|
-
}
|
|
131
|
+
return ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, /*setParentNodes*/ true, ts.ScriptKind.TS);
|
|
132
|
+
}
|
|
165
133
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
);
|
|
134
|
+
function forEachCatchClause(sf: ts.SourceFile, visit: (cc: ts.CatchClause) => void): void {
|
|
135
|
+
const walk = (node: ts.Node): void => {
|
|
136
|
+
if (ts.isCatchClause(node)) visit(node);
|
|
137
|
+
ts.forEachChild(node, walk);
|
|
138
|
+
};
|
|
139
|
+
walk(sf);
|
|
140
|
+
}
|
|
169
141
|
|
|
170
|
-
|
|
171
|
-
|
|
142
|
+
/**
|
|
143
|
+
* A catch block is "empty" if its Block has zero statements. Comments
|
|
144
|
+
* inside the block are trivia and are not Statement nodes, so a
|
|
145
|
+
* comment-only body still counts as empty — matching the intent of the
|
|
146
|
+
* old regex check but without its lexical blind spots.
|
|
147
|
+
*/
|
|
148
|
+
function findEmptyCatches(filePath: string): Array<{ line: number }> {
|
|
149
|
+
const sf = parseSourceFile(filePath);
|
|
150
|
+
const results: Array<{ line: number }> = [];
|
|
151
|
+
forEachCatchClause(sf, (cc) => {
|
|
152
|
+
if (cc.block.statements.length === 0) {
|
|
153
|
+
const { line } = sf.getLineAndCharacterOfPosition(cc.getStart(sf));
|
|
154
|
+
results.push({ line: line + 1 });
|
|
172
155
|
}
|
|
173
|
-
}
|
|
174
|
-
|
|
156
|
+
});
|
|
175
157
|
return results;
|
|
176
158
|
}
|
|
177
159
|
|
|
178
160
|
/**
|
|
179
|
-
*
|
|
180
|
-
* console.error/warn
|
|
161
|
+
* A catch block uses "raw stderr/console" if its body text calls
|
|
162
|
+
* process.stderr.write or console.error/warn *and* does NOT also call
|
|
163
|
+
* logWarning / logError.
|
|
164
|
+
*
|
|
165
|
+
* We test against the block's statement subtree text — derived from the
|
|
166
|
+
* AST node range, not a naive substring of the whole file — so string
|
|
167
|
+
* literals outside the block can never leak into the decision.
|
|
181
168
|
*/
|
|
182
|
-
function findRawStderrCatches(filePath: string): Array<{ line: number
|
|
183
|
-
const
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if (
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (!LOGGER_PATTERNS.some((p) => p.test(body))) {
|
|
196
|
-
if (/process\.stderr\.write/.test(body) || /console\.(error|warn)/.test(body)) {
|
|
197
|
-
results.push({ line: i + 1, text: line.trim() });
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
continue;
|
|
169
|
+
function findRawStderrCatches(filePath: string): Array<{ line: number }> {
|
|
170
|
+
const sf = parseSourceFile(filePath);
|
|
171
|
+
const results: Array<{ line: number }> = [];
|
|
172
|
+
forEachCatchClause(sf, (cc) => {
|
|
173
|
+
const bodyText = cc.block.getText(sf);
|
|
174
|
+
const usesLogger = /\blogWarning\s*\(|\blogError\s*\(/.test(bodyText);
|
|
175
|
+
if (usesLogger) return;
|
|
176
|
+
if (
|
|
177
|
+
/\bprocess\.stderr\.write\b/.test(bodyText) ||
|
|
178
|
+
/\bconsole\.(?:error|warn)\b/.test(bodyText)
|
|
179
|
+
) {
|
|
180
|
+
const { line } = sf.getLineAndCharacterOfPosition(cc.getStart(sf));
|
|
181
|
+
results.push({ line: line + 1 });
|
|
201
182
|
}
|
|
202
|
-
|
|
203
|
-
// Multi-line catch
|
|
204
|
-
let j = i + 1;
|
|
205
|
-
let depth = 1;
|
|
206
|
-
const bodyLines: string[] = [];
|
|
207
|
-
while (j < lines.length && depth > 0) {
|
|
208
|
-
for (const ch of lines[j]) {
|
|
209
|
-
if (ch === "{") depth++;
|
|
210
|
-
else if (ch === "}") depth--;
|
|
211
|
-
}
|
|
212
|
-
bodyLines.push(lines[j]);
|
|
213
|
-
j++;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const bodyText = bodyLines.slice(0, -1).join("\n");
|
|
217
|
-
if (!LOGGER_PATTERNS.some((p) => p.test(bodyText))) {
|
|
218
|
-
if (/process\.stderr\.write/.test(bodyText) || /console\.(error|warn)/.test(bodyText)) {
|
|
219
|
-
results.push({ line: i + 1, text: line.trim() });
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
183
|
+
});
|
|
224
184
|
return results;
|
|
225
185
|
}
|
|
226
186
|
|
|
@@ -248,7 +208,7 @@ describe("workflow-logger coverage (#3348)", () => {
|
|
|
248
208
|
|
|
249
209
|
const empties = findEmptyCatches(file);
|
|
250
210
|
for (const empty of empties) {
|
|
251
|
-
violations.push(`${rel}:${empty.line}
|
|
211
|
+
violations.push(`${rel}:${empty.line}`);
|
|
252
212
|
}
|
|
253
213
|
}
|
|
254
214
|
|
|
@@ -271,7 +231,7 @@ describe("workflow-logger coverage (#3348)", () => {
|
|
|
271
231
|
|
|
272
232
|
const issues = findRawStderrCatches(file);
|
|
273
233
|
for (const issue of issues) {
|
|
274
|
-
violations.push(`${rel}:${issue.line}
|
|
234
|
+
violations.push(`${rel}:${issue.line}`);
|
|
275
235
|
}
|
|
276
236
|
}
|
|
277
237
|
|
|
@@ -4,7 +4,13 @@ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { loadSkills } from "@gsd/pi-coding-agent";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
buildPlanMilestonePrompt,
|
|
9
|
+
buildResearchMilestonePrompt,
|
|
10
|
+
buildSkillActivationBlock,
|
|
11
|
+
} from "../auto-prompts.js";
|
|
12
|
+
import { warnIfManifestHasMissingSkills } from "../skill-manifest.js";
|
|
13
|
+
import { _resetLogs, drainLogs, setStderrLoggingEnabled } from "../workflow-logger.js";
|
|
8
14
|
import type { GSDPreferences } from "../preferences.js";
|
|
9
15
|
|
|
10
16
|
function makeTempBase(): string {
|
|
@@ -25,6 +31,11 @@ function loadOnlyTestSkills(base: string): void {
|
|
|
25
31
|
loadSkills({ cwd: base, includeDefaults: false, skillPaths: [join(base, "skills")] });
|
|
26
32
|
}
|
|
27
33
|
|
|
34
|
+
function writeProjectPreferences(base: string, preferences: string): void {
|
|
35
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
36
|
+
writeFileSync(join(base, ".gsd", "PREFERENCES.md"), `---\n${preferences}---\n`);
|
|
37
|
+
}
|
|
38
|
+
|
|
28
39
|
function buildBlock(
|
|
29
40
|
base: string,
|
|
30
41
|
params: Partial<Parameters<typeof buildSkillActivationBlock>[0]> = {},
|
|
@@ -231,3 +242,111 @@ test("buildSkillActivationBlock allows valid skill names and rejects invalid one
|
|
|
231
242
|
cleanup(base);
|
|
232
243
|
}
|
|
233
244
|
});
|
|
245
|
+
|
|
246
|
+
// ─── Per-unit-type skill manifest (RFC #4779) ─────────────────────────────────
|
|
247
|
+
|
|
248
|
+
test("buildSkillActivationBlock: explicit always_use_skills bypass the unit-type manifest", () => {
|
|
249
|
+
const base = makeTempBase();
|
|
250
|
+
try {
|
|
251
|
+
// write-docs is in the research-milestone manifest; swiftui is not.
|
|
252
|
+
// Both are in always_use_skills — a user-explicit source — so BOTH
|
|
253
|
+
// should activate regardless of the manifest. User intent wins over
|
|
254
|
+
// unit-type defaults. See RFC #4779 and skill-manifest.ts rationale.
|
|
255
|
+
writeSkill(base, "write-docs", "Use when writing docs or RFCs.");
|
|
256
|
+
writeSkill(base, "swiftui", "Use for SwiftUI views.");
|
|
257
|
+
loadOnlyTestSkills(base);
|
|
258
|
+
|
|
259
|
+
const result = buildBlock(base, { unitType: "research-milestone" }, {
|
|
260
|
+
always_use_skills: ["write-docs", "swiftui"],
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
assert.match(result, /Call Skill\(\{ skill: 'write-docs' \}\)/);
|
|
264
|
+
assert.match(result, /Call Skill\(\{ skill: 'swiftui' \}\)/);
|
|
265
|
+
} finally {
|
|
266
|
+
cleanup(base);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test("buildSkillActivationBlock falls through to all skills for unknown unit type", () => {
|
|
271
|
+
const base = makeTempBase();
|
|
272
|
+
try {
|
|
273
|
+
writeSkill(base, "swiftui", "Use for SwiftUI views.");
|
|
274
|
+
loadOnlyTestSkills(base);
|
|
275
|
+
|
|
276
|
+
const result = buildBlock(base, { unitType: "unknown-unit-type" }, {
|
|
277
|
+
always_use_skills: ["swiftui"],
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// Unknown unit type = wildcard fallback (pre-manifest behavior).
|
|
281
|
+
assert.match(result, /Call Skill\(\{ skill: 'swiftui' \}\)/);
|
|
282
|
+
} finally {
|
|
283
|
+
cleanup(base);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test("buildSkillActivationBlock without unitType preserves pre-manifest behavior", () => {
|
|
288
|
+
const base = makeTempBase();
|
|
289
|
+
try {
|
|
290
|
+
writeSkill(base, "swiftui", "Use for SwiftUI views.");
|
|
291
|
+
loadOnlyTestSkills(base);
|
|
292
|
+
|
|
293
|
+
// No unitType param — filter should no-op.
|
|
294
|
+
const result = buildBlock(base, {}, {
|
|
295
|
+
always_use_skills: ["swiftui"],
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
assert.match(result, /Call Skill\(\{ skill: 'swiftui' \}\)/);
|
|
299
|
+
} finally {
|
|
300
|
+
cleanup(base);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test("milestone prompt builders propagate always_use_skills through buildSkillActivationBlock", async () => {
|
|
305
|
+
const base = makeTempBase();
|
|
306
|
+
try {
|
|
307
|
+
// Both skills are in always_use_skills — explicit user intent bypasses
|
|
308
|
+
// the unit-type manifest, so both activate in both milestone flows.
|
|
309
|
+
writeSkill(base, "write-docs", "Use when writing docs or RFCs.");
|
|
310
|
+
writeSkill(base, "swiftui", "Use for SwiftUI views.");
|
|
311
|
+
writeProjectPreferences(base, "always_use_skills:\n - write-docs\n - swiftui\n");
|
|
312
|
+
loadOnlyTestSkills(base);
|
|
313
|
+
|
|
314
|
+
const researchPrompt = await buildResearchMilestonePrompt("M001", "Test", base);
|
|
315
|
+
assert.match(researchPrompt, /Call Skill\(\{ skill: 'write-docs' \}\)/);
|
|
316
|
+
assert.match(researchPrompt, /Call Skill\(\{ skill: 'swiftui' \}\)/);
|
|
317
|
+
|
|
318
|
+
const planPrompt = await buildPlanMilestonePrompt("M001", "Test", base);
|
|
319
|
+
assert.match(planPrompt, /Call Skill\(\{ skill: 'write-docs' \}\)/);
|
|
320
|
+
assert.match(planPrompt, /Call Skill\(\{ skill: 'swiftui' \}\)/);
|
|
321
|
+
} finally {
|
|
322
|
+
cleanup(base);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test("skill manifest strict warnings require GSD_SKILL_MANIFEST_STRICT=1", (t) => {
|
|
327
|
+
const previousStrict = process.env.GSD_SKILL_MANIFEST_STRICT;
|
|
328
|
+
const previousStderr = setStderrLoggingEnabled(false);
|
|
329
|
+
t.after(() => {
|
|
330
|
+
if (previousStrict === undefined) {
|
|
331
|
+
delete process.env.GSD_SKILL_MANIFEST_STRICT;
|
|
332
|
+
} else {
|
|
333
|
+
process.env.GSD_SKILL_MANIFEST_STRICT = previousStrict;
|
|
334
|
+
}
|
|
335
|
+
setStderrLoggingEnabled(previousStderr);
|
|
336
|
+
_resetLogs();
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
process.env.GSD_SKILL_MANIFEST_STRICT = "0";
|
|
340
|
+
_resetLogs();
|
|
341
|
+
warnIfManifestHasMissingSkills("research-milestone", new Set());
|
|
342
|
+
assert.equal(drainLogs().length, 0, "strict=0 must preserve silent behavior");
|
|
343
|
+
|
|
344
|
+
process.env.GSD_SKILL_MANIFEST_STRICT = "1";
|
|
345
|
+
_resetLogs();
|
|
346
|
+
warnIfManifestHasMissingSkills("research-milestone", new Set());
|
|
347
|
+
const logs = drainLogs();
|
|
348
|
+
assert.ok(
|
|
349
|
+
logs.some(log => log.message.includes("skill-manifest: references uninstalled skill")),
|
|
350
|
+
"strict=1 should warn about missing manifest entries",
|
|
351
|
+
);
|
|
352
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// GSD2 + skill-manifest.test — unit coverage for the skill manifest resolver
|
|
2
|
+
//
|
|
3
|
+
// Focused tests for `resolveSkillManifest` and `filterSkillsByManifest`.
|
|
4
|
+
// Covers the wildcard semantics, the newly seeded unit-type entries
|
|
5
|
+
// (complete-milestone, validate-milestone, reassess-roadmap, research-slice,
|
|
6
|
+
// plan-slice, refine-slice, replan-slice, run-uat), and the deliberate
|
|
7
|
+
// wildcard fallback for the execute-task hot path (RFC #4779).
|
|
8
|
+
|
|
9
|
+
import test from "node:test";
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
resolveSkillManifest,
|
|
14
|
+
filterSkillsByManifest,
|
|
15
|
+
} from "../skill-manifest.js";
|
|
16
|
+
|
|
17
|
+
const NEWLY_WIRED_UNIT_TYPES = [
|
|
18
|
+
"complete-milestone",
|
|
19
|
+
"validate-milestone",
|
|
20
|
+
"reassess-roadmap",
|
|
21
|
+
"research-slice",
|
|
22
|
+
"plan-slice",
|
|
23
|
+
"refine-slice",
|
|
24
|
+
"replan-slice",
|
|
25
|
+
"run-uat",
|
|
26
|
+
] as const;
|
|
27
|
+
|
|
28
|
+
test("resolveSkillManifest returns null for undefined unit type (wildcard)", () => {
|
|
29
|
+
assert.equal(resolveSkillManifest(undefined), null);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("resolveSkillManifest returns null for unknown unit types (wildcard fallback)", () => {
|
|
33
|
+
assert.equal(resolveSkillManifest("nonexistent-unit-type"), null);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("resolveSkillManifest returns null for execute-task (intentional wildcard)", () => {
|
|
37
|
+
// execute-task is the implementation hot path; allowlisting it requires
|
|
38
|
+
// per-task skill hints from task-plan frontmatter. Documented in
|
|
39
|
+
// skill-manifest.ts — regression guard.
|
|
40
|
+
assert.equal(resolveSkillManifest("execute-task"), null);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
for (const unitType of NEWLY_WIRED_UNIT_TYPES) {
|
|
44
|
+
test(`resolveSkillManifest returns a non-empty allowlist for '${unitType}'`, () => {
|
|
45
|
+
const allowlist = resolveSkillManifest(unitType);
|
|
46
|
+
assert.ok(allowlist !== null, `${unitType} should resolve to an allowlist, not wildcard`);
|
|
47
|
+
assert.ok(allowlist.length > 0, `${unitType} allowlist should not be empty`);
|
|
48
|
+
// Every entry must be lowercase (normalized).
|
|
49
|
+
for (const name of allowlist) {
|
|
50
|
+
assert.equal(name, name.toLowerCase(), `${unitType} entry '${name}' should be lowercase`);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
test("resolveSkillManifest: slice-level manifests include decompose-into-slices", () => {
|
|
56
|
+
// Planning-shaped slice flows all benefit from the decomposition skill.
|
|
57
|
+
// Sanity-check a representative entry from each.
|
|
58
|
+
for (const unitType of ["research-slice", "plan-slice", "refine-slice", "replan-slice"] as const) {
|
|
59
|
+
const allowlist = resolveSkillManifest(unitType);
|
|
60
|
+
assert.ok(
|
|
61
|
+
allowlist?.includes("decompose-into-slices"),
|
|
62
|
+
`${unitType} should list decompose-into-slices`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("resolveSkillManifest: validation / completion flows include verify-before-complete", () => {
|
|
68
|
+
for (const unitType of ["complete-milestone", "validate-milestone", "run-uat"] as const) {
|
|
69
|
+
const allowlist = resolveSkillManifest(unitType);
|
|
70
|
+
assert.ok(
|
|
71
|
+
allowlist?.includes("verify-before-complete"),
|
|
72
|
+
`${unitType} should list verify-before-complete`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("filterSkillsByManifest: pass-through when unit type is unknown", () => {
|
|
78
|
+
const skills = [{ name: "swiftui" }, { name: "solidity-security" }];
|
|
79
|
+
const result = filterSkillsByManifest(skills, "nonexistent-unit-type");
|
|
80
|
+
assert.deepEqual(result, skills);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("filterSkillsByManifest: pass-through when unitType is undefined", () => {
|
|
84
|
+
const skills = [{ name: "swiftui" }];
|
|
85
|
+
const result = filterSkillsByManifest(skills, undefined);
|
|
86
|
+
assert.deepEqual(result, skills);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("filterSkillsByManifest: restricts to allowlisted names for known unit type", () => {
|
|
90
|
+
// research-slice allowlists include decompose-into-slices but not swiftui.
|
|
91
|
+
const skills = [
|
|
92
|
+
{ name: "decompose-into-slices" },
|
|
93
|
+
{ name: "swiftui" },
|
|
94
|
+
{ name: "write-docs" },
|
|
95
|
+
];
|
|
96
|
+
const result = filterSkillsByManifest(skills, "research-slice");
|
|
97
|
+
const names = result.map(s => s.name);
|
|
98
|
+
assert.ok(names.includes("decompose-into-slices"));
|
|
99
|
+
assert.ok(names.includes("write-docs"));
|
|
100
|
+
assert.ok(!names.includes("swiftui"));
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("filterSkillsByManifest: matching is case-insensitive via normalize", () => {
|
|
104
|
+
const skills = [
|
|
105
|
+
{ name: "Write-Docs" }, // different case from manifest entry
|
|
106
|
+
{ name: "SWIFTUI" },
|
|
107
|
+
];
|
|
108
|
+
const result = filterSkillsByManifest(skills, "research-milestone");
|
|
109
|
+
const names = result.map(s => s.name);
|
|
110
|
+
assert.ok(names.includes("Write-Docs"));
|
|
111
|
+
assert.ok(!names.includes("SWIFTUI"));
|
|
112
|
+
});
|