@agents-inc/cli 0.34.1 → 0.38.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 (196) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/config/skills-matrix.yaml +26 -26
  3. package/config/stacks.yaml +8 -8
  4. package/dist/{chunk-HTTPKSL6.js → chunk-2XX4TMCI.js} +2 -2
  5. package/dist/{chunk-CEWNZQMH.js → chunk-3E2V5YL3.js} +8 -2
  6. package/dist/chunk-3E2V5YL3.js.map +1 -0
  7. package/dist/{chunk-QC37C32G.js → chunk-3NQJOJZL.js} +3 -3
  8. package/dist/chunk-54ZZCWN4.js +51 -0
  9. package/dist/chunk-54ZZCWN4.js.map +1 -0
  10. package/dist/{chunk-VEZ2GZEK.js → chunk-ATLRUR3B.js} +2 -2
  11. package/dist/{chunk-ZI5EBHCC.js → chunk-CYFU3ARY.js} +38 -17
  12. package/dist/chunk-CYFU3ARY.js.map +1 -0
  13. package/dist/{chunk-X7SPCWY6.js → chunk-DUQFF45G.js} +14 -9
  14. package/dist/chunk-DUQFF45G.js.map +1 -0
  15. package/dist/{chunk-CB7GYRUP.js → chunk-EISBUEBL.js} +101 -52
  16. package/dist/chunk-EISBUEBL.js.map +1 -0
  17. package/dist/chunk-EXFVAEPY.js +80 -0
  18. package/dist/chunk-EXFVAEPY.js.map +1 -0
  19. package/dist/{chunk-ANGAZ444.js → chunk-FWQK3HWB.js} +4 -4
  20. package/dist/chunk-FWQK3HWB.js.map +1 -0
  21. package/dist/{chunk-DC5AK3LW.js → chunk-GG4BSB6S.js} +5 -11
  22. package/dist/chunk-GG4BSB6S.js.map +1 -0
  23. package/dist/{chunk-GGHH3KR2.js → chunk-HKDE4LJW.js} +2 -2
  24. package/dist/{chunk-R3AD6XBJ.js → chunk-HRMQ2RGY.js} +81 -26
  25. package/dist/chunk-HRMQ2RGY.js.map +1 -0
  26. package/dist/{chunk-YPJKOM42.js → chunk-HRW7BIDE.js} +2 -2
  27. package/dist/{chunk-LFHZBF6N.js → chunk-IVIK776Y.js} +4 -3
  28. package/dist/chunk-IVIK776Y.js.map +1 -0
  29. package/dist/{chunk-ALS7SH7X.js → chunk-IWNPFIGY.js} +38 -48
  30. package/dist/chunk-IWNPFIGY.js.map +1 -0
  31. package/dist/{chunk-GIFEDW27.js → chunk-IZRVFC2Z.js} +7 -7
  32. package/dist/chunk-IZRVFC2Z.js.map +1 -0
  33. package/dist/chunk-K77I4XGL.js +47 -0
  34. package/dist/chunk-K77I4XGL.js.map +1 -0
  35. package/dist/{chunk-JMVWYAHT.js → chunk-KUV24B5M.js} +4 -4
  36. package/dist/chunk-KUV24B5M.js.map +1 -0
  37. package/dist/{chunk-KENWMEKN.js → chunk-M6PGIZNS.js} +6 -6
  38. package/dist/{chunk-B47QYIUL.js → chunk-NFV4SKH5.js} +4 -4
  39. package/dist/chunk-NI2RSNWB.js +156 -0
  40. package/dist/chunk-NI2RSNWB.js.map +1 -0
  41. package/dist/{chunk-ZP4BI6J2.js → chunk-OEX5JDQD.js} +7 -7
  42. package/dist/chunk-OEX5JDQD.js.map +1 -0
  43. package/dist/{chunk-OKILA27U.js → chunk-TA6IIQI4.js} +86 -99
  44. package/dist/chunk-TA6IIQI4.js.map +1 -0
  45. package/dist/{chunk-JZOLJVWA.js → chunk-TBDIR6LY.js} +12 -11
  46. package/dist/chunk-TBDIR6LY.js.map +1 -0
  47. package/dist/{chunk-XYCN2GCV.js → chunk-TNFACSWF.js} +3 -3
  48. package/dist/{chunk-ZE355C6C.js → chunk-TY5GELDB.js} +9 -4
  49. package/dist/chunk-TY5GELDB.js.map +1 -0
  50. package/dist/{chunk-TM4I4EHK.js → chunk-U5OB5ADP.js} +2829 -2793
  51. package/dist/chunk-U5OB5ADP.js.map +1 -0
  52. package/dist/{chunk-JXMRTHDT.js → chunk-UOMMQ5M6.js} +2 -2
  53. package/dist/{chunk-A5CYQQVG.js → chunk-UV6JUGIY.js} +2 -2
  54. package/dist/{chunk-5YNZJ5TP.js → chunk-VAHVSQIG.js} +2 -2
  55. package/dist/{chunk-TKB4O2RY.js → chunk-VAK5PX72.js} +5 -5
  56. package/dist/chunk-WSGGJKD5.js +113 -0
  57. package/dist/chunk-WSGGJKD5.js.map +1 -0
  58. package/dist/{chunk-GVMA2EKC.js → chunk-YHQNTBBN.js} +2 -2
  59. package/dist/{chunk-NLR6Z37M.js → chunk-YJIJTBSX.js} +81 -97
  60. package/dist/chunk-YJIJTBSX.js.map +1 -0
  61. package/dist/{chunk-YCS7GF6Y.js → chunk-ZBJQXDQN.js} +3 -1
  62. package/dist/{chunk-YCS7GF6Y.js.map → chunk-ZBJQXDQN.js.map} +1 -1
  63. package/dist/cli/defaults/agent-mappings.yaml +4 -4
  64. package/dist/commands/build/marketplace.js +3 -3
  65. package/dist/commands/build/plugins.js +5 -5
  66. package/dist/commands/build/stack.js +5 -5
  67. package/dist/commands/compile.js +25 -19
  68. package/dist/commands/compile.js.map +1 -1
  69. package/dist/commands/config/get.js +8 -8
  70. package/dist/commands/config/get.js.map +1 -1
  71. package/dist/commands/config/index.js +5 -5
  72. package/dist/commands/config/path.js +4 -4
  73. package/dist/commands/config/set-project.js +7 -7
  74. package/dist/commands/config/set-project.js.map +1 -1
  75. package/dist/commands/config/show.js +5 -5
  76. package/dist/commands/config/unset-project.js +5 -5
  77. package/dist/commands/config/unset-project.js.map +1 -1
  78. package/dist/commands/diff.js +8 -8
  79. package/dist/commands/diff.js.map +1 -1
  80. package/dist/commands/doctor.js +4 -4
  81. package/dist/commands/edit.js +37 -28
  82. package/dist/commands/edit.js.map +1 -1
  83. package/dist/commands/eject.js +6 -6
  84. package/dist/commands/eject.js.map +1 -1
  85. package/dist/commands/import/skill.js +16 -16
  86. package/dist/commands/import/skill.js.map +1 -1
  87. package/dist/commands/info.js +5 -5
  88. package/dist/commands/init.js +31 -26
  89. package/dist/commands/init.js.map +1 -1
  90. package/dist/commands/list.js +4 -4
  91. package/dist/commands/new/agent.js +5 -5
  92. package/dist/commands/new/skill.js +8 -8
  93. package/dist/commands/new/skill.js.map +1 -1
  94. package/dist/commands/outdated.js +4 -4
  95. package/dist/commands/search.js +7 -7
  96. package/dist/commands/uninstall.js +181 -97
  97. package/dist/commands/uninstall.js.map +1 -1
  98. package/dist/commands/update.js +6 -6
  99. package/dist/commands/validate.js +5 -5
  100. package/dist/commands/version/bump.js +4 -4
  101. package/dist/commands/version/index.js +4 -4
  102. package/dist/commands/version/set.js +4 -4
  103. package/dist/commands/version/show.js +4 -4
  104. package/dist/components/skill-search/skill-search.js +3 -3
  105. package/dist/components/wizard/category-grid.js +3 -3
  106. package/dist/components/wizard/category-grid.test.js +42 -21
  107. package/dist/components/wizard/category-grid.test.js.map +1 -1
  108. package/dist/components/wizard/checkbox-grid.js +10 -0
  109. package/dist/components/wizard/checkbox-grid.test.js +260 -0
  110. package/dist/components/wizard/checkbox-grid.test.js.map +1 -0
  111. package/dist/components/wizard/domain-selection.js +7 -5
  112. package/dist/components/wizard/help-modal.js +2 -2
  113. package/dist/components/wizard/menu-item.js +2 -2
  114. package/dist/components/wizard/search-modal.js +3 -3
  115. package/dist/components/wizard/search-modal.test.js +3 -3
  116. package/dist/components/wizard/section-progress.js +2 -2
  117. package/dist/components/wizard/section-progress.test.js +2 -2
  118. package/dist/components/wizard/source-grid.js +5 -5
  119. package/dist/components/wizard/source-grid.test.js +5 -5
  120. package/dist/components/wizard/stack-selection.js +8 -7
  121. package/dist/components/wizard/step-agents.js +16 -0
  122. package/dist/components/wizard/step-agents.js.map +1 -0
  123. package/dist/components/wizard/step-agents.test.js +185 -0
  124. package/dist/components/wizard/step-agents.test.js.map +1 -0
  125. package/dist/components/wizard/step-build.js +9 -7
  126. package/dist/components/wizard/step-build.test.js +25 -22
  127. package/dist/components/wizard/step-build.test.js.map +1 -1
  128. package/dist/components/wizard/step-confirm.js +2 -2
  129. package/dist/components/wizard/step-confirm.test.js +6 -5
  130. package/dist/components/wizard/step-confirm.test.js.map +1 -1
  131. package/dist/components/wizard/step-refine.js +2 -2
  132. package/dist/components/wizard/step-refine.test.js +2 -2
  133. package/dist/components/wizard/step-settings.js +6 -6
  134. package/dist/components/wizard/step-settings.test.js +9 -9
  135. package/dist/components/wizard/step-sources.js +12 -10
  136. package/dist/components/wizard/step-sources.test.js +14 -12
  137. package/dist/components/wizard/step-sources.test.js.map +1 -1
  138. package/dist/components/wizard/step-stack.js +11 -9
  139. package/dist/components/wizard/step-stack.test.js +12 -10
  140. package/dist/components/wizard/step-stack.test.js.map +1 -1
  141. package/dist/components/wizard/view-title.js +2 -2
  142. package/dist/components/wizard/wizard-layout.js +8 -7
  143. package/dist/components/wizard/wizard-tabs.js +2 -2
  144. package/dist/components/wizard/wizard-tabs.test.js +6 -4
  145. package/dist/components/wizard/wizard-tabs.test.js.map +1 -1
  146. package/dist/components/wizard/wizard.js +27 -23
  147. package/dist/config/skills-matrix.yaml +26 -26
  148. package/dist/config/stacks.yaml +8 -8
  149. package/dist/hooks/init.js +5 -3
  150. package/dist/hooks/init.js.map +1 -1
  151. package/dist/{source-manager-WJYANKDI.js → source-manager-FEGVYDFZ.js} +4 -4
  152. package/dist/source-manager-FEGVYDFZ.js.map +1 -0
  153. package/dist/stores/wizard-store.js +5 -4
  154. package/dist/stores/wizard-store.test.js +287 -15
  155. package/dist/stores/wizard-store.test.js.map +1 -1
  156. package/package.json +1 -1
  157. package/src/schemas/agent.schema.json +3 -3
  158. package/src/schemas/metadata.schema.json +14 -14
  159. package/src/schemas/project-config.schema.json +46 -2
  160. package/src/schemas/project-source-config.schema.json +17 -5
  161. package/src/schemas/skills-matrix.schema.json +4 -4
  162. package/src/schemas/stack.schema.json +1 -1
  163. package/src/schemas/stacks.schema.json +42 -1
  164. package/dist/chunk-2LUXM5FB.js +0 -277
  165. package/dist/chunk-2LUXM5FB.js.map +0 -1
  166. package/dist/chunk-ALS7SH7X.js.map +0 -1
  167. package/dist/chunk-ANGAZ444.js.map +0 -1
  168. package/dist/chunk-CB7GYRUP.js.map +0 -1
  169. package/dist/chunk-CEWNZQMH.js.map +0 -1
  170. package/dist/chunk-DC5AK3LW.js.map +0 -1
  171. package/dist/chunk-GIFEDW27.js.map +0 -1
  172. package/dist/chunk-JMVWYAHT.js.map +0 -1
  173. package/dist/chunk-JZOLJVWA.js.map +0 -1
  174. package/dist/chunk-LFHZBF6N.js.map +0 -1
  175. package/dist/chunk-NLR6Z37M.js.map +0 -1
  176. package/dist/chunk-OKILA27U.js.map +0 -1
  177. package/dist/chunk-R3AD6XBJ.js.map +0 -1
  178. package/dist/chunk-TM4I4EHK.js.map +0 -1
  179. package/dist/chunk-X7SPCWY6.js.map +0 -1
  180. package/dist/chunk-ZE355C6C.js.map +0 -1
  181. package/dist/chunk-ZI5EBHCC.js.map +0 -1
  182. package/dist/chunk-ZP4BI6J2.js.map +0 -1
  183. /package/dist/{chunk-HTTPKSL6.js.map → chunk-2XX4TMCI.js.map} +0 -0
  184. /package/dist/{chunk-QC37C32G.js.map → chunk-3NQJOJZL.js.map} +0 -0
  185. /package/dist/{chunk-VEZ2GZEK.js.map → chunk-ATLRUR3B.js.map} +0 -0
  186. /package/dist/{chunk-GGHH3KR2.js.map → chunk-HKDE4LJW.js.map} +0 -0
  187. /package/dist/{chunk-YPJKOM42.js.map → chunk-HRW7BIDE.js.map} +0 -0
  188. /package/dist/{chunk-KENWMEKN.js.map → chunk-M6PGIZNS.js.map} +0 -0
  189. /package/dist/{chunk-B47QYIUL.js.map → chunk-NFV4SKH5.js.map} +0 -0
  190. /package/dist/{chunk-XYCN2GCV.js.map → chunk-TNFACSWF.js.map} +0 -0
  191. /package/dist/{chunk-JXMRTHDT.js.map → chunk-UOMMQ5M6.js.map} +0 -0
  192. /package/dist/{chunk-A5CYQQVG.js.map → chunk-UV6JUGIY.js.map} +0 -0
  193. /package/dist/{chunk-5YNZJ5TP.js.map → chunk-VAHVSQIG.js.map} +0 -0
  194. /package/dist/{chunk-TKB4O2RY.js.map → chunk-VAK5PX72.js.map} +0 -0
  195. /package/dist/{chunk-GVMA2EKC.js.map → chunk-YHQNTBBN.js.map} +0 -0
  196. /package/dist/{source-manager-WJYANKDI.js.map → components/wizard/checkbox-grid.js.map} +0 -0
