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.
Files changed (279) hide show
  1. package/README.md +12 -0
  2. package/dist/cli.js +21 -18
  3. package/dist/help-text.d.ts +2 -0
  4. package/dist/help-text.js +47 -0
  5. package/dist/loader.js +2 -16
  6. package/dist/mcp-server.d.ts +18 -0
  7. package/dist/mcp-server.js +53 -0
  8. package/dist/onboarding.d.ts +0 -6
  9. package/dist/onboarding.js +0 -28
  10. package/dist/resources/agents/javascript-pro.md +280 -0
  11. package/dist/resources/agents/typescript-pro.md +255 -0
  12. package/dist/resources/extensions/bg-shell/index.ts +14 -2
  13. package/dist/resources/extensions/bg-shell/utilities.ts +14 -0
  14. package/dist/resources/extensions/browser-tools/{core.js → core.ts} +329 -190
  15. package/dist/resources/extensions/gsd/auto-dashboard.ts +2 -1
  16. package/dist/resources/extensions/gsd/auto-prompts.ts +42 -1
  17. package/dist/resources/extensions/gsd/auto-worktree.ts +35 -2
  18. package/dist/resources/extensions/gsd/auto.ts +116 -10
  19. package/dist/resources/extensions/gsd/claude-import.ts +656 -0
  20. package/dist/resources/extensions/gsd/collision-diagnostics.ts +332 -0
  21. package/dist/resources/extensions/gsd/commands.ts +60 -7
  22. package/dist/resources/extensions/gsd/diff-context.ts +220 -0
  23. package/dist/resources/extensions/gsd/docs/claude-marketplace-import.md +214 -0
  24. package/dist/resources/extensions/gsd/docs/preferences-reference.md +2 -1
  25. package/dist/resources/extensions/gsd/doctor.ts +12 -4
  26. package/dist/resources/extensions/gsd/file-watcher.ts +97 -0
  27. package/dist/resources/extensions/gsd/files.ts +1 -1
  28. package/dist/resources/extensions/gsd/forensics.ts +596 -0
  29. package/dist/resources/extensions/gsd/git-service.ts +2 -1
  30. package/dist/resources/extensions/gsd/github-client.ts +235 -0
  31. package/dist/resources/extensions/gsd/gitignore.ts +1 -0
  32. package/dist/resources/extensions/gsd/marketplace-discovery.ts +507 -0
  33. package/dist/resources/extensions/gsd/mcp-server.ts +87 -0
  34. package/dist/resources/extensions/gsd/namespaced-registry.ts +467 -0
  35. package/dist/resources/extensions/gsd/namespaced-resolver.ts +307 -0
  36. package/dist/resources/extensions/gsd/plugin-importer.ts +410 -0
  37. package/dist/resources/extensions/gsd/preferences.ts +14 -3
  38. package/dist/resources/extensions/gsd/prompts/execute-task.md +1 -1
  39. package/dist/resources/extensions/gsd/prompts/forensics.md +71 -0
  40. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  41. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
  42. package/dist/resources/extensions/gsd/prompts/system.md +12 -3
  43. package/dist/resources/extensions/gsd/roadmap-slices.ts +1 -1
  44. package/dist/resources/extensions/gsd/session-forensics.ts +5 -5
  45. package/dist/resources/extensions/gsd/tests/claude-import-tui.test.ts +351 -0
  46. package/dist/resources/extensions/gsd/tests/collision-diagnostics.test.ts +705 -0
  47. package/dist/resources/extensions/gsd/tests/context-compression.test.ts +13 -0
  48. package/dist/resources/extensions/gsd/tests/diff-context.test.ts +136 -0
  49. package/dist/resources/extensions/gsd/tests/doctor-git.test.ts +118 -0
  50. package/dist/resources/extensions/gsd/tests/marketplace-discovery.test.ts +202 -0
  51. package/dist/resources/extensions/gsd/tests/marketplace-test-fixtures.ts +91 -0
  52. package/dist/resources/extensions/gsd/tests/namespaced-registry.test.ts +1027 -0
  53. package/dist/resources/extensions/gsd/tests/namespaced-resolver.test.ts +671 -0
  54. package/dist/resources/extensions/gsd/tests/none-mode-gates.test.ts +105 -0
  55. package/dist/resources/extensions/gsd/tests/plugin-importer-live.test.ts +481 -0
  56. package/dist/resources/extensions/gsd/tests/plugin-importer.test.ts +1383 -0
  57. package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +21 -2
  58. package/dist/resources/extensions/gsd/tests/preferences-schema-validation.test.ts +8 -1
  59. package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +11 -0
  60. package/dist/resources/extensions/gsd/tests/worktree-bugfix.test.ts +120 -0
  61. package/dist/resources/extensions/gsd/token-counter.ts +45 -0
  62. package/dist/resources/extensions/gsd/visualizer-data.ts +2 -2
  63. package/dist/resources/extensions/gsd/worktree-manager.ts +29 -1
  64. package/dist/resources/extensions/gsd/worktree.ts +3 -0
  65. package/dist/resources/extensions/mcporter/index.ts +90 -7
  66. package/dist/resources/extensions/search-the-web/native-search.ts +2 -0
  67. package/dist/resources/extensions/search-the-web/tool-fetch-page.ts +9 -1
  68. package/dist/resources/extensions/search-the-web/url-utils.ts +35 -1
  69. package/dist/resources/extensions/shared/terminal.ts +1 -1
  70. package/dist/resources/extensions/universal-config/discovery.ts +4 -0
  71. package/dist/resources/extensions/universal-config/format.ts +35 -4
  72. package/dist/resources/extensions/universal-config/index.ts +5 -3
  73. package/dist/resources/extensions/universal-config/scanners.ts +65 -2
  74. package/dist/resources/extensions/universal-config/tests/discovery.test.ts +9 -1
  75. package/dist/resources/extensions/universal-config/tests/format.test.ts +22 -6
  76. package/dist/resources/extensions/universal-config/tests/scanners.test.ts +18 -0
  77. package/dist/resources/extensions/universal-config/types.ts +20 -1
  78. package/dist/resources/skills/lint/SKILL.md +141 -0
  79. package/dist/resources/skills/review/SKILL.md +214 -0
  80. package/dist/resources/skills/test/SKILL.md +201 -0
  81. package/dist/tool-bootstrap.js +2 -1
  82. package/dist/wizard.js +2 -0
  83. package/package.json +12 -7
  84. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
  85. package/packages/pi-ai/dist/providers/azure-openai-responses.js +12 -1
  86. package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
  87. package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  88. package/packages/pi-ai/dist/providers/openai-responses.js +12 -1
  89. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
  90. package/packages/pi-ai/node_modules/@smithy/node-http-handler/LICENSE +201 -0
  91. package/packages/pi-ai/node_modules/@smithy/node-http-handler/README.md +9 -0
  92. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-cjs/index.js +762 -0
  93. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/build-abort-error.js +19 -0
  94. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/constants.js +1 -0
  95. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/get-transformed-headers.js +9 -0
  96. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/index.js +3 -0
  97. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http-handler.js +230 -0
  98. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-manager.js +87 -0
  99. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-pool.js +32 -0
  100. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http2-handler.js +169 -0
  101. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/readable.mock.js +21 -0
  102. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/server.mock.js +88 -0
  103. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-connection-timeout.js +36 -0
  104. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-request-timeout.js +21 -0
  105. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-socket-keep-alive.js +22 -0
  106. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-socket-timeout.js +23 -0
  107. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/stream-collector/collector.js +8 -0
  108. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/stream-collector/index.js +41 -0
  109. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/stream-collector/readable.mock.js +21 -0
  110. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/timing.js +4 -0
  111. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/write-request-body.js +63 -0
  112. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/build-abort-error.d.ts +10 -0
  113. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/constants.d.ts +5 -0
  114. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/get-transformed-headers.d.ts +4 -0
  115. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/index.d.ts +3 -0
  116. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http-handler.d.ts +46 -0
  117. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http2-connection-manager.d.ts +24 -0
  118. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http2-connection-pool.d.ts +12 -0
  119. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http2-handler.d.ts +63 -0
  120. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/readable.mock.d.ts +13 -0
  121. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/server.mock.d.ts +12 -0
  122. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-connection-timeout.d.ts +2 -0
  123. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-request-timeout.d.ts +6 -0
  124. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-socket-keep-alive.d.ts +6 -0
  125. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-socket-timeout.d.ts +2 -0
  126. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/stream-collector/collector.d.ts +5 -0
  127. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/stream-collector/index.d.ts +6 -0
  128. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/stream-collector/readable.mock.d.ts +13 -0
  129. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/timing.d.ts +8 -0
  130. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/build-abort-error.d.ts +10 -0
  131. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/constants.d.ts +5 -0
  132. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/get-transformed-headers.d.ts +4 -0
  133. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/index.d.ts +3 -0
  134. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http-handler.d.ts +46 -0
  135. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http2-connection-manager.d.ts +24 -0
  136. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http2-connection-pool.d.ts +12 -0
  137. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http2-handler.d.ts +63 -0
  138. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/readable.mock.d.ts +13 -0
  139. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/server.mock.d.ts +12 -0
  140. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-connection-timeout.d.ts +2 -0
  141. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-request-timeout.d.ts +6 -0
  142. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-socket-keep-alive.d.ts +6 -0
  143. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-socket-timeout.d.ts +2 -0
  144. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/stream-collector/collector.d.ts +5 -0
  145. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/stream-collector/index.d.ts +6 -0
  146. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/stream-collector/readable.mock.d.ts +13 -0
  147. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/timing.d.ts +8 -0
  148. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/write-request-body.d.ts +12 -0
  149. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/write-request-body.d.ts +12 -0
  150. package/packages/pi-ai/node_modules/@smithy/node-http-handler/package.json +68 -0
  151. package/packages/pi-ai/package.json +3 -0
  152. package/packages/pi-ai/pnpm-lock.yaml +2022 -0
  153. package/packages/pi-ai/src/providers/azure-openai-responses.ts +12 -1
  154. package/packages/pi-ai/src/providers/openai-responses.ts +12 -1
  155. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
  156. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
  157. package/packages/pi-coding-agent/dist/core/extensions/index.js +1 -1
  158. package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
  159. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +1 -0
  160. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  161. package/packages/pi-coding-agent/dist/core/extensions/loader.js +12 -1
  162. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  163. package/packages/pi-coding-agent/dist/core/extensions/loader.test.d.ts +2 -0
  164. package/packages/pi-coding-agent/dist/core/extensions/loader.test.d.ts.map +1 -0
  165. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +113 -0
  166. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -0
  167. package/packages/pi-coding-agent/dist/core/extensions/project-trust.d.ts +4 -0
  168. package/packages/pi-coding-agent/dist/core/extensions/project-trust.d.ts.map +1 -0
  169. package/packages/pi-coding-agent/dist/core/extensions/project-trust.js +42 -0
  170. package/packages/pi-coding-agent/dist/core/extensions/project-trust.js.map +1 -0
  171. package/packages/pi-coding-agent/dist/core/resolve-config-value.d.ts +1 -0
  172. package/packages/pi-coding-agent/dist/core/resolve-config-value.d.ts.map +1 -1
  173. package/packages/pi-coding-agent/dist/core/resolve-config-value.js +18 -0
  174. package/packages/pi-coding-agent/dist/core/resolve-config-value.js.map +1 -1
  175. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.d.ts +2 -0
  176. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.d.ts.map +1 -0
  177. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +114 -0
  178. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -0
  179. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  180. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +8 -2
  181. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  182. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts +2 -0
  183. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
  184. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +23 -9
  185. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
  186. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.d.ts +2 -0
  187. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.d.ts.map +1 -0
  188. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +67 -0
  189. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -0
  190. package/packages/pi-coding-agent/package.json +1 -1
  191. package/packages/pi-coding-agent/pnpm-lock.yaml +454 -0
  192. package/packages/pi-coding-agent/scripts/copy-assets.cjs +24 -0
  193. package/packages/pi-coding-agent/src/core/extensions/index.ts +3 -0
  194. package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +141 -0
  195. package/packages/pi-coding-agent/src/core/extensions/loader.ts +14 -1
  196. package/packages/pi-coding-agent/src/core/extensions/project-trust.ts +51 -0
  197. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +132 -0
  198. package/packages/pi-coding-agent/src/core/resolve-config-value.ts +20 -0
  199. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +7 -2
  200. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +98 -0
  201. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +24 -9
  202. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  203. package/packages/pi-tui/dist/tui.js +2 -2
  204. package/packages/pi-tui/dist/tui.js.map +1 -1
  205. package/packages/pi-tui/package.json +3 -1
  206. package/packages/pi-tui/src/tui.ts +2 -2
  207. package/src/resources/agents/javascript-pro.md +280 -0
  208. package/src/resources/agents/typescript-pro.md +255 -0
  209. package/src/resources/extensions/bg-shell/index.ts +14 -2
  210. package/src/resources/extensions/bg-shell/utilities.ts +14 -0
  211. package/src/resources/extensions/browser-tools/{core.js → core.ts} +329 -190
  212. package/src/resources/extensions/gsd/auto-dashboard.ts +2 -1
  213. package/src/resources/extensions/gsd/auto-prompts.ts +42 -1
  214. package/src/resources/extensions/gsd/auto-worktree.ts +35 -2
  215. package/src/resources/extensions/gsd/auto.ts +116 -10
  216. package/src/resources/extensions/gsd/claude-import.ts +656 -0
  217. package/src/resources/extensions/gsd/collision-diagnostics.ts +332 -0
  218. package/src/resources/extensions/gsd/commands.ts +60 -7
  219. package/src/resources/extensions/gsd/diff-context.ts +220 -0
  220. package/src/resources/extensions/gsd/docs/claude-marketplace-import.md +214 -0
  221. package/src/resources/extensions/gsd/docs/preferences-reference.md +2 -1
  222. package/src/resources/extensions/gsd/doctor.ts +12 -4
  223. package/src/resources/extensions/gsd/file-watcher.ts +97 -0
  224. package/src/resources/extensions/gsd/files.ts +1 -1
  225. package/src/resources/extensions/gsd/forensics.ts +596 -0
  226. package/src/resources/extensions/gsd/git-service.ts +2 -1
  227. package/src/resources/extensions/gsd/github-client.ts +235 -0
  228. package/src/resources/extensions/gsd/gitignore.ts +1 -0
  229. package/src/resources/extensions/gsd/marketplace-discovery.ts +507 -0
  230. package/src/resources/extensions/gsd/mcp-server.ts +87 -0
  231. package/src/resources/extensions/gsd/namespaced-registry.ts +467 -0
  232. package/src/resources/extensions/gsd/namespaced-resolver.ts +307 -0
  233. package/src/resources/extensions/gsd/plugin-importer.ts +410 -0
  234. package/src/resources/extensions/gsd/preferences.ts +14 -3
  235. package/src/resources/extensions/gsd/prompts/execute-task.md +1 -1
  236. package/src/resources/extensions/gsd/prompts/forensics.md +71 -0
  237. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  238. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  239. package/src/resources/extensions/gsd/prompts/system.md +12 -3
  240. package/src/resources/extensions/gsd/roadmap-slices.ts +1 -1
  241. package/src/resources/extensions/gsd/session-forensics.ts +5 -5
  242. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +351 -0
  243. package/src/resources/extensions/gsd/tests/collision-diagnostics.test.ts +705 -0
  244. package/src/resources/extensions/gsd/tests/context-compression.test.ts +13 -0
  245. package/src/resources/extensions/gsd/tests/diff-context.test.ts +136 -0
  246. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +118 -0
  247. package/src/resources/extensions/gsd/tests/marketplace-discovery.test.ts +202 -0
  248. package/src/resources/extensions/gsd/tests/marketplace-test-fixtures.ts +91 -0
  249. package/src/resources/extensions/gsd/tests/namespaced-registry.test.ts +1027 -0
  250. package/src/resources/extensions/gsd/tests/namespaced-resolver.test.ts +671 -0
  251. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +105 -0
  252. package/src/resources/extensions/gsd/tests/plugin-importer-live.test.ts +481 -0
  253. package/src/resources/extensions/gsd/tests/plugin-importer.test.ts +1383 -0
  254. package/src/resources/extensions/gsd/tests/preferences-git.test.ts +21 -2
  255. package/src/resources/extensions/gsd/tests/preferences-schema-validation.test.ts +8 -1
  256. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +11 -0
  257. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +120 -0
  258. package/src/resources/extensions/gsd/token-counter.ts +45 -0
  259. package/src/resources/extensions/gsd/visualizer-data.ts +2 -2
  260. package/src/resources/extensions/gsd/worktree-manager.ts +29 -1
  261. package/src/resources/extensions/gsd/worktree.ts +3 -0
  262. package/src/resources/extensions/mcporter/index.ts +90 -7
  263. package/src/resources/extensions/search-the-web/native-search.ts +2 -0
  264. package/src/resources/extensions/search-the-web/tool-fetch-page.ts +9 -1
  265. package/src/resources/extensions/search-the-web/url-utils.ts +35 -1
  266. package/src/resources/extensions/shared/terminal.ts +1 -1
  267. package/src/resources/extensions/universal-config/discovery.ts +4 -0
  268. package/src/resources/extensions/universal-config/format.ts +35 -4
  269. package/src/resources/extensions/universal-config/index.ts +5 -3
  270. package/src/resources/extensions/universal-config/scanners.ts +65 -2
  271. package/src/resources/extensions/universal-config/tests/discovery.test.ts +9 -1
  272. package/src/resources/extensions/universal-config/tests/format.test.ts +22 -6
  273. package/src/resources/extensions/universal-config/tests/scanners.test.ts +18 -0
  274. package/src/resources/extensions/universal-config/types.ts +20 -1
  275. package/src/resources/skills/lint/SKILL.md +141 -0
  276. package/src/resources/skills/review/SKILL.md +214 -0
  277. package/src/resources/skills/test/SKILL.md +201 -0
  278. package/dist/resources/extensions/browser-tools/core.d.ts +0 -205
  279. package/src/resources/extensions/browser-tools/core.d.ts +0 -205
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Namespaced Resolver Module
3
+ *
4
+ * Implements context-aware resolution with three-tier lookup precedence:
5
+ * 1. Canonical (fully-qualified names with `:`)
6
+ * 2. Local-first (caller namespace + bare name)
7
+ * 3. Shorthand (bare name matched across all namespaces)
8
+ *
9
+ * This is the core logic for D003 (same-plugin local-first) and R007/R008 (safe shorthand).
10
+ */
11
+
12
+ import type { NamespacedRegistry, NamespacedComponent, ComponentType } from './namespaced-registry.js';
13
+
14
+ // ============================================================================
15
+ // Type Definitions
16
+ // ============================================================================
17
+
18
+ /**
19
+ * Resolution context provided by the caller.
20
+ * Used to enable local-first resolution within a namespace.
21
+ */
22
+ export interface ResolutionContext {
23
+ /** The namespace of the calling component (e.g., "farm" from "farm:caller") */
24
+ callerNamespace?: string;
25
+ }
26
+
27
+ /**
28
+ * Base structure for all resolution results.
29
+ */
30
+ interface ResolutionResultBase {
31
+ /** The original name passed to resolve() */
32
+ requestedName: string;
33
+
34
+ /** How the resolution was performed */
35
+ resolution: 'canonical' | 'alias' | 'local-first' | 'shorthand' | 'ambiguous' | 'not-found';
36
+ }
37
+
38
+ /**
39
+ * Result when a canonical (fully-qualified) name matches exactly.
40
+ * Example: "farm:call-horse" resolves directly to the component with that canonical name.
41
+ */
42
+ export interface CanonicalResolution extends ResolutionResultBase {
43
+ resolution: 'canonical';
44
+ /** The matched component */
45
+ component: NamespacedComponent;
46
+ }
47
+
48
+ /**
49
+ * Result when an alias resolves to a canonical name.
50
+ * Example: "py3d" resolves via alias to "python-tools:3d-visualizer".
51
+ */
52
+ export interface AliasResolution extends ResolutionResultBase {
53
+ resolution: 'alias';
54
+ /** The matched component */
55
+ component: NamespacedComponent;
56
+ /** The alias that was resolved */
57
+ alias: string;
58
+ /** The canonical name the alias points to */
59
+ canonicalName: string;
60
+ }
61
+
62
+ /**
63
+ * Result when a bare name resolves via local-first lookup.
64
+ * Example: A caller in namespace "farm" resolving bare "call-horse" matches "farm:call-horse".
65
+ */
66
+ export interface LocalFirstResolution extends ResolutionResultBase {
67
+ resolution: 'local-first';
68
+ /** The matched component */
69
+ component: NamespacedComponent;
70
+ /** The namespace used for local-first resolution */
71
+ matchedNamespace: string;
72
+ }
73
+
74
+ /**
75
+ * Result when a bare name matches exactly one component across all namespaces.
76
+ * Example: "feed-chickens" resolves if only "farm:feed-chickens" exists.
77
+ */
78
+ export interface ShorthandResolution extends ResolutionResultBase {
79
+ resolution: 'shorthand';
80
+ /** The matched component */
81
+ component: NamespacedComponent;
82
+ }
83
+
84
+ /**
85
+ * Result when a bare name matches multiple components across namespaces.
86
+ * Returns all candidates for diagnostic consumption without throwing.
87
+ * Example: "call-horse" matches both "farm:call-horse" and "zoo:call-horse".
88
+ */
89
+ export interface AmbiguousResolution extends ResolutionResultBase {
90
+ resolution: 'ambiguous';
91
+ /** All components matching the bare name */
92
+ candidates: NamespacedComponent[];
93
+ }
94
+
95
+ /**
96
+ * Result when no component matches the requested name.
97
+ */
98
+ export interface NotFoundResolution extends ResolutionResultBase {
99
+ resolution: 'not-found';
100
+ }
101
+
102
+ /**
103
+ * Discriminated union of all resolution results.
104
+ * The `resolution` field indicates which variant applies.
105
+ */
106
+ export type ResolutionResult =
107
+ | CanonicalResolution
108
+ | AliasResolution
109
+ | LocalFirstResolution
110
+ | ShorthandResolution
111
+ | AmbiguousResolution
112
+ | NotFoundResolution;
113
+
114
+ // ============================================================================
115
+ // NamespacedResolver Class
116
+ // ============================================================================
117
+
118
+ /**
119
+ * Resolver for namespaced components with context-aware lookup.
120
+ *
121
+ * Implements four-tier resolution precedence:
122
+ * 1. **Canonical**: If name contains `:`, try exact match → return canonical result
123
+ * 2. **Alias**: If name is a registered alias → return alias result
124
+ * 3. **Local-first**: If `context.callerNamespace` exists, try `${callerNamespace}:${name}` → return local-first result
125
+ * 4. **Shorthand**: Scan all components for bare name match → single match returns shorthand, multiple returns ambiguous
126
+ *
127
+ * Usage:
128
+ * ```typescript
129
+ * const registry = new NamespacedRegistry();
130
+ * // ... populate registry ...
131
+ * // ... register aliases ...
132
+ *
133
+ * const resolver = new NamespacedResolver(registry);
134
+ *
135
+ * // Canonical lookup
136
+ * const canon = resolver.resolve('farm:call-horse');
137
+ * // canon.resolution === 'canonical'
138
+ *
139
+ * // Alias resolution
140
+ * const alias = resolver.resolve('py3d');
141
+ * // alias.resolution === 'alias', alias.canonicalName === 'python-tools:3d-visualizer'
142
+ *
143
+ * // Local-first resolution from caller context
144
+ * const local = resolver.resolve('call-horse', { callerNamespace: 'farm' });
145
+ * // local.resolution === 'local-first'
146
+ *
147
+ * // Unambiguous shorthand
148
+ * const short = resolver.resolve('unique-skill');
149
+ * // short.resolution === 'shorthand'
150
+ *
151
+ * // Ambiguous shorthand
152
+ * const amb = resolver.resolve('common-skill');
153
+ * // amb.resolution === 'ambiguous', amb.candidates has all matches
154
+ * ```
155
+ */
156
+ export class NamespacedResolver {
157
+ /** The registry to resolve against */
158
+ private registry: NamespacedRegistry;
159
+
160
+ /**
161
+ * Create a new resolver for the given registry.
162
+ *
163
+ * @param registry - The namespaced registry to resolve against
164
+ */
165
+ constructor(registry: NamespacedRegistry) {
166
+ this.registry = registry;
167
+ }
168
+
169
+ /**
170
+ * Resolve a component name with context-aware lookup.
171
+ *
172
+ * Implements four-tier resolution precedence:
173
+ * 1. **Canonical**: If name contains `:`, try exact match → return canonical result
174
+ * 2. **Alias**: If name is a registered alias → return alias result
175
+ * 3. **Local-first**: If `context.callerNamespace` exists, try `${callerNamespace}:${name}` → return local-first result
176
+ * 4. **Shorthand**: Scan all components for bare name match → single match returns shorthand, multiple returns ambiguous
177
+ *
178
+ * @param name - The name to resolve (canonical or bare)
179
+ * @param context - Optional resolution context with caller namespace
180
+ * @param type - Optional type filter (skill or agent)
181
+ * @returns Resolution result indicating how the match was found
182
+ */
183
+ resolve(
184
+ name: string,
185
+ context?: ResolutionContext,
186
+ type?: ComponentType
187
+ ): ResolutionResult {
188
+ // Tier 1: Canonical lookup (name contains `:`)
189
+ if (name.includes(':')) {
190
+ const component = this.registry.getByCanonical(name);
191
+
192
+ if (component && this.matchesType(component, type)) {
193
+ return {
194
+ requestedName: name,
195
+ resolution: 'canonical',
196
+ component,
197
+ };
198
+ }
199
+
200
+ // Canonical name not found
201
+ return {
202
+ requestedName: name,
203
+ resolution: 'not-found',
204
+ };
205
+ }
206
+
207
+ // Tier 2: Alias lookup (before local-first and shorthand)
208
+ const aliasTarget = this.registry.resolveAlias(name);
209
+ if (aliasTarget) {
210
+ const component = this.registry.getByCanonical(aliasTarget);
211
+ if (component && this.matchesType(component, type)) {
212
+ return {
213
+ requestedName: name,
214
+ resolution: 'alias',
215
+ component,
216
+ alias: name,
217
+ canonicalName: aliasTarget,
218
+ };
219
+ }
220
+ }
221
+
222
+ // Tier 3: Local-first resolution (if caller namespace provided)
223
+ if (context?.callerNamespace) {
224
+ const localCanonical = `${context.callerNamespace}:${name}`;
225
+ const component = this.registry.getByCanonical(localCanonical);
226
+
227
+ if (component && this.matchesType(component, type)) {
228
+ return {
229
+ requestedName: name,
230
+ resolution: 'local-first',
231
+ component,
232
+ matchedNamespace: context.callerNamespace,
233
+ };
234
+ }
235
+ }
236
+
237
+ // Tier 4: Shorthand resolution (scan all components)
238
+ const candidates = this.findBareNameMatches(name, type);
239
+
240
+ if (candidates.length === 0) {
241
+ return {
242
+ requestedName: name,
243
+ resolution: 'not-found',
244
+ };
245
+ }
246
+
247
+ if (candidates.length === 1) {
248
+ return {
249
+ requestedName: name,
250
+ resolution: 'shorthand',
251
+ component: candidates[0],
252
+ };
253
+ }
254
+
255
+ // Multiple matches - ambiguous
256
+ return {
257
+ requestedName: name,
258
+ resolution: 'ambiguous',
259
+ candidates,
260
+ };
261
+ }
262
+
263
+ /**
264
+ * Find all components whose local name (without namespace) matches the given bare name.
265
+ * Optionally filters by component type.
266
+ *
267
+ * @param bareName - The bare name to match
268
+ * @param type - Optional type filter
269
+ * @returns Array of matching components
270
+ */
271
+ private findBareNameMatches(
272
+ bareName: string,
273
+ type?: ComponentType
274
+ ): NamespacedComponent[] {
275
+ const all = this.registry.getAll();
276
+
277
+ return all.filter((component) => {
278
+ // Match by local name (component.name)
279
+ if (component.name !== bareName) {
280
+ return false;
281
+ }
282
+
283
+ // Apply type filter if provided
284
+ return this.matchesType(component, type);
285
+ });
286
+ }
287
+
288
+ /**
289
+ * Check if a component matches the optional type filter.
290
+ *
291
+ * @param component - The component to check
292
+ * @param type - Optional type filter
293
+ * @returns true if no filter or type matches
294
+ */
295
+ private matchesType(
296
+ component: NamespacedComponent,
297
+ type?: ComponentType
298
+ ): boolean {
299
+ return type === undefined || component.type === type;
300
+ }
301
+ }
302
+
303
+ // ============================================================================
304
+ // Exports
305
+ // ============================================================================
306
+
307
+ export default NamespacedResolver;
@@ -0,0 +1,410 @@
1
+ /**
2
+ * PluginImporter Service
3
+ *
4
+ * Composes S01-S04 modules into a staged discover → select → validate → commit pipeline.
5
+ * Each stage is independently testable. The service owns no UI — it produces data structures
6
+ * that the command layer (T02) consumes.
7
+ *
8
+ * Pipeline stages:
9
+ * 1. discover(marketplacePaths) - Read marketplace manifests, populate registry
10
+ * 2. selectComponents(filter) - Filter to user-chosen components
11
+ * 3. validateImport(selected) - Check for collisions, return diagnostics
12
+ * 4. getImportManifest(selected) - Produce serializable config structure
13
+ *
14
+ * This service implements R012 (discover/select/import flow) and R013 (canonical name preservation).
15
+ */
16
+
17
+ import {
18
+ discoverMarketplace,
19
+ type MarketplaceDiscoveryResult,
20
+ type DiscoveredPlugin,
21
+ } from './marketplace-discovery.js';
22
+ import {
23
+ NamespacedRegistry,
24
+ componentsFromDiscovery,
25
+ type NamespacedComponent,
26
+ } from './namespaced-registry.js';
27
+ import { NamespacedResolver } from './namespaced-resolver.js';
28
+ import {
29
+ analyzeCollisions,
30
+ type ClassifiedDiagnostic,
31
+ } from './collision-diagnostics.js';
32
+
33
+ // ============================================================================
34
+ // Type Definitions
35
+ // ============================================================================
36
+
37
+ /**
38
+ * Result of the discovery stage.
39
+ * Contains all discovered plugins and the populated registry.
40
+ */
41
+ export interface DiscoveryResult {
42
+ /** All discovery results from each marketplace path */
43
+ marketplaceResults: MarketplaceDiscoveryResult[];
44
+
45
+ /** All discovered plugins aggregated */
46
+ plugins: DiscoveredPlugin[];
47
+
48
+ /** The populated registry with all components */
49
+ registry: NamespacedRegistry;
50
+
51
+ /** Summary counts */
52
+ summary: {
53
+ marketplacesProcessed: number;
54
+ marketplacesWithErrors: number;
55
+ totalPlugins: number;
56
+ pluginsWithErrors: number;
57
+ totalComponents: number;
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Result of the validation stage.
63
+ * Contains diagnostics and a proceed flag.
64
+ */
65
+ export interface ValidationResult {
66
+ /** All classified diagnostics (errors and warnings) */
67
+ diagnostics: ClassifiedDiagnostic[];
68
+
69
+ /** True if import can proceed (no error-severity diagnostics) */
70
+ canProceed: boolean;
71
+
72
+ /** Summary counts */
73
+ summary: {
74
+ total: number;
75
+ errors: number;
76
+ warnings: number;
77
+ };
78
+ }
79
+
80
+ /**
81
+ * A single entry in the import manifest config.
82
+ * Represents one component to be imported.
83
+ */
84
+ export interface ImportManifestEntry {
85
+ /** Canonical name: `namespace:name` or bare `name` */
86
+ canonicalName: string;
87
+
88
+ /** Component type: 'skill' or 'agent' */
89
+ type: 'skill' | 'agent';
90
+
91
+ /** Local component name (without namespace) */
92
+ name: string;
93
+
94
+ /** Plugin namespace (undefined for flat components) */
95
+ namespace: string | undefined;
96
+
97
+ /** Absolute path to the component's definition file */
98
+ filePath: string;
99
+
100
+ /** Source identifier (e.g., "plugin:my-plugin") */
101
+ source: string;
102
+
103
+ /** Optional description */
104
+ description?: string;
105
+
106
+ /** Plugin metadata for provenance */
107
+ metadata: {
108
+ pluginVersion?: string;
109
+ pluginAuthor?: string;
110
+ pluginHomepage?: string;
111
+ pluginCategory?: string;
112
+ };
113
+ }
114
+
115
+ /**
116
+ * The complete import manifest structure.
117
+ * Serializable to JSON for persistence.
118
+ */
119
+ export interface ImportManifest {
120
+ /** Schema version for future compatibility */
121
+ schemaVersion: '1.0';
122
+
123
+ /** Timestamp when manifest was generated */
124
+ generatedAt: string;
125
+
126
+ /** All entries to be imported */
127
+ entries: ImportManifestEntry[];
128
+
129
+ /** Summary counts */
130
+ summary: {
131
+ total: number;
132
+ skills: number;
133
+ agents: number;
134
+ namespaces: string[];
135
+ };
136
+ }
137
+
138
+ // ============================================================================
139
+ // PluginImporter Class
140
+ // ============================================================================
141
+
142
+ /**
143
+ * Service for discovering, selecting, validating, and importing plugin components.
144
+ *
145
+ * Usage:
146
+ * ```typescript
147
+ * const importer = new PluginImporter();
148
+ *
149
+ * // Stage 1: Discover
150
+ * const discovery = importer.discover(['../claude-plugins']);
151
+ *
152
+ * // Stage 2: Select
153
+ * const selected = importer.selectComponents(c => c.namespace === 'my-plugin');
154
+ *
155
+ * // Stage 3: Validate
156
+ * const validation = importer.validateImport(selected);
157
+ * if (!validation.canProceed) {
158
+ * console.error('Cannot import:', validation.diagnostics);
159
+ * return;
160
+ * }
161
+ *
162
+ * // Stage 4: Get manifest for persistence
163
+ * const manifest = importer.getImportManifest(selected);
164
+ * ```
165
+ */
166
+ export class PluginImporter {
167
+ /** The internal registry populated during discovery */
168
+ private registry: NamespacedRegistry | null = null;
169
+
170
+ /** All discovered plugins from the last discovery run */
171
+ private discoveredPlugins: DiscoveredPlugin[] = [];
172
+
173
+ /** Last discovery result for inspection */
174
+ private lastDiscoveryResult: DiscoveryResult | null = null;
175
+
176
+ /** Last validation result for inspection */
177
+ private lastValidationResult: ValidationResult | null = null;
178
+
179
+ /**
180
+ * Stage 1: Discover plugins from marketplace paths.
181
+ *
182
+ * Calls `discoverMarketplace()` for each path and populates a `NamespacedRegistry`
183
+ * via `componentsFromDiscovery()`.
184
+ *
185
+ * @param marketplacePaths - Array of paths to marketplace directories
186
+ * @returns Discovery result with registry and summary
187
+ */
188
+ discover(marketplacePaths: string[]): DiscoveryResult {
189
+ // Reset state for fresh discovery
190
+ this.registry = new NamespacedRegistry();
191
+ this.discoveredPlugins = [];
192
+ this.lastValidationResult = null;
193
+
194
+ const marketplaceResults: MarketplaceDiscoveryResult[] = [];
195
+ let marketplacesWithErrors = 0;
196
+ let pluginsWithErrors = 0;
197
+
198
+ // Process each marketplace path
199
+ for (const marketplacePath of marketplacePaths) {
200
+ const result = discoverMarketplace(marketplacePath);
201
+ marketplaceResults.push(result);
202
+
203
+ if (result.status === 'error') {
204
+ marketplacesWithErrors++;
205
+ }
206
+
207
+ // Collect all plugins
208
+ for (const plugin of result.plugins) {
209
+ this.discoveredPlugins.push(plugin);
210
+
211
+ if (plugin.status === 'error') {
212
+ pluginsWithErrors++;
213
+ }
214
+
215
+ // Convert plugin inventory to components and register
216
+ const components = componentsFromDiscovery(plugin);
217
+ for (const component of components) {
218
+ this.registry!.register(component);
219
+ }
220
+ }
221
+ }
222
+
223
+ // Build summary
224
+ const summary = {
225
+ marketplacesProcessed: marketplacePaths.length,
226
+ marketplacesWithErrors,
227
+ totalPlugins: this.discoveredPlugins.length,
228
+ pluginsWithErrors,
229
+ totalComponents: this.registry.size,
230
+ };
231
+
232
+ this.lastDiscoveryResult = {
233
+ marketplaceResults,
234
+ plugins: this.discoveredPlugins,
235
+ registry: this.registry,
236
+ summary,
237
+ };
238
+
239
+ return this.lastDiscoveryResult;
240
+ }
241
+
242
+ /**
243
+ * Stage 2: Select components by filter function.
244
+ *
245
+ * Returns a filtered subset of registered components.
246
+ * Must be called after discover().
247
+ *
248
+ * @param componentFilter - Filter function returning true for selected components
249
+ * @returns Array of selected components
250
+ */
251
+ selectComponents(
252
+ componentFilter: (component: NamespacedComponent) => boolean
253
+ ): NamespacedComponent[] {
254
+ if (!this.registry) {
255
+ throw new Error('Must call discover() before selectComponents()');
256
+ }
257
+
258
+ return this.registry.getAll().filter(componentFilter);
259
+ }
260
+
261
+ /**
262
+ * Stage 3: Validate selected components for import.
263
+ *
264
+ * Builds a `NamespacedResolver`, runs `analyzeCollisions()`, and returns
265
+ * `{ diagnostics, canProceed }` where `canProceed` is false if any
266
+ * error-severity diagnostics exist.
267
+ *
268
+ * @param selected - Array of components to validate
269
+ * @returns Validation result with diagnostics and proceed flag
270
+ */
271
+ validateImport(selected: NamespacedComponent[]): ValidationResult {
272
+ if (!this.registry) {
273
+ throw new Error('Must call discover() before validateImport()');
274
+ }
275
+
276
+ // Create a temporary resolver for the selected components
277
+ const tempRegistry = new NamespacedRegistry();
278
+
279
+ // Register only selected components into temp registry
280
+ for (const component of selected) {
281
+ tempRegistry.register({
282
+ name: component.name,
283
+ namespace: component.namespace,
284
+ type: component.type,
285
+ filePath: component.filePath,
286
+ source: component.source,
287
+ description: component.description,
288
+ metadata: component.metadata,
289
+ });
290
+ }
291
+
292
+ // Create resolver and analyze collisions
293
+ const resolver = new NamespacedResolver(tempRegistry);
294
+ const diagnostics = analyzeCollisions(tempRegistry, resolver);
295
+
296
+ // Count by severity
297
+ const errors = diagnostics.filter((d) => d.severity === 'error').length;
298
+ const warnings = diagnostics.filter((d) => d.severity === 'warning').length;
299
+
300
+ const summary = {
301
+ total: diagnostics.length,
302
+ errors,
303
+ warnings,
304
+ };
305
+
306
+ // canProceed is false if any error-severity diagnostics exist
307
+ const canProceed = errors === 0;
308
+
309
+ this.lastValidationResult = {
310
+ diagnostics,
311
+ canProceed,
312
+ summary,
313
+ };
314
+
315
+ return this.lastValidationResult;
316
+ }
317
+
318
+ /**
319
+ * Stage 4: Generate import manifest for selected components.
320
+ *
321
+ * Produces a serializable config structure with canonical names preserved.
322
+ * The manifest can be persisted to config files.
323
+ *
324
+ * @param selected - Array of components to include in manifest
325
+ * @returns Import manifest with all entries and metadata
326
+ */
327
+ getImportManifest(selected: NamespacedComponent[]): ImportManifest {
328
+ const entries: ImportManifestEntry[] = selected.map((component) => ({
329
+ canonicalName: component.canonicalName,
330
+ type: component.type,
331
+ name: component.name,
332
+ namespace: component.namespace,
333
+ filePath: component.filePath,
334
+ source: component.source,
335
+ description: component.description,
336
+ metadata: {
337
+ pluginVersion: component.metadata.pluginVersion,
338
+ pluginAuthor: component.metadata.pluginAuthor,
339
+ pluginHomepage: component.metadata.pluginHomepage,
340
+ pluginCategory: component.metadata.pluginCategory,
341
+ },
342
+ }));
343
+
344
+ // Count by type
345
+ const skills = entries.filter((e) => e.type === 'skill').length;
346
+ const agents = entries.filter((e) => e.type === 'agent').length;
347
+
348
+ // Collect unique namespaces
349
+ const namespaces = Array.from(
350
+ new Set(entries.map((e) => e.namespace).filter((n): n is string => n !== undefined))
351
+ ).sort();
352
+
353
+ return {
354
+ schemaVersion: '1.0',
355
+ generatedAt: new Date().toISOString(),
356
+ entries,
357
+ summary: {
358
+ total: entries.length,
359
+ skills,
360
+ agents,
361
+ namespaces,
362
+ },
363
+ };
364
+ }
365
+
366
+ /**
367
+ * Get the internal registry for inspection.
368
+ * Useful for debugging or advanced filtering.
369
+ *
370
+ * @returns The registry or null if discover() hasn't been called
371
+ */
372
+ getRegistry(): NamespacedRegistry | null {
373
+ return this.registry;
374
+ }
375
+
376
+ /**
377
+ * Get all discovered plugins.
378
+ *
379
+ * @returns Array of discovered plugins
380
+ */
381
+ getDiscoveredPlugins(): DiscoveredPlugin[] {
382
+ return this.discoveredPlugins;
383
+ }
384
+
385
+ /**
386
+ * Get the last validation result.
387
+ * Useful for re-inspecting validation without re-running.
388
+ *
389
+ * @returns Last validation result or null
390
+ */
391
+ getLastValidation(): ValidationResult | null {
392
+ return this.lastValidationResult;
393
+ }
394
+
395
+ /**
396
+ * Get the last discovery result.
397
+ * Useful for re-inspecting discovery without re-running.
398
+ *
399
+ * @returns Last discovery result or null
400
+ */
401
+ getLastDiscovery(): DiscoveryResult | null {
402
+ return this.lastDiscoveryResult;
403
+ }
404
+ }
405
+
406
+ // ============================================================================
407
+ // Exports
408
+ // ============================================================================
409
+
410
+ export default PluginImporter;