@agents-inc/cli 0.32.1

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 (497) hide show
  1. package/CHANGELOG.md +462 -0
  2. package/LICENSE +21 -0
  3. package/README.md +179 -0
  4. package/config/skills-matrix.yaml +926 -0
  5. package/config/stacks.yaml +2186 -0
  6. package/dist/chunk-3ZOIOVKT.js +365 -0
  7. package/dist/chunk-3ZOIOVKT.js.map +1 -0
  8. package/dist/chunk-4RAY5AOI.js +78 -0
  9. package/dist/chunk-4RAY5AOI.js.map +1 -0
  10. package/dist/chunk-5PIKNCZX.js +234 -0
  11. package/dist/chunk-5PIKNCZX.js.map +1 -0
  12. package/dist/chunk-66UDJBF6.js +96 -0
  13. package/dist/chunk-66UDJBF6.js.map +1 -0
  14. package/dist/chunk-7SOPVGDV.js +24 -0
  15. package/dist/chunk-7SOPVGDV.js.map +1 -0
  16. package/dist/chunk-A27LOC4Z.js +95 -0
  17. package/dist/chunk-A27LOC4Z.js.map +1 -0
  18. package/dist/chunk-B2UBHA66.js +301 -0
  19. package/dist/chunk-B2UBHA66.js.map +1 -0
  20. package/dist/chunk-BZN2Z5P7.js +882 -0
  21. package/dist/chunk-BZN2Z5P7.js.map +1 -0
  22. package/dist/chunk-BZQBJP34.js +186 -0
  23. package/dist/chunk-BZQBJP34.js.map +1 -0
  24. package/dist/chunk-DC5AK3LW.js +105 -0
  25. package/dist/chunk-DC5AK3LW.js.map +1 -0
  26. package/dist/chunk-DHET7RCE.js +50 -0
  27. package/dist/chunk-DHET7RCE.js.map +1 -0
  28. package/dist/chunk-EMJ2ZKS7.js +346 -0
  29. package/dist/chunk-EMJ2ZKS7.js.map +1 -0
  30. package/dist/chunk-FJQRVFMB.js +48 -0
  31. package/dist/chunk-FJQRVFMB.js.map +1 -0
  32. package/dist/chunk-FZGYSLJL.js +85 -0
  33. package/dist/chunk-FZGYSLJL.js.map +1 -0
  34. package/dist/chunk-H566H3MQ.js +87 -0
  35. package/dist/chunk-H566H3MQ.js.map +1 -0
  36. package/dist/chunk-IYG2LAIM.js +90 -0
  37. package/dist/chunk-IYG2LAIM.js.map +1 -0
  38. package/dist/chunk-IZZ4IIEG.js +29 -0
  39. package/dist/chunk-IZZ4IIEG.js.map +1 -0
  40. package/dist/chunk-JMVWYAHT.js +63 -0
  41. package/dist/chunk-JMVWYAHT.js.map +1 -0
  42. package/dist/chunk-LAPCUV4D.js +191 -0
  43. package/dist/chunk-LAPCUV4D.js.map +1 -0
  44. package/dist/chunk-LGUI3PMO.js +109 -0
  45. package/dist/chunk-LGUI3PMO.js.map +1 -0
  46. package/dist/chunk-MM7NK5N2.js +4542 -0
  47. package/dist/chunk-MM7NK5N2.js.map +1 -0
  48. package/dist/chunk-N6S7ZRIL.js +31 -0
  49. package/dist/chunk-N6S7ZRIL.js.map +1 -0
  50. package/dist/chunk-O4D67NN7.js +24 -0
  51. package/dist/chunk-O4D67NN7.js.map +1 -0
  52. package/dist/chunk-ODUOU55D.js +56 -0
  53. package/dist/chunk-ODUOU55D.js.map +1 -0
  54. package/dist/chunk-OGJIZ6QH.js +497 -0
  55. package/dist/chunk-OGJIZ6QH.js.map +1 -0
  56. package/dist/chunk-OMV7TLWD.js +340 -0
  57. package/dist/chunk-OMV7TLWD.js.map +1 -0
  58. package/dist/chunk-PBEHPQLK.js +146 -0
  59. package/dist/chunk-PBEHPQLK.js.map +1 -0
  60. package/dist/chunk-QPTOIZAT.js +32 -0
  61. package/dist/chunk-QPTOIZAT.js.map +1 -0
  62. package/dist/chunk-R3XFQKPG.js +111 -0
  63. package/dist/chunk-R3XFQKPG.js.map +1 -0
  64. package/dist/chunk-R74PZWQS.js +69 -0
  65. package/dist/chunk-R74PZWQS.js.map +1 -0
  66. package/dist/chunk-SO22IQPY.js +45 -0
  67. package/dist/chunk-SO22IQPY.js.map +1 -0
  68. package/dist/chunk-T4EXUIBY.js +19 -0
  69. package/dist/chunk-T4EXUIBY.js.map +1 -0
  70. package/dist/chunk-U3IGFMCY.js +31 -0
  71. package/dist/chunk-U3IGFMCY.js.map +1 -0
  72. package/dist/chunk-UICL22RT.js +318 -0
  73. package/dist/chunk-UICL22RT.js.map +1 -0
  74. package/dist/chunk-UX2H2K2G.js +183 -0
  75. package/dist/chunk-UX2H2K2G.js.map +1 -0
  76. package/dist/chunk-W2ZSCZ2U.js +93 -0
  77. package/dist/chunk-W2ZSCZ2U.js.map +1 -0
  78. package/dist/chunk-WEUVWHMA.js +189 -0
  79. package/dist/chunk-WEUVWHMA.js.map +1 -0
  80. package/dist/chunk-XY3XDVMI.js +15599 -0
  81. package/dist/chunk-XY3XDVMI.js.map +1 -0
  82. package/dist/chunk-YND42IXK.js +233 -0
  83. package/dist/chunk-YND42IXK.js.map +1 -0
  84. package/dist/chunk-YZTWZVGX.js +41 -0
  85. package/dist/chunk-YZTWZVGX.js.map +1 -0
  86. package/dist/chunk-Z4TWOP3H.js +81 -0
  87. package/dist/chunk-Z4TWOP3H.js.map +1 -0
  88. package/dist/cli/defaults/agent-mappings.yaml +271 -0
  89. package/dist/commands/build/marketplace.js +252 -0
  90. package/dist/commands/build/marketplace.js.map +1 -0
  91. package/dist/commands/build/plugins.js +114 -0
  92. package/dist/commands/build/plugins.js.map +1 -0
  93. package/dist/commands/build/stack.js +153 -0
  94. package/dist/commands/build/stack.js.map +1 -0
  95. package/dist/commands/compile.js +354 -0
  96. package/dist/commands/compile.js.map +1 -0
  97. package/dist/commands/config/get.js +61 -0
  98. package/dist/commands/config/get.js.map +1 -0
  99. package/dist/commands/config/index.js +23 -0
  100. package/dist/commands/config/index.js.map +1 -0
  101. package/dist/commands/config/path.js +34 -0
  102. package/dist/commands/config/path.js.map +1 -0
  103. package/dist/commands/config/set-project.js +61 -0
  104. package/dist/commands/config/set-project.js.map +1 -0
  105. package/dist/commands/config/show.js +14 -0
  106. package/dist/commands/config/show.js.map +1 -0
  107. package/dist/commands/config/unset-project.js +57 -0
  108. package/dist/commands/config/unset-project.js.map +1 -0
  109. package/dist/commands/diff.js +742 -0
  110. package/dist/commands/diff.js.map +1 -0
  111. package/dist/commands/doctor.js +370 -0
  112. package/dist/commands/doctor.js.map +1 -0
  113. package/dist/commands/edit.js +301 -0
  114. package/dist/commands/edit.js.map +1 -0
  115. package/dist/commands/eject.js +262 -0
  116. package/dist/commands/eject.js.map +1 -0
  117. package/dist/commands/import/skill.js +361 -0
  118. package/dist/commands/import/skill.js.map +1 -0
  119. package/dist/commands/info.js +217 -0
  120. package/dist/commands/info.js.map +1 -0
  121. package/dist/commands/init.js +443 -0
  122. package/dist/commands/init.js.map +1 -0
  123. package/dist/commands/list.js +49 -0
  124. package/dist/commands/list.js.map +1 -0
  125. package/dist/commands/new/agent.js +224 -0
  126. package/dist/commands/new/agent.js.map +1 -0
  127. package/dist/commands/new/skill.js +199 -0
  128. package/dist/commands/new/skill.js.map +1 -0
  129. package/dist/commands/outdated.js +176 -0
  130. package/dist/commands/outdated.js.map +1 -0
  131. package/dist/commands/search.js +288 -0
  132. package/dist/commands/search.js.map +1 -0
  133. package/dist/commands/uninstall.js +302 -0
  134. package/dist/commands/uninstall.js.map +1 -0
  135. package/dist/commands/update.js +304 -0
  136. package/dist/commands/update.js.map +1 -0
  137. package/dist/commands/validate.js +389 -0
  138. package/dist/commands/validate.js.map +1 -0
  139. package/dist/commands/version/bump.js +79 -0
  140. package/dist/commands/version/bump.js.map +1 -0
  141. package/dist/commands/version/index.js +54 -0
  142. package/dist/commands/version/index.js.map +1 -0
  143. package/dist/commands/version/set.js +86 -0
  144. package/dist/commands/version/set.js.map +1 -0
  145. package/dist/commands/version/show.js +54 -0
  146. package/dist/commands/version/show.js.map +1 -0
  147. package/dist/components/common/confirm.js +9 -0
  148. package/dist/components/common/confirm.js.map +1 -0
  149. package/dist/components/common/confirm.test.js +203 -0
  150. package/dist/components/common/confirm.test.js.map +1 -0
  151. package/dist/components/common/message.js +20 -0
  152. package/dist/components/common/message.js.map +1 -0
  153. package/dist/components/common/spinner.js +14 -0
  154. package/dist/components/common/spinner.js.map +1 -0
  155. package/dist/components/skill-search/skill-search.js +12 -0
  156. package/dist/components/skill-search/skill-search.js.map +1 -0
  157. package/dist/components/wizard/category-grid.js +11 -0
  158. package/dist/components/wizard/category-grid.js.map +1 -0
  159. package/dist/components/wizard/category-grid.test.js +997 -0
  160. package/dist/components/wizard/category-grid.test.js.map +1 -0
  161. package/dist/components/wizard/domain-selection.js +14 -0
  162. package/dist/components/wizard/domain-selection.js.map +1 -0
  163. package/dist/components/wizard/help-modal.js +10 -0
  164. package/dist/components/wizard/help-modal.js.map +1 -0
  165. package/dist/components/wizard/menu-item.js +10 -0
  166. package/dist/components/wizard/menu-item.js.map +1 -0
  167. package/dist/components/wizard/search-modal.js +11 -0
  168. package/dist/components/wizard/search-modal.js.map +1 -0
  169. package/dist/components/wizard/search-modal.test.js +218 -0
  170. package/dist/components/wizard/search-modal.test.js.map +1 -0
  171. package/dist/components/wizard/section-progress.js +10 -0
  172. package/dist/components/wizard/section-progress.js.map +1 -0
  173. package/dist/components/wizard/section-progress.test.js +192 -0
  174. package/dist/components/wizard/section-progress.test.js.map +1 -0
  175. package/dist/components/wizard/source-grid.js +14 -0
  176. package/dist/components/wizard/source-grid.js.map +1 -0
  177. package/dist/components/wizard/source-grid.test.js +504 -0
  178. package/dist/components/wizard/source-grid.test.js.map +1 -0
  179. package/dist/components/wizard/stack-selection.js +17 -0
  180. package/dist/components/wizard/stack-selection.js.map +1 -0
  181. package/dist/components/wizard/step-build.js +17 -0
  182. package/dist/components/wizard/step-build.js.map +1 -0
  183. package/dist/components/wizard/step-build.test.js +600 -0
  184. package/dist/components/wizard/step-build.test.js.map +1 -0
  185. package/dist/components/wizard/step-confirm.js +12 -0
  186. package/dist/components/wizard/step-confirm.js.map +1 -0
  187. package/dist/components/wizard/step-confirm.test.js +366 -0
  188. package/dist/components/wizard/step-confirm.test.js.map +1 -0
  189. package/dist/components/wizard/step-refine.js +10 -0
  190. package/dist/components/wizard/step-refine.js.map +1 -0
  191. package/dist/components/wizard/step-refine.test.js +237 -0
  192. package/dist/components/wizard/step-refine.test.js.map +1 -0
  193. package/dist/components/wizard/step-settings.js +17 -0
  194. package/dist/components/wizard/step-settings.js.map +1 -0
  195. package/dist/components/wizard/step-settings.test.js +243 -0
  196. package/dist/components/wizard/step-settings.test.js.map +1 -0
  197. package/dist/components/wizard/step-sources.js +20 -0
  198. package/dist/components/wizard/step-sources.js.map +1 -0
  199. package/dist/components/wizard/step-sources.test.js +294 -0
  200. package/dist/components/wizard/step-sources.test.js.map +1 -0
  201. package/dist/components/wizard/step-stack.js +19 -0
  202. package/dist/components/wizard/step-stack.js.map +1 -0
  203. package/dist/components/wizard/step-stack.test.js +357 -0
  204. package/dist/components/wizard/step-stack.test.js.map +1 -0
  205. package/dist/components/wizard/view-title.js +10 -0
  206. package/dist/components/wizard/view-title.js.map +1 -0
  207. package/dist/components/wizard/wizard-layout.js +16 -0
  208. package/dist/components/wizard/wizard-layout.js.map +1 -0
  209. package/dist/components/wizard/wizard-tabs.js +14 -0
  210. package/dist/components/wizard/wizard-tabs.js.map +1 -0
  211. package/dist/components/wizard/wizard-tabs.test.js +294 -0
  212. package/dist/components/wizard/wizard-tabs.test.js.map +1 -0
  213. package/dist/components/wizard/wizard.js +35 -0
  214. package/dist/components/wizard/wizard.js.map +1 -0
  215. package/dist/config/skills-matrix.yaml +926 -0
  216. package/dist/config/stacks.yaml +2186 -0
  217. package/dist/hooks/init.js +40 -0
  218. package/dist/hooks/init.js.map +1 -0
  219. package/dist/index.js +10 -0
  220. package/dist/index.js.map +1 -0
  221. package/dist/magic-string.es-RGXYGAW3.js +1316 -0
  222. package/dist/magic-string.es-RGXYGAW3.js.map +1 -0
  223. package/dist/source-manager-SBPPLOQQ.js +16 -0
  224. package/dist/source-manager-SBPPLOQQ.js.map +1 -0
  225. package/dist/src/agents/_templates/agent.liquid +140 -0
  226. package/dist/src/agents/developer/api-developer/agent.yaml +12 -0
  227. package/dist/src/agents/developer/api-developer/critical-reminders.md +23 -0
  228. package/dist/src/agents/developer/api-developer/critical-requirements.md +11 -0
  229. package/dist/src/agents/developer/api-developer/examples.md +72 -0
  230. package/dist/src/agents/developer/api-developer/intro.md +22 -0
  231. package/dist/src/agents/developer/api-developer/output-format.md +359 -0
  232. package/dist/src/agents/developer/api-developer/workflow.md +471 -0
  233. package/dist/src/agents/developer/cli-developer/agent.yaml +12 -0
  234. package/dist/src/agents/developer/cli-developer/critical-reminders.md +28 -0
  235. package/dist/src/agents/developer/cli-developer/critical-requirements.md +15 -0
  236. package/dist/src/agents/developer/cli-developer/examples.md +68 -0
  237. package/dist/src/agents/developer/cli-developer/intro.md +23 -0
  238. package/dist/src/agents/developer/cli-developer/output-format.md +216 -0
  239. package/dist/src/agents/developer/cli-developer/workflow.md +509 -0
  240. package/dist/src/agents/developer/web-architecture/agent.yaml +12 -0
  241. package/dist/src/agents/developer/web-architecture/critical-reminders.md +27 -0
  242. package/dist/src/agents/developer/web-architecture/critical-requirements.md +35 -0
  243. package/dist/src/agents/developer/web-architecture/examples.md +187 -0
  244. package/dist/src/agents/developer/web-architecture/intro.md +35 -0
  245. package/dist/src/agents/developer/web-architecture/output-format.md +261 -0
  246. package/dist/src/agents/developer/web-architecture/workflow.md +599 -0
  247. package/dist/src/agents/developer/web-developer/agent.yaml +12 -0
  248. package/dist/src/agents/developer/web-developer/critical-reminders.md +17 -0
  249. package/dist/src/agents/developer/web-developer/critical-requirements.md +15 -0
  250. package/dist/src/agents/developer/web-developer/examples.md +109 -0
  251. package/dist/src/agents/developer/web-developer/intro.md +5 -0
  252. package/dist/src/agents/developer/web-developer/output-format.md +213 -0
  253. package/dist/src/agents/developer/web-developer/workflow.md +459 -0
  254. package/dist/src/agents/meta/agent-summoner/agent.yaml +12 -0
  255. package/dist/src/agents/meta/agent-summoner/critical-reminders.md +31 -0
  256. package/dist/src/agents/meta/agent-summoner/critical-requirements.md +27 -0
  257. package/dist/src/agents/meta/agent-summoner/examples.md +176 -0
  258. package/dist/src/agents/meta/agent-summoner/intro.md +9 -0
  259. package/dist/src/agents/meta/agent-summoner/output-format.md +115 -0
  260. package/dist/src/agents/meta/agent-summoner/workflow.md +1540 -0
  261. package/dist/src/agents/meta/documentor/agent.yaml +11 -0
  262. package/dist/src/agents/meta/documentor/critical-reminders.md +23 -0
  263. package/dist/src/agents/meta/documentor/critical-requirements.md +13 -0
  264. package/dist/src/agents/meta/documentor/examples.md +147 -0
  265. package/dist/src/agents/meta/documentor/intro.md +11 -0
  266. package/dist/src/agents/meta/documentor/output-format.md +237 -0
  267. package/dist/src/agents/meta/documentor/workflow.md +1271 -0
  268. package/dist/src/agents/meta/skill-summoner/agent.yaml +13 -0
  269. package/dist/src/agents/meta/skill-summoner/critical-reminders.md +73 -0
  270. package/dist/src/agents/meta/skill-summoner/critical-requirements.md +62 -0
  271. package/dist/src/agents/meta/skill-summoner/examples.md +116 -0
  272. package/dist/src/agents/meta/skill-summoner/intro.md +5 -0
  273. package/dist/src/agents/meta/skill-summoner/output-format.md +279 -0
  274. package/dist/src/agents/meta/skill-summoner/workflow.md +1485 -0
  275. package/dist/src/agents/migration/cli-migrator/agent.yaml +12 -0
  276. package/dist/src/agents/migration/cli-migrator/anti-patterns.md +158 -0
  277. package/dist/src/agents/migration/cli-migrator/conversion-mappings.md +63 -0
  278. package/dist/src/agents/migration/cli-migrator/critical-reminders.md +17 -0
  279. package/dist/src/agents/migration/cli-migrator/critical-requirements.md +13 -0
  280. package/dist/src/agents/migration/cli-migrator/intro.md +15 -0
  281. package/dist/src/agents/migration/cli-migrator/output-format.md +164 -0
  282. package/dist/src/agents/migration/cli-migrator/workflow.md +230 -0
  283. package/dist/src/agents/pattern/pattern-scout/agent.yaml +10 -0
  284. package/dist/src/agents/pattern/pattern-scout/critical-reminders.md +58 -0
  285. package/dist/src/agents/pattern/pattern-scout/critical-requirements.md +17 -0
  286. package/dist/src/agents/pattern/pattern-scout/examples.md +93 -0
  287. package/dist/src/agents/pattern/pattern-scout/intro.md +3 -0
  288. package/dist/src/agents/pattern/pattern-scout/output-format.md +196 -0
  289. package/dist/src/agents/pattern/pattern-scout/workflow.md +1901 -0
  290. package/dist/src/agents/pattern/web-pattern-critique/agent.yaml +12 -0
  291. package/dist/src/agents/pattern/web-pattern-critique/critical-reminders.md +13 -0
  292. package/dist/src/agents/pattern/web-pattern-critique/critical-requirements.md +11 -0
  293. package/dist/src/agents/pattern/web-pattern-critique/examples.md +56 -0
  294. package/dist/src/agents/pattern/web-pattern-critique/intro.md +5 -0
  295. package/dist/src/agents/pattern/web-pattern-critique/output-format.md +257 -0
  296. package/dist/src/agents/pattern/web-pattern-critique/workflow.md +674 -0
  297. package/dist/src/agents/planning/web-pm/agent.yaml +12 -0
  298. package/dist/src/agents/planning/web-pm/critical-reminders.md +21 -0
  299. package/dist/src/agents/planning/web-pm/critical-requirements.md +17 -0
  300. package/dist/src/agents/planning/web-pm/examples.md +85 -0
  301. package/dist/src/agents/planning/web-pm/intro.md +3 -0
  302. package/dist/src/agents/planning/web-pm/output-format.md +228 -0
  303. package/dist/src/agents/planning/web-pm/workflow.md +393 -0
  304. package/dist/src/agents/researcher/api-researcher/agent.yaml +10 -0
  305. package/dist/src/agents/researcher/api-researcher/critical-reminders.md +27 -0
  306. package/dist/src/agents/researcher/api-researcher/critical-requirements.md +13 -0
  307. package/dist/src/agents/researcher/api-researcher/examples.md +116 -0
  308. package/dist/src/agents/researcher/api-researcher/intro.md +32 -0
  309. package/dist/src/agents/researcher/api-researcher/output-format.md +135 -0
  310. package/dist/src/agents/researcher/api-researcher/workflow.md +261 -0
  311. package/dist/src/agents/researcher/web-researcher/agent.yaml +10 -0
  312. package/dist/src/agents/researcher/web-researcher/critical-reminders.md +23 -0
  313. package/dist/src/agents/researcher/web-researcher/critical-requirements.md +11 -0
  314. package/dist/src/agents/researcher/web-researcher/examples.md +126 -0
  315. package/dist/src/agents/researcher/web-researcher/intro.md +31 -0
  316. package/dist/src/agents/researcher/web-researcher/output-format.md +112 -0
  317. package/dist/src/agents/researcher/web-researcher/workflow.md +322 -0
  318. package/dist/src/agents/reviewer/api-reviewer/agent.yaml +12 -0
  319. package/dist/src/agents/reviewer/api-reviewer/critical-reminders.md +16 -0
  320. package/dist/src/agents/reviewer/api-reviewer/critical-requirements.md +13 -0
  321. package/dist/src/agents/reviewer/api-reviewer/examples.md +54 -0
  322. package/dist/src/agents/reviewer/api-reviewer/intro.md +22 -0
  323. package/dist/src/agents/reviewer/api-reviewer/output-format.md +288 -0
  324. package/dist/src/agents/reviewer/api-reviewer/workflow.md +369 -0
  325. package/dist/src/agents/reviewer/cli-reviewer/agent.yaml +12 -0
  326. package/dist/src/agents/reviewer/cli-reviewer/critical-reminders.md +17 -0
  327. package/dist/src/agents/reviewer/cli-reviewer/critical-requirements.md +13 -0
  328. package/dist/src/agents/reviewer/cli-reviewer/examples.md +83 -0
  329. package/dist/src/agents/reviewer/cli-reviewer/intro.md +21 -0
  330. package/dist/src/agents/reviewer/cli-reviewer/output-format.md +330 -0
  331. package/dist/src/agents/reviewer/cli-reviewer/workflow.md +294 -0
  332. package/dist/src/agents/reviewer/web-reviewer/agent.yaml +12 -0
  333. package/dist/src/agents/reviewer/web-reviewer/critical-reminders.md +17 -0
  334. package/dist/src/agents/reviewer/web-reviewer/critical-requirements.md +11 -0
  335. package/dist/src/agents/reviewer/web-reviewer/examples.md +79 -0
  336. package/dist/src/agents/reviewer/web-reviewer/intro.md +20 -0
  337. package/dist/src/agents/reviewer/web-reviewer/output-format.md +253 -0
  338. package/dist/src/agents/reviewer/web-reviewer/workflow.md +228 -0
  339. package/dist/src/agents/tester/cli-tester/agent.yaml +12 -0
  340. package/dist/src/agents/tester/cli-tester/critical-reminders.md +19 -0
  341. package/dist/src/agents/tester/cli-tester/critical-requirements.md +17 -0
  342. package/dist/src/agents/tester/cli-tester/examples.md +80 -0
  343. package/dist/src/agents/tester/cli-tester/intro.md +19 -0
  344. package/dist/src/agents/tester/cli-tester/output-format.md +232 -0
  345. package/dist/src/agents/tester/cli-tester/workflow.md +304 -0
  346. package/dist/src/agents/tester/web-tester/agent.yaml +12 -0
  347. package/dist/src/agents/tester/web-tester/critical-reminders.md +15 -0
  348. package/dist/src/agents/tester/web-tester/critical-requirements.md +11 -0
  349. package/dist/src/agents/tester/web-tester/examples.md +68 -0
  350. package/dist/src/agents/tester/web-tester/intro.md +18 -0
  351. package/dist/src/agents/tester/web-tester/output-format.md +252 -0
  352. package/dist/src/agents/tester/web-tester/workflow.md +507 -0
  353. package/dist/stores/wizard-store.js +13 -0
  354. package/dist/stores/wizard-store.js.map +1 -0
  355. package/dist/stores/wizard-store.test.js +689 -0
  356. package/dist/stores/wizard-store.test.js.map +1 -0
  357. package/package.json +134 -0
  358. package/src/agents/_templates/agent.liquid +140 -0
  359. package/src/agents/developer/api-developer/agent.yaml +12 -0
  360. package/src/agents/developer/api-developer/critical-reminders.md +23 -0
  361. package/src/agents/developer/api-developer/critical-requirements.md +11 -0
  362. package/src/agents/developer/api-developer/examples.md +72 -0
  363. package/src/agents/developer/api-developer/intro.md +22 -0
  364. package/src/agents/developer/api-developer/output-format.md +359 -0
  365. package/src/agents/developer/api-developer/workflow.md +471 -0
  366. package/src/agents/developer/cli-developer/agent.yaml +12 -0
  367. package/src/agents/developer/cli-developer/critical-reminders.md +28 -0
  368. package/src/agents/developer/cli-developer/critical-requirements.md +15 -0
  369. package/src/agents/developer/cli-developer/examples.md +68 -0
  370. package/src/agents/developer/cli-developer/intro.md +23 -0
  371. package/src/agents/developer/cli-developer/output-format.md +216 -0
  372. package/src/agents/developer/cli-developer/workflow.md +509 -0
  373. package/src/agents/developer/web-architecture/agent.yaml +12 -0
  374. package/src/agents/developer/web-architecture/critical-reminders.md +27 -0
  375. package/src/agents/developer/web-architecture/critical-requirements.md +35 -0
  376. package/src/agents/developer/web-architecture/examples.md +187 -0
  377. package/src/agents/developer/web-architecture/intro.md +35 -0
  378. package/src/agents/developer/web-architecture/output-format.md +261 -0
  379. package/src/agents/developer/web-architecture/workflow.md +599 -0
  380. package/src/agents/developer/web-developer/agent.yaml +12 -0
  381. package/src/agents/developer/web-developer/critical-reminders.md +17 -0
  382. package/src/agents/developer/web-developer/critical-requirements.md +15 -0
  383. package/src/agents/developer/web-developer/examples.md +109 -0
  384. package/src/agents/developer/web-developer/intro.md +5 -0
  385. package/src/agents/developer/web-developer/output-format.md +213 -0
  386. package/src/agents/developer/web-developer/workflow.md +459 -0
  387. package/src/agents/meta/agent-summoner/agent.yaml +12 -0
  388. package/src/agents/meta/agent-summoner/critical-reminders.md +31 -0
  389. package/src/agents/meta/agent-summoner/critical-requirements.md +27 -0
  390. package/src/agents/meta/agent-summoner/examples.md +176 -0
  391. package/src/agents/meta/agent-summoner/intro.md +9 -0
  392. package/src/agents/meta/agent-summoner/output-format.md +115 -0
  393. package/src/agents/meta/agent-summoner/workflow.md +1540 -0
  394. package/src/agents/meta/documentor/agent.yaml +11 -0
  395. package/src/agents/meta/documentor/critical-reminders.md +23 -0
  396. package/src/agents/meta/documentor/critical-requirements.md +13 -0
  397. package/src/agents/meta/documentor/examples.md +147 -0
  398. package/src/agents/meta/documentor/intro.md +11 -0
  399. package/src/agents/meta/documentor/output-format.md +237 -0
  400. package/src/agents/meta/documentor/workflow.md +1271 -0
  401. package/src/agents/meta/skill-summoner/agent.yaml +13 -0
  402. package/src/agents/meta/skill-summoner/critical-reminders.md +73 -0
  403. package/src/agents/meta/skill-summoner/critical-requirements.md +62 -0
  404. package/src/agents/meta/skill-summoner/examples.md +116 -0
  405. package/src/agents/meta/skill-summoner/intro.md +5 -0
  406. package/src/agents/meta/skill-summoner/output-format.md +279 -0
  407. package/src/agents/meta/skill-summoner/workflow.md +1485 -0
  408. package/src/agents/migration/cli-migrator/agent.yaml +12 -0
  409. package/src/agents/migration/cli-migrator/anti-patterns.md +158 -0
  410. package/src/agents/migration/cli-migrator/conversion-mappings.md +63 -0
  411. package/src/agents/migration/cli-migrator/critical-reminders.md +17 -0
  412. package/src/agents/migration/cli-migrator/critical-requirements.md +13 -0
  413. package/src/agents/migration/cli-migrator/intro.md +15 -0
  414. package/src/agents/migration/cli-migrator/output-format.md +164 -0
  415. package/src/agents/migration/cli-migrator/workflow.md +230 -0
  416. package/src/agents/pattern/pattern-scout/agent.yaml +10 -0
  417. package/src/agents/pattern/pattern-scout/critical-reminders.md +58 -0
  418. package/src/agents/pattern/pattern-scout/critical-requirements.md +17 -0
  419. package/src/agents/pattern/pattern-scout/examples.md +93 -0
  420. package/src/agents/pattern/pattern-scout/intro.md +3 -0
  421. package/src/agents/pattern/pattern-scout/output-format.md +196 -0
  422. package/src/agents/pattern/pattern-scout/workflow.md +1901 -0
  423. package/src/agents/pattern/web-pattern-critique/agent.yaml +12 -0
  424. package/src/agents/pattern/web-pattern-critique/critical-reminders.md +13 -0
  425. package/src/agents/pattern/web-pattern-critique/critical-requirements.md +11 -0
  426. package/src/agents/pattern/web-pattern-critique/examples.md +56 -0
  427. package/src/agents/pattern/web-pattern-critique/intro.md +5 -0
  428. package/src/agents/pattern/web-pattern-critique/output-format.md +257 -0
  429. package/src/agents/pattern/web-pattern-critique/workflow.md +674 -0
  430. package/src/agents/planning/web-pm/agent.yaml +12 -0
  431. package/src/agents/planning/web-pm/critical-reminders.md +21 -0
  432. package/src/agents/planning/web-pm/critical-requirements.md +17 -0
  433. package/src/agents/planning/web-pm/examples.md +85 -0
  434. package/src/agents/planning/web-pm/intro.md +3 -0
  435. package/src/agents/planning/web-pm/output-format.md +228 -0
  436. package/src/agents/planning/web-pm/workflow.md +393 -0
  437. package/src/agents/researcher/api-researcher/agent.yaml +10 -0
  438. package/src/agents/researcher/api-researcher/critical-reminders.md +27 -0
  439. package/src/agents/researcher/api-researcher/critical-requirements.md +13 -0
  440. package/src/agents/researcher/api-researcher/examples.md +116 -0
  441. package/src/agents/researcher/api-researcher/intro.md +32 -0
  442. package/src/agents/researcher/api-researcher/output-format.md +135 -0
  443. package/src/agents/researcher/api-researcher/workflow.md +261 -0
  444. package/src/agents/researcher/web-researcher/agent.yaml +10 -0
  445. package/src/agents/researcher/web-researcher/critical-reminders.md +23 -0
  446. package/src/agents/researcher/web-researcher/critical-requirements.md +11 -0
  447. package/src/agents/researcher/web-researcher/examples.md +126 -0
  448. package/src/agents/researcher/web-researcher/intro.md +31 -0
  449. package/src/agents/researcher/web-researcher/output-format.md +112 -0
  450. package/src/agents/researcher/web-researcher/workflow.md +322 -0
  451. package/src/agents/reviewer/api-reviewer/agent.yaml +12 -0
  452. package/src/agents/reviewer/api-reviewer/critical-reminders.md +16 -0
  453. package/src/agents/reviewer/api-reviewer/critical-requirements.md +13 -0
  454. package/src/agents/reviewer/api-reviewer/examples.md +54 -0
  455. package/src/agents/reviewer/api-reviewer/intro.md +22 -0
  456. package/src/agents/reviewer/api-reviewer/output-format.md +288 -0
  457. package/src/agents/reviewer/api-reviewer/workflow.md +369 -0
  458. package/src/agents/reviewer/cli-reviewer/agent.yaml +12 -0
  459. package/src/agents/reviewer/cli-reviewer/critical-reminders.md +17 -0
  460. package/src/agents/reviewer/cli-reviewer/critical-requirements.md +13 -0
  461. package/src/agents/reviewer/cli-reviewer/examples.md +83 -0
  462. package/src/agents/reviewer/cli-reviewer/intro.md +21 -0
  463. package/src/agents/reviewer/cli-reviewer/output-format.md +330 -0
  464. package/src/agents/reviewer/cli-reviewer/workflow.md +294 -0
  465. package/src/agents/reviewer/web-reviewer/agent.yaml +12 -0
  466. package/src/agents/reviewer/web-reviewer/critical-reminders.md +17 -0
  467. package/src/agents/reviewer/web-reviewer/critical-requirements.md +11 -0
  468. package/src/agents/reviewer/web-reviewer/examples.md +79 -0
  469. package/src/agents/reviewer/web-reviewer/intro.md +20 -0
  470. package/src/agents/reviewer/web-reviewer/output-format.md +253 -0
  471. package/src/agents/reviewer/web-reviewer/workflow.md +228 -0
  472. package/src/agents/tester/cli-tester/agent.yaml +12 -0
  473. package/src/agents/tester/cli-tester/critical-reminders.md +19 -0
  474. package/src/agents/tester/cli-tester/critical-requirements.md +17 -0
  475. package/src/agents/tester/cli-tester/examples.md +80 -0
  476. package/src/agents/tester/cli-tester/intro.md +19 -0
  477. package/src/agents/tester/cli-tester/output-format.md +232 -0
  478. package/src/agents/tester/cli-tester/workflow.md +304 -0
  479. package/src/agents/tester/web-tester/agent.yaml +12 -0
  480. package/src/agents/tester/web-tester/critical-reminders.md +15 -0
  481. package/src/agents/tester/web-tester/critical-requirements.md +11 -0
  482. package/src/agents/tester/web-tester/examples.md +68 -0
  483. package/src/agents/tester/web-tester/intro.md +18 -0
  484. package/src/agents/tester/web-tester/output-format.md +252 -0
  485. package/src/agents/tester/web-tester/workflow.md +507 -0
  486. package/src/schemas/agent-frontmatter.schema.json +84 -0
  487. package/src/schemas/agent.schema.json +93 -0
  488. package/src/schemas/hooks.schema.json +47 -0
  489. package/src/schemas/marketplace.schema.json +119 -0
  490. package/src/schemas/metadata.schema.json +113 -0
  491. package/src/schemas/plugin.schema.json +130 -0
  492. package/src/schemas/project-config.schema.json +125 -0
  493. package/src/schemas/project-source-config.schema.json +81 -0
  494. package/src/schemas/skill-frontmatter.schema.json +42 -0
  495. package/src/schemas/skills-matrix.schema.json +467 -0
  496. package/src/schemas/stack.schema.json +191 -0
  497. package/src/schemas/stacks.schema.json +111 -0