@@ -1,21 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  SourceGrid
4
- } from "./chunk-CB7GYRUP.js";
4
+ } from "./chunk-EISBUEBL.js";
5
+ import {
6
+ useMeasuredHeight
7
+ } from "./chunk-K77I4XGL.js";
5
8
  import {
6
9
  ViewTitle
7
- } from "./chunk-HTTPKSL6.js";
10
+ } from "./chunk-2XX4TMCI.js";
8
11
  import {
9
12
  useWizardStore
10
- } from "./chunk-R3AD6XBJ.js";
13
+ } from "./chunk-HRMQ2RGY.js";
11
14
  import {
12
15
  resolveAllSources,
13
16
  searchExtraSources
14
- } from "./chunk-TM4I4EHK.js";
17
+ } from "./chunk-U5OB5ADP.js";
15
18
  import {
16
19
  CLI_COLORS,
17
20
  DEFAULT_BRANDING
18
- } from "./chunk-YCS7GF6Y.js";
21
+ } from "./chunk-ZBJQXDQN.js";
19
22
  import {
20
23
  init_esm_shims
21
24
  } from "./chunk-DHET7RCE.js";
@@ -35,6 +38,7 @@ var StepSources = ({
35
38
  const [view, setView] = useState("choice");
36
39
  const [choiceIndex, setChoiceIndex] = useState(0);
37
40
  const [isGridSearching, setIsGridSearching] = useState(false);
41
+ const { ref: gridRef, measuredHeight: gridHeight } = useMeasuredHeight();
38
42
  const handleGridSelect = useCallback(
39
43
  (skillId, sourceId) => {
40
44
  store.setSourceSelection(skillId, sourceId);
@@ -97,18 +101,19 @@ var StepSources = ({
97
101
  });
98
102
  if (view === "customize") {
99
103
  const rows2 = store.buildSourceRows(matrix);
100
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: "100%", children: [
104
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: "100%", flexGrow: 1, flexBasis: 0, children: [
101
105
  /* @__PURE__ */ jsx(ViewTitle, { children: "Customize skill sources" }),
102
- /* @__PURE__ */ jsx(
106
+ /* @__PURE__ */ jsx(Box, { ref: gridRef, flexGrow: 1, flexBasis: 0, children: /* @__PURE__ */ jsx(
103
107
  SourceGrid,
104
108
  {
105
109
  rows: rows2,
110
+ availableHeight: gridHeight,
106
111
  onSelect: handleGridSelect,
107
112
  onSearch: handleSearch,
108
113
  onBind: handleBind,
109
114
  onSearchStateChange: handleSearchStateChange
110
115
  }
111
- )
116
+ ) })
112
117
  ] });
113
118
  }
114
119
  const selectedTechnologies = store.getAllSelectedTechnologies();
@@ -186,4 +191,4 @@ var StepSources = ({
186
191
  export {
187
192
  StepSources
188
193
  };
189
- //# sourceMappingURL=chunk-X7SPCWY6.js.map
194
+ //# sourceMappingURL=chunk-DUQFF45G.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/wizard/step-sources.tsx"],"sourcesContent":["import React, { useState, useCallback } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useWizardStore } from \"../../stores/wizard-store.js\";\nimport type {\n BoundSkillCandidate,\n MergedSkillsMatrix,\n SkillAlias,\n SkillId,\n} from \"../../types/index.js\";\nimport { CLI_COLORS, DEFAULT_BRANDING } from \"../../consts.js\";\nimport { useMeasuredHeight } from \"../hooks/use-measured-height.js\";\nimport { SourceGrid } from \"./source-grid.js\";\nimport { ViewTitle } from \"./view-title.js\";\nimport { searchExtraSources } from \"../../lib/loading/multi-source-loader.js\";\nimport { resolveAllSources } from \"../../lib/configuration/index.js\";\n\nexport type StepSourcesProps = {\n matrix: MergedSkillsMatrix;\n projectDir?: string;\n onContinue: () => void;\n onBack: () => void;\n};\n\ntype SourcesView = \"choice\" | \"customize\";\n\nexport const StepSources: React.FC<StepSourcesProps> = ({\n matrix,\n projectDir,\n onContinue,\n onBack,\n}) => {\n const store = useWizardStore();\n const [view, setView] = useState<SourcesView>(\"choice\");\n const [choiceIndex, setChoiceIndex] = useState(0);\n const [isGridSearching, setIsGridSearching] = useState(false);\n const { ref: gridRef, measuredHeight: gridHeight } = useMeasuredHeight();\n\n const handleGridSelect = useCallback(\n (skillId: SkillId, sourceId: string) => {\n store.setSourceSelection(skillId, sourceId);\n },\n [store],\n );\n\n const handleSearch = useCallback(\n async (alias: SkillAlias): Promise<BoundSkillCandidate[]> => {\n if (!projectDir) return [];\n try {\n const sources = await resolveAllSources(projectDir);\n return await searchExtraSources(alias, sources.extras);\n } catch {\n return [];\n }\n },\n [projectDir],\n );\n\n const handleBind = useCallback(\n (candidate: BoundSkillCandidate) => {\n store.bindSkill({\n id: candidate.id,\n sourceUrl: candidate.sourceUrl,\n sourceName: candidate.sourceName,\n boundTo: candidate.alias,\n description: candidate.description,\n });\n },\n [store],\n );\n\n const handleSearchStateChange = useCallback((active: boolean) => {\n setIsGridSearching(active);\n }, []);\n\n useInput((_input, key) => {\n if (view === \"choice\") {\n if (key.return) {\n if (choiceIndex === 0) {\n onContinue();\n } else {\n store.setCustomizeSources(true);\n setView(\"customize\");\n }\n }\n if (key.escape) {\n onBack();\n }\n if (key.upArrow || key.downArrow) {\n setChoiceIndex((prev) => (prev === 0 ? 1 : 0));\n }\n } else if (view === \"customize\") {\n if (isGridSearching) return;\n\n if (key.return) {\n onContinue();\n }\n if (key.escape) {\n store.setCustomizeSources(false);\n setView(\"choice\");\n }\n }\n });\n\n if (view === \"customize\") {\n const rows = store.buildSourceRows(matrix);\n return (\n <Box flexDirection=\"column\" width=\"100%\" flexGrow={1} flexBasis={0}>\n <ViewTitle>Customize skill sources</ViewTitle>\n <Box ref={gridRef} flexGrow={1} flexBasis={0}>\n <SourceGrid\n rows={rows}\n availableHeight={gridHeight}\n onSelect={handleGridSelect}\n onSearch={handleSearch}\n onBind={handleBind}\n onSearchStateChange={handleSearchStateChange}\n />\n </Box>\n </Box>\n );\n }\n\n const selectedTechnologies = store.getAllSelectedTechnologies();\n const rows = store.buildSourceRows(matrix);\n const isRecommendedSelected = choiceIndex === 0;\n const hasLocalSkills = rows.some((row) =>\n row.options.some((o) => o.installed && o.id === \"local\"),\n );\n\n return (\n <Box flexDirection=\"column\" paddingX={2}>\n <Text>\n Your stack includes{\" \"}\n <Text color={CLI_COLORS.PRIMARY} bold>\n {selectedTechnologies.length}\n </Text>{\" \"}\n technologies.\n </Text>\n <Text> </Text>\n\n <Box\n borderStyle=\"round\"\n borderColor={isRecommendedSelected ? CLI_COLORS.SUCCESS : CLI_COLORS.NEUTRAL}\n paddingX={2}\n paddingY={1}\n marginBottom={1}\n >\n <Box flexDirection=\"column\">\n <Text\n color={isRecommendedSelected ? CLI_COLORS.SUCCESS : undefined}\n bold={isRecommendedSelected}\n >\n {isRecommendedSelected ? \">\" : \"\\u25CB\"}{\" \"}\n {hasLocalSkills\n ? \"Use installed skill sources\"\n : \"Use all recommended skills (verified)\"}\n </Text>\n <Text> </Text>\n <Text dimColor>\n {hasLocalSkills\n ? \"Keep your current local and public skill selections.\"\n : \"This is the fastest option. All skills are verified and\"}\n </Text>\n {!hasLocalSkills && <Text dimColor>maintained by {DEFAULT_BRANDING.NAME}</Text>}\n </Box>\n </Box>\n\n <Box\n borderStyle=\"round\"\n borderColor={!isRecommendedSelected ? CLI_COLORS.SUCCESS : CLI_COLORS.NEUTRAL}\n paddingX={2}\n paddingY={1}\n >\n <Box flexDirection=\"column\">\n <Text\n color={!isRecommendedSelected ? CLI_COLORS.SUCCESS : undefined}\n bold={!isRecommendedSelected}\n >\n {!isRecommendedSelected ? \">\" : \"\\u25CB\"} Customize skill sources\n </Text>\n <Text> </Text>\n <Text dimColor>Choose alternative skills for each technology</Text>\n </Box>\n </Box>\n </Box>\n );\n};\n"],"mappings":"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","names":["rows"]}
@@ -2,24 +2,24 @@
2
2
  import {
3
3
  useModalState
4
4
  } from "./chunk-7SOPVGDV.js";
5
- import {
6
- useFocusedListItem
7
- } from "./chunk-DC5AK3LW.js";
8
5
  import {
9
6
  SearchModal
10
- } from "./chunk-QC37C32G.js";
7
+ } from "./chunk-3NQJOJZL.js";
8
+ import {
9
+ useFocusedListItem
10
+ } from "./chunk-GG4BSB6S.js";
11
11
  import {
12
12
  CLI_COLORS,
13
- UI_SYMBOLS
14
- } from "./chunk-YCS7GF6Y.js";
13
+ SCROLL_VIEWPORT
14
+ } from "./chunk-ZBJQXDQN.js";
15
15
  import {
16
16
  init_esm_shims
17
17
  } from "./chunk-DHET7RCE.js";
18
18
 
19
19
  // src/cli/components/wizard/source-grid.tsx
20
20
  init_esm_shims();
21
- import { useCallback as useCallback2 } from "react";
22
- import { Box, Text, useInput } from "ink";
21
+ import { useCallback as useCallback2, useEffect, useRef, useState as useState2 } from "react";
22
+ import { Box, Text, measureElement, useInput } from "ink";
23
23
 
24
24
  // src/cli/components/hooks/use-source-grid-search-modal.ts
25
25
  init_esm_shims();
@@ -87,18 +87,28 @@ var SourceTag = ({
87
87
  option,
88
88
  isFocused
89
89
  }) => {
90
- const borderColor = option.selected ? CLI_COLORS.PRIMARY : isFocused ? CLI_COLORS.UNFOCUSED : CLI_COLORS.NEUTRAL;
91
- const textColor = option.selected ? CLI_COLORS.PRIMARY : void 0;
90
+ const getBorderColor = () => {
91
+ if (isFocused) {
92
+ return option.selected ? CLI_COLORS.PRIMARY : CLI_COLORS.UNFOCUSED;
93
+ }
94
+ return CLI_COLORS.NEUTRAL;
95
+ };
96
+ const textColor = option.selected ? CLI_COLORS.PRIMARY : CLI_COLORS.NEUTRAL;
92
97
  const isBold = isFocused || option.selected;
93
- const symbol = option.selected ? UI_SYMBOLS.SELECTED : UI_SYMBOLS.UNSELECTED;
94
- return /* @__PURE__ */ jsx(Box, { marginRight: 1, borderColor, borderStyle: "single", children: /* @__PURE__ */ jsxs(Text, { color: textColor, bold: isBold, children: [
95
- " ",
96
- /* @__PURE__ */ jsx(Text, { dimColor: !option.selected, children: symbol }),
97
- " ",
98
- option.label,
99
- option.selected && /* @__PURE__ */ jsx(Text, { dimColor: true, children: " (active)" }),
100
- " "
101
- ] }) });
98
+ return /* @__PURE__ */ jsx(
99
+ Box,
100
+ {
101
+ marginRight: 1,
102
+ borderColor: getBorderColor(),
103
+ borderStyle: "single",
104
+ borderDimColor: !isFocused,
105
+ children: /* @__PURE__ */ jsxs(Text, { color: textColor, bold: isBold, dimColor: false, children: [
106
+ " ",
107
+ option.label,
108
+ " "
109
+ ] })
110
+ }
111
+ );
102
112
  };
103
113
  var SourceSection = ({
104
114
  row,
@@ -127,6 +137,7 @@ var getNavigableCount = (row, showSearchPill) => {
127
137
  };
128
138
  var SourceGrid = ({
129
139
  rows,
140
+ availableHeight = 0,
130
141
  onSelect,
131
142
  onSearch,
132
143
  onBind,
@@ -157,10 +168,49 @@ var SourceGrid = ({
157
168
  initialRow: defaultFocusedRow,
158
169
  initialCol: defaultFocusedCol
159
170
  });
171
+ const sectionRefs = useRef([]);
172
+ const [sectionHeights, setSectionHeights] = useState2([]);
173
+ const [scrollTopPx, setScrollTopPx] = useState2(0);
174
+ const setSectionRef = useCallback2((index, el) => {
175
+ sectionRefs.current[index] = el;
176
+ }, []);
177
+ useEffect(() => {
178
+ const heights = sectionRefs.current.map((el) => {
179
+ if (el) {
180
+ const { height } = measureElement(el);
181
+ return height;
182
+ }
183
+ return 0;
184
+ });
185
+ setSectionHeights((prev) => {
186
+ if (prev.length === heights.length && prev.every((h, i) => h === heights[i])) {
187
+ return prev;
188
+ }
189
+ return heights;
190
+ });
191
+ });
192
+ const scrollEnabled = availableHeight > 0 && availableHeight >= SCROLL_VIEWPORT.MIN_VIEWPORT_ROWS;
193
+ useEffect(() => {
194
+ if (!scrollEnabled || sectionHeights.length === 0) return;
195
+ let topOfFocused = 0;
196
+ for (let i = 0; i < focusedRow; i++) {
197
+ topOfFocused += sectionHeights[i] ?? 0;
198
+ }
199
+ const focusedHeight = sectionHeights[focusedRow] ?? 0;
200
+ const bottomOfFocused = topOfFocused + focusedHeight;
201
+ setScrollTopPx((prev) => {
202
+ if (topOfFocused < prev) {
203
+ return topOfFocused;
204
+ }
205
+ if (bottomOfFocused > prev + availableHeight) {
206
+ return bottomOfFocused - availableHeight;
207
+ }
208
+ return prev;
209
+ });
210
+ }, [focusedRow, sectionHeights, scrollEnabled, availableHeight]);
160
211
  useInput(
161
212
  useCallback2(
162
213
  (input, key) => {
163
- if (searchModal.isOpen) return;
164
214
  if (input === " ") {
165
215
  const currentRow = rows[focusedRow];
166
216
  if (!currentRow) return;
@@ -190,45 +240,44 @@ var SourceGrid = ({
190
240
  moveFocus("down");
191
241
  }
192
242
  },
193
- [
194
- rows,
195
- focusedRow,
196
- focusedCol,
197
- onSelect,
198
- showSearchPill,
199
- searchModal.isOpen,
200
- handleSearchTrigger,
201
- moveFocus
202
- ]
203
- )
243
+ [rows, focusedRow, focusedCol, onSelect, showSearchPill, handleSearchTrigger, moveFocus]
244
+ ),
245
+ { isActive: !searchModal.isOpen }
204
246
  );
205
247
  if (rows.length === 0) {
206
248
  return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No skills to display." }) });
207
249
  }
208
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
209
- rows.map((row, rowIndex) => /* @__PURE__ */ jsx(
210
- SourceSection,
211
- {
212
- row,
213
- isFocused: rowIndex === focusedRow,
214
- focusedOptionIndex: focusedCol,
215
- showSearchPill
216
- },
217
- row.skillId
218
- )),
219
- searchModal.isOpen && /* @__PURE__ */ jsx(
220
- SearchModal,
221
- {
222
- results: searchResults,
223
- alias: searchAlias,
224
- onBind: handleBind,
225
- onClose: handleCloseSearch
226
- }
227
- )
250
+ const sectionElements = rows.map((row, rowIndex) => /* @__PURE__ */ jsx(Box, { ref: (el) => setSectionRef(rowIndex, el), flexShrink: 0, children: /* @__PURE__ */ jsx(
251
+ SourceSection,
252
+ {
253
+ row,
254
+ isFocused: rowIndex === focusedRow,
255
+ focusedOptionIndex: focusedCol,
256
+ showSearchPill
257
+ }
258
+ ) }, row.skillId));
259
+ const searchModalElement = searchModal.isOpen && /* @__PURE__ */ jsx(
260
+ SearchModal,
261
+ {
262
+ results: searchResults,
263
+ alias: searchAlias,
264
+ onBind: handleBind,
265
+ onClose: handleCloseSearch
266
+ }
267
+ );
268
+ if (!scrollEnabled) {
269
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [
270
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: sectionElements }),
271
+ searchModalElement
272
+ ] });
273
+ }
274
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", height: availableHeight, children: [
275
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", overflow: "hidden", flexGrow: 1, children: /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: scrollTopPx > 0 ? -scrollTopPx : 0, flexShrink: 0, children: sectionElements }) }),
276
+ searchModalElement
228
277
  ] });
229
278
  };
