@agents-inc/cli 0.47.0 → 0.50.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 (261) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +113 -118
  3. package/config/skill-categories.yaml +344 -0
  4. package/config/skill-rules.yaml +740 -0
  5. package/config/stacks.yaml +0 -1
  6. package/dist/{chunk-CJFWO46A.js → chunk-26MXZUHU.js} +2 -2
  7. package/dist/{chunk-KQ27IDYL.js → chunk-2BWCR762.js} +2 -3
  8. package/dist/chunk-2BWCR762.js.map +1 -0
  9. package/dist/chunk-3VOL4WEG.js +31 -0
  10. package/dist/chunk-3VOL4WEG.js.map +1 -0
  11. package/dist/chunk-4QWDB2MD.js +571 -0
  12. package/dist/chunk-4QWDB2MD.js.map +1 -0
  13. package/dist/{chunk-LJ5E4GXC.js → chunk-4R52TQ3K.js} +2 -2
  14. package/dist/{chunk-SPSGZWTZ.js → chunk-5FCHJLM7.js} +34 -16
  15. package/dist/chunk-5FCHJLM7.js.map +1 -0
  16. package/dist/chunk-5FPIKTSA.js +40 -0
  17. package/dist/chunk-5FPIKTSA.js.map +1 -0
  18. package/dist/{chunk-HLTJK3XB.js → chunk-5L724R4C.js} +5 -6
  19. package/dist/{chunk-HLTJK3XB.js.map → chunk-5L724R4C.js.map} +1 -1
  20. package/dist/{chunk-JTTTXGHX.js → chunk-7LV4V6A4.js} +4 -4
  21. package/dist/{chunk-FTD5Z6QD.js → chunk-AWP5A6IM.js} +15 -7
  22. package/dist/chunk-AWP5A6IM.js.map +1 -0
  23. package/dist/chunk-C3Q43WLC.js +118 -0
  24. package/dist/chunk-C3Q43WLC.js.map +1 -0
  25. package/dist/{chunk-B4QYXVPZ.js → chunk-CMNKHDOX.js} +2 -2
  26. package/dist/{chunk-TBB3THSL.js → chunk-D72AFYQR.js} +8 -13
  27. package/dist/chunk-D72AFYQR.js.map +1 -0
  28. package/dist/{chunk-QR2TM4OY.js → chunk-DCE423KO.js} +46 -30
  29. package/dist/chunk-DCE423KO.js.map +1 -0
  30. package/dist/chunk-GBOW6FUW.js +74 -0
  31. package/dist/chunk-GBOW6FUW.js.map +1 -0
  32. package/dist/{chunk-GFDGYQ6M.js → chunk-HMSHB5EQ.js} +569 -269
  33. package/dist/chunk-HMSHB5EQ.js.map +1 -0
  34. package/dist/{chunk-NRCKIHND.js → chunk-HYEUETIC.js} +2 -2
  35. package/dist/{chunk-OEJDFGAF.js → chunk-I6IOGZSZ.js} +61 -52
  36. package/dist/chunk-I6IOGZSZ.js.map +1 -0
  37. package/dist/{chunk-3APMMQUA.js → chunk-JFF7P4LC.js} +30 -93
  38. package/dist/chunk-JFF7P4LC.js.map +1 -0
  39. package/dist/{chunk-N5OCAAXY.js → chunk-JWYRXE6C.js} +2 -2
  40. package/dist/{chunk-TWDVLTU6.js → chunk-KAO3LKB5.js} +3 -3
  41. package/dist/{chunk-XTRPYUWK.js → chunk-KQOU4POU.js} +26 -28
  42. package/dist/chunk-KQOU4POU.js.map +1 -0
  43. package/dist/{chunk-YTRFL3MR.js → chunk-M3GQ2R3E.js} +29 -28
  44. package/dist/chunk-M3GQ2R3E.js.map +1 -0
  45. package/dist/{chunk-LNA6M2IE.js → chunk-PGY5XROM.js} +2 -2
  46. package/dist/chunk-PGY5XROM.js.map +1 -0
  47. package/dist/{chunk-VAQJLHUW.js → chunk-QB5HHTAA.js} +8 -19
  48. package/dist/chunk-QB5HHTAA.js.map +1 -0
  49. package/dist/{chunk-VSZ5GDET.js → chunk-QYLCINGC.js} +2 -2
  50. package/dist/{chunk-IRJADQM7.js → chunk-RA2IPRO2.js} +2 -2
  51. package/dist/{chunk-DC333ZDC.js → chunk-RDWGYKDY.js} +4 -4
  52. package/dist/{chunk-FXQYEXLS.js → chunk-RFKDGJAJ.js} +26 -57
  53. package/dist/chunk-RFKDGJAJ.js.map +1 -0
  54. package/dist/{chunk-HPJP3HFD.js → chunk-SPFHPHYL.js} +7 -7
  55. package/dist/{chunk-EZ46ZTAQ.js → chunk-U2W5SENM.js} +3 -3
  56. package/dist/{chunk-MVEYK55V.js → chunk-U7X4V4HE.js} +2 -2
  57. package/dist/{chunk-INKJBMPJ.js → chunk-UAD3SC27.js} +4 -12
  58. package/dist/chunk-UAD3SC27.js.map +1 -0
  59. package/dist/{chunk-C4QI54PN.js → chunk-WBHPCBVN.js} +57 -34
  60. package/dist/chunk-WBHPCBVN.js.map +1 -0
  61. package/dist/chunk-WFFV254H.js +314 -0
  62. package/dist/chunk-WFFV254H.js.map +1 -0
  63. package/dist/{chunk-ZWAL2ZY7.js → chunk-WJHFV6RI.js} +3 -2
  64. package/dist/chunk-WJHFV6RI.js.map +1 -0
  65. package/dist/{chunk-2RQYJFKA.js → chunk-WLZHCM7O.js} +2 -2
  66. package/dist/{chunk-DVW6ASTO.js → chunk-XDSVV5GZ.js} +4 -4
  67. package/dist/{chunk-JZHIF3K7.js → chunk-YDYRAXSY.js} +57 -27
  68. package/dist/chunk-YDYRAXSY.js.map +1 -0
  69. package/dist/commands/build/marketplace.js +4 -4
  70. package/dist/commands/build/plugins.js +5 -5
  71. package/dist/commands/build/stack.js +5 -5
  72. package/dist/commands/compile.js +11 -9
  73. package/dist/commands/compile.js.map +1 -1
  74. package/dist/commands/config/get.js +4 -4
  75. package/dist/commands/config/index.js +5 -5
  76. package/dist/commands/config/path.js +4 -4
  77. package/dist/commands/config/set-project.js +4 -4
  78. package/dist/commands/config/show.js +5 -5
  79. package/dist/commands/config/unset-project.js +4 -4
  80. package/dist/commands/diff.js +4 -4
  81. package/dist/commands/doctor.js +4 -4
  82. package/dist/commands/edit.js +58 -52
  83. package/dist/commands/edit.js.map +1 -1
  84. package/dist/commands/eject.js +4 -4
  85. package/dist/commands/import/skill.js +7 -8
  86. package/dist/commands/import/skill.js.map +1 -1
  87. package/dist/commands/info.js +5 -5
  88. package/dist/commands/init.js +40 -435
  89. package/dist/commands/init.js.map +1 -1
  90. package/dist/commands/list.js +4 -4
  91. package/dist/commands/new/agent.js +11 -10
  92. package/dist/commands/new/agent.js.map +1 -1
  93. package/dist/commands/new/marketplace.js +24 -5
  94. package/dist/commands/new/marketplace.js.map +1 -1
  95. package/dist/commands/new/skill.js +15 -209
  96. package/dist/commands/new/skill.js.map +1 -1
  97. package/dist/commands/outdated.js +11 -7
  98. package/dist/commands/outdated.js.map +1 -1
  99. package/dist/commands/search.js +7 -8
  100. package/dist/commands/search.js.map +1 -1
  101. package/dist/commands/uninstall.js +6 -6
  102. package/dist/commands/update.js +6 -6
  103. package/dist/commands/validate.js +62 -250
  104. package/dist/commands/validate.js.map +1 -1
  105. package/dist/components/skill-search/skill-search.js +3 -3
  106. package/dist/components/wizard/category-grid.js +3 -2
  107. package/dist/components/wizard/category-grid.test.js +112 -58
  108. package/dist/components/wizard/category-grid.test.js.map +1 -1
  109. package/dist/components/wizard/checkbox-grid.js +5 -3
  110. package/dist/components/wizard/checkbox-grid.test.js +5 -4
  111. package/dist/components/wizard/checkbox-grid.test.js.map +1 -1
  112. package/dist/components/wizard/domain-selection.js +11 -9
  113. package/dist/components/wizard/help-modal.js +2 -2
  114. package/dist/components/wizard/menu-item.js +1 -1
  115. package/dist/components/wizard/search-modal.js +2 -2
  116. package/dist/components/wizard/search-modal.test.js +2 -2
  117. package/dist/components/wizard/section-progress.js +2 -2
  118. package/dist/components/wizard/section-progress.test.js +2 -2
  119. package/dist/components/wizard/selection-card.js +2 -2
  120. package/dist/components/wizard/source-grid.js +4 -3
  121. package/dist/components/wizard/source-grid.test.js +4 -3
  122. package/dist/components/wizard/source-grid.test.js.map +1 -1
  123. package/dist/components/wizard/stack-selection.js +8 -8
  124. package/dist/components/wizard/step-agents.js +10 -8
  125. package/dist/components/wizard/step-agents.test.js +18 -17
  126. package/dist/components/wizard/step-agents.test.js.map +1 -1
  127. package/dist/components/wizard/step-build.js +9 -8
  128. package/dist/components/wizard/step-build.test.js +17 -35
  129. package/dist/components/wizard/step-build.test.js.map +1 -1
  130. package/dist/components/wizard/step-confirm.js +4 -4
  131. package/dist/components/wizard/step-confirm.test.js +8 -8
  132. package/dist/components/wizard/step-refine.js +2 -2
  133. package/dist/components/wizard/step-refine.test.js +2 -2
  134. package/dist/components/wizard/step-settings.js +7 -5
  135. package/dist/components/wizard/step-settings.test.js +10 -8
  136. package/dist/components/wizard/step-settings.test.js.map +1 -1
  137. package/dist/components/wizard/step-sources.js +11 -10
  138. package/dist/components/wizard/step-sources.test.js +12 -11
  139. package/dist/components/wizard/step-sources.test.js.map +1 -1
  140. package/dist/components/wizard/step-stack.js +15 -12
  141. package/dist/components/wizard/step-stack.test.js +16 -13
  142. package/dist/components/wizard/step-stack.test.js.map +1 -1
  143. package/dist/components/wizard/view-title.js +2 -2
  144. package/dist/components/wizard/wizard-layout.js +8 -8
  145. package/dist/components/wizard/wizard-tabs.js +2 -2
  146. package/dist/components/wizard/wizard-tabs.test.js +2 -2
  147. package/dist/components/wizard/wizard.js +27 -25
  148. package/dist/config/skill-categories.yaml +344 -0
  149. package/dist/config/skill-rules.yaml +740 -0
  150. package/dist/config/stacks.yaml +0 -1
  151. package/dist/hooks/init.js +55 -3
  152. package/dist/hooks/init.js.map +1 -1
  153. package/dist/{source-manager-Q34LTUVM.js → source-manager-BVB2SG73.js} +4 -4
  154. package/dist/src/agents/meta/agent-summoner/critical-reminders.md +1 -1
  155. package/dist/src/agents/meta/agent-summoner/critical-requirements.md +1 -1
  156. package/dist/src/agents/meta/agent-summoner/examples.md +2 -2
  157. package/dist/src/agents/meta/agent-summoner/output-format.md +1 -1
  158. package/dist/src/agents/meta/agent-summoner/workflow.md +5 -7
  159. package/{src/agents/meta/documentor/agent.yaml → dist/src/agents/meta/documentor/metadata.yaml} +1 -0
  160. package/dist/stores/wizard-store.js +5 -5
  161. package/dist/stores/wizard-store.test.js +79 -27
  162. package/dist/stores/wizard-store.test.js.map +1 -1
  163. package/package.json +5 -1
  164. package/src/agents/meta/agent-summoner/critical-reminders.md +1 -1
  165. package/src/agents/meta/agent-summoner/critical-requirements.md +1 -1
  166. package/src/agents/meta/agent-summoner/examples.md +2 -2
  167. package/src/agents/meta/agent-summoner/output-format.md +1 -1
  168. package/src/agents/meta/agent-summoner/workflow.md +5 -7
  169. package/{dist/src/agents/meta/documentor/agent.yaml → src/agents/meta/documentor/metadata.yaml} +1 -0
  170. package/src/schemas/agent.schema.json +1 -1
  171. package/src/schemas/metadata.schema.json +2 -5
  172. package/src/schemas/project-config.schema.json +0 -3
  173. package/src/schemas/project-source-config.schema.json +4 -1
  174. package/config/skills-matrix.yaml +0 -918
  175. package/dist/chunk-3APMMQUA.js.map +0 -1
  176. package/dist/chunk-C4QI54PN.js.map +0 -1
  177. package/dist/chunk-FMQ3A7W4.js +0 -29
  178. package/dist/chunk-FMQ3A7W4.js.map +0 -1
  179. package/dist/chunk-FTD5Z6QD.js.map +0 -1
  180. package/dist/chunk-FXQYEXLS.js.map +0 -1
  181. package/dist/chunk-GFDGYQ6M.js.map +0 -1
  182. package/dist/chunk-INKJBMPJ.js.map +0 -1
  183. package/dist/chunk-JZHIF3K7.js.map +0 -1
  184. package/dist/chunk-KQ27IDYL.js.map +0 -1
  185. package/dist/chunk-LNA6M2IE.js.map +0 -1
  186. package/dist/chunk-M4P5YJ45.js +0 -99
  187. package/dist/chunk-M4P5YJ45.js.map +0 -1
  188. package/dist/chunk-OEJDFGAF.js.map +0 -1
  189. package/dist/chunk-QR2TM4OY.js.map +0 -1
  190. package/dist/chunk-SPSGZWTZ.js.map +0 -1
  191. package/dist/chunk-TBB3THSL.js.map +0 -1
  192. package/dist/chunk-VAQJLHUW.js.map +0 -1
  193. package/dist/chunk-XTRPYUWK.js.map +0 -1
  194. package/dist/chunk-YTRFL3MR.js.map +0 -1
  195. package/dist/chunk-ZWAL2ZY7.js.map +0 -1
  196. package/dist/config/skills-matrix.yaml +0 -918
  197. package/dist/src/agents/migration/cli-migrator/agent.yaml +0 -12
  198. package/dist/src/agents/migration/cli-migrator/anti-patterns.md +0 -158
  199. package/dist/src/agents/migration/cli-migrator/conversion-mappings.md +0 -63
  200. package/dist/src/agents/migration/cli-migrator/critical-reminders.md +0 -17
  201. package/dist/src/agents/migration/cli-migrator/critical-requirements.md +0 -13
  202. package/dist/src/agents/migration/cli-migrator/intro.md +0 -15
  203. package/dist/src/agents/migration/cli-migrator/output-format.md +0 -164
  204. package/dist/src/agents/migration/cli-migrator/workflow.md +0 -230
  205. package/src/agents/migration/cli-migrator/agent.yaml +0 -12
  206. package/src/agents/migration/cli-migrator/anti-patterns.md +0 -158
  207. package/src/agents/migration/cli-migrator/conversion-mappings.md +0 -63
  208. package/src/agents/migration/cli-migrator/critical-reminders.md +0 -17
  209. package/src/agents/migration/cli-migrator/critical-requirements.md +0 -13
  210. package/src/agents/migration/cli-migrator/intro.md +0 -15
  211. package/src/agents/migration/cli-migrator/output-format.md +0 -164
  212. package/src/agents/migration/cli-migrator/workflow.md +0 -230
  213. package/src/schemas/skills-matrix.schema.json +0 -179
  214. /package/dist/{chunk-CJFWO46A.js.map → chunk-26MXZUHU.js.map} +0 -0
  215. /package/dist/{chunk-LJ5E4GXC.js.map → chunk-4R52TQ3K.js.map} +0 -0
  216. /package/dist/{chunk-JTTTXGHX.js.map → chunk-7LV4V6A4.js.map} +0 -0
  217. /package/dist/{chunk-B4QYXVPZ.js.map → chunk-CMNKHDOX.js.map} +0 -0
  218. /package/dist/{chunk-NRCKIHND.js.map → chunk-HYEUETIC.js.map} +0 -0
  219. /package/dist/{chunk-N5OCAAXY.js.map → chunk-JWYRXE6C.js.map} +0 -0
  220. /package/dist/{chunk-TWDVLTU6.js.map → chunk-KAO3LKB5.js.map} +0 -0
  221. /package/dist/{chunk-VSZ5GDET.js.map → chunk-QYLCINGC.js.map} +0 -0
  222. /package/dist/{chunk-IRJADQM7.js.map → chunk-RA2IPRO2.js.map} +0 -0
  223. /package/dist/{chunk-DC333ZDC.js.map → chunk-RDWGYKDY.js.map} +0 -0
  224. /package/dist/{chunk-HPJP3HFD.js.map → chunk-SPFHPHYL.js.map} +0 -0
  225. /package/dist/{chunk-EZ46ZTAQ.js.map → chunk-U2W5SENM.js.map} +0 -0
  226. /package/dist/{chunk-MVEYK55V.js.map → chunk-U7X4V4HE.js.map} +0 -0
  227. /package/dist/{chunk-2RQYJFKA.js.map → chunk-WLZHCM7O.js.map} +0 -0
  228. /package/dist/{chunk-DVW6ASTO.js.map → chunk-XDSVV5GZ.js.map} +0 -0
  229. /package/dist/{source-manager-Q34LTUVM.js.map → source-manager-BVB2SG73.js.map} +0 -0
  230. /package/dist/src/agents/developer/api-developer/{agent.yaml → metadata.yaml} +0 -0
  231. /package/dist/src/agents/developer/cli-developer/{agent.yaml → metadata.yaml} +0 -0
  232. /package/dist/src/agents/developer/web-architecture/{agent.yaml → metadata.yaml} +0 -0
  233. /package/dist/src/agents/developer/web-developer/{agent.yaml → metadata.yaml} +0 -0
  234. /package/dist/src/agents/meta/agent-summoner/{agent.yaml → metadata.yaml} +0 -0
  235. /package/dist/src/agents/meta/skill-summoner/{agent.yaml → metadata.yaml} +0 -0
  236. /package/dist/src/agents/pattern/pattern-scout/{agent.yaml → metadata.yaml} +0 -0
  237. /package/dist/src/agents/pattern/web-pattern-critique/{agent.yaml → metadata.yaml} +0 -0
  238. /package/dist/src/agents/planning/web-pm/{agent.yaml → metadata.yaml} +0 -0
  239. /package/dist/src/agents/researcher/api-researcher/{agent.yaml → metadata.yaml} +0 -0
  240. /package/dist/src/agents/researcher/web-researcher/{agent.yaml → metadata.yaml} +0 -0
  241. /package/dist/src/agents/reviewer/api-reviewer/{agent.yaml → metadata.yaml} +0 -0
  242. /package/dist/src/agents/reviewer/cli-reviewer/{agent.yaml → metadata.yaml} +0 -0
  243. /package/dist/src/agents/reviewer/web-reviewer/{agent.yaml → metadata.yaml} +0 -0
  244. /package/dist/src/agents/tester/cli-tester/{agent.yaml → metadata.yaml} +0 -0
  245. /package/dist/src/agents/tester/web-tester/{agent.yaml → metadata.yaml} +0 -0
  246. /package/src/agents/developer/api-developer/{agent.yaml → metadata.yaml} +0 -0
  247. /package/src/agents/developer/cli-developer/{agent.yaml → metadata.yaml} +0 -0
  248. /package/src/agents/developer/web-architecture/{agent.yaml → metadata.yaml} +0 -0
  249. /package/src/agents/developer/web-developer/{agent.yaml → metadata.yaml} +0 -0
  250. /package/src/agents/meta/agent-summoner/{agent.yaml → metadata.yaml} +0 -0
  251. /package/src/agents/meta/skill-summoner/{agent.yaml → metadata.yaml} +0 -0
  252. /package/src/agents/pattern/pattern-scout/{agent.yaml → metadata.yaml} +0 -0
  253. /package/src/agents/pattern/web-pattern-critique/{agent.yaml → metadata.yaml} +0 -0
  254. /package/src/agents/planning/web-pm/{agent.yaml → metadata.yaml} +0 -0
  255. /package/src/agents/researcher/api-researcher/{agent.yaml → metadata.yaml} +0 -0
  256. /package/src/agents/researcher/web-researcher/{agent.yaml → metadata.yaml} +0 -0
  257. /package/src/agents/reviewer/api-reviewer/{agent.yaml → metadata.yaml} +0 -0
  258. /package/src/agents/reviewer/cli-reviewer/{agent.yaml → metadata.yaml} +0 -0
  259. /package/src/agents/reviewer/web-reviewer/{agent.yaml → metadata.yaml} +0 -0
  260. /package/src/agents/tester/cli-tester/{agent.yaml → metadata.yaml} +0 -0
  261. /package/src/agents/tester/web-tester/{agent.yaml → metadata.yaml} +0 -0