@@ -0,0 +1,4542 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ typedEntries,
4
+ typedKeys
5
+ } from "./chunk-T4EXUIBY.js";
6
+ import {
7
+ SKILL_ID_PATTERN,
8
+ agentFrontmatterValidationSchema,
9
+ agentYamlConfigSchema,
10
+ categoryPathSchema,
11
+ copy,
12
+ directoryExists,
13
+ ensureDir,
14
+ fileExists,
15
+ formatZodErrors,
16
+ getErrorMessage,
17
+ glob,
18
+ hooksRecordSchema,
19
+ listDirectories,
20
+ localRawMetadataSchema,
21
+ localSkillMetadataSchema,
22
+ log,
23
+ marketplaceSchema,
24
+ pluginAuthorSchema,
25
+ pluginManifestSchema,
26
+ projectConfigLoaderSchema,
27
+ projectSourceConfigSchema,
28
+ readFile,
29
+ readFileOptional,
30
+ readFileSafe,
31
+ remove,
32
+ skillDisplayNameSchema,
33
+ skillFrontmatterLoaderSchema,
34
+ skillFrontmatterValidationSchema,
35
+ skillIdSchema,
36
+ skillMetadataLoaderSchema,
37
+ skillsMatrixConfigSchema,
38
+ stacksConfigSchema,
39
+ validateNestingDepth,
40
+ verbose,
41
+ warn,
42
+ warnUnknownFields,
43
+ writeFile
44
+ } from "./chunk-BZN2Z5P7.js";
45
+ import {
46
+ ARCHIVED_SKILLS_DIR_NAME,
47
+ CACHE_DIR,
48
+ CACHE_HASH_LENGTH,
49
+ CACHE_READABLE_PREFIX_LENGTH,
50
+ CLAUDE_DIR,
51
+ CLAUDE_SRC_DIR,
52
+ DEFAULT_BRANDING,
53
+ DEFAULT_DISPLAY_VERSION,
54
+ DEFAULT_PLUGIN_NAME,
55
+ DEFAULT_VERSION,
56
+ DIRS,
57
+ GITHUB_SOURCE,
58
+ HASH_PREFIX_LENGTH,
59
+ LOCAL_SKILLS_PATH,
60
+ MAX_CONFIG_FILE_SIZE,
61
+ MAX_JSON_NESTING_DEPTH,
62
+ MAX_MARKETPLACE_FILE_SIZE,
63
+ MAX_MARKETPLACE_PLUGINS,
64
+ MAX_PLUGIN_FILE_SIZE,
65
+ PLUGINS_SUBDIR,
66
+ PLUGIN_MANIFEST_DIR,
67
+ PLUGIN_MANIFEST_FILE,
68
+ PROJECT_ROOT,
69
+ SCHEMA_PATHS,
70
+ SKILLS_DIR_PATH,
71
+ SKILLS_MATRIX_PATH,
72
+ STACKS_FILE_PATH,
73
+ STANDARD_DIRS,
74
+ STANDARD_FILES,
75
+ YAML_FORMATTING,
76
+ yamlSchemaComment
77
+ } from "./chunk-LAPCUV4D.js";
78
+ import {
79
+ init_esm_shims
80
+ } from "./chunk-DHET7RCE.js";
81
+
82
+ // src/cli/lib/configuration/source-manager.ts
83
+ init_esm_shims();
84
+
85
+ // src/cli/lib/configuration/config.ts
86
+ init_esm_shims();
87
+ import path from "path";
88
+ import { stringify as stringifyYaml } from "yaml";
89
+
90
+ // src/cli/utils/yaml.ts
91
+ init_esm_shims();
92
+ import { parse as parseYaml } from "yaml";
93
+ async function safeLoadYamlFile(filePath, schema, maxSizeBytes = MAX_CONFIG_FILE_SIZE) {
94
+ try {
95
+ const content = await readFileSafe(filePath, maxSizeBytes);
96
+ const parsed = parseYaml(content);
97
+ const result = schema.safeParse(parsed);
98
+ if (!result.success) {
99
+ warn(`Invalid YAML at '${filePath}': ${result.error.message}`);
100
+ return null;
101
+ }
102
+ return result.data;
103
+ } catch (error) {
104
+ warn(`Failed to parse YAML at '${filePath}': ${error}`);
105
+ return null;
106
+ }
107
+ }
108
+
109
+ // src/cli/lib/configuration/config.ts
110
+ var DEFAULT_SOURCE = `${GITHUB_SOURCE.GITHUB_PREFIX}claude-collective/skills`;
111
+ var SOURCE_ENV_VAR = "CC_SOURCE";
112
+ var PROJECT_CONFIG_FILE = STANDARD_FILES.CONFIG_YAML;
113
+ function getProjectConfigPath(projectDir) {
114
+ return path.join(projectDir, CLAUDE_SRC_DIR, PROJECT_CONFIG_FILE);
115
+ }
116
+ async function loadProjectSourceConfig(projectDir) {
117
+ const srcConfigPath = getProjectConfigPath(projectDir);
118
+ const legacyConfigPath = path.join(projectDir, CLAUDE_DIR, STANDARD_FILES.CONFIG_YAML);
119
+ let configPath = srcConfigPath;
120
+ if (!await fileExists(srcConfigPath)) {
121
+ if (await fileExists(legacyConfigPath)) {
122
+ configPath = legacyConfigPath;
123
+ verbose(`Using legacy config location: ${legacyConfigPath}`);
124
+ } else {
125
+ verbose(`Project config not found at ${srcConfigPath} or ${legacyConfigPath}`);
126
+ return null;
127
+ }
128
+ }
129
+ const data = await safeLoadYamlFile(configPath, projectSourceConfigSchema);
130
+ if (!data) return null;
131
+ verbose(`Loaded project config from ${configPath}`);
132
+ return data;
133
+ }
134
+ async function saveProjectConfig(projectDir, config) {
135
+ const configPath = getProjectConfigPath(projectDir);
136
+ await ensureDir(path.join(projectDir, CLAUDE_SRC_DIR));
137
+ const schemaComment = `${yamlSchemaComment(SCHEMA_PATHS.projectSourceConfig)}
138
+ `;
139
+ const content = stringifyYaml(config, { lineWidth: YAML_FORMATTING.LINE_WIDTH_NONE });
140
+ await writeFile(configPath, `${schemaComment}${content}`);
141
+ verbose(`Saved project config to ${configPath}`);
142
+ }
143
+ async function resolveSource(flagValue, projectDir) {
144
+ const projectConfig = projectDir ? await loadProjectSourceConfig(projectDir) : null;
145
+ const marketplace = projectConfig?.marketplace;
146
+ if (flagValue !== void 0) {
147
+ if (flagValue === "" || flagValue.trim() === "") {
148
+ throw new Error(
149
+ "--source flag cannot be empty. Provide a valid source: a local directory path or a git repository URL (e.g., './my-skills' or 'https://github.com/user/repo')"
150
+ );
151
+ }
152
+ validateSourceFormat(flagValue.trim(), "--source");
153
+ verbose(`Source from --source flag: ${flagValue}`);
154
+ return { source: flagValue, sourceOrigin: "flag", marketplace };
155
+ }
156
+ const envValue = process.env[SOURCE_ENV_VAR];
157
+ if (envValue) {
158
+ const trimmed = envValue.trim();
159
+ if (trimmed === "") {
160
+ warn(`${SOURCE_ENV_VAR} is set but empty \u2014 ignoring and falling back to next source.`);
161
+ } else {
162
+ try {
163
+ validateSourceFormat(trimmed, SOURCE_ENV_VAR);
164
+ verbose(`Source from ${SOURCE_ENV_VAR} env var: ${trimmed}`);
165
+ return { source: trimmed, sourceOrigin: "env", marketplace };
166
+ } catch (error) {
167
+ const message = error instanceof Error ? error.message : String(error);
168
+ warn(
169
+ `${SOURCE_ENV_VAR} has an invalid value \u2014 ignoring and falling back to next source.
170
+ ${message}`
171
+ );
172
+ }
173
+ }
174
+ }
175
+ if (projectConfig?.source) {
176
+ verbose(`Source from project config: ${projectConfig.source}`);
177
+ return {
178
+ source: projectConfig.source,
179
+ sourceOrigin: "project",
180
+ marketplace
181
+ };
182
+ }
183
+ verbose(`Using default source: ${DEFAULT_SOURCE}`);
184
+ return { source: DEFAULT_SOURCE, sourceOrigin: "default", marketplace };
185
+ }
186
+ async function resolveAgentsSource(flagValue, projectDir) {
187
+ if (flagValue !== void 0) {
188
+ if (flagValue === "" || flagValue.trim() === "") {
189
+ throw new Error(
190
+ "--agent-source flag cannot be empty. Provide a valid source: a local directory path or a git repository URL (e.g., './my-agents' or 'https://github.com/user/repo')"
191
+ );
192
+ }
193
+ validateSourceFormat(flagValue.trim(), "--agent-source");
194
+ verbose(`Agents source from --agent-source flag: ${flagValue}`);
195
+ return { agentsSource: flagValue, agentsSourceOrigin: "flag" };
196
+ }
197
+ const projectConfig = projectDir ? await loadProjectSourceConfig(projectDir) : null;
198
+ if (projectConfig?.agents_source) {
199
+ verbose(`Agents source from project config: ${projectConfig.agents_source}`);
200
+ return {
201
+ agentsSource: projectConfig.agents_source,
202
+ agentsSourceOrigin: "project"
203
+ };
204
+ }
205
+ verbose("Using default agents source (local CLI)");
206
+ return { agentsSource: void 0, agentsSourceOrigin: "default" };
207
+ }
208
+ var PROJECT_ORIGIN_LABEL = "project config (.claude-src/config.yaml)";
209
+ function formatOrigin(type, origin) {
210
+ if (origin === "project") return PROJECT_ORIGIN_LABEL;
211
+ if (type === "source") {
212
+ switch (origin) {
213
+ case "flag":
214
+ return "--source flag";
215
+ case "env":
216
+ return `${SOURCE_ENV_VAR} environment variable`;
217
+ case "default":
218
+ return "default";
219
+ default:
220
+ break;
221
+ }
222
+ }
223
+ switch (origin) {
224
+ case "flag":
225
+ return "--agent-source flag";
226
+ case "default":
227
+ return "default (local CLI)";
228
+ default:
229
+ break;
230
+ }
231
+ return origin;
232
+ }
233
+ async function resolveAuthor(projectDir) {
234
+ const projectConfig = projectDir ? await loadProjectSourceConfig(projectDir) : null;
235
+ return projectConfig?.author;
236
+ }
237
+ async function resolveBranding(projectDir) {
238
+ const projectConfig = projectDir ? await loadProjectSourceConfig(projectDir) : null;
239
+ return {
240
+ name: projectConfig?.branding?.name ?? DEFAULT_BRANDING.NAME,
241
+ tagline: projectConfig?.branding?.tagline ?? DEFAULT_BRANDING.TAGLINE
242
+ };
243
+ }
244
+ async function resolveAllSources(projectDir) {
245
+ const projectConfig = projectDir ? await loadProjectSourceConfig(projectDir) : null;
246
+ const resolvedConfig = await resolveSource(void 0, projectDir);
247
+ const primary = {
248
+ name: "marketplace",
249
+ url: resolvedConfig.source,
250
+ description: "Primary skills marketplace"
251
+ };
252
+ const extras = [];
253
+ const seenNames = /* @__PURE__ */ new Set();
254
+ if (projectConfig?.sources) {
255
+ for (const source of projectConfig.sources) {
256
+ if (!seenNames.has(source.name)) {
257
+ seenNames.add(source.name);
258
+ extras.push(source);
259
+ }
260
+ }
261
+ }
262
+ return { primary, extras };
263
+ }
264
+ var REMOTE_PROTOCOLS = [
265
+ GITHUB_SOURCE.GITHUB_PREFIX,
266
+ // "github:"
267
+ GITHUB_SOURCE.GH_PREFIX,
268
+ // "gh:"
269
+ "gitlab:",
270
+ "bitbucket:",
271
+ "sourcehut:",
272
+ "https://",
273
+ "http://"
274
+ ];
275
+ var MIN_REMOTE_PATH_LENGTH = 3;
276
+ var MAX_SOURCE_LENGTH = 512;
277
+ var NULL_BYTE_PATTERN = /\0/;
278
+ var PATH_TRAVERSAL_PATTERN = /\.\./;
279
+ var UNC_PATH_PATTERN = /^(?:\/\/|\\\\)/;
280
+ var PRIVATE_IPV4_PATTERN = /^(?:127\.\d+\.\d+\.\d+|10\.\d+\.\d+\.\d+|172\.(?:1[6-9]|2\d|3[01])\.\d+\.\d+|192\.168\.\d+\.\d+|0\.0\.0\.0|169\.254\.\d+\.\d+)$/;
281
+ var PRIVATE_IPV6_PATTERN = /^\[(?:::1|::ffff:(?:127\.\d+\.\d+\.\d+|10\.\d+\.\d+\.\d+|192\.168\.\d+\.\d+)|fd[0-9a-f]{2}:.*|fe80:.*)\]$/i;
282
+ function validateSourceFormat(source, flagName) {
283
+ if (NULL_BYTE_PATTERN.test(source)) {
284
+ throw new Error(
285
+ `${flagName} contains invalid characters.
286
+
287
+ Source values must not contain null bytes.
288
+ Examples:
289
+ ${flagName} ./my-skills
290
+ ${flagName} github:user/repo`
291
+ );
292
+ }
293
+ if (source.length > MAX_SOURCE_LENGTH) {
294
+ throw new Error(
295
+ `${flagName} value is too long (${source.length} characters, max ${MAX_SOURCE_LENGTH}).
296
+
297
+ Provide a shorter source path or URL.
298
+ Examples:
299
+ ${flagName} ./my-skills
300
+ ${flagName} github:user/repo`
301
+ );
302
+ }
303
+ const matchedProtocol = REMOTE_PROTOCOLS.find((prefix) => source.startsWith(prefix));
304
+ if (matchedProtocol) {
305
+ validateRemoteSource(source, matchedProtocol, flagName);
306
+ } else {
307
+ validateLocalPath(source, flagName);
308
+ }
309
+ }
310
+ function validateRemoteSource(source, protocol, flagName) {
311
+ const pathAfterProtocol = source.slice(protocol.length).trim();
312
+ if (pathAfterProtocol.length < MIN_REMOTE_PATH_LENGTH) {
313
+ throw new Error(
314
+ `${flagName} has an incomplete URL: "${source}"
315
+
316
+ A repository path is required after the protocol prefix.
317
+ Examples:
318
+ ${flagName} github:user/repo
319
+ ${flagName} https://github.com/user/repo`
320
+ );
321
+ }
322
+ if (PATH_TRAVERSAL_PATTERN.test(pathAfterProtocol)) {
323
+ throw new Error(
324
+ `${flagName} contains path traversal in URL: "${source}"
325
+
326
+ Remote source URLs must not contain '..' sequences.
327
+ Examples:
328
+ ${flagName} github:user/repo
329
+ ${flagName} https://github.com/user/repo`
330
+ );
331
+ }
332
+ if (protocol === "https://" || protocol === "http://") {
333
+ validateHttpUrl(source, flagName);
334
+ }
335
+ if (protocol !== "https://" && protocol !== "http://") {
336
+ validateGitShorthand(source, pathAfterProtocol, flagName);
337
+ }
338
+ }
339
+ function validateHttpUrl(source, flagName) {
340
+ const afterProtocol = source.replace(/^https?:\/\//, "");
341
+ const hostnameWithPort = afterProtocol.split("/")[0] ?? "";
342
+ const hostname = hostnameWithPort.split(":")[0] ?? "";
343
+ const isBracketedIPv6 = hostnameWithPort.startsWith("[") && hostnameWithPort.includes("]");
344
+ if (!hostname || !hostname.includes(".") && hostname !== "localhost" && !isBracketedIPv6) {
345
+ throw new Error(
346
+ `${flagName} has an invalid URL: "${source}"
347
+
348
+ The URL must include a valid hostname.
349
+ Examples:
350
+ ${flagName} https://github.com/user/repo
351
+ ${flagName} https://gitlab.company.com/team/skills`
352
+ );
353
+ }
354
+ if (PRIVATE_IPV4_PATTERN.test(hostname) || PRIVATE_IPV6_PATTERN.test(hostnameWithPort)) {
355
+ throw new Error(
356
+ `${flagName} points to a private or reserved IP address: "${source}"
357
+
358
+ Source URLs must not target private network addresses.
359
+ Use a public hostname instead.
360
+ Examples:
361
+ ${flagName} https://github.com/user/repo
362
+ ${flagName} https://gitlab.company.com/team/skills`
363
+ );
364
+ }
365
+ }
366
+ function validateGitShorthand(source, repoPath, flagName) {
367
+ if (!repoPath.includes("/")) {
368
+ throw new Error(
369
+ `${flagName} has an invalid repository reference: "${source}"
370
+
371
+ Git shorthand sources require an owner/repo format.
372
+ Examples:
373
+ ${flagName} github:user/repo
374
+ ${flagName} gh:organization/skills`
375
+ );
376
+ }
377
+ }
378
+ function validateLocalPath(source, flagName) {
379
+ const CONTROL_CHAR_PATTERN = /[\x00-\x08\x0E-\x1F\x7F]/u;
380
+ if (CONTROL_CHAR_PATTERN.test(source)) {
381
+ throw new Error(
382
+ `${flagName} contains invalid characters: "${source}"
383
+
384
+ Source paths must not contain control characters.
385
+ Examples:
386
+ ${flagName} ./my-skills
387
+ ${flagName} /home/user/skills`
388
+ );
389
+ }
390
+ if (UNC_PATH_PATTERN.test(source)) {
391
+ throw new Error(
392
+ `${flagName} contains a UNC network path: "${source}"
393
+
394
+ Network paths (\\\\server\\share or //server/share) are not allowed for security reasons.
395
+ Use a local directory path or a remote URL instead.
396
+ Examples:
397
+ ${flagName} ./my-skills
398
+ ${flagName} /home/user/skills
399
+ ${flagName} https://github.com/user/repo`
400
+ );
401
+ }
402
+ }
403
+ function isLocalSource(source) {
404
+ if (source.startsWith("/") || source.startsWith(".")) {
405
+ return true;
406
+ }
407
+ const hasRemoteProtocol = REMOTE_PROTOCOLS.some((prefix) => source.startsWith(prefix));
408
+ if (!hasRemoteProtocol) {
409
+ if (source.includes("..") || source.includes("~")) {
410
+ throw new Error(
411
+ `Invalid source path: ${source}. Path traversal patterns like '..' and '~' are not allowed for security reasons. Use absolute paths or remote URLs instead (e.g., '/home/user/skills' or 'https://github.com/user/repo').`
412
+ );
413
+ }
414
+ }
415
+ return !hasRemoteProtocol;
416
+ }
417
+
418
+ // src/cli/lib/loading/source-fetcher.ts
419
+ init_esm_shims();
420
+ import { createHash as createHash2 } from "crypto";
421
+ import { downloadTemplate } from "giget";
422
+ import path24 from "path";
423
+
424
+ // src/cli/lib/configuration/index.ts
425
+ init_esm_shims();
426
+
427
+ // src/cli/lib/configuration/config-generator.ts
428
+ init_esm_shims();
429
+
430
+ // src/cli/lib/skills/index.ts
431
+ init_esm_shims();
432
+
433
+ // src/cli/lib/skills/skill-metadata.ts
434
+ init_esm_shims();
435
+ import path3 from "path";
436
+ import { sortBy } from "remeda";
437
+ import { parse as parseYaml2, stringify as stringifyYaml2 } from "yaml";
438
+
439
+ // src/cli/lib/versioning.ts
440
+ init_esm_shims();
441
+ import { createHash } from "crypto";
442
+ import path2 from "path";
443
+
444
+ // src/cli/lib/metadata-keys.ts
445
+ init_esm_shims();
446
+ var METADATA_KEYS = {
447
+ CLI_NAME: "cli_name",
448
+ CLI_DESCRIPTION: "cli_description",
449
+ CATEGORY: "category",
450
+ FORKED_FROM: "forked_from",
451
+ CONTENT_HASH: "content_hash",
452
+ USAGE_GUIDANCE: "usage_guidance"
453
+ };
454
+ var IMPORT_DEFAULTS = {
455
+ CATEGORY: "imported",
456
+ AUTHOR: "@imported"
457
+ };
458
+ var LOCAL_DEFAULTS = {
459
+ CATEGORY: "local",
460
+ AUTHOR: "@local"
461
+ };
462
+ var SKILL_CONTENT_FILES = [STANDARD_FILES.SKILL_MD, STANDARD_FILES.REFERENCE_MD];
463
+ var SKILL_CONTENT_DIRS = [STANDARD_DIRS.EXAMPLES, STANDARD_DIRS.SCRIPTS];
464
+
465
+ // src/cli/lib/versioning.ts
466
+ function getCurrentDate() {
467
+ return (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
468
+ }
469
+ function computeStringHash(content) {
470
+ const hash = createHash("sha256");
471
+ hash.update(content);
472
+ return hash.digest("hex").slice(0, HASH_PREFIX_LENGTH);
473
+ }
474
+ async function computeFileHash(filePath) {
475
+ const content = await readFile(filePath);
476
+ return computeStringHash(content);
477
+ }
478
+ async function computeSkillFolderHash(skillPath) {
479
+ const contents = [];
480
+ for (const fileName of SKILL_CONTENT_FILES) {
481
+ const filePath = path2.join(skillPath, fileName);
482
+ if (await fileExists(filePath)) {
483
+ const content = await readFile(filePath);
484
+ contents.push(`${fileName}:${content}`);
485
+ }
486
+ }
487
+ for (const dirName of SKILL_CONTENT_DIRS) {
488
+ const dirPath = path2.join(skillPath, dirName);
489
+ if (await fileExists(dirPath)) {
490
+ const files = await glob("**/*", dirPath);
491
+ for (const file of files.sort()) {
492
+ const filePath = path2.join(dirPath, file);
493
+ const content = await readFile(filePath);
494
+ contents.push(`${dirName}/${file}:${content}`);
495
+ }
496
+ }
497
+ }
498
+ const combined = contents.join("\n---\n");
499
+ return computeStringHash(combined);
500
+ }
501
+ var CONTENT_HASH_FILE = ".content-hash";
502
+ function parseMajorVersion(version) {
503
+ const match = version.match(/^(\d+)\./);
504
+ return match ? parseInt(match[1], 10) : 1;
505
+ }
506
+ function bumpMajorVersion(version) {
507
+ const major = parseMajorVersion(version);
508
+ return `${major + 1}.0.0`;
509
+ }
510
+ async function readExistingPluginManifest(pluginDir, getManifestPath) {
511
+ const manifestPath = getManifestPath(pluginDir);
512
+ if (!await fileExists(manifestPath)) {
513
+ return null;
514
+ }
515
+ try {
516
+ const content = await readFileSafe(manifestPath, MAX_PLUGIN_FILE_SIZE);
517
+ const manifest = pluginManifestSchema.parse(JSON.parse(content));
518
+ const hashFilePath = manifestPath.replace(STANDARD_FILES.PLUGIN_JSON, CONTENT_HASH_FILE);
519
+ let contentHash;
520
+ if (await fileExists(hashFilePath)) {
521
+ contentHash = (await readFile(hashFilePath)).trim();
522
+ }
523
+ return {
524
+ version: manifest.version ?? DEFAULT_VERSION,
525
+ contentHash
526
+ };
527
+ } catch (error) {
528
+ warn(`Failed to read plugin manifest at '${manifestPath}': ${getErrorMessage(error)}`);
529
+ return null;
530
+ }
531
+ }
532
+ async function determinePluginVersion(newHash, pluginDir, getManifestPath) {
533
+ const existing = await readExistingPluginManifest(pluginDir, getManifestPath);
534
+ if (!existing) {
535
+ return {
536
+ version: DEFAULT_VERSION,
537
+ contentHash: newHash
538
+ };
539
+ }
540
+ if (existing.contentHash !== newHash) {
541
+ return {
542
+ version: bumpMajorVersion(existing.version),
543
+ contentHash: newHash
544
+ };
545
+ }
546
+ return {
547
+ version: existing.version,
548
+ contentHash: newHash
549
+ };
550
+ }
551
+ async function writeContentHash(pluginDir, contentHash, getManifestPath) {
552
+ const hashFilePath = getManifestPath(pluginDir).replace(
553
+ STANDARD_FILES.PLUGIN_JSON,
554
+ CONTENT_HASH_FILE
555
+ );
556
+ await writeFile(hashFilePath, contentHash);
557
+ }
558
+
559
+ // src/cli/lib/skills/skill-metadata.ts
560
+ async function readForkedFromMetadata(skillDir) {
561
+ const metadataPath = path3.join(skillDir, STANDARD_FILES.METADATA_YAML);
562
+ if (!await fileExists(metadataPath)) {
563
+ return null;
564
+ }
565
+ const content = await readFile(metadataPath);
566
+ const result = localSkillMetadataSchema.safeParse(parseYaml2(content));
567
+ if (!result.success) {
568
+ warn(`Invalid metadata.yaml at ${metadataPath}: ${formatZodErrors(result.error.issues)}`);
569
+ return null;
570
+ }
571
+ return result.data.forked_from ?? null;
572
+ }
573
+ async function getLocalSkillsWithMetadata(projectDir) {
574
+ const localSkillsPath = path3.join(projectDir, LOCAL_SKILLS_PATH);
575
+ const result = /* @__PURE__ */ new Map();
576
+ if (!await fileExists(localSkillsPath)) {
577
+ return result;
578
+ }
579
+ const skillDirs = await listDirectories(localSkillsPath);
580
+ for (const dirName of skillDirs) {
581
+ const skillDir = path3.join(localSkillsPath, dirName);
582
+ const forkedFrom = await readForkedFromMetadata(skillDir);
583
+ const skillId = forkedFrom?.skill_id ?? dirName;
584
+ result.set(skillId, { dirName, forkedFrom });
585
+ }
586
+ return result;
587
+ }
588
+ async function computeSourceHash(sourcePath, skillPath) {
589
+ const skillMdPath = path3.join(sourcePath, "src", skillPath, STANDARD_FILES.SKILL_MD);
590
+ if (!await fileExists(skillMdPath)) {
591
+ return null;
592
+ }
593
+ return computeFileHash(skillMdPath);
594
+ }
595
+ async function compareLocalSkillsWithSource(projectDir, sourcePath, sourceSkills) {
596
+ const results = [];
597
+ const localSkills = await getLocalSkillsWithMetadata(projectDir);
598
+ for (const [skillId, { dirName, forkedFrom }] of localSkills) {
599
+ if (!forkedFrom) {
600
+ results.push({
601
+ id: skillId,
602
+ localHash: null,
603
+ sourceHash: null,
604
+ status: "local-only",
605
+ dirName
606
+ });
607
+ continue;
608
+ }
609
+ const localHash = forkedFrom.content_hash;
610
+ const sourceSkill = sourceSkills[forkedFrom.skill_id];
611
+ if (!sourceSkill) {
612
+ results.push({
613
+ id: forkedFrom.skill_id,
614
+ localHash,
615
+ sourceHash: null,
616
+ status: "local-only",
617
+ dirName
618
+ });
619
+ continue;
620
+ }
621
+ const sourceHash = await computeSourceHash(sourcePath, sourceSkill.path);
622
+ if (sourceHash === null) {
623
+ results.push({
624
+ id: forkedFrom.skill_id,
625
+ localHash,
626
+ sourceHash: null,
627
+ status: "local-only",
628
+ dirName
629
+ });
630
+ continue;
631
+ }
632
+ const status = localHash === sourceHash ? "current" : "outdated";
633
+ results.push({
634
+ id: forkedFrom.skill_id,
635
+ localHash,
636
+ sourceHash,
637
+ status,
638
+ dirName,
639
+ sourcePath: sourceSkill.path
640
+ });
641
+ }
642
+ return sortBy(results, (r) => r.id);
643
+ }
644
+ async function injectForkedFromMetadata(destPath, skillId, contentHash) {
645
+ const metadataPath = path3.join(destPath, STANDARD_FILES.METADATA_YAML);
646
+ const rawContent = await readFile(metadataPath);
647
+ const lines = rawContent.split("\n");
648
+ let yamlContent = rawContent;
649
+ if (lines[0]?.startsWith("# yaml-language-server:")) {
650
+ yamlContent = lines.slice(1).join("\n");
651
+ }
652
+ const parseResult = localSkillMetadataSchema.safeParse(parseYaml2(yamlContent));
653
+ if (!parseResult.success) {
654
+ warn(`Malformed metadata.yaml at '${metadataPath}' \u2014 existing fields may be lost`);
655
+ }
656
+ const metadata = parseResult.success ? parseResult.data : { forked_from: void 0 };
657
+ metadata.forked_from = {
658
+ skill_id: skillId,
659
+ content_hash: contentHash,
660
+ date: getCurrentDate()
661
+ };
662
+ const schemaComment = `${yamlSchemaComment(SCHEMA_PATHS.metadata)}
663
+ `;
664
+ const newYamlContent = stringifyYaml2(metadata, { lineWidth: YAML_FORMATTING.LINE_WIDTH_NONE });
665
+ await writeFile(metadataPath, `${schemaComment}${newYamlContent}`);
666
+ }
667
+
668
+ // src/cli/lib/skills/skill-copier.ts
669
+ init_esm_shims();
670
+ import path4 from "path";
671
+ var NULL_BYTE_PATTERN2 = /\0/;
672
+ function validateSkillPath(resolvedPath, expectedParent, skillPath) {
673
+ if (NULL_BYTE_PATTERN2.test(skillPath)) {
674
+ throw new Error(`Invalid skill path: '${skillPath}' contains null bytes`);
675
+ }
676
+ const normalizedResolved = path4.resolve(resolvedPath);
677
+ const normalizedParent = path4.resolve(expectedParent);
678
+ if (!normalizedResolved.startsWith(normalizedParent + path4.sep) && normalizedResolved !== normalizedParent) {
679
+ throw new Error(
680
+ `Invalid skill path: '${skillPath}' escapes expected directory '${normalizedParent}'`
681
+ );
682
+ }
683
+ }
684
+ function resolveSkillPath(basePath, skillPath) {
685
+ const resolved = path4.join(basePath, skillPath);
686
+ validateSkillPath(resolved, basePath, skillPath);
687
+ return resolved;
688
+ }
689
+ async function generateSkillHash(skillSourcePath) {
690
+ const skillMdPath = path4.join(skillSourcePath, STANDARD_FILES.SKILL_MD);
691
+ return computeFileHash(skillMdPath);
692
+ }
693
+ function getSkillSourcePathFromSource(skill, sourceResult) {
694
+ const srcDir = path4.join(sourceResult.sourcePath, "src");
695
+ return resolveSkillPath(srcDir, skill.path);
696
+ }
697
+ function getFlattenedSkillDestPath(skill, localSkillsDir) {
698
+ return resolveSkillPath(localSkillsDir, skill.id);
699
+ }
700
+ async function copySkillToLocalFlattened(skill, localSkillsDir, sourceResult) {
701
+ const sourcePath = getSkillSourcePathFromSource(skill, sourceResult);
702
+ const destPath = getFlattenedSkillDestPath(skill, localSkillsDir);
703
+ const contentHash = await generateSkillHash(sourcePath);
704
+ await ensureDir(path4.dirname(destPath));
705
+ await copy(sourcePath, destPath);
706
+ await injectForkedFromMetadata(destPath, skill.id, contentHash);
707
+ return {
708
+ skillId: skill.id,
709
+ contentHash,
710
+ sourcePath,
711
+ destPath
712
+ };
713
+ }
714
+ async function copySkillsToLocalFlattened(selectedSkillIds, localSkillsDir, matrix, sourceResult, sourceSelections) {
715
+ const results = await Promise.all(
716
+ selectedSkillIds.map(async (skillId) => {
717
+ const skill = matrix.skills[skillId];
718
+ if (!skill) {
719
+ warn(`Skill not found in matrix: '${skillId}'`);
720
+ return null;
721
+ }
722
+ const selectedSource = sourceSelections?.[skillId];
723
+ const userSelectedRemote = selectedSource && selectedSource !== "local";
724
+ if (skill.local && skill.localPath && !userSelectedRemote) {
725
+ const localSkillPath = path4.join(process.cwd(), skill.localPath);
726
+ const contentHash = await generateSkillHash(localSkillPath);
727
+ return {
728
+ skillId: skill.id,
729
+ sourcePath: skill.localPath,
730
+ destPath: skill.localPath,
731
+ contentHash,
732
+ local: true
733
+ };
734
+ }
735
+ return copySkillToLocalFlattened(skill, localSkillsDir, sourceResult);
736
+ })
737
+ );
738
+ return results.filter((r) => r !== null);
739
+ }
740
+
741
+ // src/cli/lib/skills/skill-agent-mappings.ts
742
+ init_esm_shims();
743
+
744
+ // src/cli/lib/loading/index.ts
745
+ init_esm_shims();
746
+
747
+ // src/cli/lib/loading/loader.ts
748
+ init_esm_shims();
749
+ import { parse as parseYaml3 } from "yaml";
750
+ import path5 from "path";
751
+ import { unique } from "remeda";
752
+ var FRONTMATTER_REGEX = /^---\n([\s\S]*?)\n---/;
753
+ function parseFrontmatter(content, filePath) {
754
+ const match = content.match(FRONTMATTER_REGEX);
755
+ if (!match) return null;
756
+ const yamlContent = match[1];
757
+ const parsed = skillFrontmatterLoaderSchema.safeParse(parseYaml3(yamlContent));
758
+ if (!parsed.success) {
759
+ const location = filePath ?? "unknown file";
760
+ warn(`Invalid SKILL.md frontmatter in '${location}': ${formatZodErrors(parsed.error.issues)}`);
761
+ return null;
762
+ }
763
+ return parsed.data;
764
+ }
765
+ async function loadAllAgents(projectRoot) {
766
+ const agents = {};
767
+ const agentSourcesDir = path5.join(projectRoot, DIRS.agents);
768
+ const files = await glob("**/agent.yaml", agentSourcesDir);
769
+ for (const file of files) {
770
+ const fullPath = path5.join(agentSourcesDir, file);
771
+ try {
772
+ const content = await readFile(fullPath);
773
+ const config = agentYamlConfigSchema.parse(parseYaml3(content));
774
+ const agentPath = path5.dirname(file);
775
+ agents[config.id] = {
776
+ title: config.title,
777
+ description: config.description,
778
+ model: config.model,
779
+ tools: config.tools,
780
+ path: agentPath,
781
+ sourceRoot: projectRoot
782
+ };
783
+ verbose(`Loaded agent: ${config.id} from ${file}`);
784
+ } catch (error) {
785
+ warn(`Skipping invalid agent.yaml at '${fullPath}': ${getErrorMessage(error)}`);
786
+ }
787
+ }
788
+ return agents;
789
+ }
790
+ async function loadProjectAgents(projectRoot) {
791
+ const agents = {};
792
+ const projectAgentsDir = path5.join(projectRoot, CLAUDE_SRC_DIR, "agents");
793
+ if (!await directoryExists(projectAgentsDir)) {
794
+ verbose(`No project agents directory at ${projectAgentsDir}`);
795
+ return agents;
796
+ }
797
+ const files = await glob("**/agent.yaml", projectAgentsDir);
798
+ for (const file of files) {
799
+ const fullPath = path5.join(projectAgentsDir, file);
800
+ try {
801
+ const content = await readFile(fullPath);
802
+ const config = agentYamlConfigSchema.parse(parseYaml3(content));
803
+ const agentPath = path5.dirname(file);
804
+ agents[config.id] = {
805
+ title: config.title,
806
+ description: config.description,
807
+ model: config.model,
808
+ tools: config.tools,
809
+ path: agentPath,
810
+ sourceRoot: projectRoot,
811
+ agentBaseDir: `${CLAUDE_SRC_DIR}/agents`
812
+ // Project agents are in .claude-src/agents/
813
+ };
814
+ verbose(`Loaded project agent: ${config.id} from ${file}`);
815
+ } catch (error) {
816
+ warn(`Skipping invalid agent.yaml at '${fullPath}': ${getErrorMessage(error)}`);
817
+ }
818
+ }
819
+ return agents;
820
+ }
821
+ async function buildIdToDirectoryPathMap(skillsDir) {
822
+ const map = {};
823
+ const files = await glob("**/SKILL.md", skillsDir);
824
+ for (const file of files) {
825
+ const fullPath = path5.join(skillsDir, file);
826
+ const content = await readFile(fullPath);
827
+ const frontmatter = parseFrontmatter(content, fullPath);
828
+ if (frontmatter?.name) {
829
+ const directoryPath = file.replace("/SKILL.md", "");
830
+ map[frontmatter.name] = directoryPath;
831
+ map[directoryPath] = directoryPath;
832
+ }
833
+ }
834
+ return map;
835
+ }
836
+ async function loadSkillsByIds(skillIds, projectRoot) {
837
+ const skills = {};
838
+ const skillsDir = path5.join(projectRoot, DIRS.skills);
839
+ const idToDirectoryPath = await buildIdToDirectoryPathMap(skillsDir);
840
+ const allSkillIds = Object.keys(idToDirectoryPath);
841
+ const expandedSkillIds = [];
842
+ for (const { id: skillId } of skillIds) {
843
+ if (idToDirectoryPath[skillId]) {
844
+ expandedSkillIds.push(skillId);
845
+ } else {
846
+ const childSkills = allSkillIds.filter((id) => {
847
+ const dirPath = idToDirectoryPath[id];
848
+ return dirPath.startsWith(`${skillId}/`);
849
+ });
850
+ if (childSkills.length > 0) {
851
+ expandedSkillIds.push(...childSkills);
852
+ verbose(`Expanded directory '${skillId}' to ${childSkills.length} skills`);
853
+ } else {
854
+ warn(`Unknown skill reference '${skillId}'`);
855
+ }
856
+ }
857
+ }
858
+ const uniqueSkillIds = unique(expandedSkillIds);
859
+ for (const skillId of uniqueSkillIds) {
860
+ const directoryPath = idToDirectoryPath[skillId];
861
+ if (!directoryPath) {
862
+ warn(`Could not find skill '${skillId}': no matching skill found`);
863
+ continue;
864
+ }
865
+ const skillPath = path5.join(skillsDir, directoryPath);
866
+ const skillMdPath = path5.join(skillPath, STANDARD_FILES.SKILL_MD);
867
+ try {
868
+ const content = await readFile(skillMdPath);
869
+ const frontmatter = parseFrontmatter(content, skillMdPath);
870
+ if (!frontmatter) {
871
+ warn(`Skipping '${skillId}': missing or invalid frontmatter`);
872
+ continue;
873
+ }
874
+ const canonicalId = frontmatter.name;
875
+ const skillDef = {
876
+ id: canonicalId,
877
+ path: `${DIRS.skills}/${directoryPath}/`,
878
+ description: frontmatter.description
879
+ };
880
+ skills[canonicalId] = skillDef;
881
+ if (directoryPath !== canonicalId) {
882
+ skills[directoryPath] = skillDef;
883
+ }
884
+ verbose(`Loaded skill: ${canonicalId} (from ${directoryPath})`);
885
+ } catch (error) {
886
+ warn(`Could not load skill '${skillId}': ${error}`);
887
+ }
888
+ }
889
+ return skills;
890
+ }
891
+ async function loadPluginSkills(pluginDir) {
892
+ const skills = {};
893
+ const pluginSkillsDir = path5.join(pluginDir, "skills");
894
+ if (!await directoryExists(pluginSkillsDir)) {
895
+ return skills;
896
+ }
897
+ const files = await glob("**/SKILL.md", pluginSkillsDir);
898
+ for (const file of files) {
899
+ const fullPath = path5.join(pluginSkillsDir, file);
900
+ const content = await readFile(fullPath);
901
+ const frontmatter = parseFrontmatter(content, fullPath);
902
+ if (!frontmatter) {
903
+ warn(`Skipping '${file}': missing or invalid frontmatter`);
904
+ continue;
905
+ }
906
+ const folderPath = file.replace("/SKILL.md", "");
907
+ const skillPath = `skills/${folderPath}/`;
908
+ const skillId = frontmatter.name;
909
+ skills[skillId] = {
910
+ id: skillId,
911
+ path: skillPath,
912
+ description: frontmatter.description
913
+ };
914
+ verbose(`Loaded plugin skill: ${skillId} from ${file}`);
915
+ }
916
+ return skills;
917
+ }
918
+
919
+ // src/cli/lib/loading/source-loader.ts
920
+ init_esm_shims();
921
+ import path19 from "path";
922
+
923
+ // src/cli/lib/matrix/index.ts
924
+ init_esm_shims();
925
+
926
+ // src/cli/lib/matrix/matrix-loader.ts
927
+ init_esm_shims();
928
+ import { parse as parseYaml4 } from "yaml";
929
+ import path6 from "path";
930
+ import { z } from "zod";
931
+ var rawMetadataSchema = z.object({
932
+ category: categoryPathSchema,
933
+ category_exclusive: z.boolean().optional(),
934
+ author: z.string(),
935
+ version: z.coerce.string(),
936
+ cli_name: z.string().optional(),
937
+ cli_description: z.string().optional(),
938
+ usage_guidance: z.string().optional(),
939
+ tags: z.array(z.string()).optional(),
940
+ // Lenient: accepts display names and skill IDs from YAML, resolved to canonical IDs during matrix merge
941
+ compatible_with: z.array(z.string()).optional(),
942
+ conflicts_with: z.array(z.string()).optional(),
943
+ requires: z.array(z.string()).optional(),
944
+ requires_setup: z.array(z.string()).optional(),
945
+ provides_setup_for: z.array(z.string()).optional()
946
+ });
947
+ async function loadSkillsMatrix(configPath) {
948
+ const content = await readFile(configPath);
949
+ const raw = parseYaml4(content);
950
+ const result = skillsMatrixConfigSchema.safeParse(raw);
951
+ if (!result.success) {
952
+ throw new Error(
953
+ `Invalid skills matrix at '${configPath}': ${formatZodErrors(result.error.issues)}`
954
+ );
955
+ }
956
+ verbose(`Loaded skills matrix: ${configPath}`);
957
+ return result.data;
958
+ }
959
+ async function extractAllSkills(skillsDir) {
960
+ const skills = [];
961
+ const metadataFiles = await glob(`**/${STANDARD_FILES.METADATA_YAML}`, skillsDir);
962
+ for (const metadataFile of metadataFiles) {
963
+ const skillDir = path6.dirname(metadataFile);
964
+ const skillMdPath = path6.join(skillsDir, skillDir, STANDARD_FILES.SKILL_MD);
965
+ const metadataPath = path6.join(skillsDir, metadataFile);
966
+ if (!await fileExists(skillMdPath)) {
967
+ verbose(`Skipping ${metadataFile}: No ${STANDARD_FILES.SKILL_MD} found`);
968
+ continue;
969
+ }
970
+ const metadataContent = await readFile(metadataPath);
971
+ const rawMetadata = parseYaml4(metadataContent);
972
+ const metadataResult = rawMetadataSchema.safeParse(rawMetadata);
973
+ if (!metadataResult.success) {
974
+ warn(
975
+ `Skipping '${metadataFile}': invalid metadata.yaml \u2014 ${formatZodErrors(metadataResult.error.issues)}`
976
+ );
977
+ continue;
978
+ }
979
+ const metadata = metadataResult.data;
980
+ const skillMdContent = await readFile(skillMdPath);
981
+ const frontmatter = parseFrontmatter(skillMdContent, skillMdPath);
982
+ if (!frontmatter) {
983
+ verbose(`Skipping ${metadataFile}: Invalid SKILL.md frontmatter`);
984
+ continue;
985
+ }
986
+ if (!metadata.cli_name) {
987
+ throw new Error(
988
+ `Skill at ${metadataFile} is missing required '${METADATA_KEYS.CLI_NAME}' field in metadata.yaml`
989
+ );
990
+ }
991
+ const skillId = frontmatter.name;
992
+ const extracted = {
993
+ id: skillId,
994
+ directoryPath: skillDir,
995
+ description: metadata.cli_description || frontmatter.description,
996
+ usageGuidance: metadata.usage_guidance,
997
+ category: metadata.category,
998
+ categoryExclusive: metadata.category_exclusive ?? true,
999
+ author: metadata.author,
1000
+ tags: metadata.tags ?? [],
1001
+ compatibleWith: metadata.compatible_with ?? [],
1002
+ conflictsWith: metadata.conflicts_with ?? [],
1003
+ requires: metadata.requires ?? [],
1004
+ requiresSetup: metadata.requires_setup ?? [],
1005
+ providesSetupFor: metadata.provides_setup_for ?? [],
1006
+ path: `skills/${skillDir}/`
1007
+ };
1008
+ skills.push(extracted);
1009
+ verbose(`Extracted skill: ${skillId}`);
1010
+ }
1011
+ return skills;
1012
+ }
1013
+ function buildReverseDisplayNames(displayNameToId) {
1014
+ const reverse = {};
1015
+ for (const [name, fullId] of Object.entries(displayNameToId)) {
1016
+ const nameResult = skillDisplayNameSchema.safeParse(name);
1017
+ const idResult = skillIdSchema.safeParse(fullId);
1018
+ if (nameResult.success && idResult.success) {
1019
+ reverse[idResult.data] = nameResult.data;
1020
+ } else {
1021
+ warn(
1022
+ `Invalid skill alias mapping: '${name}' -> '${fullId}'${!nameResult.success ? ` (invalid display name: ${nameResult.error.message})` : ""}${!idResult.success ? ` (invalid skill ID: ${idResult.error.message})` : ""}`
1023
+ );
1024
+ }
1025
+ }
1026
+ return reverse;
1027
+ }
1028
+ function buildAliasTargetToSkillIdMap(displayNameToId, skills) {
1029
+ const map = {};
1030
+ for (const skill of skills) {
1031
+ const parts = skill.id.split("/");
1032
+ const shortForm = parts[parts.length - 1];
1033
+ if (shortForm && shortForm !== skill.id) {
1034
+ map[shortForm] = skill.id;
1035
+ }
1036
+ if (skill.directoryPath && skill.directoryPath !== skill.id) {
1037
+ map[skill.directoryPath] = skill.id;
1038
+ }
1039
+ }
1040
+ const aliasTargets = new Set(Object.values(displayNameToId));
1041
+ for (const skill of skills) {
1042
+ let slashIdx = skill.id.indexOf("/");
1043
+ while (slashIdx !== -1) {
1044
+ const suffix = skill.id.slice(slashIdx + 1);
1045
+ if (suffix && aliasTargets.has(suffix) && suffix !== skill.id) {
1046
+ map[suffix] = skill.id;
1047
+ }
1048
+ slashIdx = skill.id.indexOf("/", slashIdx + 1);
1049
+ }
1050
+ }
1051
+ return map;
1052
+ }
1053
+ function buildDirectoryPathToIdMap(skills) {
1054
+ const map = {};
1055
+ for (const skill of skills) {
1056
+ if (skill.directoryPath && skill.directoryPath !== skill.id) {
1057
+ map[skill.directoryPath] = skill.id;
1058
+ }
1059
+ }
1060
+ return map;
1061
+ }
1062
+ function resolveToCanonicalId(nameOrId, displayNameToId, directoryPathToId = {}, aliasTargetToSkillId = {}, context) {
1063
+ const displayNameResult = displayNameToId[nameOrId];
1064
+ if (displayNameResult) {
1065
+ return displayNameResult;
1066
+ }
1067
+ if (directoryPathToId[nameOrId]) {
1068
+ return directoryPathToId[nameOrId];
1069
+ }
1070
+ if (aliasTargetToSkillId[nameOrId]) {
1071
+ return aliasTargetToSkillId[nameOrId];
1072
+ }
1073
+ if (context) {
1074
+ verbose(`Unresolved ID '${nameOrId}' in ${context} \u2014 passing through as-is`);
1075
+ }
1076
+ return nameOrId;
1077
+ }
1078
+ async function mergeMatrixWithSkills(matrix, skills) {
1079
+ const displayNameToId = matrix.skill_aliases;
1080
+ const displayNames = buildReverseDisplayNames(displayNameToId);
1081
+ const directoryPathToId = buildDirectoryPathToIdMap(skills);
1082
+ const aliasTargetToSkillId = buildAliasTargetToSkillIdMap(displayNameToId, skills);
1083
+ const resolvedSkills = {};
1084
+ for (const skill of skills) {
1085
+ const resolved = buildResolvedSkill(
1086
+ skill,
1087
+ matrix,
1088
+ displayNameToId,
1089
+ displayNames,
1090
+ directoryPathToId,
1091
+ aliasTargetToSkillId
1092
+ );
1093
+ resolvedSkills[skill.id] = resolved;
1094
+ }
1095
+ const suggestedStacks = resolveSuggestedStacks();
1096
+ const merged = {
1097
+ version: matrix.version,
1098
+ categories: matrix.categories,
1099
+ skills: resolvedSkills,
1100
+ suggestedStacks,
1101
+ displayNameToId,
1102
+ displayNames,
1103
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
1104
+ };
1105
+ return merged;
1106
+ }
1107
+ function resolveConflicts(skillId, metadataConflicts, conflictRules, resolve) {
1108
+ const conflicts = [];
1109
+ for (const conflictRef of metadataConflicts) {
1110
+ conflicts.push({
1111
+ skillId: resolve(conflictRef, "conflictsWith"),
1112
+ reason: "Defined in skill metadata"
1113
+ });
1114
+ }
1115
+ for (const rule of conflictRules) {
1116
+ const resolved = rule.skills.map((id) => resolve(id, "conflicts"));
1117
+ if (!resolved.includes(skillId)) continue;
1118
+ for (const other of resolved) {
1119
+ if (other !== skillId && !conflicts.some((c) => c.skillId === other)) {
1120
+ conflicts.push({ skillId: other, reason: rule.reason });
1121
+ }
1122
+ }
1123
+ }
1124
+ return conflicts;
1125
+ }
1126
+ function resolveRecommends(skillId, compatibleWith, recommendRules, resolve) {
1127
+ const recommends = [];
1128
+ for (const compatRef of compatibleWith) {
1129
+ recommends.push({
1130
+ skillId: resolve(compatRef, "compatibleWith"),
1131
+ reason: "Compatible with this skill"
1132
+ });
1133
+ }
1134
+ for (const rule of recommendRules) {
1135
+ if (resolve(rule.when, "recommends.when") !== skillId) continue;
1136
+ for (const suggested of rule.suggest) {
1137
+ const canonicalId = resolve(suggested, "recommends.suggest");
1138
+ if (!recommends.some((r) => r.skillId === canonicalId)) {
1139
+ recommends.push({ skillId: canonicalId, reason: rule.reason });
1140
+ }
1141
+ }
1142
+ }
1143
+ return recommends;
1144
+ }
1145
+ function resolveRequirements(skillId, metadataRequires, requireRules, resolve) {
1146
+ const requires = [];
1147
+ if (metadataRequires.length > 0) {
1148
+ requires.push({
1149
+ skillIds: metadataRequires.map((id) => resolve(id, "requires")),
1150
+ needsAny: false,
1151
+ reason: "Defined in skill metadata"
1152
+ });
1153
+ }
1154
+ for (const rule of requireRules) {
1155
+ if (resolve(rule.skill, "requires.skill") !== skillId) continue;
1156
+ requires.push({
1157
+ skillIds: rule.needs.map((id) => resolve(id, "requires.needs")),
1158
+ needsAny: rule.needs_any ?? false,
1159
+ reason: rule.reason
1160
+ });
1161
+ }
1162
+ return requires;
1163
+ }
1164
+ function resolveAlternatives(skillId, alternativeGroups, resolve) {
1165
+ const alternatives = [];
1166
+ for (const group of alternativeGroups) {
1167
+ const resolved = group.skills.map((id) => resolve(id, "alternatives"));
1168
+ if (!resolved.includes(skillId)) continue;
1169
+ for (const alt of resolved) {
1170
+ if (alt !== skillId) {
1171
+ alternatives.push({ skillId: alt, purpose: group.purpose });
1172
+ }
1173
+ }
1174
+ }
1175
+ return alternatives;
1176
+ }
1177
+ function resolveDiscourages(skillId, discourageRules, resolve) {
1178
+ if (!discourageRules) return [];
1179
+ const discourages = [];
1180
+ for (const rule of discourageRules) {
1181
+ const resolved = rule.skills.map((id) => resolve(id, "discourages"));
1182
+ if (!resolved.includes(skillId)) continue;
1183
+ for (const other of resolved) {
1184
+ if (other !== skillId && !discourages.some((d) => d.skillId === other)) {
1185
+ discourages.push({ skillId: other, reason: rule.reason });
1186
+ }
1187
+ }
1188
+ }
1189
+ return discourages;
1190
+ }
1191
+ function buildResolvedSkill(skill, matrix, displayNameToId, displayNames, directoryPathToId, aliasTargetToSkillId) {
1192
+ const resolve = (id, context) => resolveToCanonicalId(
1193
+ id,
1194
+ displayNameToId,
1195
+ directoryPathToId,
1196
+ aliasTargetToSkillId,
1197
+ context ? `${skill.id} ${context}` : void 0
1198
+ );
1199
+ const { relationships } = matrix;
1200
+ return {
1201
+ id: skill.id,
1202
+ displayName: displayNames[skill.id],
1203
+ description: skill.description,
1204
+ usageGuidance: skill.usageGuidance,
1205
+ category: skill.category,
1206
+ categoryExclusive: skill.categoryExclusive,
1207
+ tags: skill.tags,
1208
+ author: skill.author,
1209
+ conflictsWith: resolveConflicts(
1210
+ skill.id,
1211
+ skill.conflictsWith,
1212
+ relationships.conflicts,
1213
+ resolve
1214
+ ),
1215
+ recommends: resolveRecommends(
1216
+ skill.id,
1217
+ skill.compatibleWith,
1218
+ relationships.recommends,
1219
+ resolve
1220
+ ),
1221
+ requires: resolveRequirements(skill.id, skill.requires, relationships.requires, resolve),
1222
+ alternatives: resolveAlternatives(skill.id, relationships.alternatives, resolve),
1223
+ discourages: resolveDiscourages(skill.id, relationships.discourages, resolve),
1224
+ compatibleWith: skill.compatibleWith.map((id) => resolve(id, "compatibleWith")),
1225
+ requiresSetup: skill.requiresSetup.map((id) => resolve(id, "requiresSetup")),
1226
+ providesSetupFor: skill.providesSetupFor.map((id) => resolve(id, "providesSetupFor")),
1227
+ path: skill.path
1228
+ };
1229
+ }
1230
+ function resolveSuggestedStacks() {
1231
+ return [];
1232
+ }
1233
+
1234
+ // src/cli/lib/matrix/matrix-resolver.ts
1235
+ init_esm_shims();
1236
+ import { groupBy } from "remeda";
1237
+ function getLabel(skill, fallback) {
1238
+ return skill?.displayName || skill?.id || fallback;
1239
+ }
1240
+ function resolveAlias(aliasOrId, matrix) {
1241
+ return matrix.displayNameToId[aliasOrId] || aliasOrId;
1242
+ }
1243
+ function initializeSelectionContext(currentSelections, matrix) {
1244
+ const resolvedSelections = currentSelections.map((s) => resolveAlias(s, matrix));
1245
+ const selectedSet = new Set(resolvedSelections);
1246
+ return { resolvedSelections, selectedSet };
1247
+ }
1248
+ function isDisabled(skillId, currentSelections, matrix, options) {
1249
+ if (options?.expertMode) {
1250
+ return false;
1251
+ }
1252
+ const fullId = resolveAlias(skillId, matrix);
1253
+ const skill = matrix.skills[fullId];
1254
+ if (!skill) {
1255
+ return false;
1256
+ }
1257
+ for (const selectedId of currentSelections) {
1258
+ const selectedFullId = resolveAlias(selectedId, matrix);
1259
+ if (skill.conflictsWith.some((c) => c.skillId === selectedFullId)) {
1260
+ return true;
1261
+ }
1262
+ const selectedSkill = matrix.skills[selectedFullId];
1263
+ if (selectedSkill?.conflictsWith.some((c) => c.skillId === fullId)) {
1264
+ return true;
1265
+ }
1266
+ }
1267
+ const { selectedSet } = initializeSelectionContext(currentSelections, matrix);
1268
+ for (const requirement of skill.requires) {
1269
+ if (requirement.needsAny) {
1270
+ const hasAny = requirement.skillIds.some((reqId) => selectedSet.has(reqId));
1271
+ if (!hasAny) {
1272
+ return true;
1273
+ }
1274
+ } else {
1275
+ const hasAll = requirement.skillIds.every((reqId) => selectedSet.has(reqId));
1276
+ if (!hasAll) {
1277
+ return true;
1278
+ }
1279
+ }
1280
+ }
1281
+ return false;
1282
+ }
1283
+ function getDisableReason(skillId, currentSelections, matrix) {
1284
+ const fullId = resolveAlias(skillId, matrix);
1285
+ const skill = matrix.skills[fullId];
1286
+ if (!skill) {
1287
+ return void 0;
1288
+ }
1289
+ const { resolvedSelections, selectedSet } = initializeSelectionContext(currentSelections, matrix);
1290
+ for (const selectedId of resolvedSelections) {
1291
+ const conflict = skill.conflictsWith.find((c) => c.skillId === selectedId);
1292
+ if (conflict) {
1293
+ const selectedSkill2 = matrix.skills[selectedId];
1294
+ return `${conflict.reason} (conflicts with ${getLabel(selectedSkill2, selectedId)})`;
1295
+ }
1296
+ const selectedSkill = matrix.skills[selectedId];
1297
+ if (selectedSkill) {
1298
+ const reverseConflict = selectedSkill.conflictsWith.find((c) => c.skillId === fullId);
1299
+ if (reverseConflict) {
1300
+ return `${reverseConflict.reason} (conflicts with ${getLabel(selectedSkill, selectedId)})`;
1301
+ }
1302
+ }
1303
+ }
1304
+ for (const requirement of skill.requires) {
1305
+ if (requirement.needsAny) {
1306
+ const hasAny = requirement.skillIds.some((reqId) => selectedSet.has(reqId));
1307
+ if (!hasAny) {
1308
+ const requiredNames = requirement.skillIds.map((id) => getLabel(matrix.skills[id], id)).join(" or ");
1309
+ return `${requirement.reason} (requires ${requiredNames})`;
1310
+ }
1311
+ } else {
1312
+ const missingIds = requirement.skillIds.filter((reqId) => !selectedSet.has(reqId));
1313
+ if (missingIds.length > 0) {
1314
+ const missingNames = missingIds.map((id) => getLabel(matrix.skills[id], id)).join(", ");
1315
+ return `${requirement.reason} (requires ${missingNames})`;
1316
+ }
1317
+ }
1318
+ }
1319
+ return void 0;
1320
+ }
1321
+ function isDiscouraged(skillId, currentSelections, matrix) {
1322
+ const fullId = resolveAlias(skillId, matrix);
1323
+ const skill = matrix.skills[fullId];
1324
+ if (!skill) {
1325
+ return false;
1326
+ }
1327
+ const { resolvedSelections } = initializeSelectionContext(currentSelections, matrix);
1328
+ for (const selectedId of resolvedSelections) {
1329
+ const selectedSkill = matrix.skills[selectedId];
1330
+ if (selectedSkill?.discourages.some((d) => d.skillId === fullId)) {
1331
+ return true;
1332
+ }
1333
+ if (skill.discourages.some((d) => d.skillId === selectedId)) {
1334
+ return true;
1335
+ }
1336
+ }
1337
+ return false;
1338
+ }
1339
+ function getDiscourageReason(skillId, currentSelections, matrix) {
1340
+ const fullId = resolveAlias(skillId, matrix);
1341
+ const skill = matrix.skills[fullId];
1342
+ if (!skill) {
1343
+ return void 0;
1344
+ }
1345
+ const { resolvedSelections } = initializeSelectionContext(currentSelections, matrix);
1346
+ for (const selectedId of resolvedSelections) {
1347
+ const selectedSkill = matrix.skills[selectedId];
1348
+ if (selectedSkill) {
1349
+ const discourage = selectedSkill.discourages.find((d) => d.skillId === fullId);
1350
+ if (discourage) {
1351
+ return discourage.reason;
1352
+ }
1353
+ }
1354
+ const reverseDiscourage = skill.discourages.find((d) => d.skillId === selectedId);
1355
+ if (reverseDiscourage) {
1356
+ return reverseDiscourage.reason;
1357
+ }
1358
+ }
1359
+ return void 0;
1360
+ }
1361
+ function isRecommended(skillId, currentSelections, matrix) {
1362
+ const fullId = resolveAlias(skillId, matrix);
1363
+ const skill = matrix.skills[fullId];
1364
+ if (!skill) {
1365
+ return false;
1366
+ }
1367
+ const { resolvedSelections } = initializeSelectionContext(currentSelections, matrix);
1368
+ for (const selectedId of resolvedSelections) {
1369
+ const selectedSkill = matrix.skills[selectedId];
1370
+ if (selectedSkill?.recommends.some((r) => r.skillId === fullId)) {
1371
+ return true;
1372
+ }
1373
+ }
1374
+ return false;
1375
+ }
1376
+ function getRecommendReason(skillId, currentSelections, matrix) {
1377
+ const fullId = resolveAlias(skillId, matrix);
1378
+ const skill = matrix.skills[fullId];
1379
+ if (!skill) {
1380
+ return void 0;
1381
+ }
1382
+ const { resolvedSelections } = initializeSelectionContext(currentSelections, matrix);
1383
+ for (const selectedId of resolvedSelections) {
1384
+ const selectedSkill = matrix.skills[selectedId];
1385
+ if (selectedSkill) {
1386
+ const recommendation = selectedSkill.recommends.find((r) => r.skillId === fullId);
1387
+ if (recommendation) {
1388
+ return `${recommendation.reason} (recommended by ${getLabel(selectedSkill, selectedId)})`;
1389
+ }
1390
+ }
1391
+ }
1392
+ return void 0;
1393
+ }
1394
+ function validateConflicts(resolvedSelections, matrix) {
1395
+ const errors = [];
1396
+ for (let i = 0; i < resolvedSelections.length; i++) {
1397
+ const skillA = matrix.skills[resolvedSelections[i]];
1398
+ if (!skillA) continue;
1399
+ for (let j = i + 1; j < resolvedSelections.length; j++) {
1400
+ const skillBId = resolvedSelections[j];
1401
+ const conflict = skillA.conflictsWith.find((c) => c.skillId === skillBId);
1402
+ if (conflict) {
1403
+ errors.push({
1404
+ type: "conflict",
1405
+ message: `${getLabel(skillA, skillA.id)} conflicts with ${getLabel(matrix.skills[skillBId], skillBId)}: ${conflict.reason}`,
1406
+ skills: [skillA.id, skillBId]
1407
+ });
1408
+ }
1409
+ }
1410
+ }
1411
+ return { errors, warnings: [] };
1412
+ }
1413
+ function validateRequirements(resolvedSelections, selectedSet, matrix) {
1414
+ const errors = [];
1415
+ for (const skillId of resolvedSelections) {
1416
+ const skill = matrix.skills[skillId];
1417
+ if (!skill) continue;
1418
+ for (const requirement of skill.requires) {
1419
+ if (requirement.needsAny) {
1420
+ const hasAny = requirement.skillIds.some((reqId) => selectedSet.has(reqId));
1421
+ if (!hasAny) {
1422
+ errors.push({
1423
+ type: "missing_requirement",
1424
+ message: `${getLabel(skill, skillId)} requires one of: ${requirement.skillIds.map((id) => getLabel(matrix.skills[id], id)).join(", ")}`,
1425
+ skills: [skillId, ...requirement.skillIds]
1426
+ });
1427
+ }
1428
+ } else {
1429
+ const missingIds = requirement.skillIds.filter((reqId) => !selectedSet.has(reqId));
1430
+ if (missingIds.length > 0) {
1431
+ errors.push({
1432
+ type: "missing_requirement",
1433
+ message: `${getLabel(skill, skillId)} requires: ${missingIds.map((id) => getLabel(matrix.skills[id], id)).join(", ")}`,
1434
+ skills: [skillId, ...missingIds]
1435
+ });
1436
+ }
1437
+ }
1438
+ }
1439
+ }
1440
+ return { errors, warnings: [] };
1441
+ }
1442
+ function validateExclusivity(resolvedSelections, matrix) {
1443
+ const errors = [];
1444
+ const validSkills = resolvedSelections.map((skillId) => ({ skillId, skill: matrix.skills[skillId] })).filter((entry) => entry.skill != null);
1445
+ const categorySelections = groupBy(validSkills, (entry) => entry.skill.category);
1446
+ for (const [categoryId, entries] of typedEntries(categorySelections)) {
1447
+ if (entries.length > 1) {
1448
+ const skillIds = entries.map((e) => e.skillId);
1449
+ const category = matrix.categories[categoryId];
1450
+ if (category?.exclusive) {
1451
+ errors.push({
1452
+ type: "category_exclusive",
1453
+ message: `Category "${category.displayName}" only allows one selection, but multiple selected: ${skillIds.map((id) => getLabel(matrix.skills[id], id)).join(", ")}`,
1454
+ skills: skillIds
1455
+ });
1456
+ }
1457
+ }
1458
+ }
1459
+ return { errors, warnings: [] };
1460
+ }
1461
+ function validateRecommendations(resolvedSelections, selectedSet, matrix) {
1462
+ const warnings = [];
1463
+ for (const skillId of resolvedSelections) {
1464
+ const skill = matrix.skills[skillId];
1465
+ if (!skill) continue;
1466
+ for (const recommendation of skill.recommends) {
1467
+ if (!selectedSet.has(recommendation.skillId)) {
1468
+ const recommendedSkill = matrix.skills[recommendation.skillId];
1469
+ if (recommendedSkill) {
1470
+ const hasConflict = recommendedSkill.conflictsWith.some(
1471
+ (c) => selectedSet.has(c.skillId)
1472
+ );
1473
+ if (!hasConflict) {
1474
+ warnings.push({
1475
+ type: "missing_recommendation",
1476
+ message: `${getLabel(skill, skillId)} recommends ${getLabel(recommendedSkill, recommendation.skillId)}: ${recommendation.reason}`,
1477
+ skills: [skillId, recommendation.skillId]
1478
+ });
1479
+ }
1480
+ }
1481
+ }
1482
+ }
1483
+ }
1484
+ return { errors: [], warnings };
1485
+ }
1486
+ function validateSetupUsage(resolvedSelections, selectedSet, matrix) {
1487
+ const warnings = [];
1488
+ for (const skillId of resolvedSelections) {
1489
+ const skill = matrix.skills[skillId];
1490
+ if (!skill || skill.providesSetupFor.length === 0) continue;
1491
+ const hasUsageSkill = skill.providesSetupFor.some((usageId) => selectedSet.has(usageId));
1492
+ if (!hasUsageSkill) {
1493
+ warnings.push({
1494
+ type: "unused_setup",
1495
+ message: `Setup skill "${getLabel(skill, skillId)}" selected but no corresponding usage skills: ${skill.providesSetupFor.map((id) => getLabel(matrix.skills[id], id)).join(", ")}`,
1496
+ skills: [skillId, ...skill.providesSetupFor]
1497
+ });
1498
+ }
1499
+ }
1500
+ return { errors: [], warnings };
1501
+ }
1502
+ function mergeValidationResults(results) {
1503
+ return {
1504
+ errors: results.flatMap((r) => r.errors),
1505
+ warnings: results.flatMap((r) => r.warnings)
1506
+ };
1507
+ }
1508
+ function validateSelection(selections, matrix) {
1509
+ const { resolvedSelections, selectedSet } = initializeSelectionContext(selections, matrix);
1510
+ const { errors, warnings } = mergeValidationResults([
1511
+ validateConflicts(resolvedSelections, matrix),
1512
+ validateRequirements(resolvedSelections, selectedSet, matrix),
1513
+ validateExclusivity(resolvedSelections, matrix),
1514
+ validateRecommendations(resolvedSelections, selectedSet, matrix),
1515
+ validateSetupUsage(resolvedSelections, selectedSet, matrix)
1516
+ ]);
1517
+ return {
1518
+ valid: errors.length === 0,
1519
+ errors,
1520
+ warnings
1521
+ };
1522
+ }
1523
+ function getAvailableSkills(categoryId, currentSelections, matrix, options) {
1524
+ const skillOptions = [];
1525
+ const { selectedSet } = initializeSelectionContext(currentSelections, matrix);
1526
+ for (const skill of Object.values(matrix.skills)) {
1527
+ if (!skill) continue;
1528
+ if (skill.category !== categoryId) {
1529
+ continue;
1530
+ }
1531
+ const disabled = isDisabled(skill.id, currentSelections, matrix, options);
1532
+ const discouraged = !disabled && isDiscouraged(skill.id, currentSelections, matrix);
1533
+ const recommended = !disabled && !discouraged && isRecommended(skill.id, currentSelections, matrix);
1534
+ skillOptions.push({
1535
+ id: skill.id,
1536
+ displayName: skill.displayName,
1537
+ description: skill.description,
1538
+ disabled,
1539
+ disabledReason: disabled ? getDisableReason(skill.id, currentSelections, matrix) : void 0,
1540
+ discouraged,
1541
+ discouragedReason: discouraged ? getDiscourageReason(skill.id, currentSelections, matrix) : void 0,
1542
+ recommended,
1543
+ recommendedReason: recommended ? getRecommendReason(skill.id, currentSelections, matrix) : void 0,
1544
+ selected: selectedSet.has(skill.id),
1545
+ alternatives: skill.alternatives.map((a) => a.skillId)
1546
+ });
1547
+ }
1548
+ return skillOptions;
1549
+ }
1550
+
1551
+ // src/cli/lib/matrix/matrix-health-check.ts
1552
+ init_esm_shims();
1553
+ function checkMatrixHealth(matrix) {
1554
+ const issues = [];
1555
+ const skillIds = new Set(typedKeys(matrix.skills));
1556
+ checkRelationshipTargets(matrix, skillIds, issues);
1557
+ checkSubcategoryDomains(matrix, issues);
1558
+ checkSkillCategories(matrix, issues);
1559
+ checkCompatibleWithTargets(matrix, skillIds, issues);
1560
+ checkStackSkillIds(matrix, skillIds, issues);
1561
+ for (const issue of issues) {
1562
+ warn(`[matrix] ${issue.details}`);
1563
+ }
1564
+ return issues;
1565
+ }
1566
+ function checkRelationshipTargets(matrix, skillIds, issues) {
1567
+ for (const [skillId, skill] of typedEntries(matrix.skills)) {
1568
+ if (!skill) continue;
1569
+ for (const conflict of skill.conflictsWith) {
1570
+ if (!skillIds.has(conflict.skillId)) {
1571
+ issues.push({
1572
+ severity: "warning",
1573
+ finding: "ghost-relationship-target",
1574
+ details: `Skill '${skillId}' conflicts with '${conflict.skillId}' which does not exist in the matrix`
1575
+ });
1576
+ }
1577
+ }
1578
+ for (const recommend of skill.recommends) {
1579
+ if (!skillIds.has(recommend.skillId)) {
1580
+ issues.push({
1581
+ severity: "warning",
1582
+ finding: "ghost-relationship-target",
1583
+ details: `Skill '${skillId}' recommends '${recommend.skillId}' which does not exist in the matrix`
1584
+ });
1585
+ }
1586
+ }
1587
+ for (const requirement of skill.requires) {
1588
+ for (const reqId of requirement.skillIds) {
1589
+ if (!skillIds.has(reqId)) {
1590
+ issues.push({
1591
+ severity: "error",
1592
+ finding: "ghost-requirement-target",
1593
+ details: `Skill '${skillId}' requires '${reqId}' which does not exist in the matrix`
1594
+ });
1595
+ }
1596
+ }
1597
+ }
1598
+ for (const alt of skill.alternatives) {
1599
+ if (!skillIds.has(alt.skillId)) {
1600
+ issues.push({
1601
+ severity: "warning",
1602
+ finding: "ghost-alternative-target",
1603
+ details: `Skill '${skillId}' lists alternative '${alt.skillId}' which does not exist in the matrix`
1604
+ });
1605
+ }
1606
+ }
1607
+ for (const discourage of skill.discourages) {
1608
+ if (!skillIds.has(discourage.skillId)) {
1609
+ issues.push({
1610
+ severity: "warning",
1611
+ finding: "ghost-relationship-target",
1612
+ details: `Skill '${skillId}' discourages '${discourage.skillId}' which does not exist in the matrix`
1613
+ });
1614
+ }
1615
+ }
1616
+ for (const setupId of skill.requiresSetup) {
1617
+ if (!skillIds.has(setupId)) {
1618
+ issues.push({
1619
+ severity: "warning",
1620
+ finding: "ghost-setup-target",
1621
+ details: `Skill '${skillId}' requiresSetup '${setupId}' which does not exist in the matrix`
1622
+ });
1623
+ }
1624
+ }
1625
+ for (const providesId of skill.providesSetupFor) {
1626
+ if (!skillIds.has(providesId)) {
1627
+ issues.push({
1628
+ severity: "warning",
1629
+ finding: "ghost-setup-target",
1630
+ details: `Skill '${skillId}' providesSetupFor '${providesId}' which does not exist in the matrix`
1631
+ });
1632
+ }
1633
+ }
1634
+ }
1635
+ }
1636
+ function checkSubcategoryDomains(matrix, issues) {
1637
+ for (const [catId, cat] of typedEntries(matrix.categories)) {
1638
+ if (!cat) continue;
1639
+ if (!cat.domain) {
1640
+ issues.push({
1641
+ severity: "warning",
1642
+ finding: "category-missing-domain",
1643
+ details: `Category '${catId}' has no domain \u2014 it won't appear in any wizard domain view`
1644
+ });
1645
+ }
1646
+ }
1647
+ }
1648
+ function checkSkillCategories(matrix, issues) {
1649
+ for (const [skillId, skill] of typedEntries(matrix.skills)) {
1650
+ if (!skill) continue;
1651
+ if (!matrix.categories[skill.category]) {
1652
+ issues.push({
1653
+ severity: "warning",
1654
+ finding: "skill-unknown-category",
1655
+ details: `Skill '${skillId}' references category '${skill.category}' which does not exist in the matrix`
1656
+ });
1657
+ }
1658
+ }
1659
+ }
1660
+ function checkCompatibleWithTargets(matrix, skillIds, issues) {
1661
+ for (const [skillId, skill] of typedEntries(matrix.skills)) {
1662
+ if (!skill) continue;
1663
+ for (const compatId of skill.compatibleWith) {
1664
+ if (!skillIds.has(compatId)) {
1665
+ issues.push({
1666
+ severity: "warning",
1667
+ finding: "ghost-compatible-with-target",
1668
+ details: `Skill '${skillId}' has compatibleWith '${compatId}' which does not exist in the matrix`
1669
+ });
1670
+ }
1671
+ }
1672
+ }
1673
+ }
1674
+ function checkStackSkillIds(matrix, skillIds, issues) {
1675
+ for (const stack of matrix.suggestedStacks) {
1676
+ for (const stackSkillId of stack.allSkillIds) {
1677
+ if (!skillIds.has(stackSkillId)) {
1678
+ issues.push({
1679
+ severity: "warning",
1680
+ finding: "stack-ghost-skill",
1681
+ details: `Stack '${stack.id}' references skill '${stackSkillId}' which does not exist in the matrix`
1682
+ });
1683
+ }
1684
+ }
1685
+ }
1686
+ }
1687
+
1688
+ // src/cli/lib/loading/multi-source-loader.ts
1689
+ init_esm_shims();
1690
+ import path18 from "path";
1691
+
1692
+ // src/cli/lib/plugins/index.ts
1693
+ init_esm_shims();
1694
+
1695
+ // src/cli/lib/plugins/plugin-manifest.ts
1696
+ init_esm_shims();
1697
+ import path7 from "path";
1698
+ var PLUGIN_DIR_NAME = PLUGIN_MANIFEST_DIR;
1699
+ var PLUGIN_MANIFEST_FILE2 = STANDARD_FILES.PLUGIN_JSON;
1700
+ var SKILL_PLUGIN_PREFIX = "";
1701
+ var AGENT_PLUGIN_PREFIX = "agent-";
1702
+ function buildAuthor(name, email) {
1703
+ if (!name) {
1704
+ return void 0;
1705
+ }
1706
+ const author = { name };
1707
+ if (email) {
1708
+ author.email = email;
1709
+ }
1710
+ return author;
1711
+ }
1712
+ function generateSkillPluginManifest(options) {
1713
+ const manifest = {
1714
+ name: `${SKILL_PLUGIN_PREFIX}${options.skillName}`,
1715
+ version: options.version ?? DEFAULT_VERSION,
1716
+ skills: "./skills/"
1717
+ };
1718
+ if (options.description) {
1719
+ manifest.description = options.description;
1720
+ }
1721
+ const author = buildAuthor(options.author, options.authorEmail);
1722
+ if (author) {
1723
+ manifest.author = author;
1724
+ }
1725
+ if (options.keywords && options.keywords.length > 0) {
1726
+ manifest.keywords = options.keywords;
1727
+ }
1728
+ return manifest;
1729
+ }
1730
+ function generateAgentPluginManifest(options) {
1731
+ const manifest = {
1732
+ name: `${AGENT_PLUGIN_PREFIX}${options.agentName}`,
1733
+ version: options.version ?? DEFAULT_VERSION,
1734
+ agents: "./agents/"
1735
+ };
1736
+ if (options.description) {
1737
+ manifest.description = options.description;
1738
+ }
1739
+ return manifest;
1740
+ }
1741
+ function generateStackPluginManifest(options) {
1742
+ const manifest = {
1743
+ name: options.stackName,
1744
+ version: options.version ?? DEFAULT_VERSION
1745
+ };
1746
+ if (options.hasSkills) {
1747
+ manifest.skills = "./skills/";
1748
+ }
1749
+ if (options.description) {
1750
+ manifest.description = options.description;
1751
+ }
1752
+ const author = buildAuthor(options.author, options.authorEmail);
1753
+ if (author) {
1754
+ manifest.author = author;
1755
+ }
1756
+ if (options.keywords && options.keywords.length > 0) {
1757
+ manifest.keywords = options.keywords;
1758
+ }
1759
+ if (options.hasHooks) {
1760
+ manifest.hooks = "./hooks/hooks.json";
1761
+ }
1762
+ return manifest;
1763
+ }
1764
+ async function writePluginManifest(outputDir, manifest) {
1765
+ const pluginDir = path7.join(outputDir, PLUGIN_DIR_NAME);
1766
+ const manifestPath = path7.join(pluginDir, PLUGIN_MANIFEST_FILE2);
1767
+ await ensureDir(pluginDir);
1768
+ const content = JSON.stringify(manifest, null, 2);
1769
+ await writeFile(manifestPath, content);
1770
+ return manifestPath;
1771
+ }
1772
+
1773
+ // src/cli/lib/plugins/plugin-manifest-finder.ts
1774
+ init_esm_shims();
1775
+ import path8 from "path";
1776
+ async function findPluginManifest(startDir) {
1777
+ let currentDir = startDir;
1778
+ const root = path8.parse(currentDir).root;
1779
+ while (currentDir !== root) {
1780
+ const manifestPath = path8.join(currentDir, PLUGIN_MANIFEST_DIR, PLUGIN_MANIFEST_FILE);
1781
+ if (await fileExists(manifestPath)) {
1782
+ return manifestPath;
1783
+ }
1784
+ currentDir = path8.dirname(currentDir);
1785
+ }
1786
+ return null;
1787
+ }
1788
+
1789
+ // src/cli/lib/plugins/plugin-finder.ts
1790
+ init_esm_shims();
1791
+ import path9 from "path";
1792
+ import { last, zip } from "remeda";
1793
+ function getProjectPluginsDir(projectDir) {
1794
+ const dir = projectDir ?? process.cwd();
1795
+ return path9.join(dir, CLAUDE_DIR, PLUGINS_SUBDIR);
1796
+ }
1797
+ function getPluginAgentsDir(pluginDir) {
1798
+ return path9.join(pluginDir, "agents");
1799
+ }
1800
+ function getPluginManifestPath(pluginDir) {
1801
+ return path9.join(pluginDir, PLUGIN_MANIFEST_DIR, PLUGIN_MANIFEST_FILE);
1802
+ }
1803
+
1804
+ // src/cli/lib/plugins/plugin-info.ts
1805
+ init_esm_shims();
1806
+ import { readdir } from "fs/promises";
1807
+
1808
+ // src/cli/lib/installation/index.ts
1809
+ init_esm_shims();
1810
+
1811
+ // src/cli/lib/installation/installation.ts
1812
+ init_esm_shims();
1813
+ import path10 from "path";
1814
+ async function detectInstallation(projectDir = process.cwd()) {
1815
+ const srcConfigPath = path10.join(projectDir, CLAUDE_SRC_DIR, STANDARD_FILES.CONFIG_YAML);
1816
+ const legacyConfigPath = path10.join(projectDir, CLAUDE_DIR, STANDARD_FILES.CONFIG_YAML);
1817
+ const localConfigPath = await fileExists(srcConfigPath) ? srcConfigPath : await fileExists(legacyConfigPath) ? legacyConfigPath : null;
1818
+ if (!localConfigPath) {
1819
+ return null;
1820
+ }
1821
+ const loaded = await loadProjectConfig(projectDir);
1822
+ const mode = loaded?.config?.installMode ?? "local";
1823
+ if (mode === "local") {
1824
+ return {
1825
+ mode: "local",
1826
+ configPath: localConfigPath,
1827
+ agentsDir: path10.join(projectDir, CLAUDE_DIR, "agents"),
1828
+ skillsDir: path10.join(projectDir, CLAUDE_DIR, "skills"),
1829
+ projectDir
1830
+ };
1831
+ }
1832
+ return {
1833
+ mode: "plugin",
1834
+ configPath: localConfigPath,
1835
+ agentsDir: path10.join(projectDir, CLAUDE_DIR, "agents"),
1836
+ skillsDir: path10.join(projectDir, CLAUDE_DIR, PLUGINS_SUBDIR),
1837
+ projectDir
1838
+ };
1839
+ }
1840
+
1841
+ // src/cli/lib/installation/local-installer.ts
1842
+ init_esm_shims();
1843
+ import path14 from "path";
1844
+ import { stringify as stringifyYaml3 } from "yaml";
1845
+
1846
+ // src/cli/lib/stacks/index.ts
1847
+ init_esm_shims();
1848
+
1849
+ // src/cli/lib/stacks/stacks-loader.ts
1850
+ init_esm_shims();
1851
+ import { parse as parseYaml5 } from "yaml";
1852
+ import path11 from "path";
1853
+ import { mapValues, pipe, flatMap, unique as unique2 } from "remeda";
1854
+ var stacksCache = /* @__PURE__ */ new Map();
1855
+ function normalizeAgentConfig(agentConfig) {
1856
+ return mapValues(agentConfig, (value) => {
1857
+ const items = Array.isArray(value) ? value : [value];
1858
+ return items.map(
1859
+ (item) => typeof item === "string" ? { id: item, preloaded: false } : item
1860
+ );
1861
+ });
1862
+ }
1863
+ function normalizeStackRecord(rawStack) {
1864
+ return mapValues(rawStack, (agentConfig) => normalizeAgentConfig(agentConfig));
1865
+ }
1866
+ async function loadStacks(configDir, stacksFile) {
1867
+ const resolvedStacksFile = stacksFile ?? STACKS_FILE_PATH;
1868
+ const cacheKey = `${configDir}:${resolvedStacksFile}`;
1869
+ const cached = stacksCache.get(cacheKey);
1870
+ if (cached) return cached;
1871
+ const stacksPath = path11.join(configDir, resolvedStacksFile);
1872
+ if (!await fileExists(stacksPath)) {
1873
+ verbose(`No stacks file found at ${stacksPath}`);
1874
+ return [];
1875
+ }
1876
+ try {
1877
+ const content = await readFile(stacksPath);
1878
+ const result = stacksConfigSchema.safeParse(parseYaml5(content));
1879
+ if (!result.success) {
1880
+ throw new Error(
1881
+ `Invalid stacks.yaml at '${stacksPath}': ${formatZodErrors(result.error.issues)}`
1882
+ );
1883
+ }
1884
+ const stacks = result.data.stacks.map((stack) => ({
1885
+ ...stack,
1886
+ agents: mapValues(
1887
+ stack.agents,
1888
+ (agentConfig) => normalizeAgentConfig(agentConfig)
1889
+ )
1890
+ }));
1891
+ stacksCache.set(cacheKey, stacks);
1892
+ verbose(`Loaded ${stacks.length} stacks from ${stacksPath}`);
1893
+ return stacks;
1894
+ } catch (error) {
1895
+ const errorMessage = getErrorMessage(error);
1896
+ throw new Error(`Failed to load stacks from '${stacksPath}': ${errorMessage}`);
1897
+ }
1898
+ }
1899
+ async function loadStackById(stackId, configDir) {
1900
+ const stacks = await loadStacks(configDir);
1901
+ const stack = stacks.find((s) => s.id === stackId);
1902
+ if (!stack) {
1903
+ verbose(`Stack '${stackId}' not found`);
1904
+ return null;
1905
+ }
1906
+ verbose(`Found stack: ${stack.name} (${stackId})`);
1907
+ return stack;
1908
+ }
1909
+ function resolveAgentConfigToSkills(agentConfig) {
1910
+ const skillRefs = [];
1911
+ for (const [subcategory, assignments] of typedEntries(
1912
+ agentConfig
1913
+ )) {
1914
+ if (!assignments) continue;
1915
+ for (const assignment of assignments) {
1916
+ if (!SKILL_ID_PATTERN.test(assignment.id)) {
1917
+ warn(
1918
+ `Invalid skill ID '${assignment.id}' for subcategory '${subcategory}' in stack config. Skipping.`
1919
+ );
1920
+ continue;
1921
+ }
1922
+ skillRefs.push({
1923
+ id: assignment.id,
1924
+ usage: `when working with ${subcategory}`,
1925
+ preloaded: assignment.preloaded ?? false
1926
+ });
1927
+ }
1928
+ }
1929
+ return skillRefs;
1930
+ }
1931
+ function getStackSkillIds(stack) {
1932
+ return pipe(
1933
+ Object.values(stack),
1934
+ flatMap(resolveAgentConfigToSkills),
1935
+ (refs) => refs.map((r) => r.id),
1936
+ unique2()
1937
+ );
1938
+ }
1939
+
1940
+ // src/cli/lib/stacks/stack-plugin-compiler.ts
1941
+ init_esm_shims();
1942
+ import path13 from "path";
1943
+
1944
+ // src/cli/lib/compiler.ts
1945
+ init_esm_shims();
1946
+ import { Liquid } from "liquidjs";
1947
+ import path12 from "path";
1948
+ import { pipe as pipe2, flatMap as flatMap2, filter, uniqueBy } from "remeda";
1949
+
1950
+ // src/cli/lib/resolver.ts
1951
+ init_esm_shims();
1952
+ function resolveSkillReference(ref, skills) {
1953
+ const definition = skills[ref.id];
1954
+ if (!definition) {
1955
+ verbose(`Skill '${ref.id}' not found in available skills, skipping`);
1956
+ return null;
1957
+ }
1958
+ return {
1959
+ ...definition,
1960
+ usage: ref.usage,
1961
+ preloaded: ref.preloaded ?? false
1962
+ };
1963
+ }
1964
+ function resolveSkillReferences(skillRefs, skills) {
1965
+ return skillRefs.map((ref) => resolveSkillReference(ref, skills)).filter((skill) => skill !== null);
1966
+ }
1967
+ function buildSkillRefsFromConfig(agentStack) {
1968
+ return resolveAgentConfigToSkills(agentStack);
1969
+ }
1970
+ function resolveAgentSkillsFromStack(agentName, stack) {
1971
+ const agentConfig = stack.agents[agentName];
1972
+ if (!agentConfig) {
1973
+ verbose(`Agent '${agentName}' not found in stack '${stack.id}'`);
1974
+ return [];
1975
+ }
1976
+ if (typedKeys(agentConfig).length === 0) {
1977
+ verbose(`Agent '${agentName}' has no technology config in stack '${stack.id}'`);
1978
+ return [];
1979
+ }
1980
+ const skillRefs = resolveAgentConfigToSkills(agentConfig);
1981
+ verbose(`Resolved ${skillRefs.length} skills for agent '${agentName}' from stack '${stack.id}'`);
1982
+ return skillRefs;
1983
+ }
1984
+ async function resolveAgentSkillRefs(agentName, agentConfig, stack) {
1985
+ if (agentConfig.skills && agentConfig.skills.length > 0) {
1986
+ return agentConfig.skills;
1987
+ }
1988
+ if (stack) {
1989
+ const stackSkills = resolveAgentSkillsFromStack(agentName, stack);
1990
+ if (stackSkills.length > 0) {
1991
+ verbose(`Resolved ${stackSkills.length} skills from stack for ${agentName}`);
1992
+ return stackSkills;
1993
+ }
1994
+ }
1995
+ return [];
1996
+ }
1997
+ async function resolveAgents(agents, skills, compileConfig, _projectRoot, stack) {
1998
+ const resolved = {};
1999
+ const agentNames = typedKeys(compileConfig.agents);
2000
+ for (const agentName of agentNames) {
2001
+ const definition = agents[agentName];
2002
+ if (!definition) {
2003
+ const availableAgents = typedKeys(agents);
2004
+ const agentList = availableAgents.length > 0 ? `Available agents: ${availableAgents.slice(0, 5).join(", ")}${availableAgents.length > 5 ? ` (and ${availableAgents.length - 5} more)` : ""}` : "No agents found in scanned directories";
2005
+ throw new Error(
2006
+ `Agent '${agentName}' referenced in compile config but not found in scanned agents. ${agentList}. Check that src/agents/${agentName}/agent.yaml exists.`
2007
+ );
2008
+ }
2009
+ const agentConfig = compileConfig.agents[agentName];
2010
+ const skillRefs = await resolveAgentSkillRefs(agentName, agentConfig, stack);
2011
+ const resolvedSkills = resolveSkillReferences(skillRefs, skills);
2012
+ resolved[agentName] = {
2013
+ name: agentName,
2014
+ title: definition.title,
2015
+ description: definition.description,
2016
+ model: definition.model,
2017
+ tools: definition.tools,
2018
+ skills: resolvedSkills,
2019
+ path: definition.path,
2020
+ sourceRoot: definition.sourceRoot,
2021
+ agentBaseDir: definition.agentBaseDir
2022
+ };
2023
+ }
2024
+ return resolved;
2025
+ }
2026
+ function convertStackToCompileConfig(stackId, stack) {
2027
+ const agents = {};
2028
+ for (const agentId of stack.agents) {
2029
+ agents[agentId] = {};
2030
+ }
2031
+ return {
2032
+ name: stack.name,
2033
+ description: stack.description || "",
2034
+ stack: stackId,
2035
+ agents
2036
+ };
2037
+ }
2038
+
2039
+ // src/cli/utils/frontmatter.ts
2040
+ init_esm_shims();
2041
+ import { parse as parseYaml6 } from "yaml";
2042
+ function extractFrontmatter(content) {
2043
+ const frontmatterRegex = /^---\r?\n([\s\S]*?)\r?\n---/;
2044
+ const match = content.match(frontmatterRegex);
2045
+ if (!match || !match[1]) {
2046
+ return null;
2047
+ }
2048
+ try {
2049
+ return parseYaml6(match[1]);
2050
+ } catch {
2051
+ return null;
2052
+ }
2053
+ }
2054
+
2055
+ // src/cli/lib/compiler.ts
2056
+ var LIQUID_SYNTAX_PATTERN = /\{\{|\}\}|\{%|%\}/g;
2057
+ function sanitizeLiquidSyntax(value, fieldName) {
2058
+ if (!LIQUID_SYNTAX_PATTERN.test(value)) return value;
2059
+ LIQUID_SYNTAX_PATTERN.lastIndex = 0;
2060
+ const sanitized = value.replace(LIQUID_SYNTAX_PATTERN, "");
2061
+ warn(`Stripped Liquid template syntax from '${fieldName}' \u2014 possible template injection attempt`);
2062
+ return sanitized;
2063
+ }
2064
+ function sanitizeString(value, fieldName) {
2065
+ if (value === void 0) return void 0;
2066
+ return sanitizeLiquidSyntax(value, fieldName);
2067
+ }
2068
+ function sanitizeStringArray(values, fieldName) {
2069
+ if (!values) return values;
2070
+ return values.map((v) => sanitizeLiquidSyntax(v, fieldName));
2071
+ }
2072
+ function sanitizeSkills(skills) {
2073
+ return skills.map((s) => ({
2074
+ ...s,
2075
+ id: sanitizeLiquidSyntax(s.id, "skill.id"),
2076
+ description: sanitizeLiquidSyntax(s.description, "skill.description"),
2077
+ usage: sanitizeLiquidSyntax(s.usage, "skill.usage"),
2078
+ pluginRef: sanitizeString(s.pluginRef, "skill.pluginRef")
2079
+ }));
2080
+ }
2081
+ function sanitizeCompiledAgentData(data) {
2082
+ const sanitizedAgent = {
2083
+ ...data.agent,
2084
+ name: sanitizeLiquidSyntax(data.agent.name, "agent.name"),
2085
+ title: sanitizeLiquidSyntax(data.agent.title, "agent.title"),
2086
+ description: sanitizeLiquidSyntax(data.agent.description, "agent.description"),
2087
+ tools: sanitizeStringArray(data.agent.tools, "agent.tools") ?? data.agent.tools,
2088
+ disallowed_tools: sanitizeStringArray(data.agent.disallowed_tools, "agent.disallowed_tools"),
2089
+ model: sanitizeString(data.agent.model, "agent.model"),
2090
+ permission_mode: sanitizeString(
2091
+ data.agent.permission_mode,
2092
+ "agent.permission_mode"
2093
+ )
2094
+ };
2095
+ const sanitizedSkills = sanitizeSkills(data.skills);
2096
+ const sanitizedPreloaded = sanitizeSkills(data.preloadedSkills);
2097
+ const sanitizedDynamic = sanitizeSkills(data.dynamicSkills);
2098
+ const sanitizedPreloadedIds = data.preloadedSkillIds.map(
2099
+ (id) => sanitizeLiquidSyntax(String(id), "preloadedSkillId")
2100
+ );
2101
+ return {
2102
+ agent: sanitizedAgent,
2103
+ intro: sanitizeLiquidSyntax(data.intro, "intro"),
2104
+ workflow: sanitizeLiquidSyntax(data.workflow, "workflow"),
2105
+ examples: sanitizeLiquidSyntax(data.examples, "examples"),
2106
+ criticalRequirementsTop: sanitizeLiquidSyntax(
2107
+ data.criticalRequirementsTop,
2108
+ "criticalRequirementsTop"
2109
+ ),
2110
+ criticalReminders: sanitizeLiquidSyntax(data.criticalReminders, "criticalReminders"),
2111
+ outputFormat: sanitizeLiquidSyntax(data.outputFormat, "outputFormat"),
2112
+ skills: sanitizedSkills,
2113
+ preloadedSkills: sanitizedPreloaded,
2114
+ dynamicSkills: sanitizedDynamic,
2115
+ preloadedSkillIds: sanitizedPreloadedIds
2116
+ };
2117
+ }
2118
+ async function createLiquidEngine(projectDir) {
2119
+ const roots = [];
2120
+ if (projectDir) {
2121
+ const srcTemplatesDir = path12.join(projectDir, CLAUDE_SRC_DIR, "agents", "_templates");
2122
+ if (await directoryExists(srcTemplatesDir)) {
2123
+ roots.push(srcTemplatesDir);
2124
+ verbose(`Using local templates from: ${srcTemplatesDir}`);
2125
+ }
2126
+ const legacyTemplatesDir = path12.join(projectDir, CLAUDE_DIR, "templates");
2127
+ if (await directoryExists(legacyTemplatesDir)) {
2128
+ roots.push(legacyTemplatesDir);
2129
+ verbose(`Using legacy templates from: ${legacyTemplatesDir}`);
2130
+ }
2131
+ }
2132
+ roots.push(path12.join(PROJECT_ROOT, DIRS.templates));
2133
+ return new Liquid({
2134
+ root: roots,
2135
+ extname: ".liquid",
2136
+ strictVariables: false,
2137
+ strictFilters: true
2138
+ });
2139
+ }
2140
+
2141
+ // src/cli/lib/stacks/stack-plugin-compiler.ts
2142
+ import { unique as unique3 } from "remeda";
2143
+ function hashStackConfig(stack) {
2144
+ const stackSkillIds = stack.stack ? getStackSkillIds(stack.stack).sort() : [];
2145
+ const parts = [
2146
+ `name:${stack.name}`,
2147
+ `description:${stack.description ?? ""}`,
2148
+ `skills:${stackSkillIds.join(",")}`,
2149
+ `agents:${(stack.agents || []).sort().join(",")}`
2150
+ ];
2151
+ return computeStringHash(parts.join("\n"));
2152
+ }
2153
+ async function compileAgentForPlugin(name, agent, fallbackRoot, engine, installMode) {
2154
+ verbose(`Compiling agent: ${name}`);
2155
+ const agentSourceRoot = agent.sourceRoot || fallbackRoot;
2156
+ const agentBaseDir = agent.agentBaseDir || DIRS.agents;
2157
+ const agentDir = path13.join(agentSourceRoot, agentBaseDir, agent.path || name);
2158
+ const intro = await readFile(path13.join(agentDir, STANDARD_FILES.INTRO_MD));
2159
+ const workflow = await readFile(path13.join(agentDir, STANDARD_FILES.WORKFLOW_MD));
2160
+ const examples = await readFileOptional(
2161
+ path13.join(agentDir, STANDARD_FILES.EXAMPLES_MD),
2162
+ "## Examples\n\n_No examples defined._"
2163
+ );
2164
+ const criticalRequirementsTop = await readFileOptional(
2165
+ path13.join(agentDir, STANDARD_FILES.CRITICAL_REQUIREMENTS_MD),
2166
+ ""
2167
+ );
2168
+ const criticalReminders = await readFileOptional(
2169
+ path13.join(agentDir, STANDARD_FILES.CRITICAL_REMINDERS_MD),
2170
+ ""
2171
+ );
2172
+ const agentPath = agent.path || name;
2173
+ const category = agentPath.split("/")[0];
2174
+ const categoryDir = path13.join(agentSourceRoot, agentBaseDir, category);
2175
+ let outputFormat = await readFileOptional(
2176
+ path13.join(agentDir, STANDARD_FILES.OUTPUT_FORMAT_MD),
2177
+ ""
2178
+ );
2179
+ if (!outputFormat) {
2180
+ outputFormat = await readFileOptional(
2181
+ path13.join(categoryDir, STANDARD_FILES.OUTPUT_FORMAT_MD),
2182
+ ""
2183
+ );
2184
+ }
2185
+ const skills = installMode === "plugin" ? agent.skills.map((s) => ({ ...s, pluginRef: `${s.id}:${s.id}` })) : agent.skills;
2186
+ const preloadedSkills = skills.filter((s) => s.preloaded);
2187
+ const dynamicSkills = skills.filter((s) => !s.preloaded);
2188
+ const preloadedSkillIds = preloadedSkills.map((s) => s.pluginRef ?? s.id);
2189
+ verbose(
2190
+ `Skills for ${name}: ${preloadedSkills.length} preloaded, ${dynamicSkills.length} dynamic`
2191
+ );
2192
+ const data = {
2193
+ agent,
2194
+ intro,
2195
+ workflow,
2196
+ examples,
2197
+ criticalRequirementsTop,
2198
+ criticalReminders,
2199
+ outputFormat,
2200
+ skills,
2201
+ preloadedSkills,
2202
+ dynamicSkills,
2203
+ preloadedSkillIds
2204
+ };
2205
+ return engine.renderFile("agent", sanitizeCompiledAgentData(data));
2206
+ }
2207
+ function generateStackReadme(stackId, stack, agents, skillPlugins) {
2208
+ const lines = [];
2209
+ lines.push(`# ${stack.name}`);
2210
+ lines.push("");
2211
+ lines.push(stack.description || "A Claude Code stack plugin.");
2212
+ lines.push("");
2213
+ lines.push("## Installation");
2214
+ lines.push("");
2215
+ lines.push("Add this plugin to your Claude Code configuration:");
2216
+ lines.push("");
2217
+ lines.push("```json");
2218
+ lines.push("{");
2219
+ lines.push(` "plugins": ["${stackId}"]`);
2220
+ lines.push("}");
2221
+ lines.push("```");
2222
+ lines.push("");
2223
+ lines.push("## Agents");
2224
+ lines.push("");
2225
+ lines.push("This stack includes the following agents:");
2226
+ lines.push("");
2227
+ for (const agent of agents) {
2228
+ lines.push(`- \`${agent}\``);
2229
+ }
2230
+ lines.push("");
2231
+ if (skillPlugins.length > 0) {
2232
+ lines.push("## Included Skills");
2233
+ lines.push("");
2234
+ lines.push("This stack includes the following skills:");
2235
+ lines.push("");
2236
+ const uniqueSkills = unique3(skillPlugins).sort();
2237
+ for (const skill of uniqueSkills) {
2238
+ lines.push(`- \`${skill}\``);
2239
+ }
2240
+ lines.push("");
2241
+ }
2242
+ lines.push("---");
2243
+ lines.push("");
2244
+ lines.push(`*Generated by ${DEFAULT_BRANDING.NAME} stack-plugin-compiler*`);
2245
+ lines.push("");
2246
+ return lines.join("\n");
2247
+ }
2248
+ async function compileStackPlugin(options) {
2249
+ const { stackId, outputDir, projectRoot, agentSourcePath } = options;
2250
+ const localAgentRoot = agentSourcePath || projectRoot;
2251
+ verbose(`Compiling stack plugin: ${stackId}`);
2252
+ verbose(` Stack/skills source: ${projectRoot}`);
2253
+ verbose(` Local agent source: ${localAgentRoot}`);
2254
+ verbose(` CLI agent source: ${PROJECT_ROOT}`);
2255
+ const cliAgents = await loadAllAgents(PROJECT_ROOT);
2256
+ const localAgents = await loadAllAgents(localAgentRoot);
2257
+ const agents = { ...cliAgents, ...localAgents };
2258
+ verbose(
2259
+ ` Loaded ${Object.keys(localAgents).length} local agents, ${Object.keys(cliAgents).length} CLI agents`
2260
+ );
2261
+ let newStack = options.stack || await loadStackById(stackId, projectRoot);
2262
+ if (!newStack) {
2263
+ newStack = await loadStackById(stackId, PROJECT_ROOT);
2264
+ }
2265
+ let stack;
2266
+ if (newStack) {
2267
+ verbose(` Found stack: ${newStack.name}`);
2268
+ const agentSkillIds = /* @__PURE__ */ new Set();
2269
+ for (const agentName of typedKeys(newStack.agents)) {
2270
+ const agentConfig = newStack.agents[agentName];
2271
+ if (!agentConfig) continue;
2272
+ const skillRefs = resolveAgentConfigToSkills(agentConfig);
2273
+ for (const ref of skillRefs) {
2274
+ agentSkillIds.add(ref.id);
2275
+ }
2276
+ }
2277
+ stack = {
2278
+ name: newStack.name,
2279
+ description: newStack.description,
2280
+ agents: typedKeys(newStack.agents),
2281
+ skills: [...agentSkillIds],
2282
+ stack: buildStackProperty(newStack)
2283
+ };
2284
+ } else {
2285
+ throw new Error(`Stack '${stackId}' not found in config/stacks.yaml`);
2286
+ }
2287
+ const stackSkillIds = stack.stack ? getStackSkillIds(stack.stack) : [];
2288
+ const skills = await loadSkillsByIds(
2289
+ stackSkillIds.map((id) => ({ id })),
2290
+ projectRoot
2291
+ );
2292
+ const compileConfig = convertStackToCompileConfig(stackId, stack);
2293
+ const resolvedAgents = await resolveAgents(agents, skills, compileConfig, projectRoot, newStack);
2294
+ const pluginDir = path13.join(outputDir, stackId);
2295
+ const agentsDir = path13.join(pluginDir, "agents");
2296
+ await ensureDir(pluginDir);
2297
+ await ensureDir(agentsDir);
2298
+ const pluginSkillsDir = path13.join(pluginDir, "skills");
2299
+ await ensureDir(pluginSkillsDir);
2300
+ const copiedSourcePaths = /* @__PURE__ */ new Set();
2301
+ for (const resolvedSkill of Object.values(skills)) {
2302
+ const sourceSkillDir = path13.join(projectRoot, resolvedSkill.path);
2303
+ if (copiedSourcePaths.has(resolvedSkill.path)) {
2304
+ continue;
2305
+ }
2306
+ const destSkillDir = path13.join(pluginSkillsDir, resolvedSkill.id);
2307
+ if (await directoryExists(sourceSkillDir)) {
2308
+ await copy(sourceSkillDir, destSkillDir);
2309
+ copiedSourcePaths.add(resolvedSkill.path);
2310
+ verbose(` Copied skill: ${resolvedSkill.id}`);
2311
+ } else {
2312
+ verbose(` Warning: Skill directory not found: ${sourceSkillDir}`);
2313
+ }
2314
+ }
2315
+ const engine = await createLiquidEngine();
2316
+ const compiledAgentNames = [];
2317
+ const allSkillPlugins = [];
2318
+ for (const [name, agent] of typedEntries(resolvedAgents)) {
2319
+ const output = await compileAgentForPlugin(name, agent, PROJECT_ROOT, engine);
2320
+ await writeFile(path13.join(agentsDir, `${name}.md`), output);
2321
+ compiledAgentNames.push(name);
2322
+ for (const skill of agent.skills) {
2323
+ allSkillPlugins.push(skill.id);
2324
+ }
2325
+ verbose(` Compiled agent: ${name}`);
2326
+ }
2327
+ const stackDir = path13.join(projectRoot, DIRS.stacks, stackId);
2328
+ const claudeMdPath = path13.join(stackDir, STANDARD_FILES.CLAUDE_MD);
2329
+ if (await fileExists(claudeMdPath)) {
2330
+ const claudeContent = await readFile(claudeMdPath);
2331
+ await writeFile(path13.join(pluginDir, STANDARD_FILES.CLAUDE_MD), claudeContent);
2332
+ verbose(` Copied ${STANDARD_FILES.CLAUDE_MD}`);
2333
+ }
2334
+ const newHash = hashStackConfig(stack);
2335
+ const { version, contentHash } = await determinePluginVersion(
2336
+ newHash,
2337
+ pluginDir,
2338
+ getPluginManifestPath
2339
+ );
2340
+ const uniqueSkillPlugins = unique3(allSkillPlugins);
2341
+ const manifest = generateStackPluginManifest({
2342
+ stackName: stackId,
2343
+ description: stack.description,
2344
+ author: stack.author,
2345
+ version,
2346
+ keywords: void 0,
2347
+ hasAgents: true,
2348
+ hasHooks: false,
2349
+ hasSkills: true
2350
+ });
2351
+ await writePluginManifest(pluginDir, manifest);
2352
+ await writeContentHash(pluginDir, contentHash, getPluginManifestPath);
2353
+ verbose(` Wrote plugin.json (v${version})`);
2354
+ const readme = generateStackReadme(stackId, stack, compiledAgentNames, uniqueSkillPlugins);
2355
+ await writeFile(path13.join(pluginDir, "README.md"), readme);
2356
+ verbose(" Generated README.md");
2357
+ return {
2358
+ pluginPath: pluginDir,
2359
+ manifest,
2360
+ stackName: stack.name,
2361
+ agents: compiledAgentNames,
2362
+ skillPlugins: uniqueSkillPlugins,
2363
+ hasHooks: false
2364
+ };
2365
+ }
2366
+ function printStackCompilationSummary(result) {
2367
+ log(`
2368
+ Stack plugin compiled: ${result.stackName}`);
2369
+ log(` Path: ${result.pluginPath}`);
2370
+ log(` Agents: ${result.agents.length}`);
2371
+ for (const agent of result.agents) {
2372
+ log(` - ${agent}`);
2373
+ }
2374
+ if (result.skillPlugins.length > 0) {
2375
+ log(` Skills included: ${result.skillPlugins.length}`);
2376
+ for (const skill of result.skillPlugins) {
2377
+ log(` - ${skill}`);
2378
+ }
2379
+ }
2380
+ if (result.hasHooks) {
2381
+ log(" Hooks: enabled");
2382
+ }
2383
+ }
2384
+
2385
+ // src/cli/lib/installation/local-installer.ts
2386
+ function resolveInstallPaths(projectDir) {
2387
+ return {
2388
+ skillsDir: path14.join(projectDir, LOCAL_SKILLS_PATH),
2389
+ agentsDir: path14.join(projectDir, CLAUDE_DIR, "agents"),
2390
+ configPath: path14.join(projectDir, CLAUDE_SRC_DIR, STANDARD_FILES.CONFIG_YAML)
2391
+ };
2392
+ }
2393
+ async function prepareDirectories(paths) {
2394
+ await ensureDir(paths.skillsDir);
2395
+ await ensureDir(paths.agentsDir);
2396
+ await ensureDir(path14.dirname(paths.configPath));
2397
+ }
2398
+ async function archiveAndCopySkills(wizardResult, sourceResult, projectDir, skillsDir) {
2399
+ for (const skillId of wizardResult.selectedSkills) {
2400
+ const selectedSource = wizardResult.sourceSelections?.[skillId];
2401
+ if (selectedSource && selectedSource !== "public") {
2402
+ verbose(`Using alternate source '${selectedSource}' for ${skillId}`);
2403
+ await archiveLocalSkill(projectDir, skillId);
2404
+ }
2405
+ }
2406
+ return copySkillsToLocalFlattened(
2407
+ wizardResult.selectedSkills,
2408
+ skillsDir,
2409
+ sourceResult.matrix,
2410
+ sourceResult
2411
+ );
2412
+ }
2413
+ function buildLocalSkillsMap(copiedSkills, matrix) {
2414
+ const localSkillsForResolution = {};
2415
+ for (const copiedSkill of copiedSkills) {
2416
+ const skill = matrix.skills[copiedSkill.skillId];
2417
+ if (skill) {
2418
+ localSkillsForResolution[copiedSkill.skillId] = {
2419
+ id: copiedSkill.skillId,
2420
+ description: skill.description || "",
2421
+ path: copiedSkill.destPath,
2422
+ content: ""
2423
+ // Content not needed for skill references
2424
+ };
2425
+ }
2426
+ }
2427
+ return localSkillsForResolution;
2428
+ }
2429
+ async function loadMergedAgents(sourcePath) {
2430
+ const cliAgents = await loadAllAgents(PROJECT_ROOT);
2431
+ const sourceAgents = await loadAllAgents(sourcePath);
2432
+ return { ...cliAgents, ...sourceAgents };
2433
+ }
2434
+ async function buildLocalConfig(wizardResult, sourceResult) {
2435
+ let loadedStack = null;
2436
+ if (wizardResult.selectedStackId) {
2437
+ loadedStack = await loadStackById(wizardResult.selectedStackId, sourceResult.sourcePath);
2438
+ if (!loadedStack) {
2439
+ loadedStack = await loadStackById(wizardResult.selectedStackId, PROJECT_ROOT);
2440
+ }
2441
+ }
2442
+ let localConfig;
2443
+ if (wizardResult.selectedStackId) {
2444
+ if (loadedStack) {
2445
+ localConfig = generateProjectConfigFromSkills(
2446
+ DEFAULT_PLUGIN_NAME,
2447
+ wizardResult.selectedSkills,
2448
+ sourceResult.matrix
2449
+ );
2450
+ localConfig.description = loadedStack.description;
2451
+ const stackAgentIds = typedKeys(loadedStack.agents);
2452
+ for (const agentId of stackAgentIds) {
2453
+ if (!localConfig.agents.includes(agentId)) {
2454
+ localConfig.agents.push(agentId);
2455
+ }
2456
+ }
2457
+ localConfig.agents.sort();
2458
+ } else {
2459
+ throw new Error(
2460
+ `Stack '${wizardResult.selectedStackId}' not found in config/stacks.yaml. Available stacks are defined in the CLI's config/stacks.yaml file.`
2461
+ );
2462
+ }
2463
+ } else {
2464
+ localConfig = generateProjectConfigFromSkills(
2465
+ DEFAULT_PLUGIN_NAME,
2466
+ wizardResult.selectedSkills,
2467
+ sourceResult.matrix
2468
+ );
2469
+ }
2470
+ return { config: localConfig, loadedStack };
2471
+ }
2472
+ function setConfigMetadata(config, wizardResult, sourceResult, sourceFlag) {
2473
+ config.installMode = wizardResult.installMode;
2474
+ if (sourceFlag) {
2475
+ config.source = sourceFlag;
2476
+ } else if (sourceResult.sourceConfig.source) {
2477
+ config.source = sourceResult.sourceConfig.source;
2478
+ }
2479
+ if (sourceResult.marketplace) {
2480
+ config.marketplace = sourceResult.marketplace;
2481
+ }
2482
+ }
2483
+ async function buildAndMergeConfig(wizardResult, sourceResult, projectDir, sourceFlag) {
2484
+ const { config } = await buildLocalConfig(wizardResult, sourceResult);
2485
+ setConfigMetadata(config, wizardResult, sourceResult, sourceFlag);
2486
+ return mergeWithExistingConfig(config, { projectDir });
2487
+ }
2488
+ var PATH_OVERRIDES_COMMENT = [
2489
+ "",
2490
+ "# Custom paths (for marketplace repos with non-standard layouts):",
2491
+ `# skills_dir: ${SKILLS_DIR_PATH}`,
2492
+ `# agents_dir: ${DIRS.agents}`,
2493
+ `# stacks_file: ${STACKS_FILE_PATH}`,
2494
+ `# matrix_file: ${SKILLS_MATRIX_PATH}`,
2495
+ ""
2496
+ ].join("\n");
2497
+ async function writeConfigFile(config, configPath) {
2498
+ const schemaComment = `${yamlSchemaComment(SCHEMA_PATHS.projectConfig)}
2499
+ `;
2500
+ const serializable = config.stack ? { ...config, stack: compactStackForYaml(config.stack) } : config;
2501
+ const configYaml = stringifyYaml3(serializable, {
2502
+ indent: YAML_FORMATTING.INDENT,
2503
+ lineWidth: YAML_FORMATTING.LINE_WIDTH
2504
+ });
2505
+ await writeFile(configPath, `${schemaComment}${configYaml}${PATH_OVERRIDES_COMMENT}`);
2506
+ }
2507
+ function buildCompileAgents(config, agents) {
2508
+ const compileAgents = {};
2509
+ for (const agentId of config.agents) {
2510
+ if (agents[agentId]) {
2511
+ const agentStack = config.stack?.[agentId];
2512
+ compileAgents[agentId] = agentStack ? { skills: buildSkillRefsFromConfig(agentStack) } : {};
2513
+ }
2514
+ }
2515
+ return compileAgents;
2516
+ }
2517
+ async function compileAndWriteAgents(compileConfig, agents, localSkills, sourceResult, projectDir, agentsDir, installMode) {
2518
+ const engine = await createLiquidEngine(projectDir);
2519
+ const resolvedAgents = await resolveAgents(
2520
+ agents,
2521
+ localSkills,
2522
+ compileConfig,
2523
+ sourceResult.sourcePath
2524
+ );
2525
+ const compiledAgentNames = [];
2526
+ for (const [name, agent] of typedEntries(resolvedAgents)) {
2527
+ const output = await compileAgentForPlugin(
2528
+ name,
2529
+ agent,
2530
+ sourceResult.sourcePath,
2531
+ engine,
2532
+ installMode
2533
+ );
2534
+ await writeFile(path14.join(agentsDir, `${name}.md`), output);
2535
+ compiledAgentNames.push(name);
2536
+ }
2537
+ return compiledAgentNames;
2538
+ }
2539
+ async function installPluginConfig(options) {
2540
+ const { wizardResult, sourceResult, projectDir, sourceFlag } = options;
2541
+ const paths = resolveInstallPaths(projectDir);
2542
+ await ensureDir(paths.agentsDir);
2543
+ await ensureDir(path14.dirname(paths.configPath));
2544
+ const agents = await loadMergedAgents(sourceResult.sourcePath);
2545
+ const mergeResult = await buildAndMergeConfig(wizardResult, sourceResult, projectDir, sourceFlag);
2546
+ const finalConfig = mergeResult.config;
2547
+ await writeConfigFile(finalConfig, paths.configPath);
2548
+ const compileAgentsConfig = buildCompileAgents(finalConfig, agents);
2549
+ const compileConfig = {
2550
+ name: DEFAULT_PLUGIN_NAME,
2551
+ description: finalConfig.description || `Plugin setup with ${wizardResult.selectedSkills.length} skills`,
2552
+ agents: compileAgentsConfig
2553
+ };
2554
+ const stackSkillIds = finalConfig.stack ? getStackSkillIds(finalConfig.stack) : [];
2555
+ const skillsForCompilation = await loadSkillsByIds(
2556
+ stackSkillIds.map((id) => ({ id })),
2557
+ sourceResult.sourcePath
2558
+ );
2559
+ const compiledAgentNames = await compileAndWriteAgents(
2560
+ compileConfig,
2561
+ agents,
2562
+ skillsForCompilation,
2563
+ sourceResult,
2564
+ projectDir,
2565
+ paths.agentsDir,
2566
+ wizardResult.installMode
2567
+ );
2568
+ return {
2569
+ config: finalConfig,
2570
+ configPath: paths.configPath,
2571
+ compiledAgents: compiledAgentNames,
2572
+ wasMerged: mergeResult.merged,
2573
+ mergedConfigPath: mergeResult.existingConfigPath,
2574
+ agentsDir: paths.agentsDir
2575
+ };
2576
+ }
2577
+ async function installLocal(options) {
2578
+ const { wizardResult, sourceResult, projectDir, sourceFlag } = options;
2579
+ const paths = resolveInstallPaths(projectDir);
2580
+ await prepareDirectories(paths);
2581
+ const copiedSkills = await archiveAndCopySkills(
2582
+ wizardResult,
2583
+ sourceResult,
2584
+ projectDir,
2585
+ paths.skillsDir
2586
+ );
2587
+ const localSkillsForResolution = buildLocalSkillsMap(copiedSkills, sourceResult.matrix);
2588
+ const agents = await loadMergedAgents(sourceResult.sourcePath);
2589
+ const mergeResult = await buildAndMergeConfig(wizardResult, sourceResult, projectDir, sourceFlag);
2590
+ const finalConfig = mergeResult.config;
2591
+ await writeConfigFile(finalConfig, paths.configPath);
2592
+ const compileAgentsConfig = buildCompileAgents(finalConfig, agents);
2593
+ const compileConfig = {
2594
+ name: DEFAULT_PLUGIN_NAME,
2595
+ description: finalConfig.description || `Local setup with ${wizardResult.selectedSkills.length} skills`,
2596
+ agents: compileAgentsConfig
2597
+ };
2598
+ const compiledAgentNames = await compileAndWriteAgents(
2599
+ compileConfig,
2600
+ agents,
2601
+ localSkillsForResolution,
2602
+ sourceResult,
2603
+ projectDir,
2604
+ paths.agentsDir,
2605
+ wizardResult.installMode
2606
+ );
2607
+ return {
2608
+ copiedSkills,
2609
+ config: finalConfig,
2610
+ configPath: paths.configPath,
2611
+ compiledAgents: compiledAgentNames,
2612
+ wasMerged: mergeResult.merged,
2613
+ mergedConfigPath: mergeResult.existingConfigPath,
2614
+ skillsDir: paths.skillsDir,
2615
+ agentsDir: paths.agentsDir
2616
+ };
2617
+ }
2618
+
2619
+ // src/cli/lib/plugins/plugin-discovery.ts
2620
+ init_esm_shims();
2621
+
2622
+ // src/cli/lib/plugins/plugin-settings.ts
2623
+ init_esm_shims();
2624
+ import path15 from "path";
2625
+ import os from "os";
2626
+ import { z as z2 } from "zod";
2627
+ var pluginSettingsSchema = z2.object({
2628
+ enabledPlugins: z2.record(z2.string(), z2.unknown()).optional()
2629
+ }).passthrough();
2630
+ var pluginInstallationSchema = z2.object({
2631
+ scope: z2.enum(["user", "project", "local"]),
2632
+ projectPath: z2.string().optional(),
2633
+ installPath: z2.string(),
2634
+ version: z2.string(),
2635
+ installedAt: z2.string(),
2636
+ lastUpdated: z2.string().optional(),
2637
+ gitCommitSha: z2.string().optional()
2638
+ });
2639
+ var installedPluginsSchema = z2.object({
2640
+ version: z2.number(),
2641
+ plugins: z2.record(z2.string(), z2.array(pluginInstallationSchema))
2642
+ }).passthrough();
2643
+ var SETTINGS_FILE = "settings.json";
2644
+ var INSTALLED_PLUGINS_FILE = "installed_plugins.json";
2645
+ async function getEnabledPluginKeys(projectDir) {
2646
+ const settingsPath = path15.join(projectDir, CLAUDE_DIR, SETTINGS_FILE);
2647
+ if (!await fileExists(settingsPath)) {
2648
+ verbose(`No settings.json found at '${settingsPath}'`);
2649
+ return [];
2650
+ }
2651
+ try {
2652
+ const content = await readFileSafe(settingsPath, MAX_CONFIG_FILE_SIZE);
2653
+ const raw = JSON.parse(content);
2654
+ const result = pluginSettingsSchema.safeParse(raw);
2655
+ if (!result.success) {
2656
+ verbose(`Invalid settings.json structure: ${getErrorMessage(result.error)}`);
2657
+ return [];
2658
+ }
2659
+ const settings = result.data;
2660
+ if (!settings.enabledPlugins) {
2661
+ verbose(`No enabledPlugins found in '${settingsPath}'`);
2662
+ return [];
2663
+ }
2664
+ const enabledKeys = typedEntries(settings.enabledPlugins).filter(([, enabled]) => enabled === true).map(([key]) => key);
2665
+ verbose(`Found ${enabledKeys.length} enabled plugins in settings.json`);
2666
+ return enabledKeys;
2667
+ } catch (error) {
2668
+ verbose(`Failed to read settings.json: ${getErrorMessage(error)}`);
2669
+ return [];
2670
+ }
2671
+ }
2672
+ async function resolvePluginInstallPaths(pluginKeys, projectDir) {
2673
+ if (pluginKeys.length === 0) {
2674
+ return [];
2675
+ }
2676
+ const registryPath = path15.join(os.homedir(), CLAUDE_DIR, PLUGINS_SUBDIR, INSTALLED_PLUGINS_FILE);
2677
+ if (!await fileExists(registryPath)) {
2678
+ verbose(`Plugin registry not found at '${registryPath}'`);
2679
+ return [];
2680
+ }
2681
+ try {
2682
+ const content = await readFileSafe(registryPath, MAX_CONFIG_FILE_SIZE);
2683
+ const raw = JSON.parse(content);
2684
+ const result = installedPluginsSchema.safeParse(raw);
2685
+ if (!result.success) {
2686
+ verbose(`Invalid plugin registry structure: ${getErrorMessage(result.error)}`);
2687
+ return [];
2688
+ }
2689
+ const registry = result.data;
2690
+ const resolvedPaths = [];
2691
+ for (const pluginKey of pluginKeys) {
2692
+ const installations = registry.plugins[pluginKey];
2693
+ if (!installations || installations.length === 0) {
2694
+ verbose(`Plugin '${pluginKey}' not found in registry`);
2695
+ continue;
2696
+ }
2697
+ const projectInstall = installations.find(
2698
+ (install) => install.scope === "project" && install.projectPath === projectDir
2699
+ );
2700
+ if (projectInstall) {
2701
+ resolvedPaths.push({
2702
+ pluginKey,
2703
+ installPath: projectInstall.installPath
2704
+ });
2705
+ verbose(`Resolved '${pluginKey}' to '${projectInstall.installPath}'`);
2706
+ continue;
2707
+ }
2708
+ const userInstall = installations.find((install) => install.scope === "user");
2709
+ if (userInstall) {
2710
+ resolvedPaths.push({
2711
+ pluginKey,
2712
+ installPath: userInstall.installPath
2713
+ });
2714
+ verbose(`Resolved '${pluginKey}' to '${userInstall.installPath}' (user scope)`);
2715
+ continue;
2716
+ }
2717
+ verbose(`No matching installation found for '${pluginKey}'`);
2718
+ }
2719
+ return resolvedPaths;
2720
+ } catch (error) {
2721
+ verbose(`Failed to read plugin registry: ${getErrorMessage(error)}`);
2722
+ return [];
2723
+ }
2724
+ }
2725
+ async function getVerifiedPluginInstallPaths(projectDir) {
2726
+ const enabledKeys = await getEnabledPluginKeys(projectDir);
2727
+ const resolvedPaths = await resolvePluginInstallPaths(enabledKeys, projectDir);
2728
+ const verified = [];
2729
+ for (const { pluginKey, installPath } of resolvedPaths) {
2730
+ const pluginJsonPath = path15.join(installPath, PLUGIN_MANIFEST_DIR, PLUGIN_MANIFEST_FILE);
2731
+ if (await fileExists(pluginJsonPath)) {
2732
+ verified.push({ pluginKey, installPath });
2733
+ } else {
2734
+ verbose(`Plugin '${pluginKey}' manifest does not exist at: '${pluginJsonPath}'`);
2735
+ }
2736
+ }
2737
+ verbose(`Verified ${verified.length} plugin install paths`);
2738
+ return verified;
2739
+ }
2740
+
2741
+ // src/cli/lib/plugins/plugin-discovery.ts
2742
+ async function discoverAllPluginSkills(projectDir) {
2743
+ const allSkills = {};
2744
+ try {
2745
+ const pluginPaths = await getVerifiedPluginInstallPaths(projectDir);
2746
+ if (pluginPaths.length === 0) {
2747
+ verbose(`No enabled plugins found in settings.json`);
2748
+ return allSkills;
2749
+ }
2750
+ for (const { pluginKey, installPath } of pluginPaths) {
2751
+ verbose(`Discovering skills from plugin: '${pluginKey}'`);
2752
+ try {
2753
+ const pluginSkills = await loadPluginSkills(installPath);
2754
+ for (const [id, skill] of typedEntries(pluginSkills)) {
2755
+ if (skill) {
2756
+ allSkills[id] = skill;
2757
+ }
2758
+ }
2759
+ } catch (error) {
2760
+ verbose(`Failed to load skills from '${pluginKey}': ${getErrorMessage(error)}`);
2761
+ }
2762
+ }
2763
+ } catch (error) {
2764
+ verbose(`Plugin discovery failed: ${getErrorMessage(error)}`);
2765
+ }
2766
+ return allSkills;
2767
+ }
2768
+ async function hasIndividualPlugins(projectDir) {
2769
+ try {
2770
+ const pluginPaths = await getVerifiedPluginInstallPaths(projectDir);
2771
+ return pluginPaths.length > 0;
2772
+ } catch (error) {
2773
+ verbose(`Failed to check for individual plugins: ${getErrorMessage(error)}`);
2774
+ return false;
2775
+ }
2776
+ }
2777
+ async function listPluginNames(projectDir) {
2778
+ try {
2779
+ const pluginPaths = await getVerifiedPluginInstallPaths(projectDir);
2780
+ return pluginPaths.map(({ pluginKey }) => pluginKey);
2781
+ } catch (error) {
2782
+ verbose(`Failed to list plugin names: ${getErrorMessage(error)}`);
2783
+ return [];
2784
+ }
2785
+ }
2786
+
2787
+ // src/cli/lib/plugins/plugin-info.ts
2788
+ async function getInstallationInfo() {
2789
+ const installation = await detectInstallation();
2790
+ if (!installation) {
2791
+ return null;
2792
+ }
2793
+ let skillCount = 0;
2794
+ let agentCount = 0;
2795
+ let name = DEFAULT_PLUGIN_NAME;
2796
+ let version = DEFAULT_DISPLAY_VERSION;
2797
+ if (installation.mode === "plugin") {
2798
+ try {
2799
+ const pluginSkills = await discoverAllPluginSkills(installation.projectDir);
2800
+ skillCount = Object.keys(pluginSkills).length;
2801
+ } catch {
2802
+ }
2803
+ } else if (await directoryExists(installation.skillsDir)) {
2804
+ try {
2805
+ const skills = await readdir(installation.skillsDir, {
2806
+ withFileTypes: true
2807
+ });
2808
+ skillCount = skills.filter((s) => s.isDirectory()).length;
2809
+ } catch {
2810
+ }
2811
+ }
2812
+ if (await directoryExists(installation.agentsDir)) {
2813
+ try {
2814
+ const agents = await readdir(installation.agentsDir, {
2815
+ withFileTypes: true
2816
+ });
2817
+ agentCount = agents.filter((a) => a.isFile() && a.name.endsWith(".md")).length;
2818
+ } catch {
2819
+ }
2820
+ }
2821
+ const loaded = await loadProjectConfig(installation.projectDir);
2822
+ if (loaded?.config) {
2823
+ name = loaded.config.name || DEFAULT_PLUGIN_NAME;
2824
+ version = installation.mode === "local" ? "local" : "plugin";
2825
+ }
2826
+ return {
2827
+ mode: installation.mode,
2828
+ name,
2829
+ version,
2830
+ skillCount,
2831
+ agentCount,
2832
+ configPath: installation.configPath,
2833
+ agentsDir: installation.agentsDir,
2834
+ skillsDir: installation.skillsDir
2835
+ };
2836
+ }
2837
+ function formatInstallationDisplay(info) {
2838
+ const modeLabel = info.mode === "local" ? "Local" : "Plugin";
2839
+ const versionDisplay = info.mode === "local" ? "(local mode)" : `v${info.version}`;
2840
+ return `Installation: ${info.name} ${versionDisplay}
2841
+ Mode: ${modeLabel}
2842
+ Skills: ${info.skillCount}
2843
+ Agents: ${info.agentCount}
2844
+ Config: ${info.configPath}
2845
+ Agents: ${info.agentsDir}`;
2846
+ }
2847
+
2848
+ // src/cli/lib/plugins/plugin-version.ts
2849
+ init_esm_shims();
2850
+ import path16 from "path";
2851
+ function parseVersion(version) {
2852
+ const parts = version.split(".").map(Number);
2853
+ return [parts[0] || 1, parts[1] || 0, parts[2] || 0];
2854
+ }
2855
+ async function bumpPluginVersion(pluginDir, type) {
2856
+ const manifestPath = path16.join(pluginDir, PLUGIN_MANIFEST_DIR, PLUGIN_MANIFEST_FILE);
2857
+ const content = await readFileSafe(manifestPath, MAX_PLUGIN_FILE_SIZE);
2858
+ const manifest = pluginManifestSchema.parse(JSON.parse(content));
2859
+ const [major, minor, patch] = parseVersion(manifest.version || DEFAULT_VERSION);
2860
+ let newVersion;
2861
+ switch (type) {
2862
+ case "major":
2863
+ newVersion = `${major + 1}.0.0`;
2864
+ break;
2865
+ case "minor":
2866
+ newVersion = `${major}.${minor + 1}.0`;
2867
+ break;
2868
+ case "patch":
2869
+ newVersion = `${major}.${minor}.${patch + 1}`;
2870
+ break;
2871
+ default: {
2872
+ const exhaustiveCheck = type;
2873
+ throw new Error(`Unknown version bump type: ${exhaustiveCheck}`);
2874
+ }
2875
+ }
2876
+ manifest.version = newVersion;
2877
+ await writeFile(manifestPath, JSON.stringify(manifest, null, 2));
2878
+ return newVersion;
2879
+ }
2880
+ async function getPluginVersion(pluginDir) {
2881
+ const manifestPath = path16.join(pluginDir, PLUGIN_MANIFEST_DIR, PLUGIN_MANIFEST_FILE);
2882
+ const content = await readFileSafe(manifestPath, MAX_PLUGIN_FILE_SIZE);
2883
+ const manifest = pluginManifestSchema.parse(JSON.parse(content));
2884
+ return manifest.version || DEFAULT_VERSION;
2885
+ }
2886
+
2887
+ // src/cli/lib/plugins/plugin-validator.ts
2888
+ init_esm_shims();
2889
+ import { z as z3 } from "zod";
2890
+ import path17 from "path";
2891
+ import fg from "fast-glob";
2892
+ import { countBy } from "remeda";
2893
+ var PLUGIN_DIR = PLUGIN_MANIFEST_DIR;
2894
+ var PLUGIN_MANIFEST = STANDARD_FILES.PLUGIN_JSON;
2895
+ var KEBAB_CASE_REGEX = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
2896
+ var SEMVER_REGEX = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
2897
+ var pluginManifestValidationSchema = z3.object({
2898
+ name: z3.string(),
2899
+ version: z3.string().optional(),
2900
+ description: z3.string().optional(),
2901
+ author: pluginAuthorSchema.optional(),
2902
+ keywords: z3.array(z3.string()).optional(),
2903
+ commands: z3.union([z3.string(), z3.array(z3.string())]).optional(),
2904
+ agents: z3.union([z3.string(), z3.array(z3.string())]).optional(),
2905
+ skills: z3.union([z3.string(), z3.array(z3.string())]).optional(),
2906
+ hooks: z3.union([z3.string(), hooksRecordSchema]).optional()
2907
+ }).strict();
2908
+ function formatZodErrors2(error) {
2909
+ return error.issues.map((issue) => {
2910
+ const path25 = issue.path.join(".");
2911
+ if (issue.code === "unrecognized_keys") {
2912
+ return `Unrecognized key: "${issue.keys.join('", "')}"`;
2913
+ }
2914
+ return path25 ? `${path25}: ${issue.message}` : issue.message;
2915
+ });
2916
+ }
2917
+ function isKebabCase(str) {
2918
+ return KEBAB_CASE_REGEX.test(str);
2919
+ }
2920
+ function isValidSemver(str) {
2921
+ return SEMVER_REGEX.test(str);
2922
+ }
2923
+ async function validatePluginStructure(pluginPath) {
2924
+ const errors = [];
2925
+ const warnings = [];
2926
+ if (!await directoryExists(pluginPath)) {
2927
+ return {
2928
+ valid: false,
2929
+ errors: [`Plugin directory does not exist: ${pluginPath}`],
2930
+ warnings: []
2931
+ };
2932
+ }
2933
+ const pluginDir = path17.join(pluginPath, PLUGIN_DIR);
2934
+ if (!await directoryExists(pluginDir)) {
2935
+ errors.push(`Missing ${PLUGIN_DIR}/ directory`);
2936
+ }
2937
+ const manifestPath = path17.join(pluginDir, PLUGIN_MANIFEST);
2938
+ if (!await fileExists(manifestPath)) {
2939
+ errors.push(`Missing ${PLUGIN_DIR}/${PLUGIN_MANIFEST}`);
2940
+ }
2941
+ const readmePath = path17.join(pluginPath, "README.md");
2942
+ if (!await fileExists(readmePath)) {
2943
+ warnings.push("Missing README.md (recommended for documentation)");
2944
+ }
2945
+ return {
2946
+ valid: errors.length === 0,
2947
+ errors,
2948
+ warnings
2949
+ };
2950
+ }
2951
+ async function validatePluginManifest(manifestPath) {
2952
+ const errors = [];
2953
+ const warnings = [];
2954
+ if (!await fileExists(manifestPath)) {
2955
+ return {
2956
+ valid: false,
2957
+ errors: [`Manifest file not found: ${manifestPath}`],
2958
+ warnings: []
2959
+ };
2960
+ }
2961
+ let manifest;
2962
+ try {
2963
+ const content = await readFileSafe(manifestPath, MAX_PLUGIN_FILE_SIZE);
2964
+ manifest = JSON.parse(content);
2965
+ } catch (err) {
2966
+ return {
2967
+ valid: false,
2968
+ errors: [`Invalid JSON in ${PLUGIN_MANIFEST}: ${getErrorMessage(err)}`],
2969
+ warnings: []
2970
+ };
2971
+ }
2972
+ const result = pluginManifestValidationSchema.safeParse(manifest);
2973
+ if (!result.success) {
2974
+ errors.push(...formatZodErrors2(result.error));
2975
+ }
2976
+ if (manifest.name && typeof manifest.name === "string") {
2977
+ if (!isKebabCase(manifest.name)) {
2978
+ errors.push(`name must be kebab-case: "${manifest.name}"`);
2979
+ }
2980
+ }
2981
+ if (manifest.version && typeof manifest.version === "string") {
2982
+ if (!isValidSemver(manifest.version)) {
2983
+ errors.push(
2984
+ `version "${manifest.version}" is not valid semver (expected: major.minor.patch)`
2985
+ );
2986
+ }
2987
+ }
2988
+ if (!manifest.description) {
2989
+ warnings.push("Missing description field (recommended for discoverability)");
2990
+ }
2991
+ const pluginDir = path17.dirname(path17.dirname(manifestPath));
2992
+ if (manifest.skills && typeof manifest.skills === "string") {
2993
+ const skillsPath = path17.join(pluginDir, manifest.skills);
2994
+ if (!await directoryExists(skillsPath)) {
2995
+ errors.push(`Skills path does not exist: ${manifest.skills}`);
2996
+ }
2997
+ }
2998
+ if (manifest.agents && typeof manifest.agents === "string") {
2999
+ const agentsPath = path17.join(pluginDir, manifest.agents);
3000
+ if (!await directoryExists(agentsPath)) {
3001
+ errors.push(`Agents path does not exist: ${manifest.agents}`);
3002
+ }
3003
+ }
3004
+ return {
3005
+ valid: errors.length === 0,
3006
+ errors,
3007
+ warnings
3008
+ };
3009
+ }
3010
+ async function validateSkillFrontmatter(skillPath) {
3011
+ const errors = [];
3012
+ const warnings = [];
3013
+ if (!await fileExists(skillPath)) {
3014
+ return {
3015
+ valid: false,
3016
+ errors: [`Skill file not found: ${skillPath}`],
3017
+ warnings: []
3018
+ };
3019
+ }
3020
+ const content = await readFile(skillPath);
3021
+ const frontmatter = extractFrontmatter(content);
3022
+ if (frontmatter === null) {
3023
+ return {
3024
+ valid: false,
3025
+ errors: ["Missing or invalid YAML frontmatter"],
3026
+ warnings: []
3027
+ };
3028
+ }
3029
+ const result = skillFrontmatterValidationSchema.safeParse(frontmatter);
3030
+ if (!result.success) {
3031
+ errors.push(...formatZodErrors2(result.error));
3032
+ }
3033
+ return {
3034
+ valid: errors.length === 0,
3035
+ errors,
3036
+ warnings
3037
+ };
3038
+ }
3039
+ async function validateAgentFrontmatter(agentPath) {
3040
+ const errors = [];
3041
+ const warnings = [];
3042
+ if (!await fileExists(agentPath)) {
3043
+ return {
3044
+ valid: false,
3045
+ errors: [`Agent file not found: ${agentPath}`],
3046
+ warnings: []
3047
+ };
3048
+ }
3049
+ const content = await readFile(agentPath);
3050
+ const frontmatter = extractFrontmatter(content);
3051
+ if (frontmatter === null) {
3052
+ return {
3053
+ valid: false,
3054
+ errors: ["Missing or invalid YAML frontmatter"],
3055
+ warnings: []
3056
+ };
3057
+ }
3058
+ const result = agentFrontmatterValidationSchema.safeParse(frontmatter);
3059
+ if (!result.success) {
3060
+ errors.push(...formatZodErrors2(result.error));
3061
+ }
3062
+ const fm = frontmatter;
3063
+ if (fm.name && typeof fm.name === "string") {
3064
+ if (!isKebabCase(fm.name)) {
3065
+ errors.push(`name must be kebab-case: "${fm.name}"`);
3066
+ }
3067
+ }
3068
+ return {
3069
+ valid: errors.length === 0,
3070
+ errors,
3071
+ warnings
3072
+ };
3073
+ }
3074
+ function mergeResults(results) {
3075
+ const errors = results.flatMap((r) => r.errors);
3076
+ const warnings = results.flatMap((r) => r.warnings);
3077
+ return { valid: errors.length === 0, errors, warnings };
3078
+ }
3079
+ var EMPTY_RESULT = { valid: true, errors: [], warnings: [] };
3080
+ function prefixResult(result, prefix) {
3081
+ return {
3082
+ valid: result.valid,
3083
+ errors: result.valid ? [] : result.errors.map((e) => `${prefix}: ${e}`),
3084
+ warnings: result.warnings.map((w) => `${prefix}: ${w}`)
3085
+ };
3086
+ }
3087
+ async function validatePluginSkillFiles(pluginPath, skillsRelPath) {
3088
+ const skillsDir = path17.join(pluginPath, skillsRelPath);
3089
+ if (!await directoryExists(skillsDir)) return EMPTY_RESULT;
3090
+ const files = await fg("**/SKILL.md", { cwd: skillsDir, absolute: true });
3091
+ if (files.length === 0) {
3092
+ return {
3093
+ valid: true,
3094
+ errors: [],
3095
+ warnings: [`Skills directory exists but contains no SKILL.md files: ${skillsRelPath}`]
3096
+ };
3097
+ }
3098
+ const results = await Promise.all(
3099
+ files.map(
3100
+ async (f) => prefixResult(await validateSkillFrontmatter(f), path17.relative(pluginPath, f))
3101
+ )
3102
+ );
3103
+ return mergeResults(results);
3104
+ }
3105
+ async function validatePluginAgentFiles(pluginPath, agentsRelPath) {
3106
+ const agentsDir = path17.join(pluginPath, agentsRelPath);
3107
+ if (!await directoryExists(agentsDir)) return EMPTY_RESULT;
3108
+ const files = await fg("*.md", { cwd: agentsDir, absolute: true });
3109
+ if (files.length === 0) {
3110
+ return {
3111
+ valid: true,
3112
+ errors: [],
3113
+ warnings: [`Agents directory exists but contains no .md files: ${agentsRelPath}`]
3114
+ };
3115
+ }
3116
+ const results = await Promise.all(
3117
+ files.map(
3118
+ async (f) => prefixResult(await validateAgentFrontmatter(f), path17.relative(pluginPath, f))
3119
+ )
3120
+ );
3121
+ return mergeResults(results);
3122
+ }
3123
+ async function loadManifestForValidation(manifestPath) {
3124
+ try {
3125
+ const content = await readFileSafe(manifestPath, MAX_PLUGIN_FILE_SIZE);
3126
+ return JSON.parse(content);
3127
+ } catch {
3128
+ return null;
3129
+ }
3130
+ }
3131
+ async function validatePlugin(pluginPath) {
3132
+ const structureResult = await validatePluginStructure(pluginPath);
3133
+ if (!structureResult.valid) return structureResult;
3134
+ const manifestPath = path17.join(pluginPath, PLUGIN_DIR, PLUGIN_MANIFEST);
3135
+ const manifestResult = await validatePluginManifest(manifestPath);
3136
+ const manifest = await loadManifestForValidation(manifestPath);
3137
+ const skillsResult = manifest?.skills && typeof manifest.skills === "string" ? await validatePluginSkillFiles(pluginPath, manifest.skills) : EMPTY_RESULT;
3138
+ const agentsResult = manifest?.agents && typeof manifest.agents === "string" ? await validatePluginAgentFiles(pluginPath, manifest.agents) : EMPTY_RESULT;
3139
+ return mergeResults([structureResult, manifestResult, skillsResult, agentsResult]);
3140
+ }
3141
+ async function validateAllPlugins(pluginsDir) {
3142
+ const results = [];
3143
+ if (!await directoryExists(pluginsDir)) {
3144
+ return {
3145
+ valid: false,
3146
+ results: [
3147
+ {
3148
+ name: pluginsDir,
3149
+ result: {
3150
+ valid: false,
3151
+ errors: [`Directory does not exist: ${pluginsDir}`],
3152
+ warnings: []
3153
+ }
3154
+ }
3155
+ ],
3156
+ summary: { total: 0, valid: 0, invalid: 1, withWarnings: 0 }
3157
+ };
3158
+ }
3159
+ const allDirs = await listDirectories(pluginsDir);
3160
+ const pluginDirs = [];
3161
+ for (const dirName of allDirs) {
3162
+ const potentialPluginDir = path17.join(pluginsDir, dirName, PLUGIN_DIR);
3163
+ if (await directoryExists(potentialPluginDir)) {
3164
+ pluginDirs.push(dirName);
3165
+ }
3166
+ }
3167
+ if (pluginDirs.length === 0) {
3168
+ return {
3169
+ valid: false,
3170
+ results: [
3171
+ {
3172
+ name: pluginsDir,
3173
+ result: {
3174
+ valid: false,
3175
+ errors: [
3176
+ `No plugins found in directory: ${pluginsDir}. Plugins must contain a ${PLUGIN_DIR}/ directory.`
3177
+ ],
3178
+ warnings: []
3179
+ }
3180
+ }
3181
+ ],
3182
+ summary: { total: 0, valid: 0, invalid: 1, withWarnings: 0 }
3183
+ };
3184
+ }
3185
+ for (const pluginName of pluginDirs) {
3186
+ const pluginPath = path17.join(pluginsDir, pluginName);
3187
+ const result = await validatePlugin(pluginPath);
3188
+ results.push({ name: pluginName, result });
3189
+ }
3190
+ const summary = {
3191
+ total: results.length,
3192
+ valid: countBy(results, (r) => String(r.result.valid))["true"] ?? 0,
3193
+ invalid: countBy(results, (r) => String(r.result.valid))["false"] ?? 0,
3194
+ withWarnings: countBy(results, (r) => String(r.result.warnings.length > 0))["true"] ?? 0
3195
+ };
3196
+ return {
3197
+ valid: summary.invalid === 0,
3198
+ results,
3199
+ summary
3200
+ };
3201
+ }
3202
+ function printPluginValidationResult(name, result, verbose2 = false) {
3203
+ const status = result.valid ? "\u2713" : "\u2717";
3204
+ if (result.valid && result.warnings.length === 0 && !verbose2) {
3205
+ return;
3206
+ }
3207
+ log(`
3208
+ ${status} ${name}`);
3209
+ if (result.errors.length > 0) {
3210
+ log(" Errors:");
3211
+ result.errors.forEach((e) => log(` - ${e}`));
3212
+ }
3213
+ if (result.warnings.length > 0) {
3214
+ log(" Warnings:");
3215
+ result.warnings.forEach((w) => log(` - ${w}`));
3216
+ }
3217
+ }
3218
+
3219
+ // src/cli/lib/loading/multi-source-loader.ts
3220
+ var PUBLIC_SOURCE_NAME = "public";
3221
+ async function loadSkillsFromAllSources(primaryMatrix, _sourceConfig, projectDir) {
3222
+ tagPrimarySourceSkills(primaryMatrix);
3223
+ tagLocalSkills(primaryMatrix);
3224
+ await tagPluginSkills(primaryMatrix, projectDir);
3225
+ await tagExtraSources(primaryMatrix, projectDir);
3226
+ setActiveSources(primaryMatrix);
3227
+ }
3228
+ function tagPrimarySourceSkills(matrix) {
3229
+ for (const [, skill] of typedEntries(matrix.skills)) {
3230
+ if (!skill) continue;
3231
+ const source = {
3232
+ name: PUBLIC_SOURCE_NAME,
3233
+ type: "public",
3234
+ version: skill.version,
3235
+ installed: false
3236
+ };
3237
+ skill.availableSources = skill.availableSources ?? [];
3238
+ skill.availableSources.push(source);
3239
+ }
3240
+ }
3241
+ function tagLocalSkills(matrix) {
3242
+ let count = 0;
3243
+ for (const [, skill] of typedEntries(matrix.skills)) {
3244
+ if (!skill) continue;
3245
+ if (!skill.local) continue;
3246
+ const source = {
3247
+ name: "local",
3248
+ type: "local",
3249
+ installed: true,
3250
+ installMode: "local"
3251
+ };
3252
+ skill.availableSources = skill.availableSources ?? [];
3253
+ skill.availableSources.push(source);
3254
+ count++;
3255
+ }
3256
+ verbose(`Tagged ${count} local skills with local source`);
3257
+ }
3258
+ async function tagPluginSkills(matrix, projectDir) {
3259
+ const allPluginSkillIds = await collectPluginSkillIds(matrix, projectDir);
3260
+ if (allPluginSkillIds.length === 0) {
3261
+ return;
3262
+ }
3263
+ for (const skillId of allPluginSkillIds) {
3264
+ const skill = matrix.skills[skillId];
3265
+ if (!skill) continue;
3266
+ skill.availableSources = skill.availableSources ?? [];
3267
+ const existingSource = skill.availableSources.find((s) => s.type === "public");
3268
+ if (existingSource && !existingSource.installMode) {
3269
+ existingSource.installed = true;
3270
+ existingSource.installMode = "plugin";
3271
+ } else if (!skill.availableSources.some((s) => s.installMode === "plugin")) {
3272
+ skill.availableSources.push({
3273
+ name: PUBLIC_SOURCE_NAME,
3274
+ type: "public",
3275
+ version: skill.version,
3276
+ installed: true,
3277
+ installMode: "plugin"
3278
+ });
3279
+ }
3280
+ }
3281
+ verbose(`Tagged ${allPluginSkillIds.length} plugin-installed skills`);
3282
+ }
3283
+ async function collectPluginSkillIds(_matrix, projectDir) {
3284
+ const pluginSkills = await discoverAllPluginSkills(projectDir);
3285
+ const skillIds = Object.keys(pluginSkills);
3286
+ if (skillIds.length === 0) {
3287
+ verbose("No plugin skills discovered from settings.json");
3288
+ }
3289
+ return skillIds;
3290
+ }
3291
+ async function tagExtraSources(matrix, projectDir) {
3292
+ let allSources;
3293
+ try {
3294
+ allSources = await resolveAllSources(projectDir);
3295
+ } catch (error) {
3296
+ verbose(`Failed to resolve extra sources: ${error}`);
3297
+ return;
3298
+ }
3299
+ if (allSources.extras.length === 0) {
3300
+ verbose("No extra sources configured");
3301
+ return;
3302
+ }
3303
+ for (const extraSource of allSources.extras) {
3304
+ verbose(`Loading extra source: ${extraSource.name} (${extraSource.url})`);
3305
+ try {
3306
+ const fetchResult = await fetchFromSource(extraSource.url, { forceRefresh: false });
3307
+ const skillsDir = path18.join(fetchResult.path, SKILLS_DIR_PATH);
3308
+ const skills = await extractAllSkills(skillsDir);
3309
+ let matchCount = 0;
3310
+ for (const extractedSkill of skills) {
3311
+ const matrixSkill = matrix.skills[extractedSkill.id];
3312
+ if (!matrixSkill) continue;
3313
+ const source = {
3314
+ name: extraSource.name,
3315
+ type: "private",
3316
+ url: extraSource.url,
3317
+ installed: false
3318
+ };
3319
+ matrixSkill.availableSources = matrixSkill.availableSources ?? [];
3320
+ matrixSkill.availableSources.push(source);
3321
+ matchCount++;
3322
+ }
3323
+ verbose(
3324
+ `Extra source '${extraSource.name}': ${skills.length} skills found, ${matchCount} matching`
3325
+ );
3326
+ } catch (error) {
3327
+ warn(`Failed to load extra source '${extraSource.name}' ('${extraSource.url}'): ${error}`);
3328
+ }
3329
+ }
3330
+ }
3331
+ function setActiveSources(matrix) {
3332
+ for (const [, skill] of typedEntries(matrix.skills)) {
3333
+ if (!skill) continue;
3334
+ if (!skill.availableSources || skill.availableSources.length === 0) continue;
3335
+ const installedSource = skill.availableSources.find((s) => s.installed);
3336
+ skill.activeSource = installedSource ?? skill.availableSources[0];
3337
+ }
3338
+ }
3339
+ async function searchExtraSources(alias, configuredSources) {
3340
+ const candidates = [];
3341
+ if (configuredSources.length === 0) {
3342
+ return candidates;
3343
+ }
3344
+ const lowerAlias = alias.toLowerCase();
3345
+ for (const source of configuredSources) {
3346
+ try {
3347
+ const fetchResult = await fetchFromSource(source.url, { forceRefresh: false });
3348
+ const skillsDir = path18.join(fetchResult.path, SKILLS_DIR_PATH);
3349
+ const skills = await extractAllSkills(skillsDir);
3350
+ for (const skill of skills) {
3351
+ const segments = skill.directoryPath.split("/");
3352
+ const lastSegment = segments[segments.length - 1]?.toLowerCase();
3353
+ if (lastSegment === lowerAlias) {
3354
+ candidates.push({
3355
+ id: skill.id,
3356
+ sourceUrl: source.url,
3357
+ sourceName: source.name,
3358
+ alias,
3359
+ description: skill.description
3360
+ });
3361
+ }
3362
+ }
3363
+ } catch (error) {
3364
+ warn(`Failed to search extra source '${source.name}' ('${source.url}'): ${error}`);
3365
+ }
3366
+ }
3367
+ return candidates;
3368
+ }
3369
+
3370
+ // src/cli/lib/loading/source-loader.ts
3371
+ async function loadSkillsMatrixFromSource(options = {}) {
3372
+ const { sourceFlag, projectDir, forceRefresh = false, devMode = false } = options;
3373
+ const sourceConfig = await resolveSource(sourceFlag, projectDir);
3374
+ const { source } = sourceConfig;
3375
+ verbose(`Loading skills from source: ${source}`);
3376
+ const isLocal = isLocalSource(source) || devMode === true;
3377
+ let result;
3378
+ if (isLocal) {
3379
+ result = await loadFromLocal(source, sourceConfig);
3380
+ } else {
3381
+ result = await loadFromRemote(source, sourceConfig, forceRefresh);
3382
+ }
3383
+ const resolvedProjectDir = projectDir || process.cwd();
3384
+ const localSkillsResult = await discoverLocalSkills(resolvedProjectDir);
3385
+ if (localSkillsResult && localSkillsResult.skills.length > 0) {
3386
+ verbose(
3387
+ `Found ${localSkillsResult.skills.length} local skill(s) in ${localSkillsResult.localSkillsPath}`
3388
+ );
3389
+ result.matrix = mergeLocalSkillsIntoMatrix(result.matrix, localSkillsResult);
3390
+ }
3391
+ await loadSkillsFromAllSources(result.matrix, sourceConfig, resolvedProjectDir);
3392
+ checkMatrixHealth(result.matrix);
3393
+ return result;
3394
+ }
3395
+ async function loadFromLocal(source, sourceConfig) {
3396
+ let skillsPath;
3397
+ if (isLocalSource(source)) {
3398
+ skillsPath = path19.isAbsolute(source) ? source : path19.resolve(process.cwd(), source);
3399
+ } else {
3400
+ skillsPath = PROJECT_ROOT;
3401
+ }
3402
+ verbose(`Loading skills from local path: ${skillsPath}`);
3403
+ const mergedMatrix = await loadAndMergeFromBasePath(skillsPath);
3404
+ return {
3405
+ matrix: mergedMatrix,
3406
+ sourceConfig,
3407
+ sourcePath: skillsPath,
3408
+ isLocal: true,
3409
+ marketplace: sourceConfig.marketplace
3410
+ };
3411
+ }
3412
+ async function loadFromRemote(source, sourceConfig, forceRefresh) {
3413
+ verbose(`Fetching skills from remote source: ${source}`);
3414
+ const fetchResult = await fetchFromSource(source, { forceRefresh });
3415
+ verbose(`Fetched to: ${fetchResult.path}`);
3416
+ const mergedMatrix = await loadAndMergeFromBasePath(fetchResult.path);
3417
+ let marketplace = sourceConfig.marketplace;
3418
+ if (!marketplace) {
3419
+ try {
3420
+ const marketplaceResult = await fetchMarketplace(source, { forceRefresh });
3421
+ marketplace = marketplaceResult.marketplace.name;
3422
+ verbose(`Using marketplace name from marketplace.json: ${marketplace}`);
3423
+ } catch {
3424
+ warn(`Source does not have a marketplace.json - falling back to local mode`);
3425
+ }
3426
+ }
3427
+ return {
3428
+ matrix: mergedMatrix,
3429
+ sourceConfig,
3430
+ sourcePath: fetchResult.path,
3431
+ isLocal: false,
3432
+ marketplace
3433
+ };
3434
+ }
3435
+ async function loadAndMergeFromBasePath(basePath) {
3436
+ const sourceProjectConfig = await loadProjectSourceConfig(basePath);
3437
+ const matrixRelPath = sourceProjectConfig?.matrix_file ?? SKILLS_MATRIX_PATH;
3438
+ const skillsDirRelPath = sourceProjectConfig?.skills_dir ?? SKILLS_DIR_PATH;
3439
+ const stacksRelFile = sourceProjectConfig?.stacks_file;
3440
+ const sourceMatrixPath = path19.join(basePath, matrixRelPath);
3441
+ const cliMatrixPath = path19.join(PROJECT_ROOT, SKILLS_MATRIX_PATH);
3442
+ let matrixPath;
3443
+ if (await fileExists(sourceMatrixPath)) {
3444
+ matrixPath = sourceMatrixPath;
3445
+ verbose(`Matrix from source: ${matrixPath}`);
3446
+ } else {
3447
+ matrixPath = cliMatrixPath;
3448
+ verbose(`Matrix from CLI (source has no matrix): ${matrixPath}`);
3449
+ }
3450
+ const skillsDir = path19.join(basePath, skillsDirRelPath);
3451
+ verbose(`Skills from source: ${skillsDir}`);
3452
+ const matrix = await loadSkillsMatrix(matrixPath);
3453
+ const skills = await extractAllSkills(skillsDir);
3454
+ const mergedMatrix = await mergeMatrixWithSkills(matrix, skills);
3455
+ const sourceStacks = await loadStacks(basePath, stacksRelFile);
3456
+ const stacks = sourceStacks.length > 0 ? sourceStacks : await loadStacks(PROJECT_ROOT);
3457
+ if (stacks.length > 0) {
3458
+ mergedMatrix.suggestedStacks = stacks.map((stack) => convertStackToResolvedStack(stack));
3459
+ const stackSource = sourceStacks.length > 0 ? "source" : "CLI";
3460
+ verbose(`Loaded ${stacks.length} stacks from ${stackSource}`);
3461
+ }
3462
+ return mergedMatrix;
3463
+ }
3464
+ function convertStackToResolvedStack(stack) {
3465
+ const allSkillIds = [];
3466
+ const seenSkillIds = /* @__PURE__ */ new Set();
3467
+ for (const agentId of typedKeys(stack.agents)) {
3468
+ const agentConfig = stack.agents[agentId];
3469
+ if (!agentConfig) continue;
3470
+ const skillRefs = resolveAgentConfigToSkills(agentConfig);
3471
+ for (const ref of skillRefs) {
3472
+ if (!seenSkillIds.has(ref.id)) {
3473
+ seenSkillIds.add(ref.id);
3474
+ allSkillIds.push(ref.id);
3475
+ }
3476
+ }
3477
+ }
3478
+ const agentCount = typedKeys(stack.agents).length;
3479
+ verbose(`Stack '${stack.id}' has ${allSkillIds.length} skills from ${agentCount} agents`);
3480
+ return {
3481
+ id: stack.id,
3482
+ name: stack.name,
3483
+ description: stack.description,
3484
+ audience: [],
3485
+ // Not used in new format
3486
+ skills: {},
3487
+ // Skills come from stack agent configs, resolved at runtime
3488
+ allSkillIds,
3489
+ philosophy: stack.philosophy || ""
3490
+ };
3491
+ }
3492
+ function extractSourceName(source) {
3493
+ const withoutProtocol = source.replace(/^(?:github|gh|gitlab|bitbucket|sourcehut):/, "");
3494
+ const withoutUrl = withoutProtocol.replace(/^https?:\/\/[^/]+\//, "");
3495
+ const firstSegment = withoutUrl.split("/")[0];
3496
+ return firstSegment || source;
3497
+ }
3498
+ function getMarketplaceLabel(sourceResult) {
3499
+ if (sourceResult.isLocal) return void 0;
3500
+ const { marketplace } = sourceResult;
3501
+ if (!marketplace) {
3502
+ const name = extractSourceName(sourceResult.sourceConfig.source);
3503
+ return `${name} (public)`;
3504
+ }
3505
+ const PUBLIC_MARKETPLACE_COUNT = 1;
3506
+ const isDefaultSource = sourceResult.sourceConfig.source === DEFAULT_SOURCE;
3507
+ if (!isDefaultSource) {
3508
+ return `${marketplace} + ${PUBLIC_MARKETPLACE_COUNT} public`;
3509
+ }
3510
+ return marketplace;
3511
+ }
3512
+ function mergeLocalSkillsIntoMatrix(matrix, localResult) {
3513
+ for (const metadata of localResult.skills) {
3514
+ const existingSkill = matrix.skills[metadata.id];
3515
+ const category = existingSkill?.category ?? metadata.category;
3516
+ const displayName = existingSkill?.displayName ?? matrix.displayNames[metadata.id];
3517
+ const resolvedSkill = {
3518
+ id: metadata.id,
3519
+ displayName,
3520
+ description: metadata.description,
3521
+ usageGuidance: metadata.usageGuidance,
3522
+ category,
3523
+ categoryExclusive: metadata.categoryExclusive,
3524
+ tags: metadata.tags ?? [],
3525
+ author: LOCAL_DEFAULTS.AUTHOR,
3526
+ conflictsWith: existingSkill?.conflictsWith ?? [],
3527
+ recommends: existingSkill?.recommends ?? [],
3528
+ requires: existingSkill?.requires ?? [],
3529
+ alternatives: existingSkill?.alternatives ?? [],
3530
+ discourages: existingSkill?.discourages ?? [],
3531
+ compatibleWith: existingSkill?.compatibleWith ?? [],
3532
+ requiresSetup: existingSkill?.requiresSetup ?? [],
3533
+ providesSetupFor: existingSkill?.providesSetupFor ?? [],
3534
+ path: metadata.path,
3535
+ local: true,
3536
+ localPath: metadata.localPath
3537
+ };
3538
+ matrix.skills[metadata.id] = resolvedSkill;
3539
+ verbose(`Added local skill: ${metadata.id} (category: ${category})`);
3540
+ }
3541
+ return matrix;
3542
+ }
3543
+
3544
+ // src/cli/lib/loading/defaults-loader.ts
3545
+ init_esm_shims();
3546
+ var cachedDefaults = null;
3547
+ function getCachedDefaults() {
3548
+ return cachedDefaults;
3549
+ }
3550
+
3551
+ // src/cli/lib/skills/skill-agent-mappings.ts
3552
+ var SKILL_TO_AGENTS = {
3553
+ "web/*": [
3554
+ "web-developer",
3555
+ "web-reviewer",
3556
+ "web-researcher",
3557
+ "web-pm",
3558
+ "pattern-scout",
3559
+ "web-pattern-critique",
3560
+ "agent-summoner",
3561
+ "skill-summoner",
3562
+ "documentor"
3563
+ ],
3564
+ "api/*": [
3565
+ "api-developer",
3566
+ "api-reviewer",
3567
+ "api-researcher",
3568
+ "web-architecture",
3569
+ "web-pm",
3570
+ "pattern-scout",
3571
+ "web-pattern-critique",
3572
+ "agent-summoner",
3573
+ "skill-summoner",
3574
+ "documentor"
3575
+ ],
3576
+ "mobile/*": [
3577
+ "web-developer",
3578
+ "web-reviewer",
3579
+ "web-researcher",
3580
+ "web-pm",
3581
+ "agent-summoner",
3582
+ "skill-summoner",
3583
+ "documentor"
3584
+ ],
3585
+ "infra/*": [
3586
+ "web-architecture",
3587
+ "web-developer",
3588
+ "api-developer",
3589
+ "agent-summoner",
3590
+ "skill-summoner",
3591
+ "documentor"
3592
+ ],
3593
+ "security/*": [
3594
+ "web-developer",
3595
+ "api-developer",
3596
+ "web-reviewer",
3597
+ "api-reviewer",
3598
+ "web-architecture",
3599
+ "web-pm",
3600
+ "agent-summoner",
3601
+ "skill-summoner",
3602
+ "documentor"
3603
+ ],
3604
+ "reviewing/*": [
3605
+ "web-reviewer",
3606
+ "api-reviewer",
3607
+ "cli-reviewer",
3608
+ "web-pattern-critique",
3609
+ "agent-summoner",
3610
+ "skill-summoner",
3611
+ "documentor"
3612
+ ],
3613
+ "cli/*": [
3614
+ "cli-developer",
3615
+ "cli-reviewer",
3616
+ "api-developer",
3617
+ "api-reviewer",
3618
+ "api-researcher",
3619
+ "web-architecture",
3620
+ "web-pm",
3621
+ "agent-summoner",
3622
+ "skill-summoner",
3623
+ "documentor"
3624
+ ],
3625
+ "research/*": [
3626
+ "web-researcher",
3627
+ "api-researcher",
3628
+ "web-pm",
3629
+ "pattern-scout",
3630
+ "web-pattern-critique",
3631
+ "documentor",
3632
+ "agent-summoner",
3633
+ "skill-summoner"
3634
+ ],
3635
+ "methodology/*": [
3636
+ "web-developer",
3637
+ "api-developer",
3638
+ "web-reviewer",
3639
+ "api-reviewer",
3640
+ "web-researcher",
3641
+ "api-researcher",
3642
+ "web-tester",
3643
+ "web-pm",
3644
+ "web-architecture",
3645
+ "pattern-scout",
3646
+ "web-pattern-critique",
3647
+ "agent-summoner",
3648
+ "skill-summoner",
3649
+ "documentor"
3650
+ ],
3651
+ "web/testing": ["web-tester", "web-developer", "web-reviewer"],
3652
+ "api/testing": ["web-tester", "api-developer", "api-reviewer"],
3653
+ "web/mocks": ["web-tester", "web-developer", "web-reviewer"]
3654
+ };
3655
+ var DEFAULT_AGENTS = ["agent-summoner", "skill-summoner", "documentor"];
3656
+ function getEffectiveSkillToAgents() {
3657
+ const defaults = getCachedDefaults();
3658
+ if (defaults?.skill_to_agents) {
3659
+ return defaults.skill_to_agents;
3660
+ }
3661
+ return SKILL_TO_AGENTS;
3662
+ }
3663
+ function getAgentsForSkill(skillPath, category, _projectConfig) {
3664
+ const normalizedPath = skillPath.replace(/^skills\//, "").replace(/\/$/, "");
3665
+ const skillToAgents = getEffectiveSkillToAgents();
3666
+ if (skillToAgents[category]) {
3667
+ return skillToAgents[category];
3668
+ }
3669
+ for (const [pattern, agents] of Object.entries(skillToAgents)) {
3670
+ if (normalizedPath === pattern || normalizedPath.startsWith(`${pattern}/`)) {
3671
+ return agents;
3672
+ }
3673
+ }
3674
+ for (const [pattern, agents] of Object.entries(skillToAgents)) {
3675
+ if (pattern.endsWith("/*")) {
3676
+ const prefix = pattern.slice(0, -2);
3677
+ if (normalizedPath.startsWith(prefix)) {
3678
+ return agents;
3679
+ }
3680
+ }
3681
+ }
3682
+ return DEFAULT_AGENTS;
3683
+ }
3684
+
3685
+ // src/cli/lib/skills/skill-plugin-compiler.ts
3686
+ init_esm_shims();
3687
+ import path20 from "path";
3688
+ import { parse as parseYaml7 } from "yaml";
3689
+ function sanitizeSkillName(name) {
3690
+ return name.replace(/\+/g, "-");
3691
+ }
3692
+ async function readSkillMetadata(skillPath) {
3693
+ const metadataPath = path20.join(skillPath, STANDARD_FILES.METADATA_YAML);
3694
+ if (!await fileExists(metadataPath)) {
3695
+ return null;
3696
+ }
3697
+ try {
3698
+ const content = await readFile(metadataPath);
3699
+ const lines = content.split("\n");
3700
+ const yamlContent = lines[0]?.startsWith("# yaml-language-server:") ? lines.slice(1).join("\n") : content;
3701
+ const result = skillMetadataLoaderSchema.safeParse(parseYaml7(yamlContent));
3702
+ if (!result.success) {
3703
+ warn(`Invalid metadata.yaml at '${skillPath}': ${formatZodErrors(result.error.issues)}`);
3704
+ return null;
3705
+ }
3706
+ return result.data;
3707
+ } catch (error) {
3708
+ warn(`Failed to read metadata.yaml at '${skillPath}': ${error}`);
3709
+ return null;
3710
+ }
3711
+ }
3712
+ function generateReadme(skillName, frontmatter, metadata) {
3713
+ const lines = [];
3714
+ lines.push(`# ${skillName}`);
3715
+ lines.push("");
3716
+ lines.push(frontmatter.description);
3717
+ lines.push("");
3718
+ if (metadata?.tags && metadata.tags.length > 0) {
3719
+ lines.push("## Tags");
3720
+ lines.push("");
3721
+ lines.push(metadata.tags.map((t) => `\`${t}\``).join(" "));
3722
+ lines.push("");
3723
+ }
3724
+ lines.push("## Installation");
3725
+ lines.push("");
3726
+ lines.push("Add this plugin to your Claude Code configuration:");
3727
+ lines.push("");
3728
+ lines.push("```json");
3729
+ lines.push("{");
3730
+ lines.push(` "plugins": ["${skillName}"]`);
3731
+ lines.push("}");
3732
+ lines.push("```");
3733
+ lines.push("");
3734
+ lines.push("## Usage");
3735
+ lines.push("");
3736
+ lines.push("This skill is automatically available when installed.");
3737
+ if (metadata?.requires && metadata.requires.length > 0) {
3738
+ lines.push("");
3739
+ lines.push(`**Requires:** ${metadata.requires.join(", ")}`);
3740
+ }
3741
+ lines.push("");
3742
+ lines.push("---");
3743
+ lines.push("");
3744
+ lines.push(`*Generated by ${DEFAULT_BRANDING.NAME} skill-plugin-compiler*`);
3745
+ lines.push("");
3746
+ return lines.join("\n");
3747
+ }
3748
+ async function compileSkillPlugin(options) {
3749
+ const { skillPath, outputDir, skillName: overrideName } = options;
3750
+ const dirBasename = path20.basename(skillPath);
3751
+ const skillMdPath = path20.join(skillPath, STANDARD_FILES.SKILL_MD);
3752
+ if (!await fileExists(skillMdPath)) {
3753
+ throw new Error(
3754
+ `Skill '${dirBasename}' is missing required ${STANDARD_FILES.SKILL_MD} file. Expected at: ${skillMdPath}`
3755
+ );
3756
+ }
3757
+ const skillMdContent = await readFile(skillMdPath);
3758
+ const frontmatter = parseFrontmatter(skillMdContent, skillMdPath);
3759
+ if (!frontmatter) {
3760
+ throw new Error(
3761
+ `Skill '${dirBasename}' has invalid or missing YAML frontmatter in ${STANDARD_FILES.SKILL_MD}. Required fields: 'name' and 'description'. File: ${skillMdPath}`
3762
+ );
3763
+ }
3764
+ const skillName = overrideName ?? sanitizeSkillName(frontmatter.name);
3765
+ verbose(`Compiling skill plugin: ${skillName} from ${skillPath}`);
3766
+ const metadata = await readSkillMetadata(skillPath);
3767
+ const pluginDir = path20.join(outputDir, skillName);
3768
+ const skillsDir = path20.join(pluginDir, "skills", skillName);
3769
+ await ensureDir(pluginDir);
3770
+ await ensureDir(skillsDir);
3771
+ const newHash = await computeSkillFolderHash(skillPath);
3772
+ const { version, contentHash } = await determinePluginVersion(
3773
+ newHash,
3774
+ pluginDir,
3775
+ getPluginManifestPath
3776
+ );
3777
+ const manifest = generateSkillPluginManifest({
3778
+ skillName,
3779
+ description: frontmatter.description,
3780
+ author: metadata?.author,
3781
+ version,
3782
+ keywords: metadata?.tags
3783
+ });
3784
+ await writePluginManifest(pluginDir, manifest);
3785
+ await writeContentHash(pluginDir, contentHash, getPluginManifestPath);
3786
+ verbose(` Wrote plugin.json for ${skillName} (v${version})`);
3787
+ await writeFile(path20.join(skillsDir, STANDARD_FILES.SKILL_MD), skillMdContent);
3788
+ verbose(` Copied ${STANDARD_FILES.SKILL_MD}`);
3789
+ for (const fileName of SKILL_CONTENT_FILES) {
3790
+ if (fileName === STANDARD_FILES.SKILL_MD) continue;
3791
+ const sourcePath = path20.join(skillPath, fileName);
3792
+ if (await fileExists(sourcePath)) {
3793
+ const content = await readFile(sourcePath);
3794
+ await writeFile(path20.join(skillsDir, fileName), content);
3795
+ verbose(` Copied ${fileName}`);
3796
+ }
3797
+ }
3798
+ for (const dirName of SKILL_CONTENT_DIRS) {
3799
+ const sourceDir = path20.join(skillPath, dirName);
3800
+ if (await fileExists(sourceDir)) {
3801
+ await copy(sourceDir, path20.join(skillsDir, dirName));
3802
+ verbose(` Copied ${dirName}/`);
3803
+ }
3804
+ }
3805
+ const readme = generateReadme(skillName, frontmatter, metadata);
3806
+ await writeFile(path20.join(pluginDir, "README.md"), readme);
3807
+ verbose(" Generated README.md");
3808
+ return {
3809
+ pluginPath: pluginDir,
3810
+ manifest,
3811
+ skillName
3812
+ };
3813
+ }
3814
+ async function compileAllSkillPlugins(skillsDir, outputDir) {
3815
+ const results = [];
3816
+ const skillMdFiles = await glob(`**/${STANDARD_FILES.SKILL_MD}`, skillsDir);
3817
+ for (const skillMdFile of skillMdFiles) {
3818
+ const skillPath = path20.join(skillsDir, path20.dirname(skillMdFile));
3819
+ try {
3820
+ const result = await compileSkillPlugin({
3821
+ skillPath,
3822
+ outputDir
3823
+ });
3824
+ results.push(result);
3825
+ log(` [OK] ${result.skillName}`);
3826
+ } catch (error) {
3827
+ const errorMessage = getErrorMessage(error);
3828
+ const dirBasename = path20.basename(skillPath);
3829
+ warn(`Failed to compile skill from '${dirBasename}': ${errorMessage}`);
3830
+ }
3831
+ }
3832
+ return results;
3833
+ }
3834
+ function printCompilationSummary(results) {
3835
+ log(`
3836
+ Compiled ${results.length} skill plugins:`);
3837
+ for (const result of results) {
3838
+ log(` - ${result.skillName} (v${result.manifest.version})`);
3839
+ }
3840
+ }
3841
+
3842
+ // src/cli/lib/skills/local-skill-loader.ts
3843
+ init_esm_shims();
3844
+ import { parse as parseYaml8 } from "yaml";
3845
+ import path21 from "path";
3846
+ async function discoverLocalSkills(projectDir) {
3847
+ const localSkillsPath = path21.join(projectDir, LOCAL_SKILLS_PATH);
3848
+ if (!await directoryExists(localSkillsPath)) {
3849
+ verbose(`Local skills directory not found: ${localSkillsPath}`);
3850
+ return null;
3851
+ }
3852
+ const skills = [];
3853
+ const skillDirs = await listDirectories(localSkillsPath);
3854
+ for (const skillDirName of skillDirs) {
3855
+ const skill = await extractLocalSkill(localSkillsPath, skillDirName);
3856
+ if (skill) {
3857
+ skills.push(skill);
3858
+ }
3859
+ }
3860
+ verbose(`Discovered ${skills.length} local skills from ${localSkillsPath}`);
3861
+ return {
3862
+ skills,
3863
+ localSkillsPath
3864
+ };
3865
+ }
3866
+ async function extractLocalSkill(localSkillsPath, skillDirName) {
3867
+ const skillDir = path21.join(localSkillsPath, skillDirName);
3868
+ const metadataPath = path21.join(skillDir, STANDARD_FILES.METADATA_YAML);
3869
+ const skillMdPath = path21.join(skillDir, STANDARD_FILES.SKILL_MD);
3870
+ if (!await fileExists(metadataPath)) {
3871
+ verbose(`Skipping local skill '${skillDirName}': No metadata.yaml found`);
3872
+ return null;
3873
+ }
3874
+ if (!await fileExists(skillMdPath)) {
3875
+ verbose(`Skipping local skill '${skillDirName}': No SKILL.md found`);
3876
+ return null;
3877
+ }
3878
+ const metadataContent = await readFile(metadataPath);
3879
+ const parsed = localRawMetadataSchema.safeParse(parseYaml8(metadataContent));
3880
+ if (!parsed.success) {
3881
+ warn(
3882
+ `Skipping local skill '${skillDirName}': invalid metadata.yaml \u2014 ${formatZodErrors(parsed.error.issues)}`
3883
+ );
3884
+ return null;
3885
+ }
3886
+ const metadata = parsed.data;
3887
+ if (!metadata.cli_name) {
3888
+ warn(
3889
+ `Skipping local skill '${skillDirName}': missing required '${METADATA_KEYS.CLI_NAME}' in metadata.yaml`
3890
+ );
3891
+ return null;
3892
+ }
3893
+ const skillMdContent = await readFile(skillMdPath);
3894
+ const frontmatter = parseFrontmatter(skillMdContent, skillMdPath);
3895
+ if (!frontmatter) {
3896
+ warn(`Skipping local skill '${skillDirName}': invalid SKILL.md frontmatter`);
3897
+ return null;
3898
+ }
3899
+ const relativePath = `${LOCAL_SKILLS_PATH}/${skillDirName}/`;
3900
+ const skillId = frontmatter.name;
3901
+ const category = metadata.category || LOCAL_DEFAULTS.CATEGORY;
3902
+ if (!metadata.category) {
3903
+ warn(
3904
+ `Local skill '${skillDirName}' has no category in metadata.yaml \u2014 defaulting to '${LOCAL_DEFAULTS.CATEGORY}' (will not appear in wizard domain views)`
3905
+ );
3906
+ }
3907
+ const extracted = {
3908
+ id: skillId,
3909
+ directoryPath: skillDirName,
3910
+ description: metadata.cli_description || frontmatter.description,
3911
+ usageGuidance: metadata.usage_guidance,
3912
+ category,
3913
+ categoryExclusive: metadata.category_exclusive ?? false,
3914
+ author: LOCAL_DEFAULTS.AUTHOR,
3915
+ tags: metadata.tags ?? [],
3916
+ compatibleWith: metadata.compatible_with ?? [],
3917
+ conflictsWith: metadata.conflicts_with ?? [],
3918
+ requires: metadata.requires ?? [],
3919
+ requiresSetup: metadata.requires_setup ?? [],
3920
+ providesSetupFor: metadata.provides_setup_for ?? [],
3921
+ path: relativePath,
3922
+ local: true,
3923
+ localPath: relativePath
3924
+ };
3925
+ verbose(`Extracted local skill: ${skillId}`);
3926
+ return extracted;
3927
+ }
3928
+
3929
+ // src/cli/lib/skills/source-switcher.ts
3930
+ init_esm_shims();
3931
+ import path22 from "path";
3932
+ function validateSkillId(skillId) {
3933
+ if (!SKILL_ID_PATTERN.test(skillId)) {
3934
+ return false;
3935
+ }
3936
+ return !(skillId.includes("\0") || skillId.includes("..") || skillId.includes("/") || skillId.includes("\\"));
3937
+ }
3938
+ function validatePathBoundary(resolvedPath, expectedParent) {
3939
+ const normalizedPath = path22.resolve(resolvedPath);
3940
+ const normalizedParent = path22.resolve(expectedParent);
3941
+ return normalizedPath.startsWith(normalizedParent + path22.sep);
3942
+ }
3943
+ async function archiveLocalSkill(projectDir, skillId) {
3944
+ if (!validateSkillId(skillId)) {
3945
+ warn(`Invalid skill ID for archiving: '${skillId}'`);
3946
+ return;
3947
+ }
3948
+ const skillsDir = path22.resolve(path22.join(projectDir, LOCAL_SKILLS_PATH));
3949
+ const skillPath = path22.resolve(path22.join(skillsDir, skillId));
3950
+ const archivedDir = path22.resolve(path22.join(skillsDir, ARCHIVED_SKILLS_DIR_NAME));
3951
+ const archivedSkillPath = path22.resolve(path22.join(archivedDir, skillId));
3952
+ if (!validatePathBoundary(skillPath, skillsDir) || !validatePathBoundary(archivedSkillPath, archivedDir)) {
3953
+ warn(`Skill ID '${skillId}' resolves outside the skills directory.`);
3954
+ return;
3955
+ }
3956
+ try {
3957
+ await ensureDir(archivedDir);
3958
+ await copy(skillPath, archivedSkillPath);
3959
+ await remove(skillPath);
3960
+ } catch (error) {
3961
+ warn(`Failed to archive skill '${skillId}': ${getErrorMessage(error)}`);
3962
+ return;
3963
+ }
3964
+ verbose(`Archived local skill '${skillId}' to ${ARCHIVED_SKILLS_DIR_NAME}/`);
3965
+ }
3966
+ async function restoreArchivedSkill(projectDir, skillId) {
3967
+ if (!validateSkillId(skillId)) {
3968
+ warn(`Invalid skill ID for restoring: '${skillId}'`);
3969
+ return false;
3970
+ }
3971
+ const skillsDir = path22.resolve(path22.join(projectDir, LOCAL_SKILLS_PATH));
3972
+ const skillPath = path22.resolve(path22.join(skillsDir, skillId));
3973
+ const archivedDir = path22.resolve(path22.join(skillsDir, ARCHIVED_SKILLS_DIR_NAME));
3974
+ const archivedSkillPath = path22.resolve(path22.join(archivedDir, skillId));
3975
+ if (!validatePathBoundary(skillPath, skillsDir) || !validatePathBoundary(archivedSkillPath, archivedDir)) {
3976
+ warn(`Skill ID '${skillId}' resolves outside the skills directory.`);
3977
+ return false;
3978
+ }
3979
+ try {
3980
+ await copy(archivedSkillPath, skillPath);
3981
+ await remove(archivedSkillPath);
3982
+ } catch {
3983
+ return false;
3984
+ }
3985
+ verbose(`Restored archived skill '${skillId}' from ${ARCHIVED_SKILLS_DIR_NAME}/`);
3986
+ return true;
3987
+ }
3988
+
3989
+ // src/cli/lib/configuration/config-generator.ts
3990
+ function extractSubcategoryFromPath(categoryPath) {
3991
+ if (categoryPath === "local") return void 0;
3992
+ const parts = categoryPath.split("/");
3993
+ return parts.length >= 2 ? parts[1] : parts[0];
3994
+ }
3995
+ function generateProjectConfigFromSkills(name, selectedSkillIds, matrix, options) {
3996
+ const neededAgents = /* @__PURE__ */ new Set();
3997
+ const stackProperty = {};
3998
+ for (const skillId of selectedSkillIds) {
3999
+ const skill = matrix.skills[skillId];
4000
+ if (!skill) {
4001
+ continue;
4002
+ }
4003
+ const skillPath = skill.path;
4004
+ const category = skill.category;
4005
+ const agents = getAgentsForSkill(skillPath, category);
4006
+ const subcategory = extractSubcategoryFromPath(category);
4007
+ for (const agentId of agents) {
4008
+ neededAgents.add(agentId);
4009
+ if (subcategory) {
4010
+ if (!stackProperty[agentId]) {
4011
+ stackProperty[agentId] = {};
4012
+ }
4013
+ stackProperty[agentId][subcategory] = [{ id: skillId, preloaded: false }];
4014
+ }
4015
+ }
4016
+ }
4017
+ const config = {
4018
+ name,
4019
+ agents: Array.from(neededAgents).sort(),
4020
+ skills: [...selectedSkillIds]
4021
+ };
4022
+ if (Object.keys(stackProperty).length > 0) {
4023
+ config.stack = stackProperty;
4024
+ }
4025
+ if (options?.description) {
4026
+ config.description = options.description;
4027
+ }
4028
+ if (options?.author) {
4029
+ config.author = options.author;
4030
+ }
4031
+ return config;
4032
+ }
4033
+ function buildStackProperty(stack) {
4034
+ const result = {};
4035
+ for (const [agentId, agentConfig] of typedEntries(stack.agents)) {
4036
+ if (!agentConfig || Object.keys(agentConfig).length === 0) {
4037
+ continue;
4038
+ }
4039
+ const resolvedMappings = {};
4040
+ for (const [subcategoryId, assignments] of typedEntries(
4041
+ agentConfig
4042
+ )) {
4043
+ if (!assignments || assignments.length === 0) continue;
4044
+ resolvedMappings[subcategoryId] = assignments;
4045
+ }
4046
+ if (Object.keys(resolvedMappings).length > 0) {
4047
+ result[agentId] = resolvedMappings;
4048
+ }
4049
+ }
4050
+ return result;
4051
+ }
4052
+ function compactStackForYaml(stack) {
4053
+ const result = {};
4054
+ for (const [agentId, agentConfig] of Object.entries(stack)) {
4055
+ const compacted = {};
4056
+ for (const [subcategory, assignments] of typedEntries(
4057
+ agentConfig
4058
+ )) {
4059
+ if (!assignments || assignments.length === 0) continue;
4060
+ if (assignments.length === 1) {
4061
+ const assignment = assignments[0];
4062
+ if (!assignment.preloaded) {
4063
+ compacted[subcategory] = assignment.id;
4064
+ } else {
4065
+ compacted[subcategory] = { id: assignment.id, preloaded: true };
4066
+ }
4067
+ } else {
4068
+ compacted[subcategory] = assignments.map(
4069
+ (a) => !a.preloaded ? a.id : { id: a.id, preloaded: true }
4070
+ );
4071
+ }
4072
+ }
4073
+ if (Object.keys(compacted).length > 0) {
4074
+ result[agentId] = compacted;
4075
+ }
4076
+ }
4077
+ return result;
4078
+ }
4079
+
4080
+ // src/cli/lib/configuration/config-merger.ts
4081
+ init_esm_shims();
4082
+ import { difference } from "remeda";
4083
+
4084
+ // src/cli/lib/configuration/project-config.ts
4085
+ init_esm_shims();
4086
+ import path23 from "path";
4087
+ var CONFIG_PATH = `${CLAUDE_SRC_DIR}/config.yaml`;
4088
+ var LEGACY_CONFIG_PATH = `${CLAUDE_DIR}/config.yaml`;
4089
+ async function loadProjectConfig(projectDir) {
4090
+ const srcConfigPath = path23.join(projectDir, CONFIG_PATH);
4091
+ const legacyConfigPath = path23.join(projectDir, LEGACY_CONFIG_PATH);
4092
+ let configPath = srcConfigPath;
4093
+ if (!await fileExists(srcConfigPath)) {
4094
+ if (await fileExists(legacyConfigPath)) {
4095
+ configPath = legacyConfigPath;
4096
+ verbose(`Using legacy config location: ${legacyConfigPath}`);
4097
+ } else {
4098
+ verbose(`Project config not found at ${srcConfigPath} or ${legacyConfigPath}`);
4099
+ return null;
4100
+ }
4101
+ }
4102
+ const data = await safeLoadYamlFile(configPath, projectConfigLoaderSchema);
4103
+ if (!data) return null;
4104
+ const config = data;
4105
+ if (config.stack) {
4106
+ config.stack = normalizeStackRecord(
4107
+ config.stack
4108
+ );
4109
+ }
4110
+ if (!config.name) {
4111
+ warn(
4112
+ `Project config at '${configPath}' is missing required 'name' field \u2014 defaulting to directory name`
4113
+ );
4114
+ config.name = path23.basename(projectDir);
4115
+ }
4116
+ if (!config.skills) {
4117
+ warn(`Project config at '${configPath}' is missing 'skills' array \u2014 defaulting to empty`);
4118
+ config.skills = [];
4119
+ }
4120
+ verbose(`Loaded project config from ${configPath}`);
4121
+ return {
4122
+ config,
4123
+ configPath
4124
+ };
4125
+ }
4126
+ function validateProjectConfig(config) {
4127
+ const errors = [];
4128
+ const warnings = [];
4129
+ if (!config || typeof config !== "object") {
4130
+ return { valid: false, errors: ["Config must be an object"], warnings: [] };
4131
+ }
4132
+ const c = config;
4133
+ if (!c.name || typeof c.name !== "string") {
4134
+ errors.push("name is required and must be a string");
4135
+ }
4136
+ if (!c.agents || !Array.isArray(c.agents)) {
4137
+ errors.push("agents is required and must be an array");
4138
+ } else {
4139
+ for (const agent of c.agents) {
4140
+ if (typeof agent !== "string") {
4141
+ errors.push(`agents must contain strings, found: ${typeof agent}`);
4142
+ }
4143
+ }
4144
+ }
4145
+ if (c.version !== void 0 && c.version !== "1") {
4146
+ errors.push('version must be "1" (or omitted for default)');
4147
+ }
4148
+ return {
4149
+ valid: errors.length === 0,
4150
+ errors,
4151
+ warnings
4152
+ };
4153
+ }
4154
+
4155
+ // src/cli/lib/configuration/config-merger.ts
4156
+ async function mergeWithExistingConfig(newConfig, context) {
4157
+ const localConfig = { ...newConfig };
4158
+ const existingFullConfig = await loadProjectConfig(context.projectDir);
4159
+ if (existingFullConfig) {
4160
+ const existingConfig = existingFullConfig.config;
4161
+ if (existingConfig.name) {
4162
+ localConfig.name = existingConfig.name;
4163
+ }
4164
+ if (existingConfig.description) {
4165
+ localConfig.description = existingConfig.description;
4166
+ }
4167
+ if (existingConfig.source) {
4168
+ localConfig.source = existingConfig.source;
4169
+ }
4170
+ if (existingConfig.agents && existingConfig.agents.length > 0) {
4171
+ const newAgentIds = difference(localConfig.agents, existingConfig.agents);
4172
+ localConfig.agents = [...existingConfig.agents, ...newAgentIds];
4173
+ }
4174
+ if (existingConfig.stack) {
4175
+ const mergedStack = { ...localConfig.stack };
4176
+ for (const [agentId, agentConfig] of Object.entries(existingConfig.stack)) {
4177
+ mergedStack[agentId] = { ...mergedStack[agentId], ...agentConfig };
4178
+ }
4179
+ localConfig.stack = mergedStack;
4180
+ }
4181
+ if (existingConfig.author) {
4182
+ localConfig.author = existingConfig.author;
4183
+ }
4184
+ if (existingConfig.agents_source) {
4185
+ localConfig.agents_source = existingConfig.agents_source;
4186
+ }
4187
+ if (existingConfig.marketplace) {
4188
+ localConfig.marketplace = existingConfig.marketplace;
4189
+ }
4190
+ return {
4191
+ config: localConfig,
4192
+ merged: true,
4193
+ existingConfigPath: existingFullConfig.configPath
4194
+ };
4195
+ }
4196
+ const existingProjectConfig = await loadProjectSourceConfig(context.projectDir);
4197
+ if (existingProjectConfig?.author) {
4198
+ localConfig.author = existingProjectConfig.author;
4199
+ }
4200
+ if (existingProjectConfig?.agents_source) {
4201
+ localConfig.agents_source = existingProjectConfig.agents_source;
4202
+ }
4203
+ return { config: localConfig, merged: false };
4204
+ }
4205
+
4206
+ // src/cli/lib/configuration/config-saver.ts
4207
+ init_esm_shims();
4208
+ async function saveSourceToProjectConfig(projectDir, source) {
4209
+ const existing = await loadProjectSourceConfig(projectDir) ?? {};
4210
+ await saveProjectConfig(projectDir, { ...existing, source });
4211
+ }
4212
+
4213
+ // src/cli/lib/loading/source-fetcher.ts
4214
+ var SAFE_NAME_PATTERN = /^[a-zA-Z0-9@._/ -]+$/;
4215
+ var MAX_NAME_LENGTH = 200;
4216
+ function sanitizeSourceForCache(source) {
4217
+ const hash = createHash2("sha256").update(source).digest("hex").slice(0, CACHE_HASH_LENGTH);
4218
+ const readable = source.replace(/[^a-zA-Z0-9]/g, "-").replace(/--+/g, "-").replace(/^-|-$/g, "").slice(0, CACHE_READABLE_PREFIX_LENGTH);
4219
+ return readable ? `${readable}-${hash}` : hash;
4220
+ }
4221
+ function getCacheDir(source) {
4222
+ const sanitized = sanitizeSourceForCache(source) || "unknown";
4223
+ return path24.join(CACHE_DIR, "sources", sanitized);
4224
+ }
4225
+ async function fetchFromSource(source, options = {}) {
4226
+ const { forceRefresh = false, subdir } = options;
4227
+ if (isLocalSource(source)) {
4228
+ return fetchFromLocalSource(source, subdir);
4229
+ }
4230
+ return fetchFromRemoteSource(source, { forceRefresh, subdir });
4231
+ }
4232
+ async function fetchFromLocalSource(source, subdir) {
4233
+ const fullPath = subdir ? path24.join(source, subdir) : source;
4234
+ const absolutePath = path24.isAbsolute(fullPath) ? fullPath : path24.resolve(process.cwd(), fullPath);
4235
+ if (!await directoryExists(absolutePath)) {
4236
+ throw new Error(`Local source not found: '${absolutePath}'`);
4237
+ }
4238
+ verbose(`Using local source: ${absolutePath}`);
4239
+ return {
4240
+ path: absolutePath,
4241
+ fromCache: false,
4242
+ source
4243
+ };
4244
+ }
4245
+ async function fetchFromRemoteSource(source, options) {
4246
+ const { forceRefresh = false, subdir } = options;
4247
+ const cacheDir = getCacheDir(source);
4248
+ const fullSource = subdir ? `${source}/${subdir}` : source;
4249
+ verbose(`Fetching from remote: ${fullSource}`);
4250
+ verbose(`Cache directory: ${cacheDir}`);
4251
+ if (!forceRefresh && await directoryExists(cacheDir)) {
4252
+ verbose(`Using cached source: ${cacheDir}`);
4253
+ return {
4254
+ path: cacheDir,
4255
+ fromCache: true,
4256
+ source: fullSource
4257
+ };
4258
+ }
4259
+ await ensureDir(path24.dirname(cacheDir));
4260
+ try {
4261
+ const result = await downloadTemplate(fullSource, {
4262
+ dir: cacheDir,
4263
+ force: true,
4264
+ // Always force when downloading to avoid "already exists" error
4265
+ offline: false
4266
+ });
4267
+ verbose(`Downloaded to: ${result.dir}`);
4268
+ return {
4269
+ path: result.dir,
4270
+ fromCache: false,
4271
+ source: fullSource
4272
+ };
4273
+ } catch (error) {
4274
+ throw createDetailedFetchError(error, source);
4275
+ }
4276
+ }
4277
+ function createDetailedFetchError(error, source) {
4278
+ const message = getErrorMessage(error);
4279
+ if (message.includes("404") || message.includes("Not Found")) {
4280
+ return new Error(
4281
+ `Repository not found: ${source}
4282
+
4283
+ This could mean:
4284
+ - The repository doesn't exist
4285
+ - The repository is private and you need to set authentication
4286
+ - There's a typo in the URL
4287
+
4288
+ For private repositories, set the GIGET_AUTH environment variable:
4289
+ export GIGET_AUTH=ghp_your_github_token`
4290
+ );
4291
+ }
4292
+ if (message.includes("401") || message.includes("Unauthorized")) {
4293
+ return new Error(
4294
+ `Authentication required for: ${source}
4295
+
4296
+ Set the GIGET_AUTH environment variable with a GitHub token:
4297
+ export GIGET_AUTH=ghp_your_github_token
4298
+
4299
+ Create a token at: https://github.com/settings/tokens
4300
+ Required scope: repo (for private repos) or public_repo (for public)`
4301
+ );
4302
+ }
4303
+ if (message.includes("403") || message.includes("Forbidden")) {
4304
+ return new Error(
4305
+ `Access denied to: ${source}
4306
+
4307
+ Your token may not have sufficient permissions.
4308
+ Ensure your GIGET_AUTH token has the 'repo' scope for private repositories.`
4309
+ );
4310
+ }
4311
+ if (message.includes("ENOTFOUND") || message.includes("ETIMEDOUT") || message.includes("network")) {
4312
+ return new Error(
4313
+ `Network error fetching: ${source}
4314
+
4315
+ Please check your internet connection.
4316
+ If you're behind a corporate proxy, you may need to set:
4317
+ export HTTPS_PROXY=http://your-proxy:port
4318
+ export FORCE_NODE_FETCH=true # Required for Node 20+`
4319
+ );
4320
+ }
4321
+ return new Error(`Failed to fetch ${source}: ${message}`);
4322
+ }
4323
+ async function fetchMarketplace(source, options = {}) {
4324
+ const result = await fetchFromSource(source, {
4325
+ forceRefresh: options.forceRefresh,
4326
+ subdir: ""
4327
+ // Root of repo
4328
+ });
4329
+ const marketplacePath = path24.join(result.path, PLUGIN_MANIFEST_DIR, "marketplace.json");
4330
+ if (!await directoryExists(path24.dirname(marketplacePath))) {
4331
+ throw new Error(
4332
+ `Marketplace not found for source: ${source}
4333
+
4334
+ The .claude-plugin/marketplace.json file is missing from this repository.
4335
+
4336
+ Possible causes:
4337
+ - The source URL may be incorrect
4338
+ - The repository may not have a marketplace configured
4339
+
4340
+ To create a marketplace, add a .claude-plugin/marketplace.json file to your source repository.`
4341
+ );
4342
+ }
4343
+ const content = await readFileSafe(marketplacePath, MAX_MARKETPLACE_FILE_SIZE);
4344
+ const parsed = JSON.parse(content);
4345
+ if (!validateNestingDepth(parsed, MAX_JSON_NESTING_DEPTH)) {
4346
+ throw new Error(
4347
+ `Invalid marketplace.json at: ${marketplacePath}
4348
+
4349
+ JSON structure exceeds maximum nesting depth of ${MAX_JSON_NESTING_DEPTH}.`
4350
+ );
4351
+ }
4352
+ const validation = marketplaceSchema.safeParse(parsed);
4353
+ if (!validation.success) {
4354
+ throw new Error(
4355
+ `Invalid marketplace.json at: ${marketplacePath}
4356
+
4357
+ Validation errors: ${formatZodErrors(validation.error.issues)}`
4358
+ );
4359
+ }
4360
+ const marketplace = validation.data;
4361
+ const EXPECTED_MARKETPLACE_KEYS = [
4362
+ "$schema",
4363
+ "name",
4364
+ "version",
4365
+ "description",
4366
+ "owner",
4367
+ "metadata",
4368
+ "plugins"
4369
+ ];
4370
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
4371
+ warnUnknownFields(
4372
+ parsed,
4373
+ EXPECTED_MARKETPLACE_KEYS,
4374
+ "marketplace.json"
4375
+ );
4376
+ }
4377
+ if (marketplace.plugins.length > MAX_MARKETPLACE_PLUGINS) {
4378
+ throw new Error(
4379
+ `Invalid marketplace.json at: ${marketplacePath}
4380
+
4381
+ Too many plugins: ${marketplace.plugins.length} (limit: ${MAX_MARKETPLACE_PLUGINS}).`
4382
+ );
4383
+ }
4384
+ for (const plugin of marketplace.plugins) {
4385
+ if (plugin.name.length > MAX_NAME_LENGTH) {
4386
+ warn(
4387
+ `Marketplace plugin name too long (${plugin.name.length} chars): '${plugin.name.slice(0, 50)}...'`
4388
+ );
4389
+ }
4390
+ if (!SAFE_NAME_PATTERN.test(plugin.name)) {
4391
+ warn(`Marketplace plugin name contains unsafe characters: '${plugin.name.slice(0, 50)}'`);
4392
+ }
4393
+ }
4394
+ verbose(`Loaded marketplace: ${marketplace.name} v${marketplace.version}`);
4395
+ return {
4396
+ marketplace,
4397
+ sourcePath: result.path,
4398
+ fromCache: result.fromCache ?? false
4399
+ };
4400
+ }
4401
+
4402
+ // src/cli/lib/configuration/source-manager.ts
4403
+ var DEFAULT_SOURCE_NAME = "public";
4404
+ async function addSource(projectDir, url) {
4405
+ const result = await fetchMarketplace(url, { forceRefresh: true });
4406
+ const name = result.marketplace.name;
4407
+ const skillCount = result.marketplace.plugins.length;
4408
+ const config = await loadProjectSourceConfig(projectDir) ?? {};
4409
+ const sources = config.sources ?? [];
4410
+ const exists = sources.some((s) => s.name === name);
4411
+ if (exists) {
4412
+ throw new Error(`Source "${name}" already exists`);
4413
+ }
4414
+ sources.push({ name, url });
4415
+ config.sources = sources;
4416
+ await saveProjectConfig(projectDir, config);
4417
+ verbose(`Added source "${name}" with ${skillCount} skills`);
4418
+ return { name, skillCount };
4419
+ }
4420
+ async function removeSource(projectDir, name) {
4421
+ if (name === DEFAULT_SOURCE_NAME) {
4422
+ throw new Error(`Cannot remove the "${DEFAULT_SOURCE_NAME}" source`);
4423
+ }
4424
+ const config = await loadProjectSourceConfig(projectDir) ?? {};
4425
+ const sources = config.sources ?? [];
4426
+ const filtered = sources.filter((s) => s.name !== name);
4427
+ if (filtered.length === sources.length) {
4428
+ throw new Error(`Source "${name}" not found`);
4429
+ }
4430
+ config.sources = filtered;
4431
+ await saveProjectConfig(projectDir, config);
4432
+ verbose(`Removed source "${name}"`);
4433
+ }
4434
+ async function getSourceSummary(projectDir) {
4435
+ const config = await loadProjectSourceConfig(projectDir) ?? {};
4436
+ const sources = [
4437
+ {
4438
+ name: DEFAULT_SOURCE_NAME,
4439
+ url: config.source ?? DEFAULT_SOURCE,
4440
+ enabled: true
4441
+ }
4442
+ ];
4443
+ if (config.sources) {
4444
+ for (const source of config.sources) {
4445
+ sources.push({ ...source, enabled: true });
4446
+ }
4447
+ }
4448
+ let localSkillCount = 0;
4449
+ try {
4450
+ const localResult = await discoverLocalSkills(projectDir);
4451
+ if (localResult) {
4452
+ localSkillCount = localResult.skills.length;
4453
+ }
4454
+ } catch {
4455
+ verbose("Failed to discover local skills for source summary");
4456
+ }
4457
+ let pluginSkillCount = 0;
4458
+ try {
4459
+ const discoveredSkills = await discoverAllPluginSkills(projectDir);
4460
+ pluginSkillCount = Object.keys(discoveredSkills).length;
4461
+ } catch {
4462
+ verbose("Failed to discover plugin skills for source summary");
4463
+ }
4464
+ return { sources, localSkillCount, pluginSkillCount };
4465
+ }
4466
+
4467
+ export {
4468
+ generateAgentPluginManifest,
4469
+ writePluginManifest,
4470
+ findPluginManifest,
4471
+ getProjectPluginsDir,
4472
+ getPluginAgentsDir,
4473
+ getPluginManifestPath,
4474
+ DEFAULT_SOURCE,
4475
+ SOURCE_ENV_VAR,
4476
+ getProjectConfigPath,
4477
+ loadProjectSourceConfig,
4478
+ saveProjectConfig,
4479
+ resolveSource,
4480
+ resolveAgentsSource,
4481
+ formatOrigin,
4482
+ resolveAuthor,
4483
+ resolveBranding,
4484
+ resolveAllSources,
4485
+ IMPORT_DEFAULTS,
4486
+ LOCAL_DEFAULTS,
4487
+ getCurrentDate,
4488
+ computeStringHash,
4489
+ computeFileHash,
4490
+ determinePluginVersion,
4491
+ writeContentHash,
4492
+ readForkedFromMetadata,
4493
+ compareLocalSkillsWithSource,
4494
+ injectForkedFromMetadata,
4495
+ copySkillsToLocalFlattened,
4496
+ parseFrontmatter,
4497
+ loadAllAgents,
4498
+ loadProjectAgents,
4499
+ resolveAlias,
4500
+ validateSelection,
4501
+ getAvailableSkills,
4502
+ fetchFromSource,
4503
+ searchExtraSources,
4504
+ normalizeStackRecord,
4505
+ loadStacks,
4506
+ getStackSkillIds,
4507
+ buildSkillRefsFromConfig,
4508
+ resolveAgents,
4509
+ extractFrontmatter,
4510
+ createLiquidEngine,
4511
+ compileAgentForPlugin,
4512
+ compileStackPlugin,
4513
+ printStackCompilationSummary,
4514
+ loadSkillsMatrixFromSource,
4515
+ getMarketplaceLabel,
4516
+ compileSkillPlugin,
4517
+ compileAllSkillPlugins,
4518
+ printCompilationSummary,
4519
+ discoverLocalSkills,
4520
+ archiveLocalSkill,
4521
+ restoreArchivedSkill,
4522
+ loadProjectConfig,
4523
+ validateProjectConfig,
4524
+ saveSourceToProjectConfig,
4525
+ discoverAllPluginSkills,
4526
+ hasIndividualPlugins,
4527
+ listPluginNames,
4528
+ addSource,
4529
+ removeSource,
4530
+ getSourceSummary,
4531
+ detectInstallation,
4532
+ installPluginConfig,
4533
+ installLocal,
4534
+ getInstallationInfo,
4535
+ formatInstallationDisplay,
4536
+ bumpPluginVersion,
4537
+ getPluginVersion,
4538
+ validatePlugin,
4539
+ validateAllPlugins,
4540
+ printPluginValidationResult
4541
+ };
4542
+ //# sourceMappingURL=chunk-MM7NK5N2.js.map