@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
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/commands/validate.ts","../../src/cli/lib/schema-validator.ts","../../src/cli/lib/source-validator.ts"],"sourcesContent":["import { Args, Flags } from \"@oclif/core\";\nimport path from \"path\";\nimport { BaseCommand } from \"../base-command.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport { EXIT_CODES } from \"../lib/exit-codes.js\";\nimport { ERROR_MESSAGES } from \"../utils/messages.js\";\nimport { validateAllSchemas, printValidationResults } from \"../lib/schema-validator.js\";\nimport {\n validatePlugin,\n validateAllPlugins,\n printPluginValidationResult,\n} from \"../lib/plugins/index.js\";\nimport { validateSource } from \"../lib/source-validator.js\";\n\nexport default class Validate extends BaseCommand {\n static summary =\n \"Validate YAML files against schemas, validate compiled plugins, or validate a skills source\";\n static description =\n \"Validates skill/agent YAML files against JSON schemas, validates compiled plugin structure and content, \" +\n \"or validates a skills source repository for metadata correctness. \" +\n \"Without arguments, validates all YAML files in the current directory against their schemas. \" +\n \"With --source, validates all skills in the source for schema compliance, cross-references, and conventions. \" +\n \"With a path argument or --plugins flag, validates plugin(s) instead.\";\n\n static examples = [\n {\n description: \"Validate all YAML schemas\",\n command: \"<%= config.bin %> <%= command.id %>\",\n },\n {\n description: \"Validate a skills source repository\",\n command: \"<%= config.bin %> <%= command.id %> --source .\",\n },\n {\n description: \"Validate a remote skills source\",\n command: \"<%= config.bin %> <%= command.id %> --source github:acme-corp/skills\",\n },\n {\n description: \"Validate a specific plugin\",\n command: \"<%= config.bin %> <%= command.id %> ./path/to/plugin\",\n },\n {\n description: \"Validate all plugins in a directory\",\n command: \"<%= config.bin %> <%= command.id %> ./plugins --all\",\n },\n {\n description: \"Validate plugins with verbose output\",\n command: \"<%= config.bin %> <%= command.id %> --plugins --verbose\",\n },\n ];\n\n static args = {\n path: Args.string({\n description: \"Path to plugin or plugins directory to validate\",\n required: false,\n }),\n };\n\n static flags = {\n ...BaseCommand.baseFlags,\n verbose: Flags.boolean({\n char: \"v\",\n description: \"Enable verbose logging\",\n default: false,\n }),\n all: Flags.boolean({\n char: \"a\",\n description: \"Validate all plugins in directory\",\n default: false,\n }),\n plugins: Flags.boolean({\n char: \"p\",\n description: \"Validate plugins instead of schemas\",\n default: false,\n }),\n };\n\n async run(): Promise<void> {\n const { args, flags } = await this.parse(Validate);\n\n if (flags.source) {\n await this.validateSkillsSource(flags.source);\n } else if (args.path || flags.plugins) {\n await this.validatePlugins(args.path, flags.verbose, flags.all);\n } else {\n await this.validateSchemas();\n }\n }\n\n private async validateSchemas(): Promise<void> {\n this.log(\"\");\n this.log(\"Validating all schemas\");\n this.log(\"\");\n\n try {\n const result = await validateAllSchemas();\n\n const summary = result.valid\n ? `Done: ${result.summary.validFiles}/${result.summary.totalFiles} files valid`\n : `Done: ${result.summary.invalidFiles} invalid files`;\n\n this.log(summary);\n printValidationResults(result);\n\n if (!result.valid) {\n this.exit(EXIT_CODES.ERROR);\n }\n } catch (error) {\n const message = getErrorMessage(error);\n this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });\n }\n }\n\n private async validatePlugins(\n pluginPath: string | undefined,\n verbose: boolean,\n all: boolean,\n ): Promise<void> {\n const targetPath = pluginPath ? path.resolve(pluginPath) : process.cwd();\n\n if (all) {\n await this.validateAllPluginsInDirectory(targetPath, verbose);\n } else {\n await this.validateSinglePlugin(targetPath, verbose);\n }\n }\n\n private async validateAllPluginsInDirectory(targetPath: string, verbose: boolean): Promise<void> {\n this.log(\"\");\n this.log(`Validating all plugins in: ${targetPath}`);\n this.log(\"\");\n\n try {\n const result = await validateAllPlugins(targetPath);\n\n const summary = result.valid\n ? `Done: ${result.summary.valid}/${result.summary.total} plugins valid`\n : `Done: ${result.summary.invalid} invalid plugins`;\n\n this.log(summary);\n\n this.log(\"\");\n this.log(\" Plugin Validation Summary:\");\n this.log(\" -------------------------\");\n this.log(` Total plugins: ${result.summary.total}`);\n this.log(` Valid: ${result.summary.valid}`);\n this.log(` Invalid: ${result.summary.invalid}`);\n this.log(` With warnings: ${result.summary.withWarnings}`);\n\n for (const { name, result: pluginResult } of result.results) {\n printPluginValidationResult(name, pluginResult, verbose);\n }\n\n if (result.valid) {\n this.log(\"\");\n this.logSuccess(\"All plugins validated successfully\");\n this.log(\"\");\n } else {\n this.log(\"\");\n this.error(ERROR_MESSAGES.VALIDATION_FAILED, { exit: EXIT_CODES.ERROR });\n }\n } catch (error) {\n const message = getErrorMessage(error);\n this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });\n }\n }\n\n private async validateSinglePlugin(targetPath: string, _verbose: boolean): Promise<void> {\n this.log(\"\");\n this.log(`Validating plugin: ${targetPath}`);\n this.log(\"\");\n\n try {\n const result = await validatePlugin(targetPath);\n\n const summary = result.valid ? \"Done: Plugin is valid\" : \"Done: Plugin has errors\";\n\n this.log(summary);\n\n printPluginValidationResult(path.basename(targetPath), result, true);\n\n if (result.valid && result.warnings.length === 0) {\n this.log(\"\");\n this.logSuccess(\"Plugin validated successfully\");\n this.log(\"\");\n } else if (result.valid) {\n this.log(\"\");\n this.logWarning(\"Plugin valid with warnings\");\n this.log(\"\");\n } else {\n this.log(\"\");\n this.error(ERROR_MESSAGES.VALIDATION_FAILED, { exit: EXIT_CODES.ERROR });\n }\n } catch (error) {\n const message = getErrorMessage(error);\n this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });\n }\n }\n\n private async validateSkillsSource(source: string): Promise<void> {\n this.log(\"\");\n this.log(`Validating source: ${source}`);\n this.log(\"\");\n\n try {\n const result = await validateSource(source);\n\n this.log(`Checked ${result.skillCount} skill(s)`);\n this.log(\"\");\n\n for (const issue of result.issues) {\n const prefix = issue.severity === \"error\" ? \"ERROR\" : \"WARN\";\n this.log(` [${prefix}] ${issue.file}: ${issue.message}`);\n }\n\n if (result.issues.length > 0) {\n this.log(\"\");\n }\n\n this.log(`Result: ${result.errorCount} error(s), ${result.warningCount} warning(s)`);\n\n if (result.errorCount > 0) {\n this.log(\"\");\n this.error(ERROR_MESSAGES.VALIDATION_FAILED, { exit: EXIT_CODES.ERROR });\n } else if (result.warningCount > 0) {\n this.log(\"\");\n this.logWarning(\"Source valid with warnings\");\n this.log(\"\");\n } else {\n this.log(\"\");\n this.logSuccess(\"Source validated successfully\");\n this.log(\"\");\n }\n } catch (error) {\n const message = getErrorMessage(error);\n this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });\n }\n }\n}\n","import { sumBy } from \"remeda\";\nimport { z } from \"zod\";\nimport path from \"path\";\nimport { getErrorMessage } from \"../utils/errors\";\nimport { readFile, fileExists } from \"../utils/fs\";\nimport { parse as parseYaml } from \"yaml\";\nimport fg from \"fast-glob\";\nimport { extractFrontmatter } from \"../utils/frontmatter\";\nimport { log } from \"../utils/logger\";\nimport {\n skillsMatrixConfigSchema,\n metadataValidationSchema,\n stackConfigValidationSchema,\n skillFrontmatterValidationSchema,\n agentFrontmatterValidationSchema,\n agentYamlGenerationSchema,\n stacksConfigSchema,\n projectSourceConfigSchema,\n pluginManifestSchema,\n} from \"./schemas\";\nimport { CLAUDE_DIR, CLAUDE_SRC_DIR, STANDARD_FILES } from \"../consts\";\n\ntype FileValidationError = {\n file: string;\n errors: string[];\n};\n\ntype SchemaValidationResult = {\n schemaName: string;\n valid: boolean;\n totalFiles: number;\n validFiles: number;\n invalidFiles: FileValidationError[];\n};\n\nexport type FullValidationResult = {\n valid: boolean;\n results: SchemaValidationResult[];\n summary: {\n totalSchemas: number;\n totalFiles: number;\n validFiles: number;\n invalidFiles: number;\n };\n};\n\ntype ContentExtractor = (content: string) => unknown | null;\n\ntype ValidationTarget = {\n name: string;\n schema: z.ZodType<unknown>;\n pattern: string;\n baseDir: string;\n extractor?: ContentExtractor;\n};\n\nconst VALIDATION_TARGETS: ValidationTarget[] = [\n {\n name: \"Skills Matrix\",\n schema: skillsMatrixConfigSchema,\n pattern: STANDARD_FILES.SKILLS_MATRIX_YAML,\n baseDir: \"src/config\",\n },\n {\n name: \"Skill Metadata\",\n schema: metadataValidationSchema,\n pattern: `**/${STANDARD_FILES.METADATA_YAML}`,\n baseDir: \"src/skills\",\n },\n {\n name: \"Stack Skill Metadata\",\n schema: metadataValidationSchema,\n pattern: `**/skills/**/${STANDARD_FILES.METADATA_YAML}`,\n baseDir: \"src/stacks\",\n },\n {\n name: \"Stack Config\",\n schema: stackConfigValidationSchema,\n pattern: `*/${STANDARD_FILES.CONFIG_YAML}`,\n baseDir: \"src/stacks\",\n },\n {\n name: \"Agent Definition\",\n schema: agentYamlGenerationSchema,\n pattern: `**/${STANDARD_FILES.AGENT_YAML}`,\n baseDir: \"src/agents\",\n },\n {\n name: \"Skill Frontmatter\",\n schema: skillFrontmatterValidationSchema,\n pattern: `**/${STANDARD_FILES.SKILL_MD}`,\n baseDir: \"src/skills\",\n extractor: extractFrontmatter,\n },\n {\n name: \"Stack Skill Frontmatter\",\n schema: skillFrontmatterValidationSchema,\n pattern: `**/skills/**/${STANDARD_FILES.SKILL_MD}`,\n baseDir: \"src/stacks\",\n extractor: extractFrontmatter,\n },\n {\n name: \"Stacks Config\",\n schema: stacksConfigSchema,\n pattern: \"stacks.yaml\",\n baseDir: \"config\",\n },\n {\n name: \"Project Source Config\",\n schema: projectSourceConfigSchema,\n pattern: STANDARD_FILES.CONFIG_YAML,\n baseDir: CLAUDE_SRC_DIR,\n },\n {\n name: \"Project Skill Metadata\",\n schema: metadataValidationSchema,\n pattern: `*/${STANDARD_FILES.METADATA_YAML}`,\n baseDir: `${CLAUDE_DIR}/skills`,\n },\n {\n name: \"Project Skill Frontmatter\",\n schema: skillFrontmatterValidationSchema,\n pattern: `*/${STANDARD_FILES.SKILL_MD}`,\n baseDir: `${CLAUDE_DIR}/skills`,\n extractor: extractFrontmatter,\n },\n {\n name: \"Project Agent Frontmatter\",\n schema: agentFrontmatterValidationSchema,\n pattern: \"*.md\",\n baseDir: `${CLAUDE_DIR}/agents`,\n extractor: extractFrontmatter,\n },\n {\n name: \"Plugin Manifest\",\n schema: pluginManifestSchema,\n pattern: `*/${STANDARD_FILES.PLUGIN_JSON}`,\n baseDir: `${CLAUDE_DIR}/plugins`,\n extractor: (content: string) => JSON.parse(content) as unknown,\n },\n];\n\nfunction formatZodErrors(error: z.ZodError): string[] {\n return error.issues.map((issue) => {\n const path = issue.path.join(\".\");\n if (issue.code === \"unrecognized_keys\") {\n return `Unrecognized key: \"${issue.keys.join('\", \"')}\"`;\n }\n return path ? `${path}: ${issue.message}` : issue.message;\n });\n}\n\nasync function validateFile(\n filePath: string,\n schema: z.ZodType<unknown>,\n extractor?: ContentExtractor,\n): Promise<{ valid: boolean; errors: string[] }> {\n try {\n if (!(await fileExists(filePath))) {\n return { valid: false, errors: [`File not found: ${filePath}`] };\n }\n\n const content = await readFile(filePath);\n\n let parsed: unknown;\n if (extractor) {\n parsed = extractor(content);\n if (parsed === null) {\n return {\n valid: false,\n errors: [\"Failed to extract content (no valid frontmatter found)\"],\n };\n }\n } else {\n parsed = parseYaml(content);\n }\n\n const result = schema.safeParse(parsed);\n\n if (result.success) {\n return { valid: true, errors: [] };\n }\n\n return { valid: false, errors: formatZodErrors(result.error) };\n } catch (error) {\n const message = getErrorMessage(error);\n return { valid: false, errors: [`Failed to parse content: ${message}`] };\n }\n}\n\nasync function validateTarget(\n target: ValidationTarget,\n rootDir: string = process.cwd(),\n): Promise<SchemaValidationResult> {\n const baseDir = path.join(rootDir, target.baseDir);\n const pattern = path.join(baseDir, target.pattern);\n const files = await fg(pattern, { absolute: true });\n\n const result: SchemaValidationResult = {\n schemaName: target.name,\n valid: true,\n totalFiles: files.length,\n validFiles: 0,\n invalidFiles: [],\n };\n\n if (files.length === 0) {\n return result;\n }\n\n for (const file of files) {\n const validation = await validateFile(file, target.schema, target.extractor);\n const relativePath = path.relative(rootDir, file);\n\n if (validation.valid) {\n result.validFiles++;\n } else {\n result.valid = false;\n result.invalidFiles.push({\n file: relativePath,\n errors: validation.errors,\n });\n }\n }\n\n return result;\n}\n\nexport async function validateAllSchemas(\n rootDir: string = process.cwd(),\n): Promise<FullValidationResult> {\n const results: SchemaValidationResult[] = [];\n\n for (const target of VALIDATION_TARGETS) {\n const result = await validateTarget(target, rootDir);\n results.push(result);\n }\n\n const summary = {\n totalSchemas: results.length,\n totalFiles: sumBy(results, (r) => r.totalFiles),\n validFiles: sumBy(results, (r) => r.validFiles),\n invalidFiles: sumBy(results, (r) => r.invalidFiles.length),\n };\n\n return {\n valid: results.every((r) => r.valid),\n results,\n summary,\n };\n}\n\nexport function printValidationResults(result: FullValidationResult): void {\n log(\"\\n Schema Validation Summary:\");\n log(\" ─────────────────────────\");\n log(` Total schemas checked: ${result.summary.totalSchemas}`);\n log(` Total files: ${result.summary.totalFiles}`);\n log(` Valid: ${result.summary.validFiles}`);\n log(` Invalid: ${result.summary.invalidFiles}`);\n\n for (const schemaResult of result.results) {\n if (schemaResult.totalFiles === 0) continue;\n\n const status = schemaResult.valid ? \"✓\" : \"✗\";\n log(\n `\\n ${status} ${schemaResult.schemaName}: ${schemaResult.validFiles}/${schemaResult.totalFiles} valid`,\n );\n\n if (schemaResult.invalidFiles.length > 0) {\n for (const file of schemaResult.invalidFiles) {\n log(`\\n ${file.file}:`);\n file.errors.forEach((e) => log(` - ${e}`));\n }\n }\n }\n\n if (result.valid) {\n log(\"\\n ✓ All schemas validated successfully\\n\");\n } else {\n log(\"\\n ✗ Validation failed\\n\");\n }\n}\n","import path from \"path\";\nimport { parse as parseYaml } from \"yaml\";\nimport { glob, readFile, fileExists, directoryExists } from \"../utils/fs\";\nimport { verbose } from \"../utils/logger\";\nimport { STANDARD_FILES } from \"../consts\";\nimport { metadataValidationSchema, formatZodErrors, SKILL_ID_PATTERN } from \"./schemas\";\nimport { parseFrontmatter } from \"./loading/loader\";\nimport { loadProjectSourceConfig } from \"./configuration\";\nimport {\n checkMatrixHealth,\n extractAllSkills,\n loadSkillsMatrix,\n mergeMatrixWithSkills,\n} from \"./matrix\";\n\nexport type SourceValidationIssue = {\n severity: \"error\" | \"warning\";\n file: string;\n message: string;\n};\n\nexport type SourceValidationResult = {\n issues: SourceValidationIssue[];\n skillCount: number;\n errorCount: number;\n warningCount: number;\n};\n\n/** Checks if a key uses snake_case (has underscore between lowercase letters) */\nfunction isSnakeCase(key: string): boolean {\n return /[a-z]_[a-z]/.test(key);\n}\n\n/**\n * Validates a skills source repository for metadata correctness.\n *\n * Checks:\n * 1. Every metadata.yaml against the strict validation schema\n * 2. cliName format and directory name consistency\n * 3. category values against known domain-prefixed patterns\n * 4. Cross-references resolve to existing skill IDs (via checkMatrixHealth)\n * 5. categoryExclusive presence when required\n * 6. camelCase key convention (no snake_case)\n * 7. Every skill directory has both SKILL.md and metadata.yaml\n */\nexport async function validateSource(sourcePath: string): Promise<SourceValidationResult> {\n const issues: SourceValidationIssue[] = [];\n\n const resolvedPath = path.isAbsolute(sourcePath) ? sourcePath : path.resolve(sourcePath);\n\n if (!(await directoryExists(resolvedPath))) {\n issues.push({\n severity: \"error\",\n file: resolvedPath,\n message: \"Source directory does not exist\",\n });\n return buildResult(issues, 0);\n }\n\n const sourceProjectConfig = await loadProjectSourceConfig(resolvedPath);\n const skillsDirRelPath = sourceProjectConfig?.skillsDir ?? \"src/skills\";\n const skillsDir = path.join(resolvedPath, skillsDirRelPath);\n\n if (!(await directoryExists(skillsDir))) {\n issues.push({\n severity: \"error\",\n file: skillsDir,\n message: \"Skills directory does not exist\",\n });\n return buildResult(issues, 0);\n }\n\n // Phase 1: Check every skill directory has both SKILL.md and metadata.yaml\n const skillMdFiles = await glob(`**/${STANDARD_FILES.SKILL_MD}`, skillsDir);\n const metadataFiles = await glob(`**/${STANDARD_FILES.METADATA_YAML}`, skillsDir);\n\n const skillMdDirs = new Set(skillMdFiles.map((f) => path.dirname(f)));\n const metadataDirs = new Set(metadataFiles.map((f) => path.dirname(f)));\n\n // Dirs with SKILL.md but no metadata.yaml\n for (const dir of skillMdDirs) {\n if (!metadataDirs.has(dir)) {\n issues.push({\n severity: \"error\",\n file: path.join(skillsDir, dir),\n message: `Missing ${STANDARD_FILES.METADATA_YAML} — skill directory has ${STANDARD_FILES.SKILL_MD} but no metadata`,\n });\n }\n }\n\n // Dirs with metadata.yaml but no SKILL.md\n for (const dir of metadataDirs) {\n if (!skillMdDirs.has(dir)) {\n issues.push({\n severity: \"error\",\n file: path.join(skillsDir, dir),\n message: `Missing ${STANDARD_FILES.SKILL_MD} — skill directory has ${STANDARD_FILES.METADATA_YAML} but no SKILL.md`,\n });\n }\n }\n\n // Phase 2: Validate each metadata.yaml against strict schema and conventions\n let skillCount = 0;\n for (const metadataFile of metadataFiles) {\n const metadataPath = path.join(skillsDir, metadataFile);\n const skillDir = path.dirname(metadataFile);\n const skillMdPath = path.join(skillsDir, skillDir, STANDARD_FILES.SKILL_MD);\n\n if (!(await fileExists(skillMdPath))) {\n // Already reported above\n continue;\n }\n\n skillCount++;\n const relPath = path.join(skillsDirRelPath, metadataFile);\n\n // Read and parse metadata.yaml\n let rawMetadata: unknown;\n try {\n const metadataContent = await readFile(metadataPath);\n rawMetadata = parseYaml(metadataContent);\n } catch (error) {\n issues.push({\n severity: \"error\",\n file: relPath,\n message: \"Failed to parse YAML\",\n });\n continue;\n }\n\n // Check for snake_case keys\n if (rawMetadata && typeof rawMetadata === \"object\" && !Array.isArray(rawMetadata)) {\n for (const key of Object.keys(rawMetadata as Record<string, unknown>)) {\n if (isSnakeCase(key)) {\n issues.push({\n severity: \"error\",\n file: relPath,\n message: `Key '${key}' uses snake_case — use camelCase instead`,\n });\n }\n }\n }\n\n // Validate against strict metadata schema\n const result = metadataValidationSchema.safeParse(rawMetadata);\n if (!result.success) {\n for (const issue of result.error.issues) {\n const fieldPath = issue.path.join(\".\");\n issues.push({\n severity: \"error\",\n file: relPath,\n message: `${fieldPath}: ${issue.message}`,\n });\n }\n continue;\n }\n\n const metadata = result.data;\n\n // Check cliName matches directory name\n const dirName = path.basename(skillDir);\n if (metadata.cliName !== dirName) {\n issues.push({\n severity: \"warning\",\n file: relPath,\n message: `cliName '${metadata.cliName}' does not match directory name '${dirName}'`,\n });\n }\n\n // Parse SKILL.md frontmatter and check name matches cliName\n const skillMdContent = await readFile(skillMdPath);\n const frontmatter = parseFrontmatter(skillMdContent, skillMdPath);\n if (frontmatter) {\n if (!SKILL_ID_PATTERN.test(frontmatter.name)) {\n issues.push({\n severity: \"warning\",\n file: path.join(skillsDirRelPath, skillDir, STANDARD_FILES.SKILL_MD),\n message: `SKILL.md name '${frontmatter.name}' does not match expected skill ID pattern (domain-subcategory-name)`,\n });\n }\n }\n\n // Check category follows domain-prefixed pattern\n if (\n metadata.category &&\n !/^(web|api|cli|mobile|infra|meta|security|shared)-.+$/.test(metadata.category)\n ) {\n issues.push({\n severity: \"warning\",\n file: relPath,\n message: `Category '${metadata.category}' does not follow domain-prefixed pattern (e.g., 'web-framework', 'api-database')`,\n });\n }\n }\n\n // Phase 3: Cross-reference validation via matrix health check\n try {\n const matrixRelPath = sourceProjectConfig?.matrixFile ?? \"config/skills-matrix.yaml\";\n const matrixPath = path.join(resolvedPath, matrixRelPath);\n\n if (await fileExists(matrixPath)) {\n const matrix = await loadSkillsMatrix(matrixPath);\n const skills = await extractAllSkills(skillsDir);\n const mergedMatrix = await mergeMatrixWithSkills(matrix, skills);\n const healthIssues = checkMatrixHealth(mergedMatrix);\n\n for (const healthIssue of healthIssues) {\n issues.push({\n severity: healthIssue.severity,\n file: matrixRelPath,\n message: healthIssue.details,\n });\n }\n } else {\n verbose(`No matrix file at '${matrixPath}' — skipping cross-reference validation`);\n }\n } catch (error) {\n issues.push({\n severity: \"warning\",\n file: \"config/skills-matrix.yaml\",\n message: `Cross-reference validation skipped: failed to load matrix`,\n });\n }\n\n return buildResult(issues, skillCount);\n}\n\nfunction buildResult(issues: SourceValidationIssue[], skillCount: number): SourceValidationResult {\n const errorCount = issues.filter((i) => i.severity === \"error\").length;\n const warningCount = issues.filter((i) => i.severity === \"warning\").length;\n return { issues, skillCount, errorCount, warningCount };\n}\n"],"mappings":"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","names":["path","path","path","parseYaml","path","parseYaml","verbose","path"]}
1
+ {"version":3,"sources":["../../src/cli/commands/validate.ts","../../src/cli/lib/source-validator.ts"],"sourcesContent":["import { Args, Flags } from \"@oclif/core\";\nimport path from \"path\";\nimport { BaseCommand } from \"../base-command.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport { EXIT_CODES } from \"../lib/exit-codes.js\";\nimport { ERROR_MESSAGES } from \"../utils/messages.js\";\nimport { validateAllSchemas, printValidationResults } from \"../lib/schema-validator.js\";\nimport {\n validatePlugin,\n validateAllPlugins,\n printPluginValidationResult,\n} from \"../lib/plugins/index.js\";\nimport { validateSource } from \"../lib/source-validator.js\";\n\nexport default class Validate extends BaseCommand {\n static summary =\n \"Validate YAML files against schemas, validate compiled plugins, or validate a skills source\";\n static description =\n \"Validates skill/agent YAML files against JSON schemas, validates compiled plugin structure and content, \" +\n \"or validates a skills source repository for metadata correctness. \" +\n \"Without arguments, validates all YAML files in the current directory against their schemas. \" +\n \"With --source, validates all skills in the source for schema compliance, cross-references, and conventions. \" +\n \"With a path argument or --plugins flag, validates plugin(s) instead.\";\n\n static examples = [\n {\n description: \"Validate all YAML schemas\",\n command: \"<%= config.bin %> <%= command.id %>\",\n },\n {\n description: \"Validate a skills source repository\",\n command: \"<%= config.bin %> <%= command.id %> --source .\",\n },\n {\n description: \"Validate a remote skills source\",\n command: \"<%= config.bin %> <%= command.id %> --source github:acme-corp/skills\",\n },\n {\n description: \"Validate a specific plugin\",\n command: \"<%= config.bin %> <%= command.id %> ./path/to/plugin\",\n },\n {\n description: \"Validate all plugins in a directory\",\n command: \"<%= config.bin %> <%= command.id %> ./plugins --all\",\n },\n {\n description: \"Validate plugins with verbose output\",\n command: \"<%= config.bin %> <%= command.id %> --plugins --verbose\",\n },\n ];\n\n static args = {\n path: Args.string({\n description: \"Path to plugin or plugins directory to validate\",\n required: false,\n }),\n };\n\n static flags = {\n ...BaseCommand.baseFlags,\n verbose: Flags.boolean({\n char: \"v\",\n description: \"Enable verbose logging\",\n default: false,\n }),\n all: Flags.boolean({\n char: \"a\",\n description: \"Validate all plugins in directory\",\n default: false,\n }),\n plugins: Flags.boolean({\n char: \"p\",\n description: \"Validate plugins instead of schemas\",\n default: false,\n }),\n };\n\n async run(): Promise<void> {\n const { args, flags } = await this.parse(Validate);\n\n if (flags.source) {\n await this.validateSkillsSource(flags.source);\n } else if (args.path || flags.plugins) {\n await this.validatePlugins(args.path, flags.verbose, flags.all);\n } else {\n await this.validateSchemas();\n }\n }\n\n private async validateSchemas(): Promise<void> {\n this.log(\"\");\n this.log(\"Validating all schemas\");\n this.log(\"\");\n\n try {\n const result = await validateAllSchemas();\n\n const summary = result.valid\n ? `Done: ${result.summary.validFiles}/${result.summary.totalFiles} files valid`\n : `Done: ${result.summary.invalidFiles} invalid files`;\n\n this.log(summary);\n printValidationResults(result);\n\n if (!result.valid) {\n this.exit(EXIT_CODES.ERROR);\n }\n } catch (error) {\n const message = getErrorMessage(error);\n this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });\n }\n }\n\n private async validatePlugins(\n pluginPath: string | undefined,\n verbose: boolean,\n all: boolean,\n ): Promise<void> {\n const targetPath = pluginPath ? path.resolve(pluginPath) : process.cwd();\n\n if (all) {\n await this.validateAllPluginsInDirectory(targetPath, verbose);\n } else {\n await this.validateSinglePlugin(targetPath, verbose);\n }\n }\n\n private async validateAllPluginsInDirectory(targetPath: string, verbose: boolean): Promise<void> {\n this.log(\"\");\n this.log(`Validating all plugins in: ${targetPath}`);\n this.log(\"\");\n\n try {\n const result = await validateAllPlugins(targetPath);\n\n const summary = result.valid\n ? `Done: ${result.summary.valid}/${result.summary.total} plugins valid`\n : `Done: ${result.summary.invalid} invalid plugins`;\n\n this.log(summary);\n\n this.log(\"\");\n this.log(\" Plugin Validation Summary:\");\n this.log(\" -------------------------\");\n this.log(` Total plugins: ${result.summary.total}`);\n this.log(` Valid: ${result.summary.valid}`);\n this.log(` Invalid: ${result.summary.invalid}`);\n this.log(` With warnings: ${result.summary.withWarnings}`);\n\n for (const { name, result: pluginResult } of result.results) {\n printPluginValidationResult(name, pluginResult, verbose);\n }\n\n if (result.valid) {\n this.log(\"\");\n this.logSuccess(\"All plugins validated successfully\");\n this.log(\"\");\n } else {\n this.log(\"\");\n this.error(ERROR_MESSAGES.VALIDATION_FAILED, { exit: EXIT_CODES.ERROR });\n }\n } catch (error) {\n const message = getErrorMessage(error);\n this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });\n }\n }\n\n private async validateSinglePlugin(targetPath: string, _verbose: boolean): Promise<void> {\n this.log(\"\");\n this.log(`Validating plugin: ${targetPath}`);\n this.log(\"\");\n\n try {\n const result = await validatePlugin(targetPath);\n\n const summary = result.valid ? \"Done: Plugin is valid\" : \"Done: Plugin has errors\";\n\n this.log(summary);\n\n printPluginValidationResult(path.basename(targetPath), result, true);\n\n if (result.valid && result.warnings.length === 0) {\n this.log(\"\");\n this.logSuccess(\"Plugin validated successfully\");\n this.log(\"\");\n } else if (result.valid) {\n this.log(\"\");\n this.logWarning(\"Plugin valid with warnings\");\n this.log(\"\");\n } else {\n this.log(\"\");\n this.error(ERROR_MESSAGES.VALIDATION_FAILED, { exit: EXIT_CODES.ERROR });\n }\n } catch (error) {\n const message = getErrorMessage(error);\n this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });\n }\n }\n\n private async validateSkillsSource(source: string): Promise<void> {\n this.log(\"\");\n this.log(`Validating source: ${source}`);\n this.log(\"\");\n\n try {\n const result = await validateSource(source);\n\n this.log(`Checked ${result.skillCount} skill(s)`);\n this.log(\"\");\n\n for (const issue of result.issues) {\n const prefix = issue.severity === \"error\" ? \"ERROR\" : \"WARN\";\n this.log(` [${prefix}] ${issue.file}: ${issue.message}`);\n }\n\n if (result.issues.length > 0) {\n this.log(\"\");\n }\n\n this.log(`Result: ${result.errorCount} error(s), ${result.warningCount} warning(s)`);\n\n if (result.errorCount > 0) {\n this.log(\"\");\n this.error(ERROR_MESSAGES.VALIDATION_FAILED, { exit: EXIT_CODES.ERROR });\n } else if (result.warningCount > 0) {\n this.log(\"\");\n this.logWarning(\"Source valid with warnings\");\n this.log(\"\");\n } else {\n this.log(\"\");\n this.logSuccess(\"Source validated successfully\");\n this.log(\"\");\n }\n } catch (error) {\n const message = getErrorMessage(error);\n this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });\n }\n }\n}\n","import path from \"path\";\nimport { parse as parseYaml } from \"yaml\";\nimport { glob, readFile, fileExists, directoryExists } from \"../utils/fs\";\nimport { verbose } from \"../utils/logger\";\nimport { SKILL_CATEGORIES_YAML_PATH, SKILL_RULES_YAML_PATH, STANDARD_FILES } from \"../consts\";\nimport { metadataValidationSchema, formatZodErrors, SKILL_ID_PATTERN } from \"./schemas\";\nimport { parseFrontmatter } from \"./loading/loader\";\nimport { loadProjectSourceConfig } from \"./configuration\";\nimport {\n checkMatrixHealth,\n extractAllSkills,\n loadSkillCategories,\n loadSkillRules,\n mergeMatrixWithSkills,\n} from \"./matrix\";\n\nexport type SourceValidationIssue = {\n severity: \"error\" | \"warning\";\n file: string;\n message: string;\n};\n\nexport type SourceValidationResult = {\n issues: SourceValidationIssue[];\n skillCount: number;\n errorCount: number;\n warningCount: number;\n};\n\n/** Checks if a key uses snake_case (has underscore between lowercase letters) */\nfunction isSnakeCase(key: string): boolean {\n return /[a-z]_[a-z]/.test(key);\n}\n\n/**\n * Validates a skills source repository for metadata correctness.\n *\n * Checks:\n * 1. Every metadata.yaml against the strict validation schema\n * 2. displayName format and directory name consistency\n * 3. category values against known domain-prefixed patterns\n * 4. Cross-references resolve to existing skill IDs (via checkMatrixHealth)\n * 5. camelCase key convention (no snake_case)\n * 7. Every skill directory has both SKILL.md and metadata.yaml\n */\nexport async function validateSource(sourcePath: string): Promise<SourceValidationResult> {\n const issues: SourceValidationIssue[] = [];\n\n const resolvedPath = path.isAbsolute(sourcePath) ? sourcePath : path.resolve(sourcePath);\n\n if (!(await directoryExists(resolvedPath))) {\n issues.push({\n severity: \"error\",\n file: resolvedPath,\n message: \"Source directory does not exist\",\n });\n return buildResult(issues, 0);\n }\n\n const sourceProjectConfig = await loadProjectSourceConfig(resolvedPath);\n const skillsDirRelPath = sourceProjectConfig?.skillsDir ?? \"src/skills\";\n const skillsDir = path.join(resolvedPath, skillsDirRelPath);\n\n if (!(await directoryExists(skillsDir))) {\n issues.push({\n severity: \"error\",\n file: skillsDir,\n message: \"Skills directory does not exist\",\n });\n return buildResult(issues, 0);\n }\n\n // Phase 1: Check every skill directory has both SKILL.md and metadata.yaml\n const skillMdFiles = await glob(`**/${STANDARD_FILES.SKILL_MD}`, skillsDir);\n const metadataFiles = await glob(`**/${STANDARD_FILES.METADATA_YAML}`, skillsDir);\n\n const skillMdDirs = new Set(skillMdFiles.map((f) => path.dirname(f)));\n const metadataDirs = new Set(metadataFiles.map((f) => path.dirname(f)));\n\n // Dirs with SKILL.md but no metadata.yaml\n for (const dir of skillMdDirs) {\n if (!metadataDirs.has(dir)) {\n issues.push({\n severity: \"error\",\n file: path.join(skillsDir, dir),\n message: `Missing ${STANDARD_FILES.METADATA_YAML} — skill directory has ${STANDARD_FILES.SKILL_MD} but no metadata`,\n });\n }\n }\n\n // Dirs with metadata.yaml but no SKILL.md\n for (const dir of metadataDirs) {\n if (!skillMdDirs.has(dir)) {\n issues.push({\n severity: \"error\",\n file: path.join(skillsDir, dir),\n message: `Missing ${STANDARD_FILES.SKILL_MD} — skill directory has ${STANDARD_FILES.METADATA_YAML} but no SKILL.md`,\n });\n }\n }\n\n // Phase 2: Validate each metadata.yaml against strict schema and conventions\n let skillCount = 0;\n for (const metadataFile of metadataFiles) {\n const metadataPath = path.join(skillsDir, metadataFile);\n const skillDir = path.dirname(metadataFile);\n const skillMdPath = path.join(skillsDir, skillDir, STANDARD_FILES.SKILL_MD);\n\n if (!(await fileExists(skillMdPath))) {\n // Already reported above\n continue;\n }\n\n skillCount++;\n const relPath = path.join(skillsDirRelPath, metadataFile);\n\n // Read and parse metadata.yaml\n let rawMetadata: unknown;\n try {\n const metadataContent = await readFile(metadataPath);\n rawMetadata = parseYaml(metadataContent);\n } catch (error) {\n issues.push({\n severity: \"error\",\n file: relPath,\n message: \"Failed to parse YAML\",\n });\n continue;\n }\n\n // Check for snake_case keys\n if (rawMetadata && typeof rawMetadata === \"object\" && !Array.isArray(rawMetadata)) {\n for (const key of Object.keys(rawMetadata as Record<string, unknown>)) {\n if (isSnakeCase(key)) {\n issues.push({\n severity: \"error\",\n file: relPath,\n message: `Key '${key}' uses snake_case — use camelCase instead`,\n });\n }\n }\n }\n\n // Validate against strict metadata schema\n const result = metadataValidationSchema.safeParse(rawMetadata);\n if (!result.success) {\n for (const issue of result.error.issues) {\n const fieldPath = issue.path.join(\".\");\n issues.push({\n severity: \"error\",\n file: relPath,\n message: `${fieldPath}: ${issue.message}`,\n });\n }\n continue;\n }\n\n const metadata = result.data;\n\n // Check displayName matches directory name\n const dirName = path.basename(skillDir);\n if (metadata.displayName !== dirName) {\n issues.push({\n severity: \"warning\",\n file: relPath,\n message: `displayName '${metadata.displayName}' does not match directory name '${dirName}'`,\n });\n }\n\n // Parse SKILL.md frontmatter and check name matches displayName\n const skillMdContent = await readFile(skillMdPath);\n const frontmatter = parseFrontmatter(skillMdContent, skillMdPath);\n if (frontmatter) {\n if (!SKILL_ID_PATTERN.test(frontmatter.name)) {\n issues.push({\n severity: \"warning\",\n file: path.join(skillsDirRelPath, skillDir, STANDARD_FILES.SKILL_MD),\n message: `SKILL.md name '${frontmatter.name}' does not match expected skill ID pattern (domain-subcategory-name)`,\n });\n }\n }\n\n // Check category follows domain-prefixed pattern\n if (\n metadata.category &&\n !/^(web|api|cli|mobile|infra|meta|security|shared)-.+$/.test(metadata.category)\n ) {\n issues.push({\n severity: \"warning\",\n file: relPath,\n message: `Category '${metadata.category}' does not follow domain-prefixed pattern (e.g., 'web-framework', 'api-database')`,\n });\n }\n }\n\n // Phase 3: Cross-reference validation via matrix health check\n try {\n const categoriesPath = path.join(resolvedPath, SKILL_CATEGORIES_YAML_PATH);\n const rulesPath = path.join(resolvedPath, SKILL_RULES_YAML_PATH);\n\n const hasCats = await fileExists(categoriesPath);\n const hasRules = await fileExists(rulesPath);\n\n if (hasCats || hasRules) {\n const cats = hasCats ? await loadSkillCategories(categoriesPath) : {};\n const defaultRelationships = {\n conflicts: [],\n discourages: [],\n recommends: [],\n requires: [],\n alternatives: [],\n };\n const rules = hasRules\n ? await loadSkillRules(rulesPath)\n : {\n version: \"1.0.0\",\n aliases: {},\n relationships: defaultRelationships,\n perSkill: {},\n };\n const skills = await extractAllSkills(skillsDir);\n const mergedMatrix = await mergeMatrixWithSkills(\n cats,\n rules.relationships,\n rules.aliases,\n skills,\n rules.perSkill,\n );\n const healthIssues = checkMatrixHealth(mergedMatrix);\n\n for (const healthIssue of healthIssues) {\n issues.push({\n severity: healthIssue.severity,\n file: SKILL_CATEGORIES_YAML_PATH,\n message: healthIssue.details,\n });\n }\n } else {\n verbose(\n `No categories/rules files at '${resolvedPath}' — skipping cross-reference validation`,\n );\n }\n } catch (error) {\n issues.push({\n severity: \"warning\",\n file: SKILL_CATEGORIES_YAML_PATH,\n message: `Cross-reference validation skipped: failed to load categories/rules`,\n });\n }\n\n return buildResult(issues, skillCount);\n}\n\nfunction buildResult(issues: SourceValidationIssue[], skillCount: number): SourceValidationResult {\n const errorCount = issues.filter((i) => i.severity === \"error\").length;\n const warningCount = issues.filter((i) => i.severity === \"warning\").length;\n return { issues, skillCount, errorCount, warningCount };\n}\n"],"mappings":"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","names":["path","verbose","path"]}
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  SkillSearch
4
- } from "../../chunk-EZ46ZTAQ.js";
5
- import "../../chunk-N5OCAAXY.js";
4
+ } from "../../chunk-U2W5SENM.js";
5
+ import "../../chunk-JWYRXE6C.js";
6
6
  import "../../chunk-U3IGFMCY.js";
