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
|
@@ -627,4 +627,83 @@ Integration tests mock external services.
|
|
|
627
627
|
|
|
628
628
|
assert.strictEqual(result, '', 'empty content returns empty string');
|
|
629
629
|
});
|
|
630
|
+
|
|
631
|
+
// ── Regression: issue #4719 — single-H2 with many H3 entries ──────────────
|
|
632
|
+
// A KNOWLEDGE.md structured as one top-level H2 with many H3 entries must
|
|
633
|
+
// filter at H3 granularity; otherwise one keyword match against the H2
|
|
634
|
+
// header or first paragraph returns the entire file.
|
|
635
|
+
test("single H2 with many H3 entries filters at H3 level (issue #4719)", async () => {
|
|
636
|
+
const singleH2Knowledge = `# Project Knowledge
|
|
637
|
+
|
|
638
|
+
## Patterns
|
|
639
|
+
|
|
640
|
+
### Database: prepared statements
|
|
641
|
+
Always use prepared statements with SQLite.
|
|
642
|
+
|
|
643
|
+
### API: versioned paths
|
|
644
|
+
Use /v1/resource style versioning.
|
|
645
|
+
|
|
646
|
+
### Testing: node:test
|
|
647
|
+
Prefer node:test over external frameworks.
|
|
648
|
+
|
|
649
|
+
### Deployment: blue-green
|
|
650
|
+
Blue-green deployment for zero-downtime releases.
|
|
651
|
+
`;
|
|
652
|
+
|
|
653
|
+
const result = await queryKnowledge(singleH2Knowledge, ['database']);
|
|
654
|
+
|
|
655
|
+
// Should include only the matching H3 entry, not the whole file
|
|
656
|
+
assert.match(result, /Database: prepared statements/, 'includes matching H3 entry');
|
|
657
|
+
assert.ok(
|
|
658
|
+
!result.includes('API: versioned paths'),
|
|
659
|
+
'does not include non-matching H3 entry',
|
|
660
|
+
);
|
|
661
|
+
assert.ok(
|
|
662
|
+
!result.includes('Testing: node:test'),
|
|
663
|
+
'does not include non-matching H3 entry',
|
|
664
|
+
);
|
|
665
|
+
assert.ok(
|
|
666
|
+
!result.includes('Deployment: blue-green'),
|
|
667
|
+
'does not include non-matching H3 entry',
|
|
668
|
+
);
|
|
669
|
+
// The returned payload must be dramatically smaller than the full content
|
|
670
|
+
assert.ok(
|
|
671
|
+
result.length < singleH2Knowledge.length / 2,
|
|
672
|
+
`scoped result (${result.length} chars) should be <50% of full content (${singleH2Knowledge.length} chars)`,
|
|
673
|
+
);
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
test("single H2 with H3 entries returns empty when no H3 matches (issue #4719)", async () => {
|
|
677
|
+
const singleH2Knowledge = `# Project Knowledge
|
|
678
|
+
|
|
679
|
+
## Patterns
|
|
680
|
+
|
|
681
|
+
### Database: prepared statements
|
|
682
|
+
Always use prepared statements with SQLite.
|
|
683
|
+
|
|
684
|
+
### API: versioned paths
|
|
685
|
+
Use /v1/resource style versioning.
|
|
686
|
+
`;
|
|
687
|
+
|
|
688
|
+
const result = await queryKnowledge(singleH2Knowledge, ['nonexistent']);
|
|
689
|
+
|
|
690
|
+
assert.strictEqual(result, '', 'no H3 match returns empty string');
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
test("falls back to H2 when no H3 headings exist at all", async () => {
|
|
694
|
+
// Backwards-compat: files with only H2 topic headers must still filter.
|
|
695
|
+
const h2OnlyKnowledge = `# Project Knowledge
|
|
696
|
+
|
|
697
|
+
## Database Patterns
|
|
698
|
+
Use prepared statements.
|
|
699
|
+
|
|
700
|
+
## API Design
|
|
701
|
+
REST with OpenAPI.
|
|
702
|
+
`;
|
|
703
|
+
|
|
704
|
+
const result = await queryKnowledge(h2OnlyKnowledge, ['database']);
|
|
705
|
+
|
|
706
|
+
assert.match(result, /Database Patterns/, 'H2-only file falls back to H2 filtering');
|
|
707
|
+
assert.ok(!result.includes('API Design'), 'non-matching H2 section excluded');
|
|
708
|
+
});
|
|
630
709
|
});
|
|
@@ -2,6 +2,7 @@ import test from "node:test";
|
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import { extractSourceRegion } from "./test-helpers.ts";
|
|
5
6
|
|
|
6
7
|
test("copyPlanningArtifacts skips when source and destination .gsd resolve to the same path", () => {
|
|
7
8
|
const srcPath = join(import.meta.dirname, "..", "auto-worktree.ts");
|
|
@@ -10,7 +11,7 @@ test("copyPlanningArtifacts skips when source and destination .gsd resolve to th
|
|
|
10
11
|
const fnIdx = src.indexOf("function copyPlanningArtifacts");
|
|
11
12
|
assert.ok(fnIdx !== -1, "copyPlanningArtifacts function exists");
|
|
12
13
|
|
|
13
|
-
const fnBody = src
|
|
14
|
+
const fnBody = extractSourceRegion(src, "function copyPlanningArtifacts");
|
|
14
15
|
|
|
15
16
|
const guardIdx = fnBody.indexOf("if (isSamePath(srcGsd, dstGsd)) return;");
|
|
16
17
|
const copyIdx = fnBody.indexOf("safeCopyRecursive(join(srcGsd, \"milestones\")");
|
|
@@ -11,6 +11,7 @@ import { describe, it } from 'node:test'
|
|
|
11
11
|
import assert from 'node:assert/strict'
|
|
12
12
|
import { readFileSync } from 'node:fs'
|
|
13
13
|
import { resolve } from 'node:path'
|
|
14
|
+
import { extractSourceRegion } from "./test-helpers.ts";
|
|
14
15
|
|
|
15
16
|
const template = readFileSync(
|
|
16
17
|
resolve(process.cwd(), 'src', 'resources', 'extensions', 'gsd', 'prompts', 'guided-discuss-slice.md'),
|
|
@@ -37,7 +38,7 @@ describe('discuss-slice structuredQuestionsAvailable template variable', () => {
|
|
|
37
38
|
const falseIdx = template.indexOf('`{{structuredQuestionsAvailable}}` is `false`')
|
|
38
39
|
assert.ok(falseIdx !== -1)
|
|
39
40
|
|
|
40
|
-
const afterFalse = template
|
|
41
|
+
const afterFalse = extractSourceRegion(template, '`{{structuredQuestionsAvailable}}` is `false`')
|
|
41
42
|
assert.ok(
|
|
42
43
|
afterFalse.includes('plain text'),
|
|
43
44
|
'when structuredQuestionsAvailable is false, questions should be in plain text',
|
|
@@ -22,6 +22,7 @@ import { join, dirname } from "node:path";
|
|
|
22
22
|
import { fileURLToPath } from "node:url";
|
|
23
23
|
|
|
24
24
|
import { DISCUSS_TOOLS_ALLOWLIST } from "../constants.ts";
|
|
25
|
+
import { extractSourceRegion } from "./test-helpers.ts";
|
|
25
26
|
|
|
26
27
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
27
28
|
const guidedFlowSource = readFileSync(join(__dirname, "..", "guided-flow.ts"), "utf-8");
|
|
@@ -58,7 +59,7 @@ describe("#3616 — discuss tool scoping must not leak across sessions", () => {
|
|
|
58
59
|
);
|
|
59
60
|
const newSessionStart = agentSessionSource.indexOf("async newSession(options?:");
|
|
60
61
|
assert.ok(newSessionStart >= 0, "should find newSession");
|
|
61
|
-
const body = agentSessionSource
|
|
62
|
+
const body = extractSourceRegion(agentSessionSource, "async newSession(options?:");
|
|
62
63
|
|
|
63
64
|
// Both branches (cwd-changed and cwd-unchanged) must include extension tools
|
|
64
65
|
assert.ok(
|
|
@@ -4,6 +4,7 @@ import { readFileSync } from "node:fs";
|
|
|
4
4
|
import { join, dirname } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { AutoSession } from "../auto/session.ts";
|
|
7
|
+
import { extractSourceRegion } from "./test-helpers.ts";
|
|
7
8
|
|
|
8
9
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
10
|
|
|
@@ -20,7 +21,7 @@ describe("double mergeAndExit guard (#2645)", () => {
|
|
|
20
21
|
const completeIdx = phasesSrc.indexOf('state.phase === "complete"');
|
|
21
22
|
assert.ok(completeIdx > 0, "phases.ts should have a 'complete' phase check");
|
|
22
23
|
|
|
23
|
-
const afterComplete = phasesSrc
|
|
24
|
+
const afterComplete = extractSourceRegion(phasesSrc, 'state.phase === "complete"');
|
|
24
25
|
const mergeIdx = afterComplete.indexOf("deps.resolver.mergeAndExit");
|
|
25
26
|
const flagIdx = afterComplete.indexOf("s.milestoneMergedInPhases = true");
|
|
26
27
|
|
|
@@ -42,7 +43,7 @@ describe("double mergeAndExit guard (#2645)", () => {
|
|
|
42
43
|
const allCompleteIdx = phasesSrc.indexOf("incomplete.length === 0");
|
|
43
44
|
assert.ok(allCompleteIdx > 0, "phases.ts should have an all-milestones-complete check");
|
|
44
45
|
|
|
45
|
-
const afterAllComplete = phasesSrc
|
|
46
|
+
const afterAllComplete = extractSourceRegion(phasesSrc, "incomplete.length === 0");
|
|
46
47
|
const mergeIdx = afterAllComplete.indexOf("deps.resolver.mergeAndExit");
|
|
47
48
|
const flagIdx = afterAllComplete.indexOf("s.milestoneMergedInPhases = true");
|
|
48
49
|
|
|
@@ -64,7 +65,7 @@ describe("double mergeAndExit guard (#2645)", () => {
|
|
|
64
65
|
const step4Idx = autoSrc.indexOf("Step 4: Auto-worktree exit");
|
|
65
66
|
assert.ok(step4Idx > 0, "auto.ts should have Step 4 worktree exit");
|
|
66
67
|
|
|
67
|
-
const step4Block = autoSrc
|
|
68
|
+
const step4Block = extractSourceRegion(autoSrc, "Step 4: Auto-worktree exit");
|
|
68
69
|
assert.ok(
|
|
69
70
|
step4Block.includes("milestoneMergedInPhases"),
|
|
70
71
|
"stopAuto Step 4 must check milestoneMergedInPhases before merging",
|
|
@@ -13,6 +13,7 @@ import assert from "node:assert/strict";
|
|
|
13
13
|
import { readFileSync } from "node:fs";
|
|
14
14
|
import { join, dirname } from "node:path";
|
|
15
15
|
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { extractSourceRegion } from "./test-helpers.ts";
|
|
16
17
|
|
|
17
18
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
19
|
const RECOVERY_PATH = join(__dirname, "..", "bootstrap", "agent-end-recovery.ts");
|
|
@@ -30,7 +31,7 @@ test("agent-end-recovery.ts does not pause on aborted messages with empty conten
|
|
|
30
31
|
assert.ok(abortIdx > -1, "abort handler must exist in agent-end-recovery.ts");
|
|
31
32
|
|
|
32
33
|
// Extract the region around the abort handler (enough to see the guard logic)
|
|
33
|
-
const abortRegion = source
|
|
34
|
+
const abortRegion = extractSourceRegion(source, 'stopReason === "aborted"', { fromIdx: abortIdx });
|
|
34
35
|
|
|
35
36
|
// Must check for empty content before pausing
|
|
36
37
|
assert.ok(
|
|
@@ -48,7 +49,7 @@ test("agent-end-recovery.ts routes empty-content aborted messages to resolveAgen
|
|
|
48
49
|
assert.ok(abortIdx > -1, "abort handler must exist");
|
|
49
50
|
|
|
50
51
|
// Get the full abort handling block (from the if to the next stopReason check or success path)
|
|
51
|
-
const afterAbort = source
|
|
52
|
+
const afterAbort = extractSourceRegion(source, 'stopReason === "aborted"');
|
|
52
53
|
|
|
53
54
|
// The abort block must have a code path that calls resolveAgentEnd (for empty-content case)
|
|
54
55
|
assert.ok(
|
|
@@ -63,7 +64,7 @@ test("agent-end-recovery.ts checks for errorMessage presence in abort handler (#
|
|
|
63
64
|
const abortIdx = source.indexOf('stopReason === "aborted"');
|
|
64
65
|
assert.ok(abortIdx > -1, "abort handler must exist");
|
|
65
66
|
|
|
66
|
-
const abortRegion = source
|
|
67
|
+
const abortRegion = extractSourceRegion(source, 'stopReason === "aborted"');
|
|
67
68
|
|
|
68
69
|
// Fatal aborts should have error context (errorMessage field).
|
|
69
70
|
// The handler should check for this to distinguish fatal from non-fatal aborts.
|
|
@@ -1,154 +1,164 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Behavioural contract for GSD extension bootstrap isolation (#4168, #4172).
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
// (
|
|
5
|
-
// Windows-specific
|
|
6
|
-
// prevented /gsd from being registered at all
|
|
3
|
+
// Guarantee: the `/gsd` slash command must be registered on pi even if the
|
|
4
|
+
// full bootstrap (shortcuts, tools, hooks, ecosystem) throws during import or
|
|
5
|
+
// execution. Prior regressions: a Windows-specific failure in register-
|
|
6
|
+
// shortcuts.ts silently prevented /gsd from being registered at all because
|
|
7
|
+
// registerGSDCommand was called inside the same try that loaded shortcuts.
|
|
8
|
+
//
|
|
9
|
+
// These tests exercise the real default export of index.ts (which calls
|
|
10
|
+
// registerGSDCommand via dynamic import, then attempts the full bootstrap)
|
|
11
|
+
// with a minimal mock ExtensionAPI and verify the observable behaviour
|
|
12
|
+
// directly: /gsd is registered in both the happy path and the degraded path.
|
|
13
|
+
//
|
|
14
|
+
// Anti-regression proof (documented in commit):
|
|
15
|
+
// neuter index.ts to register /gsd inside the same try{} as
|
|
16
|
+
// register-extension → the degraded-path test fails (no /gsd command
|
|
17
|
+
// registered when register-extension throws). Restore → passes.
|
|
7
18
|
|
|
8
19
|
import { describe, test } from "node:test";
|
|
9
20
|
import assert from "node:assert/strict";
|
|
10
|
-
import { readFileSync } from "node:fs";
|
|
11
|
-
import { join, dirname } from "node:path";
|
|
12
|
-
import { fileURLToPath } from "node:url";
|
|
13
|
-
|
|
14
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
-
const indexSrc = readFileSync(join(__dirname, "../index.ts"), "utf-8");
|
|
16
|
-
const registerExtSrc = readFileSync(
|
|
17
|
-
join(__dirname, "../bootstrap/register-extension.ts"),
|
|
18
|
-
"utf-8",
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
// ─── index.ts: core /gsd command must be registered before full bootstrap ─────
|
|
22
|
-
|
|
23
|
-
describe("index.ts bootstrap isolation", () => {
|
|
24
|
-
test("imports registerGSDCommand from commands/index.js separately", () => {
|
|
25
|
-
assert.ok(
|
|
26
|
-
indexSrc.includes('./commands/index.js"') || indexSrc.includes("./commands/index.js'"),
|
|
27
|
-
"index.ts must import registerGSDCommand from ./commands/index.js",
|
|
28
|
-
);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test("calls registerGSDCommand before importing register-extension.js", () => {
|
|
32
|
-
const gsdCommandCallPos = indexSrc.indexOf("registerGSDCommand(pi)");
|
|
33
|
-
const bootstrapImportPos = indexSrc.indexOf(
|
|
34
|
-
'./bootstrap/register-extension.js"',
|
|
35
|
-
);
|
|
36
21
|
|
|
37
|
-
|
|
38
|
-
|
|
22
|
+
import registerExtension from "../index.ts";
|
|
23
|
+
|
|
24
|
+
type RegisterFn = (name: string, def: unknown) => void;
|
|
25
|
+
|
|
26
|
+
function makePi(overrides: Partial<Record<string, unknown>> = {}) {
|
|
27
|
+
const registered: Array<[string, unknown]> = [];
|
|
28
|
+
const registerCommand: RegisterFn = (name, def) => {
|
|
29
|
+
registered.push([name, def]);
|
|
30
|
+
};
|
|
31
|
+
const events = {
|
|
32
|
+
on: () => {},
|
|
33
|
+
off: () => {},
|
|
34
|
+
emit: () => {},
|
|
35
|
+
};
|
|
36
|
+
const pi = {
|
|
37
|
+
registerCommand,
|
|
38
|
+
registerTool: () => {},
|
|
39
|
+
registerHook: () => {},
|
|
40
|
+
registerShortcut: () => {},
|
|
41
|
+
events,
|
|
42
|
+
...overrides,
|
|
43
|
+
};
|
|
44
|
+
return { pi, registered };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
describe("extension bootstrap isolation (#4168, #4172)", () => {
|
|
48
|
+
test("happy path: /gsd command is registered", async () => {
|
|
49
|
+
const { pi, registered } = makePi();
|
|
50
|
+
await registerExtension(pi as any);
|
|
51
|
+
const names = registered.map(([n]) => n);
|
|
39
52
|
assert.ok(
|
|
40
|
-
|
|
41
|
-
|
|
53
|
+
names.includes("gsd"),
|
|
54
|
+
`expected 'gsd' in registered commands, got ${JSON.stringify(names)}`,
|
|
42
55
|
);
|
|
43
56
|
});
|
|
44
57
|
|
|
45
|
-
test("
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
test("degraded path: /gsd still registered when registerCommand throws for non-core commands", async () => {
|
|
59
|
+
// Simulate the Windows-style failure: pi.registerCommand throws for a
|
|
60
|
+
// specific non-core command ('kill' is a simple target registered by
|
|
61
|
+
// the full bootstrap) — the full bootstrap must fail but /gsd must
|
|
62
|
+
// already be registered before the failure occurs.
|
|
63
|
+
const registered: Array<[string, unknown]> = [];
|
|
64
|
+
const pi = {
|
|
65
|
+
registerCommand: (name: string, def: unknown) => {
|
|
66
|
+
if (name !== "gsd" && name !== "worktree" && name !== "exit") {
|
|
67
|
+
// Let /gsd, /worktree, /exit succeed (they precede the non-core
|
|
68
|
+
// loop); throw when the first non-core registration fires.
|
|
69
|
+
}
|
|
70
|
+
if (name === "kill") throw new Error("simulated windows failure");
|
|
71
|
+
registered.push([name, def]);
|
|
72
|
+
},
|
|
73
|
+
registerTool: () => {},
|
|
74
|
+
registerHook: () => {},
|
|
75
|
+
registerShortcut: () => {},
|
|
76
|
+
events: { on: () => {}, off: () => {}, emit: () => {} },
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// registerExtension must not throw — the outer try/catch in index.ts
|
|
80
|
+
// swallows bootstrap failures after /gsd is already registered.
|
|
81
|
+
await registerExtension(pi as any);
|
|
82
|
+
|
|
83
|
+
const names = registered.map(([n]) => n);
|
|
55
84
|
assert.ok(
|
|
56
|
-
|
|
57
|
-
"
|
|
85
|
+
names.includes("gsd"),
|
|
86
|
+
"expected 'gsd' to be registered even when a later command registration throws",
|
|
58
87
|
);
|
|
59
88
|
});
|
|
60
89
|
|
|
61
|
-
test("
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
90
|
+
test("degraded path: /gsd registered BEFORE any non-core command", async () => {
|
|
91
|
+
// Ordering guard: the first registerCommand call must be for 'gsd',
|
|
92
|
+
// because index.ts awaits registerGSDCommand(pi) before importing
|
|
93
|
+
// register-extension. Regression scenario: if a future refactor moves
|
|
94
|
+
// registerGSDCommand into the try block or after other registrations,
|
|
95
|
+
// a failure in those earlier registrations would take /gsd down too.
|
|
96
|
+
const calls: string[] = [];
|
|
97
|
+
const pi = {
|
|
98
|
+
registerCommand: (name: string) => {
|
|
99
|
+
calls.push(name);
|
|
100
|
+
},
|
|
101
|
+
registerTool: () => {},
|
|
102
|
+
registerHook: () => {},
|
|
103
|
+
registerShortcut: () => {},
|
|
104
|
+
events: { on: () => {}, off: () => {}, emit: () => {} },
|
|
105
|
+
};
|
|
106
|
+
await registerExtension(pi as any);
|
|
107
|
+
assert.ok(calls.length > 0, "expected at least one registerCommand call");
|
|
108
|
+
assert.equal(
|
|
109
|
+
calls[0],
|
|
110
|
+
"gsd",
|
|
111
|
+
`expected 'gsd' to be the first command registered, got ${JSON.stringify(calls)}`,
|
|
69
112
|
);
|
|
70
113
|
});
|
|
71
114
|
});
|
|
72
115
|
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
);
|
|
98
|
-
const funcBody = registerExtSrc.slice(funcBodyStart);
|
|
99
|
-
|
|
100
|
-
assert.ok(
|
|
101
|
-
funcBody.includes("registerWorktreeCommand(pi)"),
|
|
102
|
-
"must register worktree command",
|
|
103
|
-
);
|
|
104
|
-
assert.ok(
|
|
105
|
-
funcBody.includes("registerExitCommand(pi)"),
|
|
106
|
-
"must register exit command",
|
|
107
|
-
);
|
|
108
|
-
assert.ok(
|
|
109
|
-
funcBody.includes('"kill"'),
|
|
110
|
-
"must register kill command",
|
|
111
|
-
);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
test("wraps non-critical registrations in individual try-catch blocks", () => {
|
|
115
|
-
const funcBodyStart = registerExtSrc.indexOf(
|
|
116
|
-
"export function registerGsdExtension",
|
|
117
|
-
);
|
|
118
|
-
const funcBody = registerExtSrc.slice(funcBodyStart);
|
|
119
|
-
|
|
120
|
-
// Each non-critical registration should be wrapped with error handling
|
|
121
|
-
const registrationNames = [
|
|
122
|
-
"dynamic-tools",
|
|
123
|
-
"db-tools",
|
|
124
|
-
"journal-tools",
|
|
125
|
-
"query-tools",
|
|
126
|
-
"shortcuts",
|
|
127
|
-
"hooks",
|
|
128
|
-
];
|
|
129
|
-
|
|
130
|
-
for (const name of registrationNames) {
|
|
131
|
-
assert.ok(
|
|
132
|
-
funcBody.includes(`"${name}"`),
|
|
133
|
-
`non-critical registration "${name}" must be present`,
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Must have try-catch inside the registration loop
|
|
116
|
+
// Behavioural contract for registerGsdExtension itself: each non-core
|
|
117
|
+
// registration is wrapped in its own try/catch so one failure does not
|
|
118
|
+
// prevent siblings from loading.
|
|
119
|
+
|
|
120
|
+
import { registerGsdExtension } from "../bootstrap/register-extension.ts";
|
|
121
|
+
|
|
122
|
+
describe("registerGsdExtension defensive registration", () => {
|
|
123
|
+
test("a failing shortcut registration does not prevent kill command registration", async () => {
|
|
124
|
+
// `shortcuts` is registered via a non-critical slot that is wrapped in
|
|
125
|
+
// its own try/catch. `kill` is registered before the non-critical loop
|
|
126
|
+
// as a critical command. Simulate: registerShortcut throws. Expect:
|
|
127
|
+
// 'kill' is still registered, registerGsdExtension does not throw.
|
|
128
|
+
const registered: string[] = [];
|
|
129
|
+
const pi = {
|
|
130
|
+
registerCommand: (name: string) => {
|
|
131
|
+
registered.push(name);
|
|
132
|
+
},
|
|
133
|
+
registerTool: () => {},
|
|
134
|
+
registerHook: () => {},
|
|
135
|
+
registerShortcut: () => {
|
|
136
|
+
throw new Error("simulated platform-specific shortcut failure");
|
|
137
|
+
},
|
|
138
|
+
events: { on: () => {}, off: () => {}, emit: () => {} },
|
|
139
|
+
};
|
|
140
|
+
assert.doesNotThrow(() => registerGsdExtension(pi as any));
|
|
138
141
|
assert.ok(
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
registered.includes("kill"),
|
|
143
|
+
`expected 'kill' to be registered despite shortcut failure, got ${JSON.stringify(registered)}`,
|
|
141
144
|
);
|
|
142
145
|
});
|
|
143
146
|
|
|
144
|
-
test("
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
test("does NOT register /gsd (caller's responsibility, avoids double-registration)", () => {
|
|
148
|
+
const registered: string[] = [];
|
|
149
|
+
const pi = {
|
|
150
|
+
registerCommand: (name: string) => {
|
|
151
|
+
registered.push(name);
|
|
152
|
+
},
|
|
153
|
+
registerTool: () => {},
|
|
154
|
+
registerHook: () => {},
|
|
155
|
+
registerShortcut: () => {},
|
|
156
|
+
events: { on: () => {}, off: () => {}, emit: () => {} },
|
|
157
|
+
};
|
|
158
|
+
registerGsdExtension(pi as any);
|
|
149
159
|
assert.ok(
|
|
150
|
-
|
|
151
|
-
|
|
160
|
+
!registered.includes("gsd"),
|
|
161
|
+
`registerGsdExtension must NOT register 'gsd' (it is registered separately by index.ts), got ${JSON.stringify(registered)}`,
|
|
152
162
|
);
|
|
153
163
|
});
|
|
154
164
|
});
|
|
@@ -26,14 +26,6 @@ import { MAX_FINALIZE_TIMEOUTS } from "../auto/types.ts";
|
|
|
26
26
|
|
|
27
27
|
const { assertTrue, assertEq, report } = createTestContext();
|
|
28
28
|
|
|
29
|
-
function getRunFinalizeBody(phasesSource: string): string {
|
|
30
|
-
const fnIdx = phasesSource.indexOf("export async function runFinalize(");
|
|
31
|
-
assertTrue(fnIdx > 0, "runFinalize function should exist in phases.ts");
|
|
32
|
-
|
|
33
|
-
const nextExportIdx = phasesSource.indexOf("\nexport ", fnIdx + 1);
|
|
34
|
-
return phasesSource.slice(fnIdx, nextExportIdx > fnIdx ? nextExportIdx : undefined);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
29
|
// ═══ Test: withTimeout resolves when inner promise resolves promptly ══════════
|
|
38
30
|
|
|
39
31
|
{
|
|
@@ -142,45 +134,6 @@ function getRunFinalizeBody(phasesSource: string): string {
|
|
|
142
134
|
assertEq(result.timedOut, false, "should not time out");
|
|
143
135
|
}
|
|
144
136
|
|
|
145
|
-
// ═══ Test: runFinalize wraps BOTH pre and post verification with withTimeout ═
|
|
146
|
-
|
|
147
|
-
{
|
|
148
|
-
console.log("\n=== #3757: runFinalize wraps preVerification with timeout guard ===");
|
|
149
|
-
|
|
150
|
-
const { readFileSync } = await import("node:fs");
|
|
151
|
-
const phasesSource = readFileSync(
|
|
152
|
-
new URL("../auto/phases.ts", import.meta.url),
|
|
153
|
-
"utf-8",
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
const fnBody = getRunFinalizeBody(phasesSource);
|
|
157
|
-
|
|
158
|
-
// postUnitPreVerification must be wrapped in withTimeout
|
|
159
|
-
const preTimeoutIdx = fnBody.indexOf("withTimeout(");
|
|
160
|
-
assertTrue(preTimeoutIdx > 0, "withTimeout should appear in runFinalize");
|
|
161
|
-
|
|
162
|
-
const preVerIdx = fnBody.indexOf("postUnitPreVerification");
|
|
163
|
-
assertTrue(preVerIdx > 0, "postUnitPreVerification should appear in runFinalize");
|
|
164
|
-
|
|
165
|
-
// The first withTimeout should wrap postUnitPreVerification (not postUnitPostVerification)
|
|
166
|
-
const firstWithTimeout = fnBody.slice(preTimeoutIdx, preTimeoutIdx + 200);
|
|
167
|
-
assertTrue(
|
|
168
|
-
firstWithTimeout.includes("postUnitPreVerification"),
|
|
169
|
-
"first withTimeout in runFinalize should wrap postUnitPreVerification",
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
// postUnitPostVerification must also be wrapped
|
|
173
|
-
const postVerIdx = fnBody.indexOf("postUnitPostVerification");
|
|
174
|
-
assertTrue(postVerIdx > 0, "postUnitPostVerification should appear in runFinalize");
|
|
175
|
-
|
|
176
|
-
// Count withTimeout occurrences — should be at least 2 (pre + post)
|
|
177
|
-
const timeoutCount = (fnBody.match(/withTimeout\(/g) || []).length;
|
|
178
|
-
assertTrue(
|
|
179
|
-
timeoutCount >= 2,
|
|
180
|
-
`runFinalize should have at least 2 withTimeout guards (found ${timeoutCount})`,
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
137
|
// ═══ Test: MAX_FINALIZE_TIMEOUTS is defined and reasonable ═══════════════════
|
|
185
138
|
|
|
186
139
|
{
|
|
@@ -200,62 +153,13 @@ function getRunFinalizeBody(phasesSource: string): string {
|
|
|
200
153
|
);
|
|
201
154
|
}
|
|
202
155
|
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
"utf-8",
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
const fnBody = getRunFinalizeBody(phasesSource);
|
|
215
|
-
|
|
216
|
-
const helperCallCount = (fnBody.match(/failClosedOnFinalizeTimeout\(/g) || []).length;
|
|
217
|
-
assertTrue(
|
|
218
|
-
helperCallCount >= 2,
|
|
219
|
-
`runFinalize should route both timeout branches through failClosedOnFinalizeTimeout (found ${helperCallCount})`,
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
const helperStart = phasesSource.indexOf("async function failClosedOnFinalizeTimeout");
|
|
223
|
-
assertTrue(helperStart > 0, "failClosedOnFinalizeTimeout helper should exist");
|
|
224
|
-
const helperEnd = phasesSource.indexOf("// ─── runPreDispatch", helperStart);
|
|
225
|
-
const helperBody = phasesSource.slice(helperStart, helperEnd > helperStart ? helperEnd : undefined);
|
|
226
|
-
|
|
227
|
-
const incrementCount = (helperBody.match(/consecutiveFinalizeTimeouts\+\+/g) || []).length;
|
|
228
|
-
assertTrue(
|
|
229
|
-
incrementCount >= 1,
|
|
230
|
-
`timeout helper should increment consecutiveFinalizeTimeouts (found ${incrementCount})`,
|
|
231
|
-
);
|
|
232
|
-
|
|
233
|
-
const detachCount = (helperBody.match(/s\.currentUnit\s*=\s*null/g) || []).length;
|
|
234
|
-
assertTrue(
|
|
235
|
-
detachCount >= 1,
|
|
236
|
-
`timeout helper should detach s.currentUnit (found ${detachCount})`,
|
|
237
|
-
);
|
|
238
|
-
|
|
239
|
-
const pauseCount = (helperBody.match(/pauseAuto\(/g) || []).length;
|
|
240
|
-
assertTrue(
|
|
241
|
-
pauseCount >= 1,
|
|
242
|
-
`timeout helper should pause auto-mode (found ${pauseCount})`,
|
|
243
|
-
);
|
|
244
|
-
|
|
245
|
-
assertTrue(
|
|
246
|
-
helperBody.includes('eventType: "unit-end"'),
|
|
247
|
-
"timeout helper should emit a terminal unit-end event",
|
|
248
|
-
);
|
|
249
|
-
assertTrue(
|
|
250
|
-
helperBody.includes('phase: "finalize-timeout"'),
|
|
251
|
-
"timeout helper should persist finalize-timeout runtime state",
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
// Successful finalize should reset the counter
|
|
255
|
-
assertTrue(
|
|
256
|
-
fnBody.includes("consecutiveFinalizeTimeouts = 0"),
|
|
257
|
-
"should reset consecutiveFinalizeTimeouts on successful finalize",
|
|
258
|
-
);
|
|
259
|
-
}
|
|
156
|
+
// Note: the two previous source-grep blocks that scanned phases.ts for
|
|
157
|
+
// `withTimeout(` / `failClosedOnFinalizeTimeout(` occurrences were removed
|
|
158
|
+
// under #4825 — they encoded implementation shape (Goodhart) and broke on
|
|
159
|
+
// any helper/loop refactor without catching a real regression. The intended
|
|
160
|
+
// behavioural invariant (pre+post verification hangs → pauseAuto called,
|
|
161
|
+
// unit-end emitted, escalation counter incremented) should be covered by a
|
|
162
|
+
// runFinalize integration test with mocked hanging verification — tracked
|
|
163
|
+
// separately. Refs #4825.
|
|
260
164
|
|
|
261
165
|
report();
|