gsd-pi 2.77.0-dev.58d3d4d6c → 2.77.0-dev.cfd69e714
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/claude-cli-check.js +5 -1
- package/dist/headless.js +49 -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/claude-code-cli/readiness.js +5 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +481 -17
- package/dist/resources/extensions/gsd/auto/loop.js +43 -0
- package/dist/resources/extensions/gsd/auto/phases.js +15 -21
- package/dist/resources/extensions/gsd/auto/session.js +0 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +102 -24
- package/dist/resources/extensions/gsd/auto-model-selection.js +124 -4
- package/dist/resources/extensions/gsd/auto-post-unit.js +71 -64
- package/dist/resources/extensions/gsd/auto-prompts.js +329 -102
- package/dist/resources/extensions/gsd/auto-recovery.js +195 -23
- package/dist/resources/extensions/gsd/auto-start.js +34 -24
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +47 -7
- package/dist/resources/extensions/gsd/auto-worktree.js +122 -26
- package/dist/resources/extensions/gsd/auto.js +31 -20
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +209 -0
- package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +3 -6
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -3
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +127 -9
- 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/detection.js +49 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/dist/resources/extensions/gsd/gate-registry.js +2 -2
- package/dist/resources/extensions/gsd/git-constants.js +28 -1
- package/dist/resources/extensions/gsd/git-self-heal.js +27 -0
- package/dist/resources/extensions/gsd/git-service.js +126 -2
- package/dist/resources/extensions/gsd/gsd-db.js +6 -3
- package/dist/resources/extensions/gsd/guided-flow.js +17 -5
- 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/native-git-bridge.js +34 -4
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
- package/dist/resources/extensions/gsd/safety/git-checkpoint.js +11 -0
- package/dist/resources/extensions/gsd/service-tier.js +5 -2
- package/dist/resources/extensions/gsd/session-lock.js +19 -10
- package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +278 -8
- package/dist/resources/extensions/gsd/state.js +44 -33
- package/dist/resources/extensions/gsd/sync-lock.js +98 -42
- package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
- package/dist/resources/extensions/gsd/unit-context-manifest.js +370 -0
- package/dist/resources/extensions/gsd/uok/gate-runner.js +53 -5
- package/dist/resources/extensions/gsd/workflow-mcp.js +6 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +34 -8
- 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 +5 -5
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +5 -5
- package/dist/web/standalone/.next/server/chunks/1926.js +1 -1
- 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 +2 -3
- package/packages/daemon/src/logger.ts +4 -3
- package/packages/mcp-server/dist/server.d.ts +24 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +88 -87
- package/packages/mcp-server/dist/server.js.map +1 -1
- 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/secure-env-collect.test.ts +232 -237
- package/packages/mcp-server/src/server.ts +131 -105
- package/packages/mcp-server/src/workflow-tools.test.ts +80 -39
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- 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/capability-patches.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/capability-patches.js +9 -2
- package/packages/pi-ai/dist/models/capability-patches.js.map +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 +36 -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/capability-patches.ts +10 -2
- 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 +48 -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/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/controllers/input-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +18 -3
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +125 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +105 -13
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.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/dynamic-border.test.ts +26 -20
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +146 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +20 -3
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +2 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +119 -13
- 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/editor.d.ts +14 -0
- package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/editor.js +19 -0
- package/packages/pi-tui/dist/components/editor.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/dist/editor-component.d.ts +2 -0
- package/packages/pi-tui/dist/editor-component.d.ts.map +1 -1
- package/packages/pi-tui/dist/editor-component.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/editor.ts +22 -0
- package/packages/pi-tui/src/components/image.test.ts +10 -5
- package/packages/pi-tui/src/editor-component.ts +3 -0
- 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/readiness.ts +5 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +518 -19
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +919 -75
- 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/loop.ts +47 -0
- package/src/resources/extensions/gsd/auto/phases.ts +16 -20
- package/src/resources/extensions/gsd/auto/session.ts +0 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +113 -24
- package/src/resources/extensions/gsd/auto-model-selection.ts +131 -4
- package/src/resources/extensions/gsd/auto-post-unit.ts +82 -73
- package/src/resources/extensions/gsd/auto-prompts.ts +330 -90
- package/src/resources/extensions/gsd/auto-recovery.ts +225 -24
- package/src/resources/extensions/gsd/auto-start.ts +54 -6
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +51 -7
- package/src/resources/extensions/gsd/auto-worktree.ts +130 -26
- package/src/resources/extensions/gsd/auto.ts +43 -22
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +9 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +221 -0
- package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +3 -7
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -3
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +158 -9
- 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/detection.ts +58 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/src/resources/extensions/gsd/gate-registry.ts +2 -2
- package/src/resources/extensions/gsd/git-constants.ts +30 -1
- package/src/resources/extensions/gsd/git-self-heal.ts +31 -0
- package/src/resources/extensions/gsd/git-service.ts +133 -2
- package/src/resources/extensions/gsd/gsd-db.ts +6 -3
- package/src/resources/extensions/gsd/guided-flow.ts +20 -5
- 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/native-git-bridge.ts +34 -4
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
- package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
- package/src/resources/extensions/gsd/safety/git-checkpoint.ts +15 -0
- package/src/resources/extensions/gsd/service-tier.ts +5 -2
- package/src/resources/extensions/gsd/session-lock.ts +20 -10
- package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +309 -8
- package/src/resources/extensions/gsd/state.ts +49 -44
- package/src/resources/extensions/gsd/sync-lock.ts +97 -39
- package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +270 -0
- package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +341 -0
- package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +264 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +94 -289
- package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +742 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +78 -0
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +61 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -197
- package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
- package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +16 -8
- 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/crash-recovery.test.ts +50 -1
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +91 -3
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +9 -105
- package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/gate-storage.test.ts +1 -1
- 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 -57
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +20 -0
- 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 +144 -7
- package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -16
- package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
- 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 -62
- 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 -49
- package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -133
- package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
- package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +32 -1
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +23 -24
- package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +75 -2
- 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/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/session-lock-regression.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
- package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +158 -0
- 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-parallel-orchestrator.test.ts +164 -1
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +5 -5
- package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
- package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
- package/src/resources/extensions/gsd/tests/sync-lock.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/test-helpers.test.ts +12 -61
- package/src/resources/extensions/gsd/tests/test-helpers.ts +21 -8
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +61 -1
- package/src/resources/extensions/gsd/tests/tool-naming.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 +258 -0
- package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +75 -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 -81
- 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/write-gate-planning-unit.test.ts +262 -0
- package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +7 -5
- package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
- package/src/resources/extensions/gsd/types.ts +3 -3
- package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
- package/src/resources/extensions/gsd/unit-context-manifest.ts +574 -0
- package/src/resources/extensions/gsd/uok/gate-runner.ts +65 -5
- package/src/resources/extensions/gsd/workflow-mcp.ts +6 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +55 -7
- 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 -144
- 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 -75
- package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
- package/src/resources/extensions/gsd/tests/forensics-worktree-telemetry.test.ts +0 -145
- 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 -130
- package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -43
- /package/dist/web/standalone/.next/static/{Cev5xrAYA3ZGTRLyjR2fX → SvCJDZPQW104bR1KnBQg1}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{Cev5xrAYA3ZGTRLyjR2fX → SvCJDZPQW104bR1KnBQg1}/_ssgManifest.js +0 -0
|
@@ -1,7 +1,19 @@
|
|
|
1
|
+
import type { ImageContent } from "@gsd/pi-ai";
|
|
1
2
|
import { dispatchSlashCommand } from "../slash-command-handlers.js";
|
|
2
3
|
import type { InteractiveModeStateHost } from "../interactive-mode-state.js";
|
|
3
4
|
import type { ContextualTips } from "../../../core/contextual-tips.js";
|
|
4
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Consume and clear any pending pasted images from the host.
|
|
8
|
+
* Returns undefined if there are no pending images.
|
|
9
|
+
*/
|
|
10
|
+
function consumePendingImages(host: InteractiveModeStateHost): ImageContent[] | undefined {
|
|
11
|
+
if (host.pendingImages.length === 0) return undefined;
|
|
12
|
+
const images = [...host.pendingImages];
|
|
13
|
+
host.pendingImages.length = 0;
|
|
14
|
+
return images;
|
|
15
|
+
}
|
|
16
|
+
|
|
5
17
|
export function setupEditorSubmitHandler(host: InteractiveModeStateHost & {
|
|
6
18
|
getSlashCommandContext: () => any;
|
|
7
19
|
handleBashCommand: (command: string, excludeFromContext?: boolean) => Promise<void>;
|
|
@@ -26,6 +38,7 @@ export function setupEditorSubmitHandler(host: InteractiveModeStateHost & {
|
|
|
26
38
|
const handled = await dispatchSlashCommand(text, host.getSlashCommandContext());
|
|
27
39
|
if (handled) {
|
|
28
40
|
host.editor.setText("");
|
|
41
|
+
consumePendingImages(host); // discard images on slash command
|
|
29
42
|
return;
|
|
30
43
|
}
|
|
31
44
|
if (!host.isKnownSlashCommand(text)) {
|
|
@@ -53,10 +66,14 @@ export function setupEditorSubmitHandler(host: InteractiveModeStateHost & {
|
|
|
53
66
|
await host.handleBashCommand(command, isExcluded);
|
|
54
67
|
host.isBashMode = false;
|
|
55
68
|
host.updateEditorBorderColor();
|
|
69
|
+
consumePendingImages(host); // discard images on bash command
|
|
56
70
|
return;
|
|
57
71
|
}
|
|
58
72
|
}
|
|
59
73
|
|
|
74
|
+
// Consume pending images for prompt submissions
|
|
75
|
+
const images = consumePendingImages(host);
|
|
76
|
+
|
|
60
77
|
// Evaluate contextual tips before sending to agent
|
|
61
78
|
const tip = host.contextualTips.evaluate({
|
|
62
79
|
input: text,
|
|
@@ -73,7 +90,7 @@ export function setupEditorSubmitHandler(host: InteractiveModeStateHost & {
|
|
|
73
90
|
host.editor.addToHistory?.(text);
|
|
74
91
|
host.editor.setText("");
|
|
75
92
|
try {
|
|
76
|
-
await host.session.prompt(text);
|
|
93
|
+
await host.session.prompt(text, { images });
|
|
77
94
|
} catch (error: unknown) {
|
|
78
95
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
79
96
|
host.showError(errorMessage);
|
|
@@ -87,7 +104,7 @@ export function setupEditorSubmitHandler(host: InteractiveModeStateHost & {
|
|
|
87
104
|
if (host.session.isStreaming) {
|
|
88
105
|
host.editor.addToHistory?.(text);
|
|
89
106
|
host.editor.setText("");
|
|
90
|
-
await host.session.prompt(text, { streamingBehavior: "steer" });
|
|
107
|
+
await host.session.prompt(text, { streamingBehavior: "steer", images });
|
|
91
108
|
host.updatePendingMessagesDisplay();
|
|
92
109
|
host.ui.requestRender();
|
|
93
110
|
return;
|
|
@@ -104,7 +121,7 @@ export function setupEditorSubmitHandler(host: InteractiveModeStateHost & {
|
|
|
104
121
|
if (host.options?.submitPromptsDirectly) {
|
|
105
122
|
host.editor.addToHistory?.(text);
|
|
106
123
|
try {
|
|
107
|
-
await host.session.prompt(text);
|
|
124
|
+
await host.session.prompt(text, { images });
|
|
108
125
|
} catch (error: unknown) {
|
|
109
126
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
110
127
|
host.showError(errorMessage);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ImageContent } from "@gsd/pi-ai";
|
|
1
2
|
import type { AgentSessionEvent } from "../../core/agent-session.js";
|
|
2
3
|
|
|
3
4
|
export interface InteractiveModeStateHost {
|
|
@@ -32,6 +33,7 @@ export interface InteractiveModeStateHost {
|
|
|
32
33
|
extensionEditor?: any;
|
|
33
34
|
editorContainer: any;
|
|
34
35
|
keybindingsManager?: any;
|
|
36
|
+
pendingImages: ImageContent[];
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
export type InteractiveModeEvent = AgentSessionEvent;
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Handles TUI rendering and user interaction, delegating business logic to AgentSession.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import * as crypto from "node:crypto";
|
|
7
6
|
import * as fs from "node:fs";
|
|
8
7
|
import * as os from "node:os";
|
|
9
8
|
import * as path from "node:path";
|
|
@@ -62,7 +61,7 @@ import { type SessionContext, SessionManager } from "../../core/session-manager.
|
|
|
62
61
|
import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
|
|
63
62
|
import type { TruncationResult } from "../../core/tools/truncate.js";
|
|
64
63
|
import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/changelog.js";
|
|
65
|
-
import {
|
|
64
|
+
import { readClipboardImage } from "../../utils/clipboard-image.js";
|
|
66
65
|
import { ensureTool } from "../../utils/tools-manager.js";
|
|
67
66
|
import { AssistantMessageComponent } from "./components/assistant-message.js";
|
|
68
67
|
import { BashExecutionComponent } from "./components/bash-execution.js";
|
|
@@ -240,6 +239,9 @@ export class InteractiveMode {
|
|
|
240
239
|
// Tool output expansion state
|
|
241
240
|
private toolOutputExpanded = false;
|
|
242
241
|
|
|
242
|
+
// Pasted image tracking
|
|
243
|
+
private pendingImages: ImageContent[] = [];
|
|
244
|
+
|
|
243
245
|
// Thinking block visibility state
|
|
244
246
|
private hideThinkingBlock = false;
|
|
245
247
|
|
|
@@ -648,8 +650,10 @@ export class InteractiveMode {
|
|
|
648
650
|
// Main interactive loop
|
|
649
651
|
while (true) {
|
|
650
652
|
const userInput = await this.getUserInput();
|
|
653
|
+
const images = this.pendingImages.length > 0 ? [...this.pendingImages] : undefined;
|
|
654
|
+
this.pendingImages.length = 0;
|
|
651
655
|
try {
|
|
652
|
-
await this.session.prompt(userInput);
|
|
656
|
+
await this.session.prompt(userInput, { images });
|
|
653
657
|
} catch (error: unknown) {
|
|
654
658
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
655
659
|
this.showError(errorMessage);
|
|
@@ -1814,6 +1818,13 @@ export class InteractiveMode {
|
|
|
1814
1818
|
this.editor = this.defaultEditor;
|
|
1815
1819
|
}
|
|
1816
1820
|
|
|
1821
|
+
// Ensure pasted image path handler is set on the active editor
|
|
1822
|
+
if (!this.editor.onPasteImagePath) {
|
|
1823
|
+
this.editor.onPasteImagePath = (filePath: string) => {
|
|
1824
|
+
this.handlePastedImagePath(filePath);
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1817
1828
|
this.editorContainer.addChild(this.editor as Component);
|
|
1818
1829
|
this.ui.setFocus(this.editor as Component);
|
|
1819
1830
|
this.ui.requestRender();
|
|
@@ -1948,6 +1959,7 @@ export class InteractiveMode {
|
|
|
1948
1959
|
this.session.abortBash();
|
|
1949
1960
|
} else if (this.isBashMode) {
|
|
1950
1961
|
this.editor.setText("");
|
|
1962
|
+
this.pendingImages.length = 0;
|
|
1951
1963
|
this.isBashMode = false;
|
|
1952
1964
|
this.updateEditorBorderColor();
|
|
1953
1965
|
} else if (!this.editor.getText().trim()) {
|
|
@@ -2002,6 +2014,12 @@ export class InteractiveMode {
|
|
|
2002
2014
|
this.defaultEditor.onPasteImage = () => {
|
|
2003
2015
|
this.handleClipboardImagePaste();
|
|
2004
2016
|
};
|
|
2017
|
+
|
|
2018
|
+
// Handle image file paths pasted via terminal emulator (e.g. iTerm2).
|
|
2019
|
+
// Set on defaultEditor here; setCustomEditorComponent guards re-assignment for custom editors.
|
|
2020
|
+
this.defaultEditor.onPasteImagePath = (filePath: string) => {
|
|
2021
|
+
this.handlePastedImagePath(filePath);
|
|
2022
|
+
};
|
|
2005
2023
|
}
|
|
2006
2024
|
|
|
2007
2025
|
private async handleClipboardImagePaste(): Promise<void> {
|
|
@@ -2011,21 +2029,104 @@ export class InteractiveMode {
|
|
|
2011
2029
|
return;
|
|
2012
2030
|
}
|
|
2013
2031
|
|
|
2014
|
-
//
|
|
2015
|
-
const
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2032
|
+
// Store image as base64 ImageContent for sending with the prompt
|
|
2033
|
+
const imageContent: ImageContent = {
|
|
2034
|
+
type: "image",
|
|
2035
|
+
data: Buffer.from(image.bytes).toString("base64"),
|
|
2036
|
+
mimeType: image.mimeType,
|
|
2037
|
+
};
|
|
2038
|
+
this.pendingImages.push(imageContent);
|
|
2020
2039
|
|
|
2021
|
-
// Insert file path
|
|
2022
|
-
this.
|
|
2040
|
+
// Insert friendly placeholder instead of file path
|
|
2041
|
+
const imageNum = this.pendingImages.length;
|
|
2042
|
+
this.editor.insertTextAtCursor?.(`[Image #${imageNum}]`);
|
|
2023
2043
|
this.ui.requestRender();
|
|
2024
2044
|
} catch {
|
|
2025
2045
|
// Silently ignore clipboard errors (may not have permission, etc.)
|
|
2026
2046
|
}
|
|
2027
2047
|
}
|
|
2028
2048
|
|
|
2049
|
+
// MIME types restricted to formats commonly accepted by AI vision APIs.
|
|
2050
|
+
// SVG is excluded — it is XML/JS-bearing and not safe to forward as image content.
|
|
2051
|
+
// TIFF/HEIC/HEIF/AVIF are excluded for compatibility; users can convert before pasting.
|
|
2052
|
+
private static readonly MIME_BY_EXT: Record<string, string> = {
|
|
2053
|
+
png: "image/png",
|
|
2054
|
+
jpg: "image/jpeg",
|
|
2055
|
+
jpeg: "image/jpeg",
|
|
2056
|
+
gif: "image/gif",
|
|
2057
|
+
webp: "image/webp",
|
|
2058
|
+
};
|
|
2059
|
+
|
|
2060
|
+
// Magic-byte signatures used to verify file content matches its extension,
|
|
2061
|
+
// preventing arbitrary-file-read via crafted paste of e.g. "/etc/passwd.png".
|
|
2062
|
+
private static matchesImageSignature(buf: Buffer, mimeType: string): boolean {
|
|
2063
|
+
if (buf.length < 12) return false;
|
|
2064
|
+
switch (mimeType) {
|
|
2065
|
+
case "image/png":
|
|
2066
|
+
return buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47;
|
|
2067
|
+
case "image/jpeg":
|
|
2068
|
+
return buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff;
|
|
2069
|
+
case "image/gif":
|
|
2070
|
+
return (
|
|
2071
|
+
buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x38 &&
|
|
2072
|
+
(buf[4] === 0x37 || buf[4] === 0x39) && buf[5] === 0x61
|
|
2073
|
+
);
|
|
2074
|
+
case "image/webp":
|
|
2075
|
+
return (
|
|
2076
|
+
buf[0] === 0x52 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x46 &&
|
|
2077
|
+
buf[8] === 0x57 && buf[9] === 0x45 && buf[10] === 0x42 && buf[11] === 0x50
|
|
2078
|
+
);
|
|
2079
|
+
default:
|
|
2080
|
+
return false;
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
private handlePastedImagePath(filePath: string): void {
|
|
2085
|
+
try {
|
|
2086
|
+
const ext = path.extname(filePath).slice(1).toLowerCase();
|
|
2087
|
+
const mimeType = InteractiveMode.MIME_BY_EXT[ext];
|
|
2088
|
+
if (!mimeType) {
|
|
2089
|
+
// Unsupported / unsafe extension — fall back to inserting raw path.
|
|
2090
|
+
this.editor.insertTextAtCursor?.(filePath);
|
|
2091
|
+
this.ui.requestRender();
|
|
2092
|
+
return;
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
// Reject symlinks to prevent reading sensitive files via a symlinked
|
|
2096
|
+
// `.png` that points at e.g. ~/.ssh/id_rsa.
|
|
2097
|
+
const lst = fs.lstatSync(filePath);
|
|
2098
|
+
if (!lst.isFile()) {
|
|
2099
|
+
this.editor.insertTextAtCursor?.(filePath);
|
|
2100
|
+
this.ui.requestRender();
|
|
2101
|
+
return;
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
const data = fs.readFileSync(filePath);
|
|
2105
|
+
|
|
2106
|
+
// Magic-byte check — confirms file content actually matches the
|
|
2107
|
+
// extension before we forward bytes to a model.
|
|
2108
|
+
if (!InteractiveMode.matchesImageSignature(data, mimeType)) {
|
|
2109
|
+
this.editor.insertTextAtCursor?.(filePath);
|
|
2110
|
+
this.ui.requestRender();
|
|
2111
|
+
return;
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
this.pendingImages.push({
|
|
2115
|
+
type: "image",
|
|
2116
|
+
data: data.toString("base64"),
|
|
2117
|
+
mimeType,
|
|
2118
|
+
});
|
|
2119
|
+
|
|
2120
|
+
const imageNum = this.pendingImages.length;
|
|
2121
|
+
this.editor.insertTextAtCursor?.(`[Image #${imageNum}]`);
|
|
2122
|
+
this.ui.requestRender();
|
|
2123
|
+
} catch {
|
|
2124
|
+
// Fall back to inserting the raw path if file can't be read
|
|
2125
|
+
this.editor.insertTextAtCursor?.(filePath);
|
|
2126
|
+
this.ui.requestRender();
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2029
2130
|
private getSlashCommandContext(): SlashCommandContext {
|
|
2030
2131
|
return {
|
|
2031
2132
|
session: this.session,
|
|
@@ -2562,12 +2663,16 @@ export class InteractiveMode {
|
|
|
2562
2663
|
return;
|
|
2563
2664
|
}
|
|
2564
2665
|
|
|
2666
|
+
// Consume pending images
|
|
2667
|
+
const images = this.pendingImages.length > 0 ? [...this.pendingImages] : undefined;
|
|
2668
|
+
this.pendingImages.length = 0;
|
|
2669
|
+
|
|
2565
2670
|
// Queue input during compaction (extension commands execute immediately)
|
|
2566
2671
|
if (this.session.isCompacting) {
|
|
2567
2672
|
if (this.isExtensionCommand(text)) {
|
|
2568
2673
|
this.editor.addToHistory?.(text);
|
|
2569
2674
|
this.editor.setText("");
|
|
2570
|
-
await this.session.prompt(text);
|
|
2675
|
+
await this.session.prompt(text, { images });
|
|
2571
2676
|
} else {
|
|
2572
2677
|
this.queueCompactionMessage(text, "followUp");
|
|
2573
2678
|
}
|
|
@@ -2579,7 +2684,7 @@ export class InteractiveMode {
|
|
|
2579
2684
|
if (this.session.isStreaming) {
|
|
2580
2685
|
this.editor.addToHistory?.(text);
|
|
2581
2686
|
this.editor.setText("");
|
|
2582
|
-
await this.session.prompt(text, { streamingBehavior: "followUp" });
|
|
2687
|
+
await this.session.prompt(text, { streamingBehavior: "followUp", images });
|
|
2583
2688
|
this.updatePendingMessagesDisplay();
|
|
2584
2689
|
this.ui.requestRender();
|
|
2585
2690
|
}
|
|
@@ -3863,6 +3968,7 @@ export class InteractiveMode {
|
|
|
3863
3968
|
this.streamingComponent = undefined;
|
|
3864
3969
|
this.streamingMessage = undefined;
|
|
3865
3970
|
this.pendingTools.clear();
|
|
3971
|
+
this.pendingImages.length = 0;
|
|
3866
3972
|
|
|
3867
3973
|
// Reset contextual tips for the new session
|
|
3868
3974
|
this.contextualTips.reset();
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// @gsd/pi-coding-agent + system-prompt-skill-filter.test — coverage for the
|
|
2
|
+
// optional `skillFilter` option added to buildSystemPrompt (RFC #4779). The
|
|
3
|
+
// filter lets consumers narrow the <available_skills> catalog rendered into
|
|
4
|
+
// the cached system prompt without touching skill loading or invocation.
|
|
5
|
+
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
|
|
9
|
+
import { buildSystemPrompt } from "../core/system-prompt.js";
|
|
10
|
+
import type { Skill } from "../core/skills.js";
|
|
11
|
+
|
|
12
|
+
function makeSkill(name: string, description = `description for ${name}`): Skill {
|
|
13
|
+
return {
|
|
14
|
+
name,
|
|
15
|
+
description,
|
|
16
|
+
filePath: `/tmp/${name}/SKILL.md`,
|
|
17
|
+
baseDir: `/tmp/${name}`,
|
|
18
|
+
source: "project",
|
|
19
|
+
disableModelInvocation: false,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function extractAvailableSkills(prompt: string): string {
|
|
24
|
+
const start = prompt.indexOf("<available_skills>");
|
|
25
|
+
const end = prompt.indexOf("</available_skills>");
|
|
26
|
+
if (start === -1 || end === -1) return "";
|
|
27
|
+
return prompt.slice(start, end + "</available_skills>".length);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ─── Default branch (no customPrompt) ──────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
test("buildSystemPrompt: skillFilter omits filtered-out skills from <available_skills>", () => {
|
|
33
|
+
const skills = [makeSkill("alpha"), makeSkill("beta"), makeSkill("gamma")];
|
|
34
|
+
const prompt = buildSystemPrompt({
|
|
35
|
+
skills,
|
|
36
|
+
selectedTools: ["read", "Skill"],
|
|
37
|
+
skillFilter: skill => skill.name !== "beta",
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const section = extractAvailableSkills(prompt);
|
|
41
|
+
assert.ok(section.length > 0, "catalog section should render");
|
|
42
|
+
assert.match(section, /<name>alpha<\/name>/);
|
|
43
|
+
assert.match(section, /<name>gamma<\/name>/);
|
|
44
|
+
assert.doesNotMatch(section, /<name>beta<\/name>/);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("buildSystemPrompt: skillFilter omitted preserves pre-filter behavior (all skills render)", () => {
|
|
48
|
+
const skills = [makeSkill("alpha"), makeSkill("beta")];
|
|
49
|
+
const prompt = buildSystemPrompt({
|
|
50
|
+
skills,
|
|
51
|
+
selectedTools: ["read", "Skill"],
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const section = extractAvailableSkills(prompt);
|
|
55
|
+
assert.match(section, /<name>alpha<\/name>/);
|
|
56
|
+
assert.match(section, /<name>beta<\/name>/);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("buildSystemPrompt: skillFilter that rejects every skill suppresses the <available_skills> block", () => {
|
|
60
|
+
const skills = [makeSkill("alpha"), makeSkill("beta")];
|
|
61
|
+
const prompt = buildSystemPrompt({
|
|
62
|
+
skills,
|
|
63
|
+
selectedTools: ["read", "Skill"],
|
|
64
|
+
skillFilter: () => false,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// With zero visible skills, formatSkillsForPrompt returns an empty string,
|
|
68
|
+
// so the opening tag should not appear anywhere.
|
|
69
|
+
assert.ok(!prompt.includes("<available_skills>"));
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// ─── Custom-prompt branch ──────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
test("buildSystemPrompt (customPrompt): skillFilter applies to the catalog appended onto a custom prompt", () => {
|
|
75
|
+
const skills = [makeSkill("alpha"), makeSkill("beta"), makeSkill("gamma")];
|
|
76
|
+
const prompt = buildSystemPrompt({
|
|
77
|
+
customPrompt: "CUSTOM BASE",
|
|
78
|
+
skills,
|
|
79
|
+
selectedTools: ["read", "Skill"],
|
|
80
|
+
skillFilter: skill => skill.name === "alpha",
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const section = extractAvailableSkills(prompt);
|
|
84
|
+
assert.match(section, /<name>alpha<\/name>/);
|
|
85
|
+
assert.doesNotMatch(section, /<name>beta<\/name>/);
|
|
86
|
+
assert.doesNotMatch(section, /<name>gamma<\/name>/);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// ─── Interaction with disableModelInvocation ──────────────────────────────
|
|
90
|
+
|
|
91
|
+
test("buildSystemPrompt: skillFilter composes with disableModelInvocation (both must pass)", () => {
|
|
92
|
+
// A skill already hidden from the catalog by disableModelInvocation must
|
|
93
|
+
// remain hidden even if skillFilter would otherwise admit it. The filter
|
|
94
|
+
// narrows, it does not override the existing invisibility contract.
|
|
95
|
+
const skills: Skill[] = [
|
|
96
|
+
{ ...makeSkill("visible"), disableModelInvocation: false },
|
|
97
|
+
{ ...makeSkill("hidden"), disableModelInvocation: true },
|
|
98
|
+
];
|
|
99
|
+
const prompt = buildSystemPrompt({
|
|
100
|
+
skills,
|
|
101
|
+
selectedTools: ["read", "Skill"],
|
|
102
|
+
skillFilter: () => true,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const section = extractAvailableSkills(prompt);
|
|
106
|
+
assert.match(section, /<name>visible<\/name>/);
|
|
107
|
+
assert.doesNotMatch(section, /<name>hidden<\/name>/);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// ─── Pass-through of non-filtered fields ──────────────────────────────────
|
|
111
|
+
|
|
112
|
+
test("buildSystemPrompt: skillFilter does not affect context files or cwd rendering", () => {
|
|
113
|
+
const skills = [makeSkill("alpha")];
|
|
114
|
+
const prompt = buildSystemPrompt({
|
|
115
|
+
skills,
|
|
116
|
+
cwd: "/tmp/example",
|
|
117
|
+
contextFiles: [{ path: "CLAUDE.md", content: "project instructions" }],
|
|
118
|
+
selectedTools: ["read", "Skill"],
|
|
119
|
+
skillFilter: () => false,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
assert.ok(prompt.includes("/tmp/example"), "cwd should still render");
|
|
123
|
+
assert.ok(prompt.includes("project instructions"), "context files should still render");
|
|
124
|
+
assert.ok(!prompt.includes("<available_skills>"), "no skill catalog when filter rejects all");
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// ─── Exception safety ─────────────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
test("buildSystemPrompt: skillFilter that throws falls back to unfiltered list and does not propagate", (t) => {
|
|
130
|
+
// A buggy consumer predicate must not bubble out of buildSystemPrompt.
|
|
131
|
+
// If it did, _rebuildSystemPrompt could unwind mid-setTools() and leave
|
|
132
|
+
// the session with updated tools but a stale system prompt.
|
|
133
|
+
const skills = [makeSkill("alpha"), makeSkill("beta")];
|
|
134
|
+
|
|
135
|
+
// Suppress the console.warn the fallback emits so test output stays clean.
|
|
136
|
+
const originalWarn = console.warn;
|
|
137
|
+
const warnings: string[] = [];
|
|
138
|
+
console.warn = (...args: unknown[]) => { warnings.push(args.join(" ")); };
|
|
139
|
+
t.after(() => { console.warn = originalWarn; });
|
|
140
|
+
|
|
141
|
+
let prompt = "";
|
|
142
|
+
assert.doesNotThrow(() => {
|
|
143
|
+
prompt = buildSystemPrompt({
|
|
144
|
+
skills,
|
|
145
|
+
selectedTools: ["read", "Skill"],
|
|
146
|
+
skillFilter: () => { throw new Error("consumer bug"); },
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const section = extractAvailableSkills(prompt);
|
|
151
|
+
assert.match(section, /<name>alpha<\/name>/, "alpha should render (fallback to unfiltered)");
|
|
152
|
+
assert.match(section, /<name>beta<\/name>/, "beta should render (fallback to unfiltered)");
|
|
153
|
+
assert.ok(
|
|
154
|
+
warnings.some(w => w.includes("skillFilter threw") && w.includes("consumer bug")),
|
|
155
|
+
"fallback should emit an identifying warning",
|
|
156
|
+
);
|
|
157
|
+
});
|