7
- import "../../chunk-FTD5Z6QD.js";
7
+ import "../../chunk-AWP5A6IM.js";
8
8
  import "../../chunk-DHET7RCE.js";
9
9
  export {
10
10
  SkillSearch
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CategoryGrid
4
- } from "../../chunk-3APMMQUA.js";
4
+ } from "../../chunk-JFF7P4LC.js";
5
5
  import "../../chunk-GG4BSB6S.js";
6
- import "../../chunk-FTD5Z6QD.js";
6
+ import "../../chunk-GBOW6FUW.js";
7
+ import "../../chunk-AWP5A6IM.js";
7
8
  import "../../chunk-DHET7RCE.js";
8
9
  export {
9
10
  CategoryGrid
@@ -21,9 +21,10 @@ import {
21
21
  } from "../../chunk-XY3XDVMI.js";
22
22
  import {
23
23
  CategoryGrid
24
- } from "../../chunk-3APMMQUA.js";
24
+ } from "../../chunk-JFF7P4LC.js";
25
25
  import "../../chunk-GG4BSB6S.js";
26
- import "../../chunk-FTD5Z6QD.js";
26
+ import "../../chunk-GBOW6FUW.js";
27
+ import "../../chunk-AWP5A6IM.js";
27
28
  import {
28
29
  init_esm_shims
29
30
  } from "../../chunk-DHET7RCE.js";
