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,705 @@
1
+ /**
2
+ * Collision Diagnostics Contract Tests
3
+ *
4
+ * Tests that prove:
5
+ * - R010: Collision reporting distinguishes canonical-conflict from shorthand-overlap
6
+ * - R011: Doctor provides actionable advice with canonical name suggestions
7
+ */
8
+
9
+ import { describe, it, beforeEach } from 'node:test';
10
+ import assert from 'node:assert';
11
+ import { NamespacedRegistry } from '../namespaced-registry.js';
12
+ import { NamespacedResolver } from '../namespaced-resolver.js';
13
+ import {
14
+ analyzeCollisions,
15
+ doctorReport,
16
+ type ClassifiedDiagnostic,
17
+ type DoctorReport,
18
+ } from '../collision-diagnostics.js';
19
+
20
+ describe('collision-diagnostics', () => {
21
+ let registry: NamespacedRegistry;
22
+ let resolver: NamespacedResolver;
23
+
24
+ beforeEach(() => {
25
+ registry = new NamespacedRegistry();
26
+ resolver = new NamespacedResolver(registry);
27
+ });
28
+
29
+ describe('analyzeCollisions', () => {
30
+ describe('canonical-conflict detection', () => {
31
+ it('should detect canonical conflict when same canonical name registered twice', () => {
32
+ // First registration wins
33
+ registry.register({
34
+ name: 'code-review',
35
+ namespace: 'my-plugin',
36
+ type: 'skill',
37
+ filePath: '/plugins/my-plugin/skills/code-review/SKILL.md',
38
+ source: 'plugin:my-plugin',
39
+ description: 'Reviews code',
40
+ metadata: {},
41
+ });
42
+
43
+ // Second registration with same canonical name loses
44
+ registry.register({
45
+ name: 'code-review',
46
+ namespace: 'my-plugin',
47
+ type: 'skill',
48
+ filePath: '/plugins/other/skills/code-review/SKILL.md',
49
+ source: 'plugin:other',
50
+ description: 'Another code review',
51
+ metadata: {},
52
+ });
53
+
54
+ const diagnostics = analyzeCollisions(registry, resolver);
55
+
56
+ assert.strictEqual(diagnostics.length, 1);
57
+ assert.strictEqual(diagnostics[0].class, 'canonical-conflict');
58
+ assert.strictEqual(diagnostics[0].severity, 'error');
59
+ assert.strictEqual(diagnostics[0].involvedCanonicalNames[0], 'my-plugin:code-review');
60
+ assert.ok(diagnostics[0].filePaths.includes('/plugins/my-plugin/skills/code-review/SKILL.md'));
61
+ assert.ok(diagnostics[0].filePaths.includes('/plugins/other/skills/code-review/SKILL.md'));
62
+ });
63
+
64
+ it('should include remediation advice for canonical conflict', () => {
65
+ registry.register({
66
+ name: 'test-skill',
67
+ namespace: 'plugin-a',
68
+ type: 'skill',
69
+ filePath: '/a/test-skill/SKILL.md',
70
+ source: 'plugin:plugin-a',
71
+ description: 'Test',
72
+ metadata: {},
73
+ });
74
+ registry.register({
75
+ name: 'test-skill',
76
+ namespace: 'plugin-a',
77
+ type: 'skill',
78
+ filePath: '/b/test-skill/SKILL.md',
79
+ source: 'plugin:plugin-b',
80
+ description: 'Test duplicate',
81
+ metadata: {},
82
+ });
83
+
84
+ const diagnostics = analyzeCollisions(registry, resolver);
85
+
86
+ assert.ok(diagnostics[0].remediation.includes('Rename one of the conflicting components'));
87
+ });
88
+ });
89
+
90
+ describe('shorthand-overlap detection', () => {
91
+ it('should detect shorthand overlap when bare name matches multiple namespaces', () => {
92
+ // Same bare name in different namespaces
93
+ registry.register({
94
+ name: 'common-skill',
95
+ namespace: 'plugin-a',
96
+ type: 'skill',
97
+ filePath: '/a/common-skill/SKILL.md',
98
+ source: 'plugin:plugin-a',
99
+ description: 'A common skill',
100
+ metadata: {},
101
+ });
102
+ registry.register({
103
+ name: 'common-skill',
104
+ namespace: 'plugin-b',
105
+ type: 'skill',
106
+ filePath: '/b/common-skill/SKILL.md',
107
+ source: 'plugin:plugin-b',
108
+ description: 'B common skill',
109
+ metadata: {},
110
+ });
111
+
112
+ const diagnostics = analyzeCollisions(registry, resolver);
113
+
114
+ assert.strictEqual(diagnostics.length, 1);
115
+ assert.strictEqual(diagnostics[0].class, 'shorthand-overlap');
116
+ assert.strictEqual(diagnostics[0].severity, 'warning');
117
+ assert.strictEqual(diagnostics[0].ambiguousBareName, 'common-skill');
118
+ assert.ok(diagnostics[0].involvedCanonicalNames.includes('plugin-a:common-skill'));
119
+ assert.ok(diagnostics[0].involvedCanonicalNames.includes('plugin-b:common-skill'));
120
+ });
121
+
122
+ it('should NOT warn when only one component has a given bare name', () => {
123
+ registry.register({
124
+ name: 'unique-skill',
125
+ namespace: 'plugin-a',
126
+ type: 'skill',
127
+ filePath: '/a/unique-skill/SKILL.md',
128
+ source: 'plugin:plugin-a',
129
+ description: 'Unique',
130
+ metadata: {},
131
+ });
132
+ registry.register({
133
+ name: 'other-skill',
134
+ namespace: 'plugin-b',
135
+ type: 'skill',
136
+ filePath: '/b/other-skill/SKILL.md',
137
+ source: 'plugin:plugin-b',
138
+ description: 'Other',
139
+ metadata: {},
140
+ });
141
+
142
+ const diagnostics = analyzeCollisions(registry, resolver);
143
+
144
+ assert.strictEqual(diagnostics.length, 0);
145
+ });
146
+
147
+ it('should include canonical name suggestions in remediation for shorthand overlap', () => {
148
+ registry.register({
149
+ name: 'ambiguous',
150
+ namespace: 'alpha',
151
+ type: 'skill',
152
+ filePath: '/alpha/ambiguous/SKILL.md',
153
+ source: 'plugin:alpha',
154
+ description: 'Alpha ambiguous',
155
+ metadata: {},
156
+ });
157
+ registry.register({
158
+ name: 'ambiguous',
159
+ namespace: 'beta',
160
+ type: 'skill',
161
+ filePath: '/beta/ambiguous/SKILL.md',
162
+ source: 'plugin:beta',
163
+ description: 'Beta ambiguous',
164
+ metadata: {},
165
+ });
166
+
167
+ const diagnostics = analyzeCollisions(registry, resolver);
168
+
169
+ assert.ok(diagnostics[0].remediation.includes('`alpha:ambiguous`'));
170
+ assert.ok(diagnostics[0].remediation.includes('`beta:ambiguous`'));
171
+ assert.ok(diagnostics[0].remediation.includes('Use a canonical name'));
172
+ });
173
+ });
174
+
175
+ describe('clean registry', () => {
176
+ it('should return no diagnostics for empty registry', () => {
177
+ const diagnostics = analyzeCollisions(registry, resolver);
178
+ assert.strictEqual(diagnostics.length, 0);
179
+ });
180
+
181
+ it('should return no diagnostics for registry with unique bare names', () => {
182
+ registry.register({
183
+ name: 'skill-a',
184
+ namespace: 'plugin-x',
185
+ type: 'skill',
186
+ filePath: '/x/skill-a/SKILL.md',
187
+ source: 'plugin:plugin-x',
188
+ description: 'Skill A',
189
+ metadata: {},
190
+ });
191
+ registry.register({
192
+ name: 'skill-b',
193
+ namespace: 'plugin-y',
194
+ type: 'skill',
195
+ filePath: '/y/skill-b/SKILL.md',
196
+ source: 'plugin:plugin-y',
197
+ description: 'Skill B',
198
+ metadata: {},
199
+ });
200
+
201
+ const diagnostics = analyzeCollisions(registry, resolver);
202
+ assert.strictEqual(diagnostics.length, 0);
203
+ });
204
+ });
205
+
206
+ describe('mixed scenarios', () => {
207
+ it('should report both canonical conflict and shorthand overlap in mixed scenario', () => {
208
+ // Canonical conflict: same canonical name twice
209
+ registry.register({
210
+ name: 'duplicate',
211
+ namespace: 'shared',
212
+ type: 'skill',
213
+ filePath: '/first/duplicate/SKILL.md',
214
+ source: 'plugin:first',
215
+ description: 'First duplicate',
216
+ metadata: {},
217
+ });
218
+ registry.register({
219
+ name: 'duplicate',
220
+ namespace: 'shared',
221
+ type: 'skill',
222
+ filePath: '/second/duplicate/SKILL.md',
223
+ source: 'plugin:second',
224
+ description: 'Second duplicate',
225
+ metadata: {},
226
+ });
227
+
228
+ // Shorthand overlap: same bare name in different namespaces
229
+ registry.register({
230
+ name: 'overlap',
231
+ namespace: 'ns-a',
232
+ type: 'skill',
233
+ filePath: '/a/overlap/SKILL.md',
234
+ source: 'plugin:ns-a',
235
+ description: 'A overlap',
236
+ metadata: {},
237
+ });
238
+ registry.register({
239
+ name: 'overlap',
240
+ namespace: 'ns-b',
241
+ type: 'skill',
242
+ filePath: '/b/overlap/SKILL.md',
243
+ source: 'plugin:ns-b',
244
+ description: 'B overlap',
245
+ metadata: {},
246
+ });
247
+
248
+ const diagnostics = analyzeCollisions(registry, resolver);
249
+
250
+ assert.strictEqual(diagnostics.length, 2);
251
+
252
+ const canonicalConflict = diagnostics.find(d => d.class === 'canonical-conflict');
253
+ const shorthandOverlap = diagnostics.find(d => d.class === 'shorthand-overlap');
254
+
255
+ assert.ok(canonicalConflict, 'Should have canonical conflict');
256
+ assert.ok(shorthandOverlap, 'Should have shorthand overlap');
257
+
258
+ assert.strictEqual(canonicalConflict!.severity, 'error');
259
+ assert.strictEqual(shorthandOverlap!.severity, 'warning');
260
+ });
261
+ });
262
+
263
+ describe('alias-conflict detection', () => {
264
+ it('should detect alias that shadows an existing canonical name', () => {
265
+ // Register component that will be aliased to
266
+ registry.register({
267
+ name: 'utility',
268
+ namespace: 'core',
269
+ type: 'skill',
270
+ filePath: '/core/utility/SKILL.md',
271
+ source: 'plugin:core',
272
+ description: 'Utility skill',
273
+ metadata: {},
274
+ });
275
+
276
+ // Register alias for a non-existent canonical name (will succeed)
277
+ registry.registerAlias('tools:helper', 'core:utility');
278
+
279
+ // Now register the component that creates the conflict
280
+ registry.register({
281
+ name: 'helper',
282
+ namespace: 'tools',
283
+ type: 'skill',
284
+ filePath: '/tools/helper/SKILL.md',
285
+ source: 'plugin:tools',
286
+ description: 'Helper skill',
287
+ metadata: {},
288
+ });
289
+
290
+ const diagnostics = analyzeCollisions(registry, resolver);
291
+
292
+ const aliasConflict = diagnostics.find(d => d.class === 'alias-conflict');
293
+ assert.ok(aliasConflict, 'Should detect alias-conflict');
294
+ assert.strictEqual(aliasConflict!.alias, 'tools:helper');
295
+ assert.strictEqual(aliasConflict!.aliasTarget, 'core:utility');
296
+ assert.strictEqual(aliasConflict!.aliasConflictType, 'shadows-canonical');
297
+ assert.strictEqual(aliasConflict!.severity, 'warning');
298
+ });
299
+
300
+ it('should detect alias that shadows a bare component name', () => {
301
+ // Register component with bare name "helper"
302
+ registry.register({
303
+ name: 'helper',
304
+ namespace: 'tools',
305
+ type: 'skill',
306
+ filePath: '/tools/helper/SKILL.md',
307
+ source: 'plugin:tools',
308
+ description: 'Helper skill',
309
+ metadata: {},
310
+ });
311
+
312
+ // Register another component to alias to
313
+ registry.register({
314
+ name: 'utility',
315
+ namespace: 'core',
316
+ type: 'skill',
317
+ filePath: '/core/utility/SKILL.md',
318
+ source: 'plugin:core',
319
+ description: 'Utility skill',
320
+ metadata: {},
321
+ });
322
+
323
+ // Create alias "helper" that shadows the bare name
324
+ registry.registerAlias('helper', 'core:utility');
325
+
326
+ const diagnostics = analyzeCollisions(registry, resolver);
327
+
328
+ const aliasConflict = diagnostics.find(d => d.class === 'alias-conflict');
329
+ assert.ok(aliasConflict, 'Should detect alias-conflict');
330
+ assert.strictEqual(aliasConflict!.alias, 'helper');
331
+ assert.strictEqual(aliasConflict!.aliasTarget, 'core:utility');
332
+ assert.strictEqual(aliasConflict!.aliasConflictType, 'shadows-bare-name');
333
+ assert.strictEqual(aliasConflict!.severity, 'warning');
334
+ });
335
+
336
+ it('should NOT warn when alias does not conflict', () => {
337
+ registry.register({
338
+ name: 'unique-skill',
339
+ namespace: 'plugin-a',
340
+ type: 'skill',
341
+ filePath: '/a/unique-skill/SKILL.md',
342
+ source: 'plugin:plugin-a',
343
+ description: 'Unique skill',
344
+ metadata: {},
345
+ });
346
+
347
+ registry.register({
348
+ name: 'other-skill',
349
+ namespace: 'plugin-b',
350
+ type: 'skill',
351
+ filePath: '/b/other-skill/SKILL.md',
352
+ source: 'plugin:plugin-b',
353
+ description: 'Other skill',
354
+ metadata: {},
355
+ });
356
+
357
+ // Create a non-conflicting alias
358
+ registry.registerAlias('short', 'plugin-a:unique-skill');
359
+
360
+ const diagnostics = analyzeCollisions(registry, resolver);
361
+
362
+ const aliasConflict = diagnostics.find(d => d.class === 'alias-conflict');
363
+ assert.strictEqual(aliasConflict, undefined, 'Should not have alias-conflict for clean alias');
364
+ });
365
+
366
+ it('should include remediation advice for alias shadowing canonical', () => {
367
+ // Register the target component first
368
+ registry.register({
369
+ name: 'target',
370
+ namespace: 'my-plugin',
371
+ type: 'skill',
372
+ filePath: '/my-plugin/target/SKILL.md',
373
+ source: 'plugin:my-plugin',
374
+ description: 'Target skill',
375
+ metadata: {},
376
+ });
377
+
378
+ // Register alias for a non-existent canonical name (will succeed because it doesn't exist yet)
379
+ registry.registerAlias('other:conflicting', 'my-plugin:target');
380
+
381
+ // Now register the component that the alias would shadow
382
+ registry.register({
383
+ name: 'conflicting',
384
+ namespace: 'other',
385
+ type: 'skill',
386
+ filePath: '/other/conflicting/SKILL.md',
387
+ source: 'plugin:other',
388
+ description: 'Conflicting skill',
389
+ metadata: {},
390
+ });
391
+
392
+ const diagnostics = analyzeCollisions(registry, resolver);
393
+
394
+ const aliasConflict = diagnostics.find(d => d.class === 'alias-conflict');
395
+ assert.ok(aliasConflict, 'Should have alias conflict');
396
+ assert.ok(aliasConflict!.remediation.includes('shadows an existing canonical name'));
397
+ assert.ok(aliasConflict!.remediation.includes('rename or remove the alias'));
398
+ });
399
+
400
+ it('should distinguish alias conflicts from shorthand overlap', () => {
401
+ // Shorthand overlap scenario
402
+ registry.register({
403
+ name: 'common',
404
+ namespace: 'plugin-a',
405
+ type: 'skill',
406
+ filePath: '/a/common/SKILL.md',
407
+ source: 'plugin:plugin-a',
408
+ description: 'Common A',
409
+ metadata: {},
410
+ });
411
+ registry.register({
412
+ name: 'common',
413
+ namespace: 'plugin-b',
414
+ type: 'skill',
415
+ filePath: '/b/common/SKILL.md',
416
+ source: 'plugin:plugin-b',
417
+ description: 'Common B',
418
+ metadata: {},
419
+ });
420
+
421
+ // Alias conflict scenario (separate from shorthand)
422
+ registry.register({
423
+ name: 'unique',
424
+ namespace: 'plugin-c',
425
+ type: 'skill',
426
+ filePath: '/c/unique/SKILL.md',
427
+ source: 'plugin:plugin-c',
428
+ description: 'Unique C',
429
+ metadata: {},
430
+ });
431
+ registry.registerAlias('unique', 'plugin-c:unique');
432
+
433
+ const diagnostics = analyzeCollisions(registry, resolver);
434
+
435
+ const shorthandOverlap = diagnostics.find(d => d.class === 'shorthand-overlap');
436
+ const aliasConflict = diagnostics.find(d => d.class === 'alias-conflict');
437
+
438
+ assert.ok(shorthandOverlap, 'Should have shorthand overlap');
439
+ assert.ok(aliasConflict, 'Should have alias conflict');
440
+ assert.strictEqual(shorthandOverlap!.ambiguousBareName, 'common');
441
+ assert.strictEqual(aliasConflict!.alias, 'unique');
442
+ });
443
+ });
444
+ });
445
+
446
+ describe('doctorReport', () => {
447
+ it('should format report with correct summary counts', () => {
448
+ // Create scenario with 1 error and 2 warnings
449
+ registry.register({
450
+ name: 'conflict',
451
+ namespace: 'ns',
452
+ type: 'skill',
453
+ filePath: '/a/conflict/SKILL.md',
454
+ source: 'plugin:a',
455
+ description: 'A',
456
+ metadata: {},
457
+ });
458
+ registry.register({
459
+ name: 'conflict',
460
+ namespace: 'ns',
461
+ type: 'skill',
462
+ filePath: '/b/conflict/SKILL.md',
463
+ source: 'plugin:b',
464
+ description: 'B',
465
+ metadata: {},
466
+ });
467
+ registry.register({
468
+ name: 'overlap',
469
+ namespace: 'x',
470
+ type: 'skill',
471
+ filePath: '/x/overlap/SKILL.md',
472
+ source: 'plugin:x',
473
+ description: 'X',
474
+ metadata: {},
475
+ });
476
+ registry.register({
477
+ name: 'overlap',
478
+ namespace: 'y',
479
+ type: 'skill',
480
+ filePath: '/y/overlap/SKILL.md',
481
+ source: 'plugin:y',
482
+ description: 'Y',
483
+ metadata: {},
484
+ });
485
+
486
+ const diagnostics = analyzeCollisions(registry, resolver);
487
+ const report = doctorReport(diagnostics);
488
+
489
+ assert.strictEqual(report.summary.total, 2);
490
+ assert.strictEqual(report.summary.canonicalConflicts, 1);
491
+ assert.strictEqual(report.summary.shorthandOverlaps, 1);
492
+ assert.strictEqual(report.entries.length, 2);
493
+ });
494
+
495
+ it('should include error icon for canonical conflicts', () => {
496
+ registry.register({
497
+ name: 'dup',
498
+ namespace: 'ns',
499
+ type: 'skill',
500
+ filePath: '/a/dup/SKILL.md',
501
+ source: 'plugin:a',
502
+ description: 'A',
503
+ metadata: {},
504
+ });
505
+ registry.register({
506
+ name: 'dup',
507
+ namespace: 'ns',
508
+ type: 'skill',
509
+ filePath: '/b/dup/SKILL.md',
510
+ source: 'plugin:b',
511
+ description: 'B',
512
+ metadata: {},
513
+ });
514
+
515
+ const diagnostics = analyzeCollisions(registry, resolver);
516
+ const report = doctorReport(diagnostics);
517
+
518
+ assert.ok(report.entries[0].includes('❌'));
519
+ });
520
+
521
+ it('should include warning icon for shorthand overlaps', () => {
522
+ registry.register({
523
+ name: 'overlap',
524
+ namespace: 'a',
525
+ type: 'skill',
526
+ filePath: '/a/overlap/SKILL.md',
527
+ source: 'plugin:a',
528
+ description: 'A',
529
+ metadata: {},
530
+ });
531
+ registry.register({
532
+ name: 'overlap',
533
+ namespace: 'b',
534
+ type: 'skill',
535
+ filePath: '/b/overlap/SKILL.md',
536
+ source: 'plugin:b',
537
+ description: 'B',
538
+ metadata: {},
539
+ });
540
+
541
+ const diagnostics = analyzeCollisions(registry, resolver);
542
+ const report = doctorReport(diagnostics);
543
+
544
+ assert.ok(report.entries[0].includes('⚠️'));
545
+ });
546
+
547
+ it('should include file paths in formatted output', () => {
548
+ registry.register({
549
+ name: 'overlap',
550
+ namespace: 'a',
551
+ type: 'skill',
552
+ filePath: '/path/a/overlap/SKILL.md',
553
+ source: 'plugin:a',
554
+ description: 'A',
555
+ metadata: {},
556
+ });
557
+ registry.register({
558
+ name: 'overlap',
559
+ namespace: 'b',
560
+ type: 'skill',
561
+ filePath: '/path/b/overlap/SKILL.md',
562
+ source: 'plugin:b',
563
+ description: 'B',
564
+ metadata: {},
565
+ });
566
+
567
+ const diagnostics = analyzeCollisions(registry, resolver);
568
+ const report = doctorReport(diagnostics);
569
+
570
+ assert.ok(report.entries[0].includes('/path/a/overlap/SKILL.md'));
571
+ assert.ok(report.entries[0].includes('/path/b/overlap/SKILL.md'));
572
+ });
573
+
574
+ it('should include canonical name suggestions for ambiguous shorthand', () => {
575
+ registry.register({
576
+ name: 'common',
577
+ namespace: 'plugin-1',
578
+ type: 'skill',
579
+ filePath: '/1/common/SKILL.md',
580
+ source: 'plugin:plugin-1',
581
+ description: 'Common 1',
582
+ metadata: {},
583
+ });
584
+ registry.register({
585
+ name: 'common',
586
+ namespace: 'plugin-2',
587
+ type: 'skill',
588
+ filePath: '/2/common/SKILL.md',
589
+ source: 'plugin:plugin-2',
590
+ description: 'Common 2',
591
+ metadata: {},
592
+ });
593
+
594
+ const diagnostics = analyzeCollisions(registry, resolver);
595
+ const report = doctorReport(diagnostics);
596
+
597
+ assert.ok(report.entries[0].includes('`plugin-1:common`'));
598
+ assert.ok(report.entries[0].includes('`plugin-2:common`'));
599
+ });
600
+
601
+ it('should return empty arrays for clean registry', () => {
602
+ const diagnostics = analyzeCollisions(registry, resolver);
603
+ const report = doctorReport(diagnostics);
604
+
605
+ assert.strictEqual(report.summary.total, 0);
606
+ assert.strictEqual(report.summary.canonicalConflicts, 0);
607
+ assert.strictEqual(report.summary.shorthandOverlaps, 0);
608
+ assert.strictEqual(report.summary.aliasConflicts, 0);
609
+ assert.deepStrictEqual(report.entries, []);
610
+ });
611
+
612
+ it('should include alias conflicts in summary counts', () => {
613
+ registry.register({
614
+ name: 'target',
615
+ namespace: 'my-plugin',
616
+ type: 'skill',
617
+ filePath: '/my-plugin/target/SKILL.md',
618
+ source: 'plugin:my-plugin',
619
+ description: 'Target skill',
620
+ metadata: {},
621
+ });
622
+
623
+ registry.register({
624
+ name: 'helper',
625
+ namespace: 'other',
626
+ type: 'skill',
627
+ filePath: '/other/helper/SKILL.md',
628
+ source: 'plugin:other',
629
+ description: 'Helper skill',
630
+ metadata: {},
631
+ });
632
+
633
+ // Create alias that shadows bare name
634
+ registry.registerAlias('helper', 'my-plugin:target');
635
+
636
+ const diagnostics = analyzeCollisions(registry, resolver);
637
+ const report = doctorReport(diagnostics);
638
+
639
+ assert.strictEqual(report.summary.aliasConflicts, 1);
640
+ assert.strictEqual(report.summary.total, 1);
641
+ });
642
+
643
+ it('should include warning icon for alias conflicts', () => {
644
+ registry.register({
645
+ name: 'target',
646
+ namespace: 'my-plugin',
647
+ type: 'skill',
648
+ filePath: '/my-plugin/target/SKILL.md',
649
+ source: 'plugin:my-plugin',
650
+ description: 'Target skill',
651
+ metadata: {},
652
+ });
653
+
654
+ registry.register({
655
+ name: 'shadowed',
656
+ namespace: 'other',
657
+ type: 'skill',
658
+ filePath: '/other/shadowed/SKILL.md',
659
+ source: 'plugin:other',
660
+ description: 'Shadowed skill',
661
+ metadata: {},
662
+ });
663
+
664
+ // Create alias that shadows bare name
665
+ registry.registerAlias('shadowed', 'my-plugin:target');
666
+
667
+ const diagnostics = analyzeCollisions(registry, resolver);
668
+ const report = doctorReport(diagnostics);
669
+
670
+ assert.ok(report.entries[0].includes('⚠️'));
671
+ assert.ok(report.entries[0].includes('ALIAS-CONFLICT'));
672
+ });
673
+
674
+ it('should include alias details in formatted output', () => {
675
+ registry.register({
676
+ name: 'target',
677
+ namespace: 'my-plugin',
678
+ type: 'skill',
679
+ filePath: '/my-plugin/target/SKILL.md',
680
+ source: 'plugin:my-plugin',
681
+ description: 'Target skill',
682
+ metadata: {},
683
+ });
684
+
685
+ registry.register({
686
+ name: 'shadowed',
687
+ namespace: 'other',
688
+ type: 'skill',
689
+ filePath: '/other/shadowed/SKILL.md',
690
+ source: 'plugin:other',
691
+ description: 'Shadowed skill',
692
+ metadata: {},
693
+ });
694
+
695
+ // Create alias that shadows bare name
696
+ registry.registerAlias('shadowed', 'my-plugin:target');
697
+
698
+ const diagnostics = analyzeCollisions(registry, resolver);
699
+ const report = doctorReport(diagnostics);
700
+
701
+ assert.ok(report.entries[0].includes('shadowed'));
702
+ assert.ok(report.entries[0].includes('my-plugin:target'));
703
+ });
704
+ });
705
+ });