@claude-collective/cli 0.6.0 → 0.13.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 (232) hide show
  1. package/CHANGELOG.md +192 -0
  2. package/README.md +26 -9
  3. package/dist/{chunk-TOPAIL5W.js → chunk-3U3R4NCG.js} +2 -2
  4. package/dist/chunk-3U3R4NCG.js.map +1 -0
  5. package/dist/{chunk-TFV6Z7F7.js → chunk-57Y5RALO.js} +10 -10
  6. package/dist/chunk-57Y5RALO.js.map +1 -0
  7. package/dist/chunk-66UDJBF6.js +96 -0
  8. package/dist/chunk-66UDJBF6.js.map +1 -0
  9. package/dist/chunk-6DCSSORF.js +264 -0
  10. package/dist/chunk-6DCSSORF.js.map +1 -0
  11. package/dist/chunk-6Q3Y7KVB.js +31 -0
  12. package/dist/chunk-6Q3Y7KVB.js.map +1 -0
  13. package/dist/{chunk-SJYG4EJZ.js → chunk-76DWXGQE.js} +10 -8
  14. package/dist/chunk-76DWXGQE.js.map +1 -0
  15. package/dist/{chunk-AZP2AA5M.js → chunk-7Q44DMSP.js} +241 -84
  16. package/dist/chunk-7Q44DMSP.js.map +1 -0
  17. package/dist/chunk-ACNBKXXJ.js +321 -0
  18. package/dist/chunk-ACNBKXXJ.js.map +1 -0
  19. package/dist/{chunk-JMQGWQZU.js → chunk-B7CCVP6Q.js} +42 -10
  20. package/dist/chunk-B7CCVP6Q.js.map +1 -0
  21. package/dist/chunk-BDLUZVKU.js +54 -0
  22. package/dist/chunk-BDLUZVKU.js.map +1 -0
  23. package/dist/chunk-CDX4W4DM.js +120 -0
  24. package/dist/chunk-CDX4W4DM.js.map +1 -0
  25. package/dist/{chunk-MYAVQ23U.js → chunk-CJEHB4TB.js} +23 -9
  26. package/dist/chunk-CJEHB4TB.js.map +1 -0
  27. package/dist/{chunk-6WEQADPL.js → chunk-CPZOTVCI.js} +15 -14
  28. package/dist/chunk-CPZOTVCI.js.map +1 -0
  29. package/dist/chunk-D237EVNB.js +187 -0
  30. package/dist/chunk-D237EVNB.js.map +1 -0
  31. package/dist/{chunk-UFWNMW3G.js → chunk-DRXPNNPB.js} +19 -18
  32. package/dist/chunk-DRXPNNPB.js.map +1 -0
  33. package/dist/chunk-E3FJH4TF.js +80 -0
  34. package/dist/chunk-E3FJH4TF.js.map +1 -0
  35. package/dist/{chunk-D4IQAT27.js → chunk-ED4E6Q2T.js} +10 -10
  36. package/dist/chunk-ED4E6Q2T.js.map +1 -0
  37. package/dist/{chunk-SYQ7R2JO.js → chunk-EHS3TWWP.js} +3 -3
  38. package/dist/chunk-EHS3TWWP.js.map +1 -0
  39. package/dist/{chunk-AU7XVCLO.js → chunk-GDH553MV.js} +6 -6
  40. package/dist/chunk-GDH553MV.js.map +1 -0
  41. package/dist/chunk-HLJX2FTL.js +95 -0
  42. package/dist/chunk-HLJX2FTL.js.map +1 -0
  43. package/dist/chunk-I2DSLOXZ.js +75 -0
  44. package/dist/chunk-I2DSLOXZ.js.map +1 -0
  45. package/dist/{chunk-J2Y4A3LP.js → chunk-I4TPKIYX.js} +33 -18
  46. package/dist/chunk-I4TPKIYX.js.map +1 -0
  47. package/dist/{chunk-ZSKHDU5P.js → chunk-IMDW5ZUP.js} +19 -11
  48. package/dist/chunk-IMDW5ZUP.js.map +1 -0
  49. package/dist/{chunk-U4VYHKPM.js → chunk-JIPWV2FX.js} +6 -6
  50. package/dist/chunk-JIPWV2FX.js.map +1 -0
  51. package/dist/{chunk-OSQDDJXX.js → chunk-K7EVM5LY.js} +5 -10
  52. package/dist/chunk-K7EVM5LY.js.map +1 -0
  53. package/dist/{chunk-MJSFR562.js → chunk-KAAEN2PO.js} +3 -3
  54. package/dist/chunk-KAAEN2PO.js.map +1 -0
  55. package/dist/{chunk-URDV4OCP.js → chunk-LE6IY6IT.js} +22 -17
  56. package/dist/chunk-LE6IY6IT.js.map +1 -0
  57. package/dist/{chunk-FKU7VSUD.js → chunk-NDY25DTL.js} +6 -6
  58. package/dist/chunk-NDY25DTL.js.map +1 -0
  59. package/dist/{chunk-UNHCZRO4.js → chunk-P26A2K5N.js} +7 -7
  60. package/dist/chunk-P26A2K5N.js.map +1 -0
  61. package/dist/{chunk-DHFFRMF6.js → chunk-RTE64SJA.js} +2 -2
  62. package/dist/chunk-RTE64SJA.js.map +1 -0
  63. package/dist/{chunk-6ESUJMM7.js → chunk-SGJ23HIP.js} +14 -11
  64. package/dist/chunk-SGJ23HIP.js.map +1 -0
  65. package/dist/{chunk-367K3JB3.js → chunk-SVYPSDWY.js} +10 -10
  66. package/dist/chunk-SVYPSDWY.js.map +1 -0
  67. package/dist/{chunk-MMDXNZPF.js → chunk-TKFPKEV3.js} +2 -2
  68. package/dist/chunk-TKFPKEV3.js.map +1 -0
  69. package/dist/{chunk-M7YCPFIX.js → chunk-UQTEPWU7.js} +2 -2
  70. package/dist/chunk-UQTEPWU7.js.map +1 -0
  71. package/dist/{chunk-QESUUPOE.js → chunk-V46GGCCI.js} +80 -27
  72. package/dist/chunk-V46GGCCI.js.map +1 -0
  73. package/dist/chunk-X6QONICW.js +86 -0
  74. package/dist/chunk-X6QONICW.js.map +1 -0
  75. package/dist/chunk-XY3XDVMI.js +15599 -0
  76. package/dist/chunk-XY3XDVMI.js.map +1 -0
  77. package/dist/chunk-Y2LW7R3Y.js +23 -0
  78. package/dist/chunk-Y2LW7R3Y.js.map +1 -0
  79. package/dist/chunk-Z2CWURZ6.js +78 -0
  80. package/dist/chunk-Z2CWURZ6.js.map +1 -0
  81. package/dist/chunk-Z7G4B5HJ.js +377 -0
  82. package/dist/chunk-Z7G4B5HJ.js.map +1 -0
  83. package/dist/{chunk-ZDQIUHAM.js → chunk-ZENYS6KW.js} +16 -15
  84. package/dist/chunk-ZENYS6KW.js.map +1 -0
  85. package/dist/{cli-v2 → cli}/defaults/agent-mappings.yaml +5 -5
  86. package/dist/commands/build/marketplace.js +19 -60
  87. package/dist/commands/build/marketplace.js.map +1 -1
  88. package/dist/commands/build/plugins.js +21 -59
  89. package/dist/commands/build/plugins.js.map +1 -1
  90. package/dist/commands/build/stack.js +15 -15
  91. package/dist/commands/build/stack.js.map +1 -1
  92. package/dist/commands/compile.js +21 -21
  93. package/dist/commands/compile.js.map +1 -1
  94. package/dist/commands/config/get.js +9 -8
  95. package/dist/commands/config/get.js.map +1 -1
  96. package/dist/commands/config/index.js +7 -6
  97. package/dist/commands/config/index.js.map +1 -1
  98. package/dist/commands/config/path.js +8 -7
  99. package/dist/commands/config/path.js.map +1 -1
  100. package/dist/commands/config/set-project.js +9 -8
  101. package/dist/commands/config/set-project.js.map +1 -1
  102. package/dist/commands/config/set.js +9 -8
  103. package/dist/commands/config/set.js.map +1 -1
  104. package/dist/commands/config/show.js +6 -5
  105. package/dist/commands/config/unset-project.js +9 -8
  106. package/dist/commands/config/unset-project.js.map +1 -1
  107. package/dist/commands/config/unset.js +9 -8
  108. package/dist/commands/config/unset.js.map +1 -1
  109. package/dist/commands/diff.js +12 -12
  110. package/dist/commands/diff.js.map +1 -1
  111. package/dist/commands/doctor.js +10 -10
  112. package/dist/commands/doctor.js.map +1 -1
  113. package/dist/commands/edit.js +52 -48
  114. package/dist/commands/edit.js.map +1 -1
  115. package/dist/commands/eject.js +180 -97
  116. package/dist/commands/eject.js.map +1 -1
  117. package/dist/commands/import/skill.js +339 -0
  118. package/dist/commands/import/skill.js.map +1 -0
  119. package/dist/commands/info.js +9 -9
  120. package/dist/commands/info.js.map +1 -1
  121. package/dist/commands/init.js +205 -77
  122. package/dist/commands/init.js.map +1 -1
  123. package/dist/commands/list.js +9 -9
  124. package/dist/commands/list.js.map +1 -1
  125. package/dist/commands/new/agent.js +19 -21
  126. package/dist/commands/new/agent.js.map +1 -1
  127. package/dist/commands/new/skill.js +11 -12
  128. package/dist/commands/new/skill.js.map +1 -1
  129. package/dist/commands/outdated.js +12 -12
  130. package/dist/commands/outdated.js.map +1 -1
  131. package/dist/commands/search.js +205 -17
  132. package/dist/commands/search.js.map +1 -1
  133. package/dist/commands/test-imports.js +18 -18
  134. package/dist/commands/test-imports.js.map +1 -1
  135. package/dist/commands/uninstall.js +70 -83
  136. package/dist/commands/uninstall.js.map +1 -1
  137. package/dist/commands/update.js +22 -22
  138. package/dist/commands/update.js.map +1 -1
  139. package/dist/commands/validate.js +9 -9
  140. package/dist/commands/validate.js.map +1 -1
  141. package/dist/commands/version/bump.js +8 -8
  142. package/dist/commands/version/bump.js.map +1 -1
  143. package/dist/commands/version/index.js +8 -8
  144. package/dist/commands/version/index.js.map +1 -1
  145. package/dist/commands/version/set.js +7 -7
  146. package/dist/commands/version/set.js.map +1 -1
  147. package/dist/commands/version/show.js +8 -8
  148. package/dist/commands/version/show.js.map +1 -1
  149. package/dist/components/common/confirm.js +1 -1
  150. package/dist/components/common/message.js +1 -1
  151. package/dist/components/common/message.js.map +1 -1
  152. package/dist/components/common/spinner.js +1 -1
  153. package/dist/components/common/spinner.js.map +1 -1
  154. package/dist/components/skill-search/skill-search.js +10 -0
  155. package/dist/components/wizard/category-grid.js +9 -0
  156. package/dist/components/wizard/category-grid.test.js +861 -0
  157. package/dist/components/wizard/category-grid.test.js.map +1 -0
  158. package/dist/components/wizard/section-progress.js +9 -0
  159. package/dist/components/wizard/section-progress.test.js +281 -0
  160. package/dist/components/wizard/section-progress.test.js.map +1 -0
  161. package/dist/components/wizard/step-approach.js +4 -3
  162. package/dist/components/wizard/step-build.js +16 -0
  163. package/dist/components/wizard/step-build.js.map +1 -0
  164. package/dist/components/wizard/step-build.test.js +741 -0
  165. package/dist/components/wizard/step-build.test.js.map +1 -0
  166. package/dist/components/wizard/step-confirm.js +2 -4
  167. package/dist/components/wizard/step-refine.js +10 -0
  168. package/dist/components/wizard/step-refine.js.map +1 -0
  169. package/dist/components/wizard/step-refine.test.js +236 -0
  170. package/dist/components/wizard/step-refine.test.js.map +1 -0
  171. package/dist/components/wizard/step-stack-options.js +11 -0
  172. package/dist/components/wizard/step-stack-options.js.map +1 -0
  173. package/dist/components/wizard/step-stack.js +3 -3
  174. package/dist/components/wizard/wizard-footer.js +9 -0
  175. package/dist/components/wizard/wizard-footer.js.map +1 -0
  176. package/dist/components/wizard/wizard-tabs.js +11 -0
  177. package/dist/components/wizard/wizard-tabs.js.map +1 -0
  178. package/dist/components/wizard/wizard.js +14 -11
  179. package/dist/hooks/init.js +5 -4
  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/stores/wizard-store.js +2 -2
  184. package/dist/stores/wizard-store.test.js +249 -15835
  185. package/dist/stores/wizard-store.test.js.map +1 -1
  186. package/package.json +3 -2
  187. package/dist/chunk-367K3JB3.js.map +0 -1
  188. package/dist/chunk-6ESUJMM7.js.map +0 -1
  189. package/dist/chunk-6OY6ZYQF.js +0 -93
  190. package/dist/chunk-6OY6ZYQF.js.map +0 -1
  191. package/dist/chunk-6WEQADPL.js.map +0 -1
  192. package/dist/chunk-AU7XVCLO.js.map +0 -1
  193. package/dist/chunk-AZP2AA5M.js.map +0 -1
  194. package/dist/chunk-D4IQAT27.js.map +0 -1
  195. package/dist/chunk-DHFFRMF6.js.map +0 -1
  196. package/dist/chunk-FKU7VSUD.js.map +0 -1
  197. package/dist/chunk-J2Y4A3LP.js.map +0 -1
  198. package/dist/chunk-JMQGWQZU.js.map +0 -1
  199. package/dist/chunk-JY4RO76L.js +0 -73
  200. package/dist/chunk-JY4RO76L.js.map +0 -1
  201. package/dist/chunk-M7YCPFIX.js.map +0 -1
  202. package/dist/chunk-MJSFR562.js.map +0 -1
  203. package/dist/chunk-MMDXNZPF.js.map +0 -1
  204. package/dist/chunk-MYAVQ23U.js.map +0 -1
  205. package/dist/chunk-OSQDDJXX.js.map +0 -1
  206. package/dist/chunk-QESUUPOE.js.map +0 -1
  207. package/dist/chunk-SJYG4EJZ.js.map +0 -1
  208. package/dist/chunk-SYQ7R2JO.js.map +0 -1
  209. package/dist/chunk-TD643KB3.js +0 -245
  210. package/dist/chunk-TD643KB3.js.map +0 -1
  211. package/dist/chunk-TFV6Z7F7.js.map +0 -1
  212. package/dist/chunk-TGOHJCQ4.js +0 -83
  213. package/dist/chunk-TGOHJCQ4.js.map +0 -1
  214. package/dist/chunk-TOPAIL5W.js.map +0 -1
  215. package/dist/chunk-U4VYHKPM.js.map +0 -1
  216. package/dist/chunk-UFWNMW3G.js.map +0 -1
  217. package/dist/chunk-UNHCZRO4.js.map +0 -1
  218. package/dist/chunk-URDV4OCP.js.map +0 -1
  219. package/dist/chunk-YI6JVSFO.js +0 -43
  220. package/dist/chunk-YI6JVSFO.js.map +0 -1
  221. package/dist/chunk-YNSNRR5D.js +0 -184
  222. package/dist/chunk-YNSNRR5D.js.map +0 -1
  223. package/dist/chunk-Z6DLWTBY.js +0 -46
  224. package/dist/chunk-Z6DLWTBY.js.map +0 -1
  225. package/dist/chunk-ZDQIUHAM.js.map +0 -1
  226. package/dist/chunk-ZSKHDU5P.js.map +0 -1
  227. package/dist/components/wizard/selection-header.js +0 -11
  228. package/dist/components/wizard/step-category.js +0 -12
  229. package/dist/components/wizard/step-subcategory.js +0 -13
  230. /package/dist/components/{wizard/selection-header.js.map → skill-search/skill-search.js.map} +0 -0
  231. /package/dist/components/wizard/{step-category.js.map → category-grid.js.map} +0 -0
  232. /package/dist/components/wizard/{step-subcategory.js.map → section-progress.js.map} +0 -0
