@agents-inc/cli 0.45.0 → 0.47.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 (175) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/{chunk-V43QDMYQ.js → chunk-2RQYJFKA.js} +2 -2
  3. package/dist/{chunk-ABE55TEU.js → chunk-3APMMQUA.js} +11 -3
  4. package/dist/chunk-3APMMQUA.js.map +1 -0
  5. package/dist/{chunk-KWWLPPHF.js → chunk-B4QYXVPZ.js} +2 -2
  6. package/dist/{chunk-473YHDYQ.js → chunk-C4QI54PN.js} +147 -52
  7. package/dist/chunk-C4QI54PN.js.map +1 -0
  8. package/dist/{chunk-CLHBKFHU.js → chunk-CJFWO46A.js} +2 -2
  9. package/dist/{chunk-M2XPTQDT.js → chunk-DC333ZDC.js} +4 -4
  10. package/dist/{chunk-M3AGB4TR.js → chunk-DVW6ASTO.js} +4 -4
  11. package/dist/{chunk-ARET3NYO.js → chunk-EZ46ZTAQ.js} +3 -3
  12. package/dist/{chunk-VTDEENSP.js → chunk-FMQ3A7W4.js} +5 -5
  13. package/dist/{chunk-VTDEENSP.js.map → chunk-FMQ3A7W4.js.map} +1 -1
  14. package/dist/{chunk-FVN5PFFY.js → chunk-FTD5Z6QD.js} +5 -1
  15. package/dist/chunk-FTD5Z6QD.js.map +1 -0
  16. package/dist/{chunk-YQFU2KZ5.js → chunk-FXQYEXLS.js} +3 -3
  17. package/dist/{chunk-ZECXM7LP.js → chunk-GFDGYQ6M.js} +3553 -3497
  18. package/dist/chunk-GFDGYQ6M.js.map +1 -0
  19. package/dist/chunk-HLTJK3XB.js +71 -0
  20. package/dist/chunk-HLTJK3XB.js.map +1 -0
  21. package/dist/{chunk-KVHLKPYB.js → chunk-HPJP3HFD.js} +7 -7
  22. package/dist/{chunk-KZNPPUQG.js → chunk-INKJBMPJ.js} +5 -3
  23. package/dist/chunk-INKJBMPJ.js.map +1 -0
  24. package/dist/{chunk-SBWMSNS2.js → chunk-IRJADQM7.js} +2 -2
  25. package/dist/{chunk-SYGEV3KV.js → chunk-JTTTXGHX.js} +4 -4
  26. package/dist/{chunk-ELRGSZHZ.js → chunk-JZHIF3K7.js} +5 -5
  27. package/dist/{chunk-OALQWRLG.js → chunk-KQ27IDYL.js} +2 -2
  28. package/dist/{chunk-OLZBZAW4.js → chunk-LJ5E4GXC.js} +2 -2
  29. package/dist/{chunk-SRFNNOLC.js → chunk-LNA6M2IE.js} +2 -2
  30. package/dist/{chunk-GUIRWCKI.js → chunk-M4P5YJ45.js} +3 -3
  31. package/dist/{chunk-3S4GIO4B.js → chunk-MVEYK55V.js} +2 -2
  32. package/dist/{chunk-HKRLWERR.js → chunk-N5OCAAXY.js} +2 -2
  33. package/dist/{chunk-YRVTXSXP.js → chunk-NRCKIHND.js} +9 -15
  34. package/dist/chunk-NRCKIHND.js.map +1 -0
  35. package/dist/{chunk-DAVOSI4M.js → chunk-OEJDFGAF.js} +5 -5
  36. package/dist/{chunk-ENWMWIHP.js → chunk-QR2TM4OY.js} +5 -12
  37. package/dist/chunk-QR2TM4OY.js.map +1 -0
  38. package/dist/{chunk-FYNMNY4P.js → chunk-SPSGZWTZ.js} +25 -12
  39. package/dist/chunk-SPSGZWTZ.js.map +1 -0
  40. package/dist/{chunk-3JRWNWBF.js → chunk-TBB3THSL.js} +38 -4
  41. package/dist/chunk-TBB3THSL.js.map +1 -0
  42. package/dist/chunk-TWDVLTU6.js +132 -0
  43. package/dist/chunk-TWDVLTU6.js.map +1 -0
  44. package/dist/{chunk-KCYNTAAF.js → chunk-VAQJLHUW.js} +12 -10
  45. package/dist/chunk-VAQJLHUW.js.map +1 -0
  46. package/dist/{chunk-F3O5YHSI.js → chunk-VSZ5GDET.js} +2 -2
  47. package/dist/{chunk-Q5BSIARS.js → chunk-XTRPYUWK.js} +15 -15
  48. package/dist/chunk-XTRPYUWK.js.map +1 -0
  49. package/dist/{chunk-WWSKP5SR.js → chunk-YTRFL3MR.js} +70 -24
  50. package/dist/chunk-YTRFL3MR.js.map +1 -0
  51. package/dist/{chunk-NTPHCNJO.js → chunk-ZWAL2ZY7.js} +2 -2
  52. package/dist/commands/build/marketplace.js +12 -126
  53. package/dist/commands/build/marketplace.js.map +1 -1
  54. package/dist/commands/build/plugins.js +5 -5
  55. package/dist/commands/build/stack.js +5 -5
  56. package/dist/commands/compile.js +6 -6
  57. package/dist/commands/config/get.js +4 -4
  58. package/dist/commands/config/index.js +5 -5
  59. package/dist/commands/config/path.js +4 -4
  60. package/dist/commands/config/set-project.js +4 -4
  61. package/dist/commands/config/show.js +5 -5
  62. package/dist/commands/config/unset-project.js +4 -4
  63. package/dist/commands/diff.js +4 -4
  64. package/dist/commands/doctor.js +4 -4
  65. package/dist/commands/edit.js +29 -29
  66. package/dist/commands/eject.js +19 -23
  67. package/dist/commands/eject.js.map +1 -1
  68. package/dist/commands/import/skill.js +5 -5
  69. package/dist/commands/info.js +5 -5
  70. package/dist/commands/init.js +30 -29
  71. package/dist/commands/init.js.map +1 -1
  72. package/dist/commands/list.js +4 -4
  73. package/dist/commands/new/agent.js +52 -38
  74. package/dist/commands/new/agent.js.map +1 -1
  75. package/dist/commands/new/marketplace.js +252 -0
  76. package/dist/commands/new/marketplace.js.map +1 -0
  77. package/dist/commands/new/skill.js +35 -16
  78. package/dist/commands/new/skill.js.map +1 -1
  79. package/dist/commands/outdated.js +4 -4
  80. package/dist/commands/search.js +7 -7
  81. package/dist/commands/uninstall.js +6 -6
  82. package/dist/commands/update.js +6 -6
  83. package/dist/commands/validate.js +229 -19
  84. package/dist/commands/validate.js.map +1 -1
  85. package/dist/components/skill-search/skill-search.js +3 -3
  86. package/dist/components/wizard/category-grid.js +2 -2
  87. package/dist/components/wizard/category-grid.test.js +122 -2
  88. package/dist/components/wizard/category-grid.test.js.map +1 -1
  89. package/dist/components/wizard/checkbox-grid.js +3 -3
  90. package/dist/components/wizard/checkbox-grid.test.js +3 -3
  91. package/dist/components/wizard/domain-selection.js +9 -8
  92. package/dist/components/wizard/help-modal.js +2 -2
  93. package/dist/components/wizard/menu-item.js +1 -1
  94. package/dist/components/wizard/search-modal.js +2 -2
  95. package/dist/components/wizard/search-modal.test.js +2 -2
  96. package/dist/components/wizard/section-progress.js +2 -2
  97. package/dist/components/wizard/section-progress.test.js +2 -2
  98. package/dist/components/wizard/selection-card.js +2 -2
  99. package/dist/components/wizard/source-grid.js +3 -3
  100. package/dist/components/wizard/source-grid.test.js +3 -3
  101. package/dist/components/wizard/stack-selection.js +8 -9
  102. package/dist/components/wizard/step-agents.js +8 -7
  103. package/dist/components/wizard/step-agents.test.js +25 -20
  104. package/dist/components/wizard/step-agents.test.js.map +1 -1
  105. package/dist/components/wizard/step-build.js +8 -8
  106. package/dist/components/wizard/step-build.test.js +78 -46
  107. package/dist/components/wizard/step-build.test.js.map +1 -1
  108. package/dist/components/wizard/step-confirm.js +4 -4
  109. package/dist/components/wizard/step-confirm.test.js +8 -8
  110. package/dist/components/wizard/step-refine.js +2 -2
  111. package/dist/components/wizard/step-refine.test.js +2 -2
  112. package/dist/components/wizard/step-settings.js +5 -5
  113. package/dist/components/wizard/step-settings.test.js +8 -8
  114. package/dist/components/wizard/step-sources.js +10 -10
  115. package/dist/components/wizard/step-sources.test.js +11 -11
  116. package/dist/components/wizard/step-stack.js +12 -12
  117. package/dist/components/wizard/step-stack.test.js +27 -15
  118. package/dist/components/wizard/step-stack.test.js.map +1 -1
  119. package/dist/components/wizard/view-title.js +2 -2
  120. package/dist/components/wizard/wizard-layout.js +8 -8
  121. package/dist/components/wizard/wizard-tabs.js +2 -2
  122. package/dist/components/wizard/wizard-tabs.test.js +2 -2
  123. package/dist/components/wizard/wizard.js +25 -25
  124. package/dist/hooks/init.js +3 -5
  125. package/dist/hooks/init.js.map +1 -1
  126. package/dist/{source-manager-HXFXBZJU.js → source-manager-Q34LTUVM.js} +4 -4
  127. package/dist/src/agents/meta/documentor/examples.md +35 -36
  128. package/dist/src/agents/meta/documentor/workflow.md +91 -105
  129. package/dist/stores/wizard-store.js +5 -5
  130. package/dist/stores/wizard-store.test.js +48 -6
  131. package/dist/stores/wizard-store.test.js.map +1 -1
  132. package/package.json +1 -1
  133. package/src/agents/meta/documentor/examples.md +35 -36
  134. package/src/agents/meta/documentor/workflow.md +91 -105
  135. package/src/schemas/agent.schema.json +6 -0
  136. package/src/schemas/metadata.schema.json +7 -41
  137. package/src/schemas/project-config.schema.json +8 -4
  138. package/src/schemas/skills-matrix.schema.json +11 -298
  139. package/src/schemas/stacks.schema.json +2 -4
  140. package/dist/chunk-3JRWNWBF.js.map +0 -1
  141. package/dist/chunk-473YHDYQ.js.map +0 -1
  142. package/dist/chunk-5BDYODWP.js +0 -45
  143. package/dist/chunk-5BDYODWP.js.map +0 -1
  144. package/dist/chunk-ABE55TEU.js.map +0 -1
  145. package/dist/chunk-ENWMWIHP.js.map +0 -1
  146. package/dist/chunk-FVN5PFFY.js.map +0 -1
  147. package/dist/chunk-FYNMNY4P.js.map +0 -1
  148. package/dist/chunk-KCYNTAAF.js.map +0 -1
  149. package/dist/chunk-KZNPPUQG.js.map +0 -1
  150. package/dist/chunk-Q5BSIARS.js.map +0 -1
  151. package/dist/chunk-WWSKP5SR.js.map +0 -1
  152. package/dist/chunk-YRVTXSXP.js.map +0 -1
  153. package/dist/chunk-ZECXM7LP.js.map +0 -1
  154. package/dist/cli/defaults/agent-mappings.yaml +0 -215
  155. /package/dist/{chunk-V43QDMYQ.js.map → chunk-2RQYJFKA.js.map} +0 -0
  156. /package/dist/{chunk-KWWLPPHF.js.map → chunk-B4QYXVPZ.js.map} +0 -0
  157. /package/dist/{chunk-CLHBKFHU.js.map → chunk-CJFWO46A.js.map} +0 -0
  158. /package/dist/{chunk-M2XPTQDT.js.map → chunk-DC333ZDC.js.map} +0 -0
  159. /package/dist/{chunk-M3AGB4TR.js.map → chunk-DVW6ASTO.js.map} +0 -0
  160. /package/dist/{chunk-ARET3NYO.js.map → chunk-EZ46ZTAQ.js.map} +0 -0
  161. /package/dist/{chunk-YQFU2KZ5.js.map → chunk-FXQYEXLS.js.map} +0 -0
  162. /package/dist/{chunk-KVHLKPYB.js.map → chunk-HPJP3HFD.js.map} +0 -0
  163. /package/dist/{chunk-SBWMSNS2.js.map → chunk-IRJADQM7.js.map} +0 -0
  164. /package/dist/{chunk-SYGEV3KV.js.map → chunk-JTTTXGHX.js.map} +0 -0
  165. /package/dist/{chunk-ELRGSZHZ.js.map → chunk-JZHIF3K7.js.map} +0 -0
  166. /package/dist/{chunk-OALQWRLG.js.map → chunk-KQ27IDYL.js.map} +0 -0
  167. /package/dist/{chunk-OLZBZAW4.js.map → chunk-LJ5E4GXC.js.map} +0 -0
  168. /package/dist/{chunk-SRFNNOLC.js.map → chunk-LNA6M2IE.js.map} +0 -0
  169. /package/dist/{chunk-GUIRWCKI.js.map → chunk-M4P5YJ45.js.map} +0 -0
  170. /package/dist/{chunk-3S4GIO4B.js.map → chunk-MVEYK55V.js.map} +0 -0
  171. /package/dist/{chunk-HKRLWERR.js.map → chunk-N5OCAAXY.js.map} +0 -0
  172. /package/dist/{chunk-DAVOSI4M.js.map → chunk-OEJDFGAF.js.map} +0 -0
  173. /package/dist/{chunk-F3O5YHSI.js.map → chunk-VSZ5GDET.js.map} +0 -0
  174. /package/dist/{chunk-NTPHCNJO.js.map → chunk-ZWAL2ZY7.js.map} +0 -0
  175. /package/dist/{source-manager-HXFXBZJU.js.map → source-manager-Q34LTUVM.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -9,6 +9,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ---
11
11
 
12
+ ## [0.47.0] - 2026-02-25
13
+
14
+ **Custom domain support, source validation, wizard improvements, dead code cleanup**
15
+
16
+ - Domain field on skill metadata and agent YAML for custom marketplace support
17
+ - `validate --source` command for source repository validation
18
+ - Fix custom skill ID rejection in stacks validation
19
+ - Remove redundant verifyHash and legacy slash-in-skill-ID code
20
+
21
+ See [changelogs/0.47.0.md](./changelogs/0.47.0.md) for full details.
22
+
23
+ ---
24
+
25
+ ## [0.46.0] - 2026-02-24
26
+
27
+ **Custom extensibility, new commands, agent-mapping removal, wizard improvements**
28
+
29
+ - Custom extensibility foundation: runtime-extensible schemas for custom skills, agents, categories, and domains from private marketplaces
30
+ - Remove agent-mappings.yaml and skill-to-agent routing — all skills assigned to all selected agents
31
+ - Promote `eject templates` to first-class type, add `new marketplace` command, improve `new skill` and `new agent`
32
+ - Dynamic wizard domains/agents from merged matrix, fix stack domain preselection
33
+ - Fix plugin mode marketplace registration (strip `github:` prefix)
34
+
35
+ See [changelogs/0.46.0.md](./changelogs/0.46.0.md) for full details.
36
+
37
+ ---
38
+
12
39
  ## [0.45.0] - 2026-02-22
13
40
 
14
41
  **Persist agent selection, skill relation validation, eject fix, test fixtures**
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  CLI_BIN_NAME,
4
4
  DEFAULT_BRANDING
5
- } from "./chunk-FVN5PFFY.js";
5
+ } from "./chunk-FTD5Z6QD.js";
6
6
  import {
7
7
  init_esm_shims
8
8
  } from "./chunk-DHET7RCE.js";