230
279
 
231
280
  export {
232
281
  SourceGrid
233
282
  };
234
- //# sourceMappingURL=chunk-CB7GYRUP.js.map
283
+ //# sourceMappingURL=chunk-EISBUEBL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/wizard/source-grid.tsx","../src/cli/components/hooks/use-source-grid-search-modal.ts"],"sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { Box, type DOMElement, Text, measureElement, useInput } from \"ink\";\nimport type { BoundSkillCandidate, SkillAlias, SkillId } from \"../../types/index.js\";\nimport { CLI_COLORS, SCROLL_VIEWPORT } from \"../../consts.js\";\nimport { useFocusedListItem } from \"../hooks/use-focused-list-item.js\";\nimport { useSourceGridSearchModal } from \"../hooks/use-source-grid-search-modal.js\";\nimport { SearchModal } from \"./search-modal.js\";\n\nconst SEARCH_PILL_LABEL = \"\\u2315 Search\";\n\nexport type SourceOption = {\n id: string;\n label: string;\n selected: boolean;\n installed: boolean;\n};\n\nexport type SourceRow = {\n skillId: SkillId;\n displayName: string;\n alias: SkillAlias;\n options: SourceOption[];\n};\n\nexport type SourceGridProps = {\n rows: SourceRow[];\n /** Available height in terminal lines for the scrollable viewport. 0 = no constraint. */\n availableHeight?: number;\n onSelect: (skillId: SkillId, sourceId: string) => void;\n onSearch?: (alias: SkillAlias) => Promise<BoundSkillCandidate[]>;\n onBind?: (candidate: BoundSkillCandidate) => void;\n onSearchStateChange?: (active: boolean) => 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\ntype SearchPillProps = {\n isFocused: boolean;\n};\n\nconst SearchPill: React.FC<SearchPillProps> = ({ isFocused }) => {\n const borderColor = isFocused ? CLI_COLORS.UNFOCUSED : CLI_COLORS.NEUTRAL;\n\n return (\n <Box marginRight={1} borderColor={borderColor} borderStyle=\"single\" borderDimColor={!isFocused}>\n <Text dimColor={!isFocused} bold={isFocused}>\n {\" \"}\n {SEARCH_PILL_LABEL}{\" \"}\n </Text>\n </Box>\n );\n};\n\ntype SourceSectionProps = {\n row: SourceRow;\n isFocused: boolean;\n focusedOptionIndex: number;\n showSearchPill: boolean;\n};\n\nconst SourceTag: React.FC<{ option: SourceOption; isFocused: boolean }> = ({\n option,\n isFocused,\n}) => {\n const getBorderColor = (): string => {\n if (isFocused) {\n return option.selected ? CLI_COLORS.PRIMARY : CLI_COLORS.UNFOCUSED;\n }\n return CLI_COLORS.NEUTRAL;\n };\n\n const textColor = option.selected ? CLI_COLORS.PRIMARY : CLI_COLORS.NEUTRAL;\n const isBold = isFocused || option.selected;\n\n return (\n <Box\n marginRight={1}\n borderColor={getBorderColor()}\n borderStyle=\"single\"\n borderDimColor={!isFocused}\n >\n <Text color={textColor} bold={isBold} dimColor={false}>\n {\" \"}\n {option.label}{\" \"}\n </Text>\n </Box>\n );\n};\n\nconst SourceSection: React.FC<SourceSectionProps> = ({\n row,\n isFocused,\n focusedOptionIndex,\n showSearchPill,\n}) => {\n const searchPillIndex = row.options.length;\n\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box flexDirection=\"row\">\n <Text>{row.displayName}</Text>\n </Box>\n\n <Box flexDirection=\"row\" flexWrap=\"wrap\" marginTop={0}>\n {row.options.map((option, index) => (\n <SourceTag\n key={option.id}\n option={option}\n isFocused={isFocused && index === focusedOptionIndex}\n />\n ))}\n {showSearchPill && (\n <SearchPill isFocused={isFocused && focusedOptionIndex === searchPillIndex} />\n )}\n </Box>\n </Box>\n );\n};\n\n/** Total navigable columns for a row (options + search pill if applicable) */\nconst getNavigableCount = (row: SourceRow, showSearchPill: boolean): number => {\n return row.options.length + (showSearchPill ? 1 : 0);\n};\n\nexport const SourceGrid: React.FC<SourceGridProps> = ({\n rows,\n availableHeight = 0,\n onSelect,\n onSearch,\n onBind,\n onSearchStateChange,\n defaultFocusedRow = 0,\n defaultFocusedCol = 0,\n onFocusChange,\n}) => {\n const {\n searchModal,\n searchResults,\n searchAlias,\n handleSearchTrigger,\n handleBind,\n handleCloseSearch,\n } = useSourceGridSearchModal({ rows, onSearch, onBind, onSearchStateChange });\n\n const showSearchPill = !!onSearch;\n\n const getColCount = useCallback(\n (row: number): number => {\n const rowData = rows[row];\n return rowData ? getNavigableCount(rowData, showSearchPill) : 0;\n },\n [rows, showSearchPill],\n );\n\n const { focusedRow, focusedCol, moveFocus } = useFocusedListItem(rows.length, getColCount, {\n wrap: true,\n onChange: onFocusChange,\n initialRow: defaultFocusedRow,\n initialCol: defaultFocusedCol,\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 useInput(\n useCallback(\n (\n input: string,\n key: {\n leftArrow: boolean;\n rightArrow: boolean;\n upArrow: boolean;\n downArrow: boolean;\n return: boolean;\n },\n ) => {\n if (input === \" \") {\n const currentRow = rows[focusedRow];\n if (!currentRow) return;\n if (showSearchPill && focusedCol === currentRow.options.length) {\n void handleSearchTrigger(focusedRow);\n return;\n }\n if (focusedCol < currentRow.options.length) {\n const currentOption = currentRow.options[focusedCol];\n if (currentOption) {\n onSelect(currentRow.skillId, currentOption.id);\n }\n }\n return;\n }\n\n const isLeft = key.leftArrow;\n const isRight = key.rightArrow;\n const isUp = key.upArrow;\n const isDown = key.downArrow;\n\n if (isLeft) {\n moveFocus(\"left\");\n } else if (isRight) {\n moveFocus(\"right\");\n } else if (isUp) {\n moveFocus(\"up\");\n } else if (isDown) {\n moveFocus(\"down\");\n }\n },\n [rows, focusedRow, focusedCol, onSelect, showSearchPill, handleSearchTrigger, moveFocus],\n ),\n { isActive: !searchModal.isOpen },\n );\n\n if (rows.length === 0) {\n return (\n <Box flexDirection=\"column\">\n <Text dimColor>No skills to display.</Text>\n </Box>\n );\n }\n\n const sectionElements = rows.map((row, rowIndex) => (\n <Box key={row.skillId} ref={(el) => setSectionRef(rowIndex, el)} flexShrink={0}>\n <SourceSection\n row={row}\n isFocused={rowIndex === focusedRow}\n focusedOptionIndex={focusedCol}\n showSearchPill={showSearchPill}\n />\n </Box>\n ));\n\n const searchModalElement = searchModal.isOpen && (\n <SearchModal\n results={searchResults}\n alias={searchAlias}\n onBind={handleBind}\n onClose={handleCloseSearch}\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}>\n <Box flexDirection=\"column\" flexGrow={1} overflow=\"hidden\">\n {sectionElements}\n </Box>\n {searchModalElement}\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" height={availableHeight}>\n <Box flexDirection=\"column\" overflow=\"hidden\" flexGrow={1}>\n <Box flexDirection=\"column\" marginTop={scrollTopPx > 0 ? -scrollTopPx : 0} flexShrink={0}>\n {sectionElements}\n </Box>\n </Box>\n {searchModalElement}\n </Box>\n );\n};\n","import { useCallback, useState } from \"react\";\nimport type { BoundSkillCandidate, SkillAlias } from \"../../types/index.js\";\nimport { useModalState } from \"./use-modal-state.js\";\nimport type { SourceRow } from \"../wizard/source-grid.js\";\n\ntype UseSourceGridSearchModalOptions = {\n rows: SourceRow[];\n onSearch?: (alias: SkillAlias) => Promise<BoundSkillCandidate[]>;\n onBind?: (candidate: BoundSkillCandidate) => void;\n onSearchStateChange?: (active: boolean) => void;\n};\n\ntype UseSourceGridSearchModalResult = {\n searchModal: { isOpen: boolean };\n searchResults: BoundSkillCandidate[];\n searchAlias: string;\n handleSearchTrigger: (rowIndex: number) => Promise<void>;\n handleBind: (candidate: BoundSkillCandidate) => void;\n handleCloseSearch: () => void;\n};\n\nexport function useSourceGridSearchModal({\n rows,\n onSearch,\n onBind,\n onSearchStateChange,\n}: UseSourceGridSearchModalOptions): UseSourceGridSearchModalResult {\n const searchModal = useModalState<number>();\n const [searchResults, setSearchResults] = useState<BoundSkillCandidate[]>([]);\n const [searchAlias, setSearchAlias] = useState(\"\");\n\n const resetSearch = useCallback(() => {\n searchModal.close();\n setSearchResults([]);\n setSearchAlias(\"\");\n onSearchStateChange?.(false);\n }, [onSearchStateChange, searchModal]);\n\n const handleSearchTrigger = useCallback(\n async (rowIndex: number) => {\n const row = rows[rowIndex];\n if (!row || !onSearch) return;\n\n const alias = row.alias;\n setSearchAlias(alias);\n searchModal.open(rowIndex);\n onSearchStateChange?.(true);\n\n const results = await onSearch(alias);\n setSearchResults(results);\n },\n [rows, onSearch, onSearchStateChange, searchModal],\n );\n\n const handleBind = useCallback(\n (candidate: BoundSkillCandidate) => {\n onBind?.(candidate);\n resetSearch();\n },\n [onBind, resetSearch],\n );\n\n const handleCloseSearch = useCallback(() => {\n resetSearch();\n }, [resetSearch]);\n\n return {\n searchModal: { isOpen: searchModal.isOpen },\n searchResults,\n searchAlias,\n handleSearchTrigger,\n handleBind,\n handleCloseSearch,\n };\n}\n"],"mappings":"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","names":["useCallback","useState","useCallback","useState"]}
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ CheckboxGrid
4
+ } from "./chunk-IWNPFIGY.js";
5
+ import {
6
+ useWizardStore
7
+ } from "./chunk-HRMQ2RGY.js";
8
+ import {
9
+ init_esm_shims
10
+ } from "./chunk-DHET7RCE.js";
11
+
12
+ // src/cli/components/wizard/step-agents.tsx
13
+ init_esm_shims();
14
+ import { jsx } from "react/jsx-runtime";
15
+ var AVAILABLE_AGENTS = [
16
+ {
17
+ id: "web-developer",
18
+ label: "Web Developer",
19
+ description: "Frontend features, components, TypeScript"
20
+ },
21
+ {
22
+ id: "api-developer",
23
+ label: "API Developer",
24
+ description: "Backend routes, database, middleware"
25
+ },
26
+ { id: "cli-developer", label: "CLI Developer", description: "CLI commands, interactive prompts" },
27
+ {
28
+ id: "web-architecture",
29
+ label: "Web Architecture",
30
+ description: "App scaffolding, foundational patterns"
31
+ },
32
+ { id: "web-reviewer", label: "Web Reviewer", description: "UI component code review" },
33
+ { id: "api-reviewer", label: "API Reviewer", description: "Backend and config code review" },
34
+ { id: "cli-reviewer", label: "CLI Reviewer", description: "CLI code review" },
35
+ { id: "web-researcher", label: "Web Researcher", description: "Frontend pattern discovery" },
36
+ { id: "api-researcher", label: "API Researcher", description: "Backend pattern discovery" },
37
+ { id: "web-tester", label: "Web Tester", description: "Frontend tests, E2E, component tests" },
38
+ { id: "cli-tester", label: "CLI Tester", description: "CLI application tests" },
39
+ { id: "web-pm", label: "Web PM", description: "Implementation specs and planning" },
40
+ {
41
+ id: "pattern-scout",
42
+ label: "Pattern Scout",
43
+ description: "Extract codebase patterns and standards"
44
+ },
45
+ {
46
+ id: "web-pattern-critique",
47
+ label: "Pattern Critique",
48
+ description: "Critique patterns against industry standards"
49
+ },
50
+ { id: "agent-summoner", label: "Agent Summoner", description: "Create and improve agents" },
51
+ {
52
+ id: "skill-summoner",
53
+ label: "Skill Summoner",
54
+ description: "Create technology-specific skills"
55
+ },
56
+ { id: "documentor", label: "Documentor", description: "AI-focused documentation" },
57
+ { id: "cli-migrator", label: "CLI Migrator", description: "Commander.js to oclif migration" }
58
+ ];
59
+ var StepAgents = () => {
60
+ const store = useWizardStore();
61
+ return /* @__PURE__ */ jsx(
62
+ CheckboxGrid,
63
+ {
64
+ title: "Select agents to compile:",
65
+ subtitle: "Toggle agents on/off, then continue",
66
+ items: AVAILABLE_AGENTS,
67
+ selectedIds: store.selectedAgents,
68
+ onToggle: store.toggleAgent,
69
+ onContinue: () => store.setStep("confirm"),
70
+ onBack: store.goBack,
71
+ continueLabel: (count) => `Continue with ${count} agent(s)`,
72
+ emptyMessage: "Please select at least one agent"
73
+ }
74
+ );
75
+ };
76
+
77
+ export {
78
+ StepAgents
79
+ };
80
+ //# sourceMappingURL=chunk-EXFVAEPY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/wizard/step-agents.tsx"],"sourcesContent":["import React from \"react\";\nimport type { AgentName } from \"../../types/index.js\";\nimport { useWizardStore } from \"../../stores/wizard-store.js\";\nimport { CheckboxGrid, type CheckboxItem } from \"./checkbox-grid.js\";\n\nconst AVAILABLE_AGENTS: CheckboxItem<AgentName>[] = [\n {\n id: \"web-developer\",\n label: \"Web Developer\",\n description: \"Frontend features, components, TypeScript\",\n },\n {\n id: \"api-developer\",\n label: \"API Developer\",\n description: \"Backend routes, database, middleware\",\n },\n { id: \"cli-developer\", label: \"CLI Developer\", description: \"CLI commands, interactive prompts\" },\n {\n id: \"web-architecture\",\n label: \"Web Architecture\",\n description: \"App scaffolding, foundational patterns\",\n },\n { id: \"web-reviewer\", label: \"Web Reviewer\", description: \"UI component code review\" },\n { id: \"api-reviewer\", label: \"API Reviewer\", description: \"Backend and config code review\" },\n { id: \"cli-reviewer\", label: \"CLI Reviewer\", description: \"CLI code review\" },\n { id: \"web-researcher\", label: \"Web Researcher\", description: \"Frontend pattern discovery\" },\n { id: \"api-researcher\", label: \"API Researcher\", description: \"Backend pattern discovery\" },\n { id: \"web-tester\", label: \"Web Tester\", description: \"Frontend tests, E2E, component tests\" },\n { id: \"cli-tester\", label: \"CLI Tester\", description: \"CLI application tests\" },\n { id: \"web-pm\", label: \"Web PM\", description: \"Implementation specs and planning\" },\n {\n id: \"pattern-scout\",\n label: \"Pattern Scout\",\n description: \"Extract codebase patterns and standards\",\n },\n {\n id: \"web-pattern-critique\",\n label: \"Pattern Critique\",\n description: \"Critique patterns against industry standards\",\n },\n { id: \"agent-summoner\", label: \"Agent Summoner\", description: \"Create and improve agents\" },\n {\n id: \"skill-summoner\",\n label: \"Skill Summoner\",\n description: \"Create technology-specific skills\",\n },\n { id: \"documentor\", label: \"Documentor\", description: \"AI-focused documentation\" },\n { id: \"cli-migrator\", label: \"CLI Migrator\", description: \"Commander.js to oclif migration\" },\n];\n\nexport const StepAgents: React.FC = () => {\n const store = useWizardStore();\n\n return (\n <CheckboxGrid\n title=\"Select agents to compile:\"\n subtitle=\"Toggle agents on/off, then continue\"\n items={AVAILABLE_AGENTS}\n selectedIds={store.selectedAgents}\n onToggle={store.toggleAgent}\n onContinue={() => store.setStep(\"confirm\")}\n onBack={store.goBack}\n continueLabel={(count) => `Continue with ${count} agent(s)`}\n emptyMessage=\"Please select at least one agent\"\n />\n );\n};\n"],"mappings":";;;;;;;;;;;;AAAA;AAsDI;AAjDJ,IAAM,mBAA8C;AAAA,EAClD;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,EAAE,IAAI,iBAAiB,OAAO,iBAAiB,aAAa,oCAAoC;AAAA,EAChG;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,EAAE,IAAI,gBAAgB,OAAO,gBAAgB,aAAa,2BAA2B;AAAA,EACrF,EAAE,IAAI,gBAAgB,OAAO,gBAAgB,aAAa,iCAAiC;AAAA,EAC3F,EAAE,IAAI,gBAAgB,OAAO,gBAAgB,aAAa,kBAAkB;AAAA,EAC5E,EAAE,IAAI,kBAAkB,OAAO,kBAAkB,aAAa,6BAA6B;AAAA,EAC3F,EAAE,IAAI,kBAAkB,OAAO,kBAAkB,aAAa,4BAA4B;AAAA,EAC1F,EAAE,IAAI,cAAc,OAAO,cAAc,aAAa,uCAAuC;AAAA,EAC7F,EAAE,IAAI,cAAc,OAAO,cAAc,aAAa,wBAAwB;AAAA,EAC9E,EAAE,IAAI,UAAU,OAAO,UAAU,aAAa,oCAAoC;AAAA,EAClF;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,EAAE,IAAI,kBAAkB,OAAO,kBAAkB,aAAa,4BAA4B;AAAA,EAC1F;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,EAAE,IAAI,cAAc,OAAO,cAAc,aAAa,2BAA2B;AAAA,EACjF,EAAE,IAAI,gBAAgB,OAAO,gBAAgB,aAAa,kCAAkC;AAC9F;AAEO,IAAM,aAAuB,MAAM;AACxC,QAAM,QAAQ,eAAe;AAE7B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,UAAS;AAAA,MACT,OAAO;AAAA,MACP,aAAa,MAAM;AAAA,MACnB,UAAU,MAAM;AAAA,MAChB,YAAY,MAAM,MAAM,QAAQ,SAAS;AAAA,MACzC,QAAQ,MAAM;AAAA,MACd,eAAe,CAAC,UAAU,iBAAiB,KAAK;AAAA,MAChD,cAAa;AAAA;AAAA,EACf;AAEJ;","names":[]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  DEFAULT_BRANDING
4
- } from "./chunk-YCS7GF6Y.js";
4
+ } from "./chunk-ZBJQXDQN.js";
5
5
  import {
6
6
  init_esm_shims
7
7
  } from "./chunk-DHET7RCE.js";
