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
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
2
|
+
import { SettingsManager, getAgentDir } from "@gsd/pi-coding-agent";
|
|
3
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
4
|
+
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { PluginImporter, type ImportManifestEntry } from "./plugin-importer.js";
|
|
7
|
+
import type { NamespacedComponent } from "./namespaced-registry.js";
|
|
8
|
+
|
|
9
|
+
export interface ClaudeSkillCandidate {
|
|
10
|
+
type: "skill";
|
|
11
|
+
name: string;
|
|
12
|
+
path: string;
|
|
13
|
+
root: string;
|
|
14
|
+
sourceLabel: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ClaudePluginCandidate {
|
|
18
|
+
type: "plugin";
|
|
19
|
+
name: string;
|
|
20
|
+
path: string;
|
|
21
|
+
root: string;
|
|
22
|
+
sourceLabel: string;
|
|
23
|
+
packageName?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const SKIP_DIRS = new Set([
|
|
27
|
+
".git",
|
|
28
|
+
"node_modules",
|
|
29
|
+
".worktrees",
|
|
30
|
+
"dist",
|
|
31
|
+
"build",
|
|
32
|
+
".next",
|
|
33
|
+
".turbo",
|
|
34
|
+
"cache",
|
|
35
|
+
".cache",
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
function uniqueExistingDirs(paths: string[]): string[] {
|
|
39
|
+
const seen = new Set<string>();
|
|
40
|
+
const out: string[] = [];
|
|
41
|
+
for (const candidate of paths) {
|
|
42
|
+
const resolvedPath = resolve(candidate);
|
|
43
|
+
if (seen.has(resolvedPath)) continue;
|
|
44
|
+
seen.add(resolvedPath);
|
|
45
|
+
if (existsSync(resolvedPath)) out.push(resolvedPath);
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function getClaudeSearchRoots(cwd: string): { skillRoots: string[]; pluginRoots: string[] } {
|
|
51
|
+
const home = homedir();
|
|
52
|
+
const parent = resolve(cwd, "..");
|
|
53
|
+
const grandparent = resolve(cwd, "..", "..");
|
|
54
|
+
|
|
55
|
+
// Claude Code user-scope skills live under ~/.claude/skills.
|
|
56
|
+
// Keep sibling/local clone fallbacks for developer workflows, but they are
|
|
57
|
+
// examples/convenience paths rather than the primary Claude storage model.
|
|
58
|
+
const skillRoots = uniqueExistingDirs([
|
|
59
|
+
join(home, ".claude", "skills"),
|
|
60
|
+
join(home, "repos", "claude_skills"),
|
|
61
|
+
join(home, "repos", "skills"),
|
|
62
|
+
join(parent, "claude_skills"),
|
|
63
|
+
join(parent, "skills"),
|
|
64
|
+
join(grandparent, "claude_skills"),
|
|
65
|
+
join(grandparent, "skills"),
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
// Anthropic docs model marketplaces as sources users add with
|
|
69
|
+
// `/plugin marketplace add ...`, and Claude stores those marketplaces under
|
|
70
|
+
// ~/.claude/plugins/marketplaces/. Installed plugin payloads are copied into
|
|
71
|
+
// ~/.claude/plugins/cache/. We prefer those stable Claude-managed locations
|
|
72
|
+
// before local example clones.
|
|
73
|
+
const pluginRoots = uniqueExistingDirs([
|
|
74
|
+
join(home, ".claude", "plugins", "marketplaces"),
|
|
75
|
+
join(home, ".claude", "plugins", "cache"),
|
|
76
|
+
join(home, ".claude", "plugins"),
|
|
77
|
+
join(home, "repos", "claude-plugins-official"),
|
|
78
|
+
join(home, "repos", "claude_skills"),
|
|
79
|
+
join(parent, "claude-plugins-official"),
|
|
80
|
+
join(parent, "claude_skills"),
|
|
81
|
+
join(grandparent, "claude-plugins-official"),
|
|
82
|
+
join(grandparent, "claude_skills"),
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
return { skillRoots, pluginRoots };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function sourceLabel(path: string): string {
|
|
89
|
+
const home = homedir();
|
|
90
|
+
if (path.startsWith(join(home, ".claude"))) return "claude-home";
|
|
91
|
+
if (path.startsWith(join(home, "repos"))) return "repos";
|
|
92
|
+
return "local";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Check if a path is a marketplace directory (contains .claude-plugin/marketplace.json).
|
|
97
|
+
* Marketplace paths use the PluginImporter flow; non-marketplace use the legacy flat flow.
|
|
98
|
+
*/
|
|
99
|
+
function isMarketplacePath(pluginPath: string): boolean {
|
|
100
|
+
const marketplaceJson = join(pluginPath, ".claude-plugin", "marketplace.json");
|
|
101
|
+
return existsSync(marketplaceJson);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Detect which plugin roots are marketplaces and which are legacy flat paths.
|
|
106
|
+
*/
|
|
107
|
+
function categorizePluginRoots(pluginRoots: string[]): { marketplaces: string[]; flat: string[] } {
|
|
108
|
+
const marketplaces: string[] = [];
|
|
109
|
+
const flat: string[] = [];
|
|
110
|
+
|
|
111
|
+
for (const root of pluginRoots) {
|
|
112
|
+
if (isMarketplacePath(root)) {
|
|
113
|
+
marketplaces.push(root);
|
|
114
|
+
} else {
|
|
115
|
+
flat.push(root);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return { marketplaces, flat };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function walkDirs(root: string, visit: (dir: string, depth: number) => void, maxDepth = 4): void {
|
|
123
|
+
function walk(dir: string, depth: number) {
|
|
124
|
+
visit(dir, depth);
|
|
125
|
+
if (depth >= maxDepth) return;
|
|
126
|
+
let entries: Array<{ name: string; isDirectory: () => boolean }> = [];
|
|
127
|
+
try {
|
|
128
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
129
|
+
} catch {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
for (const entry of entries) {
|
|
133
|
+
if (!entry.isDirectory()) continue;
|
|
134
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
135
|
+
walk(join(dir, entry.name), depth + 1);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
walk(root, 0);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function discoverClaudeSkills(cwd: string): ClaudeSkillCandidate[] {
|
|
142
|
+
const { skillRoots } = getClaudeSearchRoots(cwd);
|
|
143
|
+
const results: ClaudeSkillCandidate[] = [];
|
|
144
|
+
const seen = new Set<string>();
|
|
145
|
+
|
|
146
|
+
for (const root of skillRoots) {
|
|
147
|
+
walkDirs(root, (dir) => {
|
|
148
|
+
const skillFile = join(dir, "SKILL.md");
|
|
149
|
+
if (!existsSync(skillFile)) return;
|
|
150
|
+
const resolvedDir = resolve(dir);
|
|
151
|
+
if (seen.has(resolvedDir)) return;
|
|
152
|
+
seen.add(resolvedDir);
|
|
153
|
+
results.push({
|
|
154
|
+
type: "skill",
|
|
155
|
+
name: basename(dir),
|
|
156
|
+
path: resolvedDir,
|
|
157
|
+
root,
|
|
158
|
+
sourceLabel: sourceLabel(root),
|
|
159
|
+
});
|
|
160
|
+
}, 5);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return results.sort((a, b) => a.name.localeCompare(b.name) || a.path.localeCompare(b.path));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function discoverClaudePlugins(cwd: string): ClaudePluginCandidate[] {
|
|
167
|
+
const { pluginRoots } = getClaudeSearchRoots(cwd);
|
|
168
|
+
const results: ClaudePluginCandidate[] = [];
|
|
169
|
+
const seen = new Set<string>();
|
|
170
|
+
|
|
171
|
+
for (const root of pluginRoots) {
|
|
172
|
+
walkDirs(root, (dir) => {
|
|
173
|
+
const pkgPath = join(dir, "package.json");
|
|
174
|
+
if (!existsSync(pkgPath)) return;
|
|
175
|
+
const resolvedDir = resolve(dir);
|
|
176
|
+
if (seen.has(resolvedDir)) return;
|
|
177
|
+
seen.add(resolvedDir);
|
|
178
|
+
let packageName: string | undefined;
|
|
179
|
+
try {
|
|
180
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as { name?: string };
|
|
181
|
+
packageName = pkg.name;
|
|
182
|
+
} catch {
|
|
183
|
+
packageName = undefined;
|
|
184
|
+
}
|
|
185
|
+
results.push({
|
|
186
|
+
type: "plugin",
|
|
187
|
+
name: packageName || basename(dir),
|
|
188
|
+
packageName,
|
|
189
|
+
path: resolvedDir,
|
|
190
|
+
root,
|
|
191
|
+
sourceLabel: sourceLabel(root),
|
|
192
|
+
});
|
|
193
|
+
}, 4);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return results.sort((a, b) => a.name.localeCompare(b.name) || a.path.localeCompare(b.path));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function chooseMany<T extends { name: string; path: string; root: string; sourceLabel: string }>(
|
|
200
|
+
ctx: ExtensionCommandContext,
|
|
201
|
+
title: string,
|
|
202
|
+
candidates: T[],
|
|
203
|
+
): Promise<T[]> {
|
|
204
|
+
if (candidates.length === 0) return [];
|
|
205
|
+
|
|
206
|
+
const mode = await ctx.ui.select(`${title} (${candidates.length} found)`, [
|
|
207
|
+
"Import all discovered",
|
|
208
|
+
"Select individually",
|
|
209
|
+
"Cancel",
|
|
210
|
+
]);
|
|
211
|
+
|
|
212
|
+
if (!mode || mode === "Cancel") return [];
|
|
213
|
+
if (mode === "Import all discovered") return candidates;
|
|
214
|
+
|
|
215
|
+
const remaining = [...candidates];
|
|
216
|
+
const selected: T[] = [];
|
|
217
|
+
while (remaining.length > 0) {
|
|
218
|
+
const options = [
|
|
219
|
+
...remaining.map((item) => `${item.name} — ${item.sourceLabel} — ${relative(item.root, item.path) || "."}`),
|
|
220
|
+
"Done selecting",
|
|
221
|
+
];
|
|
222
|
+
const picked = await ctx.ui.select(`${title}: choose an item`, options);
|
|
223
|
+
if (!picked || picked === "Done selecting") break;
|
|
224
|
+
const pickedStr = Array.isArray(picked) ? picked[0] : picked;
|
|
225
|
+
if (!pickedStr) break;
|
|
226
|
+
const idx = options.indexOf(pickedStr);
|
|
227
|
+
if (idx < 0 || idx >= remaining.length) break;
|
|
228
|
+
selected.push(remaining[idx]!);
|
|
229
|
+
remaining.splice(idx, 1);
|
|
230
|
+
}
|
|
231
|
+
return selected;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function mergeStringList(existing: unknown, additions: string[]): string[] {
|
|
235
|
+
const list = Array.isArray(existing) ? existing.filter((v): v is string => typeof v === "string") : [];
|
|
236
|
+
const seen = new Set(list);
|
|
237
|
+
for (const item of additions) {
|
|
238
|
+
if (!seen.has(item)) {
|
|
239
|
+
list.push(item);
|
|
240
|
+
seen.add(item);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return list;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function mergePackageSources(existing: unknown, additions: string[]): Array<string | { source: string }> {
|
|
247
|
+
const current = Array.isArray(existing)
|
|
248
|
+
? existing.filter((v): v is string | { source: string } => typeof v === "string" || (typeof v === "object" && v !== null && typeof (v as { source?: unknown }).source === "string"))
|
|
249
|
+
: [];
|
|
250
|
+
|
|
251
|
+
const seen = new Set(current.map((entry) => typeof entry === "string" ? entry : entry.source));
|
|
252
|
+
const merged = [...current];
|
|
253
|
+
for (const add of additions) {
|
|
254
|
+
if (!seen.has(add)) {
|
|
255
|
+
merged.push(add);
|
|
256
|
+
seen.add(add);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return merged;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// ============================================================================
|
|
263
|
+
// Marketplace PluginImporter Integration (T02)
|
|
264
|
+
// ============================================================================
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Component candidate from marketplace discovery.
|
|
268
|
+
* Extends NamespacedComponent with UI-friendly fields.
|
|
269
|
+
*/
|
|
270
|
+
interface MarketplaceComponentCandidate {
|
|
271
|
+
component: NamespacedComponent;
|
|
272
|
+
displayName: string;
|
|
273
|
+
pluginName: string;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Format a component for display in selection UI.
|
|
278
|
+
*/
|
|
279
|
+
function formatComponentForSelection(comp: NamespacedComponent): string {
|
|
280
|
+
const typeLabel = comp.type === 'skill' ? '🔧' : '🤖';
|
|
281
|
+
const nsLabel = comp.namespace ? `${comp.namespace}:` : '';
|
|
282
|
+
return `${typeLabel} ${nsLabel}${comp.name}`;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Present marketplace components for user selection, grouped by plugin.
|
|
287
|
+
* Returns the selected components for import.
|
|
288
|
+
*/
|
|
289
|
+
async function selectMarketplaceComponents(
|
|
290
|
+
ctx: ExtensionCommandContext,
|
|
291
|
+
importer: PluginImporter,
|
|
292
|
+
scope: "global" | "project"
|
|
293
|
+
): Promise<NamespacedComponent[]> {
|
|
294
|
+
const plugins = importer.getDiscoveredPlugins();
|
|
295
|
+
|
|
296
|
+
if (plugins.length === 0) {
|
|
297
|
+
ctx.ui.notify("No plugins discovered in marketplace.", "info");
|
|
298
|
+
return [];
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Build component candidates grouped by plugin
|
|
302
|
+
const allComponents: MarketplaceComponentCandidate[] = [];
|
|
303
|
+
for (const plugin of plugins) {
|
|
304
|
+
const components = importer.selectComponents(c => c.namespace === plugin.canonicalName);
|
|
305
|
+
for (const comp of components) {
|
|
306
|
+
allComponents.push({
|
|
307
|
+
component: comp,
|
|
308
|
+
displayName: formatComponentForSelection(comp),
|
|
309
|
+
pluginName: plugin.canonicalName,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (allComponents.length === 0) {
|
|
315
|
+
ctx.ui.notify("No components (skills/agents) found in marketplace plugins.", "info");
|
|
316
|
+
return [];
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Ask user for selection mode
|
|
320
|
+
const mode = await ctx.ui.select(
|
|
321
|
+
`Marketplace components → ${scope} config (${allComponents.length} found across ${plugins.length} plugins)`,
|
|
322
|
+
[
|
|
323
|
+
"Import all components",
|
|
324
|
+
"Select by plugin",
|
|
325
|
+
"Select individually",
|
|
326
|
+
"Cancel",
|
|
327
|
+
]
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
if (!mode || mode === "Cancel") return [];
|
|
331
|
+
|
|
332
|
+
if (mode === "Import all components") {
|
|
333
|
+
return allComponents.map(c => c.component);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (mode === "Select by plugin") {
|
|
337
|
+
// Let user select plugins, then import all their components
|
|
338
|
+
const pluginNames = plugins.map(p => p.canonicalName);
|
|
339
|
+
const selectedPluginNames: string[] = [];
|
|
340
|
+
|
|
341
|
+
while (true) {
|
|
342
|
+
const remaining = pluginNames.filter(n => !selectedPluginNames.includes(n));
|
|
343
|
+
if (remaining.length === 0) break;
|
|
344
|
+
|
|
345
|
+
const options = [...remaining, "Done selecting"];
|
|
346
|
+
const picked = await ctx.ui.select("Select a plugin to import all its components", options);
|
|
347
|
+
|
|
348
|
+
if (!picked || picked === "Done selecting") break;
|
|
349
|
+
const pickedStr = Array.isArray(picked) ? picked[0] : picked;
|
|
350
|
+
if (!pickedStr) break;
|
|
351
|
+
selectedPluginNames.push(pickedStr);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return allComponents
|
|
355
|
+
.filter(c => selectedPluginNames.includes(c.pluginName))
|
|
356
|
+
.map(c => c.component);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Select individually
|
|
360
|
+
const remaining = [...allComponents];
|
|
361
|
+
const selected: NamespacedComponent[] = [];
|
|
362
|
+
|
|
363
|
+
while (remaining.length > 0) {
|
|
364
|
+
const options = remaining.map(c =>
|
|
365
|
+
`${c.displayName} — ${c.pluginName}`
|
|
366
|
+
);
|
|
367
|
+
options.push("Done selecting");
|
|
368
|
+
|
|
369
|
+
const picked = await ctx.ui.select("Select a component to import", options);
|
|
370
|
+
if (!picked || picked === "Done selecting") break;
|
|
371
|
+
const pickedStr = Array.isArray(picked) ? picked[0] : picked;
|
|
372
|
+
if (!pickedStr) break;
|
|
373
|
+
|
|
374
|
+
const idx = options.indexOf(pickedStr);
|
|
375
|
+
if (idx < 0 || idx >= remaining.length) break;
|
|
376
|
+
|
|
377
|
+
selected.push(remaining[idx]!.component);
|
|
378
|
+
remaining.splice(idx, 1);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return selected;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Format diagnostics for display to user.
|
|
386
|
+
* Returns a human-readable summary string.
|
|
387
|
+
*/
|
|
388
|
+
function formatDiagnosticsForUser(
|
|
389
|
+
diagnostics: Array<{ severity: string; class: string; remediation: string; involvedCanonicalNames: string[] }>
|
|
390
|
+
): string {
|
|
391
|
+
const lines: string[] = [];
|
|
392
|
+
|
|
393
|
+
const errors = diagnostics.filter(d => d.severity === 'error');
|
|
394
|
+
const warnings = diagnostics.filter(d => d.severity === 'warning');
|
|
395
|
+
|
|
396
|
+
if (errors.length > 0) {
|
|
397
|
+
lines.push(`❌ ${errors.length} error(s) blocking import:`);
|
|
398
|
+
for (const err of errors) {
|
|
399
|
+
lines.push(` - ${err.class}: ${err.involvedCanonicalNames.join(', ')}`);
|
|
400
|
+
lines.push(` ${err.remediation}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (warnings.length > 0) {
|
|
405
|
+
lines.push(`⚠️ ${warnings.length} warning(s):`);
|
|
406
|
+
for (const warn of warnings) {
|
|
407
|
+
lines.push(` - ${warn.class}: ${warn.involvedCanonicalNames.join(', ')}`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return lines.join('\n');
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Persist import manifest entries to settings.
|
|
416
|
+
* Maps manifest entries to the appropriate settings format.
|
|
417
|
+
*/
|
|
418
|
+
function persistManifestToSettings(
|
|
419
|
+
manifestEntries: ImportManifestEntry[],
|
|
420
|
+
settingsManager: SettingsManager,
|
|
421
|
+
scope: "global" | "project"
|
|
422
|
+
): void {
|
|
423
|
+
// Group entries by namespace for organized persistence
|
|
424
|
+
const skillPaths = manifestEntries
|
|
425
|
+
.filter(e => e.type === 'skill')
|
|
426
|
+
.map(e => e.filePath);
|
|
427
|
+
|
|
428
|
+
const agentPaths = manifestEntries
|
|
429
|
+
.filter(e => e.type === 'agent')
|
|
430
|
+
.map(e => e.filePath);
|
|
431
|
+
|
|
432
|
+
// For marketplace plugins, we also want to store plugin-level metadata
|
|
433
|
+
// Currently this adds component paths to skills/agents lists
|
|
434
|
+
// Future enhancement: store canonical names with metadata
|
|
435
|
+
|
|
436
|
+
if (skillPaths.length > 0) {
|
|
437
|
+
if (scope === "project") {
|
|
438
|
+
settingsManager.setProjectSkillPaths(
|
|
439
|
+
mergeStringList(settingsManager.getProjectSettings().skills, skillPaths)
|
|
440
|
+
);
|
|
441
|
+
} else {
|
|
442
|
+
settingsManager.setSkillPaths(
|
|
443
|
+
mergeStringList(settingsManager.getGlobalSettings().skills, skillPaths)
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Do not persist imported marketplace agents into settings.packages.
|
|
449
|
+
// Claude plugin agent directories contain markdown agent definitions, not loadable Pi
|
|
450
|
+
// extension packages. Writing `.../agents` paths into packages makes startup treat
|
|
451
|
+
// them as extension roots and produces module-load errors.
|
|
452
|
+
//
|
|
453
|
+
// For now, marketplace agents remain discoverable via the import manifest and
|
|
454
|
+
// canonical metadata, but are not persisted into package sources.
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
export async function runClaudeImportFlow(
|
|
459
|
+
ctx: ExtensionCommandContext,
|
|
460
|
+
scope: "global" | "project",
|
|
461
|
+
readPrefs: () => Record<string, unknown>,
|
|
462
|
+
writePrefs: (prefs: Record<string, unknown>) => Promise<void>,
|
|
463
|
+
): Promise<void> {
|
|
464
|
+
const cwd = process.cwd();
|
|
465
|
+
const settingsManager = SettingsManager.create(cwd, getAgentDir());
|
|
466
|
+
const { skillRoots, pluginRoots } = getClaudeSearchRoots(cwd);
|
|
467
|
+
|
|
468
|
+
// Categorize plugin roots into marketplaces vs flat paths
|
|
469
|
+
const { marketplaces, flat } = categorizePluginRoots(pluginRoots);
|
|
470
|
+
|
|
471
|
+
// Determine import mode
|
|
472
|
+
const assetChoice = await ctx.ui.select("Import Claude assets into GSD/Pi config", [
|
|
473
|
+
"Skills + plugins",
|
|
474
|
+
"Skills only",
|
|
475
|
+
"Plugins only",
|
|
476
|
+
"Cancel",
|
|
477
|
+
]);
|
|
478
|
+
if (!assetChoice || assetChoice === "Cancel") return;
|
|
479
|
+
|
|
480
|
+
const importSkills = assetChoice !== "Plugins only";
|
|
481
|
+
const importPlugins = assetChoice !== "Skills only";
|
|
482
|
+
|
|
483
|
+
// Track what we're importing
|
|
484
|
+
let importedSkillsCount = 0;
|
|
485
|
+
let importedPluginsCount = 0;
|
|
486
|
+
let importedMarketplaceComponents = 0;
|
|
487
|
+
const canonicalNamesPersisted: string[] = [];
|
|
488
|
+
|
|
489
|
+
// ========== SKILLS (legacy flat flow) ==========
|
|
490
|
+
if (importSkills) {
|
|
491
|
+
const discoveredSkills = discoverClaudeSkills(cwd);
|
|
492
|
+
const selectedSkills = await chooseMany(ctx, `Claude skills → ${scope} preferences`, discoveredSkills);
|
|
493
|
+
|
|
494
|
+
if (selectedSkills.length > 0) {
|
|
495
|
+
const prefMode = await ctx.ui.select("How should GSD treat the imported skills?", [
|
|
496
|
+
"Always use when relevant",
|
|
497
|
+
"Prefer when relevant",
|
|
498
|
+
"Do not modify skill preferences",
|
|
499
|
+
]);
|
|
500
|
+
|
|
501
|
+
const prefs = readPrefs();
|
|
502
|
+
const skillPaths = selectedSkills.map((skill) => skill.path);
|
|
503
|
+
if (prefMode === "Always use when relevant") {
|
|
504
|
+
prefs.always_use_skills = mergeStringList(prefs.always_use_skills, skillPaths);
|
|
505
|
+
} else if (prefMode === "Prefer when relevant") {
|
|
506
|
+
prefs.prefer_skills = mergeStringList(prefs.prefer_skills, skillPaths);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
await writePrefs(prefs);
|
|
510
|
+
|
|
511
|
+
if (scope === "project") {
|
|
512
|
+
settingsManager.setProjectSkillPaths(mergeStringList(settingsManager.getProjectSettings().skills, skillPaths));
|
|
513
|
+
} else {
|
|
514
|
+
settingsManager.setSkillPaths(mergeStringList(settingsManager.getGlobalSettings().skills, skillPaths));
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
importedSkillsCount = selectedSkills.length;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// ========== MARKETPLACE PLUGINS (new PluginImporter flow) ==========
|
|
522
|
+
if (importPlugins && marketplaces.length > 0) {
|
|
523
|
+
const marketplaceChoice = await ctx.ui.select(
|
|
524
|
+
`Found ${marketplaces.length} marketplace(s). Import from marketplace?`,
|
|
525
|
+
[
|
|
526
|
+
"Yes - discover plugins and select components",
|
|
527
|
+
"Skip marketplaces (use legacy plugin paths only)",
|
|
528
|
+
"Cancel",
|
|
529
|
+
]
|
|
530
|
+
);
|
|
531
|
+
|
|
532
|
+
if (marketplaceChoice === "Yes - discover plugins and select components") {
|
|
533
|
+
// Instantiate PluginImporter and discover
|
|
534
|
+
const importer = new PluginImporter();
|
|
535
|
+
const discovery = importer.discover(marketplaces);
|
|
536
|
+
|
|
537
|
+
if (discovery.summary.totalPlugins > 0) {
|
|
538
|
+
// Present components for selection
|
|
539
|
+
const selectedComponents = await selectMarketplaceComponents(ctx, importer, scope);
|
|
540
|
+
|
|
541
|
+
if (selectedComponents.length > 0) {
|
|
542
|
+
// Run validation (pre-import diagnostics)
|
|
543
|
+
const validation = importer.validateImport(selectedComponents);
|
|
544
|
+
|
|
545
|
+
// Show diagnostics
|
|
546
|
+
if (validation.diagnostics.length > 0) {
|
|
547
|
+
const diagMessage = formatDiagnosticsForUser(validation.diagnostics);
|
|
548
|
+
ctx.ui.notify(diagMessage, validation.canProceed ? "warning" : "error");
|
|
549
|
+
|
|
550
|
+
// Block if errors exist
|
|
551
|
+
if (!validation.canProceed) {
|
|
552
|
+
ctx.ui.notify(
|
|
553
|
+
"Import blocked due to canonical name conflicts. Please resolve the errors above.",
|
|
554
|
+
"error"
|
|
555
|
+
);
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Warn but allow proceed for warnings
|
|
560
|
+
const proceed = await ctx.ui.select(
|
|
561
|
+
"Warnings detected. Continue with import?",
|
|
562
|
+
["Yes, continue", "Cancel"]
|
|
563
|
+
);
|
|
564
|
+
if (proceed !== "Yes, continue") {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Generate manifest and persist
|
|
570
|
+
const manifest = importer.getImportManifest(selectedComponents);
|
|
571
|
+
persistManifestToSettings(manifest.entries, settingsManager, scope);
|
|
572
|
+
|
|
573
|
+
importedMarketplaceComponents = selectedComponents.length;
|
|
574
|
+
canonicalNamesPersisted.push(...manifest.entries.map(e => e.canonicalName));
|
|
575
|
+
}
|
|
576
|
+
} else {
|
|
577
|
+
ctx.ui.notify(`No plugins discovered in ${marketplaces.length} marketplace(s).`, "info");
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// ========== FLAT PLUGIN PATHS (legacy flow) ==========
|
|
583
|
+
if (importPlugins && flat.length > 0) {
|
|
584
|
+
// Use legacy discovery for non-marketplace paths
|
|
585
|
+
const discoveredPlugins: ClaudePluginCandidate[] = [];
|
|
586
|
+
const seen = new Set<string>();
|
|
587
|
+
|
|
588
|
+
for (const root of flat) {
|
|
589
|
+
walkDirs(root, (dir) => {
|
|
590
|
+
const pkgPath = join(dir, "package.json");
|
|
591
|
+
if (!existsSync(pkgPath)) return;
|
|
592
|
+
const resolvedDir = resolve(dir);
|
|
593
|
+
if (seen.has(resolvedDir)) return;
|
|
594
|
+
seen.add(resolvedDir);
|
|
595
|
+
let packageName: string | undefined;
|
|
596
|
+
try {
|
|
597
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as { name?: string };
|
|
598
|
+
packageName = pkg.name;
|
|
599
|
+
} catch {
|
|
600
|
+
packageName = undefined;
|
|
601
|
+
}
|
|
602
|
+
discoveredPlugins.push({
|
|
603
|
+
type: "plugin",
|
|
604
|
+
name: packageName || basename(dir),
|
|
605
|
+
packageName,
|
|
606
|
+
path: resolvedDir,
|
|
607
|
+
root,
|
|
608
|
+
sourceLabel: sourceLabel(root),
|
|
609
|
+
});
|
|
610
|
+
}, 4);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const sortedPlugins = discoveredPlugins.sort((a, b) => a.name.localeCompare(b.name) || a.path.localeCompare(b.path));
|
|
614
|
+
const selectedPlugins = await chooseMany(ctx, `Claude plugins/packages → ${scope} Pi settings`, sortedPlugins);
|
|
615
|
+
|
|
616
|
+
if (selectedPlugins.length > 0) {
|
|
617
|
+
const pluginPaths = selectedPlugins.map((plugin) => plugin.path);
|
|
618
|
+
if (scope === "project") {
|
|
619
|
+
settingsManager.setProjectPackages(mergePackageSources(settingsManager.getProjectSettings().packages, pluginPaths));
|
|
620
|
+
} else {
|
|
621
|
+
settingsManager.setPackages(mergePackageSources(settingsManager.getGlobalSettings().packages, pluginPaths));
|
|
622
|
+
}
|
|
623
|
+
importedPluginsCount = selectedPlugins.length;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// ========== FINAL SUMMARY ==========
|
|
628
|
+
if (importedSkillsCount === 0 && importedPluginsCount === 0 && importedMarketplaceComponents === 0) {
|
|
629
|
+
ctx.ui.notify("Claude import cancelled or nothing selected.", "info");
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
await ctx.waitForIdle();
|
|
634
|
+
await ctx.reload();
|
|
635
|
+
|
|
636
|
+
const lines = [
|
|
637
|
+
`Imported Claude assets into ${scope} config:`,
|
|
638
|
+
`- Skills (flat): ${importedSkillsCount}`,
|
|
639
|
+
`- Plugins (flat paths): ${importedPluginsCount}`,
|
|
640
|
+
`- Marketplace components: ${importedMarketplaceComponents}`,
|
|
641
|
+
];
|
|
642
|
+
if (importedSkillsCount > 0) {
|
|
643
|
+
lines.push(`- Skill paths added to Pi settings (${scope}) for availability`);
|
|
644
|
+
lines.push(`- Skill refs added to GSD preferences (${scope}) when selected`);
|
|
645
|
+
}
|
|
646
|
+
if (importedPluginsCount > 0) {
|
|
647
|
+
lines.push(`- Plugin/package paths added to Pi settings (${scope}) packages`);
|
|
648
|
+
}
|
|
649
|
+
if (importedMarketplaceComponents > 0) {
|
|
650
|
+
lines.push(`- Canonical names preserved: ${canonicalNamesPersisted.length} entries`);
|
|
651
|
+
if (canonicalNamesPersisted.length <= 10) {
|
|
652
|
+
lines.push(` Names: ${canonicalNamesPersisted.join(', ')}`);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
656
|
+
}
|