gsd-pi 2.20.0 → 2.22.0
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 +12 -0
- package/dist/cli.js +21 -18
- package/dist/help-text.d.ts +2 -0
- package/dist/help-text.js +47 -0
- package/dist/loader.js +2 -16
- package/dist/mcp-server.d.ts +18 -0
- package/dist/mcp-server.js +53 -0
- package/dist/onboarding.d.ts +0 -6
- package/dist/onboarding.js +0 -28
- package/dist/resources/agents/javascript-pro.md +280 -0
- package/dist/resources/agents/typescript-pro.md +255 -0
- package/dist/resources/extensions/bg-shell/index.ts +14 -2
- package/dist/resources/extensions/bg-shell/utilities.ts +14 -0
- package/dist/resources/extensions/browser-tools/{core.js → core.ts} +329 -190
- package/dist/resources/extensions/gsd/auto-dashboard.ts +2 -1
- package/dist/resources/extensions/gsd/auto-prompts.ts +42 -1
- package/dist/resources/extensions/gsd/auto-worktree.ts +35 -2
- package/dist/resources/extensions/gsd/auto.ts +116 -10
- package/dist/resources/extensions/gsd/claude-import.ts +656 -0
- package/dist/resources/extensions/gsd/collision-diagnostics.ts +332 -0
- package/dist/resources/extensions/gsd/commands.ts +60 -7
- package/dist/resources/extensions/gsd/diff-context.ts +220 -0
- package/dist/resources/extensions/gsd/docs/claude-marketplace-import.md +214 -0
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +2 -1
- package/dist/resources/extensions/gsd/doctor.ts +12 -4
- package/dist/resources/extensions/gsd/file-watcher.ts +97 -0
- package/dist/resources/extensions/gsd/files.ts +1 -1
- package/dist/resources/extensions/gsd/forensics.ts +596 -0
- package/dist/resources/extensions/gsd/git-service.ts +2 -1
- package/dist/resources/extensions/gsd/github-client.ts +235 -0
- package/dist/resources/extensions/gsd/gitignore.ts +1 -0
- package/dist/resources/extensions/gsd/marketplace-discovery.ts +507 -0
- package/dist/resources/extensions/gsd/mcp-server.ts +87 -0
- package/dist/resources/extensions/gsd/namespaced-registry.ts +467 -0
- package/dist/resources/extensions/gsd/namespaced-resolver.ts +307 -0
- package/dist/resources/extensions/gsd/plugin-importer.ts +410 -0
- package/dist/resources/extensions/gsd/preferences.ts +14 -3
- package/dist/resources/extensions/gsd/prompts/execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/forensics.md +71 -0
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/system.md +12 -3
- package/dist/resources/extensions/gsd/roadmap-slices.ts +1 -1
- package/dist/resources/extensions/gsd/session-forensics.ts +5 -5
- package/dist/resources/extensions/gsd/tests/claude-import-tui.test.ts +351 -0
- package/dist/resources/extensions/gsd/tests/collision-diagnostics.test.ts +705 -0
- package/dist/resources/extensions/gsd/tests/context-compression.test.ts +13 -0
- package/dist/resources/extensions/gsd/tests/diff-context.test.ts +136 -0
- package/dist/resources/extensions/gsd/tests/doctor-git.test.ts +118 -0
- package/dist/resources/extensions/gsd/tests/marketplace-discovery.test.ts +202 -0
- package/dist/resources/extensions/gsd/tests/marketplace-test-fixtures.ts +91 -0
- package/dist/resources/extensions/gsd/tests/namespaced-registry.test.ts +1027 -0
- package/dist/resources/extensions/gsd/tests/namespaced-resolver.test.ts +671 -0
- package/dist/resources/extensions/gsd/tests/none-mode-gates.test.ts +105 -0
- package/dist/resources/extensions/gsd/tests/plugin-importer-live.test.ts +481 -0
- package/dist/resources/extensions/gsd/tests/plugin-importer.test.ts +1383 -0
- package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +21 -2
- package/dist/resources/extensions/gsd/tests/preferences-schema-validation.test.ts +8 -1
- package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +11 -0
- package/dist/resources/extensions/gsd/tests/worktree-bugfix.test.ts +120 -0
- package/dist/resources/extensions/gsd/token-counter.ts +45 -0
- package/dist/resources/extensions/gsd/visualizer-data.ts +2 -2
- package/dist/resources/extensions/gsd/worktree-manager.ts +29 -1
- package/dist/resources/extensions/gsd/worktree.ts +3 -0
- package/dist/resources/extensions/mcporter/index.ts +90 -7
- package/dist/resources/extensions/search-the-web/native-search.ts +2 -0
- package/dist/resources/extensions/search-the-web/tool-fetch-page.ts +9 -1
- package/dist/resources/extensions/search-the-web/url-utils.ts +35 -1
- package/dist/resources/extensions/shared/terminal.ts +1 -1
- package/dist/resources/extensions/universal-config/discovery.ts +4 -0
- package/dist/resources/extensions/universal-config/format.ts +35 -4
- package/dist/resources/extensions/universal-config/index.ts +5 -3
- package/dist/resources/extensions/universal-config/scanners.ts +65 -2
- package/dist/resources/extensions/universal-config/tests/discovery.test.ts +9 -1
- package/dist/resources/extensions/universal-config/tests/format.test.ts +22 -6
- package/dist/resources/extensions/universal-config/tests/scanners.test.ts +18 -0
- package/dist/resources/extensions/universal-config/types.ts +20 -1
- package/dist/resources/skills/lint/SKILL.md +141 -0
- package/dist/resources/skills/review/SKILL.md +214 -0
- package/dist/resources/skills/test/SKILL.md +201 -0
- package/dist/tool-bootstrap.js +2 -1
- package/dist/wizard.js +2 -0
- package/package.json +12 -7
- package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.js +12 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js +12 -1
- package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/LICENSE +201 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/README.md +9 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-cjs/index.js +762 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/build-abort-error.js +19 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/constants.js +1 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/get-transformed-headers.js +9 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/index.js +3 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http-handler.js +230 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-manager.js +87 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-pool.js +32 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http2-handler.js +169 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/readable.mock.js +21 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/server.mock.js +88 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-connection-timeout.js +36 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-request-timeout.js +21 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-socket-keep-alive.js +22 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-socket-timeout.js +23 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/stream-collector/collector.js +8 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/stream-collector/index.js +41 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/stream-collector/readable.mock.js +21 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/timing.js +4 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/write-request-body.js +63 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/build-abort-error.d.ts +10 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/constants.d.ts +5 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/get-transformed-headers.d.ts +4 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/index.d.ts +3 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http-handler.d.ts +46 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http2-connection-manager.d.ts +24 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http2-connection-pool.d.ts +12 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http2-handler.d.ts +63 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/readable.mock.d.ts +13 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/server.mock.d.ts +12 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-connection-timeout.d.ts +2 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-request-timeout.d.ts +6 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-socket-keep-alive.d.ts +6 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-socket-timeout.d.ts +2 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/stream-collector/collector.d.ts +5 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/stream-collector/index.d.ts +6 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/stream-collector/readable.mock.d.ts +13 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/timing.d.ts +8 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/build-abort-error.d.ts +10 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/constants.d.ts +5 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/get-transformed-headers.d.ts +4 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/index.d.ts +3 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http-handler.d.ts +46 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http2-connection-manager.d.ts +24 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http2-connection-pool.d.ts +12 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http2-handler.d.ts +63 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/readable.mock.d.ts +13 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/server.mock.d.ts +12 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-connection-timeout.d.ts +2 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-request-timeout.d.ts +6 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-socket-keep-alive.d.ts +6 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-socket-timeout.d.ts +2 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/stream-collector/collector.d.ts +5 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/stream-collector/index.d.ts +6 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/stream-collector/readable.mock.d.ts +13 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/timing.d.ts +8 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/write-request-body.d.ts +12 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/write-request-body.d.ts +12 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/package.json +68 -0
- package/packages/pi-ai/package.json +3 -0
- package/packages/pi-ai/pnpm-lock.yaml +2022 -0
- package/packages/pi-ai/src/providers/azure-openai-responses.ts +12 -1
- package/packages/pi-ai/src/providers/openai-responses.ts +12 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.js +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +1 -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 +12 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +113 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/project-trust.d.ts +4 -0
- package/packages/pi-coding-agent/dist/core/extensions/project-trust.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/project-trust.js +42 -0
- package/packages/pi-coding-agent/dist/core/extensions/project-trust.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resolve-config-value.js +18 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +114 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +8 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts +2 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +23 -9
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +67 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -0
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/pnpm-lock.yaml +454 -0
- package/packages/pi-coding-agent/scripts/copy-assets.cjs +24 -0
- package/packages/pi-coding-agent/src/core/extensions/index.ts +3 -0
- package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +141 -0
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +14 -1
- package/packages/pi-coding-agent/src/core/extensions/project-trust.ts +51 -0
- package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +132 -0
- package/packages/pi-coding-agent/src/core/resolve-config-value.ts +20 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +7 -2
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +98 -0
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +24 -9
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +2 -2
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/package.json +3 -1
- package/packages/pi-tui/src/tui.ts +2 -2
- package/src/resources/agents/javascript-pro.md +280 -0
- package/src/resources/agents/typescript-pro.md +255 -0
- package/src/resources/extensions/bg-shell/index.ts +14 -2
- package/src/resources/extensions/bg-shell/utilities.ts +14 -0
- package/src/resources/extensions/browser-tools/{core.js → core.ts} +329 -190
- package/src/resources/extensions/gsd/auto-dashboard.ts +2 -1
- package/src/resources/extensions/gsd/auto-prompts.ts +42 -1
- package/src/resources/extensions/gsd/auto-worktree.ts +35 -2
- package/src/resources/extensions/gsd/auto.ts +116 -10
- package/src/resources/extensions/gsd/claude-import.ts +656 -0
- package/src/resources/extensions/gsd/collision-diagnostics.ts +332 -0
- package/src/resources/extensions/gsd/commands.ts +60 -7
- package/src/resources/extensions/gsd/diff-context.ts +220 -0
- package/src/resources/extensions/gsd/docs/claude-marketplace-import.md +214 -0
- package/src/resources/extensions/gsd/docs/preferences-reference.md +2 -1
- package/src/resources/extensions/gsd/doctor.ts +12 -4
- package/src/resources/extensions/gsd/file-watcher.ts +97 -0
- package/src/resources/extensions/gsd/files.ts +1 -1
- package/src/resources/extensions/gsd/forensics.ts +596 -0
- package/src/resources/extensions/gsd/git-service.ts +2 -1
- package/src/resources/extensions/gsd/github-client.ts +235 -0
- package/src/resources/extensions/gsd/gitignore.ts +1 -0
- package/src/resources/extensions/gsd/marketplace-discovery.ts +507 -0
- package/src/resources/extensions/gsd/mcp-server.ts +87 -0
- package/src/resources/extensions/gsd/namespaced-registry.ts +467 -0
- package/src/resources/extensions/gsd/namespaced-resolver.ts +307 -0
- package/src/resources/extensions/gsd/plugin-importer.ts +410 -0
- package/src/resources/extensions/gsd/preferences.ts +14 -3
- package/src/resources/extensions/gsd/prompts/execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/forensics.md +71 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/system.md +12 -3
- package/src/resources/extensions/gsd/roadmap-slices.ts +1 -1
- package/src/resources/extensions/gsd/session-forensics.ts +5 -5
- package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +351 -0
- package/src/resources/extensions/gsd/tests/collision-diagnostics.test.ts +705 -0
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +13 -0
- package/src/resources/extensions/gsd/tests/diff-context.test.ts +136 -0
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +118 -0
- package/src/resources/extensions/gsd/tests/marketplace-discovery.test.ts +202 -0
- package/src/resources/extensions/gsd/tests/marketplace-test-fixtures.ts +91 -0
- package/src/resources/extensions/gsd/tests/namespaced-registry.test.ts +1027 -0
- package/src/resources/extensions/gsd/tests/namespaced-resolver.test.ts +671 -0
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +105 -0
- package/src/resources/extensions/gsd/tests/plugin-importer-live.test.ts +481 -0
- package/src/resources/extensions/gsd/tests/plugin-importer.test.ts +1383 -0
- package/src/resources/extensions/gsd/tests/preferences-git.test.ts +21 -2
- package/src/resources/extensions/gsd/tests/preferences-schema-validation.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +11 -0
- package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +120 -0
- package/src/resources/extensions/gsd/token-counter.ts +45 -0
- package/src/resources/extensions/gsd/visualizer-data.ts +2 -2
- package/src/resources/extensions/gsd/worktree-manager.ts +29 -1
- package/src/resources/extensions/gsd/worktree.ts +3 -0
- package/src/resources/extensions/mcporter/index.ts +90 -7
- package/src/resources/extensions/search-the-web/native-search.ts +2 -0
- package/src/resources/extensions/search-the-web/tool-fetch-page.ts +9 -1
- package/src/resources/extensions/search-the-web/url-utils.ts +35 -1
- package/src/resources/extensions/shared/terminal.ts +1 -1
- package/src/resources/extensions/universal-config/discovery.ts +4 -0
- package/src/resources/extensions/universal-config/format.ts +35 -4
- package/src/resources/extensions/universal-config/index.ts +5 -3
- package/src/resources/extensions/universal-config/scanners.ts +65 -2
- package/src/resources/extensions/universal-config/tests/discovery.test.ts +9 -1
- package/src/resources/extensions/universal-config/tests/format.test.ts +22 -6
- package/src/resources/extensions/universal-config/tests/scanners.test.ts +18 -0
- package/src/resources/extensions/universal-config/types.ts +20 -1
- package/src/resources/skills/lint/SKILL.md +141 -0
- package/src/resources/skills/review/SKILL.md +214 -0
- package/src/resources/skills/test/SKILL.md +201 -0
- package/dist/resources/extensions/browser-tools/core.d.ts +0 -205
- package/src/resources/extensions/browser-tools/core.d.ts +0 -205
|
@@ -74,6 +74,19 @@ test("compression: buildExecuteTaskPrompt minimal truncates prior summaries", ()
|
|
|
74
74
|
);
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
+
test("compression: buildExecuteTaskPrompt passes verificationBudget to loadPrompt (#707)", () => {
|
|
78
|
+
// The execute-task template declares {{verificationBudget}} — the builder must supply it
|
|
79
|
+
assert.ok(
|
|
80
|
+
promptsSrc.includes("verificationBudget"),
|
|
81
|
+
"buildExecuteTaskPrompt should pass verificationBudget in the loadPrompt vars object",
|
|
82
|
+
);
|
|
83
|
+
// Verify it computes the budget from computeBudgets
|
|
84
|
+
assert.ok(
|
|
85
|
+
promptsSrc.includes("computeBudgets(contextWindow)"),
|
|
86
|
+
"buildExecuteTaskPrompt should compute budgets from the executor context window",
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
77
90
|
test("compression: buildPlanMilestonePrompt minimal drops project/requirements/decisions files", () => {
|
|
78
91
|
// The plan-milestone builder should gate root file inlining on inlineLevel
|
|
79
92
|
assert.ok(
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for diff-context.ts — diff-aware context module.
|
|
3
|
+
* Tests git-based file discovery and relevance ranking.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it } from "node:test";
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { mkdtempSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
getRecentlyChangedFiles,
|
|
14
|
+
getChangedFilesWithContext,
|
|
15
|
+
rankFilesByRelevance,
|
|
16
|
+
} from "../diff-context.js";
|
|
17
|
+
|
|
18
|
+
// ─── getRecentlyChangedFiles ────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
describe("diff-context: getRecentlyChangedFiles", () => {
|
|
21
|
+
it("returns an array of file paths in the current git repo", async () => {
|
|
22
|
+
// Use the project root — guaranteed to be a git repo
|
|
23
|
+
const cwd = process.cwd();
|
|
24
|
+
const files = await getRecentlyChangedFiles(cwd);
|
|
25
|
+
|
|
26
|
+
assert.ok(Array.isArray(files), "should return an array");
|
|
27
|
+
// The result may be empty if the repo is totally clean with no recent
|
|
28
|
+
// commits, but the function should not throw.
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("respects maxFiles option", async () => {
|
|
32
|
+
const cwd = process.cwd();
|
|
33
|
+
const files = await getRecentlyChangedFiles(cwd, { maxFiles: 3 });
|
|
34
|
+
|
|
35
|
+
assert.ok(files.length <= 3, "should not exceed maxFiles");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("returns empty array for non-git directory", async () => {
|
|
39
|
+
const tmp = mkdtempSync(join(tmpdir(), "diff-ctx-test-"));
|
|
40
|
+
const files = await getRecentlyChangedFiles(tmp);
|
|
41
|
+
|
|
42
|
+
assert.deepStrictEqual(files, [], "should return empty array for non-git dir");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("returns deduplicated paths", async () => {
|
|
46
|
+
const cwd = process.cwd();
|
|
47
|
+
const files = await getRecentlyChangedFiles(cwd, { maxFiles: 100 });
|
|
48
|
+
const unique = new Set(files);
|
|
49
|
+
|
|
50
|
+
assert.equal(files.length, unique.size, "should have no duplicates");
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// ─── getChangedFilesWithContext ─────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
describe("diff-context: getChangedFilesWithContext", () => {
|
|
57
|
+
it("returns array of ChangedFileInfo objects", async () => {
|
|
58
|
+
const cwd = process.cwd();
|
|
59
|
+
const infos = await getChangedFilesWithContext(cwd);
|
|
60
|
+
|
|
61
|
+
assert.ok(Array.isArray(infos), "should return an array");
|
|
62
|
+
|
|
63
|
+
for (const info of infos) {
|
|
64
|
+
assert.ok(typeof info.path === "string", "path should be a string");
|
|
65
|
+
assert.ok(
|
|
66
|
+
["modified", "added", "deleted", "staged"].includes(info.changeType),
|
|
67
|
+
`changeType should be valid, got: ${info.changeType}`,
|
|
68
|
+
);
|
|
69
|
+
if (info.linesChanged !== undefined) {
|
|
70
|
+
assert.ok(typeof info.linesChanged === "number", "linesChanged should be a number");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("returns empty array for non-git directory", async () => {
|
|
76
|
+
const tmp = mkdtempSync(join(tmpdir(), "diff-ctx-test2-"));
|
|
77
|
+
const infos = await getChangedFilesWithContext(tmp);
|
|
78
|
+
|
|
79
|
+
assert.deepStrictEqual(infos, [], "should return empty array for non-git dir");
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// ─── rankFilesByRelevance ───────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
describe("diff-context: rankFilesByRelevance", () => {
|
|
86
|
+
it("places changed files before unchanged files", () => {
|
|
87
|
+
const allFiles = ["a.ts", "b.ts", "c.ts", "d.ts"];
|
|
88
|
+
const changed = ["c.ts", "a.ts"];
|
|
89
|
+
|
|
90
|
+
const ranked = rankFilesByRelevance(allFiles, changed);
|
|
91
|
+
|
|
92
|
+
// Changed files come first, sorted by changedFiles priority (c before a)
|
|
93
|
+
assert.equal(ranked[0], "c.ts");
|
|
94
|
+
assert.equal(ranked[1], "a.ts");
|
|
95
|
+
// Unchanged files follow in original order
|
|
96
|
+
assert.equal(ranked[2], "b.ts");
|
|
97
|
+
assert.equal(ranked[3], "d.ts");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("preserves order of changed files based on changedFiles priority", () => {
|
|
101
|
+
const allFiles = ["x.ts", "y.ts", "z.ts", "w.ts"];
|
|
102
|
+
const changed = ["z.ts", "x.ts"]; // z has higher priority (index 0)
|
|
103
|
+
|
|
104
|
+
const ranked = rankFilesByRelevance(allFiles, changed);
|
|
105
|
+
|
|
106
|
+
assert.equal(ranked[0], "z.ts", "z.ts should be first (higher priority in changedFiles)");
|
|
107
|
+
assert.equal(ranked[1], "x.ts", "x.ts should be second");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("returns unchanged files in original order when no changed files match", () => {
|
|
111
|
+
const allFiles = ["a.ts", "b.ts", "c.ts"];
|
|
112
|
+
const changed = ["x.ts", "y.ts"]; // none match
|
|
113
|
+
|
|
114
|
+
const ranked = rankFilesByRelevance(allFiles, changed);
|
|
115
|
+
|
|
116
|
+
assert.deepStrictEqual(ranked, ["a.ts", "b.ts", "c.ts"]);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("handles empty inputs gracefully", () => {
|
|
120
|
+
assert.deepStrictEqual(rankFilesByRelevance([], []), []);
|
|
121
|
+
assert.deepStrictEqual(rankFilesByRelevance(["a.ts"], []), ["a.ts"]);
|
|
122
|
+
assert.deepStrictEqual(rankFilesByRelevance([], ["a.ts"]), []);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("handles all files being changed", () => {
|
|
126
|
+
const allFiles = ["a.ts", "b.ts"];
|
|
127
|
+
const changed = ["b.ts", "a.ts"];
|
|
128
|
+
|
|
129
|
+
const ranked = rankFilesByRelevance(allFiles, changed);
|
|
130
|
+
|
|
131
|
+
// Both are changed, so sorted by changedFiles order: b first, then a
|
|
132
|
+
assert.equal(ranked[0], "b.ts");
|
|
133
|
+
assert.equal(ranked[1], "a.ts");
|
|
134
|
+
assert.equal(ranked.length, 2);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
@@ -65,6 +65,27 @@ _None_
|
|
|
65
65
|
return dir;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/** Write a .gsd/preferences.md with the given git isolation mode. */
|
|
69
|
+
function writePreferencesFile(dir: string, isolation: "none" | "worktree" | "branch"): void {
|
|
70
|
+
const gsdDir = join(dir, ".gsd");
|
|
71
|
+
mkdirSync(gsdDir, { recursive: true });
|
|
72
|
+
writeFileSync(join(gsdDir, "preferences.md"), `---\ngit:\n isolation: "${isolation}"\n---\n`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Write preferences to the test runner's cwd .gsd/preferences.md.
|
|
77
|
+
* loadEffectiveGSDPreferences() resolves PROJECT_PREFERENCES_PATH at module
|
|
78
|
+
* load time from process.cwd(), so we must write there — not to the temp dir.
|
|
79
|
+
*/
|
|
80
|
+
const RUNNER_PREFS_PATH = join(process.cwd(), ".gsd", "preferences.md");
|
|
81
|
+
function writeRunnerPreferences(isolation: "none" | "worktree" | "branch"): void {
|
|
82
|
+
mkdirSync(join(process.cwd(), ".gsd"), { recursive: true });
|
|
83
|
+
writeFileSync(RUNNER_PREFS_PATH, `---\ngit:\n isolation: "${isolation}"\n---\n`);
|
|
84
|
+
}
|
|
85
|
+
function removeRunnerPreferences(): void {
|
|
86
|
+
try { rmSync(RUNNER_PREFS_PATH); } catch { /* ignore if already gone */ }
|
|
87
|
+
}
|
|
88
|
+
|
|
68
89
|
/** Create a repo with an in-progress milestone. */
|
|
69
90
|
function createRepoWithActiveMilestone(): string {
|
|
70
91
|
const dir = realpathSync(mkdtempSync(join(tmpdir(), "doc-git-test-")));
|
|
@@ -252,6 +273,103 @@ async function main(): Promise<void> {
|
|
|
252
273
|
console.log("\n=== active worktree safety (skipped on Windows) ===");
|
|
253
274
|
}
|
|
254
275
|
|
|
276
|
+
// ─── Test 7: none-mode skips orphaned worktree check ───────────────
|
|
277
|
+
// NOTE: loadEffectiveGSDPreferences() resolves PROJECT_PREFERENCES_PATH
|
|
278
|
+
// at module load time from process.cwd(). We write the prefs file to
|
|
279
|
+
// the test runner's cwd .gsd/preferences.md and clean up afterwards.
|
|
280
|
+
if (process.platform !== "win32") {
|
|
281
|
+
console.log("\n=== none-mode skips orphaned worktree ===");
|
|
282
|
+
{
|
|
283
|
+
const dir = createRepoWithCompletedMilestone();
|
|
284
|
+
cleanups.push(dir);
|
|
285
|
+
|
|
286
|
+
// Create worktree with milestone/M001 branch under .gsd/worktrees/
|
|
287
|
+
mkdirSync(join(dir, ".gsd", "worktrees"), { recursive: true });
|
|
288
|
+
run("git worktree add -b milestone/M001 .gsd/worktrees/M001", dir);
|
|
289
|
+
|
|
290
|
+
// Write preferences to runner's cwd (where the module resolves project prefs)
|
|
291
|
+
writeRunnerPreferences("none");
|
|
292
|
+
try {
|
|
293
|
+
const result = await runGSDDoctor(dir);
|
|
294
|
+
const orphanIssues = result.issues.filter(i => i.code === "orphaned_auto_worktree");
|
|
295
|
+
assertEq(orphanIssues.length, 0, "none-mode: orphaned worktree NOT detected");
|
|
296
|
+
} finally {
|
|
297
|
+
removeRunnerPreferences();
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
} else {
|
|
301
|
+
console.log("\n=== none-mode skips orphaned worktree (skipped on Windows) ===");
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// ─── Test 8: none-mode skips stale branch check ────────────────────
|
|
305
|
+
if (process.platform !== "win32") {
|
|
306
|
+
console.log("\n=== none-mode skips stale branch ===");
|
|
307
|
+
{
|
|
308
|
+
const dir = createRepoWithCompletedMilestone();
|
|
309
|
+
cleanups.push(dir);
|
|
310
|
+
|
|
311
|
+
// Create a milestone/M001 branch (no worktree)
|
|
312
|
+
run("git branch milestone/M001", dir);
|
|
313
|
+
|
|
314
|
+
// Write preferences to runner's cwd
|
|
315
|
+
writeRunnerPreferences("none");
|
|
316
|
+
try {
|
|
317
|
+
const result = await runGSDDoctor(dir);
|
|
318
|
+
const staleIssues = result.issues.filter(i => i.code === "stale_milestone_branch");
|
|
319
|
+
assertEq(staleIssues.length, 0, "none-mode: stale branch NOT detected");
|
|
320
|
+
} finally {
|
|
321
|
+
removeRunnerPreferences();
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
console.log("\n=== none-mode skips stale branch (skipped on Windows) ===");
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ─── Test 9: none-mode still detects corrupt merge state ───────────
|
|
329
|
+
console.log("\n=== none-mode keeps corrupt merge state ===");
|
|
330
|
+
{
|
|
331
|
+
const dir = createRepoWithCompletedMilestone();
|
|
332
|
+
cleanups.push(dir);
|
|
333
|
+
|
|
334
|
+
// Inject MERGE_HEAD into .git
|
|
335
|
+
const headHash = run("git rev-parse HEAD", dir);
|
|
336
|
+
writeFileSync(join(dir, ".git", "MERGE_HEAD"), headHash + "\n");
|
|
337
|
+
|
|
338
|
+
// Write preferences to runner's cwd
|
|
339
|
+
writeRunnerPreferences("none");
|
|
340
|
+
try {
|
|
341
|
+
const result = await runGSDDoctor(dir);
|
|
342
|
+
const mergeIssues = result.issues.filter(i => i.code === "corrupt_merge_state");
|
|
343
|
+
assertTrue(mergeIssues.length > 0, "none-mode: corrupt merge state IS detected");
|
|
344
|
+
} finally {
|
|
345
|
+
removeRunnerPreferences();
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// ─── Test 10: none-mode still detects tracked runtime files ────────
|
|
350
|
+
console.log("\n=== none-mode keeps tracked runtime files ===");
|
|
351
|
+
{
|
|
352
|
+
const dir = createRepoWithCompletedMilestone();
|
|
353
|
+
cleanups.push(dir);
|
|
354
|
+
|
|
355
|
+
// Force-add a runtime file
|
|
356
|
+
const activityDir = join(dir, ".gsd", "activity");
|
|
357
|
+
mkdirSync(activityDir, { recursive: true });
|
|
358
|
+
writeFileSync(join(activityDir, "test.log"), "log data\n");
|
|
359
|
+
run("git add -f .gsd/activity/test.log", dir);
|
|
360
|
+
run("git commit -m \"track runtime file\"", dir);
|
|
361
|
+
|
|
362
|
+
// Write preferences to runner's cwd
|
|
363
|
+
writeRunnerPreferences("none");
|
|
364
|
+
try {
|
|
365
|
+
const result = await runGSDDoctor(dir);
|
|
366
|
+
const trackedIssues = result.issues.filter(i => i.code === "tracked_runtime_files");
|
|
367
|
+
assertTrue(trackedIssues.length > 0, "none-mode: tracked runtime files IS detected");
|
|
368
|
+
} finally {
|
|
369
|
+
removeRunnerPreferences();
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
255
373
|
} finally {
|
|
256
374
|
for (const dir of cleanups) {
|
|
257
375
|
try { rmSync(dir, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marketplace Discovery Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the marketplace discovery module that reads marketplace.json
|
|
5
|
+
* from real Claude marketplace repos, resolves plugin roots, parses plugin.json
|
|
6
|
+
* manifests, and inventories components.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, it } from 'node:test';
|
|
10
|
+
import assert from 'node:assert';
|
|
11
|
+
import * as path from 'node:path';
|
|
12
|
+
import * as fs from 'node:fs';
|
|
13
|
+
import {
|
|
14
|
+
parseMarketplaceJson,
|
|
15
|
+
inspectPlugin,
|
|
16
|
+
discoverMarketplace,
|
|
17
|
+
resolvePluginRoot
|
|
18
|
+
} from '../marketplace-discovery.js';
|
|
19
|
+
import { getMarketplaceFixtures } from './marketplace-test-fixtures.js';
|
|
20
|
+
|
|
21
|
+
const fixtureSetup = getMarketplaceFixtures(import.meta.dirname);
|
|
22
|
+
const fixtures = fixtureSetup.fixtures;
|
|
23
|
+
const CLAUDE_SKILLS_PATH = fixtures?.claudeSkillsPath;
|
|
24
|
+
const CLAUDE_PLUGINS_OFFICIAL_PATH = fixtures?.claudePluginsOfficialPath;
|
|
25
|
+
|
|
26
|
+
const skipReason = !fixtureSetup.available
|
|
27
|
+
? fixtureSetup.skipReason ?? 'Marketplace repos not found'
|
|
28
|
+
: undefined;
|
|
29
|
+
|
|
30
|
+
describe('parseMarketplaceJson', { skip: skipReason }, () => {
|
|
31
|
+
it('should parse jamie-style marketplace.json', () => {
|
|
32
|
+
const result = parseMarketplaceJson(CLAUDE_SKILLS_PATH!);
|
|
33
|
+
assert.strictEqual(result.success, true);
|
|
34
|
+
if (result.success) {
|
|
35
|
+
assert.strictEqual(result.manifest.name, 'jamie-bitflight-skills');
|
|
36
|
+
assert.strictEqual(result.manifest.plugins.length, 26);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should parse official-style marketplace.json', () => {
|
|
41
|
+
const result = parseMarketplaceJson(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
|
42
|
+
assert.strictEqual(result.success, true);
|
|
43
|
+
if (result.success) {
|
|
44
|
+
assert.strictEqual(result.manifest.name, 'claude-plugins-official');
|
|
45
|
+
assert.ok(result.manifest.plugins.length > 50);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should return error for missing marketplace.json', () => {
|
|
50
|
+
const result = parseMarketplaceJson('/tmp/nonexistent');
|
|
51
|
+
assert.strictEqual(result.success, false);
|
|
52
|
+
if (!result.success) {
|
|
53
|
+
assert.ok(result.error.includes('not found'));
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should return error for malformed JSON', () => {
|
|
58
|
+
const tmpDir = '/tmp/test-marketplace-json-' + Date.now();
|
|
59
|
+
fs.mkdirSync(tmpDir + '/.claude-plugin', { recursive: true });
|
|
60
|
+
fs.writeFileSync(tmpDir + '/.claude-plugin/marketplace.json', '{ invalid json');
|
|
61
|
+
|
|
62
|
+
const result = parseMarketplaceJson(tmpDir);
|
|
63
|
+
assert.strictEqual(result.success, false);
|
|
64
|
+
if (!result.success) {
|
|
65
|
+
assert.ok(result.error.includes('Failed to parse'));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
fs.rmSync(tmpDir, { recursive: true });
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('resolvePluginRoot', { skip: skipReason }, () => {
|
|
73
|
+
it('should resolve relative paths correctly', () => {
|
|
74
|
+
const result = resolvePluginRoot(CLAUDE_SKILLS_PATH!, './plugins/python3-development');
|
|
75
|
+
assert.strictEqual(result, path.join(CLAUDE_SKILLS_PATH!, 'plugins/python3-development'));
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should handle paths without ./ prefix', () => {
|
|
79
|
+
const result = resolvePluginRoot(CLAUDE_SKILLS_PATH!, 'plugins/python3-development');
|
|
80
|
+
assert.strictEqual(result, path.join(CLAUDE_SKILLS_PATH!, 'plugins/python3-development'));
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should return null for external sources', () => {
|
|
84
|
+
const result = resolvePluginRoot(CLAUDE_SKILLS_PATH!, 'https://github.com/example/plugin');
|
|
85
|
+
assert.strictEqual(result, null);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should return null for git sources', () => {
|
|
89
|
+
const result = resolvePluginRoot(CLAUDE_SKILLS_PATH!, { source: 'github', repo: 'example/plugin' });
|
|
90
|
+
assert.strictEqual(result, null);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('inspectPlugin', { skip: skipReason }, () => {
|
|
95
|
+
it('should inspect a plugin with plugin.json', () => {
|
|
96
|
+
const pluginDir = path.join(CLAUDE_SKILLS_PATH!, 'plugins/python3-development');
|
|
97
|
+
const result = inspectPlugin(pluginDir);
|
|
98
|
+
|
|
99
|
+
assert.strictEqual(result.status, 'ok');
|
|
100
|
+
assert.strictEqual(result.manifestSource, 'plugin.json');
|
|
101
|
+
assert.strictEqual(result.name, 'python3-development');
|
|
102
|
+
assert.ok(result.description !== undefined);
|
|
103
|
+
assert.ok(result.version !== undefined);
|
|
104
|
+
assert.ok(result.inventory.skills.length > 0);
|
|
105
|
+
assert.ok(result.inventory.agents.length > 0);
|
|
106
|
+
assert.ok(result.inventory.commands.length > 0);
|
|
107
|
+
assert.ok(Object.keys(result.inventory.mcpServers).length > 0);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should return error for non-existent plugin directory', () => {
|
|
111
|
+
const result = inspectPlugin('/tmp/nonexistent-plugin');
|
|
112
|
+
assert.strictEqual(result.status, 'error');
|
|
113
|
+
assert.ok(result.error !== undefined, 'error should be defined');
|
|
114
|
+
assert.ok(result.error.includes('not found'));
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('discoverMarketplace', { skip: skipReason }, () => {
|
|
119
|
+
it('should discover all plugins in jamie-style marketplace', () => {
|
|
120
|
+
const result = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
|
121
|
+
|
|
122
|
+
assert.strictEqual(result.status, 'ok');
|
|
123
|
+
assert.strictEqual(result.pluginFormat, 'jamie-style');
|
|
124
|
+
assert.ok(result.plugins.length > 0);
|
|
125
|
+
assert.ok(result.plugins.every((p: { status: string }) => p.status === 'ok'));
|
|
126
|
+
|
|
127
|
+
assert.strictEqual(result.summary.total, result.plugins.length);
|
|
128
|
+
assert.strictEqual(result.summary.ok, result.plugins.length);
|
|
129
|
+
assert.strictEqual(result.summary.error, 0);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should discover all plugins in official-style marketplace', () => {
|
|
133
|
+
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
|
134
|
+
|
|
135
|
+
assert.strictEqual(result.status, 'ok');
|
|
136
|
+
assert.strictEqual(result.pluginFormat, 'official-style');
|
|
137
|
+
assert.ok(result.plugins.length > 50);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should return structured error for missing marketplace', () => {
|
|
141
|
+
const result = discoverMarketplace('/tmp/nonexistent');
|
|
142
|
+
|
|
143
|
+
assert.strictEqual(result.status, 'error');
|
|
144
|
+
assert.ok(result.error !== undefined);
|
|
145
|
+
assert.ok(result.error.includes('not found'));
|
|
146
|
+
assert.deepStrictEqual(result.plugins, []);
|
|
147
|
+
assert.strictEqual(result.summary.total, 0);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should inventory skills, agents, commands correctly', () => {
|
|
151
|
+
const result = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
|
152
|
+
const pythonPlugin = result.plugins.find((p: { name: string }) => p.name === 'python3-development');
|
|
153
|
+
|
|
154
|
+
assert.ok(pythonPlugin !== undefined);
|
|
155
|
+
if (pythonPlugin) {
|
|
156
|
+
assert.ok(pythonPlugin.inventory.skills.length > 30);
|
|
157
|
+
assert.ok(pythonPlugin.inventory.agents.length > 10);
|
|
158
|
+
assert.ok(pythonPlugin.inventory.commands.length > 0);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should discover MCP servers from plugin.json', () => {
|
|
163
|
+
const result = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
|
164
|
+
const pythonPlugin = result.plugins.find(p => p.name === 'python3-development');
|
|
165
|
+
|
|
166
|
+
assert.ok(pythonPlugin !== undefined);
|
|
167
|
+
if (pythonPlugin) {
|
|
168
|
+
assert.ok(Object.keys(pythonPlugin.inventory.mcpServers).includes('cocoindex-code'));
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('should discover LSP servers from marketplace.json', () => {
|
|
173
|
+
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
|
174
|
+
const tsPlugin = result.plugins.find(p => p.name === 'typescript-lsp');
|
|
175
|
+
|
|
176
|
+
assert.ok(tsPlugin !== undefined);
|
|
177
|
+
if (tsPlugin) {
|
|
178
|
+
assert.ok(Object.keys(tsPlugin.inventory.lspServers).includes('typescript'));
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should detect external plugins correctly', () => {
|
|
183
|
+
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
|
184
|
+
const externalPlugin = result.plugins.find(p => p.name === 'atlassian');
|
|
185
|
+
|
|
186
|
+
assert.ok(externalPlugin !== undefined);
|
|
187
|
+
if (externalPlugin) {
|
|
188
|
+
assert.strictEqual(externalPlugin.resolvedPath, null);
|
|
189
|
+
assert.strictEqual(externalPlugin.status, 'ok');
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe('smoke test', { skip: skipReason }, () => {
|
|
195
|
+
it('should be able to run discovery from both marketplace repos', () => {
|
|
196
|
+
const jamieResult = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
|
197
|
+
const officialResult = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
|
198
|
+
|
|
199
|
+
assert.strictEqual(jamieResult.status, 'ok');
|
|
200
|
+
assert.strictEqual(officialResult.status, 'ok');
|
|
201
|
+
});
|
|
202
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { existsSync, mkdtempSync, rmSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
|
|
6
|
+
export interface MarketplaceFixtureSet {
|
|
7
|
+
claudeSkillsPath: string;
|
|
8
|
+
claudePluginsOfficialPath: string;
|
|
9
|
+
source: 'local' | 'cloned';
|
|
10
|
+
cleanup: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const CLAUDE_SKILLS_REPO = 'https://github.com/Jamie-BitFlight/claude_skills.git';
|
|
14
|
+
const CLAUDE_PLUGINS_OFFICIAL_REPO = 'https://github.com/Jamie-BitFlight/claude-plugins-official.git';
|
|
15
|
+
const CLONE_FIXTURES_ENABLED = process.env.GSD_TEST_CLONE_MARKETPLACES === '1';
|
|
16
|
+
|
|
17
|
+
function canRunGit(): boolean {
|
|
18
|
+
const result = spawnSync('git', ['--version'], { stdio: 'ignore' });
|
|
19
|
+
return result.status === 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function cloneRepo(repo: string, dest: string): void {
|
|
23
|
+
const result = spawnSync('git', ['clone', '--depth', '1', repo, dest], {
|
|
24
|
+
stdio: 'pipe',
|
|
25
|
+
encoding: 'utf8',
|
|
26
|
+
timeout: 120000,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (result.status !== 0) {
|
|
30
|
+
const stderr = (result.stderr || result.stdout || '').trim();
|
|
31
|
+
throw new Error(`git clone failed for ${repo}: ${stderr}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getMarketplaceFixtures(testFileDir: string): { available: boolean; skipReason?: string; fixtures?: MarketplaceFixtureSet } {
|
|
36
|
+
const gsd2Root = resolve(testFileDir, '../../../../..');
|
|
37
|
+
const localClaudeSkillsPath = resolve(gsd2Root, '../claude_skills');
|
|
38
|
+
const localClaudePluginsOfficialPath = resolve(gsd2Root, '../claude-plugins-official');
|
|
39
|
+
|
|
40
|
+
if (existsSync(localClaudeSkillsPath) && existsSync(localClaudePluginsOfficialPath)) {
|
|
41
|
+
return {
|
|
42
|
+
available: true,
|
|
43
|
+
fixtures: {
|
|
44
|
+
claudeSkillsPath: localClaudeSkillsPath,
|
|
45
|
+
claudePluginsOfficialPath: localClaudePluginsOfficialPath,
|
|
46
|
+
source: 'local',
|
|
47
|
+
cleanup: () => {},
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!CLONE_FIXTURES_ENABLED) {
|
|
53
|
+
return {
|
|
54
|
+
available: false,
|
|
55
|
+
skipReason: 'Marketplace repos absent and clone-based fixtures are disabled (set GSD_TEST_CLONE_MARKETPLACES=1 to enable)',
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!canRunGit()) {
|
|
60
|
+
return {
|
|
61
|
+
available: false,
|
|
62
|
+
skipReason: 'Marketplace repos absent and git is unavailable for cloning test fixtures',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const fixtureRoot = mkdtempSync(join(tmpdir(), 'gsd-marketplace-fixtures-'));
|
|
68
|
+
const clonedClaudeSkillsPath = join(fixtureRoot, 'claude_skills');
|
|
69
|
+
const clonedClaudePluginsOfficialPath = join(fixtureRoot, 'claude-plugins-official');
|
|
70
|
+
|
|
71
|
+
cloneRepo(CLAUDE_SKILLS_REPO, clonedClaudeSkillsPath);
|
|
72
|
+
cloneRepo(CLAUDE_PLUGINS_OFFICIAL_REPO, clonedClaudePluginsOfficialPath);
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
available: true,
|
|
76
|
+
fixtures: {
|
|
77
|
+
claudeSkillsPath: clonedClaudeSkillsPath,
|
|
78
|
+
claudePluginsOfficialPath: clonedClaudePluginsOfficialPath,
|
|
79
|
+
source: 'cloned',
|
|
80
|
+
cleanup: () => {
|
|
81
|
+
rmSync(fixtureRoot, { recursive: true, force: true });
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
} catch (error) {
|
|
86
|
+
return {
|
|
87
|
+
available: false,
|
|
88
|
+
skipReason: error instanceof Error ? error.message : String(error),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|