@@ -67,4 +67,4 @@ export {
67
67
  INFO_MESSAGES,
68
68
  DRY_RUN_MESSAGES
69
69
  };
70
- //# sourceMappingURL=chunk-V43QDMYQ.js.map
70
+ //# sourceMappingURL=chunk-2RQYJFKA.js.map
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  CLI_COLORS,
7
7
  SCROLL_VIEWPORT
8
- } from "./chunk-FVN5PFFY.js";
8
+ } from "./chunk-FTD5Z6QD.js";
9
9
  import {
10
10
  init_esm_shims
11
11
  } from "./chunk-DHET7RCE.js";
@@ -209,14 +209,21 @@ var CategorySection = ({
209
209
  isLocked,
210
210
  isFocused,
211
211
  focusedOptionIndex,
212
- showLabels
212
+ showLabels,
213
+ expertMode
213
214
  }) => {
215
+ const selectedCount = options.filter((o) => o.selected).length;
216
+ const selectionCounter = expertMode ? null : category.exclusive ? `(${selectedCount} of 1)` : `(${selectedCount} selected)`;
214
217
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: isFirst ? 0 : 1, children: [
215
218
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
216
219
  /* @__PURE__ */ jsx(Text, { dimColor: isLocked, color: isFocused ? "#fff" : "gray", children: category.displayName }),
217
220
  category.required && /* @__PURE__ */ jsxs(Text, { color: isLocked ? CLI_COLORS.NEUTRAL : CLI_COLORS.ERROR, dimColor: isLocked, children: [
218
221
  " ",
219
222
  SYMBOL_REQUIRED
223
+ ] }),
224
+ selectionCounter && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
225
+ " ",
226
+ selectionCounter
220
227
  ] })