@@ -13,7 +13,7 @@ init_esm_shims();
13
13
  init_esm_shims();
14
14
  import path from "path";
15
15
  import { fileURLToPath } from "url";
16
- import { parse as parseYaml } from "yaml";
16
+ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
17
17
  import { run } from "@oclif/core";
18
18
  var __filename = fileURLToPath(import.meta.url);
19
19
  var __dirname = path.dirname(__filename);
@@ -117,7 +117,7 @@ var SKILL_FIXTURES = {
117
117
  },
118
118
  zustand: {
119
119
  id: "web-state-zustand",
120
- category: "web/state",
120
+ category: "web/client-state",
121
121
  displayName: "zustand",
122
122
  description: "Bear necessities state management",
123
123
  tags: ["state", "react", "zustand"]
@@ -183,4 +183,4 @@ export {
183
183
  createMockCategory,
184
184
  createMockResolvedStack
185
185
  };
186
- //# sourceMappingURL=chunk-ANGAZ444.js.map
186
+ //# sourceMappingURL=chunk-FWQK3HWB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/lib/__tests__/test-fixtures.ts","../src/cli/lib/__tests__/helpers.ts"],"sourcesContent":["import type { CategoryPath, ResolvedSkill, SkillDisplayName, SkillId } from \"../../types\";\nimport { createMockSkill } from \"./helpers\";\n\ninterface SkillFixtureConfig {\n id: SkillId;\n category: CategoryPath;\n displayName?: SkillDisplayName;\n description: string;\n tags: string[];\n}\n\nconst SKILL_FIXTURES: Record<string, SkillFixtureConfig> = {\n react: {\n id: \"web-framework-react\",\n category: \"web/framework\",\n displayName: \"react\",\n description: \"React framework for building user interfaces\",\n tags: [\"react\", \"web\", \"ui\", \"component\"],\n },\n zustand: {\n id: \"web-state-zustand\",\n category: \"web/client-state\",\n displayName: \"zustand\",\n description: \"Bear necessities state management\",\n tags: [\"state\", \"react\", \"zustand\"],\n },\n hono: {\n id: \"api-framework-hono\",\n category: \"api/framework\",\n displayName: \"hono\",\n description: \"Lightweight web framework for the edge\",\n tags: [\"api\", \"api\", \"edge\", \"serverless\"],\n },\n vitest: {\n id: \"web-testing-vitest\",\n category: \"testing\",\n displayName: \"vitest\",\n description: \"Next generation testing framework\",\n tags: [\"testing\", \"vitest\", \"unit\"],\n },\n vue: {\n id: \"web-framework-vue\",\n category: \"web/framework\",\n displayName: \"vue\",\n description: \"Progressive JavaScript framework\",\n tags: [\"vue\", \"web\", \"reactive\"],\n },\n \"auth-patterns\": {\n id: \"api-security-auth-patterns\",\n category: \"api/security\",\n description: \"Authentication and authorization patterns\",\n tags: [\"auth\", \"security\", \"jwt\", \"oauth\"],\n },\n drizzle: {\n id: \"api-database-drizzle\",\n category: \"api/database\",\n displayName: \"drizzle\",\n description: \"TypeScript ORM for SQL databases\",\n tags: [\"database\", \"orm\", \"sql\"],\n },\n methodology: {\n id: \"meta-methodology-anti-over-engineering\",\n category: \"meta/methodology\",\n description: \"Surgical implementation, not architectural innovation\",\n tags: [\"methodology\", \"foundational\"],\n },\n \"scss-modules\": {\n id: \"web-styling-scss-modules\",\n category: \"web/styling\",\n displayName: \"scss-modules\",\n description: \"CSS Modules with SCSS\",\n tags: [\"css\", \"scss\", \"modules\"],\n },\n} as const;\n\nexport type TestSkillName = keyof typeof SKILL_FIXTURES;\n\nexport function getTestSkill(\n name: TestSkillName,\n overrides?: Partial<ResolvedSkill>,\n): ResolvedSkill {\n const config = SKILL_FIXTURES[name];\n const { id, category, ...defaults } = config;\n return createMockSkill(id, category, { ...defaults, ...overrides });\n}\n","import path from \"path\";\nimport os from \"os\";\nimport { fileURLToPath } from \"url\";\nimport { mkdtemp, rm, mkdir, writeFile, readFile, stat } from \"fs/promises\";\nimport { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport { run, Errors } from \"@oclif/core\";\nimport ansis from \"ansis\";\nimport { DEFAULT_BRANDING, DEFAULT_PLUGIN_NAME, STANDARD_FILES } from \"../../consts\";\nimport { typedEntries } from \"../../utils/typed-object\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nexport const CLI_ROOT = path.resolve(__dirname, \"../../../..\");\n\nexport const OUTPUT_STRINGS = {\n CONFIG_HEADER: `${DEFAULT_BRANDING.NAME} Configuration`,\n CONFIG_PATHS_HEADER: \"Configuration File Paths\",\n CONFIG_LAYERS_HEADER: \"Configuration Layers:\",\n CONFIG_PRECEDENCE: \"Precedence: flag > env > project > global > default\",\n SOURCE_LABEL: \"Source:\",\n MARKETPLACE_LABEL: \"Marketplace:\",\n AGENTS_SOURCE_LABEL: \"Agents Source:\",\n GLOBAL_LABEL: \"Global:\",\n PROJECT_LABEL: \"Project:\",\n\n // Setup/Init outputs\n INIT_HEADER: `${DEFAULT_BRANDING.NAME} Setup`,\n INIT_SUCCESS: `${DEFAULT_BRANDING.NAME} initialized successfully!`,\n LOADING_MATRIX: \"Loading skills matrix...\",\n LOADING_SKILLS: \"Loading skills...\",\n LOADING_AGENTS: \"Loading agent partials...\",\n\n // Plugin/installation outputs\n NO_PLUGIN_FOUND: \"No plugin found\",\n NO_INSTALLATION_FOUND: \"No installation found\",\n NO_PLUGIN_INSTALLATION: \"No plugin installation found\",\n NOT_INSTALLED: `${DEFAULT_BRANDING.NAME} is not installed`,\n UNINSTALL_HEADER: `${DEFAULT_BRANDING.NAME} Uninstall`,\n UNINSTALL_COMPLETE: `${DEFAULT_BRANDING.NAME} has been uninstalled`,\n EJECT_HEADER: `${DEFAULT_BRANDING.NAME} Eject`,\n\n // Doctor command outputs\n DOCTOR_HEADER: `${DEFAULT_BRANDING.NAME} Doctor`,\n\n // Error message patterns (lowercase for case-insensitive matching)\n ERROR_MISSING_ARG: \"missing required arg\",\n ERROR_UNEXPECTED_ARG: \"unexpected argument\",\n ERROR_UNKNOWN_FLAG: \"unknown flag\",\n ERROR_PARSE: \"parse\",\n} as const;\n\n/**\n * Run a CLI command and capture its output.\n *\n * Bun's `console.log` does not go through `process.stdout.write`, so\n * `@oclif/test`'s `runCommand` (which only intercepts `process.stdout.write`)\n * returns empty stdout/stderr in bun. This helper intercepts both layers\n * to work correctly in both Node.js and bun environments.\n */\nexport async function runCliCommand(args: string[]) {\n const origStdoutWrite = process.stdout.write;\n const origStderrWrite = process.stderr.write;\n const origLog = console.log;\n const origWarn = console.warn;\n const origError = console.error;\n\n const stdoutBuf: string[] = [];\n const stderrBuf: string[] = [];\n\n // Intercept process.stdout/stderr.write (Node.js path)\n process.stdout.write = function (str: unknown, encoding?: unknown, cb?: unknown): boolean {\n stdoutBuf.push(String(str));\n if (typeof encoding === \"function\") {\n (encoding as () => void)();\n } else if (typeof cb === \"function\") {\n (cb as () => void)();\n }\n return true;\n } as typeof process.stdout.write;\n\n process.stderr.write = function (str: unknown, encoding?: unknown, cb?: unknown): boolean {\n stderrBuf.push(String(str));\n if (typeof encoding === \"function\") {\n (encoding as () => void)();\n } else if (typeof cb === \"function\") {\n (cb as () => void)();\n }\n return true;\n } as typeof process.stderr.write;\n\n // Intercept console methods (bun path — console.log bypasses process.stdout.write)\n console.log = (...logArgs: unknown[]) => {\n stdoutBuf.push(logArgs.map(String).join(\" \") + \"\\n\");\n };\n console.warn = (...warnArgs: unknown[]) => {\n stderrBuf.push(warnArgs.map(String).join(\" \") + \"\\n\");\n };\n console.error = (...errArgs: unknown[]) => {\n stderrBuf.push(errArgs.map(String).join(\" \") + \"\\n\");\n };\n\n let error: (Error & Partial<Errors.CLIError>) | undefined;\n try {\n await run(args, { root: CLI_ROOT });\n } catch (e) {\n if (e instanceof Error) {\n error = Object.assign(e, { message: ansis.strip(e.message) }) as Error &\n Partial<Errors.CLIError>;\n }\n } finally {\n process.stdout.write = origStdoutWrite;\n process.stderr.write = origStderrWrite;\n console.log = origLog;\n console.warn = origWarn;\n console.error = origError;\n }\n\n return {\n stdout: stdoutBuf.map((s) => ansis.strip(s)).join(\"\"),\n stderr: stderrBuf.map((s) => ansis.strip(s)).join(\"\"),\n error,\n };\n}\nimport type {\n AgentConfig,\n AgentDefinition,\n CategoryDefinition,\n CategoryPath,\n CompileContext,\n Domain,\n DomainSelections,\n ExtractedSkillMetadata,\n MergedSkillsMatrix,\n ResolvedSkill,\n ResolvedStack,\n Skill,\n SkillDisplayName,\n SkillId,\n Subcategory,\n} from \"../../types\";\nimport type { WizardResultV2 } from \"../../components/wizard/wizard\";\nimport type { SourceLoadResult } from \"../loading/source-loader\";\nimport type { ResolvedConfig } from \"../configuration/config\";\nimport { getTestSkill } from \"./test-fixtures\";\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n const s = await stat(filePath);\n return s.isFile();\n } catch {\n return false;\n }\n}\n\nexport async function directoryExists(dirPath: string): Promise<boolean> {\n try {\n const s = await stat(dirPath);\n return s.isDirectory();\n } catch {\n return false;\n }\n}\n\nexport async function readTestYaml<T>(filePath: string): Promise<T> {\n const content = await readFile(filePath, \"utf-8\");\n // Boundary cast: YAML parse returns `unknown`, caller provides expected type\n return parseYaml(content) as T;\n}\n\nexport function buildWizardResult(\n selectedSkills: SkillId[],\n overrides?: Partial<WizardResultV2>,\n): WizardResultV2 {\n return {\n selectedSkills,\n selectedAgents: [],\n selectedStackId: null,\n domainSelections: {} as DomainSelections,\n sourceSelections: {},\n expertMode: false,\n installMode: \"local\",\n cancelled: false,\n validation: { valid: true, errors: [], warnings: [] },\n ...overrides,\n };\n}\n\nexport function buildSourceResult(\n matrix: MergedSkillsMatrix,\n sourcePath: string,\n overrides?: Partial<SourceLoadResult>,\n): SourceLoadResult {\n const sourceConfig: ResolvedConfig = {\n source: sourcePath,\n sourceOrigin: \"flag\",\n };\n return {\n matrix,\n sourceConfig,\n sourcePath,\n isLocal: true,\n ...overrides,\n };\n}\n\n/**\n * Lightweight frontmatter parser for test assertions.\n * Returns raw key-value pairs (unlike the production parseFrontmatter which\n * returns typed SkillFrontmatter with Zod validation).\n */\nexport function parseTestFrontmatter(content: string): Record<string, unknown> | null {\n if (!content.startsWith(\"---\")) {\n return null;\n }\n\n const endIndex = content.indexOf(\"---\", 3);\n if (endIndex === -1) {\n return null;\n }\n\n const yamlContent = content.slice(3, endIndex).trim();\n try {\n // Boundary cast: YAML parse returns `unknown`\n return parseYaml(yamlContent) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nexport async function createTempDir(prefix = \"cc-test-\"): Promise<string> {\n return mkdtemp(path.join(os.tmpdir(), prefix));\n}\n\nconst CLEANUP_MAX_RETRIES = 3;\nconst CLEANUP_RETRY_DELAY_MS = 100;\n\nexport async function cleanupTempDir(dirPath: string): Promise<void> {\n for (let attempt = 0; attempt < CLEANUP_MAX_RETRIES; attempt++) {\n try {\n await rm(dirPath, { recursive: true, force: true });\n return;\n } catch (error: unknown) {\n const isRetryable =\n error instanceof Error &&\n \"code\" in error &&\n (error as NodeJS.ErrnoException).code === \"ENOTEMPTY\";\n if (!isRetryable || attempt === CLEANUP_MAX_RETRIES - 1) {\n throw error;\n }\n // Transient ENOTEMPTY on macOS: kernel hasn't released directory entries yet\n await new Promise((resolve) => setTimeout(resolve, CLEANUP_RETRY_DELAY_MS));\n }\n }\n}\n\nexport interface TestDirs {\n tempDir: string;\n projectDir: string;\n pluginDir: string;\n skillsDir: string;\n agentsDir: string;\n}\n\nexport async function createTestDirs(prefix = \"cc-test-\"): Promise<TestDirs> {\n const tempDir = await createTempDir(prefix);\n const projectDir = path.join(tempDir, \"project\");\n const pluginDir = path.join(projectDir, \".claude\", \"plugins\", DEFAULT_PLUGIN_NAME);\n const skillsDir = path.join(pluginDir, \"skills\");\n const agentsDir = path.join(pluginDir, \"agents\");\n\n await mkdir(skillsDir, { recursive: true });\n await mkdir(agentsDir, { recursive: true });\n\n return { tempDir, projectDir, pluginDir, skillsDir, agentsDir };\n}\n\nexport async function cleanupTestDirs(dirs: TestDirs): Promise<void> {\n await cleanupTempDir(dirs.tempDir);\n}\n\nexport function createMockSkill(\n id: SkillId,\n category: CategoryPath,\n overrides?: Partial<ResolvedSkill>,\n): ResolvedSkill {\n return {\n id,\n description: `${id} skill`,\n category,\n categoryExclusive: false,\n tags: [],\n author: \"@test\",\n conflictsWith: [],\n recommends: [],\n requires: [],\n alternatives: [],\n discourages: [],\n compatibleWith: [],\n requiresSetup: [],\n providesSetupFor: [],\n path: `skills/${category}/${id}/`,\n ...overrides,\n };\n}\n\n/**\n * Creates a mock ExtractedSkillMetadata for testing.\n * Used when mocking extractAllSkills() return values.\n */\nexport function createMockExtractedSkill(\n id: SkillId,\n overrides?: Partial<ExtractedSkillMetadata>,\n): ExtractedSkillMetadata {\n // Derive directory path and category from the skill ID convention: \"domain-subcategory-name\"\n const segments = id.split(\"-\");\n const domain = segments[0] ?? \"web\";\n const subcategory = segments[1] ?? \"framework\";\n const name = segments.slice(2).join(\"-\") || \"skill\";\n const directoryPath = `${domain}/${subcategory}/${name}`;\n\n return {\n id,\n directoryPath,\n description: `${id} skill`,\n category: `${subcategory}` as CategoryPath,\n categoryExclusive: true,\n author: \"@test\",\n tags: [],\n compatibleWith: [],\n conflictsWith: [],\n requires: [],\n requiresSetup: [],\n providesSetupFor: [],\n path: `skills/${directoryPath}/`,\n ...overrides,\n };\n}\n\nexport function createMockMatrix(\n skills: Record<string, ResolvedSkill>,\n overrides?: Partial<MergedSkillsMatrix>,\n): MergedSkillsMatrix {\n return {\n version: \"1.0.0\",\n categories: {} as Record<Subcategory, import(\"../../types\").CategoryDefinition>,\n skills,\n suggestedStacks: [],\n displayNameToId: {} as Record<SkillDisplayName, SkillId>,\n displayNames: {} as Record<SkillId, SkillDisplayName>,\n generatedAt: new Date().toISOString(),\n ...overrides,\n };\n}\n\nexport function createMockAgent(\n name: string,\n overrides?: Partial<AgentDefinition>,\n): AgentDefinition {\n return {\n title: name,\n description: `${name} agent`,\n tools: [\"Read\", \"Write\", \"Edit\", \"Grep\", \"Glob\", \"Bash\"],\n model: \"opus\",\n permissionMode: \"default\",\n ...overrides,\n };\n}\n\nexport function createMockAgentConfig(\n name: string,\n skills: Skill[] = [],\n overrides?: Partial<AgentConfig>,\n): AgentConfig {\n return {\n name,\n title: `${name} agent`,\n description: `Test ${name}`,\n tools: [\"Read\", \"Write\"],\n skills,\n path: name,\n ...overrides,\n };\n}\n\nexport function createMockSkillEntry(\n id: SkillId,\n preloaded = false,\n overrides?: Partial<Skill>,\n): Skill {\n return {\n id,\n path: `skills/${id}/`,\n description: `${id} skill`,\n usage: `when working with ${id}`,\n preloaded,\n ...overrides,\n };\n}\n\nexport function createCompileContext(overrides?: Partial<CompileContext>): CompileContext {\n return {\n stackId: \"test-stack\",\n verbose: false,\n projectRoot: \"/project\",\n outputDir: `/project/.claude/plugins/${DEFAULT_PLUGIN_NAME}`,\n ...overrides,\n };\n}\n\nexport function createSkillContent(name: string, description = \"A test skill\"): string {\n return `---\nname: ${name}\ndescription: ${description}\ncategory: test\n---\n\n# ${name}\n\nThis is a test skill.\n`;\n}\n\nfunction createMetadataContent(author = \"@test\"): string {\n return `version: 1\nauthor: ${author}\n`;\n}\n\nexport function createAgentYamlContent(name: string, description = `Test ${name} agent`): string {\n return `id: ${name}\ntitle: ${name} Agent\ndescription: ${description}\ntools:\n - Read\n - Write`;\n}\n\nexport async function writeTestSkill(\n skillsDir: string,\n skillName: string,\n options?: {\n author?: string;\n description?: string;\n /** Extra fields to merge into metadata.yaml (e.g., forkedFrom, cliName) */\n extraMetadata?: Record<string, unknown>;\n /** Skip metadata.yaml creation entirely */\n skipMetadata?: boolean;\n /** Custom SKILL.md content (overrides default generated content) */\n skillContent?: string;\n },\n): Promise<string> {\n const skillDir = path.join(skillsDir, skillName);\n await mkdir(skillDir, { recursive: true });\n\n await writeFile(\n path.join(skillDir, STANDARD_FILES.SKILL_MD),\n options?.skillContent ?? createSkillContent(skillName, options?.description),\n );\n\n if (!options?.skipMetadata) {\n if (options?.extraMetadata) {\n const metadata = {\n version: 1,\n author: options?.author ?? \"@test\",\n ...options.extraMetadata,\n };\n await writeFile(path.join(skillDir, STANDARD_FILES.METADATA_YAML), stringifyYaml(metadata));\n } else {\n await writeFile(\n path.join(skillDir, STANDARD_FILES.METADATA_YAML),\n createMetadataContent(options?.author),\n );\n }\n }\n\n return skillDir;\n}\n\n/**\n * Creates a source-level skill directory with SKILL.md and rich metadata.yaml.\n * Use this when testing `extractAllSkills()` and `mergeMatrixWithSkills()`.\n *\n * Unlike `writeTestSkill()` which creates installed skills, this writes skills\n * in the source directory layout (under `src/skills/<domain>/<subcategory>/<name>/`).\n */\nexport async function writeSourceSkill(\n skillsDir: string,\n directoryPath: string,\n config: {\n id: string;\n description: string;\n category: string;\n author?: string;\n tags?: string[];\n categoryExclusive?: boolean;\n content?: string;\n },\n): Promise<string> {\n const skillDir = path.join(skillsDir, directoryPath);\n await mkdir(skillDir, { recursive: true });\n\n await writeFile(\n path.join(skillDir, STANDARD_FILES.SKILL_MD),\n createSkillContent(config.id, config.description),\n );\n\n const metadata: Record<string, unknown> = {\n cliName: config.id,\n category: config.category,\n author: config.author ?? \"@test\",\n version: \"1\",\n };\n if (config.tags) {\n metadata.tags = config.tags;\n }\n if (config.categoryExclusive !== undefined) {\n metadata.categoryExclusive = config.categoryExclusive;\n }\n\n await writeFile(path.join(skillDir, STANDARD_FILES.METADATA_YAML), stringifyYaml(metadata));\n\n return skillDir;\n}\n\nexport async function writeTestAgent(\n agentsDir: string,\n agentName: string,\n options?: { description?: string },\n): Promise<string> {\n const agentDir = path.join(agentsDir, agentName);\n await mkdir(agentDir, { recursive: true });\n\n await writeFile(\n path.join(agentDir, STANDARD_FILES.AGENT_YAML),\n createAgentYamlContent(agentName, options?.description),\n );\n\n return agentDir;\n}\n\nexport function createMockCategory(\n id: Subcategory,\n displayName: string,\n overrides?: Partial<CategoryDefinition>,\n): CategoryDefinition {\n return {\n id,\n displayName,\n description: `${displayName} category`,\n exclusive: true,\n required: false,\n order: 0,\n ...overrides,\n };\n}\n\nexport function createMockResolvedStack(\n id: string,\n name: string,\n overrides?: Partial<ResolvedStack>,\n): ResolvedStack {\n return {\n id,\n name,\n description: `${name} stack`,\n audience: [],\n skills: {},\n allSkillIds: [],\n philosophy: \"\",\n ...overrides,\n };\n}\n\n/**\n * Builds a comprehensive test matrix with 7 skills across 6 categories,\n * 2 suggested stacks, display name mappings, and relationship data\n * (conflicts, recommends). Suitable for full wizard and compilation tests.\n * @returns A fully populated MergedSkillsMatrix with realistic test data\n */\nexport function createComprehensiveMatrix(\n overrides?: Partial<MergedSkillsMatrix>,\n): MergedSkillsMatrix {\n // Skill categories use bare Subcategory IDs (matching production metadata.yaml\n // and the categories map keys). The test fixture factories default to \"web/framework\"\n // CategoryPath format, but the wizard's populateFromStack needs bare IDs to match\n // the categories map lookup (e.g., \"framework\" not \"web/framework\").\n const skills = {\n \"web-framework-react\": getTestSkill(\"react\", { category: \"framework\" }),\n \"web-framework-vue\": getTestSkill(\"vue\", {\n category: \"framework\",\n conflictsWith: [{ skillId: \"web-framework-react\", reason: \"Choose one framework\" }],\n }),\n \"web-state-zustand\": getTestSkill(\"zustand\", {\n category: \"client-state\",\n recommends: [{ skillId: \"web-framework-react\", reason: \"Works great with React\" }],\n }),\n \"web-styling-scss-modules\": getTestSkill(\"scss-modules\", { category: \"styling\" }),\n \"api-framework-hono\": getTestSkill(\"hono\", { category: \"api\" }),\n \"api-database-drizzle\": getTestSkill(\"drizzle\", { category: \"database\" }),\n \"web-testing-vitest\": getTestSkill(\"vitest\", { category: \"testing\" }),\n };\n\n const categories = {\n framework: createMockCategory(\"framework\" as Subcategory, \"Framework\", {\n domain: \"web\" as Domain,\n exclusive: true,\n required: true,\n }),\n \"client-state\": createMockCategory(\"client-state\" as Subcategory, \"State\", {\n domain: \"web\" as Domain,\n order: 1,\n }),\n styling: createMockCategory(\"styling\" as Subcategory, \"Styling\", {\n domain: \"web\" as Domain,\n order: 2,\n }),\n api: createMockCategory(\"api\" as Subcategory, \"Backend Framework\", {\n domain: \"api\" as Domain,\n exclusive: true,\n required: true,\n }),\n database: createMockCategory(\"database\" as Subcategory, \"Database\", {\n domain: \"api\" as Domain,\n order: 1,\n }),\n testing: createMockCategory(\"testing\" as Subcategory, \"Testing\", {\n domain: \"shared\" as Domain,\n exclusive: false,\n order: 10,\n }),\n } as Record<Subcategory, CategoryDefinition>;\n\n const suggestedStacks: ResolvedStack[] = [\n createMockResolvedStack(\"nextjs-fullstack\", \"Next.js Fullstack\", {\n description: \"Complete Next.js stack with React and Hono\",\n audience: [\"startups\", \"enterprise\"],\n skills: {\n \"web-developer\": {\n framework: \"web-framework-react\",\n \"client-state\": \"web-state-zustand\",\n styling: \"web-styling-scss-modules\",\n },\n \"api-developer\": {\n api: \"api-framework-hono\",\n database: \"api-database-drizzle\",\n },\n } as ResolvedStack[\"skills\"],\n allSkillIds: [\n \"web-framework-react\",\n \"web-state-zustand\",\n \"web-styling-scss-modules\",\n \"api-framework-hono\",\n \"api-database-drizzle\",\n ],\n philosophy: \"Modern, type-safe fullstack development\",\n }),\n createMockResolvedStack(\"vue-stack\", \"Vue Stack\", {\n description: \"Vue.js frontend stack\",\n audience: [\"startups\"],\n skills: {\n \"web-developer\": {\n framework: \"web-framework-vue\",\n },\n } as ResolvedStack[\"skills\"],\n allSkillIds: [\"web-framework-vue\"],\n philosophy: \"Progressive framework approach\",\n }),\n ];\n\n const displayNameToId = {\n react: \"web-framework-react\",\n vue: \"web-framework-vue\",\n zustand: \"web-state-zustand\",\n \"scss-modules\": \"web-styling-scss-modules\",\n hono: \"api-framework-hono\",\n drizzle: \"api-database-drizzle\",\n vitest: \"web-testing-vitest\",\n // Double cast needed: object literal's string keys are not assignable to branded\n // SkillDisplayName/SkillId types without going through `unknown` first (boundary cast)\n } as unknown as Record<SkillDisplayName, SkillId>;\n\n const displayNames = {} as Record<SkillId, SkillDisplayName>;\n for (const [displayName, fullId] of typedEntries(displayNameToId)) {\n (displayNames as Record<string, string>)[fullId] = displayName;\n }\n\n return createMockMatrix(skills, {\n categories,\n suggestedStacks,\n displayNameToId,\n displayNames,\n ...overrides,\n });\n}\n\n/**\n * Builds a lightweight test matrix with 4 skills, 4 categories, and 2 stacks.\n * Use instead of createComprehensiveMatrix when relationship data is not needed.\n * @returns A minimal MergedSkillsMatrix for basic integration tests\n */\nexport function createBasicMatrix(overrides?: Partial<MergedSkillsMatrix>): MergedSkillsMatrix {\n // Bare Subcategory IDs — see createComprehensiveMatrix comment\n const skills = {\n \"web-framework-react\": getTestSkill(\"react\", { category: \"framework\" }),\n \"web-state-zustand\": getTestSkill(\"zustand\", { category: \"client-state\" }),\n \"api-framework-hono\": getTestSkill(\"hono\", { category: \"api\" }),\n \"web-testing-vitest\": getTestSkill(\"vitest\", { category: \"testing\" }),\n };\n\n const suggestedStacks: ResolvedStack[] = [\n createMockResolvedStack(\"react-fullstack\", \"React Fullstack\", {\n allSkillIds: [\"web-framework-react\", \"web-state-zustand\", \"api-framework-hono\"],\n }),\n createMockResolvedStack(\"testing-stack\", \"Testing Stack\", {\n allSkillIds: [\"web-testing-vitest\"],\n }),\n ];\n\n return createMockMatrix(skills, {\n suggestedStacks,\n categories: {\n framework: createMockCategory(\"framework\" as Subcategory, \"Framework\", {\n domain: \"web\" as Domain,\n exclusive: true,\n required: true,\n }),\n \"client-state\": createMockCategory(\"client-state\" as Subcategory, \"State\", {\n domain: \"web\" as Domain,\n order: 1,\n }),\n api: createMockCategory(\"api\" as Subcategory, \"Backend Framework\", {\n domain: \"api\" as Domain,\n exclusive: true,\n required: true,\n }),\n testing: createMockCategory(\"testing\" as Subcategory, \"Testing Framework\", {\n domain: \"shared\" as Domain,\n exclusive: false,\n }),\n } as Record<Subcategory, CategoryDefinition>,\n ...overrides,\n });\n}\n\nexport { getTestSkill } from \"./test-fixtures\";\nexport type { TestSkillName } from \"./test-fixtures\";\n"],"mappings":";;;;;;;;;AAAA;;;ACAA;AAAA,OAAO,UAAU;AAEjB,SAAS,qBAAqB;AAE9B,SAAS,SAAS,WAAW,aAAa,qBAAqB;AAC/D,SAAS,WAAmB;AAK5B,IAAM,aAAa,cAAc,YAAY,GAAG;AAChD,IAAM,YAAY,KAAK,QAAQ,UAAU;AAElC,IAAM,WAAW,KAAK,QAAQ,WAAW,aAAa;AAEtD,IAAM,iBAAiB;AAAA,EAC5B,eAAe,GAAG,iBAAiB,IAAI;AAAA,EACvC,qBAAqB;AAAA,EACrB,sBAAsB;AAAA,EACtB,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,qBAAqB;AAAA,EACrB,cAAc;AAAA,EACd,eAAe;AAAA;AAAA,EAGf,aAAa,GAAG,iBAAiB,IAAI;AAAA,EACrC,cAAc,GAAG,iBAAiB,IAAI;AAAA,EACtC,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,gBAAgB;AAAA;AAAA,EAGhB,iBAAiB;AAAA,EACjB,uBAAuB;AAAA,EACvB,wBAAwB;AAAA,EACxB,eAAe,GAAG,iBAAiB,IAAI;AAAA,EACvC,kBAAkB,GAAG,iBAAiB,IAAI;AAAA,EAC1C,oBAAoB,GAAG,iBAAiB,IAAI;AAAA,EAC5C,cAAc,GAAG,iBAAiB,IAAI;AAAA;AAAA,EAGtC,eAAe,GAAG,iBAAiB,IAAI;AAAA;AAAA,EAGvC,mBAAmB;AAAA,EACnB,sBAAsB;AAAA,EACtB,oBAAoB;AAAA,EACpB,aAAa;AACf;AAuOO,SAAS,gBACd,IACA,UACA,WACe;AACf,SAAO;AAAA,IACL;AAAA,IACA,aAAa,GAAG,EAAE;AAAA,IAClB;AAAA,IACA,mBAAmB;AAAA,IACnB,MAAM,CAAC;AAAA,IACP,QAAQ;AAAA,IACR,eAAe,CAAC;AAAA,IAChB,YAAY,CAAC;AAAA,IACb,UAAU,CAAC;AAAA,IACX,cAAc,CAAC;AAAA,IACf,aAAa,CAAC;AAAA,IACd,gBAAgB,CAAC;AAAA,IACjB,eAAe,CAAC;AAAA,IAChB,kBAAkB,CAAC;AAAA,IACnB,MAAM,UAAU,QAAQ,IAAI,EAAE;AAAA,IAC9B,GAAG;AAAA,EACL;AACF;AAmCO,SAAS,iBACd,QACA,WACoB;AACpB,SAAO;AAAA,IACL,SAAS;AAAA,IACT,YAAY,CAAC;AAAA,IACb;AAAA,IACA,iBAAiB,CAAC;AAAA,IAClB,iBAAiB,CAAC;AAAA,IAClB,cAAc,CAAC;AAAA,IACf,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC,GAAG;AAAA,EACL;AACF;AA4LO,SAAS,mBACd,IACA,aACA,WACoB;AACpB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,aAAa,GAAG,WAAW;AAAA,IAC3B,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,GAAG;AAAA,EACL;AACF;AAEO,SAAS,wBACd,IACA,MACA,WACe;AACf,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,aAAa,GAAG,IAAI;AAAA,IACpB,UAAU,CAAC;AAAA,IACX,QAAQ,CAAC;AAAA,IACT,aAAa,CAAC;AAAA,IACd,YAAY;AAAA,IACZ,GAAG;AAAA,EACL;AACF;;;ADjjBA,IAAM,iBAAqD;AAAA,EACzD,OAAO;AAAA,IACL,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,aAAa;AAAA,IACb,aAAa;AAAA,IACb,MAAM,CAAC,SAAS,OAAO,MAAM,WAAW;AAAA,EAC1C;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,aAAa;AAAA,IACb,aAAa;AAAA,IACb,MAAM,CAAC,SAAS,SAAS,SAAS;AAAA,EACpC;AAAA,EACA,MAAM;AAAA,IACJ,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,aAAa;AAAA,IACb,aAAa;AAAA,IACb,MAAM,CAAC,OAAO,OAAO,QAAQ,YAAY;AAAA,EAC3C;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,aAAa;AAAA,IACb,aAAa;AAAA,IACb,MAAM,CAAC,WAAW,UAAU,MAAM;AAAA,EACpC;AAAA,EACA,KAAK;AAAA,IACH,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,aAAa;AAAA,IACb,aAAa;AAAA,IACb,MAAM,CAAC,OAAO,OAAO,UAAU;AAAA,EACjC;AAAA,EACA,iBAAiB;AAAA,IACf,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,aAAa;AAAA,IACb,MAAM,CAAC,QAAQ,YAAY,OAAO,OAAO;AAAA,EAC3C;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,aAAa;AAAA,IACb,aAAa;AAAA,IACb,MAAM,CAAC,YAAY,OAAO,KAAK;AAAA,EACjC;AAAA,EACA,aAAa;AAAA,IACX,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,aAAa;AAAA,IACb,MAAM,CAAC,eAAe,cAAc;AAAA,EACtC;AAAA,EACA,gBAAgB;AAAA,IACd,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,aAAa;AAAA,IACb,aAAa;AAAA,IACb,MAAM,CAAC,OAAO,QAAQ,SAAS;AAAA,EACjC;AACF;AAIO,SAAS,aACd,MACA,WACe;AACf,QAAM,SAAS,eAAe,IAAI;AAClC,QAAM,EAAE,IAAI,UAAU,GAAG,SAAS,IAAI;AACtC,SAAO,gBAAgB,IAAI,UAAU,EAAE,GAAG,UAAU,GAAG,UAAU,CAAC;AACpE;","names":[]}
@@ -5,7 +5,7 @@ import {
5
5
 
6
6
  // src/cli/components/hooks/use-focused-list-item.ts
7
7
  init_esm_shims();
8
- import { useState, useCallback, useEffect, useRef } from "react";
8
+ import { useState, useCallback, useRef } from "react";
9
9
  function useFocusedListItem(rowCount, getColCount, options = {}) {
10
10
  const {
11
11
  wrap = true,
@@ -19,17 +19,11 @@ function useFocusedListItem(rowCount, getColCount, options = {}) {
19
19
  const [focusedRow, setFocusedRow] = useState(initialRow);
20
20
  const [focusedCol, setFocusedCol] = useState(initialCol);
21
21
  const focusedRowRef = useRef(focusedRow);
22
+ focusedRowRef.current = focusedRow;
22
23
  const focusedColRef = useRef(focusedCol);
24
+ focusedColRef.current = focusedCol;
23
25
  const onChangeRef = useRef(onChange);
24
- useEffect(() => {
25
- focusedRowRef.current = focusedRow;
26
- }, [focusedRow]);
27
- useEffect(() => {
28
- focusedColRef.current = focusedCol;
29
- }, [focusedCol]);
30
- useEffect(() => {
31
- onChangeRef.current = onChange;
32
- }, [onChange]);
26
+ onChangeRef.current = onChange;
33
27
  const applyFocus = useCallback((row, col) => {
34
28
  setFocusedRow(row);
35
29
  setFocusedCol(col);
@@ -102,4 +96,4 @@ function useFocusedListItem(rowCount, getColCount, options = {}) {
102
96
  export {
103
97
  useFocusedListItem
104
98
  };
105
- //# sourceMappingURL=chunk-DC5AK3LW.js.map
99
+ //# sourceMappingURL=chunk-GG4BSB6S.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/hooks/use-focused-list-item.ts"],"sourcesContent":["import { useState, useCallback, useRef } from \"react\";\n\ntype Direction = \"up\" | \"down\" | \"left\" | \"right\";\n\ntype UseFocusedListItemOptions = {\n /** Wrap around when reaching boundaries (default: true) */\n wrap?: boolean;\n /** Returns true if a row should be skipped during vertical navigation */\n isRowLocked?: (row: number) => boolean;\n /** Custom column finder for skipping disabled items on horizontal nav.\n * Receives the row, current column, and direction (+1 right, -1 left).\n * Should return the next valid column index. */\n findValidCol?: (row: number, currentCol: number, direction: 1 | -1) => number;\n /** Called after vertical navigation to adjust the clamped column\n * (e.g. to skip disabled items). Returns the adjusted column index. */\n adjustCol?: (row: number, clampedCol: number) => number;\n /** Called whenever focused position changes */\n onChange?: (row: number, col: number) => void;\n /** Initial row index (default: 0) */\n initialRow?: number;\n /** Initial col index (default: 0) */\n initialCol?: number;\n};\n\ntype UseFocusedListItemResult = {\n focusedRow: number;\n focusedCol: number;\n setFocused: (row: number, col: number) => void;\n moveFocus: (direction: Direction) => void;\n};\n\n/**\n * 2D grid focus management: tracks (row, col) position and handles\n * directional movement with wrapping, column clamping, row locking,\n * and optional disabled-column skipping.\n */\nexport function useFocusedListItem(\n rowCount: number,\n getColCount: (row: number) => number,\n options: UseFocusedListItemOptions = {},\n): UseFocusedListItemResult {\n const {\n wrap = true,\n isRowLocked,\n findValidCol,\n adjustCol,\n onChange,\n initialRow = 0,\n initialCol = 0,\n } = options;\n\n const [focusedRow, setFocusedRow] = useState(initialRow);\n const [focusedCol, setFocusedCol] = useState(initialCol);\n\n // Refs for stable callback access without stale closures.\n // Synced during render (not via useEffect) to prevent a timing gap where\n // the ref holds stale values when an input event arrives between render\n // and effect execution (e.g. after a domain-switch remount).\n const focusedRowRef = useRef(focusedRow);\n focusedRowRef.current = focusedRow;\n\n const focusedColRef = useRef(focusedCol);\n focusedColRef.current = focusedCol;\n\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n\n const applyFocus = useCallback((row: number, col: number) => {\n setFocusedRow(row);\n setFocusedCol(col);\n onChangeRef.current?.(row, col);\n }, []);\n\n const setFocused = applyFocus;\n\n const findNextUnlockedRow = useCallback(\n (fromRow: number, direction: 1 | -1): number => {\n if (!isRowLocked || rowCount === 0) {\n if (wrap) {\n return (fromRow + direction + rowCount) % rowCount;\n }\n const next = fromRow + direction;\n return Math.max(0, Math.min(rowCount - 1, next));\n }\n\n let index = fromRow;\n let attempts = 0;\n\n while (attempts < rowCount) {\n index += direction;\n\n if (wrap) {\n if (index < 0) index = rowCount - 1;\n if (index >= rowCount) index = 0;\n } else {\n if (index < 0) index = 0;\n if (index >= rowCount) index = rowCount - 1;\n }\n\n if (!isRowLocked(index)) {\n return index;\n }\n\n attempts++;\n }\n\n return fromRow;\n },\n [rowCount, wrap, isRowLocked],\n );\n\n const moveFocus = useCallback(\n (direction: Direction) => {\n const currentRow = focusedRowRef.current;\n const currentCol = focusedColRef.current;\n\n if (direction === \"left\" || direction === \"right\") {\n const colCount = getColCount(currentRow);\n if (colCount === 0) return;\n\n const step = direction === \"right\" ? 1 : -1;\n\n if (findValidCol) {\n const newCol = findValidCol(currentRow, currentCol, step);\n applyFocus(currentRow, newCol);\n } else if (wrap) {\n const newCol = (currentCol + step + colCount) % colCount;\n applyFocus(currentRow, newCol);\n } else {\n const newCol = Math.max(0, Math.min(colCount - 1, currentCol + step));\n applyFocus(currentRow, newCol);\n }\n } else {\n const step = direction === \"down\" ? 1 : -1;\n const newRow = findNextUnlockedRow(currentRow, step);\n const newRowColCount = getColCount(newRow);\n let finalCol = Math.min(currentCol, Math.max(0, newRowColCount - 1));\n\n if (adjustCol) {\n finalCol = adjustCol(newRow, finalCol);\n }\n\n applyFocus(newRow, finalCol);\n }\n },\n [getColCount, wrap, findValidCol, adjustCol, findNextUnlockedRow, applyFocus],\n );\n\n return { focusedRow, focusedCol, setFocused, moveFocus };\n}\n"],"mappings":";;;;;;AAAA;AAAA,SAAS,UAAU,aAAa,cAAc;AAoCvC,SAAS,mBACd,UACA,aACA,UAAqC,CAAC,GACZ;AAC1B,QAAM;AAAA,IACJ,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb,aAAa;AAAA,EACf,IAAI;AAEJ,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,UAAU;AACvD,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,UAAU;AAMvD,QAAM,gBAAgB,OAAO,UAAU;AACvC,gBAAc,UAAU;AAExB,QAAM,gBAAgB,OAAO,UAAU;AACvC,gBAAc,UAAU;AAExB,QAAM,cAAc,OAAO,QAAQ;AACnC,cAAY,UAAU;AAEtB,QAAM,aAAa,YAAY,CAAC,KAAa,QAAgB;AAC3D,kBAAc,GAAG;AACjB,kBAAc,GAAG;AACjB,gBAAY,UAAU,KAAK,GAAG;AAAA,EAChC,GAAG,CAAC,CAAC;AAEL,QAAM,aAAa;AAEnB,QAAM,sBAAsB;AAAA,IAC1B,CAAC,SAAiB,cAA8B;AAC9C,UAAI,CAAC,eAAe,aAAa,GAAG;AAClC,YAAI,MAAM;AACR,kBAAQ,UAAU,YAAY,YAAY;AAAA,QAC5C;AACA,cAAM,OAAO,UAAU;AACvB,eAAO,KAAK,IAAI,GAAG,KAAK,IAAI,WAAW,GAAG,IAAI,CAAC;AAAA,MACjD;AAEA,UAAI,QAAQ;AACZ,UAAI,WAAW;AAEf,aAAO,WAAW,UAAU;AAC1B,iBAAS;AAET,YAAI,MAAM;AACR,cAAI,QAAQ,EAAG,SAAQ,WAAW;AAClC,cAAI,SAAS,SAAU,SAAQ;AAAA,QACjC,OAAO;AACL,cAAI,QAAQ,EAAG,SAAQ;AACvB,cAAI,SAAS,SAAU,SAAQ,WAAW;AAAA,QAC5C;AAEA,YAAI,CAAC,YAAY,KAAK,GAAG;AACvB,iBAAO;AAAA,QACT;AAEA;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,UAAU,MAAM,WAAW;AAAA,EAC9B;AAEA,QAAM,YAAY;AAAA,IAChB,CAAC,cAAyB;AACxB,YAAM,aAAa,cAAc;AACjC,YAAM,aAAa,cAAc;AAEjC,UAAI,cAAc,UAAU,cAAc,SAAS;AACjD,cAAM,WAAW,YAAY,UAAU;AACvC,YAAI,aAAa,EAAG;AAEpB,cAAM,OAAO,cAAc,UAAU,IAAI;AAEzC,YAAI,cAAc;AAChB,gBAAM,SAAS,aAAa,YAAY,YAAY,IAAI;AACxD,qBAAW,YAAY,MAAM;AAAA,QAC/B,WAAW,MAAM;AACf,gBAAM,UAAU,aAAa,OAAO,YAAY;AAChD,qBAAW,YAAY,MAAM;AAAA,QAC/B,OAAO;AACL,gBAAM,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,WAAW,GAAG,aAAa,IAAI,CAAC;AACpE,qBAAW,YAAY,MAAM;AAAA,QAC/B;AAAA,MACF,OAAO;AACL,cAAM,OAAO,cAAc,SAAS,IAAI;AACxC,cAAM,SAAS,oBAAoB,YAAY,IAAI;AACnD,cAAM,iBAAiB,YAAY,MAAM;AACzC,YAAI,WAAW,KAAK,IAAI,YAAY,KAAK,IAAI,GAAG,iBAAiB,CAAC,CAAC;AAEnE,YAAI,WAAW;AACb,qBAAW,UAAU,QAAQ,QAAQ;AAAA,QACvC;AAEA,mBAAW,QAAQ,QAAQ;AAAA,MAC7B;AAAA,IACF;AAAA,IACA,CAAC,aAAa,MAAM,cAAc,WAAW,qBAAqB,UAAU;AAAA,EAC9E;AAEA,SAAO,EAAE,YAAY,YAAY,YAAY,UAAU;AACzD;","names":[]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  DEFAULT_BRANDING
4
- } from "./chunk-YCS7GF6Y.js";
4
+ } from "./chunk-ZBJQXDQN.js";
5
5
  import {
6
6
  init_esm_shims
7
7
  } from "./chunk-DHET7RCE.js";
@@ -66,4 +66,4 @@ export {
66
66
  INFO_MESSAGES,
67
67
  DRY_RUN_MESSAGES
68
68
  };
69
- //# sourceMappingURL=chunk-GGHH3KR2.js.map
69
+ //# sourceMappingURL=chunk-HKDE4LJW.js.map