@@ -1,73 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- getTopLevelCategories
4
- } from "./chunk-AZP2AA5M.js";
5
- import {
6
- useWizardStore
7
- } from "./chunk-6OY6ZYQF.js";
8
- import {
9
- init_esm_shims
10
- } from "./chunk-DHET7RCE.js";
11
-
12
- // src/cli-v2/components/wizard/step-category.tsx
13
- init_esm_shims();
14
- import { Box, Text } from "ink";
15
- import { Select } from "@inkjs/ui";
16
- import { jsx, jsxs } from "react/jsx-runtime";
17
- var BACK_VALUE = "__back__";
18
- var CONTINUE_VALUE = "__continue__";
19
- var StepCategory = ({ matrix }) => {
20
- const {
21
- selectedSkills,
22
- visitedCategories,
23
- setCategory,
24
- setStep,
25
- goBack,
26
- markCategoryVisited
27
- } = useWizardStore();
28
- const topCategoryIds = getTopLevelCategories(matrix);
29
- const unvisitedCategories = topCategoryIds.filter(
30
- (catId) => !visitedCategories.has(catId)
31
- );
32
- const options = [
33
- { value: BACK_VALUE, label: "\u2190 Back" }
34
- ];
35
- if (selectedSkills.length > 0) {
36
- options.push({
37
- value: CONTINUE_VALUE,
38
- label: `\u2192 Continue with ${selectedSkills.length} skill(s)`
39
- });
40
- }
41
- for (const catId of topCategoryIds) {
42
- const cat = matrix.categories[catId];
43
- const isVisited = visitedCategories.has(catId);
44
- const label = isVisited ? cat.name : `${cat.name} (new)`;
45
- options.push({ value: catId, label });
46
- }
47
- const handleSelect = (value) => {
48
- if (value === BACK_VALUE) {
49
- goBack();
50
- return;
51
- }
52
- if (value === CONTINUE_VALUE) {
53
- setStep("confirm");
54
- return;
55
- }
56
- setCategory(value);
57
- markCategoryVisited(value);
58
- setStep("subcategory");
59
- };
60
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
61
- /* @__PURE__ */ jsxs(Text, { bold: true, children: [
62
- "Select a category to configure (",
63
- unvisitedCategories.length,
64
- " remaining):"
65
- ] }),
66
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Select, { options, onChange: handleSelect }) })
67
- ] });
68
- };
69
-
70
- export {
71
- StepCategory
72
- };
73
- //# sourceMappingURL=chunk-JY4RO76L.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli-v2/components/wizard/step-category.tsx"],"sourcesContent":["import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport { Select } from \"@inkjs/ui\";\nimport { useWizardStore } from \"../../stores/wizard-store.js\";\nimport { getTopLevelCategories } from \"../../lib/matrix-resolver.js\";\nimport type { MergedSkillsMatrix } from \"../../types-matrix.js\";\n\nconst BACK_VALUE = \"__back__\";\nconst CONTINUE_VALUE = \"__continue__\";\n\ninterface StepCategoryProps {\n matrix: MergedSkillsMatrix;\n}\n\nexport const StepCategory: React.FC<StepCategoryProps> = ({ matrix }) => {\n const {\n selectedSkills,\n visitedCategories,\n setCategory,\n setStep,\n goBack,\n markCategoryVisited,\n } = useWizardStore();\n\n const topCategoryIds = getTopLevelCategories(matrix);\n const unvisitedCategories = topCategoryIds.filter(\n (catId) => !visitedCategories.has(catId),\n );\n\n // Build options\n const options: Array<{ value: string; label: string }> = [\n { value: BACK_VALUE, label: \"← Back\" },\n ];\n\n // Add continue option if skills selected\n if (selectedSkills.length > 0) {\n options.push({\n value: CONTINUE_VALUE,\n label: `→ Continue with ${selectedSkills.length} skill(s)`,\n });\n }\n\n // Add category options with unvisited indicators\n for (const catId of topCategoryIds) {\n const cat = matrix.categories[catId];\n const isVisited = visitedCategories.has(catId);\n const label = isVisited ? cat.name : `${cat.name} (new)`;\n options.push({ value: catId, label });\n }\n\n const handleSelect = (value: string) => {\n if (value === BACK_VALUE) {\n goBack();\n return;\n }\n if (value === CONTINUE_VALUE) {\n setStep(\"confirm\");\n return;\n }\n\n setCategory(value);\n markCategoryVisited(value);\n setStep(\"subcategory\");\n };\n\n return (\n <Box flexDirection=\"column\">\n <Text bold>\n Select a category to configure ({unvisitedCategories.length} remaining):\n </Text>\n <Box marginTop={1}>\n <Select options={options} onChange={handleSelect} />\n </Box>\n </Box>\n );\n};\n"],"mappings":";;;;;;;;;;;;AAAA;AACA,SAAS,KAAK,YAAY;AAC1B,SAAS,cAAc;AAiEjB,SAIE,KAJF;AA5DN,IAAM,aAAa;AACnB,IAAM,iBAAiB;AAMhB,IAAM,eAA4C,CAAC,EAAE,OAAO,MAAM;AACvE,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,eAAe;AAEnB,QAAM,iBAAiB,sBAAsB,MAAM;AACnD,QAAM,sBAAsB,eAAe;AAAA,IACzC,CAAC,UAAU,CAAC,kBAAkB,IAAI,KAAK;AAAA,EACzC;AAGA,QAAM,UAAmD;AAAA,IACvD,EAAE,OAAO,YAAY,OAAO,cAAS;AAAA,EACvC;AAGA,MAAI,eAAe,SAAS,GAAG;AAC7B,YAAQ,KAAK;AAAA,MACX,OAAO;AAAA,MACP,OAAO,wBAAmB,eAAe,MAAM;AAAA,IACjD,CAAC;AAAA,EACH;AAGA,aAAW,SAAS,gBAAgB;AAClC,UAAM,MAAM,OAAO,WAAW,KAAK;AACnC,UAAM,YAAY,kBAAkB,IAAI,KAAK;AAC7C,UAAM,QAAQ,YAAY,IAAI,OAAO,GAAG,IAAI,IAAI;AAChD,YAAQ,KAAK,EAAE,OAAO,OAAO,MAAM,CAAC;AAAA,EACtC;AAEA,QAAM,eAAe,CAAC,UAAkB;AACtC,QAAI,UAAU,YAAY;AACxB,aAAO;AACP;AAAA,IACF;AACA,QAAI,UAAU,gBAAgB;AAC5B,cAAQ,SAAS;AACjB;AAAA,IACF;AAEA,gBAAY,KAAK;AACjB,wBAAoB,KAAK;AACzB,YAAQ,aAAa;AAAA,EACvB;AAEA,SACE,qBAAC,OAAI,eAAc,UACjB;AAAA,yBAAC,QAAK,MAAI,MAAC;AAAA;AAAA,MACwB,oBAAoB;AAAA,MAAO;AAAA,OAC9D;AAAA,IACA,oBAAC,OAAI,WAAW,GACd,8BAAC,UAAO,SAAkB,UAAU,cAAc,GACpD;AAAA,KACF;AAEJ;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli-v2/utils/exec.ts"],"sourcesContent":["import { spawn } from \"child_process\";\n\nexport interface ExecResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n/**\n * Execute a command and return the result\n */\nexport async function execCommand(\n command: string,\n args: string[],\n options?: { cwd?: string; env?: NodeJS.ProcessEnv },\n): Promise<ExecResult> {\n return new Promise((resolve, reject) => {\n const proc = spawn(command, args, {\n cwd: options?.cwd,\n env: { ...process.env, ...options?.env },\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n proc.stdout.on(\"data\", (data) => {\n stdout += data.toString();\n });\n\n proc.stderr.on(\"data\", (data) => {\n stderr += data.toString();\n });\n\n proc.on(\"close\", (code) => {\n resolve({\n stdout,\n stderr,\n exitCode: code ?? 1,\n });\n });\n\n proc.on(\"error\", (err) => {\n reject(err);\n });\n });\n}\n\n/**\n * Install a plugin using the native claude CLI\n */\nexport async function claudePluginInstall(\n pluginPath: string,\n scope: \"project\" | \"user\",\n projectDir: string,\n): Promise<void> {\n const args = [\"plugin\", \"install\", pluginPath, \"--scope\", scope];\n const result = await execCommand(\"claude\", args, { cwd: projectDir });\n\n if (result.exitCode !== 0) {\n const errorMessage = result.stderr || result.stdout || \"Unknown error\";\n throw new Error(`Plugin installation failed: ${errorMessage.trim()}`);\n }\n}\n\n/**\n * Check if the claude CLI is available\n */\nexport async function isClaudeCLIAvailable(): Promise<boolean> {\n try {\n const result = await execCommand(\"claude\", [\"--version\"], {});\n return result.exitCode === 0;\n } catch {\n return false;\n }\n}\n\nexport interface MarketplaceInfo {\n name: string;\n source: string;\n repo?: string;\n path?: string;\n}\n\n/**\n * List configured marketplaces in Claude Code\n */\nexport async function claudePluginMarketplaceList(): Promise<\n MarketplaceInfo[]\n> {\n try {\n const result = await execCommand(\n \"claude\",\n [\"plugin\", \"marketplace\", \"list\", \"--json\"],\n {},\n );\n\n if (result.exitCode !== 0) {\n return [];\n }\n\n return JSON.parse(result.stdout);\n } catch {\n // Returns empty array if claude CLI is not available or parsing fails\n return [];\n }\n}\n\n/**\n * Check if a marketplace with the given name exists\n */\nexport async function claudePluginMarketplaceExists(\n name: string,\n): Promise<boolean> {\n const marketplaces = await claudePluginMarketplaceList();\n return marketplaces.some((m) => m.name === name);\n}\n\n/**\n * Add a marketplace to Claude Code from a GitHub repository\n */\nexport async function claudePluginMarketplaceAdd(\n githubRepo: string,\n name: string,\n): Promise<void> {\n const args = [\"plugin\", \"marketplace\", \"add\", githubRepo, \"--name\", name];\n let result;\n try {\n result = await execCommand(\"claude\", args, {});\n } catch (err) {\n throw new Error(\n `Failed to add marketplace: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n );\n }\n\n if (result.exitCode !== 0) {\n const errorMessage = result.stderr || result.stdout || \"Unknown error\";\n if (errorMessage.includes(\"already installed\")) {\n return;\n }\n throw new Error(`Failed to add marketplace: ${errorMessage.trim()}`);\n }\n}\n\n/**\n * Uninstall a plugin using the native claude CLI\n */\nexport async function claudePluginUninstall(\n pluginName: string,\n scope: \"project\" | \"user\",\n projectDir: string,\n): Promise<void> {\n const args = [\"plugin\", \"uninstall\", pluginName, \"--scope\", scope];\n const result = await execCommand(\"claude\", args, { cwd: projectDir });\n\n if (result.exitCode !== 0) {\n const errorMessage = result.stderr || result.stdout || \"Unknown error\";\n // Ignore \"not installed\" errors - plugin may already be removed\n if (\n errorMessage.includes(\"not installed\") ||\n errorMessage.includes(\"not found\")\n ) {\n return;\n }\n throw new Error(`Plugin uninstall failed: ${errorMessage.trim()}`);\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli-v2/lib/versioning.ts"],"sourcesContent":["import { createHash } from \"crypto\";\nimport path from \"path\";\nimport { stringify as stringifyYaml, parse as parseYaml } from \"yaml\";\nimport { readFile, writeFile, glob, fileExists } from \"../utils/fs\";\nimport { verbose } from \"../utils/logger\";\n\nconst HASH_PREFIX_LENGTH = 7;\n\nconst METADATA_FILE_NAME = \"metadata.yaml\";\n\nconst HASHABLE_FILES = [\"SKILL.md\", \"reference.md\"];\n\nconst HASHABLE_DIRS = [\"examples\", \"scripts\"];\n\ninterface VersionedMetadata {\n version: number;\n content_hash?: string;\n updated?: string;\n [key: string]: unknown;\n}\n\nexport interface VersionCheckResult {\n skillPath: string;\n previousVersion: number;\n newVersion: number;\n previousHash: string | undefined;\n newHash: string;\n changed: boolean;\n}\n\nexport function getCurrentDate(): string {\n return new Date().toISOString().split(\"T\")[0];\n}\n\nexport function hashString(content: string): string {\n const hash = createHash(\"sha256\");\n hash.update(content);\n return hash.digest(\"hex\").slice(0, HASH_PREFIX_LENGTH);\n}\n\nexport async function hashFile(filePath: string): Promise<string> {\n const content = await readFile(filePath);\n return hashString(content);\n}\n\nexport async function hashSkillFolder(skillPath: string): Promise<string> {\n const contents: string[] = [];\n\n for (const fileName of HASHABLE_FILES) {\n const filePath = path.join(skillPath, fileName);\n if (await fileExists(filePath)) {\n const content = await readFile(filePath);\n contents.push(`${fileName}:${content}`);\n }\n }\n\n for (const dirName of HASHABLE_DIRS) {\n const dirPath = path.join(skillPath, dirName);\n if (await fileExists(dirPath)) {\n const files = await glob(\"**/*\", dirPath);\n for (const file of files.sort()) {\n const filePath = path.join(dirPath, file);\n const content = await readFile(filePath);\n contents.push(`${dirName}/${file}:${content}`);\n }\n }\n }\n\n const combined = contents.join(\"\\n---\\n\");\n return hashString(combined);\n}\n\nasync function readMetadata(\n skillPath: string,\n): Promise<{ metadata: VersionedMetadata; schemaComment: string }> {\n const metadataPath = path.join(skillPath, METADATA_FILE_NAME);\n const rawContent = await readFile(metadataPath);\n\n const lines = rawContent.split(\"\\n\");\n let schemaComment = \"\";\n let yamlContent = rawContent;\n\n if (lines[0]?.startsWith(\"# yaml-language-server:\")) {\n schemaComment = lines[0] + \"\\n\";\n yamlContent = lines.slice(1).join(\"\\n\");\n }\n\n const metadata = parseYaml(yamlContent) as VersionedMetadata;\n return { metadata, schemaComment };\n}\n\nasync function writeMetadata(\n skillPath: string,\n metadata: VersionedMetadata,\n schemaComment: string,\n): Promise<void> {\n const metadataPath = path.join(skillPath, METADATA_FILE_NAME);\n const yamlContent = stringifyYaml(metadata, { lineWidth: 0 });\n await writeFile(metadataPath, schemaComment + yamlContent);\n}\n\nexport async function versionSkill(\n skillPath: string,\n): Promise<VersionCheckResult> {\n const newHash = await hashSkillFolder(skillPath);\n\n const { metadata, schemaComment } = await readMetadata(skillPath);\n const previousVersion = metadata.version;\n const previousHash = metadata.content_hash;\n\n const changed = previousHash !== newHash;\n\n if (changed) {\n metadata.version = previousVersion + 1;\n metadata.content_hash = newHash;\n metadata.updated = getCurrentDate();\n\n await writeMetadata(skillPath, metadata, schemaComment);\n\n verbose(\n ` Version bumped: ${skillPath} (v${previousVersion} -> v${metadata.version})`,\n );\n }\n\n return {\n skillPath,\n previousVersion,\n newVersion: changed ? previousVersion + 1 : previousVersion,\n previousHash,\n newHash,\n changed,\n };\n}\n\nexport async function versionAllSkills(\n skillsDir: string,\n): Promise<VersionCheckResult[]> {\n const results: VersionCheckResult[] = [];\n\n const metadataFiles = await glob(\"**/metadata.yaml\", skillsDir);\n\n for (const metadataFile of metadataFiles) {\n const skillPath = path.join(skillsDir, path.dirname(metadataFile));\n\n try {\n const result = await versionSkill(skillPath);\n results.push(result);\n } catch (error) {\n console.warn(\n ` Warning: Failed to version skill at ${skillPath}: ${error}`,\n );\n }\n }\n\n return results;\n}\n\nexport function printVersionResults(results: VersionCheckResult[]): void {\n const changed = results.filter((r) => r.changed);\n const unchanged = results.filter((r) => !r.changed);\n\n if (changed.length > 0) {\n console.log(`\\n Version Updates:`);\n for (const result of changed) {\n const skillName = path.basename(result.skillPath);\n console.log(\n ` ✓ ${skillName}: v${result.previousVersion} -> v${result.newVersion}`,\n );\n }\n }\n\n console.log(\n `\\n Summary: ${changed.length} updated, ${unchanged.length} unchanged`,\n );\n}\n"],"mappings":";;;;;;;;;;;AAAA;AAAA,SAAS,kBAAkB;AAC3B,OAAO,UAAU;AACjB,SAAS,aAAa,eAAe,SAAS,iBAAiB;AAI/D,IAAM,qBAAqB;AAI3B,IAAM,iBAAiB,CAAC,YAAY,cAAc;AAElD,IAAM,gBAAgB,CAAC,YAAY,SAAS;AAsBrC,SAAS,WAAW,SAAyB;AAClD,QAAM,OAAO,WAAW,QAAQ;AAChC,OAAK,OAAO,OAAO;AACnB,SAAO,KAAK,OAAO,KAAK,EAAE,MAAM,GAAG,kBAAkB;AACvD;AAEA,eAAsB,SAAS,UAAmC;AAChE,QAAM,UAAU,MAAM,SAAS,QAAQ;AACvC,SAAO,WAAW,OAAO;AAC3B;AAEA,eAAsB,gBAAgB,WAAoC;AACxE,QAAM,WAAqB,CAAC;AAE5B,aAAW,YAAY,gBAAgB;AACrC,UAAM,WAAW,KAAK,KAAK,WAAW,QAAQ;AAC9C,QAAI,MAAM,WAAW,QAAQ,GAAG;AAC9B,YAAM,UAAU,MAAM,SAAS,QAAQ;AACvC,eAAS,KAAK,GAAG,QAAQ,IAAI,OAAO,EAAE;AAAA,IACxC;AAAA,EACF;AAEA,aAAW,WAAW,eAAe;AACnC,UAAM,UAAU,KAAK,KAAK,WAAW,OAAO;AAC5C,QAAI,MAAM,WAAW,OAAO,GAAG;AAC7B,YAAM,QAAQ,MAAM,KAAK,QAAQ,OAAO;AACxC,iBAAW,QAAQ,MAAM,KAAK,GAAG;AAC/B,cAAM,WAAW,KAAK,KAAK,SAAS,IAAI;AACxC,cAAM,UAAU,MAAM,SAAS,QAAQ;AACvC,iBAAS,KAAK,GAAG,OAAO,IAAI,IAAI,IAAI,OAAO,EAAE;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW,SAAS,KAAK,SAAS;AACxC,SAAO,WAAW,QAAQ;AAC5B;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli-v2/utils/fs.ts"],"sourcesContent":["import fs from \"fs-extra\";\nimport fg from \"fast-glob\";\nimport path from \"path\";\n\nexport async function readFile(filePath: string): Promise<string> {\n return fs.readFile(filePath, \"utf-8\");\n}\n\nexport async function readFileOptional(\n filePath: string,\n fallback = \"\",\n): Promise<string> {\n try {\n return await fs.readFile(filePath, \"utf-8\");\n } catch {\n return fallback;\n }\n}\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n return fs.pathExists(filePath);\n}\n\nexport async function directoryExists(dirPath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(dirPath);\n return stat.isDirectory();\n } catch {\n return false;\n }\n}\n\nexport async function listDirectories(dirPath: string): Promise<string[]> {\n try {\n const entries = await fs.readdir(dirPath, { withFileTypes: true });\n return entries.filter((e) => e.isDirectory()).map((e) => e.name);\n } catch {\n return [];\n }\n}\n\nexport async function glob(pattern: string, cwd: string): Promise<string[]> {\n return fg(pattern, { cwd, onlyFiles: true });\n}\n\nexport async function writeFile(\n filePath: string,\n content: string,\n): Promise<void> {\n await fs.ensureDir(path.dirname(filePath));\n await fs.writeFile(filePath, content, \"utf-8\");\n}\n\nexport async function ensureDir(dirPath: string): Promise<void> {\n await fs.ensureDir(dirPath);\n}\n\nexport async function remove(filePath: string): Promise<void> {\n await fs.remove(filePath);\n}\n\nexport async function copy(src: string, dest: string): Promise<void> {\n await fs.copy(src, dest);\n}\n"],"mappings":";;;;;;AAAA;AAAA,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,UAAU;AAEjB,eAAsB,SAAS,UAAmC;AAChE,SAAO,GAAG,SAAS,UAAU,OAAO;AACtC;AAEA,eAAsB,iBACpB,UACA,WAAW,IACM;AACjB,MAAI;AACF,WAAO,MAAM,GAAG,SAAS,UAAU,OAAO;AAAA,EAC5C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,WAAW,UAAoC;AACnE,SAAO,GAAG,WAAW,QAAQ;AAC/B;AAEA,eAAsB,gBAAgB,SAAmC;AACvE,MAAI;AACF,UAAM,OAAO,MAAM,GAAG,KAAK,OAAO;AAClC,WAAO,KAAK,YAAY;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,gBAAgB,SAAoC;AACxE,MAAI;AACF,UAAM,UAAU,MAAM,GAAG,QAAQ,SAAS,EAAE,eAAe,KAAK,CAAC;AACjE,WAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,EACjE,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAsB,KAAK,SAAiB,KAAgC;AAC1E,SAAO,GAAG,SAAS,EAAE,KAAK,WAAW,KAAK,CAAC;AAC7C;AAEA,eAAsB,UACpB,UACA,SACe;AACf,QAAM,GAAG,UAAU,KAAK,QAAQ,QAAQ,CAAC;AACzC,QAAM,GAAG,UAAU,UAAU,SAAS,OAAO;AAC/C;AAEA,eAAsB,UAAU,SAAgC;AAC9D,QAAM,GAAG,UAAU,OAAO;AAC5B;AAEA,eAAsB,OAAO,UAAiC;AAC5D,QAAM,GAAG,OAAO,QAAQ;AAC1B;AAEA,eAAsB,KAAK,KAAa,MAA6B;AACnE,QAAM,GAAG,KAAK,KAAK,IAAI;AACzB;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli-v2/lib/project-config.ts"],"sourcesContent":["import path from \"path\";\nimport { parse as parseYaml } from \"yaml\";\nimport { readFile, fileExists } from \"../utils/fs\";\nimport { verbose } from \"../utils/logger\";\nimport type {\n ProjectConfig,\n StackConfig,\n SkillAssignment,\n SkillEntry,\n AgentSkillConfig,\n ValidationResult,\n CustomAgentConfig,\n} from \"../../types\";\n\nconst CONFIG_PATH = \".claude/config.yaml\";\n\nexport interface LoadedProjectConfig {\n config: ProjectConfig;\n configPath: string;\n /** true if was StackConfig format (legacy) */\n isLegacy: boolean;\n}\n\n/**\n * Load project config from .claude/config.yaml\n */\nexport async function loadProjectConfig(\n projectDir: string,\n): Promise<LoadedProjectConfig | null> {\n const configPath = path.join(projectDir, CONFIG_PATH);\n\n if (!(await fileExists(configPath))) {\n verbose(`Project config not found at ${configPath}`);\n return null;\n }\n\n try {\n const content = await readFile(configPath);\n const parsed = parseYaml(content);\n\n if (!parsed || typeof parsed !== \"object\") {\n verbose(`Invalid project config structure at ${configPath}`);\n return null;\n }\n\n // Detect if this is legacy StackConfig format\n const isLegacy = isLegacyStackConfig(parsed);\n\n if (isLegacy) {\n verbose(`Detected legacy StackConfig format at ${configPath}`);\n const normalized = normalizeStackConfig(parsed as StackConfig);\n return {\n config: normalized,\n configPath,\n isLegacy: true,\n };\n }\n\n verbose(`Loaded project config from ${configPath}`);\n return {\n config: parsed as ProjectConfig,\n configPath,\n isLegacy: false,\n };\n } catch (error) {\n verbose(`Failed to parse project config: ${error}`);\n return null;\n }\n}\n\n/**\n * Check if a config object is in legacy StackConfig format.\n * Detection logic:\n * - If version is semver (e.g., \"1.0.0\") -> legacy\n * - If version is \"1\" -> new format\n * - If has 'id' field -> legacy\n * - If has 'created' or 'updated' fields -> legacy\n */\nexport function isLegacyStackConfig(config: unknown): boolean {\n if (!config || typeof config !== \"object\") return false;\n\n const obj = config as Record<string, unknown>;\n\n // If version looks like semver (x.y.z), it's legacy\n if (typeof obj.version === \"string\" && obj.version.includes(\".\")) {\n return true;\n }\n\n // If has legacy-only fields, it's legacy\n if (\n obj.id !== undefined ||\n obj.created !== undefined ||\n obj.updated !== undefined\n ) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Normalize StackConfig to ProjectConfig (for backward compatibility)\n */\nexport function normalizeStackConfig(stackConfig: StackConfig): ProjectConfig {\n const config: ProjectConfig = {\n name: stackConfig.name,\n agents: stackConfig.agents,\n };\n\n // Copy optional fields\n if (stackConfig.description) {\n config.description = stackConfig.description;\n }\n\n if (stackConfig.skills && stackConfig.skills.length > 0) {\n config.skills = stackConfig.skills;\n }\n\n if (stackConfig.agent_skills) {\n // StackConfig agent_skills is always categorized format\n config.agent_skills = stackConfig.agent_skills;\n }\n\n if (stackConfig.hooks) {\n config.hooks = stackConfig.hooks;\n }\n\n if (stackConfig.author) {\n config.author = stackConfig.author;\n }\n\n if (stackConfig.framework) {\n config.framework = stackConfig.framework;\n }\n\n if (stackConfig.philosophy) {\n config.philosophy = stackConfig.philosophy;\n }\n\n if (stackConfig.principles && stackConfig.principles.length > 0) {\n config.principles = stackConfig.principles;\n }\n\n if (stackConfig.tags && stackConfig.tags.length > 0) {\n config.tags = stackConfig.tags;\n }\n\n return config;\n}\n\n/**\n * Validate project config structure.\n * Returns validation result with errors and warnings.\n */\nexport function validateProjectConfig(config: unknown): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n if (!config || typeof config !== \"object\") {\n return { valid: false, errors: [\"Config must be an object\"], warnings: [] };\n }\n\n const c = config as Record<string, unknown>;\n\n // Required: name\n if (!c.name || typeof c.name !== \"string\") {\n errors.push(\"name is required and must be a string\");\n }\n\n // Required: agents (for compilation)\n if (!c.agents || !Array.isArray(c.agents)) {\n errors.push(\"agents is required and must be an array\");\n } else {\n for (const agent of c.agents) {\n if (typeof agent !== \"string\") {\n errors.push(`agents must contain strings, found: ${typeof agent}`);\n }\n }\n }\n\n // Optional: version\n if (c.version !== undefined && c.version !== \"1\") {\n errors.push('version must be \"1\" (or omitted for default)');\n }\n\n // Optional: skills\n if (c.skills !== undefined) {\n if (!Array.isArray(c.skills)) {\n errors.push(\"skills must be an array\");\n } else {\n for (const skill of c.skills) {\n const skillError = validateSkillEntry(skill);\n if (skillError) {\n errors.push(skillError);\n }\n }\n }\n }\n\n // Optional: agent_skills\n if (c.agent_skills !== undefined) {\n if (typeof c.agent_skills !== \"object\" || c.agent_skills === null) {\n errors.push(\"agent_skills must be an object\");\n } else {\n for (const [agentName, agentSkills] of Object.entries(c.agent_skills)) {\n const agentSkillsError = validateAgentSkillConfig(\n agentName,\n agentSkills,\n );\n if (agentSkillsError) {\n errors.push(agentSkillsError);\n }\n }\n }\n }\n\n // Optional: preload_patterns\n if (c.preload_patterns !== undefined) {\n if (typeof c.preload_patterns !== \"object\" || c.preload_patterns === null) {\n errors.push(\"preload_patterns must be an object\");\n } else {\n for (const [agentName, patterns] of Object.entries(c.preload_patterns)) {\n if (!Array.isArray(patterns)) {\n errors.push(\n `preload_patterns.${agentName} must be an array of strings`,\n );\n } else {\n for (const pattern of patterns) {\n if (typeof pattern !== \"string\") {\n errors.push(\n `preload_patterns.${agentName} must contain only strings`,\n );\n break;\n }\n }\n }\n }\n }\n }\n\n // Optional: custom_agents\n if (c.custom_agents !== undefined) {\n const customAgentsErrors = validateCustomAgents(c.custom_agents, c.agents);\n errors.push(...customAgentsErrors);\n }\n\n // Warnings for deprecated patterns\n if (c.id !== undefined) {\n warnings.push(\"id field is deprecated in project config\");\n }\n if (c.created !== undefined) {\n warnings.push(\"created field is deprecated in project config\");\n }\n if (c.updated !== undefined) {\n warnings.push(\"updated field is deprecated in project config\");\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/** Maximum number of custom agents allowed per project */\nconst MAX_CUSTOM_AGENTS = 20;\n\n/** Valid model values for custom agents */\nconst VALID_MODELS = [\"sonnet\", \"opus\", \"haiku\", \"inherit\"];\n\n/** Valid permission modes for custom agents */\nconst VALID_PERMISSION_MODES = [\n \"default\",\n \"acceptEdits\",\n \"dontAsk\",\n \"bypassPermissions\",\n \"plan\",\n \"delegate\",\n];\n\n/**\n * Validate custom_agents section of config\n */\nfunction validateCustomAgents(\n customAgents: unknown,\n agents: unknown,\n): string[] {\n const errors: string[] = [];\n\n if (typeof customAgents !== \"object\" || customAgents === null) {\n errors.push(\"custom_agents must be an object\");\n return errors;\n }\n\n const customAgentEntries = Object.entries(customAgents);\n\n // Check maximum limit\n if (customAgentEntries.length > MAX_CUSTOM_AGENTS) {\n errors.push(\n `custom_agents cannot exceed ${MAX_CUSTOM_AGENTS} agents (found ${customAgentEntries.length})`,\n );\n }\n\n // Collect custom agent names for circular reference check\n const customAgentNames = new Set(customAgentEntries.map(([name]) => name));\n\n for (const [agentName, agentConfig] of customAgentEntries) {\n const agentErrors = validateCustomAgentConfig(\n agentName,\n agentConfig,\n customAgentNames,\n );\n errors.push(...agentErrors);\n }\n\n return errors;\n}\n\n/**\n * Validate a single custom agent configuration\n */\nfunction validateCustomAgentConfig(\n agentName: string,\n config: unknown,\n customAgentNames: Set<string>,\n): string[] {\n const errors: string[] = [];\n const prefix = `custom_agents.${agentName}`;\n\n if (typeof config !== \"object\" || config === null) {\n errors.push(`${prefix} must be an object`);\n return errors;\n }\n\n const c = config as Record<string, unknown>;\n\n // Required: title\n if (!c.title || typeof c.title !== \"string\") {\n errors.push(`${prefix}.title is required and must be a string`);\n }\n\n // Required: description\n if (!c.description || typeof c.description !== \"string\") {\n errors.push(`${prefix}.description is required and must be a string`);\n }\n\n // Optional: extends\n if (c.extends !== undefined) {\n if (typeof c.extends !== \"string\") {\n errors.push(`${prefix}.extends must be a string`);\n } else if (customAgentNames.has(c.extends)) {\n // Custom agents cannot extend other custom agents\n errors.push(\n `${prefix}.extends cannot reference another custom agent \"${c.extends}\"`,\n );\n }\n }\n\n // Optional: model\n if (c.model !== undefined) {\n if (typeof c.model !== \"string\" || !VALID_MODELS.includes(c.model)) {\n errors.push(`${prefix}.model must be one of: ${VALID_MODELS.join(\", \")}`);\n }\n }\n\n // Optional: tools\n if (c.tools !== undefined) {\n if (!Array.isArray(c.tools)) {\n errors.push(`${prefix}.tools must be an array`);\n } else {\n for (const tool of c.tools) {\n if (typeof tool !== \"string\") {\n errors.push(`${prefix}.tools must contain only strings`);\n break;\n }\n }\n }\n }\n\n // Optional: disallowed_tools\n if (c.disallowed_tools !== undefined) {\n if (!Array.isArray(c.disallowed_tools)) {\n errors.push(`${prefix}.disallowed_tools must be an array`);\n } else {\n for (const tool of c.disallowed_tools) {\n if (typeof tool !== \"string\") {\n errors.push(`${prefix}.disallowed_tools must contain only strings`);\n break;\n }\n }\n }\n }\n\n // Optional: permission_mode\n if (c.permission_mode !== undefined) {\n if (\n typeof c.permission_mode !== \"string\" ||\n !VALID_PERMISSION_MODES.includes(c.permission_mode)\n ) {\n errors.push(\n `${prefix}.permission_mode must be one of: ${VALID_PERMISSION_MODES.join(\", \")}`,\n );\n }\n }\n\n // Optional: skills\n if (c.skills !== undefined) {\n if (!Array.isArray(c.skills)) {\n errors.push(`${prefix}.skills must be an array`);\n } else {\n for (const skill of c.skills) {\n const skillError = validateSkillEntry(skill);\n if (skillError) {\n errors.push(`${prefix}.skills: ${skillError}`);\n }\n }\n }\n }\n\n // Optional: hooks\n if (c.hooks !== undefined) {\n if (typeof c.hooks !== \"object\" || c.hooks === null) {\n errors.push(`${prefix}.hooks must be an object`);\n }\n // Note: Detailed hook validation could be added later if needed\n }\n\n return errors;\n}\n\n/**\n * Validate a single skill entry\n */\nfunction validateSkillEntry(skill: unknown): string | null {\n if (typeof skill === \"string\") {\n return null; // String skill IDs are valid\n }\n\n if (typeof skill !== \"object\" || skill === null) {\n return \"skills must be strings or objects\";\n }\n\n const s = skill as Record<string, unknown>;\n\n if (!s.id || typeof s.id !== \"string\") {\n return \"skill object must have an id string\";\n }\n\n if (s.local === true && !s.path) {\n return `local skill \"${s.id}\" must have a path`;\n }\n\n if (s.preloaded !== undefined && typeof s.preloaded !== \"boolean\") {\n return `skill \"${s.id}\" preloaded must be a boolean`;\n }\n\n if (s.local !== undefined && typeof s.local !== \"boolean\") {\n return `skill \"${s.id}\" local must be a boolean`;\n }\n\n if (s.path !== undefined && typeof s.path !== \"string\") {\n return `skill \"${s.id}\" path must be a string`;\n }\n\n return null;\n}\n\n/**\n * Validate agent skill config (can be simple list or categorized)\n */\nfunction validateAgentSkillConfig(\n agentName: string,\n agentSkills: unknown,\n): string | null {\n // Check if it's a simple list (array)\n if (Array.isArray(agentSkills)) {\n for (const skill of agentSkills) {\n const skillError = validateSkillEntry(skill);\n if (skillError) {\n return `agent_skills.${agentName}: ${skillError}`;\n }\n }\n return null;\n }\n\n // Check if it's categorized (object with array values)\n if (typeof agentSkills === \"object\" && agentSkills !== null) {\n for (const [category, skills] of Object.entries(agentSkills)) {\n if (!Array.isArray(skills)) {\n return `agent_skills.${agentName}.${category} must be an array`;\n }\n for (const skill of skills) {\n const skillError = validateSkillEntry(skill);\n if (skillError) {\n return `agent_skills.${agentName}.${category}: ${skillError}`;\n }\n }\n }\n return null;\n }\n\n return `agent_skills.${agentName} must be an array or object`;\n}\n\n/**\n * Check if agent_skills value is in simple list format (array)\n */\nexport function isSimpleAgentSkills(value: unknown): value is SkillEntry[] {\n return Array.isArray(value);\n}\n\n/**\n * Normalize a skill entry to SkillAssignment\n */\nexport function normalizeSkillEntry(entry: SkillEntry): SkillAssignment {\n if (typeof entry === \"string\") {\n return { id: entry };\n }\n return entry;\n}\n\n/**\n * Normalize agent_skills to always be categorized format for internal use.\n * Simple lists are placed under an \"uncategorized\" key.\n */\nexport function normalizeAgentSkills(\n agentSkills: Record<string, AgentSkillConfig>,\n): Record<string, Record<string, SkillAssignment[]>> {\n const result: Record<string, Record<string, SkillAssignment[]>> = {};\n\n for (const [agentName, skills] of Object.entries(agentSkills)) {\n if (isSimpleAgentSkills(skills)) {\n // Simple list -> put under \"uncategorized\"\n result[agentName] = {\n uncategorized: skills.map(normalizeSkillEntry),\n };\n } else {\n // Already categorized -> normalize entries\n result[agentName] = {};\n for (const [category, categorySkills] of Object.entries(skills)) {\n result[agentName][category] = categorySkills.map(normalizeSkillEntry);\n }\n }\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;AAAA;AAAA,OAAO,UAAU;AACjB,SAAS,SAAS,iBAAiB;AAanC,IAAM,cAAc;AAYpB,eAAsB,kBACpB,YACqC;AACrC,QAAM,aAAa,KAAK,KAAK,YAAY,WAAW;AAEpD,MAAI,CAAE,MAAM,WAAW,UAAU,GAAI;AACnC,YAAQ,+BAA+B,UAAU,EAAE;AACnD,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,UAAU;AACzC,UAAM,SAAS,UAAU,OAAO;AAEhC,QAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,cAAQ,uCAAuC,UAAU,EAAE;AAC3D,aAAO;AAAA,IACT;AAGA,UAAM,WAAW,oBAAoB,MAAM;AAE3C,QAAI,UAAU;AACZ,cAAQ,yCAAyC,UAAU,EAAE;AAC7D,YAAM,aAAa,qBAAqB,MAAqB;AAC7D,aAAO;AAAA,QACL,QAAQ;AAAA,QACR;AAAA,QACA,UAAU;AAAA,MACZ;AAAA,IACF;AAEA,YAAQ,8BAA8B,UAAU,EAAE;AAClD,WAAO;AAAA,MACL,QAAQ;AAAA,MACR;AAAA,MACA,UAAU;AAAA,IACZ;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,mCAAmC,KAAK,EAAE;AAClD,WAAO;AAAA,EACT;AACF;AAUO,SAAS,oBAAoB,QAA0B;AAC5D,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAElD,QAAM,MAAM;AAGZ,MAAI,OAAO,IAAI,YAAY,YAAY,IAAI,QAAQ,SAAS,GAAG,GAAG;AAChE,WAAO;AAAA,EACT;AAGA,MACE,IAAI,OAAO,UACX,IAAI,YAAY,UAChB,IAAI,YAAY,QAChB;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAKO,SAAS,qBAAqB,aAAyC;AAC5E,QAAM,SAAwB;AAAA,IAC5B,MAAM,YAAY;AAAA,IAClB,QAAQ,YAAY;AAAA,EACtB;AAGA,MAAI,YAAY,aAAa;AAC3B,WAAO,cAAc,YAAY;AAAA,EACnC;AAEA,MAAI,YAAY,UAAU,YAAY,OAAO,SAAS,GAAG;AACvD,WAAO,SAAS,YAAY;AAAA,EAC9B;AAEA,MAAI,YAAY,cAAc;AAE5B,WAAO,eAAe,YAAY;AAAA,EACpC;AAEA,MAAI,YAAY,OAAO;AACrB,WAAO,QAAQ,YAAY;AAAA,EAC7B;AAEA,MAAI,YAAY,QAAQ;AACtB,WAAO,SAAS,YAAY;AAAA,EAC9B;AAEA,MAAI,YAAY,WAAW;AACzB,WAAO,YAAY,YAAY;AAAA,EACjC;AAEA,MAAI,YAAY,YAAY;AAC1B,WAAO,aAAa,YAAY;AAAA,EAClC;AAEA,MAAI,YAAY,cAAc,YAAY,WAAW,SAAS,GAAG;AAC/D,WAAO,aAAa,YAAY;AAAA,EAClC;AAEA,MAAI,YAAY,QAAQ,YAAY,KAAK,SAAS,GAAG;AACnD,WAAO,OAAO,YAAY;AAAA,EAC5B;AAEA,SAAO;AACT;AAMO,SAAS,sBAAsB,QAAmC;AACvE,QAAM,SAAmB,CAAC;AAC1B,QAAM,WAAqB,CAAC;AAE5B,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,WAAO,EAAE,OAAO,OAAO,QAAQ,CAAC,0BAA0B,GAAG,UAAU,CAAC,EAAE;AAAA,EAC5E;AAEA,QAAM,IAAI;AAGV,MAAI,CAAC,EAAE,QAAQ,OAAO,EAAE,SAAS,UAAU;AACzC,WAAO,KAAK,uCAAuC;AAAA,EACrD;AAGA,MAAI,CAAC,EAAE,UAAU,CAAC,MAAM,QAAQ,EAAE,MAAM,GAAG;AACzC,WAAO,KAAK,yCAAyC;AAAA,EACvD,OAAO;AACL,eAAW,SAAS,EAAE,QAAQ;AAC5B,UAAI,OAAO,UAAU,UAAU;AAC7B,eAAO,KAAK,uCAAuC,OAAO,KAAK,EAAE;AAAA,MACnE;AAAA,IACF;AAAA,EACF;AAGA,MAAI,EAAE,YAAY,UAAa,EAAE,YAAY,KAAK;AAChD,WAAO,KAAK,8CAA8C;AAAA,EAC5D;AAGA,MAAI,EAAE,WAAW,QAAW;AAC1B,QAAI,CAAC,MAAM,QAAQ,EAAE,MAAM,GAAG;AAC5B,aAAO,KAAK,yBAAyB;AAAA,IACvC,OAAO;AACL,iBAAW,SAAS,EAAE,QAAQ;AAC5B,cAAM,aAAa,mBAAmB,KAAK;AAC3C,YAAI,YAAY;AACd,iBAAO,KAAK,UAAU;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,EAAE,iBAAiB,QAAW;AAChC,QAAI,OAAO,EAAE,iBAAiB,YAAY,EAAE,iBAAiB,MAAM;AACjE,aAAO,KAAK,gCAAgC;AAAA,IAC9C,OAAO;AACL,iBAAW,CAAC,WAAW,WAAW,KAAK,OAAO,QAAQ,EAAE,YAAY,GAAG;AACrE,cAAM,mBAAmB;AAAA,UACvB;AAAA,UACA;AAAA,QACF;AACA,YAAI,kBAAkB;AACpB,iBAAO,KAAK,gBAAgB;AAAA,QAC9B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,EAAE,qBAAqB,QAAW;AACpC,QAAI,OAAO,EAAE,qBAAqB,YAAY,EAAE,qBAAqB,MAAM;AACzE,aAAO,KAAK,oCAAoC;AAAA,IAClD,OAAO;AACL,iBAAW,CAAC,WAAW,QAAQ,KAAK,OAAO,QAAQ,EAAE,gBAAgB,GAAG;AACtE,YAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG;AAC5B,iBAAO;AAAA,YACL,oBAAoB,SAAS;AAAA,UAC/B;AAAA,QACF,OAAO;AACL,qBAAW,WAAW,UAAU;AAC9B,gBAAI,OAAO,YAAY,UAAU;AAC/B,qBAAO;AAAA,gBACL,oBAAoB,SAAS;AAAA,cAC/B;AACA;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,EAAE,kBAAkB,QAAW;AACjC,UAAM,qBAAqB,qBAAqB,EAAE,eAAe,EAAE,MAAM;AACzE,WAAO,KAAK,GAAG,kBAAkB;AAAA,EACnC;AAGA,MAAI,EAAE,OAAO,QAAW;AACtB,aAAS,KAAK,0CAA0C;AAAA,EAC1D;AACA,MAAI,EAAE,YAAY,QAAW;AAC3B,aAAS,KAAK,+CAA+C;AAAA,EAC/D;AACA,MAAI,EAAE,YAAY,QAAW;AAC3B,aAAS,KAAK,+CAA+C;AAAA,EAC/D;AAEA,SAAO;AAAA,IACL,OAAO,OAAO,WAAW;AAAA,IACzB;AAAA,IACA;AAAA,EACF;AACF;AAGA,IAAM,oBAAoB;AAG1B,IAAM,eAAe,CAAC,UAAU,QAAQ,SAAS,SAAS;AAG1D,IAAM,yBAAyB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAKA,SAAS,qBACP,cACA,QACU;AACV,QAAM,SAAmB,CAAC;AAE1B,MAAI,OAAO,iBAAiB,YAAY,iBAAiB,MAAM;AAC7D,WAAO,KAAK,iCAAiC;AAC7C,WAAO;AAAA,EACT;AAEA,QAAM,qBAAqB,OAAO,QAAQ,YAAY;AAGtD,MAAI,mBAAmB,SAAS,mBAAmB;AACjD,WAAO;AAAA,MACL,+BAA+B,iBAAiB,kBAAkB,mBAAmB,MAAM;AAAA,IAC7F;AAAA,EACF;AAGA,QAAM,mBAAmB,IAAI,IAAI,mBAAmB,IAAI,CAAC,CAAC,IAAI,MAAM,IAAI,CAAC;AAEzE,aAAW,CAAC,WAAW,WAAW,KAAK,oBAAoB;AACzD,UAAM,cAAc;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,WAAO,KAAK,GAAG,WAAW;AAAA,EAC5B;AAEA,SAAO;AACT;AAKA,SAAS,0BACP,WACA,QACA,kBACU;AACV,QAAM,SAAmB,CAAC;AAC1B,QAAM,SAAS,iBAAiB,SAAS;AAEzC,MAAI,OAAO,WAAW,YAAY,WAAW,MAAM;AACjD,WAAO,KAAK,GAAG,MAAM,oBAAoB;AACzC,WAAO;AAAA,EACT;AAEA,QAAM,IAAI;AAGV,MAAI,CAAC,EAAE,SAAS,OAAO,EAAE,UAAU,UAAU;AAC3C,WAAO,KAAK,GAAG,MAAM,yCAAyC;AAAA,EAChE;AAGA,MAAI,CAAC,EAAE,eAAe,OAAO,EAAE,gBAAgB,UAAU;AACvD,WAAO,KAAK,GAAG,MAAM,+CAA+C;AAAA,EACtE;AAGA,MAAI,EAAE,YAAY,QAAW;AAC3B,QAAI,OAAO,EAAE,YAAY,UAAU;AACjC,aAAO,KAAK,GAAG,MAAM,2BAA2B;AAAA,IAClD,WAAW,iBAAiB,IAAI,EAAE,OAAO,GAAG;AAE1C,aAAO;AAAA,QACL,GAAG,MAAM,mDAAmD,EAAE,OAAO;AAAA,MACvE;AAAA,IACF;AAAA,EACF;AAGA,MAAI,EAAE,UAAU,QAAW;AACzB,QAAI,OAAO,EAAE,UAAU,YAAY,CAAC,aAAa,SAAS,EAAE,KAAK,GAAG;AAClE,aAAO,KAAK,GAAG,MAAM,0BAA0B,aAAa,KAAK,IAAI,CAAC,EAAE;AAAA,IAC1E;AAAA,EACF;AAGA,MAAI,EAAE,UAAU,QAAW;AACzB,QAAI,CAAC,MAAM,QAAQ,EAAE,KAAK,GAAG;AAC3B,aAAO,KAAK,GAAG,MAAM,yBAAyB;AAAA,IAChD,OAAO;AACL,iBAAW,QAAQ,EAAE,OAAO;AAC1B,YAAI,OAAO,SAAS,UAAU;AAC5B,iBAAO,KAAK,GAAG,MAAM,kCAAkC;AACvD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,EAAE,qBAAqB,QAAW;AACpC,QAAI,CAAC,MAAM,QAAQ,EAAE,gBAAgB,GAAG;AACtC,aAAO,KAAK,GAAG,MAAM,oCAAoC;AAAA,IAC3D,OAAO;AACL,iBAAW,QAAQ,EAAE,kBAAkB;AACrC,YAAI,OAAO,SAAS,UAAU;AAC5B,iBAAO,KAAK,GAAG,MAAM,6CAA6C;AAClE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,EAAE,oBAAoB,QAAW;AACnC,QACE,OAAO,EAAE,oBAAoB,YAC7B,CAAC,uBAAuB,SAAS,EAAE,eAAe,GAClD;AACA,aAAO;AAAA,QACL,GAAG,MAAM,oCAAoC,uBAAuB,KAAK,IAAI,CAAC;AAAA,MAChF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,EAAE,WAAW,QAAW;AAC1B,QAAI,CAAC,MAAM,QAAQ,EAAE,MAAM,GAAG;AAC5B,aAAO,KAAK,GAAG,MAAM,0BAA0B;AAAA,IACjD,OAAO;AACL,iBAAW,SAAS,EAAE,QAAQ;AAC5B,cAAM,aAAa,mBAAmB,KAAK;AAC3C,YAAI,YAAY;AACd,iBAAO,KAAK,GAAG,MAAM,YAAY,UAAU,EAAE;AAAA,QAC/C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,EAAE,UAAU,QAAW;AACzB,QAAI,OAAO,EAAE,UAAU,YAAY,EAAE,UAAU,MAAM;AACnD,aAAO,KAAK,GAAG,MAAM,0BAA0B;AAAA,IACjD;AAAA,EAEF;AAEA,SAAO;AACT;AAKA,SAAS,mBAAmB,OAA+B;AACzD,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAO;AAAA,EACT;AAEA,QAAM,IAAI;AAEV,MAAI,CAAC,EAAE,MAAM,OAAO,EAAE,OAAO,UAAU;AACrC,WAAO;AAAA,EACT;AAEA,MAAI,EAAE,UAAU,QAAQ,CAAC,EAAE,MAAM;AAC/B,WAAO,gBAAgB,EAAE,EAAE;AAAA,EAC7B;AAEA,MAAI,EAAE,cAAc,UAAa,OAAO,EAAE,cAAc,WAAW;AACjE,WAAO,UAAU,EAAE,EAAE;AAAA,EACvB;AAEA,MAAI,EAAE,UAAU,UAAa,OAAO,EAAE,UAAU,WAAW;AACzD,WAAO,UAAU,EAAE,EAAE;AAAA,EACvB;AAEA,MAAI,EAAE,SAAS,UAAa,OAAO,EAAE,SAAS,UAAU;AACtD,WAAO,UAAU,EAAE,EAAE;AAAA,EACvB;AAEA,SAAO;AACT;AAKA,SAAS,yBACP,WACA,aACe;AAEf,MAAI,MAAM,QAAQ,WAAW,GAAG;AAC9B,eAAW,SAAS,aAAa;AAC/B,YAAM,aAAa,mBAAmB,KAAK;AAC3C,UAAI,YAAY;AACd,eAAO,gBAAgB,SAAS,KAAK,UAAU;AAAA,MACjD;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,gBAAgB,YAAY,gBAAgB,MAAM;AAC3D,eAAW,CAAC,UAAU,MAAM,KAAK,OAAO,QAAQ,WAAW,GAAG;AAC5D,UAAI,CAAC,MAAM,QAAQ,MAAM,GAAG;AAC1B,eAAO,gBAAgB,SAAS,IAAI,QAAQ;AAAA,MAC9C;AACA,iBAAW,SAAS,QAAQ;AAC1B,cAAM,aAAa,mBAAmB,KAAK;AAC3C,YAAI,YAAY;AACd,iBAAO,gBAAgB,SAAS,IAAI,QAAQ,KAAK,UAAU;AAAA,QAC7D;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,SAAO,gBAAgB,SAAS;AAClC;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli-v2/lib/skill-copier.ts"],"sourcesContent":["import path from \"path\";\nimport { stringify as stringifyYaml, parse as parseYaml } from \"yaml\";\nimport { copy, ensureDir, readFile, writeFile } from \"../utils/fs\";\nimport { hashFile } from \"./versioning\";\nimport type { MergedSkillsMatrix, ResolvedSkill } from \"../types-matrix\";\nimport type { SourceLoadResult } from \"./source-loader\";\n\ninterface ForkedFromMetadata {\n skill_id: string;\n content_hash: string;\n date: string;\n}\n\ninterface SkillMetadata {\n content_hash?: string;\n forked_from?: ForkedFromMetadata;\n [key: string]: unknown;\n}\n\nconst METADATA_FILE_NAME = \"metadata.yaml\";\n\nexport interface CopiedSkill {\n skillId: string;\n contentHash: string;\n sourcePath: string;\n destPath: string;\n local?: boolean;\n}\n\nfunction getSkillSourcePath(\n skill: ResolvedSkill,\n registryRoot: string,\n): string {\n return path.join(registryRoot, \"src\", skill.path);\n}\n\nfunction getSkillDestPath(skill: ResolvedSkill, stackDir: string): string {\n const skillRelativePath = skill.path.replace(/^skills\\//, \"\");\n return path.join(stackDir, \"skills\", skillRelativePath);\n}\n\nasync function generateSkillHash(skillSourcePath: string): Promise<string> {\n const skillMdPath = path.join(skillSourcePath, \"SKILL.md\");\n return hashFile(skillMdPath);\n}\n\nfunction getCurrentDate(): string {\n return new Date().toISOString().split(\"T\")[0];\n}\n\nasync function injectForkedFromMetadata(\n destPath: string,\n skillId: string,\n contentHash: string,\n): Promise<void> {\n const metadataPath = path.join(destPath, METADATA_FILE_NAME);\n const rawContent = await readFile(metadataPath);\n\n const lines = rawContent.split(\"\\n\");\n let yamlContent = rawContent;\n\n if (lines[0]?.startsWith(\"# yaml-language-server:\")) {\n yamlContent = lines.slice(1).join(\"\\n\");\n }\n\n const metadata = parseYaml(yamlContent) as SkillMetadata;\n\n metadata.forked_from = {\n skill_id: skillId,\n content_hash: contentHash,\n date: getCurrentDate(),\n };\n\n const newYamlContent = stringifyYaml(metadata, { lineWidth: 0 });\n await writeFile(metadataPath, newYamlContent);\n}\n\nexport async function copySkill(\n skill: ResolvedSkill,\n stackDir: string,\n registryRoot: string,\n): Promise<CopiedSkill> {\n const sourcePath = getSkillSourcePath(skill, registryRoot);\n const destPath = getSkillDestPath(skill, stackDir);\n\n const contentHash = await generateSkillHash(sourcePath);\n\n await ensureDir(path.dirname(destPath));\n await copy(sourcePath, destPath);\n\n await injectForkedFromMetadata(destPath, skill.id, contentHash);\n\n return {\n skillId: skill.id,\n contentHash,\n sourcePath,\n destPath,\n };\n}\n\nfunction getSkillSourcePathFromSource(\n skill: ResolvedSkill,\n sourceResult: SourceLoadResult,\n): string {\n return path.join(sourceResult.sourcePath, \"src\", skill.path);\n}\n\nexport async function copySkillFromSource(\n skill: ResolvedSkill,\n stackDir: string,\n sourceResult: SourceLoadResult,\n): Promise<CopiedSkill> {\n const sourcePath = getSkillSourcePathFromSource(skill, sourceResult);\n const destPath = getSkillDestPath(skill, stackDir);\n\n const contentHash = await generateSkillHash(sourcePath);\n\n await ensureDir(path.dirname(destPath));\n await copy(sourcePath, destPath);\n\n await injectForkedFromMetadata(destPath, skill.id, contentHash);\n\n return {\n skillId: skill.id,\n contentHash,\n sourcePath,\n destPath,\n };\n}\n\nexport async function copySkillsToPluginFromSource(\n selectedSkillIds: string[],\n pluginDir: string,\n matrix: MergedSkillsMatrix,\n sourceResult: SourceLoadResult,\n): Promise<CopiedSkill[]> {\n const copiedSkills: CopiedSkill[] = [];\n\n for (const skillId of selectedSkillIds) {\n const skill = matrix.skills[skillId];\n if (!skill) {\n console.warn(`Warning: Skill not found in matrix: ${skillId}`);\n continue;\n }\n\n if (skill.local && skill.localPath) {\n const localSkillPath = path.join(process.cwd(), skill.localPath);\n const contentHash = await generateSkillHash(localSkillPath);\n\n copiedSkills.push({\n skillId: skill.id,\n sourcePath: skill.localPath,\n destPath: skill.localPath,\n contentHash,\n local: true,\n });\n continue;\n }\n\n const copied = await copySkillFromSource(skill, pluginDir, sourceResult);\n copiedSkills.push(copied);\n }\n\n return copiedSkills;\n}\n\nfunction getFlattenedSkillDestPath(\n skill: ResolvedSkill,\n localSkillsDir: string,\n): string {\n const skillFolderName = skill.alias || extractSkillNameFromId(skill.id);\n return path.join(localSkillsDir, skillFolderName);\n}\n\nfunction extractSkillNameFromId(skillId: string): string {\n const withoutAuthor = skillId.replace(/\\s*\\(@\\w+\\)$/, \"\").trim();\n return withoutAuthor;\n}\n\nasync function copySkillToLocalFlattened(\n skill: ResolvedSkill,\n localSkillsDir: string,\n sourceResult: SourceLoadResult,\n): Promise<CopiedSkill> {\n const sourcePath = getSkillSourcePathFromSource(skill, sourceResult);\n const destPath = getFlattenedSkillDestPath(skill, localSkillsDir);\n\n const contentHash = await generateSkillHash(sourcePath);\n\n await ensureDir(path.dirname(destPath));\n await copy(sourcePath, destPath);\n\n await injectForkedFromMetadata(destPath, skill.id, contentHash);\n\n return {\n skillId: skill.id,\n contentHash,\n sourcePath,\n destPath,\n };\n}\n\nexport async function copySkillsToLocalFlattened(\n selectedSkillIds: string[],\n localSkillsDir: string,\n matrix: MergedSkillsMatrix,\n sourceResult: SourceLoadResult,\n): Promise<CopiedSkill[]> {\n const copiedSkills: CopiedSkill[] = [];\n\n for (const skillId of selectedSkillIds) {\n const skill = matrix.skills[skillId];\n if (!skill) {\n console.warn(`Warning: Skill not found in matrix: ${skillId}`);\n continue;\n }\n\n if (skill.local && skill.localPath) {\n const localSkillPath = path.join(process.cwd(), skill.localPath);\n const contentHash = await generateSkillHash(localSkillPath);\n\n copiedSkills.push({\n skillId: skill.id,\n sourcePath: skill.localPath,\n destPath: skill.localPath,\n contentHash,\n local: true,\n });\n continue;\n }\n\n const copied = await copySkillToLocalFlattened(\n skill,\n localSkillsDir,\n sourceResult,\n );\n copiedSkills.push(copied);\n }\n\n return copiedSkills;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli-v2/lib/config.ts"],"sourcesContent":["import path from \"path\";\nimport os from \"os\";\nimport { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport { readFile, writeFile, fileExists, ensureDir } from \"../utils/fs\";\nimport { verbose } from \"../utils/logger\";\n\nconst PROJECT_CONFIG_DIR = \".claude-collective\";\n\nexport const DEFAULT_SOURCE = \"github:claude-collective/skills\";\nexport const SOURCE_ENV_VAR = \"CC_SOURCE\";\n/** Environment variable to override global config directory (used for testing) */\nexport const CONFIG_HOME_ENV_VAR = \"CC_CONFIG_HOME\";\nexport const GLOBAL_CONFIG_FILE = \"config.yaml\";\nexport const PROJECT_CONFIG_FILE = \"config.yaml\";\n\n/**\n * Get global config directory path.\n * Can be overridden via CC_CONFIG_HOME environment variable for testing isolation.\n * This is a function (not constant) to allow tests to set the env var after module load.\n */\nexport function getGlobalConfigDir(): string {\n return (\n process.env[CONFIG_HOME_ENV_VAR] ||\n path.join(os.homedir(), \".claude-collective\")\n );\n}\n\n/**\n * @deprecated Use getGlobalConfigDir() instead. This constant is kept for backwards compatibility\n * but won't respect CC_CONFIG_HOME set after module load.\n */\nexport const GLOBAL_CONFIG_DIR = path.join(os.homedir(), \".claude-collective\");\n\nexport interface GlobalConfig {\n source?: string;\n author?: string;\n marketplace?: string;\n agents_source?: string;\n}\n\nexport interface ProjectConfig {\n source?: string;\n marketplace?: string;\n agents_source?: string;\n}\n\nexport interface ResolvedConfig {\n source: string;\n sourceOrigin: \"flag\" | \"env\" | \"project\" | \"global\" | \"default\";\n marketplace?: string;\n}\n\nfunction isValidGlobalConfig(obj: unknown): obj is GlobalConfig {\n if (typeof obj !== \"object\" || obj === null) return false;\n const config = obj as Record<string, unknown>;\n if (config.source !== undefined && typeof config.source !== \"string\")\n return false;\n if (config.author !== undefined && typeof config.author !== \"string\")\n return false;\n if (\n config.marketplace !== undefined &&\n typeof config.marketplace !== \"string\"\n )\n return false;\n if (\n config.agents_source !== undefined &&\n typeof config.agents_source !== \"string\"\n )\n return false;\n return true;\n}\n\nfunction isValidProjectConfig(obj: unknown): obj is ProjectConfig {\n if (typeof obj !== \"object\" || obj === null) return false;\n const config = obj as Record<string, unknown>;\n if (config.source !== undefined && typeof config.source !== \"string\")\n return false;\n if (\n config.marketplace !== undefined &&\n typeof config.marketplace !== \"string\"\n )\n return false;\n if (\n config.agents_source !== undefined &&\n typeof config.agents_source !== \"string\"\n )\n return false;\n return true;\n}\n\nexport function getGlobalConfigPath(): string {\n return path.join(getGlobalConfigDir(), GLOBAL_CONFIG_FILE);\n}\n\nexport function getProjectConfigPath(projectDir: string): string {\n return path.join(projectDir, PROJECT_CONFIG_DIR, PROJECT_CONFIG_FILE);\n}\n\nexport async function loadGlobalConfig(): Promise<GlobalConfig | null> {\n const configPath = getGlobalConfigPath();\n\n if (!(await fileExists(configPath))) {\n verbose(`Global config not found at ${configPath}`);\n return null;\n }\n\n try {\n const content = await readFile(configPath);\n const parsed = parseYaml(content);\n if (!isValidGlobalConfig(parsed)) {\n verbose(`Invalid global config structure at ${configPath}`);\n return null;\n }\n verbose(`Loaded global config from ${configPath}`);\n return parsed;\n } catch (error) {\n verbose(`Failed to parse global config: ${error}`);\n return null;\n }\n}\n\nexport async function loadProjectConfig(\n projectDir: string,\n): Promise<ProjectConfig | null> {\n const configPath = getProjectConfigPath(projectDir);\n\n if (!(await fileExists(configPath))) {\n verbose(`Project config not found at ${configPath}`);\n return null;\n }\n\n try {\n const content = await readFile(configPath);\n const parsed = parseYaml(content);\n if (!isValidProjectConfig(parsed)) {\n verbose(`Invalid project config structure at ${configPath}`);\n return null;\n }\n verbose(`Loaded project config from ${configPath}`);\n return parsed;\n } catch (error) {\n verbose(`Failed to parse project config: ${error}`);\n return null;\n }\n}\n\nexport async function saveGlobalConfig(config: GlobalConfig): Promise<void> {\n const configPath = getGlobalConfigPath();\n await ensureDir(getGlobalConfigDir());\n const content = stringifyYaml(config, { lineWidth: 0 });\n await writeFile(configPath, content);\n verbose(`Saved global config to ${configPath}`);\n}\n\nexport async function saveProjectConfig(\n projectDir: string,\n config: ProjectConfig,\n): Promise<void> {\n const configPath = getProjectConfigPath(projectDir);\n await ensureDir(path.join(projectDir, PROJECT_CONFIG_DIR));\n const content = stringifyYaml(config, { lineWidth: 0 });\n await writeFile(configPath, content);\n verbose(`Saved project config to ${configPath}`);\n}\n\n/** Resolve source with precedence: flag > env > project > global > default */\nexport async function resolveSource(\n flagValue?: string,\n projectDir?: string,\n): Promise<ResolvedConfig> {\n // Load configs to get marketplace (marketplace is resolved separately from source)\n const projectConfig = projectDir ? await loadProjectConfig(projectDir) : null;\n const globalConfig = await loadGlobalConfig();\n\n // Resolve marketplace: project > global (no flag/env support for marketplace)\n const marketplace = projectConfig?.marketplace || globalConfig?.marketplace;\n\n if (flagValue !== undefined) {\n if (flagValue === \"\" || flagValue.trim() === \"\") {\n throw new Error(\"--source flag cannot be empty\");\n }\n verbose(`Source from --source flag: ${flagValue}`);\n return { source: flagValue, sourceOrigin: \"flag\", marketplace };\n }\n\n const envValue = process.env[SOURCE_ENV_VAR];\n if (envValue) {\n verbose(`Source from ${SOURCE_ENV_VAR} env var: ${envValue}`);\n return { source: envValue, sourceOrigin: \"env\", marketplace };\n }\n\n if (projectConfig?.source) {\n verbose(`Source from project config: ${projectConfig.source}`);\n return {\n source: projectConfig.source,\n sourceOrigin: \"project\",\n marketplace,\n };\n }\n\n if (globalConfig?.source) {\n verbose(`Source from global config: ${globalConfig.source}`);\n return { source: globalConfig.source, sourceOrigin: \"global\", marketplace };\n }\n\n verbose(`Using default source: ${DEFAULT_SOURCE}`);\n return { source: DEFAULT_SOURCE, sourceOrigin: \"default\", marketplace };\n}\n\nexport type AgentsSourceOrigin = \"flag\" | \"project\" | \"global\" | \"default\";\n\nexport interface ResolvedAgentsSource {\n agentsSource?: string;\n agentsSourceOrigin: AgentsSourceOrigin;\n}\n\n/** Resolve agents_source with precedence: flag > project > global > default (undefined) */\nexport async function resolveAgentsSource(\n flagValue?: string,\n projectDir?: string,\n): Promise<ResolvedAgentsSource> {\n if (flagValue !== undefined) {\n if (flagValue === \"\" || flagValue.trim() === \"\") {\n throw new Error(\"--agent-source flag cannot be empty\");\n }\n verbose(`Agents source from --agent-source flag: ${flagValue}`);\n return { agentsSource: flagValue, agentsSourceOrigin: \"flag\" };\n }\n\n const projectConfig = projectDir ? await loadProjectConfig(projectDir) : null;\n if (projectConfig?.agents_source) {\n verbose(\n `Agents source from project config: ${projectConfig.agents_source}`,\n );\n return {\n agentsSource: projectConfig.agents_source,\n agentsSourceOrigin: \"project\",\n };\n }\n\n const globalConfig = await loadGlobalConfig();\n if (globalConfig?.agents_source) {\n verbose(`Agents source from global config: ${globalConfig.agents_source}`);\n return {\n agentsSource: globalConfig.agents_source,\n agentsSourceOrigin: \"global\",\n };\n }\n\n verbose(\"Using default agents source (local CLI)\");\n return { agentsSource: undefined, agentsSourceOrigin: \"default\" };\n}\n\nexport function formatAgentsSourceOrigin(origin: AgentsSourceOrigin): string {\n switch (origin) {\n case \"flag\":\n return \"--agent-source flag\";\n case \"project\":\n return \"project config (.claude-collective/config.yaml)\";\n case \"global\":\n return \"global config (~/.claude-collective/config.yaml)\";\n case \"default\":\n return \"default (local CLI)\";\n }\n}\n\nexport function formatSourceOrigin(\n origin: ResolvedConfig[\"sourceOrigin\"],\n): string {\n switch (origin) {\n case \"flag\":\n return \"--source flag\";\n case \"env\":\n return `${SOURCE_ENV_VAR} environment variable`;\n case \"project\":\n return \"project config (.claude-collective/config.yaml)\";\n case \"global\":\n return \"global config (~/.claude-collective/config.yaml)\";\n case \"default\":\n return \"default\";\n }\n}\n\nexport function isLocalSource(source: string): boolean {\n if (source.startsWith(\"/\") || source.startsWith(\".\")) {\n return true;\n }\n\n const remoteProtocols = [\n \"github:\",\n \"gh:\",\n \"gitlab:\",\n \"bitbucket:\",\n \"sourcehut:\",\n \"https://\",\n \"http://\",\n ];\n\n const hasRemoteProtocol = remoteProtocols.some((prefix) =>\n source.startsWith(prefix),\n );\n\n if (!hasRemoteProtocol) {\n if (source.includes(\"..\") || source.includes(\"~\")) {\n throw new Error(\n `Invalid source path: ${source}. Path traversal patterns are not allowed.`,\n );\n }\n }\n\n return !hasRemoteProtocol;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli-v2/consts.ts"],"sourcesContent":["import path from \"path\";\nimport os from \"os\";\nimport { fileURLToPath } from \"url\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// After tsup build, dist/ is flat, so we go up one level from dist/ to get CLI root\n// In development (src/cli-v2/consts.ts), we go up two levels\nconst isInDist = __dirname.includes(\"/dist\");\nexport const CLI_ROOT = isInDist\n ? path.resolve(__dirname, \"..\")\n : path.resolve(__dirname, \"../..\");\nexport const PROJECT_ROOT = CLI_ROOT;\n\nexport const OUTPUT_DIR = \".claude\";\nexport const GITHUB_REPO = \"claude-collective/skills\";\nexport const DEFAULT_MATRIX_PATH = \"src/config/skills-matrix.yaml\";\n\nexport const PLUGIN_NAME = \"claude-collective\";\n\nexport const CLAUDE_DIR = \".claude\";\nexport const PLUGINS_SUBDIR = \"plugins\";\nexport const PLUGIN_MANIFEST_DIR = \".claude-plugin\";\nexport const PLUGIN_MANIFEST_FILE = \"plugin.json\";\n\nexport const CACHE_DIR = path.join(os.homedir(), \".cache\", \"claude-collective\");\n\nexport const SKILLS_MATRIX_PATH = \"config/skills-matrix.yaml\";\nexport const SKILLS_DIR_PATH = \"src/skills\";\nexport const LOCAL_SKILLS_PATH = \".claude/skills\";\n\nexport const DIRS = {\n agents: \"src/agents\",\n skills: \"src/skills\",\n stacks: \"src/stacks\",\n templates: \"src/agents/_templates\",\n commands: \"src/commands\",\n} as const;\n\nexport const DEFAULT_VERSION = \"1.0.0\";\n\n/** Uses \"0.0.0\" to clearly indicate \"no version was explicitly set\" */\nexport const DEFAULT_DISPLAY_VERSION = \"0.0.0\";\n\n/**\n * Skills that are preselected by default in the wizard.\n * These are foundational methodology skills that apply to all projects.\n */\nexport const DEFAULT_PRESELECTED_SKILLS = [\n \"meta/methodology/anti-over-engineering (@vince)\",\n \"meta/methodology/context-management (@vince)\",\n \"meta/methodology/improvement-protocol (@vince)\",\n \"meta/methodology/investigation-requirements (@vince)\",\n \"meta/methodology/success-criteria (@vince)\",\n \"meta/methodology/write-verification (@vince)\",\n] as const;\n"],"mappings":";;;;;;AAAA;AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,SAAS,qBAAqB;AAE9B,IAAM,aAAa,cAAc,YAAY,GAAG;AAChD,IAAM,YAAY,KAAK,QAAQ,UAAU;AAIzC,IAAM,WAAW,UAAU,SAAS,OAAO;AACpC,IAAM,WAAW,WACpB,KAAK,QAAQ,WAAW,IAAI,IAC5B,KAAK,QAAQ,WAAW,OAAO;AAC5B,IAAM,eAAe;AAQrB,IAAM,aAAa;AACnB,IAAM,iBAAiB;AACvB,IAAM,sBAAsB;AAC5B,IAAM,uBAAuB;AAE7B,IAAM,YAAY,KAAK,KAAK,GAAG,QAAQ,GAAG,UAAU,mBAAmB;AAEvE,IAAM,qBAAqB;AAC3B,IAAM,kBAAkB;AACxB,IAAM,oBAAoB;AAE1B,IAAM,OAAO;AAAA,EAClB,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,UAAU;AACZ;AAEO,IAAM,kBAAkB;AAGxB,IAAM,0BAA0B;AAMhC,IAAM,6BAA6B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli-v2/lib/exit-codes.ts","../src/cli-v2/base-command.ts"],"sourcesContent":["/**\n * CLI exit codes for standardized process termination.\n *\n * These follow Unix conventions where 0 = success, non-zero = error.\n * Use named constants instead of magic numbers for clarity and consistency.\n */\nexport const EXIT_CODES = {\n /** Operation completed successfully */\n SUCCESS: 0,\n /** General error - operation failed */\n ERROR: 1,\n /** Invalid command-line arguments or options */\n INVALID_ARGS: 2,\n /** Network request failed (connection, timeout, etc.) */\n NETWORK_ERROR: 3,\n /** User cancelled the operation (Ctrl+C or prompt cancel) */\n CANCELLED: 4,\n} as const;\n\nexport type ExitCode = (typeof EXIT_CODES)[keyof typeof EXIT_CODES];\n","/**\n * Base command class for all oclif commands in CLI v2.\n *\n * Provides:\n * - Shared flags (--dry-run, --source)\n * - Error handling with proper exit codes\n * - Common logging utilities\n * - Config access (set by init hook)\n */\nimport { Command, Flags } from \"@oclif/core\";\nimport { EXIT_CODES } from \"./lib/exit-codes.js\";\nimport type { ResolvedConfig } from \"./lib/config.js\";\n\nexport abstract class BaseCommand extends Command {\n /**\n * Base flags available to all commands.\n * Commands should merge these with their own flags using spread syntax.\n *\n * @example\n * static flags = {\n * ...BaseCommand.baseFlags,\n * myFlag: Flags.string({ description: \"My custom flag\" }),\n * };\n */\n static baseFlags = {\n \"dry-run\": Flags.boolean({\n description: \"Preview operations without executing\",\n default: false,\n }),\n source: Flags.string({\n char: \"s\",\n description: \"Skills source path or URL\",\n required: false,\n }),\n };\n\n /**\n * Loaded configuration - set by init hook and stored in config object.\n * Available after command initialization.\n */\n public get sourceConfig(): ResolvedConfig | undefined {\n return (this.config as any).sourceConfig;\n }\n\n /**\n * Handle errors with proper exit codes.\n * Logs the error message and exits the process.\n *\n * @param error - The error to handle\n * @returns Never returns (exits process)\n */\n protected handleError(error: unknown): never {\n const message = error instanceof Error ? error.message : String(error);\n this.error(message, { exit: EXIT_CODES.ERROR });\n }\n\n /**\n * Log a success message with a green checkmark.\n *\n * @param message - The success message to display\n */\n protected logSuccess(message: string): void {\n this.log(`✓ ${message}`);\n }\n\n /**\n * Log a warning message.\n * Uses oclif's warn method which outputs to stderr.\n *\n * @param message - The warning message to display\n */\n protected logWarning(message: string): void {\n this.warn(message);\n }\n\n /**\n * Log an informational message.\n *\n * @param message - The info message to display\n */\n protected logInfo(message: string): void {\n this.log(message);\n }\n}\n"],"mappings":";;;;;;AAAA;AAMO,IAAM,aAAa;AAAA;AAAA,EAExB,SAAS;AAAA;AAAA,EAET,OAAO;AAAA;AAAA,EAEP,cAAc;AAAA;AAAA,EAEd,eAAe;AAAA;AAAA,EAEf,WAAW;AACb;;;ACjBA;AASA,SAAS,SAAS,aAAa;AAIxB,IAAe,cAAf,cAAmC,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWhD,OAAO,YAAY;AAAA,IACjB,WAAW,MAAM,QAAQ;AAAA,MACvB,aAAa;AAAA,MACb,SAAS;AAAA,IACX,CAAC;AAAA,IACD,QAAQ,MAAM,OAAO;AAAA,MACnB,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAW,eAA2C;AACpD,WAAQ,KAAK,OAAe;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASU,YAAY,OAAuB;AAC3C,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,SAAK,MAAM,SAAS,EAAE,MAAM,WAAW,MAAM,CAAC;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOU,WAAW,SAAuB;AAC1C,SAAK,IAAI,UAAK,OAAO,EAAE;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQU,WAAW,SAAuB;AAC1C,SAAK,KAAK,OAAO;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOU,QAAQ,SAAuB;AACvC,SAAK,IAAI,OAAO;AAAA,EAClB;AACF;","names":[]}
@@ -1,245 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- getAvailableSkills,
4
- getDependentSkills,
5
- getSubcategories,
6
- isCategoryAllDisabled,
7
- resolveAlias
8
- } from "./chunk-AZP2AA5M.js";
9
- import {
10
- useWizardStore
11
- } from "./chunk-6OY6ZYQF.js";
12
- import {
13
- Confirm
14
- } from "./chunk-DHFFRMF6.js";
15
- import {
16
- init_esm_shims
17
- } from "./chunk-DHET7RCE.js";
18
-
19
- // src/cli-v2/components/wizard/step-subcategory.tsx
20
- init_esm_shims();
21
- import { useState } from "react";
22
- import { Box, Text } from "ink";
23
- import { Select } from "@inkjs/ui";
24
- import { jsx, jsxs } from "react/jsx-runtime";
25
- var BACK_VALUE = "__back__";
26
- function collectAllDependents(skillId, currentSelections, matrix) {
27
- const allDependents = [];
28
- const visited = /* @__PURE__ */ new Set();
29
- const queue = [skillId];
30
- while (queue.length > 0) {
31
- const current = queue.shift();
32
- if (visited.has(current)) continue;
33
- visited.add(current);
34
- const directDependents = getDependentSkills(
35
- current,
36
- currentSelections,
37
- matrix
38
- );
39
- for (const dependent of directDependents) {
40
- if (!visited.has(dependent) && !allDependents.includes(dependent)) {
41
- allDependents.push(dependent);
42
- queue.push(dependent);
43
- }
44
- }
45
- }
46
- return allDependents;
47
- }
48
- function formatSkillOption(skill) {
49
- let label = skill.name;
50
- if (skill.selected) {
51
- label = `\u2713 ${skill.name}`;
52
- } else if (skill.disabled) {
53
- const shortReason = skill.disabledReason?.split(" (")[0]?.toLowerCase() || "requirements not met";
54
- label = `${skill.name} (disabled, ${shortReason})`;
55
- } else if (skill.discouraged) {
56
- label = `${skill.name} (not recommended)`;
57
- } else if (skill.recommended) {
58
- label = `${skill.name} (recommended)`;
59
- }
60
- return {
61
- value: skill.id,
62
- label
63
- };
64
- }
65
- var StepSubcategory = ({ matrix }) => {
66
- const {
67
- currentTopCategory,
68
- currentSubcategory,
69
- selectedSkills,
70
- expertMode,
71
- setStep,
72
- setSubcategory,
73
- toggleSkill,
74
- goBack,
75
- setLastSelectedSubcategory,
76
- setLastSelectedSkill,
77
- markCategoryVisited
78
- } = useWizardStore();
79
- const [viewMode, setViewMode] = useState(
80
- currentSubcategory ? "skill" : "subcategory"
81
- );
82
- const [confirmDeselect, setConfirmDeselect] = useState(null);
83
- if (!currentTopCategory) {
84
- goBack();
85
- return null;
86
- }
87
- const topCategory = matrix.categories[currentTopCategory];
88
- const subcategoryIds = getSubcategories(currentTopCategory, matrix);
89
- const checkOptions = { expertMode };
90
- if (viewMode === "subcategory") {
91
- const subcategoryOptions = subcategoryIds.map((subId) => {
92
- const sub = matrix.categories[subId];
93
- const skills2 = getAvailableSkills(
94
- subId,
95
- selectedSkills,
96
- matrix,
97
- checkOptions
98
- );
99
- const selectedInCategory = skills2.filter((s) => s.selected);
100
- const hasSelection = selectedInCategory.length > 0;
101
- const categoryDisabled = isCategoryAllDisabled(
102
- subId,
103
- selectedSkills,
104
- matrix,
105
- checkOptions
106
- );
107
- let label;
108
- if (hasSelection) {
109
- label = `${sub.name} (${selectedInCategory[0].name} selected)`;
110
- } else if (categoryDisabled.disabled) {
111
- const shortReason = categoryDisabled.reason?.toLowerCase() || "requirements not met";
112
- label = `${sub.name} (disabled, ${shortReason})`;
113
- } else if (sub.required) {
114
- label = `${sub.name} (required)`;
115
- } else {
116
- label = sub.name;
117
- }
118
- return {
119
- value: subId,
120
- label
121
- };
122
- });
123
- const options2 = [
124
- { value: BACK_VALUE, label: "\u2190 Back" },
125
- ...subcategoryOptions
126
- ];
127
- const handleSubcategorySelect = (value) => {
128
- if (value === BACK_VALUE) {
129
- if (currentTopCategory) {
130
- markCategoryVisited(currentTopCategory);
131
- }
132
- setSubcategory(null);
133
- setLastSelectedSubcategory(null);
134
- goBack();
135
- return;
136
- }
137
- setLastSelectedSubcategory(value);
138
- setSubcategory(value);
139
- setViewMode("skill");
140
- };
141
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
142
- /* @__PURE__ */ jsxs(Text, { bold: true, children: [
143
- topCategory.name,
144
- " - Select a subcategory:"
145
- ] }),
146
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Select, { options: options2, onChange: handleSubcategorySelect }) })
147
- ] });
148
- }
149
- if (!currentSubcategory) {
150
- setViewMode("subcategory");
151
- return null;
152
- }
153
- const subcategory = matrix.categories[currentSubcategory];
154
- const skills = getAvailableSkills(
155
- currentSubcategory,
156
- selectedSkills,
157
- matrix,
158
- checkOptions
159
- );
160
- const skillOptions = skills.map(formatSkillOption);
161
- const options = [{ value: BACK_VALUE, label: "\u2190 Back" }, ...skillOptions];
162
- const handleSkillSelect = (value) => {
163
- if (value === BACK_VALUE) {
164
- setSubcategory(null);
165
- setLastSelectedSkill(null);
166
- setViewMode("subcategory");
167
- return;
168
- }
169
- const selectedSkillId = value;
170
- setLastSelectedSkill(selectedSkillId);
171
- const selectedOption = skills.find((s) => s.id === selectedSkillId);
172
- if (selectedOption?.disabled) {
173
- return;
174
- }
175
- const alreadySelected = selectedSkills.includes(selectedSkillId);
176
- if (alreadySelected) {
177
- const allDependents = collectAllDependents(
178
- selectedSkillId,
179
- selectedSkills,
180
- matrix
181
- );
182
- if (allDependents.length > 0) {
183
- const skillName = matrix.skills[resolveAlias(selectedSkillId, matrix)]?.name || selectedSkillId;
184
- setConfirmDeselect({
185
- skillId: selectedSkillId,
186
- skillName,
187
- dependents: allDependents
188
- });
189
- return;
190
- }
191
- toggleSkill(selectedSkillId);
192
- } else {
193
- if (subcategory?.exclusive) {
194
- const skillsToRemove = selectedSkills.filter((id) => {
195
- const skill = matrix.skills[id];
196
- return skill?.category === currentSubcategory;
197
- });
198
- skillsToRemove.forEach((id) => {
199
- if (selectedSkills.includes(id)) {
200
- toggleSkill(id);
201
- }
202
- });
203
- }
204
- toggleSkill(selectedSkillId);
205
- }
206
- };
207
- if (confirmDeselect) {
208
- const dependentNames = confirmDeselect.dependents.map((id) => matrix.skills[id]?.name || id).join(", ");
209
- const message = `Deselecting ${confirmDeselect.skillName} will also remove: ${dependentNames}. Continue?`;
210
- return /* @__PURE__ */ jsx(
211
- Confirm,
212
- {
213
- message,
214
- defaultValue: false,
215
- onConfirm: () => {
216
- const toRemove = /* @__PURE__ */ new Set([
217
- confirmDeselect.skillId,
218
- ...confirmDeselect.dependents
219
- ]);
220
- toRemove.forEach((id) => {
221
- if (selectedSkills.includes(id)) {
222
- toggleSkill(id);
223
- }
224
- });
225
- setConfirmDeselect(null);
226
- },
227
- onCancel: () => {
228
- setConfirmDeselect(null);
229
- }
230
- }
231
- );
232
- }
233
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
234
- /* @__PURE__ */ jsxs(Text, { bold: true, children: [
235
- subcategory.name,
236
- ":"
237
- ] }),
238
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Select, { options, onChange: handleSkillSelect }) })
239
- ] });
240
- };
241
-
242
- export {
243
- StepSubcategory
244
- };
245
- //# sourceMappingURL=chunk-TD643KB3.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli-v2/components/wizard/step-subcategory.tsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport { Box, Text } from \"ink\";\nimport { Select } from \"@inkjs/ui\";\nimport { useWizardStore } from \"../../stores/wizard-store.js\";\nimport {\n getSubcategories,\n getAvailableSkills,\n isCategoryAllDisabled,\n getDependentSkills,\n resolveAlias,\n type SkillCheckOptions,\n} from \"../../lib/matrix-resolver.js\";\nimport type { MergedSkillsMatrix } from \"../../types-matrix.js\";\nimport { Confirm } from \"../common/confirm.js\";\n\nconst BACK_VALUE = \"__back__\";\n\ntype ViewMode = \"subcategory\" | \"skill\";\n\ninterface StepSubcategoryProps {\n matrix: MergedSkillsMatrix;\n}\n\n/**\n * Collects all dependent skills recursively for a given skill ID.\n * Used when deselecting a skill to warn about cascade deselection.\n */\nfunction collectAllDependents(\n skillId: string,\n currentSelections: string[],\n matrix: MergedSkillsMatrix,\n): string[] {\n const allDependents: string[] = [];\n const visited = new Set<string>();\n const queue = [skillId];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n if (visited.has(current)) continue;\n visited.add(current);\n\n const directDependents = getDependentSkills(\n current,\n currentSelections,\n matrix,\n );\n\n for (const dependent of directDependents) {\n if (!visited.has(dependent) && !allDependents.includes(dependent)) {\n allDependents.push(dependent);\n queue.push(dependent);\n }\n }\n }\n\n return allDependents;\n}\n\n/**\n * Formats a skill option for display in the Select component.\n */\nfunction formatSkillOption(skill: {\n id: string;\n name: string;\n description?: string;\n selected: boolean;\n disabled: boolean;\n disabledReason?: string;\n discouraged?: boolean;\n recommended?: boolean;\n}): {\n value: string;\n label: string;\n} {\n let label = skill.name;\n\n if (skill.selected) {\n label = `✓ ${skill.name}`;\n } else if (skill.disabled) {\n const shortReason =\n skill.disabledReason?.split(\" (\")[0]?.toLowerCase() ||\n \"requirements not met\";\n label = `${skill.name} (disabled, ${shortReason})`;\n } else if (skill.discouraged) {\n label = `${skill.name} (not recommended)`;\n } else if (skill.recommended) {\n label = `${skill.name} (recommended)`;\n }\n\n return {\n value: skill.id,\n label,\n };\n}\n\nexport const StepSubcategory: React.FC<StepSubcategoryProps> = ({ matrix }) => {\n const {\n currentTopCategory,\n currentSubcategory,\n selectedSkills,\n expertMode,\n setStep,\n setSubcategory,\n toggleSkill,\n goBack,\n setLastSelectedSubcategory,\n setLastSelectedSkill,\n markCategoryVisited,\n } = useWizardStore();\n\n const [viewMode, setViewMode] = useState<ViewMode>(\n currentSubcategory ? \"skill\" : \"subcategory\",\n );\n const [confirmDeselect, setConfirmDeselect] = useState<{\n skillId: string;\n skillName: string;\n dependents: string[];\n } | null>(null);\n\n if (!currentTopCategory) {\n // Should not happen, but handle gracefully\n goBack();\n return null;\n }\n\n const topCategory = matrix.categories[currentTopCategory];\n const subcategoryIds = getSubcategories(currentTopCategory, matrix);\n const checkOptions: SkillCheckOptions = { expertMode };\n\n // Handle subcategory selection view\n if (viewMode === \"subcategory\") {\n const subcategoryOptions = subcategoryIds.map((subId) => {\n const sub = matrix.categories[subId];\n const skills = getAvailableSkills(\n subId,\n selectedSkills,\n matrix,\n checkOptions,\n );\n const selectedInCategory = skills.filter((s) => s.selected);\n const hasSelection = selectedInCategory.length > 0;\n\n const categoryDisabled = isCategoryAllDisabled(\n subId,\n selectedSkills,\n matrix,\n checkOptions,\n );\n\n let label: string;\n if (hasSelection) {\n label = `${sub.name} (${selectedInCategory[0].name} selected)`;\n } else if (categoryDisabled.disabled) {\n const shortReason =\n categoryDisabled.reason?.toLowerCase() || \"requirements not met\";\n label = `${sub.name} (disabled, ${shortReason})`;\n } else if (sub.required) {\n label = `${sub.name} (required)`;\n } else {\n label = sub.name;\n }\n\n return {\n value: subId,\n label,\n };\n });\n\n const options = [\n { value: BACK_VALUE, label: \"← Back\" },\n ...subcategoryOptions,\n ];\n\n const handleSubcategorySelect = (value: string) => {\n if (value === BACK_VALUE) {\n if (currentTopCategory) {\n markCategoryVisited(currentTopCategory);\n }\n setSubcategory(null);\n setLastSelectedSubcategory(null);\n goBack();\n return;\n }\n\n setLastSelectedSubcategory(value);\n setSubcategory(value);\n setViewMode(\"skill\");\n };\n\n return (\n <Box flexDirection=\"column\">\n <Text bold>{topCategory.name} - Select a subcategory:</Text>\n <Box marginTop={1}>\n <Select options={options} onChange={handleSubcategorySelect} />\n </Box>\n </Box>\n );\n }\n\n // Handle skill selection view (when a subcategory is selected)\n if (!currentSubcategory) {\n // Should not happen in skill mode\n setViewMode(\"subcategory\");\n return null;\n }\n\n const subcategory = matrix.categories[currentSubcategory];\n const skills = getAvailableSkills(\n currentSubcategory,\n selectedSkills,\n matrix,\n checkOptions,\n );\n\n const skillOptions = skills.map(formatSkillOption);\n const options = [{ value: BACK_VALUE, label: \"← Back\" }, ...skillOptions];\n\n const handleSkillSelect = (value: string) => {\n if (value === BACK_VALUE) {\n setSubcategory(null);\n setLastSelectedSkill(null);\n setViewMode(\"subcategory\");\n return;\n }\n\n const selectedSkillId = value;\n setLastSelectedSkill(selectedSkillId);\n\n const selectedOption = skills.find((s) => s.id === selectedSkillId);\n\n // Ignore disabled skills\n if (selectedOption?.disabled) {\n return;\n }\n\n const alreadySelected = selectedSkills.includes(selectedSkillId);\n\n if (alreadySelected) {\n // Check if deselecting would affect dependents\n const allDependents = collectAllDependents(\n selectedSkillId,\n selectedSkills,\n matrix,\n );\n\n if (allDependents.length > 0) {\n const skillName =\n matrix.skills[resolveAlias(selectedSkillId, matrix)]?.name ||\n selectedSkillId;\n\n // Show confirmation dialog\n setConfirmDeselect({\n skillId: selectedSkillId,\n skillName,\n dependents: allDependents,\n });\n return;\n }\n\n // No dependents, just toggle off\n toggleSkill(selectedSkillId);\n } else {\n // Selecting a skill - handle exclusive categories\n if (subcategory?.exclusive) {\n // First, remove all skills in this category\n const skillsToRemove = selectedSkills.filter((id) => {\n const skill = matrix.skills[id];\n return skill?.category === currentSubcategory;\n });\n skillsToRemove.forEach((id) => {\n if (selectedSkills.includes(id)) {\n toggleSkill(id);\n }\n });\n }\n\n // Then add the new skill\n toggleSkill(selectedSkillId);\n }\n };\n\n // Handle deselection confirmation\n if (confirmDeselect) {\n const dependentNames = confirmDeselect.dependents\n .map((id) => matrix.skills[id]?.name || id)\n .join(\", \");\n\n const message = `Deselecting ${confirmDeselect.skillName} will also remove: ${dependentNames}. Continue?`;\n\n return (\n <Confirm\n message={message}\n defaultValue={false}\n onConfirm={() => {\n // Remove the skill and all dependents\n const toRemove = new Set([\n confirmDeselect.skillId,\n ...confirmDeselect.dependents,\n ]);\n toRemove.forEach((id) => {\n if (selectedSkills.includes(id)) {\n toggleSkill(id);\n }\n });\n setConfirmDeselect(null);\n }}\n onCancel={() => {\n // Just close the confirm dialog\n setConfirmDeselect(null);\n }}\n />\n );\n }\n\n return (\n <Box flexDirection=\"column\">\n <Text bold>{subcategory.name}:</Text>\n <Box marginTop={1}>\n <Select options={options} onChange={handleSkillSelect} />\n </Box>\n </Box>\n );\n};\n"],"mappings":"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","names":["skills","options"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli-v2/lib/plugin-info.ts"],"sourcesContent":["import { readdir } from \"fs/promises\";\nimport {\n getCollectivePluginDir,\n getPluginSkillsDir,\n getPluginAgentsDir,\n readPluginManifest,\n} from \"./plugin-finder\";\nimport { directoryExists } from \"../utils/fs\";\nimport { DEFAULT_DISPLAY_VERSION } from \"../consts\";\nimport { detectInstallation, type InstallMode } from \"./installation\";\nimport { loadProjectConfig } from \"./project-config\";\n\nconst DEFAULT_NAME = \"claude-collective\";\n\nexport interface PluginInfo {\n name: string;\n version: string;\n skillCount: number;\n agentCount: number;\n path: string;\n}\n\nexport interface InstallationInfo {\n mode: InstallMode;\n name: string;\n version: string;\n skillCount: number;\n agentCount: number;\n configPath: string;\n agentsDir: string;\n skillsDir: string;\n}\n\nexport async function getPluginInfo(): Promise<PluginInfo | null> {\n const pluginDir = getCollectivePluginDir();\n\n if (!(await directoryExists(pluginDir))) {\n return null;\n }\n\n const manifest = await readPluginManifest(pluginDir);\n if (!manifest) {\n return null;\n }\n\n const skillsDir = getPluginSkillsDir(pluginDir);\n const agentsDir = getPluginAgentsDir(pluginDir);\n\n let skillCount = 0;\n let agentCount = 0;\n\n if (await directoryExists(skillsDir)) {\n const skills = await readdir(skillsDir, { withFileTypes: true });\n skillCount = skills.filter((s) => s.isDirectory()).length;\n }\n\n if (await directoryExists(agentsDir)) {\n const agents = await readdir(agentsDir, { withFileTypes: true });\n agentCount = agents.filter(\n (a) => a.isFile() && a.name.endsWith(\".md\"),\n ).length;\n }\n\n return {\n name: manifest.name || DEFAULT_NAME,\n version: manifest.version || DEFAULT_DISPLAY_VERSION,\n skillCount,\n agentCount,\n path: pluginDir,\n };\n}\n\nexport function formatPluginDisplay(info: PluginInfo): string {\n return `Plugin: ${info.name} v${info.version}\n Skills: ${info.skillCount}\n Agents: ${info.agentCount}\n Path: ${info.path}`;\n}\n\n/**\n * Get installation info for either local or plugin mode.\n * Auto-detects the installation mode and returns unified info.\n */\nexport async function getInstallationInfo(): Promise<InstallationInfo | null> {\n const installation = await detectInstallation();\n\n if (!installation) {\n return null;\n }\n\n let skillCount = 0;\n let agentCount = 0;\n let name = DEFAULT_NAME;\n let version = DEFAULT_DISPLAY_VERSION;\n\n // Count skills\n if (await directoryExists(installation.skillsDir)) {\n try {\n const skills = await readdir(installation.skillsDir, {\n withFileTypes: true,\n });\n skillCount = skills.filter((s) => s.isDirectory()).length;\n } catch {\n // Ignore errors\n }\n }\n\n // Count agents\n if (await directoryExists(installation.agentsDir)) {\n try {\n const agents = await readdir(installation.agentsDir, {\n withFileTypes: true,\n });\n agentCount = agents.filter(\n (a) => a.isFile() && a.name.endsWith(\".md\"),\n ).length;\n } catch {\n // Ignore errors\n }\n }\n\n // Get name/version from config or manifest depending on mode\n if (installation.mode === \"local\") {\n const loaded = await loadProjectConfig(installation.projectDir);\n if (loaded?.config) {\n name = loaded.config.name || DEFAULT_NAME;\n // Local mode doesn't have version in the same way\n version = \"local\";\n }\n } else {\n // Plugin mode - read from manifest\n const pluginDir = getCollectivePluginDir(installation.projectDir);\n const manifest = await readPluginManifest(pluginDir);\n if (manifest) {\n name = manifest.name || DEFAULT_NAME;\n version = manifest.version || DEFAULT_DISPLAY_VERSION;\n }\n }\n\n return {\n mode: installation.mode,\n name,\n version,\n skillCount,\n agentCount,\n configPath: installation.configPath,\n agentsDir: installation.agentsDir,\n skillsDir: installation.skillsDir,\n };\n}\n\nexport function formatInstallationDisplay(info: InstallationInfo): string {\n const modeLabel = info.mode === \"local\" ? \"Local\" : \"Plugin\";\n const versionDisplay =\n info.mode === \"local\" ? \"(local mode)\" : `v${info.version}`;\n\n return `Installation: ${info.name} ${versionDisplay}\n Mode: ${modeLabel}\n Skills: ${info.skillCount}\n Agents: ${info.agentCount}\n Config: ${info.configPath}\n Agents: ${info.agentsDir}`;\n}\n"],"mappings":"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","names":[]}