@claude-collective/cli 0.21.0 → 0.26.0

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