@@ -9,6 +9,7 @@ import {
9
9
  agentFrontmatterValidationSchema,
10
10
  agentNameSchema,
11
11
  agentYamlConfigSchema,
12
+ agentYamlGenerationSchema,
12
13
  categoryPathSchema,
13
14
  copy,
14
15
  directoryExists,
@@ -26,6 +27,7 @@ import {
26
27
  localSkillMetadataSchema,
27
28
  log,
28
29
  marketplaceSchema,
30
+ metadataValidationSchema,
29
31
  pluginAuthorSchema,
30
32
  pluginManifestSchema,
31
33
  projectConfigLoaderSchema,
@@ -34,19 +36,21 @@ import {
34
36
  readFileOptional,
35
37
  readFileSafe,
36
38
  remove,
39
+ skillCategoriesFileSchema,
37
40
  skillDisplayNameSchema,
38
41
  skillFrontmatterLoaderSchema,
39
42
  skillFrontmatterValidationSchema,
40
43
  skillIdSchema,
41
44
  skillMetadataLoaderSchema,
42
- skillsMatrixConfigSchema,
45
+ skillRulesFileSchema,
46
+ stackConfigValidationSchema,
43
47
  stacksConfigSchema,
44
48
  validateNestingDepth,
45
49
  verbose,
46
50
  warn,
47
51
  warnUnknownFields,
48
52
  writeFile
49
- } from "./chunk-C4QI54PN.js";
53
+ } from "./chunk-WBHPCBVN.js";
50
54
  import {
51
55
  ARCHIVED_SKILLS_DIR_NAME,
52
56
  CACHE_DIR,
@@ -74,13 +78,14 @@ import {
74
78
  PROJECT_ROOT,
75
79
  SCHEMA_PATHS,
76
80
  SKILLS_DIR_PATH,
77
- SKILLS_MATRIX_PATH,
81
+ SKILL_CATEGORIES_YAML_PATH,
82
+ SKILL_RULES_YAML_PATH,
78
83
  STACKS_FILE_PATH,
79
84
  STANDARD_DIRS,
80
85
  STANDARD_FILES,
81
86
  YAML_FORMATTING,
82
87
  yamlSchemaComment
83
- } from "./chunk-FTD5Z6QD.js";
88
+ } from "./chunk-AWP5A6IM.js";
84
89
  import {
85
90
  init_esm_shims
86
91
  } from "./chunk-DHET7RCE.js";
@@ -90,6 +95,7 @@ init_esm_shims();
90
95
 
91
96
  // src/cli/lib/configuration/config.ts
92
97
  init_esm_shims();
98
+ import os from "os";
93
99
  import path from "path";
94
100
  import { stringify as stringifyYaml } from "yaml";
95
101
 
@@ -137,6 +143,18 @@ async function loadProjectSourceConfig(projectDir) {
137
143
  verbose(`Loaded project config from ${configPath}`);
138
144
  return data;
139
145
  }
146
+ async function loadGlobalSourceConfig() {
147
+ const homeDir = os.homedir();
148
+ const globalConfigPath = path.join(homeDir, CLAUDE_SRC_DIR, PROJECT_CONFIG_FILE);
149
+ if (!await fileExists(globalConfigPath)) {
150
+ verbose(`Global config not found at ${globalConfigPath}`);
151
+ return null;
152
+ }
153
+ const data = await safeLoadYamlFile(globalConfigPath, projectSourceConfigSchema);
154
+ if (!data) return null;
155
+ verbose(`Loaded global config from ${globalConfigPath}`);
156
+ return data;
157
+ }
140
158
  async function saveProjectConfig(projectDir, config) {
141
159
  const configPath = getProjectConfigPath(projectDir);
142
160
  await ensureDir(path.join(projectDir, CLAUDE_SRC_DIR));
@@ -146,9 +164,13 @@ async function saveProjectConfig(projectDir, config) {
146
164
  await writeFile(configPath, `${schemaComment}${content}`);
147
165
  verbose(`Saved project config to ${configPath}`);
148
166
  }
149
- async function resolveSource(flagValue, projectDir) {
167
+ async function loadEffectiveSourceConfig(projectDir) {
150
168
  const projectConfig = projectDir ? await loadProjectSourceConfig(projectDir) : null;
151
- const marketplace = projectConfig?.marketplace;
169
+ return projectConfig ?? await loadGlobalSourceConfig();
170
+ }
171
+ async function resolveSource(flagValue, projectDir) {
172
+ const effectiveConfig = await loadEffectiveSourceConfig(projectDir);
173
+ const marketplace = effectiveConfig?.marketplace;
152
174
  if (flagValue !== void 0) {
153
175
  if (flagValue === "" || flagValue.trim() === "") {
154
176
  throw new Error(
@@ -178,10 +200,10 @@ ${message}`
178
200
  }
179
201
  }
180
202
  }
181
- if (projectConfig?.source) {
182
- verbose(`Source from project config: ${projectConfig.source}`);
203
+ if (effectiveConfig?.source) {
204
+ verbose(`Source from project config: ${effectiveConfig.source}`);
183
205
  return {
184
- source: projectConfig.source,
206
+ source: effectiveConfig.source,
185
207
  sourceOrigin: "project",
186
208
  marketplace
187
209
  };
@@ -200,11 +222,11 @@ async function resolveAgentsSource(flagValue, projectDir) {
200
222
  verbose(`Agents source from --agent-source flag: ${flagValue}`);
201
223
  return { agentsSource: flagValue, agentsSourceOrigin: "flag" };
202
224
  }
203
- const projectConfig = projectDir ? await loadProjectSourceConfig(projectDir) : null;
204
- if (projectConfig?.agentsSource) {
205
- verbose(`Agents source from project config: ${projectConfig.agentsSource}`);
225
+ const effectiveConfig = await loadEffectiveSourceConfig(projectDir);
226
+ if (effectiveConfig?.agentsSource) {
227
+ verbose(`Agents source from project config: ${effectiveConfig.agentsSource}`);
206
228
  return {
207
- agentsSource: projectConfig.agentsSource,
229
+ agentsSource: effectiveConfig.agentsSource,
208
230
  agentsSourceOrigin: "project"
209
231
  };
210
232
  }
@@ -237,11 +259,11 @@ function formatOrigin(type, origin) {
237
259
  return origin;
238
260
  }
239
261
  async function resolveAuthor(projectDir) {
240
- const projectConfig = projectDir ? await loadProjectSourceConfig(projectDir) : null;
241
- return projectConfig?.author;
262
+ const effectiveConfig = await loadEffectiveSourceConfig(projectDir);
263
+ return effectiveConfig?.author;
242
264
  }
243
265
  async function resolveAllSources(projectDir) {
244
- const projectConfig = projectDir ? await loadProjectSourceConfig(projectDir) : null;
266
+ const effectiveConfig = await loadEffectiveSourceConfig(projectDir);
245
267
  const resolvedConfig = await resolveSource(void 0, projectDir);
246
268
  const primary = {
247
269
  name: "marketplace",
@@ -250,8 +272,8 @@ async function resolveAllSources(projectDir) {
250
272
  };
251
273
  const extras = [];
252
274
  const seenNames = /* @__PURE__ */ new Set();
253
- if (projectConfig?.sources) {
254
- for (const source of projectConfig.sources) {
275
+ if (effectiveConfig?.sources) {
276
+ for (const source of effectiveConfig.sources) {
255
277
  if (!seenNames.has(source.name)) {
256
278
  seenNames.add(source.name);
257
279
  extras.push(source);
@@ -418,7 +440,7 @@ function isLocalSource(source) {
418
440
  init_esm_shims();
419
441
  import { createHash } from "crypto";
420
442
  import { downloadTemplate } from "giget";
421
- import os from "os";
443
+ import os3 from "os";
422
444
  import path4 from "path";
423
445
 
424
446
  // src/cli/lib/configuration/index.ts
@@ -533,6 +555,7 @@ import { difference } from "remeda";
533
555
 
534
556
  // src/cli/lib/configuration/project-config.ts
535
557
  init_esm_shims();
558
+ import os2 from "os";
536
559
  import path3 from "path";
537
560
 
538
561
  // src/cli/lib/stacks/stacks-loader.ts
@@ -629,7 +652,7 @@ function getStackSkillIds(stack) {
629
652
  // src/cli/lib/configuration/project-config.ts
630
653
  var CONFIG_PATH = `${CLAUDE_SRC_DIR}/config.yaml`;
631
654
  var LEGACY_CONFIG_PATH = `${CLAUDE_DIR}/config.yaml`;
632
- async function loadProjectConfig(projectDir) {
655
+ async function loadProjectConfigFromDir(projectDir) {
633
656
  const srcConfigPath = path3.join(projectDir, CONFIG_PATH);
634
657
  const legacyConfigPath = path3.join(projectDir, LEGACY_CONFIG_PATH);
635
658
  let configPath = srcConfigPath;
@@ -666,6 +689,15 @@ async function loadProjectConfig(projectDir) {
666
689
  configPath
667
690
  };
668
691
  }
692
+ async function loadProjectConfig(projectDir) {
693
+ const projectResult = await loadProjectConfigFromDir(projectDir);
694
+ if (projectResult) return projectResult;
695
+ const homeDir = os2.homedir();
696
+ if (projectDir !== homeDir) {
697
+ return loadProjectConfigFromDir(homeDir);
698
+ }
699
+ return null;
700
+ }
669
701
  function validateProjectConfig(config) {
670
702
  const errors = [];
671
703
  const warnings = [];
@@ -803,7 +835,7 @@ function getGigetCacheDir(source) {
803
835
  return void 0;
804
836
  }
805
837
  const templateName = uriMatch.groups.repo.replace("/", "-").replace(/[^\da-z-]/gi, "-");
806
- const gigetCacheRoot = process.env.XDG_CACHE_HOME ? path4.resolve(process.env.XDG_CACHE_HOME, "giget") : path4.resolve(os.homedir(), ".cache", "giget");
838
+ const gigetCacheRoot = process.env.XDG_CACHE_HOME ? path4.resolve(process.env.XDG_CACHE_HOME, "giget") : path4.resolve(os3.homedir(), ".cache", "giget");
807
839
  return path4.join(gigetCacheRoot, providerName, templateName);
808
840
  }
809
841
  async function clearGigetCache(source) {
@@ -977,8 +1009,8 @@ Too many plugins: ${marketplace.plugins.length} (limit: ${MAX_MARKETPLACE_PLUGIN
977
1009
 
978
1010
  // src/cli/lib/skills/local-skill-loader.ts
979
1011
  init_esm_shims();
980
- import { parse as parseYaml8 } from "yaml";
981
- import path22 from "path";
1012
+ import { parse as parseYaml9 } from "yaml";
1013
+ import path23 from "path";
982
1014
 
983
1015
  // src/cli/lib/loading/index.ts
984
1016
  init_esm_shims();
@@ -1004,7 +1036,7 @@ function parseFrontmatter(content, filePath) {
1004
1036
  async function loadAllAgents(projectRoot) {
1005
1037
  const agents = {};
1006
1038
  const agentSourcesDir = path5.join(projectRoot, DIRS.agents);
1007
- const files = await glob("**/agent.yaml", agentSourcesDir);
1039
+ const files = await glob(`**/${STANDARD_FILES.AGENT_METADATA_YAML}`, agentSourcesDir);
1008
1040
  for (const file of files) {
1009
1041
  const fullPath = path5.join(agentSourcesDir, file);
1010
1042
  try {
@@ -1022,7 +1054,7 @@ async function loadAllAgents(projectRoot) {
1022
1054
  };
1023
1055
  verbose(`Loaded agent: ${config.id} from ${file}`);
1024
1056
  } catch (error) {
1025
- warn(`Skipping invalid agent.yaml at '${fullPath}': ${getErrorMessage(error)}`);
1057
+ warn(`Skipping invalid metadata.yaml at '${fullPath}': ${getErrorMessage(error)}`);
1026
1058
  }
1027
1059
  }
1028
1060
  return agents;
@@ -1034,7 +1066,7 @@ async function loadProjectAgents(projectRoot) {
1034
1066
  verbose(`No project agents directory at ${projectAgentsDir}`);
1035
1067
  return agents;
1036
1068
  }
1037
- const files = await glob("**/agent.yaml", projectAgentsDir);
1069
+ const files = await glob(`**/${STANDARD_FILES.AGENT_METADATA_YAML}`, projectAgentsDir);
1038
1070
  for (const file of files) {
1039
1071
  const fullPath = path5.join(projectAgentsDir, file);
1040
1072
  try {
@@ -1054,7 +1086,7 @@ async function loadProjectAgents(projectRoot) {
1054
1086
  };
1055
1087
  verbose(`Loaded project agent: ${config.id} from ${file}`);
1056
1088
  } catch (error) {
1057
- warn(`Skipping invalid agent.yaml at '${fullPath}': ${getErrorMessage(error)}`);
1089
+ warn(`Skipping invalid metadata.yaml at '${fullPath}': ${getErrorMessage(error)}`);
1058
1090
  }
1059
1091
  }
1060
1092
  return agents;
@@ -1159,13 +1191,14 @@ async function loadPluginSkills(pluginDir) {
1159
1191
 
1160
1192
  // src/cli/lib/loading/source-loader.ts
1161
1193
  init_esm_shims();
1162
- import path21 from "path";
1194
+ import os6 from "os";
1195
+ import path22 from "path";
1163
1196
  import { unique as unique4 } from "remeda";
1164
1197
 
1165
1198
  // src/cli/lib/metadata-keys.ts
1166
1199
  init_esm_shims();
1167
1200
  var METADATA_KEYS = {
1168
- CLI_NAME: "cliName",
1201
+ DISPLAY_NAME: "displayName",
1169
1202
  CLI_DESCRIPTION: "cliDescription",
1170
1203
  CATEGORY: "category",
1171
1204
  FORKED_FROM: "forkedFrom",
@@ -1291,6 +1324,10 @@ async function writeContentHash(pluginDir, contentHash, getManifestPath) {
1291
1324
 
1292
1325
  // src/cli/lib/skills/skill-metadata.ts
1293
1326
  async function readForkedFromMetadata(skillDir) {
1327
+ const metadata = await readLocalSkillMetadata(skillDir);
1328
+ return metadata?.forkedFrom ?? null;
1329
+ }
1330
+ async function readLocalSkillMetadata(skillDir) {
1294
1331
  const metadataPath = path7.join(skillDir, STANDARD_FILES.METADATA_YAML);
1295
1332
  if (!await fileExists(metadataPath)) {
1296
1333
  return null;
@@ -1301,7 +1338,7 @@ async function readForkedFromMetadata(skillDir) {
1301
1338
  warn(`Invalid metadata.yaml at ${metadataPath}: ${formatZodErrors(result.error.issues)}`);
1302
1339
  return null;
1303
1340
  }
1304
- return result.data.forkedFrom ?? null;
1341
+ return result.data;
1305
1342
  }
1306
1343
  async function getLocalSkillsWithMetadata(projectDir) {
1307
1344
  const localSkillsPath = path7.join(projectDir, LOCAL_SKILLS_PATH);
@@ -1474,8 +1511,8 @@ async function copySkillsToLocalFlattened(selectedSkillIds, localSkillsDir, matr
1474
1511
 
1475
1512
  // src/cli/lib/skills/skill-plugin-compiler.ts
1476
1513
  init_esm_shims();
1477
- import path17 from "path";
1478
- import { parse as parseYaml6 } from "yaml";
1514
+ import path18 from "path";
1515
+ import { parse as parseYaml7 } from "yaml";
1479
1516
 
1480
1517
  // src/cli/lib/plugins/index.ts
1481
1518
  init_esm_shims();
@@ -1582,19 +1619,21 @@ init_esm_shims();
1582
1619
 
1583
1620
  // src/cli/lib/installation/installation.ts
1584
1621
  init_esm_shims();
1622
+ import os4 from "os";
1585
1623
  import path11 from "path";
1586
- async function detectInstallation(projectDir = process.cwd()) {
1624
+ async function detectProjectInstallation(projectDir) {
1587
1625
  const srcConfigPath = path11.join(projectDir, CLAUDE_SRC_DIR, STANDARD_FILES.CONFIG_YAML);
1588
1626
  const legacyConfigPath = path11.join(projectDir, CLAUDE_DIR, STANDARD_FILES.CONFIG_YAML);
1589
1627
  const localConfigPath = await fileExists(srcConfigPath) ? srcConfigPath : await fileExists(legacyConfigPath) ? legacyConfigPath : null;
1590
1628
  if (!localConfigPath) {
1591
1629
  return null;
1592
1630
  }
1593
- const loaded = await loadProjectConfig(projectDir);
1631
+ const loaded = await loadProjectConfigFromDir(projectDir);
1594
1632
  const mode = loaded?.config?.installMode ?? "local";
1595
1633
  if (mode === "local") {
1596
1634
  return {
1597
1635
  mode: "local",
1636
+ scope: "project",
1598
1637
  configPath: localConfigPath,
1599
1638
  agentsDir: path11.join(projectDir, CLAUDE_DIR, "agents"),
1600
1639
  skillsDir: path11.join(projectDir, CLAUDE_DIR, "skills"),
@@ -1603,12 +1642,45 @@ async function detectInstallation(projectDir = process.cwd()) {
1603
1642
  }
1604
1643
  return {
1605
1644
  mode: "plugin",
1645
+ scope: "project",
1606
1646
  configPath: localConfigPath,
1607
1647
  agentsDir: path11.join(projectDir, CLAUDE_DIR, "agents"),
1608
1648
  skillsDir: path11.join(projectDir, CLAUDE_DIR, PLUGINS_SUBDIR),
1609
1649
  projectDir
1610
1650
  };
1611
1651
  }
1652
+ async function detectGlobalInstallation() {
1653
+ const homeDir = os4.homedir();
1654
+ const srcConfigPath = path11.join(homeDir, CLAUDE_SRC_DIR, STANDARD_FILES.CONFIG_YAML);
1655
+ if (!await fileExists(srcConfigPath)) {
1656
+ return null;
1657
+ }
1658
+ const loaded = await loadProjectConfigFromDir(homeDir);
1659
+ const mode = loaded?.config?.installMode ?? "local";
1660
+ if (mode === "local") {
1661
+ return {
1662
+ mode: "local",
1663
+ scope: "global",
1664
+ configPath: srcConfigPath,
1665
+ agentsDir: path11.join(homeDir, CLAUDE_DIR, "agents"),
1666
+ skillsDir: path11.join(homeDir, CLAUDE_DIR, "skills"),
1667
+ projectDir: homeDir
1668
+ };
1669
+ }
1670
+ return {
1671
+ mode: "plugin",
1672
+ scope: "global",
1673
+ configPath: srcConfigPath,
1674
+ agentsDir: path11.join(homeDir, CLAUDE_DIR, "agents"),
1675
+ skillsDir: path11.join(homeDir, CLAUDE_DIR, PLUGINS_SUBDIR),
1676
+ projectDir: homeDir
1677
+ };
1678
+ }
1679
+ async function detectInstallation(projectDir = process.cwd()) {
1680
+ const projectInstallation = await detectProjectInstallation(projectDir);
1681
+ if (projectInstallation) return projectInstallation;
1682
+ return detectGlobalInstallation();
1683
+ }
1612
1684
 
1613
1685
  // src/cli/lib/installation/local-installer.ts
1614
1686
  init_esm_shims();
@@ -1684,7 +1756,7 @@ async function resolveAgents(agents, skills, compileConfig, _projectRoot, stack)
1684
1756
  const availableAgents = typedKeys(agents);
1685
1757
  const agentList = availableAgents.length > 0 ? `Available agents: ${availableAgents.slice(0, 5).join(", ")}${availableAgents.length > 5 ? ` (and ${availableAgents.length - 5} more)` : ""}` : "No agents found in scanned directories";
1686
1758
  throw new Error(
1687
- `Agent '${agentName}' referenced in compile config but not found in scanned agents. ${agentList}. Check that src/agents/${agentName}/agent.yaml exists.`
1759
+ `Agent '${agentName}' referenced in compile config but not found in scanned agents. ${agentList}. Check that src/agents/${agentName}/metadata.yaml exists.`
1688
1760
  );
1689
1761
  }
1690
1762
  const agentConfig = compileConfig.agents[agentName];
@@ -2181,9 +2253,6 @@ async function buildLocalConfig(wizardResult, sourceResult) {
2181
2253
  }
2182
2254
  function setConfigMetadata(config, wizardResult, sourceResult, sourceFlag) {
2183
2255
  config.installMode = wizardResult.installMode;
2184
- if (wizardResult.expertMode) {
2185
- config.expertMode = true;
2186
- }
2187
2256
  if (wizardResult.selectedDomains && wizardResult.selectedDomains.length > 0) {
2188
2257
  config.domains = wizardResult.selectedDomains;
2189
2258
  }
@@ -2225,7 +2294,8 @@ var PATH_OVERRIDES_COMMENT = [
2225
2294
  `# skillsDir: ${SKILLS_DIR_PATH}`,
2226
2295
  `# agentsDir: ${DIRS.agents}`,
2227
2296
  `# stacksFile: ${STACKS_FILE_PATH}`,
2228
- `# matrixFile: ${SKILLS_MATRIX_PATH}`,
2297
+ `# categoriesFile: ${SKILL_CATEGORIES_YAML_PATH}`,
2298
+ `# rulesFile: ${SKILL_RULES_YAML_PATH}`,
2229
2299
  ""
2230
2300
  ].join("\n");
2231
2301
  async function writeConfigFile(config, configPath) {
@@ -2359,7 +2429,7 @@ init_esm_shims();
2359
2429
  // src/cli/lib/plugins/plugin-settings.ts
2360
2430
  init_esm_shims();
2361
2431
  import path15 from "path";
2362
- import os2 from "os";
2432
+ import os5 from "os";
2363
2433
  import { z } from "zod";
2364
2434
  var pluginSettingsSchema = z.object({
2365
2435
  enabledPlugins: z.record(z.string(), z.unknown()).optional()
@@ -2410,7 +2480,7 @@ async function resolvePluginInstallPaths(pluginKeys, projectDir) {
2410
2480
  if (pluginKeys.length === 0) {
2411
2481
  return [];
2412
2482
  }
2413
- const registryPath = path15.join(os2.homedir(), CLAUDE_DIR, PLUGINS_SUBDIR, INSTALLED_PLUGINS_FILE);
2483
+ const registryPath = path15.join(os5.homedir(), CLAUDE_DIR, PLUGINS_SUBDIR, INSTALLED_PLUGINS_FILE);
2414
2484
  if (!await fileExists(registryPath)) {
2415
2485
  verbose(`Plugin registry not found at '${registryPath}'`);
2416
2486
  return [];
@@ -2585,9 +2655,221 @@ function formatInstallationDisplay(info) {
2585
2655
  // src/cli/lib/plugins/plugin-validator.ts
2586
2656
  init_esm_shims();
2587
2657
  import { z as z2 } from "zod";
2658
+ import path17 from "path";
2659
+ import fg2 from "fast-glob";
2660
+ import { countBy } from "remeda";
2661
+
2662
+ // src/cli/lib/schema-validator.ts
2663
+ init_esm_shims();
2664
+ import { sumBy } from "remeda";
2588
2665
  import path16 from "path";
2666
+ import { parse as parseYaml6 } from "yaml";
2589
2667
  import fg from "fast-glob";
2590
- import { countBy } from "remeda";
2668
+ var VALIDATION_TARGETS = [
2669
+ {
2670
+ name: "Skill Categories",
2671
+ schema: skillCategoriesFileSchema,
2672
+ pattern: STANDARD_FILES.SKILL_CATEGORIES_YAML,
2673
+ baseDir: "config"
2674
+ },
2675
+ {
2676
+ name: "Skill Rules",
2677
+ schema: skillRulesFileSchema,
2678
+ pattern: STANDARD_FILES.SKILL_RULES_YAML,
2679
+ baseDir: "config"
2680
+ },
2681
+ {
2682
+ name: "Skill Metadata",
2683
+ schema: metadataValidationSchema,
2684
+ pattern: `**/${STANDARD_FILES.METADATA_YAML}`,
2685
+ baseDir: "src/skills"
2686
+ },
2687
+ {
2688
+ name: "Stack Skill Metadata",
2689
+ schema: metadataValidationSchema,
2690
+ pattern: `**/skills/**/${STANDARD_FILES.METADATA_YAML}`,
2691
+ baseDir: "src/stacks"
2692
+ },
2693
+ {
2694
+ name: "Stack Config",
2695
+ schema: stackConfigValidationSchema,
2696
+ pattern: `*/${STANDARD_FILES.CONFIG_YAML}`,
2697
+ baseDir: "src/stacks"
2698
+ },
2699
+ {
2700
+ name: "Agent Definition",
2701
+ schema: agentYamlGenerationSchema,
2702
+ pattern: `**/${STANDARD_FILES.AGENT_METADATA_YAML}`,
2703
+ baseDir: "src/agents"
2704
+ },
2705
+ {
2706
+ name: "Skill Frontmatter",
2707
+ schema: skillFrontmatterValidationSchema,
2708
+ pattern: `**/${STANDARD_FILES.SKILL_MD}`,
2709
+ baseDir: "src/skills",
2710
+ extractor: extractFrontmatter
2711
+ },
2712
+ {
2713
+ name: "Stack Skill Frontmatter",
2714
+ schema: skillFrontmatterValidationSchema,
2715
+ pattern: `**/skills/**/${STANDARD_FILES.SKILL_MD}`,
2716
+ baseDir: "src/stacks",
2717
+ extractor: extractFrontmatter
2718
+ },
2719
+ {
2720
+ name: "Stacks Config",
2721
+ schema: stacksConfigSchema,
2722
+ pattern: "stacks.yaml",
2723
+ baseDir: "config"
2724
+ },
2725
+ {
2726
+ name: "Project Source Config",
2727
+ schema: projectSourceConfigSchema,
2728
+ pattern: STANDARD_FILES.CONFIG_YAML,
2729
+ baseDir: CLAUDE_SRC_DIR
2730
+ },
2731
+ {
2732
+ name: "Project Skill Metadata",
2733
+ schema: metadataValidationSchema,
2734
+ pattern: `*/${STANDARD_FILES.METADATA_YAML}`,
2735
+ baseDir: `${CLAUDE_DIR}/skills`
2736
+ },
2737
+ {
2738
+ name: "Project Skill Frontmatter",
2739
+ schema: skillFrontmatterValidationSchema,
2740
+ pattern: `*/${STANDARD_FILES.SKILL_MD}`,
2741
+ baseDir: `${CLAUDE_DIR}/skills`,
2742
+ extractor: extractFrontmatter
2743
+ },
2744
+ {
2745
+ name: "Project Agent Frontmatter",
2746
+ schema: agentFrontmatterValidationSchema,
2747
+ pattern: "*.md",
2748
+ baseDir: `${CLAUDE_DIR}/agents`,
2749
+ extractor: extractFrontmatter
2750
+ },
2751
+ {
2752
+ name: "Plugin Manifest",
2753
+ schema: pluginManifestSchema,
2754
+ pattern: `*/${STANDARD_FILES.PLUGIN_JSON}`,
2755
+ baseDir: `${CLAUDE_DIR}/plugins`,
2756
+ extractor: (content) => JSON.parse(content)
2757
+ }
2758
+ ];
2759
+ function formatZodErrors2(error) {
2760
+ return error.issues.map((issue) => {
2761
+ const path24 = issue.path.join(".");
2762
+ if (issue.code === "unrecognized_keys") {
2763
+ return `Unrecognized key: "${issue.keys.join('", "')}"`;
2764
+ }
2765
+ return path24 ? `${path24}: ${issue.message}` : issue.message;
2766
+ });
2767
+ }
2768
+ async function validateFile(filePath, schema, extractor) {
2769
+ try {
2770
+ if (!await fileExists(filePath)) {
2771
+ return { valid: false, errors: [`File not found: ${filePath}`] };
2772
+ }
2773
+ const content = await readFile(filePath);
2774
+ let parsed;
2775
+ if (extractor) {
2776
+ parsed = extractor(content);
2777
+ if (parsed === null) {
2778
+ return {
2779
+ valid: false,
2780
+ errors: ["Failed to extract content (no valid frontmatter found)"]
2781
+ };
2782
+ }
2783
+ } else {
2784
+ parsed = parseYaml6(content);
2785
+ }
2786
+ const result = schema.safeParse(parsed);
2787
+ if (result.success) {
2788
+ return { valid: true, errors: [] };
2789
+ }
2790
+ return { valid: false, errors: formatZodErrors2(result.error) };
2791
+ } catch (error) {
2792
+ const message = getErrorMessage(error);
2793
+ return { valid: false, errors: [`Failed to parse content: ${message}`] };
2794
+ }
2795
+ }
2796
+ async function validateTarget(target, rootDir = process.cwd()) {
2797
+ const baseDir = path16.join(rootDir, target.baseDir);
2798
+ const pattern = path16.join(baseDir, target.pattern);
2799
+ const files = await fg(pattern, { absolute: true });
2800
+ const result = {
2801
+ schemaName: target.name,
2802
+ valid: true,
2803
+ totalFiles: files.length,
2804
+ validFiles: 0,
2805
+ invalidFiles: []
2806
+ };
2807
+ if (files.length === 0) {
2808
+ return result;
2809
+ }
2810
+ for (const file of files) {
2811
+ const validation = await validateFile(file, target.schema, target.extractor);
2812
+ const relativePath = path16.relative(rootDir, file);
2813
+ if (validation.valid) {
2814
+ result.validFiles++;
2815
+ } else {
2816
+ result.valid = false;
2817
+ result.invalidFiles.push({
2818
+ file: relativePath,
2819
+ errors: validation.errors
2820
+ });
2821
+ }
2822
+ }
2823
+ return result;
2824
+ }
2825
+ async function validateAllSchemas(rootDir = process.cwd()) {
2826
+ const results = [];
2827
+ for (const target of VALIDATION_TARGETS) {
2828
+ const result = await validateTarget(target, rootDir);
2829
+ results.push(result);
2830
+ }
2831
+ const summary = {
2832
+ totalSchemas: results.length,
2833
+ totalFiles: sumBy(results, (r) => r.totalFiles),
2834
+ validFiles: sumBy(results, (r) => r.validFiles),
2835
+ invalidFiles: sumBy(results, (r) => r.invalidFiles.length)
2836
+ };
2837
+ return {
2838
+ valid: results.every((r) => r.valid),
2839
+ results,
2840
+ summary
2841
+ };
2842
+ }
2843
+ function printValidationResults(result) {
2844
+ log("\n Schema Validation Summary:");
2845
+ log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
2846
+ log(` Total schemas checked: ${result.summary.totalSchemas}`);
2847
+ log(` Total files: ${result.summary.totalFiles}`);
2848
+ log(` Valid: ${result.summary.validFiles}`);
2849
+ log(` Invalid: ${result.summary.invalidFiles}`);
2850
+ for (const schemaResult of result.results) {
2851
+ if (schemaResult.totalFiles === 0) continue;
2852
+ const status = schemaResult.valid ? "\u2713" : "\u2717";
2853
+ log(
2854
+ `
2855
+ ${status} ${schemaResult.schemaName}: ${schemaResult.validFiles}/${schemaResult.totalFiles} valid`
2856
+ );
2857
+ if (schemaResult.invalidFiles.length > 0) {
2858
+ for (const file of schemaResult.invalidFiles) {
2859
+ log(`
2860
+ ${file.file}:`);
2861
+ file.errors.forEach((e) => log(` - ${e}`));
2862
+ }
2863
+ }
2864
+ }
2865
+ if (result.valid) {
2866
+ log("\n \u2713 All schemas validated successfully\n");
2867
+ } else {
2868
+ log("\n \u2717 Validation failed\n");
2869
+ }
2870
+ }
2871
+
2872
+ // src/cli/lib/plugins/plugin-validator.ts
2591
2873
  var PLUGIN_DIR = PLUGIN_MANIFEST_DIR;
2592
2874
  var PLUGIN_MANIFEST = STANDARD_FILES.PLUGIN_JSON;
2593
2875
  var KEBAB_CASE_REGEX = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
@@ -2603,15 +2885,6 @@ var pluginManifestValidationSchema = z2.object({
2603
2885
  skills: z2.union([z2.string(), z2.array(z2.string())]).optional(),
2604
2886
  hooks: z2.union([z2.string(), hooksRecordSchema]).optional()
2605
2887
  }).strict();
2606
- function formatZodErrors2(error) {
2607
- return error.issues.map((issue) => {
2608
- const path23 = issue.path.join(".");
2609
- if (issue.code === "unrecognized_keys") {
2610
- return `Unrecognized key: "${issue.keys.join('", "')}"`;
2611
- }
2612
- return path23 ? `${path23}: ${issue.message}` : issue.message;
2613
- });
2614
- }
2615
2888
  function isKebabCase(str) {
2616
2889
  return KEBAB_CASE_REGEX.test(str);
2617
2890
  }
@@ -2628,15 +2901,15 @@ async function validatePluginStructure(pluginPath) {
2628
2901
  warnings: []
2629
2902
  };
2630
2903
  }
2631
- const pluginDir = path16.join(pluginPath, PLUGIN_DIR);
2904
+ const pluginDir = path17.join(pluginPath, PLUGIN_DIR);
2632
2905
  if (!await directoryExists(pluginDir)) {
2633
2906
  errors.push(`Missing ${PLUGIN_DIR}/ directory`);
2634
2907
  }
2635
- const manifestPath = path16.join(pluginDir, PLUGIN_MANIFEST);
2908
+ const manifestPath = path17.join(pluginDir, PLUGIN_MANIFEST);
2636
2909
  if (!await fileExists(manifestPath)) {
2637
2910
  errors.push(`Missing ${PLUGIN_DIR}/${PLUGIN_MANIFEST}`);
2638
2911
  }
2639
- const readmePath = path16.join(pluginPath, "README.md");
2912
+ const readmePath = path17.join(pluginPath, "README.md");
2640
2913
  if (!await fileExists(readmePath)) {
2641
2914
  warnings.push("Missing README.md (recommended for documentation)");
2642
2915
  }
@@ -2686,15 +2959,15 @@ async function validatePluginManifest(manifestPath) {
2686
2959
  if (!manifest.description) {
2687
2960
  warnings.push("Missing description field (recommended for discoverability)");
2688
2961
  }
2689
- const pluginDir = path16.dirname(path16.dirname(manifestPath));
2962
+ const pluginDir = path17.dirname(path17.dirname(manifestPath));
2690
2963
  if (manifest.skills && typeof manifest.skills === "string") {
2691
- const skillsPath = path16.join(pluginDir, manifest.skills);
2964
+ const skillsPath = path17.join(pluginDir, manifest.skills);
2692
2965
  if (!await directoryExists(skillsPath)) {
2693
2966
  errors.push(`Skills path does not exist: ${manifest.skills}`);
2694
2967
  }
2695
2968
  }
2696
2969
  if (manifest.agents && typeof manifest.agents === "string") {
2697
- const agentsPath = path16.join(pluginDir, manifest.agents);
2970
+ const agentsPath = path17.join(pluginDir, manifest.agents);
2698
2971
  if (!await directoryExists(agentsPath)) {
2699
2972
  errors.push(`Agents path does not exist: ${manifest.agents}`);
2700
2973
  }
@@ -2783,9 +3056,9 @@ function prefixResult(result, prefix) {
2783
3056
  };
2784
3057
  }
2785
3058
  async function validatePluginSkillFiles(pluginPath, skillsRelPath) {
2786
- const skillsDir = path16.join(pluginPath, skillsRelPath);
3059
+ const skillsDir = path17.join(pluginPath, skillsRelPath);
2787
3060
  if (!await directoryExists(skillsDir)) return EMPTY_RESULT;
2788
- const files = await fg("**/SKILL.md", { cwd: skillsDir, absolute: true });
3061
+ const files = await fg2("**/SKILL.md", { cwd: skillsDir, absolute: true });
2789
3062
  if (files.length === 0) {
2790
3063
  return {
2791
3064
  valid: true,
@@ -2795,15 +3068,15 @@ async function validatePluginSkillFiles(pluginPath, skillsRelPath) {
2795
3068
  }
2796
3069
  const results = await Promise.all(
2797
3070
  files.map(
2798
- async (f) => prefixResult(await validateSkillFrontmatter(f), path16.relative(pluginPath, f))
3071
+ async (f) => prefixResult(await validateSkillFrontmatter(f), path17.relative(pluginPath, f))
2799
3072
  )
2800
3073
  );
2801
3074
  return mergeResults(results);
2802
3075
  }
2803
3076
  async function validatePluginAgentFiles(pluginPath, agentsRelPath) {
2804
- const agentsDir = path16.join(pluginPath, agentsRelPath);
3077
+ const agentsDir = path17.join(pluginPath, agentsRelPath);
2805
3078
  if (!await directoryExists(agentsDir)) return EMPTY_RESULT;
2806
- const files = await fg("*.md", { cwd: agentsDir, absolute: true });
3079
+ const files = await fg2("*.md", { cwd: agentsDir, absolute: true });
2807
3080
  if (files.length === 0) {
2808
3081
  return {
2809
3082
  valid: true,
@@ -2813,7 +3086,7 @@ async function validatePluginAgentFiles(pluginPath, agentsRelPath) {
2813
3086
  }
2814
3087
  const results = await Promise.all(
2815
3088
  files.map(
2816
- async (f) => prefixResult(await validateAgentFrontmatter(f), path16.relative(pluginPath, f))
3089
+ async (f) => prefixResult(await validateAgentFrontmatter(f), path17.relative(pluginPath, f))
2817
3090
  )
2818
3091
  );
2819
3092
  return mergeResults(results);
@@ -2829,7 +3102,7 @@ async function loadManifestForValidation(manifestPath) {
2829
3102
  async function validatePlugin(pluginPath) {
2830
3103
  const structureResult = await validatePluginStructure(pluginPath);
2831
3104
  if (!structureResult.valid) return structureResult;
2832
- const manifestPath = path16.join(pluginPath, PLUGIN_DIR, PLUGIN_MANIFEST);
3105
+ const manifestPath = path17.join(pluginPath, PLUGIN_DIR, PLUGIN_MANIFEST);
2833
3106
  const manifestResult = await validatePluginManifest(manifestPath);
2834
3107
  const manifest = await loadManifestForValidation(manifestPath);
2835
3108
  const skillsResult = manifest?.skills && typeof manifest.skills === "string" ? await validatePluginSkillFiles(pluginPath, manifest.skills) : EMPTY_RESULT;
@@ -2857,7 +3130,7 @@ async function validateAllPlugins(pluginsDir) {
2857
3130
  const allDirs = await listDirectories(pluginsDir);
2858
3131
  const pluginDirs = [];
2859
3132
  for (const dirName of allDirs) {
2860
- const potentialPluginDir = path16.join(pluginsDir, dirName, PLUGIN_DIR);
3133
+ const potentialPluginDir = path17.join(pluginsDir, dirName, PLUGIN_DIR);
2861
3134
  if (await directoryExists(potentialPluginDir)) {
2862
3135
  pluginDirs.push(dirName);
2863
3136
  }
@@ -2881,7 +3154,7 @@ async function validateAllPlugins(pluginsDir) {
2881
3154
  };
2882
3155
  }
2883
3156
  for (const pluginName of pluginDirs) {
2884
- const pluginPath = path16.join(pluginsDir, pluginName);
3157
+ const pluginPath = path17.join(pluginsDir, pluginName);
2885
3158
  const result = await validatePlugin(pluginPath);
2886
3159
  results.push({ name: pluginName, result });
2887
3160
  }
@@ -2919,7 +3192,7 @@ function sanitizeSkillName(name) {
2919
3192
  return name.replace(/\+/g, "-");
2920
3193
  }
2921
3194
  async function readSkillMetadata(skillPath) {
2922
- const metadataPath = path17.join(skillPath, STANDARD_FILES.METADATA_YAML);
3195
+ const metadataPath = path18.join(skillPath, STANDARD_FILES.METADATA_YAML);
2923
3196
  if (!await fileExists(metadataPath)) {
2924
3197
  return null;
2925
3198
  }
@@ -2927,7 +3200,7 @@ async function readSkillMetadata(skillPath) {
2927
3200
  const content = await readFile(metadataPath);
2928
3201
  const lines = content.split("\n");
2929
3202
  const yamlContent = lines[0]?.startsWith("# yaml-language-server:") ? lines.slice(1).join("\n") : content;
2930
- const result = skillMetadataLoaderSchema.safeParse(parseYaml6(yamlContent));
3203
+ const result = skillMetadataLoaderSchema.safeParse(parseYaml7(yamlContent));
2931
3204
  if (!result.success) {
2932
3205
  warn(`Invalid metadata.yaml at '${skillPath}': ${formatZodErrors(result.error.issues)}`);
2933
3206
  return null;
@@ -2976,8 +3249,8 @@ function generateReadme(skillName, frontmatter, metadata) {
2976
3249
  }
2977
3250
  async function compileSkillPlugin(options) {
2978
3251
  const { skillPath, outputDir, skillName: overrideName } = options;
2979
- const dirBasename = path17.basename(skillPath);
2980
- const skillMdPath = path17.join(skillPath, STANDARD_FILES.SKILL_MD);
3252
+ const dirBasename = path18.basename(skillPath);
3253
+ const skillMdPath = path18.join(skillPath, STANDARD_FILES.SKILL_MD);
2981
3254
  if (!await fileExists(skillMdPath)) {
2982
3255
  throw new Error(
2983
3256
  `Skill '${dirBasename}' is missing required ${STANDARD_FILES.SKILL_MD} file. Expected at: ${skillMdPath}`
@@ -2993,8 +3266,8 @@ async function compileSkillPlugin(options) {
2993
3266
  const skillName = overrideName ?? sanitizeSkillName(frontmatter.name);
2994
3267
  verbose(`Compiling skill plugin: ${skillName} from ${skillPath}`);
2995
3268
  const metadata = await readSkillMetadata(skillPath);
2996
- const pluginDir = path17.join(outputDir, skillName);
2997
- const skillsDir = path17.join(pluginDir, "skills", skillName);
3269
+ const pluginDir = path18.join(outputDir, skillName);
3270
+ const skillsDir = path18.join(pluginDir, "skills", skillName);
2998
3271
  await ensureDir(pluginDir);
2999
3272
  await ensureDir(skillsDir);
3000
3273
  const newHash = await computeSkillFolderHash(skillPath);
@@ -3013,26 +3286,26 @@ async function compileSkillPlugin(options) {
3013
3286
  await writePluginManifest(pluginDir, manifest);
3014
3287
  await writeContentHash(pluginDir, contentHash, getPluginManifestPath);
3015
3288
  verbose(` Wrote plugin.json for ${skillName} (v${version})`);
3016
- await writeFile(path17.join(skillsDir, STANDARD_FILES.SKILL_MD), skillMdContent);
3289
+ await writeFile(path18.join(skillsDir, STANDARD_FILES.SKILL_MD), skillMdContent);
3017
3290
  verbose(` Copied ${STANDARD_FILES.SKILL_MD}`);
3018
3291
  for (const fileName of SKILL_CONTENT_FILES) {
3019
3292
  if (fileName === STANDARD_FILES.SKILL_MD) continue;
3020
- const sourcePath = path17.join(skillPath, fileName);
3293
+ const sourcePath = path18.join(skillPath, fileName);
3021
3294
  if (await fileExists(sourcePath)) {
3022
3295
  const content = await readFile(sourcePath);
3023
- await writeFile(path17.join(skillsDir, fileName), content);
3296
+ await writeFile(path18.join(skillsDir, fileName), content);
3024
3297
  verbose(` Copied ${fileName}`);
3025
3298
  }
3026
3299
  }
3027
3300
  for (const dirName of SKILL_CONTENT_DIRS) {
3028
- const sourceDir = path17.join(skillPath, dirName);
3301
+ const sourceDir = path18.join(skillPath, dirName);
3029
3302
  if (await fileExists(sourceDir)) {
3030
- await copy(sourceDir, path17.join(skillsDir, dirName));
3303
+ await copy(sourceDir, path18.join(skillsDir, dirName));
3031
3304
  verbose(` Copied ${dirName}/`);
3032
3305
  }
3033
3306
  }
3034
3307
  const readme = generateReadme(skillName, frontmatter, metadata);
3035
- await writeFile(path17.join(pluginDir, "README.md"), readme);
3308
+ await writeFile(path18.join(pluginDir, "README.md"), readme);
3036
3309
  verbose(" Generated README.md");
3037
3310
  return {
3038
3311
  pluginPath: pluginDir,
@@ -3044,7 +3317,7 @@ async function compileAllSkillPlugins(skillsDir, outputDir) {
3044
3317
  const results = [];
3045
3318
  const skillMdFiles = await glob(`**/${STANDARD_FILES.SKILL_MD}`, skillsDir);
3046
3319
  for (const skillMdFile of skillMdFiles) {
3047
- const skillPath = path17.join(skillsDir, path17.dirname(skillMdFile));
3320
+ const skillPath = path18.join(skillsDir, path18.dirname(skillMdFile));
3048
3321
  try {
3049
3322
  const result = await compileSkillPlugin({
3050
3323
  skillPath,
@@ -3054,7 +3327,7 @@ async function compileAllSkillPlugins(skillsDir, outputDir) {
3054
3327
  log(` [OK] ${result.skillName}`);
3055
3328
  } catch (error) {
3056
3329
  const errorMessage = getErrorMessage(error);
3057
- const dirBasename = path17.basename(skillPath);
3330
+ const dirBasename = path18.basename(skillPath);
3058
3331
  warn(`Failed to compile skill from '${dirBasename}': ${errorMessage}`);
3059
3332
  }
3060
3333
  }
@@ -3070,7 +3343,7 @@ Compiled ${results.length} skill plugins:`);
3070
3343
 
3071
3344
  // src/cli/lib/skills/source-switcher.ts
3072
3345
  init_esm_shims();
3073
- import path18 from "path";
3346
+ import path19 from "path";
3074
3347
  function validateSkillId(skillId) {
3075
3348
  if (!isValidSkillId(skillId)) {
3076
3349
  return false;
@@ -3078,19 +3351,19 @@ function validateSkillId(skillId) {
3078
3351
  return !(skillId.includes("\0") || skillId.includes("..") || skillId.includes("/") || skillId.includes("\\"));
3079
3352
  }
3080
3353
  function validatePathBoundary(resolvedPath, expectedParent) {
3081
- const normalizedPath = path18.resolve(resolvedPath);
3082
- const normalizedParent = path18.resolve(expectedParent);
3083
- return normalizedPath.startsWith(normalizedParent + path18.sep);
3354
+ const normalizedPath = path19.resolve(resolvedPath);
3355
+ const normalizedParent = path19.resolve(expectedParent);
3356
+ return normalizedPath.startsWith(normalizedParent + path19.sep);
3084
3357
  }
3085
3358
  async function archiveLocalSkill(projectDir, skillId) {
3086
3359
  if (!validateSkillId(skillId)) {
3087
3360
  warn(`Invalid skill ID for archiving: '${skillId}'`);
3088
3361
  return;
3089
3362
  }
3090
- const skillsDir = path18.resolve(path18.join(projectDir, LOCAL_SKILLS_PATH));
3091
- const skillPath = path18.resolve(path18.join(skillsDir, skillId));
3092
- const archivedDir = path18.resolve(path18.join(skillsDir, ARCHIVED_SKILLS_DIR_NAME));
3093
- const archivedSkillPath = path18.resolve(path18.join(archivedDir, skillId));
3363
+ const skillsDir = path19.resolve(path19.join(projectDir, LOCAL_SKILLS_PATH));
3364
+ const skillPath = path19.resolve(path19.join(skillsDir, skillId));
3365
+ const archivedDir = path19.resolve(path19.join(skillsDir, ARCHIVED_SKILLS_DIR_NAME));
3366
+ const archivedSkillPath = path19.resolve(path19.join(archivedDir, skillId));
3094
3367
  if (!validatePathBoundary(skillPath, skillsDir) || !validatePathBoundary(archivedSkillPath, archivedDir)) {
3095
3368
  warn(`Skill ID '${skillId}' resolves outside the skills directory.`);
3096
3369
  return;
@@ -3110,10 +3383,10 @@ async function restoreArchivedSkill(projectDir, skillId) {
3110
3383
  warn(`Invalid skill ID for restoring: '${skillId}'`);
3111
3384
  return false;
3112
3385
  }
3113
- const skillsDir = path18.resolve(path18.join(projectDir, LOCAL_SKILLS_PATH));
3114
- const skillPath = path18.resolve(path18.join(skillsDir, skillId));
3115
- const archivedDir = path18.resolve(path18.join(skillsDir, ARCHIVED_SKILLS_DIR_NAME));
3116
- const archivedSkillPath = path18.resolve(path18.join(archivedDir, skillId));
3386
+ const skillsDir = path19.resolve(path19.join(projectDir, LOCAL_SKILLS_PATH));
3387
+ const skillPath = path19.resolve(path19.join(skillsDir, skillId));
3388
+ const archivedDir = path19.resolve(path19.join(skillsDir, ARCHIVED_SKILLS_DIR_NAME));
3389
+ const archivedSkillPath = path19.resolve(path19.join(archivedDir, skillId));
3117
3390
  if (!validatePathBoundary(skillPath, skillsDir) || !validatePathBoundary(archivedSkillPath, archivedDir)) {
3118
3391
  warn(`Skill ID '${skillId}' resolves outside the skills directory.`);
3119
3392
  return false;
@@ -3133,39 +3406,61 @@ init_esm_shims();
3133
3406
 
3134
3407
  // src/cli/lib/matrix/matrix-loader.ts
3135
3408
  init_esm_shims();
3136
- import { parse as parseYaml7 } from "yaml";
3137
- import path19 from "path";
3409
+ import { parse as parseYaml8 } from "yaml";
3410
+ import path20 from "path";
3138
3411
  import { z as z3 } from "zod";
3139
3412
  var rawMetadataSchema = z3.object({
3140
3413
  category: categoryPathSchema,
3141
- categoryExclusive: z3.boolean().optional(),
3142
3414
  author: z3.string(),
3143
- cliName: z3.string().optional(),
3415
+ displayName: z3.string().optional(),
3144
3416
  cliDescription: z3.string().optional(),
3145
3417
  usageGuidance: z3.string().optional(),
3146
3418
  tags: z3.array(z3.string()).optional(),
3147
- // Lenient: accepts display names and skill IDs from YAML, resolved to canonical IDs during matrix merge
3148
- compatibleWith: z3.array(z3.string()).optional(),
3149
- conflictsWith: z3.array(z3.string()).optional(),
3150
- requires: z3.array(z3.string()).optional(),
3151
- requiresSetup: z3.array(z3.string()).optional(),
3152
- providesSetupFor: z3.array(z3.string()).optional(),
3153
3419
  domain: extensibleDomainSchema.optional(),
3154
3420
  custom: z3.boolean().optional()
3155
3421
  });
3156
- async function loadSkillsMatrix(configPath) {
3422
+ var KNOWN_DOMAINS = new Set(DOMAIN_VALUES);
3423
+ var AUTO_SYNTH_ORDER = 999;
3424
+ function synthesizeCategory(categoryPath, skillDomain) {
3425
+ const prefix = categoryPath.split("-")[0];
3426
+ const domain = skillDomain ?? (KNOWN_DOMAINS.has(prefix) ? prefix : void 0);
3427
+ const displayName = categoryPath.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
3428
+ return {
3429
+ id: categoryPath,
3430
+ displayName,
3431
+ description: `Auto-generated category for ${categoryPath}`,
3432
+ domain,
3433
+ exclusive: true,
3434
+ required: false,
3435
+ order: AUTO_SYNTH_ORDER,
3436
+ custom: true
3437
+ };
3438
+ }
3439
+ async function loadSkillCategories(configPath) {
3440
+ const content = await readFile(configPath);
3441
+ const raw = parseYaml8(content);
3442
+ const result = skillCategoriesFileSchema.safeParse(raw);
3443
+ if (!result.success) {
3444
+ throw new Error(
3445
+ `Invalid skill categories at '${configPath}': ${formatZodErrors(result.error.issues)}`
3446
+ );
3447
+ }
3448
+ verbose(`Loaded skill categories: ${configPath}`);
3449
+ return result.data.categories;
3450
+ }
3451
+ async function loadSkillRules(configPath) {
3157
3452
  const content = await readFile(configPath);
3158
- const raw = parseYaml7(content);
3159
- const result = skillsMatrixConfigSchema.safeParse(raw);
3453
+ const raw = parseYaml8(content);
3454
+ const result = skillRulesFileSchema.safeParse(raw);
3160
3455
  if (!result.success) {
3161
3456
  throw new Error(
3162
- `Invalid skills matrix at '${configPath}': ${formatZodErrors(result.error.issues)}`
3457
+ `Invalid skill rules at '${configPath}': ${formatZodErrors(result.error.issues)}`
3163
3458
  );
3164
3459
  }
3165
3460
  const data = result.data;
3166
- const matrix = {
3461
+ const config = {
3167
3462
  version: data.version,
3168
- categories: data.categories,
3463
+ aliases: data.aliases ?? {},
3169
3464
  relationships: data.relationships ?? {
3170
3465
  conflicts: [],
3171
3466
  discourages: [],
@@ -3173,24 +3468,24 @@ async function loadSkillsMatrix(configPath) {
3173
3468
  requires: [],
3174
3469
  alternatives: []
3175
3470
  },
3176
- skillAliases: data.skillAliases ?? {}
3471
+ perSkill: data["per-skill"] ?? {}
3177
3472
  };
3178
- verbose(`Loaded skills matrix: ${configPath}`);
3179
- return matrix;
3473
+ verbose(`Loaded skill rules: ${configPath}`);
3474
+ return config;
3180
3475
  }
3181
3476
  async function extractAllSkills(skillsDir) {
3182
3477
  const skills = [];
3183
3478
  const metadataFiles = await glob(`**/${STANDARD_FILES.METADATA_YAML}`, skillsDir);
3184
3479
  for (const metadataFile of metadataFiles) {
3185
- const skillDir = path19.dirname(metadataFile);
3186
- const skillMdPath = path19.join(skillsDir, skillDir, STANDARD_FILES.SKILL_MD);
3187
- const metadataPath = path19.join(skillsDir, metadataFile);
3480
+ const skillDir = path20.dirname(metadataFile);
3481
+ const skillMdPath = path20.join(skillsDir, skillDir, STANDARD_FILES.SKILL_MD);
3482
+ const metadataPath = path20.join(skillsDir, metadataFile);
3188
3483
  if (!await fileExists(skillMdPath)) {
3189
3484
  verbose(`Skipping ${metadataFile}: No ${STANDARD_FILES.SKILL_MD} found`);
3190
3485
  continue;
3191
3486
  }
3192
3487
  const metadataContent = await readFile(metadataPath);
3193
- const rawMetadata = parseYaml7(metadataContent);
3488
+ const rawMetadata = parseYaml8(metadataContent);
3194
3489
  const metadataResult = rawMetadataSchema.safeParse(rawMetadata);
3195
3490
  if (!metadataResult.success) {
3196
3491
  warn(
@@ -3205,9 +3500,9 @@ async function extractAllSkills(skillsDir) {
3205
3500
  verbose(`Skipping ${metadataFile}: Invalid SKILL.md frontmatter`);
3206
3501
  continue;
3207
3502
  }
3208
- if (!metadata.cliName) {
3503
+ if (!metadata.displayName) {
3209
3504
  throw new Error(
3210
- `Skill at ${metadataFile} is missing required '${METADATA_KEYS.CLI_NAME}' field in metadata.yaml`
3505
+ `Skill at ${metadataFile} is missing required '${METADATA_KEYS.DISPLAY_NAME}' field in metadata.yaml`
3211
3506
  );
3212
3507
  }
3213
3508
  const skillId = frontmatter.name;
@@ -3217,14 +3512,8 @@ async function extractAllSkills(skillsDir) {
3217
3512
  description: metadata.cliDescription || frontmatter.description,
3218
3513
  usageGuidance: metadata.usageGuidance,
3219
3514
  category: metadata.category,
3220
- categoryExclusive: metadata.categoryExclusive ?? true,
3221
3515
  author: metadata.author,
3222
3516
  tags: metadata.tags ?? [],
3223
- compatibleWith: metadata.compatibleWith ?? [],
3224
- conflictsWith: metadata.conflictsWith ?? [],
3225
- requires: metadata.requires ?? [],
3226
- requiresSetup: metadata.requiresSetup ?? [],
3227
- providesSetupFor: metadata.providesSetupFor ?? [],
3228
3517
  path: `skills/${skillDir}/`,
3229
3518
  ...metadata.domain ? { domain: metadata.domain } : {},
3230
3519
  ...metadata.custom === true ? { custom: true } : {}
@@ -3271,25 +3560,36 @@ function resolveToCanonicalId(nameOrId, displayNameToId, directoryPathToId = {},
3271
3560
  }
3272
3561
  return nameOrId;
3273
3562
  }
3274
- async function mergeMatrixWithSkills(matrix, skills) {
3275
- const displayNameToId = matrix.skillAliases;
3563
+ async function mergeMatrixWithSkills(categories, relationships, aliases, skills, perSkillRules) {
3564
+ const displayNameToId = aliases;
3276
3565
  const displayNames = buildReverseDisplayNames(displayNameToId);
3277
3566
  const directoryPathToId = buildDirectoryPathToIdMap(skills);
3278
3567
  const resolvedSkills = {};
3279
3568
  for (const skill of skills) {
3280
3569
  const resolved = buildResolvedSkill(
3281
3570
  skill,
3282
- matrix,
3571
+ categories,
3572
+ relationships,
3283
3573
  displayNameToId,
3284
3574
  displayNames,
3285
- directoryPathToId
3575
+ directoryPathToId,
3576
+ perSkillRules
3286
3577
  );
3287
3578
  resolvedSkills[skill.id] = resolved;
3288
3579
  }
3580
+ const synthesizedCategories = { ...categories };
3581
+ for (const skill of skills) {
3582
+ const subcategory = skill.category;
3583
+ if (!synthesizedCategories[subcategory]) {
3584
+ const synthesized = synthesizeCategory(skill.category, skill.domain);
3585
+ synthesizedCategories[subcategory] = synthesized;
3586
+ verbose(`Auto-synthesized category '${skill.category}' for skill '${skill.id}'`);
3587
+ }
3588
+ }
3289
3589
  const suggestedStacks = resolveSuggestedStacks();
3290
3590
  const merged = {
3291
- version: matrix.version,
3292
- categories: matrix.categories,
3591
+ version: "1.0.0",
3592
+ categories: synthesizedCategories,
3293
3593
  skills: resolvedSkills,
3294
3594
  suggestedStacks,
3295
3595
  displayNameToId,
@@ -3382,41 +3682,48 @@ function resolveDiscourages(skillId, discourageRules, resolve) {
3382
3682
  }
3383
3683
  return discourages;
3384
3684
  }
3385
- function buildResolvedSkill(skill, matrix, displayNameToId, displayNames, directoryPathToId) {
3685
+ function buildResolvedSkill(skill, _categories, relationships, displayNameToId, displayNames, directoryPathToId, perSkillRules) {
3386
3686
  const resolve = (id, context) => resolveToCanonicalId(
3387
3687
  id,
3388
3688
  displayNameToId,
3389
3689
  directoryPathToId,
3390
3690
  context ? `${skill.id} ${context}` : void 0
3391
3691
  );
3392
- const { relationships } = matrix;
3692
+ const skillAlias = displayNames[skill.id];
3693
+ const perSkill = skillAlias ? perSkillRules?.[skillAlias] : void 0;
3393
3694
  return {
3394
3695
  id: skill.id,
3395
3696
  displayName: displayNames[skill.id],
3396
3697
  description: skill.description,
3397
3698
  usageGuidance: skill.usageGuidance,
3398
3699
  category: skill.category,
3399
- categoryExclusive: skill.categoryExclusive,
3400
3700
  tags: skill.tags,
3401
3701
  author: skill.author,
3402
3702
  conflictsWith: resolveConflicts(
3403
3703
  skill.id,
3404
- skill.conflictsWith,
3704
+ perSkill?.conflictsWith ?? [],
3405
3705
  relationships.conflicts,
3406
3706
  resolve
3407
3707
  ),
3408
3708
  recommends: resolveRecommends(
3409
3709
  skill.id,
3410
- skill.compatibleWith,
3710
+ perSkill?.compatibleWith ?? [],
3411
3711
  relationships.recommends,
3412
3712
  resolve
3413
3713
  ),
3414
- requires: resolveRequirements(skill.id, skill.requires, relationships.requires, resolve),
3714
+ requires: resolveRequirements(
3715
+ skill.id,
3716
+ perSkill?.requires ?? [],
3717
+ relationships.requires,
3718
+ resolve
3719
+ ),
3415
3720
  alternatives: resolveAlternatives(skill.id, relationships.alternatives, resolve),
3416
3721
  discourages: resolveDiscourages(skill.id, relationships.discourages, resolve),
3417
- compatibleWith: skill.compatibleWith.map((id) => resolve(id, "compatibleWith")),
3418
- requiresSetup: skill.requiresSetup.map((id) => resolve(id, "requiresSetup")),
3419
- providesSetupFor: skill.providesSetupFor.map((id) => resolve(id, "providesSetupFor")),
3722
+ compatibleWith: (perSkill?.compatibleWith ?? []).map((id) => resolve(id, "compatibleWith")),
3723
+ requiresSetup: (perSkill?.requiresSetup ?? []).map((id) => resolve(id, "requiresSetup")),
3724
+ providesSetupFor: (perSkill?.providesSetupFor ?? []).map(
3725
+ (id) => resolve(id, "providesSetupFor")
3726
+ ),
3420
3727
  path: skill.path
3421
3728
  };
3422
3729
  }
@@ -3438,26 +3745,31 @@ function initializeSelectionContext(currentSelections, matrix) {
3438
3745
  const selectedSet = new Set(resolvedSelections);
3439
3746
  return { resolvedSelections, selectedSet };
3440
3747
  }
3441
- function isDisabled(skillId, currentSelections, matrix, options) {
3442
- if (options?.expertMode) {
3443
- return false;
3444
- }
3748
+ function isDiscouraged(skillId, currentSelections, matrix) {
3445
3749
  const fullId = resolveAlias(skillId, matrix);
3446
3750
  const skill = matrix.skills[fullId];
3447
3751
  if (!skill) {
3448
3752
  return false;
3449
3753
  }
3450
- for (const selectedId of currentSelections) {
3451
- const selectedFullId = resolveAlias(selectedId, matrix);
3452
- if (skill.conflictsWith.some((c) => c.skillId === selectedFullId)) {
3754
+ const { resolvedSelections, selectedSet } = initializeSelectionContext(currentSelections, matrix);
3755
+ for (const selectedId of resolvedSelections) {
3756
+ const selectedSkill = matrix.skills[selectedId];
3757
+ if (selectedSkill?.discourages.some((d) => d.skillId === fullId)) {
3758
+ return true;
3759
+ }
3760
+ if (skill.discourages.some((d) => d.skillId === selectedId)) {
3761
+ return true;
3762
+ }
3763
+ }
3764
+ for (const selectedId of resolvedSelections) {
3765
+ if (skill.conflictsWith.some((c) => c.skillId === selectedId)) {
3453
3766
  return true;
3454
3767
  }
3455
- const selectedSkill = matrix.skills[selectedFullId];
3768
+ const selectedSkill = matrix.skills[selectedId];
3456
3769
  if (selectedSkill?.conflictsWith.some((c) => c.skillId === fullId)) {
3457
3770
  return true;
3458
3771
  }
3459
3772
  }
3460
- const { selectedSet } = initializeSelectionContext(currentSelections, matrix);
3461
3773
  for (const requirement of skill.requires) {
3462
3774
  if (requirement.needsAny) {
3463
3775
  const hasAny = requirement.skillIds.some((reqId) => selectedSet.has(reqId));
@@ -3473,13 +3785,26 @@ function isDisabled(skillId, currentSelections, matrix, options) {
3473
3785
  }
3474
3786
  return false;
3475
3787
  }
3476
- function getDisableReason(skillId, currentSelections, matrix) {
3788
+ function getDiscourageReason(skillId, currentSelections, matrix) {
3477
3789
  const fullId = resolveAlias(skillId, matrix);
3478
3790
  const skill = matrix.skills[fullId];
3479
3791
  if (!skill) {
3480
3792
  return void 0;
3481
3793
  }
3482
3794
  const { resolvedSelections, selectedSet } = initializeSelectionContext(currentSelections, matrix);
3795
+ for (const selectedId of resolvedSelections) {
3796
+ const selectedSkill = matrix.skills[selectedId];
3797
+ if (selectedSkill) {
3798
+ const discourage = selectedSkill.discourages.find((d) => d.skillId === fullId);
3799
+ if (discourage) {
3800
+ return discourage.reason;
3801
+ }
3802
+ }
3803
+ const reverseDiscourage = skill.discourages.find((d) => d.skillId === selectedId);
3804
+ if (reverseDiscourage) {
3805
+ return reverseDiscourage.reason;
3806
+ }
3807
+ }
3483
3808
  for (const selectedId of resolvedSelections) {
3484
3809
  const conflict = skill.conflictsWith.find((c) => c.skillId === selectedId);
3485
3810
  if (conflict) {
@@ -3511,46 +3836,6 @@ function getDisableReason(skillId, currentSelections, matrix) {
3511
3836
  }
3512
3837
  return void 0;
3513
3838
  }
3514
- function isDiscouraged(skillId, currentSelections, matrix) {
3515
- const fullId = resolveAlias(skillId, matrix);
3516
- const skill = matrix.skills[fullId];
3517
- if (!skill) {
3518
- return false;
3519
- }
3520
- const { resolvedSelections } = initializeSelectionContext(currentSelections, matrix);
3521
- for (const selectedId of resolvedSelections) {
3522
- const selectedSkill = matrix.skills[selectedId];
3523
- if (selectedSkill?.discourages.some((d) => d.skillId === fullId)) {
3524
- return true;
3525
- }
3526
- if (skill.discourages.some((d) => d.skillId === selectedId)) {
3527
- return true;
3528
- }
3529
- }
3530
- return false;
3531
- }
3532
- function getDiscourageReason(skillId, currentSelections, matrix) {
3533
- const fullId = resolveAlias(skillId, matrix);
3534
- const skill = matrix.skills[fullId];
3535
- if (!skill) {
3536
- return void 0;
3537
- }
3538
- const { resolvedSelections } = initializeSelectionContext(currentSelections, matrix);
3539
- for (const selectedId of resolvedSelections) {
3540
- const selectedSkill = matrix.skills[selectedId];
3541
- if (selectedSkill) {
3542
- const discourage = selectedSkill.discourages.find((d) => d.skillId === fullId);
3543
- if (discourage) {
3544
- return discourage.reason;
3545
- }
3546
- }
3547
- const reverseDiscourage = skill.discourages.find((d) => d.skillId === selectedId);
3548
- if (reverseDiscourage) {
3549
- return reverseDiscourage.reason;
3550
- }
3551
- }
3552
- return void 0;
3553
- }
3554
3839
  function isRecommended(skillId, currentSelections, matrix) {
3555
3840
  const fullId = resolveAlias(skillId, matrix);
3556
3841
  const skill = matrix.skills[fullId];
@@ -3713,7 +3998,7 @@ function validateSelection(selections, matrix) {
3713
3998
  warnings
3714
3999
  };
3715
4000
  }
3716
- function getAvailableSkills(categoryId, currentSelections, matrix, options) {
4001
+ function getAvailableSkills(categoryId, currentSelections, matrix) {
3717
4002
  const skillOptions = [];
3718
4003
  const { selectedSet } = initializeSelectionContext(currentSelections, matrix);
3719
4004
  for (const skill of Object.values(matrix.skills)) {
@@ -3721,15 +4006,12 @@ function getAvailableSkills(categoryId, currentSelections, matrix, options) {
3721
4006
  if (skill.category !== categoryId) {
3722
4007
  continue;
3723
4008
  }
3724
- const disabled = isDisabled(skill.id, currentSelections, matrix, options);
3725
- const discouraged = !disabled && isDiscouraged(skill.id, currentSelections, matrix);
3726
- const recommended = !disabled && !discouraged && isRecommended(skill.id, currentSelections, matrix);
4009
+ const discouraged = isDiscouraged(skill.id, currentSelections, matrix);
4010
+ const recommended = !discouraged && isRecommended(skill.id, currentSelections, matrix);
3727
4011
  skillOptions.push({
3728
4012
  id: skill.id,
3729
4013
  displayName: skill.displayName,
3730
4014
  description: skill.description,
3731
- disabled,
3732
- disabledReason: disabled ? getDisableReason(skill.id, currentSelections, matrix) : void 0,
3733
4015
  discouraged,
3734
4016
  discouragedReason: discouraged ? getDiscourageReason(skill.id, currentSelections, matrix) : void 0,
3735
4017
  recommended,
@@ -3768,12 +4050,15 @@ function checkSubcategoryDomains(matrix, issues) {
3768
4050
  function checkSkillCategories(matrix, issues) {
3769
4051
  for (const [skillId, skill] of typedEntries(matrix.skills)) {
3770
4052
  if (!skill) continue;
3771
- if (!matrix.categories[skill.category]) {
4053
+ const category = matrix.categories[skill.category];
4054
+ if (!category) {
3772
4055
  issues.push({
3773
4056
  severity: "warning",
3774
4057
  finding: "skill-unknown-category",
3775
4058
  details: `Skill '${skillId}' references category '${skill.category}' which does not exist in the matrix`
3776
4059
  });
4060
+ } else if (category.custom) {
4061
+ verbose(`Skill '${skillId}' uses auto-synthesized category '${skill.category}'`);
3777
4062
  }
3778
4063
  }
3779
4064
  }
@@ -3821,7 +4106,7 @@ function checkSkillRelationRefs(matrix, issues) {
3821
4106
 
3822
4107
  // src/cli/lib/loading/multi-source-loader.ts
3823
4108
  init_esm_shims();
3824
- import path20 from "path";
4109
+ import path21 from "path";
3825
4110
  async function loadSkillsFromAllSources(primaryMatrix, sourceConfig, projectDir, forceRefresh = false, marketplace) {
3826
4111
  const resolvedMarketplace = marketplace ?? sourceConfig.marketplace;
3827
4112
  const isDefaultPublicSource = sourceConfig.source === DEFAULT_SOURCE;
@@ -3910,7 +4195,7 @@ async function tagPublicSourceSkills(matrix, forceRefresh) {
3910
4195
  }
3911
4196
  try {
3912
4197
  const fetchResult = await fetchFromSource(DEFAULT_SOURCE, { forceRefresh });
3913
- const skillsDir = path20.join(fetchResult.path, SKILLS_DIR_PATH);
4198
+ const skillsDir = path21.join(fetchResult.path, SKILLS_DIR_PATH);
3914
4199
  const publicSkills = await extractAllSkills(skillsDir);
3915
4200
  let matchCount = 0;
3916
4201
  for (const publicSkill of publicSkills) {
@@ -3948,7 +4233,7 @@ async function tagExtraSources(matrix, projectDir, forceRefresh) {
3948
4233
  verbose(`Loading extra source: ${extraSource.name} (${extraSource.url})`);
3949
4234
  try {
3950
4235
  const fetchResult = await fetchFromSource(extraSource.url, { forceRefresh });
3951
- const skillsDir = path20.join(fetchResult.path, SKILLS_DIR_PATH);
4236
+ const skillsDir = path21.join(fetchResult.path, SKILLS_DIR_PATH);
3952
4237
  const skills = await extractAllSkills(skillsDir);
3953
4238
  let matchCount = 0;
3954
4239
  for (const extractedSkill of skills) {
@@ -3991,7 +4276,7 @@ async function searchExtraSources(alias, configuredSources) {
3991
4276
  for (const source of configuredSources) {
3992
4277
  try {
3993
4278
  const fetchResult = await fetchFromSource(source.url, { forceRefresh: false });
3994
- const skillsDir = path20.join(fetchResult.path, SKILLS_DIR_PATH);
4279
+ const skillsDir = path21.join(fetchResult.path, SKILLS_DIR_PATH);
3995
4280
  const skills = await extractAllSkills(skillsDir);
3996
4281
  for (const skill of skills) {
3997
4282
  const segments = skill.directoryPath.split("/");
@@ -4029,7 +4314,11 @@ async function loadSkillsMatrixFromSource(options = {}) {
4029
4314
  result = await loadFromRemote(source, sourceConfig, forceRefresh);
4030
4315
  }
4031
4316
  const resolvedProjectDir = projectDir || process.cwd();
4032
- const localSkillsResult = await discoverLocalSkills(resolvedProjectDir);
4317
+ let localSkillsResult = await discoverLocalSkills(resolvedProjectDir);
4318
+ const homeDir = os6.homedir();
4319
+ if ((!localSkillsResult || localSkillsResult.skills.length === 0) && resolvedProjectDir !== homeDir) {
4320
+ localSkillsResult = await discoverLocalSkills(homeDir);
4321
+ }
4033
4322
  if (localSkillsResult && localSkillsResult.skills.length > 0) {
4034
4323
  verbose(
4035
4324
  `Found ${localSkillsResult.skills.length} local skill(s) in ${localSkillsResult.localSkillsPath}`
@@ -4049,7 +4338,7 @@ async function loadSkillsMatrixFromSource(options = {}) {
4049
4338
  async function loadFromLocal(source, sourceConfig) {
4050
4339
  let skillsPath;
4051
4340
  if (isLocalSource(source)) {
4052
- skillsPath = path21.isAbsolute(source) ? source : path21.resolve(process.cwd(), source);
4341
+ skillsPath = path22.isAbsolute(source) ? source : path22.resolve(process.cwd(), source);
4053
4342
  } else {
4054
4343
  skillsPath = PROJECT_ROOT;
4055
4344
  }
@@ -4088,47 +4377,62 @@ async function loadFromRemote(source, sourceConfig, forceRefresh) {
4088
4377
  }
4089
4378
  async function loadAndMergeFromBasePath(basePath) {
4090
4379
  const sourceProjectConfig = await loadProjectSourceConfig(basePath);
4091
- const matrixRelPath = sourceProjectConfig?.matrixFile ?? SKILLS_MATRIX_PATH;
4092
4380
  const skillsDirRelPath = sourceProjectConfig?.skillsDir ?? SKILLS_DIR_PATH;
4093
4381
  const stacksRelFile = sourceProjectConfig?.stacksFile;
4094
- const cliMatrixPath = path21.join(PROJECT_ROOT, SKILLS_MATRIX_PATH);
4095
- const cliMatrix = await loadSkillsMatrix(cliMatrixPath);
4096
- let matrix = cliMatrix;
4382
+ const cliCategoriesPath = path22.join(PROJECT_ROOT, SKILL_CATEGORIES_YAML_PATH);
4383
+ const cliRulesPath = path22.join(PROJECT_ROOT, SKILL_RULES_YAML_PATH);
4384
+ const cliCategories = await loadSkillCategories(cliCategoriesPath);
4385
+ const cliRules = await loadSkillRules(cliRulesPath);
4386
+ let categories = cliCategories;
4387
+ let relationships = cliRules.relationships;
4388
+ let aliases = cliRules.aliases;
4389
+ let perSkillRules = cliRules.perSkill;
4097
4390
  await discoverAndExtendFromSource(basePath);
4098
- const sourceMatrixPath = path21.join(basePath, matrixRelPath);
4099
- if (await fileExists(sourceMatrixPath)) {
4100
- const sourceMatrix = await loadSkillsMatrix(sourceMatrixPath);
4101
- const mergedCategories = { ...cliMatrix.categories, ...sourceMatrix.categories };
4102
- const mergedRelationships = {
4103
- conflicts: [...cliMatrix.relationships.conflicts, ...sourceMatrix.relationships.conflicts],
4104
- discourages: [
4105
- ...cliMatrix.relationships.discourages,
4106
- ...sourceMatrix.relationships.discourages
4107
- ],
4108
- recommends: [...cliMatrix.relationships.recommends, ...sourceMatrix.relationships.recommends],
4109
- requires: [...cliMatrix.relationships.requires, ...sourceMatrix.relationships.requires],
4110
- alternatives: [
4111
- ...cliMatrix.relationships.alternatives,
4112
- ...sourceMatrix.relationships.alternatives
4113
- ]
4114
- };
4115
- const mergedAliases = { ...cliMatrix.skillAliases, ...sourceMatrix.skillAliases };
4116
- matrix = {
4117
- version: cliMatrix.version,
4118
- categories: mergedCategories,
4119
- relationships: mergedRelationships,
4120
- skillAliases: mergedAliases
4121
- };
4122
- verbose(
4123
- `Matrix merged: CLI (${typedKeys(cliMatrix.categories).length} categories) + source (${typedKeys(sourceMatrix.categories).length} categories)`
4124
- );
4391
+ const sourceCategoriesPath = path22.join(basePath, SKILL_CATEGORIES_YAML_PATH);
4392
+ const sourceRulesPath = path22.join(basePath, SKILL_RULES_YAML_PATH);
4393
+ const hasSourceCategories = await fileExists(sourceCategoriesPath);
4394
+ const hasSourceRules = await fileExists(sourceRulesPath);
4395
+ if (hasSourceCategories || hasSourceRules) {
4396
+ if (hasSourceCategories) {
4397
+ const sourceCategories = await loadSkillCategories(sourceCategoriesPath);
4398
+ categories = { ...cliCategories, ...sourceCategories };
4399
+ verbose(
4400
+ `Loaded source categories: ${sourceCategoriesPath} (${typedKeys(sourceCategories).length} categories)`
4401
+ );
4402
+ }
4403
+ if (hasSourceRules) {
4404
+ const sourceRules = await loadSkillRules(sourceRulesPath);
4405
+ relationships = {
4406
+ conflicts: [...cliRules.relationships.conflicts, ...sourceRules.relationships.conflicts],
4407
+ discourages: [
4408
+ ...cliRules.relationships.discourages,
4409
+ ...sourceRules.relationships.discourages
4410
+ ],
4411
+ recommends: [...cliRules.relationships.recommends, ...sourceRules.relationships.recommends],
4412
+ requires: [...cliRules.relationships.requires, ...sourceRules.relationships.requires],
4413
+ alternatives: [
4414
+ ...cliRules.relationships.alternatives,
4415
+ ...sourceRules.relationships.alternatives
4416
+ ]
4417
+ };
4418
+ aliases = { ...cliRules.aliases, ...sourceRules.aliases };
4419
+ perSkillRules = { ...cliRules.perSkill, ...sourceRules.perSkill };
4420
+ verbose(`Loaded source rules: ${sourceRulesPath}`);
4421
+ }
4422
+ verbose(`Matrix merged: CLI (${typedKeys(cliCategories).length} categories) + source`);
4125
4423
  } else {
4126
- verbose(`Matrix from CLI only (source has no matrix): ${cliMatrixPath}`);
4424
+ verbose(`Matrix from CLI only (source has no categories/rules files)`);
4127
4425
  }
4128
- const skillsDir = path21.join(basePath, skillsDirRelPath);
4426
+ const skillsDir = path22.join(basePath, skillsDirRelPath);
4129
4427
  verbose(`Skills from source: ${skillsDir}`);
4130
4428
  const skills = await extractAllSkills(skillsDir);
4131
- const mergedMatrix = await mergeMatrixWithSkills(matrix, skills);
4429
+ const mergedMatrix = await mergeMatrixWithSkills(
4430
+ categories,
4431
+ relationships,
4432
+ aliases,
4433
+ skills,
4434
+ perSkillRules
4435
+ );
4132
4436
  const sourceStacks = await loadStacks(basePath, stacksRelFile);
4133
4437
  const stacks = sourceStacks.length > 0 ? sourceStacks : await loadStacks(PROJECT_ROOT);
4134
4438
  if (stacks.length > 0) {
@@ -4206,9 +4510,9 @@ function getMarketplaceLabel(sourceResult) {
4206
4510
  }
4207
4511
  return marketplace;
4208
4512
  }
4209
- async function discoverCustomAgentValues(agentsDir, file, parseYaml9, builtinDomains) {
4210
- const content = await readFile(path21.join(agentsDir, file));
4211
- const raw = parseYaml9(content);
4513
+ async function discoverCustomAgentValues(agentsDir, file, parseYaml10, builtinDomains) {
4514
+ const content = await readFile(path22.join(agentsDir, file));
4515
+ const raw = parseYaml10(content);
4212
4516
  if (raw?.custom !== true) return {};
4213
4517
  const result = {};
4214
4518
  if (typeof raw?.id === "string" && !agentNameSchema.safeParse(raw.id).success) {
@@ -4219,16 +4523,16 @@ async function discoverCustomAgentValues(agentsDir, file, parseYaml9, builtinDom
4219
4523
  }
4220
4524
  return result;
4221
4525
  }
4222
- async function discoverCustomSkillValues(skillsDir, file, parseYaml9, builtinSubcategories, builtinDomains) {
4223
- const content = await readFile(path21.join(skillsDir, file));
4526
+ async function discoverCustomSkillValues(skillsDir, file, parseYaml10, builtinSubcategories, builtinDomains) {
4527
+ const content = await readFile(path22.join(skillsDir, file));
4224
4528
  const frontmatter = parseFrontmatter(content);
4225
4529
  if (!frontmatter) return {};
4226
4530
  const skillId = frontmatter.name;
4227
- const skillDir = path21.dirname(path21.join(skillsDir, file));
4228
- const metadataPath = path21.join(skillDir, STANDARD_FILES.METADATA_YAML);
4531
+ const skillDir = path22.dirname(path22.join(skillsDir, file));
4532
+ const metadataPath = path22.join(skillDir, STANDARD_FILES.METADATA_YAML);
4229
4533
  if (!await fileExists(metadataPath)) return {};
4230
4534
  const metadataContent = await readFile(metadataPath);
4231
- const metadataRaw = parseYaml9(metadataContent);
4535
+ const metadataRaw = parseYaml10(metadataContent);
4232
4536
  if (metadataRaw?.custom !== true) return {};
4233
4537
  const result = {};
4234
4538
  result.skillId = skillId;
@@ -4243,26 +4547,26 @@ async function discoverCustomSkillValues(skillsDir, file, parseYaml9, builtinSub
4243
4547
  return result;
4244
4548
  }
4245
4549
  async function discoverAndExtendFromSource(basePath) {
4246
- const { parse: parseYaml9 } = await import("yaml");
4550
+ const { parse: parseYaml10 } = await import("yaml");
4247
4551
  const builtinSubcategories = new Set(SUBCATEGORY_VALUES);
4248
4552
  const builtinDomains = new Set(DOMAIN_VALUES);
4249
4553
  const customCategories = [];
4250
4554
  const customDomains = [];
4251
4555
  const customAgentNames = [];
4252
4556
  const customSkillIds = [];
4253
- const agentsDir = path21.join(basePath, DIRS.agents);
4557
+ const agentsDir = path22.join(basePath, DIRS.agents);
4254
4558
  if (await directoryExists(agentsDir)) {
4255
- const agentFiles = await glob(`**/${STANDARD_FILES.AGENT_YAML}`, agentsDir);
4559
+ const agentFiles = await glob(`**/${STANDARD_FILES.AGENT_METADATA_YAML}`, agentsDir);
4256
4560
  for (const file of agentFiles) {
4257
4561
  try {
4258
- const result = await discoverCustomAgentValues(agentsDir, file, parseYaml9, builtinDomains);
4562
+ const result = await discoverCustomAgentValues(agentsDir, file, parseYaml10, builtinDomains);
4259
4563
  if (result.agentName) customAgentNames.push(result.agentName);
4260
4564
  if (result.domain) customDomains.push(result.domain);
4261
4565
  } catch {
4262
4566
  }
4263
4567
  }
4264
4568
  }
4265
- const skillsDir = path21.join(basePath, DIRS.skills);
4569
+ const skillsDir = path22.join(basePath, DIRS.skills);
4266
4570
  if (await directoryExists(skillsDir)) {
4267
4571
  const skillFiles = await glob(`**/${STANDARD_FILES.SKILL_MD}`, skillsDir);
4268
4572
  for (const file of skillFiles) {
@@ -4270,7 +4574,7 @@ async function discoverAndExtendFromSource(basePath) {
4270
4574
  const result = await discoverCustomSkillValues(
4271
4575
  skillsDir,
4272
4576
  file,
4273
- parseYaml9,
4577
+ parseYaml10,
4274
4578
  builtinSubcategories,
4275
4579
  builtinDomains
4276
4580
  );
@@ -4305,7 +4609,6 @@ function mergeLocalSkillsIntoMatrix(matrix, localResult) {
4305
4609
  description: metadata.description,
4306
4610
  usageGuidance: metadata.usageGuidance,
4307
4611
  category,
4308
- categoryExclusive: metadata.categoryExclusive,
4309
4612
  tags: metadata.tags ?? [],
4310
4613
  author: LOCAL_DEFAULTS.AUTHOR,
4311
4614
  conflictsWith: existingSkill?.conflictsWith ?? [],
@@ -4328,7 +4631,7 @@ function mergeLocalSkillsIntoMatrix(matrix, localResult) {
4328
4631
 
4329
4632
  // src/cli/lib/skills/local-skill-loader.ts
4330
4633
  async function discoverLocalSkills(projectDir) {
4331
- const localSkillsPath = path22.join(projectDir, LOCAL_SKILLS_PATH);
4634
+ const localSkillsPath = path23.join(projectDir, LOCAL_SKILLS_PATH);
4332
4635
  if (!await directoryExists(localSkillsPath)) {
4333
4636
  verbose(`Local skills directory not found: ${localSkillsPath}`);
4334
4637
  return null;
@@ -4348,9 +4651,9 @@ async function discoverLocalSkills(projectDir) {
4348
4651
  };
4349
4652
  }
4350
4653
  async function extractLocalSkill(localSkillsPath, skillDirName) {
4351
- const skillDir = path22.join(localSkillsPath, skillDirName);
4352
- const metadataPath = path22.join(skillDir, STANDARD_FILES.METADATA_YAML);
4353
- const skillMdPath = path22.join(skillDir, STANDARD_FILES.SKILL_MD);
4654
+ const skillDir = path23.join(localSkillsPath, skillDirName);
4655
+ const metadataPath = path23.join(skillDir, STANDARD_FILES.METADATA_YAML);
4656
+ const skillMdPath = path23.join(skillDir, STANDARD_FILES.SKILL_MD);
4354
4657
  if (!await fileExists(metadataPath)) {
4355
4658
  verbose(`Skipping local skill '${skillDirName}': No metadata.yaml found`);
4356
4659
  return null;
@@ -4360,24 +4663,23 @@ async function extractLocalSkill(localSkillsPath, skillDirName) {
4360
4663
  return null;
4361
4664
  }
4362
4665
  const metadataContent = await readFile(metadataPath);
4363
- const parsed = localRawMetadataSchema.safeParse(parseYaml8(metadataContent));
4666
+ const parsed = localRawMetadataSchema.safeParse(parseYaml9(metadataContent));
4364
4667
  if (!parsed.success) {
4365
- warn(
4668
+ verbose(
4366
4669
  `Skipping local skill '${skillDirName}': invalid metadata.yaml \u2014 ${formatZodErrors(parsed.error.issues)}`
4367
4670
  );
4368
4671
  return null;
4369
4672
  }
4370
4673
  const metadata = parsed.data;
4371
- if (!metadata.cliName) {
4372
- warn(
4373
- `Skipping local skill '${skillDirName}': missing required '${METADATA_KEYS.CLI_NAME}' in metadata.yaml`
4674
+ if (!metadata.displayName) {
4675
+ throw new Error(
4676
+ `Local skill '${skillDirName}' is missing required '${METADATA_KEYS.DISPLAY_NAME}' field in metadata.yaml`
4374
4677
  );
4375
- return null;
4376
4678
  }
4377
4679
  const skillMdContent = await readFile(skillMdPath);
4378
4680
  const frontmatter = parseFrontmatter(skillMdContent, skillMdPath);
4379
4681
  if (!frontmatter) {
4380
- warn(`Skipping local skill '${skillDirName}': invalid SKILL.md frontmatter`);
4682
+ verbose(`Skipping local skill '${skillDirName}': invalid SKILL.md frontmatter`);
4381
4683
  return null;
4382
4684
  }
4383
4685
  const relativePath = `${LOCAL_SKILLS_PATH}/${skillDirName}/`;
@@ -4394,14 +4696,8 @@ async function extractLocalSkill(localSkillsPath, skillDirName) {
4394
4696
  description: metadata.cliDescription || frontmatter.description,
4395
4697
  usageGuidance: metadata.usageGuidance,
4396
4698
  category,
4397
- categoryExclusive: metadata.categoryExclusive ?? false,
4398
4699
  author: LOCAL_DEFAULTS.AUTHOR,
4399
4700
  tags: metadata.tags ?? [],
4400
- compatibleWith: metadata.compatibleWith ?? [],
4401
- conflictsWith: metadata.conflictsWith ?? [],
4402
- requires: metadata.requires ?? [],
4403
- requiresSetup: metadata.requiresSetup ?? [],
4404
- providesSetupFor: metadata.providesSetupFor ?? [],
4405
4701
  path: relativePath,
4406
4702
  local: true,
4407
4703
  localPath: relativePath
@@ -4518,7 +4814,8 @@ export {
4518
4814
  printCompilationSummary,
4519
4815
  archiveLocalSkill,
4520
4816
  restoreArchivedSkill,
4521
- loadSkillsMatrix,
4817
+ loadSkillCategories,
4818
+ loadSkillRules,
4522
4819
  extractAllSkills,
4523
4820
  mergeMatrixWithSkills,
4524
4821
  resolveAlias,
@@ -4542,13 +4839,16 @@ export {
4542
4839
  addSource,
4543
4840
  removeSource,
4544
4841
  getSourceSummary,
4842
+ detectProjectInstallation,
4545
4843
  detectInstallation,
4546
4844
  installPluginConfig,
4547
4845
  installLocal,
4548
4846
  getInstallationInfo,
4549
4847
  formatInstallationDisplay,
4848
+ validateAllSchemas,
4849
+ printValidationResults,
4550
4850
  validatePlugin,
4551
4851
  validateAllPlugins,
4552
4852
  printPluginValidationResult
4553
4853
  };
4554
- //# sourceMappingURL=chunk-GFDGYQ6M.js.map
4854
+ //# sourceMappingURL=chunk-HMSHB5EQ.js.map