221
228
  ] }),
222
229
  /* @__PURE__ */ jsx(Box, { flexDirection: "row", flexWrap: "wrap", marginTop: 0, children: options.map((option, index) => /* @__PURE__ */ jsx(
@@ -360,6 +367,7 @@ var CategoryGrid = ({
360
367
  isFocused: index === focusedRow,
361
368
  focusedOptionIndex: focusedCol,
362
369
  showLabels,
370
+ expertMode,
363
371
  isFirst: index === 0
364
372
  }
365
373
  ) }, category.id);
@@ -373,4 +381,4 @@ var CategoryGrid = ({
373
381
  export {
374
382
  CategoryGrid
375
383
  };
376
- //# sourceMappingURL=chunk-ABE55TEU.js.map
384
+ //# sourceMappingURL=chunk-3APMMQUA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/wizard/category-grid.tsx","../src/cli/components/hooks/use-category-grid-input.ts"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\nimport { Box, type DOMElement, Text, measureElement } from \"ink\";\n\nimport { CLI_COLORS, SCROLL_VIEWPORT } from \"../../consts.js\";\nimport type { SkillId, Subcategory } from \"../../types/index.js\";\nimport { isSectionLocked, useCategoryGridInput } from \"../hooks/use-category-grid-input.js\";\nimport { useFocusedListItem } from \"../hooks/use-focused-list-item.js\";\n\nexport type OptionState = \"normal\" | \"recommended\" | \"discouraged\" | \"disabled\";\n\nexport type CategoryOption = {\n id: SkillId;\n label: string;\n state: OptionState;\n stateReason?: string;\n selected: boolean;\n local?: boolean;\n installed?: boolean;\n};\n\nexport type CategoryRow = {\n id: Subcategory;\n displayName: string;\n required: boolean;\n exclusive: boolean;\n options: CategoryOption[];\n};\n\nexport type CategoryGridProps = {\n categories: CategoryRow[];\n /** Available height in terminal lines for the scrollable viewport. 0 = no constraint. */\n availableHeight?: number;\n showLabels: boolean;\n expertMode: boolean;\n onToggle: (categoryId: Subcategory, technologyId: SkillId) => void;\n onToggleLabels: () => void;\n /** Optional initial focus row (default: 0). Use with React `key` to reset. */\n defaultFocusedRow?: number;\n /** Optional initial focus col (default: 0). Use with React `key` to reset. */\n defaultFocusedCol?: number;\n /** Optional callback fired whenever the focused position changes */\n onFocusChange?: (row: number, col: number) => void;\n};\n\nconst SYMBOL_REQUIRED = \"*\";\n\n/**\n * Priority order for skill states in the initial sort.\n * Lower numbers appear first. Selected skills are sorted above all states.\n */\nconst STATE_PRIORITY: Record<OptionState, number> = {\n recommended: 0,\n normal: 1,\n discouraged: 2,\n disabled: 3,\n};\n\n/**\n * Sort options by: selected first, then by state priority.\n * Within each group, original matrix order is preserved (stable sort).\n */\nconst stableSortByState = (options: CategoryOption[]): CategoryOption[] => {\n return [...options].sort((a, b) => {\n if (a.selected !== b.selected) return a.selected ? -1 : 1;\n return STATE_PRIORITY[a.state] - STATE_PRIORITY[b.state];\n });\n};\n\nconst findNextValidOption = (\n options: CategoryOption[],\n currentIndex: number,\n direction: 1 | -1,\n wrap = true,\n): number => {\n const length = options.length;\n if (length === 0) return currentIndex;\n\n let index = currentIndex + direction;\n\n if (wrap) {\n if (index < 0) index = length - 1;\n if (index >= length) index = 0;\n } else {\n if (index < 0) index = 0;\n if (index >= length) index = length - 1;\n }\n\n return index;\n};\n\ntype SkillTagProps = {\n option: CategoryOption;\n isFocused: boolean;\n isLocked: boolean;\n showLabels: boolean;\n};\n\nconst getCompatibilityLabel = (option: CategoryOption, isLocked: boolean): string | null => {\n if (option.selected) return \"(selected)\";\n if (isLocked || option.state === \"disabled\") return \"(disabled)\";\n if (option.state === \"recommended\") return \"(recommended)\";\n if (option.state === \"discouraged\") return \"(discouraged)\";\n return null;\n};\n\nconst SkillTag: React.FC<SkillTagProps> = ({ option, isFocused, isLocked, showLabels }) => {\n const getTextColor = (): string => {\n if (option.state === \"disabled\" && option.selected) return CLI_COLORS.PRIMARY;\n if (isLocked || option.state === \"disabled\") return CLI_COLORS.NEUTRAL;\n if (option.selected) return CLI_COLORS.PRIMARY;\n if (option.state === \"recommended\") return CLI_COLORS.UNFOCUSED;\n if (option.state === \"discouraged\") return CLI_COLORS.WARNING;\n\n return CLI_COLORS.NEUTRAL;\n };\n\n const getStateBorderColor = (): string => {\n if (option.state === \"disabled\" && option.selected) return CLI_COLORS.PRIMARY;\n if (isLocked || option.state === \"disabled\") return CLI_COLORS.NEUTRAL;\n if (option.selected) return CLI_COLORS.PRIMARY;\n if (option.state === \"recommended\") return CLI_COLORS.UNFOCUSED;\n if (option.state === \"discouraged\") return CLI_COLORS.WARNING;\n return CLI_COLORS.UNFOCUSED;\n };\n\n const textColor = getTextColor();\n const compatibilityLabel = showLabels ? getCompatibilityLabel(option, isLocked) : null;\n\n return (\n <Box\n marginRight={1}\n borderColor={isFocused ? getStateBorderColor() : CLI_COLORS.NEUTRAL}\n borderStyle=\"single\"\n flexShrink={0}\n >\n <>\n <Text color={textColor} bold dimColor={option.state === \"disabled\" && option.selected}>\n {\" \"}\n {option.label}{\" \"}\n </Text>\n {compatibilityLabel && <Text dimColor>{compatibilityLabel} </Text>}\n </>\n </Box>\n );\n};\n\ntype CategorySectionProps = {\n isFirst: boolean;\n category: CategoryRow;\n options: CategoryOption[];\n isLocked: boolean;\n isFocused: boolean;\n focusedOptionIndex: number;\n showLabels: boolean;\n expertMode: boolean;\n};\n\nconst CategorySection: React.FC<CategorySectionProps> = ({\n isFirst,\n category,\n options,\n isLocked,\n isFocused,\n focusedOptionIndex,\n showLabels,\n expertMode,\n}) => {\n const selectedCount = options.filter((o) => o.selected).length;\n\n const selectionCounter = expertMode\n ? null\n : category.exclusive\n ? `(${selectedCount} of 1)`\n : `(${selectedCount} selected)`;\n\n return (\n <Box flexDirection=\"column\" marginTop={isFirst ? 0 : 1}>\n <Box flexDirection=\"row\">\n <Text dimColor={isLocked} color={isFocused ? \"#fff\" : \"gray\"}>\n {category.displayName}\n </Text>\n {category.required && (\n <Text color={isLocked ? CLI_COLORS.NEUTRAL : CLI_COLORS.ERROR} dimColor={isLocked}>\n {\" \"}\n {SYMBOL_REQUIRED}\n </Text>\n )}\n {selectionCounter && <Text dimColor> {selectionCounter}</Text>}\n </Box>\n\n <Box flexDirection=\"row\" flexWrap=\"wrap\" marginTop={0}>\n {options.map((option, index) => (\n <SkillTag\n key={option.id}\n option={option}\n isFocused={isFocused && index === focusedOptionIndex && !isLocked}\n isLocked={isLocked}\n showLabels={showLabels}\n />\n ))}\n </Box>\n </Box>\n );\n};\n\ntype ProcessedCategory = CategoryRow & { sortedOptions: CategoryOption[] };\n\nexport const CategoryGrid: React.FC<CategoryGridProps> = ({\n categories,\n availableHeight = 0,\n showLabels,\n expertMode,\n onToggle,\n onToggleLabels,\n defaultFocusedRow = 0,\n defaultFocusedCol = 0,\n onFocusChange,\n}) => {\n // Cache the initial sort order per category so toggling selections does not reorder skills.\n // The ref resets when the component remounts (e.g., domain change via key={activeDomain}).\n const initialOrderRef = useRef<Map<string, SkillId[]>>(new Map());\n\n const processedCategories = useMemo(\n () =>\n categories.map((category) => {\n const cached = initialOrderRef.current.get(category.id);\n if (cached) {\n const orderMap = new Map(cached.map((id, idx) => [id, idx]));\n const sorted = [...category.options].sort((a, b) => {\n const aIdx = orderMap.get(a.id) ?? Infinity;\n const bIdx = orderMap.get(b.id) ?? Infinity;\n return aIdx - bIdx;\n });\n return { ...category, sortedOptions: sorted };\n }\n const sorted = stableSortByState(category.options);\n initialOrderRef.current.set(\n category.id,\n sorted.map((o) => o.id),\n );\n return { ...category, sortedOptions: sorted };\n }),\n [categories],\n );\n\n const getColCount = useCallback(\n (row: number): number => processedCategories[row]?.sortedOptions.length ?? 0,\n [processedCategories],\n );\n\n const isRowLocked = useCallback(\n (row: number): boolean => {\n const cat = processedCategories[row];\n return cat ? isSectionLocked(cat.id, categories) : false;\n },\n [processedCategories, categories],\n );\n\n const findValidCol = useCallback(\n (row: number, currentCol: number, direction: 1 | -1): number => {\n const options = processedCategories[row]?.sortedOptions || [];\n const catId = processedCategories[row]?.id;\n if (catId && isSectionLocked(catId, categories)) return currentCol;\n return findNextValidOption(options, currentCol, direction, true);\n },\n [processedCategories, categories],\n );\n\n const { focusedRow, focusedCol, setFocused, moveFocus } = useFocusedListItem(\n processedCategories.length,\n getColCount,\n {\n wrap: true,\n isRowLocked,\n findValidCol,\n onChange: onFocusChange,\n initialRow: defaultFocusedRow,\n initialCol: defaultFocusedCol,\n },\n );\n\n useCategoryGridInput({\n processedCategories,\n categories,\n focusedRow,\n focusedCol,\n setFocused,\n moveFocus,\n onToggle,\n onToggleLabels,\n });\n\n const sectionRefs = useRef<(DOMElement | null)[]>([]);\n const [sectionHeights, setSectionHeights] = useState<number[]>([]);\n const [scrollTopPx, setScrollTopPx] = useState(0);\n\n const setSectionRef = useCallback((index: number, el: DOMElement | null) => {\n sectionRefs.current[index] = el;\n }, []);\n\n useEffect(() => {\n const heights = sectionRefs.current.map((el) => {\n if (el) {\n const { height } = measureElement(el);\n return height;\n }\n return 0;\n });\n setSectionHeights((prev) => {\n if (prev.length === heights.length && prev.every((h, i) => h === heights[i])) {\n return prev;\n }\n return heights;\n });\n });\n\n const scrollEnabled = availableHeight > 0 && availableHeight >= SCROLL_VIEWPORT.MIN_VIEWPORT_ROWS;\n\n useEffect(() => {\n if (!scrollEnabled || sectionHeights.length === 0) return;\n\n let topOfFocused = 0;\n for (let i = 0; i < focusedRow; i++) {\n topOfFocused += sectionHeights[i] ?? 0;\n }\n const focusedHeight = sectionHeights[focusedRow] ?? 0;\n const bottomOfFocused = topOfFocused + focusedHeight;\n\n setScrollTopPx((prev) => {\n if (topOfFocused < prev) {\n return topOfFocused;\n }\n if (bottomOfFocused > prev + availableHeight) {\n return bottomOfFocused - availableHeight;\n }\n return prev;\n });\n }, [focusedRow, sectionHeights, scrollEnabled, availableHeight]);\n\n if (categories.length === 0) {\n return (\n <Box flexDirection=\"column\">\n <Text dimColor>No categories to display.</Text>\n </Box>\n );\n }\n\n const sectionElements = processedCategories.map((category, index) => {\n const isLocked = isSectionLocked(category.id, categories);\n\n return (\n <Box key={category.id} ref={(el) => setSectionRef(index, el)} flexShrink={0}>\n <CategorySection\n category={category}\n options={category.sortedOptions}\n isLocked={isLocked}\n isFocused={index === focusedRow}\n focusedOptionIndex={focusedCol}\n showLabels={showLabels}\n expertMode={expertMode}\n isFirst={index === 0}\n />\n </Box>\n );\n });\n\n // When no height constraint, render flat (tests, or before first measurement)\n if (!scrollEnabled) {\n return (\n <Box flexDirection=\"column\" flexGrow={1} overflow=\"hidden\">\n {sectionElements}\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" height={availableHeight} overflow=\"hidden\">\n <Box flexDirection=\"column\" marginTop={scrollTopPx > 0 ? -scrollTopPx : 0} flexShrink={0}>\n {sectionElements}\n </Box>\n </Box>\n );\n};\n","import { useCallback, useEffect, useRef } from \"react\";\nimport { useInput } from \"ink\";\n\nimport type { Subcategory, SkillId } from \"../../types/index.js\";\nimport type { CategoryOption, CategoryRow } from \"../wizard/category-grid.js\";\n\nconst FRAMEWORK_CATEGORY_ID = \"web-framework\";\n\n// Locked = non-framework section when no framework is selected\nexport const isSectionLocked = (categoryId: Subcategory, categories: CategoryRow[]): boolean => {\n if (categoryId === FRAMEWORK_CATEGORY_ID) {\n return false;\n }\n\n const frameworkCategory = categories.find((cat) => cat.id === FRAMEWORK_CATEGORY_ID);\n if (!frameworkCategory) return false;\n\n return !frameworkCategory.options.some((opt) => opt.selected);\n};\n\nexport const findValidStartColumn = (_options: CategoryOption[]): number => {\n return 0;\n};\n\n/** Find next unlocked section index (wrapping, direction: forward) */\nexport const findNextUnlockedIndex = (\n processed: { id: Subcategory; sortedOptions: CategoryOption[] }[],\n currentIndex: number,\n allCategories: CategoryRow[],\n): number => {\n const length = processed.length;\n if (length === 0) return currentIndex;\n\n let index = currentIndex;\n let attempts = 0;\n\n while (attempts < length) {\n index += 1;\n if (index >= length) index = 0;\n\n const category = processed[index];\n if (category && !isSectionLocked(category.id, allCategories)) {\n return index;\n }\n\n attempts++;\n }\n\n return currentIndex;\n};\n\ntype ProcessedCategory = CategoryRow & { sortedOptions: CategoryOption[] };\n\ntype UseCategoryGridInputOptions = {\n processedCategories: ProcessedCategory[];\n categories: CategoryRow[];\n focusedRow: number;\n focusedCol: number;\n setFocused: (row: number, col: number) => void;\n moveFocus: (direction: \"up\" | \"down\" | \"left\" | \"right\") => void;\n onToggle: (categoryId: Subcategory, technologyId: SkillId) => void;\n onToggleLabels: () => void;\n};\n\nexport function useCategoryGridInput({\n processedCategories,\n categories,\n focusedRow,\n focusedCol,\n setFocused,\n moveFocus,\n onToggle,\n onToggleLabels,\n}: UseCategoryGridInputOptions): void {\n const currentRow = processedCategories[focusedRow];\n const currentOptions = currentRow?.sortedOptions || [];\n const currentLocked = currentRow ? isSectionLocked(currentRow.id, categories) : false;\n\n // Adjust column when current row's options change externally (e.g. option becomes disabled)\n useEffect(() => {\n if (!currentRow) return;\n\n const maxCol = currentOptions.length - 1;\n if (focusedCol > maxCol) {\n const newCol = Math.max(0, maxCol);\n setFocused(focusedRow, newCol);\n }\n }, [focusedRow, currentOptions, focusedCol, setFocused, currentRow]);\n\n // Bounce off locked sections when a section becomes locked (e.g. framework deselected)\n useEffect(() => {\n if (currentRow && currentLocked) {\n const nextUnlocked = findNextUnlockedIndex(processedCategories, focusedRow, categories);\n if (nextUnlocked !== focusedRow) {\n const newRowOptions = processedCategories[nextUnlocked]?.sortedOptions || [];\n const newCol = findValidStartColumn(newRowOptions);\n setFocused(nextUnlocked, newCol);\n }\n }\n }, [currentRow, currentLocked, focusedRow, processedCategories, categories, setFocused]);\n\n // Store the latest handler in a ref so that the useInput effect never needs to\n // re-register on the event emitter. This avoids a stale-closure race condition\n // where, after a domain switch (CategoryGrid remount via key={activeDomain}),\n // the useInput effect may not yet have re-registered the updated handler when\n // the first keypress arrives — causing the first space press to be silently lost.\n type InputKey = {\n leftArrow: boolean;\n rightArrow: boolean;\n upArrow: boolean;\n downArrow: boolean;\n tab: boolean;\n shift: boolean;\n };\n\n const handlerRef = useRef<((input: string, key: InputKey) => void) | null>(null);\n handlerRef.current = (input: string, key: InputKey) => {\n if (key.tab && key.shift) {\n onToggleLabels();\n return;\n }\n\n if (key.tab && !key.shift) {\n const nextSection = findNextUnlockedIndex(processedCategories, focusedRow, categories);\n if (nextSection !== focusedRow) {\n const newRowOptions = processedCategories[nextSection]?.sortedOptions || [];\n const newCol = findValidStartColumn(newRowOptions);\n setFocused(nextSection, newCol);\n }\n return;\n }\n\n if (input === \"d\" || input === \"D\") {\n onToggleLabels();\n return;\n }\n\n if (input === \" \") {\n if (currentLocked) return;\n const currentOption = currentOptions[focusedCol];\n if (currentOption && currentOption.state !== \"disabled\") {\n onToggle(currentRow.id, currentOption.id);\n }\n return;\n }\n\n const isLeft = key.leftArrow || input === \"h\";\n const isRight = key.rightArrow || input === \"l\";\n const isUp = key.upArrow || input === \"k\";\n const isDown = key.downArrow || input === \"j\";\n\n if (isLeft) {\n if (currentLocked) return;\n moveFocus(\"left\");\n } else if (isRight) {\n if (currentLocked) return;\n moveFocus(\"right\");\n } else if (isUp) {\n moveFocus(\"up\");\n } else if (isDown) {\n moveFocus(\"down\");\n }\n };\n\n // Stable handler reference — never changes, so useInput's effect registers once\n const stableHandler = useCallback((input: string, key: InputKey) => {\n handlerRef.current?.(input, key);\n }, []);\n\n useInput(stableHandler);\n}\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useRef","sorted","useCallback","useEffect"]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CLI_COLORS
4
- } from "./chunk-FVN5PFFY.js";
4
+ } from "./chunk-FTD5Z6QD.js";
5
5
  import {
6
6
  init_esm_shims
7
7
  } from "./chunk-DHET7RCE.js";
@@ -17,4 +17,4 @@ var ViewTitle = ({ children }) => {
17
17
  export {
18
18
  ViewTitle
19
19
  };
20
- //# sourceMappingURL=chunk-KWWLPPHF.js.map
20
+ //# sourceMappingURL=chunk-B4QYXVPZ.js.map
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ KEBAB_CASE_PATTERN
4
+ } from "./chunk-FTD5Z6QD.js";
2
5
  import {
3
6
  init_esm_shims
4
7
  } from "./chunk-DHET7RCE.js";
@@ -90,7 +93,14 @@ async function copy(src, dest) {
90
93
  // src/cli/lib/schemas.ts
91
94
  init_esm_shims();
92
95
  import { z } from "zod";
93
- var domainSchema = z.enum(["web", "api", "cli", "mobile", "shared"]);
96
+ var customExtensions = {
97
+ categories: /* @__PURE__ */ new Set(),
98
+ domains: /* @__PURE__ */ new Set(),
99
+ agentNames: /* @__PURE__ */ new Set(),
100
+ skillIds: /* @__PURE__ */ new Set()
101
+ };
102
+ var DOMAIN_VALUES = ["web", "api", "cli", "mobile", "shared"];
103
+ var domainSchema = z.enum(DOMAIN_VALUES);
94
104
  var skillSourceTypeSchema = z.enum([
95
105
  "public",
96
106
  "private",
@@ -144,7 +154,6 @@ var SUBCATEGORY_VALUES = [
144
154
  "cli-testing"
145
155
  ];
146
156
  var subcategorySchema = z.enum(SUBCATEGORY_VALUES);
147
- var stackSubcategorySchema = z.enum(SUBCATEGORY_VALUES);
148
157
  var agentNameSchema = z.enum([
149
158
  "web-developer",
150
159
  "api-developer",
@@ -264,15 +273,67 @@ var skillDisplayNameSchema = z.enum([
264
273
  "context-management"
265
274
  ]);
266
275
  var SKILL_ID_PATTERN = /^(web|api|cli|mobile|infra|meta|security)-.+-.+$/;
276
+ function isValidSkillId(id) {
277
+ return SKILL_ID_PATTERN.test(id) || customExtensions.skillIds.has(id);
278
+ }
267
279
  var skillIdSchema = z.string().regex(
268
280
  SKILL_ID_PATTERN,
269
281
  "Must be a valid skill ID (e.g., 'web-framework-react')"
270
282
  );
283
+ var extensibleDomainSchema = z.string().refine(
284
+ (val) => domainSchema.safeParse(val).success || customExtensions.domains.has(val),
285
+ { message: "Must be a valid domain" }
286
+ );
287
+ var extensibleSkillIdSchema = z.string().refine(
288
+ (val) => skillIdSchema.safeParse(val).success || customExtensions.skillIds.has(val),
289
+ { message: "Must be a valid skill ID" }
290
+ );
291
+ var extensibleSubcategorySchema = z.string().refine(
292
+ (val) => subcategorySchema.safeParse(val).success || customExtensions.categories.has(val),
293
+ { message: "Must be a valid subcategory" }
294
+ );
295
+ var extensibleAgentNameSchema = z.string().refine(
296
+ (val) => agentNameSchema.safeParse(val).success || customExtensions.agentNames.has(val),
297
+ { message: "Must be a valid agent name" }
298
+ );
299
+ function validateCategoryField(val, ctx) {
300
+ if (!val.category) return;
301
+ if (val.custom) {
302
+ if (!KEBAB_CASE_PATTERN.test(val.category)) {
303
+ ctx.addIssue({
304
+ code: "custom",
305
+ path: ["category"],
306
+ message: "Custom category must be kebab-case"
307
+ });
308
+ }
309
+ return;
310
+ }
311
+ const result = categoryPathSchema.safeParse(val.category);
312
+ if (!result.success) {
313
+ for (const issue of result.error.issues) {
314
+ ctx.addIssue({ ...issue, path: ["category"] });
315
+ }
316
+ }
317
+ }
318
+ function validateExtensibleRecordKeys(builtinSet, extensionSet, label) {
319
+ return (val, ctx) => {
320
+ for (const key of Object.keys(val)) {
321
+ if (!builtinSet.has(key) && !extensionSet.has(key)) {
322
+ ctx.addIssue({
323
+ code: "custom",
324
+ path: [key],
325
+ message: `Invalid ${label} '${key}'.`
326
+ });
327
+ }
328
+ }
329
+ };
330
+ }
271
331
  var categoryPathSchema = z.string().refine(
272
332
  (val) => {
273
333
  if (val === "local") return true;
274
334
  if (/^(web|api|cli|mobile|infra|meta|security|shared)-.+$/.test(val)) return true;
275
- return subcategorySchema.safeParse(val).success;
335
+ if (subcategorySchema.safeParse(val).success) return true;
336
+ return customExtensions.categories.has(val);
276
337
  },
277
338
  {
278
339
  message: "Must be a valid category path (e.g., 'web-framework', 'shared-testing', or 'local')"
@@ -298,7 +359,7 @@ var strictHooksRecordSchema = z.record(
298
359
  z.array(strictAgentHookDefinitionSchema)
299
360
  );
300
361
  var skillAssignmentSchema = z.object({
301
- id: skillIdSchema,
362
+ id: extensibleSkillIdSchema,
302
363
  preloaded: z.boolean().optional(),
303
364
  local: z.boolean().optional(),
304
365
  path: z.string().optional()
@@ -309,14 +370,17 @@ var skillFrontmatterLoaderSchema = z.object({
309
370
  model: modelNameSchema.optional()
310
371
  });
311
372
  var skillMetadataLoaderSchema = z.object({
312
- category: categoryPathSchema.optional(),
373
+ // Field accepts any string; cross-field validation in superRefine enforces strict/custom rules
374
+ category: z.string().optional(),
313
375
  categoryExclusive: z.boolean().optional(),
314
376
  author: z.string().optional(),
315
377
  tags: z.array(z.string()).optional(),
316
- requires: z.array(skillIdSchema).optional(),
317
- compatibleWith: z.array(skillIdSchema).optional(),
318
- conflictsWith: z.array(skillIdSchema).optional()
319
- }).passthrough();
378
+ requires: z.array(extensibleSkillIdSchema).optional(),
379
+ compatibleWith: z.array(extensibleSkillIdSchema).optional(),
380
+ conflictsWith: z.array(extensibleSkillIdSchema).optional(),
381
+ domain: extensibleDomainSchema.optional(),
382
+ custom: z.boolean().optional()
383
+ }).passthrough().superRefine(validateCategoryField);
320
384
  var pluginAuthorSchema = z.object({
321
385
  name: z.string().min(1),
322
386
  email: z.string().optional()
@@ -344,7 +408,7 @@ var pluginManifestValidationSchema = z.object({
344
408
  hooks: z.union([z.string(), strictHooksRecordSchema]).optional()
345
409
  }).strict();
346
410
  var agentYamlConfigSchema = z.object({
347
- id: agentNameSchema,
411
+ id: extensibleAgentNameSchema,
348
412
  title: z.string(),
349
413
  description: z.string(),
350
414
  model: modelNameSchema.optional(),
@@ -352,24 +416,22 @@ var agentYamlConfigSchema = z.object({
352
416
  disallowedTools: z.array(z.string()).optional(),
353
417
  permissionMode: permissionModeSchema.optional(),
354
418
  hooks: hooksRecordSchema.optional(),
355
- outputFormat: z.string().optional()
419
+ outputFormat: z.string().optional(),
420
+ domain: extensibleDomainSchema.optional(),
421
+ custom: z.boolean().optional()
356
422
  });
357
- var skillAssignmentElementSchema = z.union([skillIdSchema, skillAssignmentSchema]);
358
- var stackSubcategoryValues = new Set(stackSubcategorySchema.options);
423
+ var skillAssignmentElementSchema = z.union([extensibleSkillIdSchema, skillAssignmentSchema]);
424
+ var stackSubcategoryValues = new Set(SUBCATEGORY_VALUES);
359
425
  var stackAgentConfigSchema = z.record(
360
426
  z.string(),
361
427
  z.union([skillAssignmentElementSchema, z.array(skillAssignmentElementSchema)])
362
- ).superRefine((val, ctx) => {
363
- for (const key of Object.keys(val)) {
364
- if (!stackSubcategoryValues.has(key)) {
365
- ctx.addIssue({
366
- code: "custom",
367
- path: [key],
368
- message: `Invalid subcategory '${key}'. Must be one of: ${[...stackSubcategoryValues].join(", ")}`
369
- });
370
- }
371
- }
372
- });
428
+ ).superRefine(
429
+ validateExtensibleRecordKeys(
430
+ stackSubcategoryValues,
431
+ customExtensions.categories,
432
+ "subcategory"
433
+ )
434
+ );
373
435
  var projectConfigLoaderSchema = z.object({
374
436
  version: z.literal("1").optional(),
375
437
  /** Project/plugin name in kebab-case */
@@ -378,7 +440,7 @@ var projectConfigLoaderSchema = z.object({
378
440
  /** Agent IDs to compile (e.g., ["web-developer", "api-developer"]) */
379
441
  agents: z.array(z.string()).optional(),
380
442
  /** Flat list of all skill IDs used by this project */
381
- skills: z.array(skillIdSchema).optional(),
443
+ skills: z.array(extensibleSkillIdSchema).optional(),
382
444
  /** Author handle (e.g., "@vince") */
383
445
  author: z.string().optional(),
384
446
  /** "local" = .claude/agents, "plugin" = .claude/plugins/ (DEFAULT_PLUGIN_NAME) */
@@ -386,7 +448,7 @@ var projectConfigLoaderSchema = z.object({
386
448
  /** Whether expert mode (advanced/niche skills) was enabled in the wizard */
387
449
  expertMode: z.boolean().optional(),
388
450
  /** Selected domains from the wizard (persisted for edit mode restoration) */
389
- domains: z.array(domainSchema).optional(),
451
+ domains: z.array(extensibleDomainSchema).optional(),
390
452
  /** Selected agents from the wizard (persisted for edit mode restoration) */
391
453
  selectedAgents: z.array(z.string()).optional(),
392
454
  /** Agent-to-subcategory-to-skill mappings from selected stack (accepts same formats as stacks.yaml) */
@@ -415,6 +477,8 @@ var projectConfigValidationSchema = z.object({
415
477
  expertMode: z.boolean().optional(),
416
478
  /** Selected domains from the wizard (persisted for edit mode restoration) */
417
479
  domains: z.array(domainSchema).optional(),
480
+ /** Selected agents from the wizard (persisted for edit mode restoration) */
481
+ selectedAgents: z.array(z.string()).optional(),
418
482
  /** Agent-to-subcategory-to-skill mappings from selected stack */
419
483
  stack: z.record(z.string(), stackAgentConfigSchema),
420
484
  /** Skills source path or URL (e.g., "github:my-org/skills") */
@@ -425,14 +489,15 @@ var projectConfigValidationSchema = z.object({
425
489
  agentsSource: z.string().optional()
426
490
  });
427
491
  var categoryDefinitionSchema = z.object({
428
- id: subcategorySchema,
492
+ id: extensibleSubcategorySchema,
429
493
  displayName: z.string(),
430
494
  description: z.string(),
431
- domain: domainSchema.optional(),
495
+ domain: extensibleDomainSchema.optional(),
432
496
  exclusive: z.boolean(),
433
497
  required: z.boolean(),
434
498
  order: z.number(),
435
- icon: z.string().optional()
499
+ icon: z.string().optional(),
500
+ custom: z.boolean().optional()
436
501
  });
437
502
  var skillRefInYaml = z.string();
438
503
  var conflictRuleSchema = z.object({
@@ -465,11 +530,18 @@ var relationshipDefinitionsSchema = z.object({
465
530
  requires: z.array(requireRuleSchema),
466
531
  alternatives: z.array(alternativeGroupSchema)
467
532
  });
533
+ var builtinSubcategoryValues = new Set(SUBCATEGORY_VALUES);
468
534
  var skillsMatrixConfigSchema = z.object({
469
535
  version: z.string(),
470
- categories: z.record(subcategorySchema, categoryDefinitionSchema),
471
- relationships: relationshipDefinitionsSchema,
472
- skillAliases: z.record(skillDisplayNameSchema, skillIdSchema)
536
+ categories: z.record(z.string(), categoryDefinitionSchema).superRefine(
537
+ validateExtensibleRecordKeys(
538
+ builtinSubcategoryValues,
539
+ customExtensions.categories,
540
+ "category key"
541
+ )
542
+ ),
543
+ relationships: relationshipDefinitionsSchema.optional(),
544
+ skillAliases: z.record(z.string(), extensibleSkillIdSchema).optional()
473
545
  });
474
546
  var localRawMetadataSchema = z.object({
475
547
  /** Short name shown in the wizard grid (e.g., "my-custom-react") */
@@ -477,23 +549,28 @@ var localRawMetadataSchema = z.object({
477
549
  /** One-line description for the wizard */
478
550
  cliDescription: z.string().optional(),
479
551
  /** Subcategory to place this skill in (e.g., "web-framework") */
480
- category: categoryPathSchema.optional(),
552
+ // Field accepts any string; cross-field validation in superRefine enforces strict/custom rules
553
+ category: z.string().optional(),
481
554
  /** If true, only one skill from this category can be selected */
482
555
  categoryExclusive: z.boolean().optional(),
483
556
  /** When an AI agent should invoke this skill */
484
557
  usageGuidance: z.string().optional(),
485
558
  tags: z.array(z.string()).optional(),
486
559
  /** Framework skills this is compatible with (for Build step filtering) */
487
- compatibleWith: z.array(skillIdSchema).optional(),
560
+ compatibleWith: z.array(extensibleSkillIdSchema).optional(),
488
561
  /** Skills that cannot coexist with this one */
489
- conflictsWith: z.array(skillIdSchema).optional(),
562
+ conflictsWith: z.array(extensibleSkillIdSchema).optional(),
490
563
  /** Skills that must be selected before this one */
491
- requires: z.array(skillIdSchema).optional(),
564
+ requires: z.array(extensibleSkillIdSchema).optional(),
492
565
  /** Setup skills that must be installed first (e.g., env setup) */
493
- requiresSetup: z.array(skillIdSchema).optional(),
566
+ requiresSetup: z.array(extensibleSkillIdSchema).optional(),
494
567
  /** Usage skills this setup skill configures (inverse relationship) */
495
- providesSetupFor: z.array(skillIdSchema).optional()
496
- }).passthrough();
568
+ providesSetupFor: z.array(extensibleSkillIdSchema).optional(),
569
+ /** Explicit domain assignment (overrides inference from category prefix) */
570
+ domain: extensibleDomainSchema.optional(),
571
+ /** True if this skill was created outside the CLI's built-in vocabulary */
572
+ custom: z.boolean().optional()
573
+ }).passthrough().superRefine(validateCategoryField);
497
574
  var localSkillMetadataSchema = z.object({
498
575
  forkedFrom: z.object({
499
576
  /** Original skill ID before forking (e.g., "web-framework-react") */
@@ -552,12 +629,6 @@ var marketplaceSchema = z.object({
552
629
  metadata: marketplaceMetadataSchema.optional(),
553
630
  plugins: z.array(marketplacePluginSchema).min(1)
554
631
  });
555
- var defaultMappingsSchema = z.object({
556
- /** Maps skill path patterns to the agent IDs that should receive them */
557
- skillToAgents: z.record(z.string(), z.array(z.string())),
558
- /** Maps agent IDs to skill ID prefixes they should receive by default */
559
- agentSkillPrefixes: z.record(z.string(), z.array(z.string())).optional()
560
- });
561
632
  var permissionConfigSchema = z.object({
562
633
  /** Tool names or patterns to explicitly allow */
563
634
  allow: z.array(z.string()).optional(),
@@ -639,7 +710,6 @@ var projectSourceConfigValidationSchema = z.object({
639
710
  stacksFile: z.string().optional(),
640
711
  matrixFile: z.string().optional()
641
712
  });
642
- var KEBAB_CASE_PATTERN = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
643
713
  var agentYamlGenerationSchema = z.object({
644
714
  $schema: z.string().optional(),
645
715
  id: z.string().min(1),
@@ -650,7 +720,9 @@ var agentYamlGenerationSchema = z.object({
650
720
  disallowedTools: z.array(z.string()).optional(),
651
721
  permissionMode: permissionModeSchema.optional(),
652
722
  hooks: strictHooksRecordSchema.optional(),
653
- outputFormat: z.string().optional()
723
+ outputFormat: z.string().optional(),
724
+ domain: extensibleDomainSchema.optional(),
725
+ custom: z.boolean().optional()
654
726
  }).strict();
655
727
  var agentFrontmatterValidationSchema = z.object({
656
728
  /** Agent name in kebab-case (becomes the Task tool identifier) */
@@ -685,7 +757,7 @@ var skillFrontmatterValidationSchema = z.object({
685
757
  }).strict();
686
758
  var metadataValidationSchema = z.object({
687
759
  /** Domain-prefixed subcategory (e.g., "web-framework") */
688
- category: subcategorySchema,
760
+ category: extensibleSubcategorySchema,
689
761
  categoryExclusive: z.boolean().optional(),
690
762
  /** Author handle — must start with @ (e.g., "@vince") */
691
763
  author: z.string().regex(/^@[a-z][a-z0-9-]*$/),
@@ -718,7 +790,11 @@ var metadataValidationSchema = z.object({
718
790
  source: z.string().optional(),
719
791
  /** ISO date of the fork */
720
792
  date: z.string()
721
- }).optional()
793
+ }).optional(),
794
+ /** Explicit domain assignment (overrides inference from category prefix) */
795
+ domain: extensibleDomainSchema.optional(),
796
+ /** True if this skill was created outside the CLI's built-in vocabulary */
797
+ custom: z.boolean().optional()
722
798
  }).strict();
723
799
  var stackSkillAssignmentSchema = z.object({
724
800
  id: z.string().min(1),
@@ -799,6 +875,20 @@ function warnUnknownFields(parsed, expectedKeys, context) {
799
875
  warn(`Unknown fields in ${context}: ${unknownKeys.join(", ")}`);
800
876
  }
801
877
  }
878
+ function extendSchemasWithCustomValues(options) {
879
+ for (const category of options.categories ?? []) {
880
+ customExtensions.categories.add(category);
881
+ }
882
+ for (const domain of options.domains ?? []) {
883
+ customExtensions.domains.add(domain);
884
+ }
885
+ for (const agentName of options.agentNames ?? []) {
886
+ customExtensions.agentNames.add(agentName);
887
+ }
888
+ for (const skillId of options.skillIds ?? []) {
889
+ customExtensions.skillIds.add(skillId);
890
+ }
891
+ }
802
892
 
803
893
  export {
804
894
  getErrorMessage,
@@ -817,9 +907,14 @@ export {
817
907
  ensureDir,
818
908
  remove,
819
909
  copy,
910
+ DOMAIN_VALUES,
911
+ SUBCATEGORY_VALUES,
912
+ agentNameSchema,
820
913
  skillDisplayNameSchema,
821
914
  SKILL_ID_PATTERN,
915
+ isValidSkillId,
822
916
  skillIdSchema,
917
+ extensibleDomainSchema,
823
918
  categoryPathSchema,
824
919
  hooksRecordSchema,
825
920
  skillFrontmatterLoaderSchema,
@@ -833,7 +928,6 @@ export {
833
928
  localSkillMetadataSchema,
834
929
  stacksConfigSchema,
835
930
  marketplaceSchema,
836
- defaultMappingsSchema,
837
931
  settingsFileSchema,
838
932
  importedSkillMetadataSchema,
839
933
  projectSourceConfigSchema,
@@ -844,6 +938,7 @@ export {
844
938
  stackConfigValidationSchema,
845
939
  formatZodErrors,
846
940
  validateNestingDepth,
847
- warnUnknownFields
941
+ warnUnknownFields,
942
+ extendSchemasWithCustomValues
848
943
  };
849
- //# sourceMappingURL=chunk-473YHDYQ.js.map
944
+ //# sourceMappingURL=chunk-C4QI54PN.js.map