@@ -186,7 +187,6 @@ var defaultProps = {
186
187
  defaultFocusedRow: 0,
187
188
  defaultFocusedCol: 0,
188
189
  showLabels: false,
189
- expertMode: false,
190
190
  onToggle: vi.fn(),
191
191
  onFocusChange: vi.fn(),
192
192
  onToggleLabels: vi.fn()
@@ -274,11 +274,11 @@ describe("CategoryGrid component", () => {
274
274
  const output = lastFrame();
275
275
  globalExpect(output).not.toContain("\u26A0");
276
276
  });
277
- it("should show disabled options with dimmed styling", () => {
277
+ it("should show discouraged options with warning styling", () => {
278
278
  const categories = [
279
279
  createCategory("web-testing", "Test", [
280
280
  createOption("web-forms-react-hook-form", "Option 1"),
281
- createOption("web-forms-vee-validate", "Option 2", { state: "disabled" })
281
+ createOption("web-forms-vee-validate", "Option 2", { state: "discouraged" })
282
282
  ])
283
283
  ];
284
284
  const { lastFrame, unmount } = renderGrid({ categories });
@@ -311,26 +311,26 @@ describe("CategoryGrid component", () => {
311
311
  const output = lastFrame();
312
312
  globalExpect(output).toContain("Unselected Skill");
313
313
  });
314
- it("should render disabled skills with label text", () => {
314
+ it("should render discouraged skills with label text", () => {
315
315
  const categories = [
316
316
  createCategory("web-forms", "Forms", [
317
- createOption("web-forms-react-hook-form", "Disabled Skill", { state: "disabled" })
317
+ createOption("web-forms-react-hook-form", "Discouraged Skill", { state: "discouraged" })
318
318
  ])
319
319
  ];
320
320
  const { lastFrame, unmount } = renderGrid({ categories });
321
321
  cleanup = unmount;
322
322
  const output = lastFrame();
323
- globalExpect(output).toContain("Disabled Skill");
323
+ globalExpect(output).toContain("Discouraged Skill");
324
324
  });
325
- it("should render disabled+selected skills with label text", () => {
325
+ it("should render discouraged+selected skills with label text", () => {
326
326
  const categories = [
327
327
  createCategory("web-forms", "Forms", [
328
- createOption("web-forms-react-hook-form", "Disabled Selected", {
329
- state: "disabled",
328
+ createOption("web-forms-react-hook-form", "Discouraged Selected", {
329
+ state: "discouraged",
330
330
  selected: true
331
331
  }),
332
- createOption("web-forms-vee-validate", "Disabled Only", {
333
- state: "disabled",
332
+ createOption("web-forms-vee-validate", "Discouraged Only", {
333
+ state: "discouraged",
334
334
  selected: false
335
335
  })
336
336
  ])
@@ -338,8 +338,8 @@ describe("CategoryGrid component", () => {
338
338
  const { lastFrame, unmount } = renderGrid({ categories });
339
339
  cleanup = unmount;
340
340
  const output = lastFrame();
341
- globalExpect(output).toContain("Disabled Selected");
342
- globalExpect(output).toContain("Disabled Only");
341
+ globalExpect(output).toContain("Discouraged Selected");
342
+ globalExpect(output).toContain("Discouraged Only");
343
343
  });
344
344
  it("should render discouraged skills with label text", () => {
345
345
  const categories = [
@@ -359,7 +359,7 @@ describe("CategoryGrid component", () => {
359
359
  createOption("web-forms-vee-validate", "Inactive", { selected: false })
360
360
  ])
361
361
  ];
362
- const { lastFrame, unmount } = renderGrid({ categories, expertMode: true });
362
+ const { lastFrame, unmount } = renderGrid({ categories });
363
363
  cleanup = unmount;
364
364
  const output = lastFrame();
365
365
  globalExpect(output).toContain("Active");
@@ -646,7 +646,6 @@ describe("CategoryGrid component", () => {
646
646
  defaultFocusedRow: 0,
647
647
  defaultFocusedCol: 0,
648
648
  // react (selected)
649
- expertMode: true,
650
649
  onToggle
651
650
  });
652
651
  cleanup = unmount;
@@ -655,26 +654,25 @@ describe("CategoryGrid component", () => {
655
654
  await delay(INPUT_DELAY_MS);
656
655
  globalExpect(onToggle).toHaveBeenCalledWith("web-framework", "web-framework-react");
657
656
  });
658
- it("should NOT call onToggle when all options in a category are disabled", async () => {
657
+ it("should call onToggle for discouraged options (still selectable)", async () => {
659
658
  const onToggle = vi.fn();
660
659
  const categories = [
661
660
  createCategory("web-testing", "Test", [
662
- createOption("web-forms-react-hook-form", "Option 1", { state: "disabled" }),
663
- createOption("web-forms-vee-validate", "Option 2", { state: "disabled" })
661
+ createOption("web-forms-react-hook-form", "Option 1", { state: "discouraged" }),
662
+ createOption("web-forms-vee-validate", "Option 2", { state: "discouraged" })
664
663
  ])
665
664
  ];
666
665
  const { stdin, unmount } = renderGrid({
667
666
  categories,
668
667
  defaultFocusedRow: 0,
669
668
  defaultFocusedCol: 0,
670
- expertMode: true,
671
669
  onToggle
672
670
  });
673
671
  cleanup = unmount;
674
672
  await delay(RENDER_DELAY_MS);
675
673
  await stdin.write(" ");
676
674
  await delay(INPUT_DELAY_MS);
677
- globalExpect(onToggle).not.toHaveBeenCalled();
675
+ globalExpect(onToggle).toHaveBeenCalled();
678
676
  });
679
677
  it("should bounce focus away from locked sections on mount", async () => {
680
678
  const onFocusChange = vi.fn();
@@ -689,13 +687,13 @@ describe("CategoryGrid component", () => {
689
687
  globalExpect(onFocusChange).toHaveBeenCalledWith(0, 0);
690
688
  });
691
689
  });
692
- describe("disabled options navigation", () => {
693
- it("should navigate to disabled options when navigating right", async () => {
690
+ describe("discouraged options navigation", () => {
691
+ it("should navigate to discouraged options when navigating right", async () => {
694
692
  const onFocusChange = vi.fn();
695
693
  const categories = [
696
694
  createCategory("web-testing", "Test", [
697
695
  createOption("web-forms-react-hook-form", "Option 1"),
698
- createOption("web-forms-vee-validate", "Option 2", { state: "disabled" }),
696
+ createOption("web-forms-vee-validate", "Option 2", { state: "discouraged" }),
699
697
  createOption("web-forms-zod-validation", "Option 3")
700
698
  ])
701
699
  ];
@@ -703,7 +701,6 @@ describe("CategoryGrid component", () => {
703
701
  categories,
704
702
  defaultFocusedRow: 0,
705
703
  defaultFocusedCol: 0,
706
- expertMode: true,
707
704
  onFocusChange
708
705
  });
709
706
  cleanup = unmount;
@@ -712,12 +709,12 @@ describe("CategoryGrid component", () => {
712
709
  await delay(INPUT_DELAY_MS);
713
710
  globalExpect(onFocusChange).toHaveBeenCalledWith(0, 1);
714
711
  });
715
- it("should navigate to disabled options when navigating left", async () => {
712
+ it("should navigate to discouraged options when navigating left", async () => {
716
713
  const onFocusChange = vi.fn();
717
714
  const categories = [
718
715
  createCategory("web-testing", "Test", [
719
716
  createOption("web-forms-react-hook-form", "Option 1"),
720
- createOption("web-forms-vee-validate", "Option 2", { state: "disabled" }),
717
+ createOption("web-forms-vee-validate", "Option 2", { state: "discouraged" }),
721
718
  createOption("web-forms-zod-validation", "Option 3")
722
719
  ])
723
720
  ];
@@ -725,8 +722,7 @@ describe("CategoryGrid component", () => {
725
722
  categories,
726
723
  defaultFocusedRow: 0,
727
724
  defaultFocusedCol: 2,
728
- // Start at opt2 (disabled, sorted to end)
729
- expertMode: true,
725
+ // Start at opt3 (index 2)
730
726
  onFocusChange
731
727
  });
732
728
  cleanup = unmount;
@@ -735,19 +731,18 @@ describe("CategoryGrid component", () => {
735
731
  await delay(INPUT_DELAY_MS);
736
732
  globalExpect(onFocusChange).toHaveBeenCalledWith(0, 1);
737
733
  });
738
- it("should navigate between all-disabled options in a row", async () => {
734
+ it("should navigate between all-discouraged options in a row", async () => {
739
735
  const onFocusChange = vi.fn();
740
736
  const categories = [
741
737
  createCategory("web-testing", "Test", [
742
- createOption("web-forms-react-hook-form", "Option 1", { state: "disabled" }),
743
- createOption("web-forms-vee-validate", "Option 2", { state: "disabled" })
738
+ createOption("web-forms-react-hook-form", "Option 1", { state: "discouraged" }),
739
+ createOption("web-forms-vee-validate", "Option 2", { state: "discouraged" })
744
740
  ])
745
741
  ];
746
742
  const { stdin, unmount } = renderGrid({
747
743
  categories,
748
744
  defaultFocusedRow: 0,
749
745
  defaultFocusedCol: 0,
750
- expertMode: true,
751
746
  onFocusChange
752
747
  });
753
748
  cleanup = unmount;
@@ -834,41 +829,100 @@ describe("CategoryGrid component", () => {
834
829
  globalExpect(output).not.toContain("(recommended)");
835
830
  globalExpect(output).not.toContain("(selected)");
836
831
  globalExpect(output).not.toContain("(discouraged)");
837
- globalExpect(output).not.toContain("(disabled)");
838
832
  });
839
- it("should show Disabled label for disabled options when showLabels is true", () => {
833
+ it("should show discouraged label for discouraged options when showLabels is true", () => {
840
834
  const categories = [
841
835
  createCategory("web-testing", "Test", [
842
836
  createOption("web-forms-react-hook-form", "Option 1"),
843
- createOption("web-forms-vee-validate", "Option 2", { state: "disabled" })
837
+ createOption("web-forms-vee-validate", "Option 2", { state: "discouraged" })
844
838
  ])
845
839
  ];
846
840
  const { lastFrame, unmount } = renderGrid({ categories, showLabels: true });
847
841
  cleanup = unmount;
848
842
  const output = lastFrame();
849
- globalExpect(output).toContain("(disabled)");
850
- });
851
- });
852
- describe("expert mode", () => {
853
- it("should not handle expert mode toggle locally (handled globally)", () => {
854
- const { lastFrame, unmount } = renderGrid();
855
- cleanup = unmount;
856
- const output = lastFrame();
857
- globalExpect(output).not.toContain("[e] Expert Mode");
843
+ globalExpect(output).toContain("(discouraged)");
858
844
  });
859
845
  });
860
846
  describe("option ordering", () => {
861
847
  it("should preserve original order regardless of state", () => {
862
- const { lastFrame, unmount } = renderGrid({ expertMode: false });
863
- cleanup = unmount;
864
- const output = lastFrame();
865
- globalExpect(output).toBeDefined();
866
- });
867
- it("should preserve original order in expert mode", () => {
868
- const { lastFrame, unmount } = renderGrid({ expertMode: true });
848
+ const categories = [
849
+ createCategory("web-client-state", "State", [
850
+ createOption("web-state-jotai", "Jotai"),
851
+ createOption("web-state-zustand", "Zustand", { state: "recommended" }),
852
+ createOption("web-state-redux-toolkit", "Redux", { state: "discouraged" }),
853
+ createOption("web-state-mobx", "MobX")
854
+ ])
855
+ ];
856
+ const { lastFrame, unmount } = renderGrid({ categories });
869
857
  cleanup = unmount;
870
858
  const output = lastFrame();
871
- globalExpect(output).toBeDefined();
859
+ const jotaiIdx = output.indexOf("Jotai");
860
+ const zustandIdx = output.indexOf("Zustand");
861
+ const reduxIdx = output.indexOf("Redux");
862
+ const mobxIdx = output.indexOf("MobX");
863
+ globalExpect(jotaiIdx).toBeLessThan(zustandIdx);
864
+ globalExpect(zustandIdx).toBeLessThan(reduxIdx);
865
+ globalExpect(reduxIdx).toBeLessThan(mobxIdx);
866
+ });
867
+ it("should not change order when a skill is selected", () => {
868
+ const categoriesBefore = [
869
+ createCategory("web-client-state", "State", [
870
+ createOption("web-state-jotai", "Jotai"),
871
+ createOption("web-state-zustand", "Zustand"),
872
+ createOption("web-state-redux-toolkit", "Redux")
873
+ ])
874
+ ];
875
+ const categoriesAfter = [
876
+ createCategory("web-client-state", "State", [
877
+ createOption("web-state-jotai", "Jotai"),
878
+ createOption("web-state-zustand", "Zustand", { selected: true }),
879
+ createOption("web-state-redux-toolkit", "Redux")
880
+ ])
881
+ ];
882
+ const { lastFrame: frameBefore, unmount: unmountBefore } = renderGrid({
883
+ categories: categoriesBefore
884
+ });
885
+ const outputBefore = frameBefore();
886
+ unmountBefore();
887
+ const { lastFrame: frameAfter, unmount: unmountAfter } = renderGrid({
888
+ categories: categoriesAfter
889
+ });
890
+ cleanup = unmountAfter;
891
+ const outputAfter = frameAfter();
892
+ globalExpect(outputBefore.indexOf("Jotai")).toBeLessThan(outputBefore.indexOf("Zustand"));
893
+ globalExpect(outputBefore.indexOf("Zustand")).toBeLessThan(outputBefore.indexOf("Redux"));
894
+ globalExpect(outputAfter.indexOf("Jotai")).toBeLessThan(outputAfter.indexOf("Zustand"));
895
+ globalExpect(outputAfter.indexOf("Zustand")).toBeLessThan(outputAfter.indexOf("Redux"));
896
+ });
897
+ it("should not change order when a skill state changes from normal to discouraged", () => {
898
+ const categoriesBefore = [
899
+ createCategory("web-client-state", "State", [
900
+ createOption("web-state-jotai", "Jotai"),
901
+ createOption("web-state-zustand", "Zustand"),
902
+ createOption("web-state-redux-toolkit", "Redux")
903
+ ])
904
+ ];
905
+ const categoriesAfter = [
906
+ createCategory("web-client-state", "State", [
907
+ createOption("web-state-jotai", "Jotai"),
908
+ createOption("web-state-zustand", "Zustand", { state: "discouraged" }),
909
+ createOption("web-state-redux-toolkit", "Redux")
910
+ ])
911
+ ];
912
+ const { lastFrame: frameBefore, unmount: unmountBefore } = renderGrid({
913
+ categories: categoriesBefore
914
+ });
915
+ const outputBefore = frameBefore();
916
+ unmountBefore();
917
+ const { lastFrame: frameAfter, unmount: unmountAfter } = renderGrid({
918
+ categories: categoriesAfter
919
+ });
920
+ cleanup = unmountAfter;
921
+ const outputAfter = frameAfter();
922
+ globalExpect(outputBefore.indexOf("Jotai")).toBeLessThan(outputBefore.indexOf("Zustand"));
923
+ globalExpect(outputBefore.indexOf("Zustand")).toBeLessThan(outputBefore.indexOf("Redux"));
924
+ globalExpect(outputAfter.indexOf("Jotai")).toBeLessThan(outputAfter.indexOf("Zustand"));
925
+ globalExpect(outputAfter.indexOf("Zustand")).toBeLessThan(outputAfter.indexOf("Redux"));
872
926
  });
873
927
  });
874
928
  describe("edge cases", () => {
@@ -1138,7 +1192,7 @@ describe("CategoryGrid component", () => {
1138
1192
  const output = lastFrame();
1139
1193
  globalExpect(output).toContain("(0 selected)");
1140
1194
  });
1141
- it("should hide counter in expert mode", () => {
1195
+ it("should always show selection counter", () => {
1142
1196
  const categories = [
1143
1197
  createCategory(
1144
1198
  "web-framework",
@@ -1153,11 +1207,11 @@ describe("CategoryGrid component", () => {
1153
1207
  { exclusive: false }
1154
1208
  )
1155
1209
  ];
1156
- const { lastFrame, unmount } = renderGrid({ categories, expertMode: true });
1210
+ const { lastFrame, unmount } = renderGrid({ categories });
1157
1211
  cleanup = unmount;
1158
1212
  const output = lastFrame();
1159
- globalExpect(output).not.toContain("of 1");
1160
- globalExpect(output).not.toContain("selected");
1213
+ globalExpect(output).toContain("1 of 1");
1214
+ globalExpect(output).toContain("1 selected");
1161
1215
  });
1162
1216
  it("should show correct counts for mixed exclusive and non-exclusive categories", () => {
1163
1217
  const categories = [