@agents-inc/cli 0.42.1 → 0.46.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 (186) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +128 -113
  3. package/assets/logo.png +0 -0
  4. package/assets/logo.svg +75 -0
  5. package/dist/{chunk-ZHJEZ7AU.js → chunk-3WKFSTG6.js} +2 -2
  6. package/dist/{chunk-IUYU2TP6.js → chunk-4C7PDDLY.js} +7 -7
  7. package/dist/{chunk-AQANPOLS.js → chunk-5M6JI76P.js} +2 -2
  8. package/dist/{chunk-UXNHU7Y7.js → chunk-5QRJUBK7.js} +133 -56
  9. package/dist/chunk-5QRJUBK7.js.map +1 -0
  10. package/dist/{chunk-33D24DAF.js → chunk-72GS6PIH.js} +10 -10
  11. package/dist/{chunk-PP7NDFFE.js → chunk-74HSA7C4.js} +10 -1
  12. package/dist/chunk-74HSA7C4.js.map +1 -0
  13. package/dist/chunk-7LDSHHKN.js +132 -0
  14. package/dist/chunk-7LDSHHKN.js.map +1 -0
  15. package/dist/{chunk-52SI5XJH.js → chunk-C7BO2ASM.js} +2 -7
  16. package/dist/chunk-C7BO2ASM.js.map +1 -0
  17. package/dist/{chunk-53K3URKF.js → chunk-CD64ZNYI.js} +5 -3
  18. package/dist/chunk-CD64ZNYI.js.map +1 -0
  19. package/dist/chunk-CDGHSTB6.js +69 -0
  20. package/dist/chunk-CDGHSTB6.js.map +1 -0
  21. package/dist/{chunk-FF4Z7MHY.js → chunk-CTQHZELA.js} +28 -20
  22. package/dist/chunk-CTQHZELA.js.map +1 -0
  23. package/dist/{chunk-44QCEK7E.js → chunk-D7JTL3DJ.js} +2 -2
  24. package/dist/{chunk-MMD26LKJ.js → chunk-DO5OZHSS.js} +2 -2
  25. package/dist/{chunk-ORJPGZVD.js → chunk-FKBCYT7B.js} +7 -7
  26. package/dist/chunk-FKBCYT7B.js.map +1 -0
  27. package/dist/{chunk-WMAALRQI.js → chunk-FUEZQ2H6.js} +5 -5
  28. package/dist/{chunk-WMAALRQI.js.map → chunk-FUEZQ2H6.js.map} +1 -1
  29. package/dist/{chunk-HMGWGWFT.js → chunk-G2WNOT3R.js} +2 -2
  30. package/dist/{chunk-BKW34TKI.js → chunk-GVLYNP2I.js} +4 -4
  31. package/dist/{chunk-VHGIQN5O.js → chunk-HM3DHMW7.js} +70 -24
  32. package/dist/chunk-HM3DHMW7.js.map +1 -0
  33. package/dist/{chunk-MI4NWOWD.js → chunk-I26YP2Q3.js} +5 -12
  34. package/dist/chunk-I26YP2Q3.js.map +1 -0
  35. package/dist/{chunk-FDY6SGSA.js → chunk-J64CA4V6.js} +5 -3
  36. package/dist/chunk-J64CA4V6.js.map +1 -0
  37. package/dist/{chunk-EFZN22TO.js → chunk-KWQ2BQXF.js} +3 -3
  38. package/dist/{chunk-ALWLM5MC.js → chunk-LFZXMQOH.js} +2 -2
  39. package/dist/{chunk-2TWELY5M.js → chunk-NMXNHRAK.js} +3 -3
  40. package/dist/{chunk-GCN7GGWE.js → chunk-ODQ2BKWU.js} +3 -3
  41. package/dist/{chunk-KA253GGY.js → chunk-PZLUO4OY.js} +4 -4
  42. package/dist/{chunk-X34RGEFX.js → chunk-QBUOZVNZ.js} +2 -2
  43. package/dist/{chunk-SYHRJG5G.js → chunk-RT6IBH37.js} +3498 -3592
  44. package/dist/chunk-RT6IBH37.js.map +1 -0
  45. package/dist/{chunk-YRVTXSXP.js → chunk-RWR56UVK.js} +3 -18
  46. package/dist/chunk-RWR56UVK.js.map +1 -0
  47. package/dist/{chunk-PEAPABFI.js → chunk-SGXUMZWL.js} +2 -2
  48. package/dist/{chunk-MHET2RG2.js → chunk-TOWP4T5L.js} +2 -2
  49. package/dist/{chunk-XRA4LHTJ.js → chunk-UK3AMBR7.js} +18 -14
  50. package/dist/chunk-UK3AMBR7.js.map +1 -0
  51. package/dist/{chunk-GIZ6DENW.js → chunk-VH3PI43B.js} +4 -4
  52. package/dist/{chunk-J73KIP6Z.js → chunk-XE6RTHUD.js} +66 -5
  53. package/dist/chunk-XE6RTHUD.js.map +1 -0
  54. package/dist/{chunk-EPJ2GJNJ.js → chunk-YT7UHV67.js} +10 -10
  55. package/dist/{chunk-UVVNWF43.js → chunk-YVMYQSED.js} +2 -2
  56. package/dist/commands/build/marketplace.js +12 -126
  57. package/dist/commands/build/marketplace.js.map +1 -1
  58. package/dist/commands/build/plugins.js +5 -5
  59. package/dist/commands/build/stack.js +5 -5
  60. package/dist/commands/compile.js +6 -6
  61. package/dist/commands/config/get.js +4 -4
  62. package/dist/commands/config/index.js +5 -5
  63. package/dist/commands/config/path.js +4 -4
  64. package/dist/commands/config/set-project.js +4 -4
  65. package/dist/commands/config/show.js +5 -5
  66. package/dist/commands/config/unset-project.js +4 -4
  67. package/dist/commands/diff.js +4 -4
  68. package/dist/commands/doctor.js +4 -4
  69. package/dist/commands/edit.js +33 -30
  70. package/dist/commands/edit.js.map +1 -1
  71. package/dist/commands/eject.js +54 -33
  72. package/dist/commands/eject.js.map +1 -1
  73. package/dist/commands/import/skill.js +5 -5
  74. package/dist/commands/info.js +5 -5
  75. package/dist/commands/init.js +33 -37
  76. package/dist/commands/init.js.map +1 -1
  77. package/dist/commands/list.js +4 -4
  78. package/dist/commands/new/agent.js +52 -38
  79. package/dist/commands/new/agent.js.map +1 -1
  80. package/dist/commands/new/marketplace.js +252 -0
  81. package/dist/commands/new/marketplace.js.map +1 -0
  82. package/dist/commands/new/skill.js +30 -15
  83. package/dist/commands/new/skill.js.map +1 -1
  84. package/dist/commands/outdated.js +4 -4
  85. package/dist/commands/search.js +7 -7
  86. package/dist/commands/uninstall.js +6 -6
  87. package/dist/commands/update.js +6 -6
  88. package/dist/commands/validate.js +5 -5
  89. package/dist/components/skill-search/skill-search.js +3 -3
  90. package/dist/components/wizard/category-grid.js +2 -2
  91. package/dist/components/wizard/category-grid.test.js +184 -98
  92. package/dist/components/wizard/category-grid.test.js.map +1 -1
  93. package/dist/components/wizard/checkbox-grid.js +3 -3
  94. package/dist/components/wizard/checkbox-grid.test.js +3 -3
  95. package/dist/components/wizard/domain-selection.js +9 -8
  96. package/dist/components/wizard/help-modal.js +2 -2
  97. package/dist/components/wizard/menu-item.js +1 -1
  98. package/dist/components/wizard/search-modal.js +2 -2
  99. package/dist/components/wizard/search-modal.test.js +2 -11
  100. package/dist/components/wizard/search-modal.test.js.map +1 -1
  101. package/dist/components/wizard/section-progress.js +2 -2
  102. package/dist/components/wizard/section-progress.test.js +2 -2
  103. package/dist/components/wizard/selection-card.js +2 -2
  104. package/dist/components/wizard/source-grid.js +3 -3
  105. package/dist/components/wizard/source-grid.test.js +5 -9
  106. package/dist/components/wizard/source-grid.test.js.map +1 -1
  107. package/dist/components/wizard/stack-selection.js +8 -9
  108. package/dist/components/wizard/step-agents.js +8 -7
  109. package/dist/components/wizard/step-agents.test.js +25 -20
  110. package/dist/components/wizard/step-agents.test.js.map +1 -1
  111. package/dist/components/wizard/step-build.js +8 -8
  112. package/dist/components/wizard/step-build.test.js +25 -40
  113. package/dist/components/wizard/step-build.test.js.map +1 -1
  114. package/dist/components/wizard/step-confirm.js +4 -4
  115. package/dist/components/wizard/step-confirm.test.js +8 -8
  116. package/dist/components/wizard/step-refine.js +2 -2
  117. package/dist/components/wizard/step-refine.test.js +2 -2
  118. package/dist/components/wizard/step-settings.js +5 -5
  119. package/dist/components/wizard/step-settings.test.js +8 -8
  120. package/dist/components/wizard/step-sources.js +11 -11
  121. package/dist/components/wizard/step-sources.test.js +14 -20
  122. package/dist/components/wizard/step-sources.test.js.map +1 -1
  123. package/dist/components/wizard/step-stack.js +12 -12
  124. package/dist/components/wizard/step-stack.test.js +32 -29
  125. package/dist/components/wizard/step-stack.test.js.map +1 -1
  126. package/dist/components/wizard/view-title.js +2 -2
  127. package/dist/components/wizard/wizard-layout.js +8 -8
  128. package/dist/components/wizard/wizard-tabs.js +2 -2
  129. package/dist/components/wizard/wizard-tabs.test.js +2 -2
  130. package/dist/components/wizard/wizard.js +26 -26
  131. package/dist/hooks/init.js +3 -5
  132. package/dist/hooks/init.js.map +1 -1
  133. package/dist/{source-manager-ABK5COKX.js → source-manager-6QZ2GDUA.js} +4 -4
  134. package/dist/stores/wizard-store.js +5 -5
  135. package/dist/stores/wizard-store.test.js +29 -49
  136. package/dist/stores/wizard-store.test.js.map +1 -1
  137. package/package.json +3 -1
  138. package/src/schemas/agent.schema.json +3 -0
  139. package/src/schemas/metadata.schema.json +4 -46
  140. package/src/schemas/project-config.schema.json +8 -4
  141. package/src/schemas/skills-matrix.schema.json +11 -298
  142. package/src/schemas/stacks.schema.json +2 -4
  143. package/dist/chunk-52SI5XJH.js.map +0 -1
  144. package/dist/chunk-53K3URKF.js.map +0 -1
  145. package/dist/chunk-FDY6SGSA.js.map +0 -1
  146. package/dist/chunk-FF4Z7MHY.js.map +0 -1
  147. package/dist/chunk-GVRZY5KI.js +0 -45
  148. package/dist/chunk-GVRZY5KI.js.map +0 -1
  149. package/dist/chunk-J73KIP6Z.js.map +0 -1
  150. package/dist/chunk-MI4NWOWD.js.map +0 -1
  151. package/dist/chunk-ORJPGZVD.js.map +0 -1
  152. package/dist/chunk-PP7NDFFE.js.map +0 -1
  153. package/dist/chunk-SYHRJG5G.js.map +0 -1
  154. package/dist/chunk-UXNHU7Y7.js.map +0 -1
  155. package/dist/chunk-VHGIQN5O.js.map +0 -1
  156. package/dist/chunk-XRA4LHTJ.js.map +0 -1
  157. package/dist/chunk-YRVTXSXP.js.map +0 -1
  158. package/dist/cli/defaults/agent-mappings.yaml +0 -215
  159. package/dist/commands/version/bump.js +0 -79
  160. package/dist/commands/version/bump.js.map +0 -1
  161. package/dist/commands/version/index.js +0 -54
  162. package/dist/commands/version/index.js.map +0 -1
  163. package/dist/commands/version/set.js +0 -86
  164. package/dist/commands/version/set.js.map +0 -1
  165. package/dist/commands/version/show.js +0 -54
  166. package/dist/commands/version/show.js.map +0 -1
  167. /package/dist/{chunk-ZHJEZ7AU.js.map → chunk-3WKFSTG6.js.map} +0 -0
  168. /package/dist/{chunk-IUYU2TP6.js.map → chunk-4C7PDDLY.js.map} +0 -0
  169. /package/dist/{chunk-AQANPOLS.js.map → chunk-5M6JI76P.js.map} +0 -0
  170. /package/dist/{chunk-33D24DAF.js.map → chunk-72GS6PIH.js.map} +0 -0
  171. /package/dist/{chunk-44QCEK7E.js.map → chunk-D7JTL3DJ.js.map} +0 -0
  172. /package/dist/{chunk-MMD26LKJ.js.map → chunk-DO5OZHSS.js.map} +0 -0
  173. /package/dist/{chunk-HMGWGWFT.js.map → chunk-G2WNOT3R.js.map} +0 -0
  174. /package/dist/{chunk-BKW34TKI.js.map → chunk-GVLYNP2I.js.map} +0 -0
  175. /package/dist/{chunk-EFZN22TO.js.map → chunk-KWQ2BQXF.js.map} +0 -0
  176. /package/dist/{chunk-ALWLM5MC.js.map → chunk-LFZXMQOH.js.map} +0 -0
  177. /package/dist/{chunk-2TWELY5M.js.map → chunk-NMXNHRAK.js.map} +0 -0
  178. /package/dist/{chunk-GCN7GGWE.js.map → chunk-ODQ2BKWU.js.map} +0 -0
  179. /package/dist/{chunk-KA253GGY.js.map → chunk-PZLUO4OY.js.map} +0 -0
  180. /package/dist/{chunk-X34RGEFX.js.map → chunk-QBUOZVNZ.js.map} +0 -0
  181. /package/dist/{chunk-PEAPABFI.js.map → chunk-SGXUMZWL.js.map} +0 -0
  182. /package/dist/{chunk-MHET2RG2.js.map → chunk-TOWP4T5L.js.map} +0 -0
  183. /package/dist/{chunk-GIZ6DENW.js.map → chunk-VH3PI43B.js.map} +0 -0
  184. /package/dist/{chunk-EPJ2GJNJ.js.map → chunk-YT7UHV67.js.map} +0 -0
  185. /package/dist/{chunk-UVVNWF43.js.map → chunk-YVMYQSED.js.map} +0 -0
  186. /package/dist/{source-manager-ABK5COKX.js.map → source-manager-6QZ2GDUA.js.map} +0 -0
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ensureDir,
4
+ getErrorMessage,
5
+ glob,
6
+ pluginManifestSchema,
7
+ readFileSafe,
8
+ verbose,
9
+ warn,
10
+ writeFile
11
+ } from "./chunk-5QRJUBK7.js";
12
+ import {
13
+ MAX_PLUGIN_FILE_SIZE
14
+ } from "./chunk-74HSA7C4.js";
15
+ import {
16
+ init_esm_shims
17
+ } from "./chunk-DHET7RCE.js";
18
+
19
+ // src/cli/lib/marketplace-generator.ts
20
+ init_esm_shims();
21
+ import path from "path";
22
+ import { sortBy } from "remeda";
23
+ var PLUGIN_MANIFEST_PATH = ".claude-plugin/plugin.json";
24
+ var MARKETPLACE_SCHEMA_URL = "https://anthropic.com/claude-code/marketplace.schema.json";
25
+ var CATEGORY_PATTERNS = [
26
+ { pattern: /^web-/, category: "web" },
27
+ { pattern: /^api-/, category: "api" },
28
+ { pattern: /^cli-/, category: "cli" },
29
+ { pattern: /^meta-/, category: "methodology" },
30
+ { pattern: /^infra-/, category: "infra" },
31
+ { pattern: /^mobile-/, category: "mobile" },
32
+ { pattern: /^security-/, category: "security" }
33
+ ];
34
+ function inferCategoryFromPluginName(pluginName) {
35
+ for (const { pattern, category } of CATEGORY_PATTERNS) {
36
+ if (pattern.test(pluginName)) {
37
+ return category;
38
+ }
39
+ }
40
+ return void 0;
41
+ }
42
+ async function readPluginManifest(pluginDir) {
43
+ const manifestPath = path.join(pluginDir, PLUGIN_MANIFEST_PATH);
44
+ try {
45
+ const content = await readFileSafe(manifestPath, MAX_PLUGIN_FILE_SIZE);
46
+ return pluginManifestSchema.parse(JSON.parse(content));
47
+ } catch (error) {
48
+ verbose(`Failed to read plugin manifest at '${manifestPath}': ${getErrorMessage(error)}`);
49
+ return null;
50
+ }
51
+ }
52
+ function convertManifestToMarketplacePlugin(manifest, pluginRoot, pluginDirName) {
53
+ const category = inferCategoryFromPluginName(manifest.name);
54
+ const plugin = {
55
+ name: manifest.name,
56
+ source: `./${pluginRoot}/${pluginDirName}`,
57
+ description: manifest.description,
58
+ version: manifest.version,
59
+ author: manifest.author,
60
+ keywords: manifest.keywords
61
+ };
62
+ if (category) {
63
+ plugin.category = category;
64
+ }
65
+ return plugin;
66
+ }
67
+ async function generateMarketplace(pluginsDir, options) {
68
+ verbose(`Scanning plugins directory: ${pluginsDir}`);
69
+ const manifestFiles = await glob(`**/${PLUGIN_MANIFEST_PATH}`, pluginsDir);
70
+ verbose(`Found ${manifestFiles.length} plugin manifests`);
71
+ const plugins = [];
72
+ for (const manifestFile of manifestFiles) {
73
+ const pluginDirName = manifestFile.split("/")[0];
74
+ const pluginDir = path.join(pluginsDir, pluginDirName);
75
+ const manifest = await readPluginManifest(pluginDir);
76
+ if (!manifest) {
77
+ warn(`Could not read plugin manifest: '${manifestFile}'`);
78
+ continue;
79
+ }
80
+ const plugin = convertManifestToMarketplacePlugin(
81
+ manifest,
82
+ options.pluginRoot.replace(/^\.\//, ""),
83
+ pluginDirName
84
+ );
85
+ plugins.push(plugin);
86
+ verbose(` [OK] ${plugin.name}`);
87
+ }
88
+ const sortedPlugins = sortBy(plugins, (p) => p.name);
89
+ const marketplace = {
90
+ $schema: MARKETPLACE_SCHEMA_URL,
91
+ name: options.name,
92
+ version: options.version ?? "1.0.0",
93
+ owner: {
94
+ name: options.ownerName
95
+ },
96
+ metadata: {
97
+ pluginRoot: options.pluginRoot
98
+ },
99
+ plugins: sortedPlugins
100
+ };
101
+ if (options.description) {
102
+ marketplace.description = options.description;
103
+ }
104
+ if (options.ownerEmail) {
105
+ marketplace.owner.email = options.ownerEmail;
106
+ }
107
+ return marketplace;
108
+ }
109
+ async function writeMarketplace(outputPath, marketplace) {
110
+ await ensureDir(path.dirname(outputPath));
111
+ const content = `${JSON.stringify(marketplace, null, 2)}
112
+ `;
113
+ await writeFile(outputPath, content);
114
+ }
115
+ function getMarketplaceStats(marketplace) {
116
+ const byCategory = {};
117
+ for (const plugin of marketplace.plugins) {
118
+ const category = plugin.category ?? "uncategorized";
119
+ byCategory[category] = (byCategory[category] ?? 0) + 1;
120
+ }
121
+ return {
122
+ total: marketplace.plugins.length,
123
+ byCategory
124
+ };
125
+ }
126
+
127
+ export {
128
+ generateMarketplace,
129
+ writeMarketplace,
130
+ getMarketplaceStats
131
+ };
132
+ //# sourceMappingURL=chunk-7LDSHHKN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/lib/marketplace-generator.ts"],"sourcesContent":["import path from \"path\";\nimport { sortBy } from \"remeda\";\n\nimport { MAX_PLUGIN_FILE_SIZE } from \"../consts\";\nimport { getErrorMessage } from \"../utils/errors\";\nimport { readFileSafe, writeFile, glob, ensureDir } from \"../utils/fs\";\nimport { verbose, warn } from \"../utils/logger\";\nimport type { Marketplace, MarketplacePlugin, PluginManifest } from \"../types\";\nimport { pluginManifestSchema } from \"./schemas\";\n\nconst PLUGIN_MANIFEST_PATH = \".claude-plugin/plugin.json\";\nconst MARKETPLACE_SCHEMA_URL = \"https://anthropic.com/claude-code/marketplace.schema.json\";\n\n/**\n * Category patterns for marketplace plugins.\n *\n * Plugin names match skill IDs directly (e.g., \"web-framework-react\").\n * The category is the first segment of the ID:\n * - web-* -> web\n * - api-* -> api\n * - cli-* -> cli\n * - meta-* -> methodology\n * - infra-* -> infra\n * - mobile-* -> mobile\n * - security-* -> security\n */\nconst CATEGORY_PATTERNS: Array<{ pattern: RegExp; category: string }> = [\n { pattern: /^web-/, category: \"web\" },\n { pattern: /^api-/, category: \"api\" },\n { pattern: /^cli-/, category: \"cli\" },\n { pattern: /^meta-/, category: \"methodology\" },\n { pattern: /^infra-/, category: \"infra\" },\n { pattern: /^mobile-/, category: \"mobile\" },\n { pattern: /^security-/, category: \"security\" },\n];\n\ntype MarketplaceOptions = {\n name: string;\n version?: string;\n description?: string;\n ownerName: string;\n ownerEmail?: string;\n pluginRoot: string;\n};\n\nfunction inferCategoryFromPluginName(pluginName: string): string | undefined {\n for (const { pattern, category } of CATEGORY_PATTERNS) {\n if (pattern.test(pluginName)) {\n return category;\n }\n }\n return undefined;\n}\n\nasync function readPluginManifest(pluginDir: string): Promise<PluginManifest | null> {\n const manifestPath = path.join(pluginDir, PLUGIN_MANIFEST_PATH);\n\n try {\n const content = await readFileSafe(manifestPath, MAX_PLUGIN_FILE_SIZE);\n return pluginManifestSchema.parse(JSON.parse(content));\n } catch (error) {\n verbose(`Failed to read plugin manifest at '${manifestPath}': ${getErrorMessage(error)}`);\n return null;\n }\n}\n\nfunction convertManifestToMarketplacePlugin(\n manifest: PluginManifest,\n pluginRoot: string,\n pluginDirName: string,\n): MarketplacePlugin {\n const category = inferCategoryFromPluginName(manifest.name);\n\n const plugin: MarketplacePlugin = {\n name: manifest.name,\n source: `./${pluginRoot}/${pluginDirName}`,\n description: manifest.description,\n version: manifest.version,\n author: manifest.author,\n keywords: manifest.keywords,\n };\n\n if (category) {\n plugin.category = category;\n }\n\n return plugin;\n}\n\nexport async function generateMarketplace(\n pluginsDir: string,\n options: MarketplaceOptions,\n): Promise<Marketplace> {\n verbose(`Scanning plugins directory: ${pluginsDir}`);\n\n const manifestFiles = await glob(`**/${PLUGIN_MANIFEST_PATH}`, pluginsDir);\n verbose(`Found ${manifestFiles.length} plugin manifests`);\n\n const plugins: MarketplacePlugin[] = [];\n\n for (const manifestFile of manifestFiles) {\n const pluginDirName = manifestFile.split(\"/\")[0];\n const pluginDir = path.join(pluginsDir, pluginDirName);\n\n const manifest = await readPluginManifest(pluginDir);\n if (!manifest) {\n warn(`Could not read plugin manifest: '${manifestFile}'`);\n continue;\n }\n\n const plugin = convertManifestToMarketplacePlugin(\n manifest,\n options.pluginRoot.replace(/^\\.\\//, \"\"),\n pluginDirName,\n );\n plugins.push(plugin);\n verbose(` [OK] ${plugin.name}`);\n }\n\n const sortedPlugins = sortBy(plugins, (p) => p.name);\n\n const marketplace: Marketplace = {\n $schema: MARKETPLACE_SCHEMA_URL,\n name: options.name,\n version: options.version ?? \"1.0.0\",\n owner: {\n name: options.ownerName,\n },\n metadata: {\n pluginRoot: options.pluginRoot,\n },\n plugins: sortedPlugins,\n };\n\n if (options.description) {\n marketplace.description = options.description;\n }\n\n if (options.ownerEmail) {\n marketplace.owner.email = options.ownerEmail;\n }\n\n return marketplace;\n}\n\nexport async function writeMarketplace(\n outputPath: string,\n marketplace: Marketplace,\n): Promise<void> {\n await ensureDir(path.dirname(outputPath));\n const content = `${JSON.stringify(marketplace, null, 2)}\\n`;\n await writeFile(outputPath, content);\n}\n\nexport function getMarketplaceStats(marketplace: Marketplace): {\n total: number;\n byCategory: Record<string, number>;\n} {\n const byCategory: Record<string, number> = {};\n\n for (const plugin of marketplace.plugins) {\n const category = plugin.category ?? \"uncategorized\";\n byCategory[category] = (byCategory[category] ?? 0) + 1;\n }\n\n return {\n total: marketplace.plugins.length,\n byCategory,\n };\n}\n"],"mappings":"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","names":[]}
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-KUV24B5M.js";
5
5
  import {
6
6
  CLI_COLORS
7
- } from "./chunk-PP7NDFFE.js";
7
+ } from "./chunk-74HSA7C4.js";
8
8
  import {
9
9
  init_esm_shims
10
10
  } from "./chunk-DHET7RCE.js";
@@ -18,7 +18,6 @@ var MARKER_FOCUSED = "\u25B8";
18
18
  var MARKER_SPACER = " ";
19
19
  var ResultRow = ({ candidate, isFocused }) => {
20
20
  const marker = isFocused ? MARKER_FOCUSED : MARKER_SPACER;
21
- const versionLabel = candidate.version != null ? `v${candidate.version}` : "";
22
21
  const sourceLabel = `${candidate.sourceName}/${candidate.id}`;
23
22
  return /* @__PURE__ */ jsxs(Box, { children: [
24
23
  /* @__PURE__ */ jsxs(Text, { bold: isFocused, color: isFocused ? CLI_COLORS.PRIMARY : void 0, children: [
@@ -26,10 +25,6 @@ var ResultRow = ({ candidate, isFocused }) => {
26
25
  " "
27
26
  ] }),
28
27
  /* @__PURE__ */ jsx(Text, { bold: isFocused, color: isFocused ? CLI_COLORS.PRIMARY : void 0, children: sourceLabel }),
29
- versionLabel && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
30
- " ",
31
- versionLabel
32
- ] }),
33
28
  candidate.description && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
34
29
  " ",
35
30
  candidate.description
@@ -92,4 +87,4 @@ var SearchModal = ({ results, alias, onBind, onClose }) => {
92
87
  export {
93
88
  SearchModal
94
89
  };
95
- //# sourceMappingURL=chunk-52SI5XJH.js.map
90
+ //# sourceMappingURL=chunk-C7BO2ASM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/wizard/search-modal.tsx"],"sourcesContent":["import React, { useCallback } from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { BoundSkillCandidate } from \"../../types/index.js\";\nimport { CLI_COLORS } from \"../../consts.js\";\nimport { useKeyboardNavigation } from \"../hooks/use-keyboard-navigation.js\";\n\nexport type SearchModalProps = {\n results: BoundSkillCandidate[];\n alias: string;\n onBind: (candidate: BoundSkillCandidate) => void;\n onClose: () => void;\n};\n\nconst MARKER_FOCUSED = \"\\u25B8\";\nconst MARKER_SPACER = \" \";\n\ntype ResultRowProps = {\n candidate: BoundSkillCandidate;\n isFocused: boolean;\n};\n\nconst ResultRow: React.FC<ResultRowProps> = ({ candidate, isFocused }) => {\n const marker = isFocused ? MARKER_FOCUSED : MARKER_SPACER;\n const sourceLabel = `${candidate.sourceName}/${candidate.id}`;\n\n return (\n <Box>\n <Text bold={isFocused} color={isFocused ? CLI_COLORS.PRIMARY : undefined}>\n {marker}{\" \"}\n </Text>\n <Text bold={isFocused} color={isFocused ? CLI_COLORS.PRIMARY : undefined}>\n {sourceLabel}\n </Text>\n {candidate.description && (\n <Text dimColor>\n {\" \"}\n {candidate.description}\n </Text>\n )}\n </Box>\n );\n};\n\nexport const SearchModal: React.FC<SearchModalProps> = ({ results, alias, onBind, onClose }) => {\n const handleEnter = useCallback(\n (index: number) => {\n if (results.length > 0) {\n const selected = results[index];\n if (selected) {\n onBind(selected);\n }\n }\n },\n [results, onBind],\n );\n\n const { focusedIndex } = useKeyboardNavigation(\n results.length,\n { onEnter: handleEnter, onEscape: onClose },\n { vimKeys: false },\n );\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderColor={CLI_COLORS.NEUTRAL}\n paddingX={1}\n paddingY={0}\n marginTop={1}\n >\n <Text bold>Search results for &quot;{alias}&quot;</Text>\n <Text> </Text>\n\n {results.length === 0 ? (\n <Text dimColor>No results found</Text>\n ) : (\n results.map((candidate, index) => (\n <ResultRow\n key={`${candidate.sourceName}-${candidate.id}`}\n candidate={candidate}\n isFocused={index === focusedIndex}\n />\n ))\n )}\n\n <Text> </Text>\n <Text dimColor>\n {\"\\u2191\"}/{\"\\u2193\"} navigate ENTER bind ESC close\n </Text>\n </Box>\n );\n};\n"],"mappings":";;;;;;;;;;;;AAAA;AAAA,SAAgB,mBAAmB;AACnC,SAAS,KAAK,YAAY;AA0BpB,SAGA,KAHA;AAdN,IAAM,iBAAiB;AACvB,IAAM,gBAAgB;AAOtB,IAAM,YAAsC,CAAC,EAAE,WAAW,UAAU,MAAM;AACxE,QAAM,SAAS,YAAY,iBAAiB;AAC5C,QAAM,cAAc,GAAG,UAAU,UAAU,IAAI,UAAU,EAAE;AAE3D,SACE,qBAAC,OACC;AAAA,yBAAC,QAAK,MAAM,WAAW,OAAO,YAAY,WAAW,UAAU,QAC5D;AAAA;AAAA,MAAQ;AAAA,OACX;AAAA,IACA,oBAAC,QAAK,MAAM,WAAW,OAAO,YAAY,WAAW,UAAU,QAC5D,uBACH;AAAA,IACC,UAAU,eACT,qBAAC,QAAK,UAAQ,MACX;AAAA;AAAA,MACA,UAAU;AAAA,OACb;AAAA,KAEJ;AAEJ;AAEO,IAAM,cAA0C,CAAC,EAAE,SAAS,OAAO,QAAQ,QAAQ,MAAM;AAC9F,QAAM,cAAc;AAAA,IAClB,CAAC,UAAkB;AACjB,UAAI,QAAQ,SAAS,GAAG;AACtB,cAAM,WAAW,QAAQ,KAAK;AAC9B,YAAI,UAAU;AACZ,iBAAO,QAAQ;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,SAAS,MAAM;AAAA,EAClB;AAEA,QAAM,EAAE,aAAa,IAAI;AAAA,IACvB,QAAQ;AAAA,IACR,EAAE,SAAS,aAAa,UAAU,QAAQ;AAAA,IAC1C,EAAE,SAAS,MAAM;AAAA,EACnB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAc;AAAA,MACd,aAAY;AAAA,MACZ,aAAa,WAAW;AAAA,MACxB,UAAU;AAAA,MACV,UAAU;AAAA,MACV,WAAW;AAAA,MAEX;AAAA,6BAAC,QAAK,MAAI,MAAC;AAAA;AAAA,UAA0B;AAAA,UAAM;AAAA,WAAM;AAAA,QACjD,oBAAC,QAAK,eAAC;AAAA,QAEN,QAAQ,WAAW,IAClB,oBAAC,QAAK,UAAQ,MAAC,8BAAgB,IAE/B,QAAQ,IAAI,CAAC,WAAW,UACtB;AAAA,UAAC;AAAA;AAAA,YAEC;AAAA,YACA,WAAW,UAAU;AAAA;AAAA,UAFhB,GAAG,UAAU,UAAU,IAAI,UAAU,EAAE;AAAA,QAG9C,CACD;AAAA,QAGH,oBAAC,QAAK,eAAC;AAAA,QACP,qBAAC,QAAK,UAAQ,MACX;AAAA;AAAA,UAAS;AAAA,UAAE;AAAA,UAAS;AAAA,WACvB;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  CLI_COLORS,
7
7
  SCROLL_VIEWPORT
8
- } from "./chunk-PP7NDFFE.js";
8
+ } from "./chunk-74HSA7C4.js";
9
9
  import {
10
10
  init_esm_shims
11
11
  } from "./chunk-DHET7RCE.js";
@@ -164,6 +164,7 @@ var getCompatibilityLabel = (option, isLocked) => {
164
164
  };
165
165
  var SkillTag = ({ option, isFocused, isLocked, showLabels }) => {
166
166
  const getTextColor = () => {
167
+ if (option.state === "disabled" && option.selected) return CLI_COLORS.PRIMARY;
167
168
  if (isLocked || option.state === "disabled") return CLI_COLORS.NEUTRAL;
168
169
  if (option.selected) return CLI_COLORS.PRIMARY;
169
170
  if (option.state === "recommended") return CLI_COLORS.UNFOCUSED;
@@ -171,6 +172,7 @@ var SkillTag = ({ option, isFocused, isLocked, showLabels }) => {
171
172
  return CLI_COLORS.NEUTRAL;
172
173
  };
173
174
  const getStateBorderColor = () => {
175
+ if (option.state === "disabled" && option.selected) return CLI_COLORS.PRIMARY;
174
176
  if (isLocked || option.state === "disabled") return CLI_COLORS.NEUTRAL;
175
177
  if (option.selected) return CLI_COLORS.PRIMARY;
176
178
  if (option.state === "recommended") return CLI_COLORS.UNFOCUSED;
@@ -187,7 +189,7 @@ var SkillTag = ({ option, isFocused, isLocked, showLabels }) => {
187
189
  borderStyle: "single",
188
190
  flexShrink: 0,
189
191
  children: /* @__PURE__ */ jsxs(Fragment, { children: [
190
- /* @__PURE__ */ jsxs(Text, { color: textColor, bold: true, children: [
192
+ /* @__PURE__ */ jsxs(Text, { color: textColor, bold: true, dimColor: option.state === "disabled" && option.selected, children: [
191
193
  " ",
192
194
  option.label,
193
195
  " "
@@ -371,4 +373,4 @@ var CategoryGrid = ({
371
373
  export {
372
374
  CategoryGrid
373
375
  };
374
- //# sourceMappingURL=chunk-53K3URKF.js.map
376
+ //# sourceMappingURL=chunk-CD64ZNYI.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};\n\nconst CategorySection: React.FC<CategorySectionProps> = ({\n isFirst,\n category,\n options,\n isLocked,\n isFocused,\n focusedOptionIndex,\n showLabels,\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 </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 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"]}
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getDomainDisplayName
4
+ } from "./chunk-RWR56UVK.js";
5
+ import {
6
+ CheckboxGrid
7
+ } from "./chunk-ODQ2BKWU.js";
8
+ import {
9
+ useWizardStore
10
+ } from "./chunk-UK3AMBR7.js";
11
+ import {
12
+ typedEntries
13
+ } from "./chunk-T4EXUIBY.js";
14
+ import {
15
+ init_esm_shims
16
+ } from "./chunk-DHET7RCE.js";
17
+
18
+ // src/cli/components/wizard/domain-selection.tsx
19
+ init_esm_shims();
20
+ import { useMemo } from "react";
21
+ import { unique } from "remeda";
22
+ import { jsx } from "react/jsx-runtime";
23
+ var BUILT_IN_DOMAIN_DESCRIPTIONS = {
24
+ web: "Frontend web applications",
25
+ api: "Backend APIs and services",
26
+ cli: "Command-line tools",
27
+ mobile: "Mobile applications",
28
+ shared: "Shared utilities and methodology"
29
+ };
30
+ var BUILT_IN_DOMAIN_ORDER = ["web", "api", "cli", "mobile"];
31
+ var DomainSelection = ({ matrix }) => {
32
+ const { selectedDomains, toggleDomain, setStep, setApproach, selectStack } = useWizardStore();
33
+ const availableDomains = useMemo(() => {
34
+ const matrixDomains = unique(
35
+ typedEntries(matrix.categories).map(([, cat]) => cat?.domain).filter((d) => d != null && d !== "shared")
36
+ );
37
+ const ordered = [
38
+ ...BUILT_IN_DOMAIN_ORDER.filter((d) => matrixDomains.includes(d)),
39
+ ...matrixDomains.filter((d) => !BUILT_IN_DOMAIN_ORDER.includes(d))
40
+ ];
41
+ return ordered.map((domain) => ({
42
+ id: domain,
43
+ label: getDomainDisplayName(domain),
44
+ description: BUILT_IN_DOMAIN_DESCRIPTIONS[domain] ?? `${getDomainDisplayName(domain)} skills`
45
+ }));
46
+ }, [matrix]);
47
+ const handleBack = () => {
48
+ setApproach(null);
49
+ selectStack(null);
50
+ };
51
+ return /* @__PURE__ */ jsx(
52
+ CheckboxGrid,
53
+ {
54
+ title: "Select domains to configure",
55
+ items: availableDomains,
56
+ selectedIds: selectedDomains,
57
+ onToggle: toggleDomain,
58
+ onContinue: () => setStep("build"),
59
+ onBack: handleBack,
60
+ continueLabel: (count) => `Continue with ${count} domain(s)`,
61
+ emptyMessage: "Please select at least one domain"
62
+ }
63
+ );
64
+ };
65
+
66
+ export {
67
+ DomainSelection
68
+ };
69
+ //# sourceMappingURL=chunk-CDGHSTB6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/wizard/domain-selection.tsx"],"sourcesContent":["import React, { useMemo } from \"react\";\nimport { unique } from \"remeda\";\nimport { useWizardStore } from \"../../stores/wizard-store.js\";\nimport type { Domain, MergedSkillsMatrix } from \"../../types/index.js\";\nimport { typedEntries } from \"../../utils/typed-object.js\";\nimport { CheckboxGrid, type CheckboxItem } from \"./checkbox-grid.js\";\nimport { getDomainDisplayName } from \"./utils.js\";\n\nconst BUILT_IN_DOMAIN_DESCRIPTIONS: Record<Domain, string> = {\n web: \"Frontend web applications\",\n api: \"Backend APIs and services\",\n cli: \"Command-line tools\",\n mobile: \"Mobile applications\",\n shared: \"Shared utilities and methodology\",\n};\n\n/** Built-in domain display order. Custom domains appear after these. */\nconst BUILT_IN_DOMAIN_ORDER: Domain[] = [\"web\", \"api\", \"cli\", \"mobile\"];\n\ntype DomainSelectionProps = {\n matrix: MergedSkillsMatrix;\n};\n\nexport const DomainSelection: React.FC<DomainSelectionProps> = ({ matrix }) => {\n const { selectedDomains, toggleDomain, setStep, setApproach, selectStack } = useWizardStore();\n\n const availableDomains = useMemo((): CheckboxItem<Domain>[] => {\n const matrixDomains = unique(\n typedEntries(matrix.categories)\n .map(([, cat]) => cat?.domain)\n .filter((d): d is Domain => d != null && d !== \"shared\"),\n );\n\n const ordered: Domain[] = [\n ...BUILT_IN_DOMAIN_ORDER.filter((d) => matrixDomains.includes(d)),\n ...matrixDomains.filter((d) => !BUILT_IN_DOMAIN_ORDER.includes(d)),\n ];\n\n return ordered.map((domain) => ({\n id: domain,\n label: getDomainDisplayName(domain),\n description: BUILT_IN_DOMAIN_DESCRIPTIONS[domain] ?? `${getDomainDisplayName(domain)} skills`,\n }));\n }, [matrix]);\n\n const handleBack = () => {\n setApproach(null);\n selectStack(null);\n };\n\n return (\n <CheckboxGrid\n title=\"Select domains to configure\"\n // subtitle=\"Select one or more domains, then continue\"\n items={availableDomains}\n selectedIds={selectedDomains}\n onToggle={toggleDomain}\n onContinue={() => setStep(\"build\")}\n onBack={handleBack}\n continueLabel={(count) => `Continue with ${count} domain(s)`}\n emptyMessage=\"Please select at least one domain\"\n />\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA,SAAgB,eAAe;AAC/B,SAAS,cAAc;AAkDnB;AA3CJ,IAAM,+BAAuD;AAAA,EAC3D,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,QAAQ;AACV;AAGA,IAAM,wBAAkC,CAAC,OAAO,OAAO,OAAO,QAAQ;AAM/D,IAAM,kBAAkD,CAAC,EAAE,OAAO,MAAM;AAC7E,QAAM,EAAE,iBAAiB,cAAc,SAAS,aAAa,YAAY,IAAI,eAAe;AAE5F,QAAM,mBAAmB,QAAQ,MAA8B;AAC7D,UAAM,gBAAgB;AAAA,MACpB,aAAa,OAAO,UAAU,EAC3B,IAAI,CAAC,CAAC,EAAE,GAAG,MAAM,KAAK,MAAM,EAC5B,OAAO,CAAC,MAAmB,KAAK,QAAQ,MAAM,QAAQ;AAAA,IAC3D;AAEA,UAAM,UAAoB;AAAA,MACxB,GAAG,sBAAsB,OAAO,CAAC,MAAM,cAAc,SAAS,CAAC,CAAC;AAAA,MAChE,GAAG,cAAc,OAAO,CAAC,MAAM,CAAC,sBAAsB,SAAS,CAAC,CAAC;AAAA,IACnE;AAEA,WAAO,QAAQ,IAAI,CAAC,YAAY;AAAA,MAC9B,IAAI;AAAA,MACJ,OAAO,qBAAqB,MAAM;AAAA,MAClC,aAAa,6BAA6B,MAAM,KAAK,GAAG,qBAAqB,MAAM,CAAC;AAAA,IACtF,EAAE;AAAA,EACJ,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,aAAa,MAAM;AACvB,gBAAY,IAAI;AAChB,gBAAY,IAAI;AAAA,EAClB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MAEN,OAAO;AAAA,MACP,aAAa;AAAA,MACb,UAAU;AAAA,MACV,YAAY,MAAM,QAAQ,OAAO;AAAA,MACjC,QAAQ;AAAA,MACR,eAAe,CAAC,UAAU,iBAAiB,KAAK;AAAA,MAChD,cAAa;AAAA;AAAA,EACf;AAEJ;","names":[]}
@@ -1,44 +1,44 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- StepStack
4
- } from "./chunk-WMAALRQI.js";
5
2
  import {
6
3
  WizardLayout
7
- } from "./chunk-ORJPGZVD.js";
8
- import {
9
- StepConfirm
10
- } from "./chunk-BKW34TKI.js";
4
+ } from "./chunk-FKBCYT7B.js";
11
5
  import {
12
6
  StepSettings
13
- } from "./chunk-IUYU2TP6.js";
7
+ } from "./chunk-4C7PDDLY.js";
14
8
  import {
15
9
  StepSources
16
- } from "./chunk-33D24DAF.js";
10
+ } from "./chunk-72GS6PIH.js";
11
+ import {
12
+ StepStack
13
+ } from "./chunk-FUEZQ2H6.js";
17
14
  import {
18
15
  StepAgents
19
- } from "./chunk-VHGIQN5O.js";
16
+ } from "./chunk-HM3DHMW7.js";
20
17
  import {
21
18
  StepBuild
22
- } from "./chunk-EPJ2GJNJ.js";
19
+ } from "./chunk-YT7UHV67.js";
20
+ import {
21
+ StepConfirm
22
+ } from "./chunk-GVLYNP2I.js";
23
23
  import {
24
24
  getStackName
25
- } from "./chunk-YRVTXSXP.js";
25
+ } from "./chunk-RWR56UVK.js";
26
26
  import {
27
27
  cliTheme
28
- } from "./chunk-AQANPOLS.js";
28
+ } from "./chunk-5M6JI76P.js";
29
29
  import {
30
30
  useWizardStore
31
- } from "./chunk-XRA4LHTJ.js";
31
+ } from "./chunk-UK3AMBR7.js";
32
32
  import {
33
33
  resolveAlias,
34
34
  validateSelection
35
- } from "./chunk-SYHRJG5G.js";
35
+ } from "./chunk-RT6IBH37.js";
36
36
  import {
37
37
  warn
38
- } from "./chunk-UXNHU7Y7.js";
38
+ } from "./chunk-5QRJUBK7.js";
39
39
  import {
40
40
  CLI_COLORS
41
- } from "./chunk-PP7NDFFE.js";
41
+ } from "./chunk-74HSA7C4.js";
42
42
  import {
43
43
  init_esm_shims
44
44
  } from "./chunk-DHET7RCE.js";
@@ -58,6 +58,7 @@ function useWizardInitialization({
58
58
  initialInstallMode,
59
59
  initialExpertMode,
60
60
  initialDomains,
61
+ initialAgents,
61
62
  installedSkillIds
62
63
  }) {
63
64
  const initialized = useRef(false);
@@ -78,6 +79,9 @@ function useWizardInitialization({
78
79
  if (initialDomains?.length) {
79
80
  useWizardStore.setState({ selectedDomains: initialDomains });
80
81
  }
82
+ if (initialAgents?.length) {
83
+ useWizardStore.setState({ selectedAgents: initialAgents });
84
+ }
81
85
  }
82
86
  }
83
87
 
@@ -143,6 +147,7 @@ var Wizard = ({
143
147
  initialInstallMode,
144
148
  initialExpertMode,
145
149
  initialDomains,
150
+ initialAgents,
146
151
  installedSkillIds,
147
152
  projectDir
148
153
  }) => {
@@ -159,6 +164,7 @@ var Wizard = ({
159
164
  initialInstallMode,
160
165
  initialExpertMode,
161
166
  initialDomains,
167
+ initialAgents,
162
168
  installedSkillIds
163
169
  });
164
170
  const buildStepProps = useBuildStepProps({ store, matrix, installedSkillIds });
@@ -270,7 +276,9 @@ var Wizard = ({
270
276
  matrix,
271
277
  projectDir,
272
278
  onContinue: () => {
273
- store.preselectAgentsFromDomains();
279
+ if (!initialAgents?.length) {
280
+ store.preselectAgentsFromDomains();
281
+ }
274
282
  store.setStep("agents");
275
283
  },
276
284
  onBack: store.goBack
@@ -278,7 +286,7 @@ var Wizard = ({
278
286
  );
279
287
  }
280
288
  case "agents":
281
- return /* @__PURE__ */ jsx(StepAgents, {});
289
+ return /* @__PURE__ */ jsx(StepAgents, { matrix });
282
290
  case "confirm": {
283
291
  const stackName = getStackName(store.selectedStackId, matrix);
284
292
  const technologyCount = store.getTechnologyCount();
@@ -315,4 +323,4 @@ var Wizard = ({
315
323
  export {
316
324
  Wizard
317
325
  };
318
- //# sourceMappingURL=chunk-FF4Z7MHY.js.map
326
+ //# sourceMappingURL=chunk-CTQHZELA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/wizard/wizard.tsx","../src/cli/components/hooks/use-wizard-initialization.ts","../src/cli/components/hooks/use-build-step-props.ts"],"sourcesContent":["import React, { useCallback } from \"react\";\nimport { Box, Text, useApp, useInput, useStdout } from \"ink\";\nimport { ThemeProvider } from \"@inkjs/ui\";\nimport { useWizardStore, type WizardStep } from \"../../stores/wizard-store.js\";\nimport { CLI_COLORS } from \"../../consts.js\";\nimport { cliTheme } from \"../themes/default.js\";\nimport { WizardLayout } from \"./wizard-layout.js\";\nimport { StepStack } from \"./step-stack.js\";\nimport { StepBuild } from \"./step-build.js\";\nimport { StepConfirm } from \"./step-confirm.js\";\nimport { StepSources } from \"./step-sources.js\";\nimport { StepSettings } from \"./step-settings.js\";\nimport { StepAgents } from \"./step-agents.js\";\nimport { resolveAlias, validateSelection } from \"../../lib/matrix/index.js\";\nimport type {\n AgentName,\n Domain,\n DomainSelections,\n MergedSkillsMatrix,\n SkillId,\n} from \"../../types/index.js\";\nimport { getStackName } from \"./utils.js\";\nimport { warn } from \"../../utils/logger.js\";\nimport { useWizardInitialization } from \"../hooks/use-wizard-initialization.js\";\nimport { useBuildStepProps } from \"../hooks/use-build-step-props.js\";\n\nexport type WizardResultV2 = {\n selectedSkills: SkillId[];\n selectedAgents: AgentName[];\n selectedStackId: string | null;\n domainSelections: DomainSelections;\n selectedDomains: Domain[];\n sourceSelections: Partial<Record<SkillId, string>>;\n expertMode: boolean;\n installMode: \"plugin\" | \"local\";\n cancelled: boolean;\n validation: {\n valid: boolean;\n errors: Array<{ message: string }>;\n warnings: Array<{ message: string }>;\n };\n};\n\ntype WizardProps = {\n matrix: MergedSkillsMatrix;\n onComplete: (result: WizardResultV2) => void;\n onCancel: () => void;\n version?: string;\n marketplaceLabel?: string;\n logo?: string;\n initialStep?: WizardStep;\n initialInstallMode?: \"plugin\" | \"local\";\n initialExpertMode?: boolean;\n initialDomains?: Domain[];\n initialAgents?: AgentName[];\n installedSkillIds?: SkillId[];\n projectDir?: string;\n};\n\nconst MIN_TERMINAL_WIDTH = 80;\nconst MIN_TERMINAL_HEIGHT = 15;\n\nexport const Wizard: React.FC<WizardProps> = ({\n matrix,\n onComplete,\n onCancel,\n version,\n marketplaceLabel,\n logo,\n initialStep,\n initialInstallMode,\n initialExpertMode,\n initialDomains,\n initialAgents,\n installedSkillIds,\n projectDir,\n}) => {\n const store = useWizardStore();\n const { exit } = useApp();\n const { stdout } = useStdout();\n\n const terminalWidth = stdout.columns || MIN_TERMINAL_WIDTH;\n const terminalHeight = stdout.rows || MIN_TERMINAL_HEIGHT;\n const isNarrowTerminal = terminalWidth < MIN_TERMINAL_WIDTH;\n const isShortTerminal = terminalHeight < MIN_TERMINAL_HEIGHT;\n\n useWizardInitialization({\n matrix,\n initialStep,\n initialInstallMode,\n initialExpertMode,\n initialDomains,\n initialAgents,\n installedSkillIds,\n });\n\n const buildStepProps = useBuildStepProps({ store, matrix, installedSkillIds });\n\n useInput((input, key) => {\n // ESC is handled by step-settings.tsx's own useKeyboardNavigation hook\n if (store.showSettings) {\n if (input === \"g\" || input === \"G\") {\n store.toggleSettings();\n }\n return;\n }\n\n if (store.showHelp) {\n if (key.escape || input === \"?\") {\n store.toggleHelp();\n }\n return;\n }\n\n if (input === \"?\") {\n store.toggleHelp();\n return;\n }\n\n if (key.escape) {\n // At the initial stack/scratch selection (approach not yet set), ESC cancels the wizard.\n // StackSelection handles its own ESC via the onCancel prop.\n // Other steps that don't have their own escape handler use goBack.\n if (\n store.step !== \"build\" &&\n store.step !== \"confirm\" &&\n store.step !== \"sources\" &&\n store.step !== \"agents\" &&\n store.step !== \"stack\"\n ) {\n store.goBack();\n }\n return;\n }\n\n if ((input === \"a\" || input === \"A\") && store.step === \"build\" && store.selectedStackId) {\n store.setStackAction(\"defaults\");\n store.setStep(\"confirm\");\n return;\n }\n\n if ((input === \"g\" || input === \"G\") && store.step === \"sources\") {\n store.toggleSettings();\n return;\n }\n\n if (input === \"e\" || input === \"E\") {\n store.toggleExpertMode();\n return;\n }\n if (input === \"p\" || input === \"P\") {\n store.toggleInstallMode();\n }\n });\n\n const handleComplete = useCallback(() => {\n let allSkills: SkillId[];\n\n if (store.selectedStackId && store.stackAction === \"defaults\") {\n const stack = matrix.suggestedStacks.find((s) => s.id === store.selectedStackId);\n if (!stack) {\n warn(`Stack not found in matrix: '${store.selectedStackId}'`);\n }\n allSkills = [...(stack?.allSkillIds || [])];\n } else {\n const techNames = store.getAllSelectedTechnologies();\n allSkills = techNames.map((tech) => {\n const resolved = resolveAlias(tech, matrix);\n if (!matrix.skills[resolved]) {\n warn(\n `Technology '${tech}' could not be resolved to a skill ID - it may be missing from skill_aliases`,\n );\n }\n return resolved;\n });\n }\n\n const methodologySkills = store.getDefaultMethodologySkills();\n for (const skill of methodologySkills) {\n if (!allSkills.includes(skill)) {\n allSkills.push(skill);\n }\n }\n\n const validation = validateSelection(allSkills, matrix);\n\n const result: WizardResultV2 = {\n selectedSkills: allSkills,\n selectedAgents: store.selectedAgents,\n selectedStackId: store.selectedStackId,\n domainSelections: store.domainSelections,\n selectedDomains: store.selectedDomains,\n sourceSelections: store.sourceSelections,\n expertMode: store.expertMode,\n installMode: store.installMode,\n cancelled: false,\n validation,\n };\n\n onComplete(result);\n exit();\n }, [store, matrix, onComplete, exit]);\n\n const handleCancel = useCallback(() => {\n onCancel();\n exit();\n }, [onCancel, exit]);\n\n const renderStep = () => {\n switch (store.step) {\n case \"stack\":\n return <StepStack matrix={matrix} onCancel={handleCancel} />;\n\n case \"build\":\n return <StepBuild {...buildStepProps} />;\n\n case \"sources\": {\n if (store.showSettings) {\n return (\n <StepSettings\n projectDir={projectDir || process.cwd()}\n onClose={() => store.toggleSettings()}\n />\n );\n }\n return (\n <StepSources\n matrix={matrix}\n projectDir={projectDir}\n onContinue={() => {\n if (!initialAgents?.length) {\n store.preselectAgentsFromDomains();\n }\n store.setStep(\"agents\");\n }}\n onBack={store.goBack}\n />\n );\n }\n\n case \"agents\":\n return <StepAgents matrix={matrix} />;\n\n case \"confirm\": {\n const stackName = getStackName(store.selectedStackId, matrix);\n const technologyCount = store.getTechnologyCount();\n return (\n <StepConfirm\n onComplete={handleComplete}\n stackName={stackName}\n selectedDomains={store.selectedDomains}\n domainSelections={store.domainSelections}\n technologyCount={technologyCount}\n skillCount={technologyCount}\n agentCount={store.selectedAgents.length}\n installMode={store.installMode}\n onBack={store.goBack}\n />\n );\n }\n\n default:\n return null;\n }\n };\n\n if (isNarrowTerminal || isShortTerminal) {\n const issue = isNarrowTerminal\n ? `too narrow (${terminalWidth} columns, need ${MIN_TERMINAL_WIDTH})`\n : `too short (${terminalHeight} rows, need ${MIN_TERMINAL_HEIGHT})`;\n\n return (\n <ThemeProvider theme={cliTheme}>\n <Box flexDirection=\"column\" padding={1}>\n <Text color={CLI_COLORS.WARNING}>Terminal {issue}. Please resize your terminal.</Text>\n </Box>\n </ThemeProvider>\n );\n }\n\n return (\n <ThemeProvider theme={cliTheme}>\n <WizardLayout version={version} marketplaceLabel={marketplaceLabel} logo={logo}>\n {renderStep()}\n </WizardLayout>\n </ThemeProvider>\n );\n};\n","import { useRef } from \"react\";\nimport { useWizardStore, type WizardStep } from \"../../stores/wizard-store.js\";\nimport type { AgentName, Domain, MergedSkillsMatrix, SkillId } from \"../../types/index.js\";\n\ntype UseWizardInitializationOptions = {\n matrix: MergedSkillsMatrix;\n initialStep?: WizardStep;\n initialInstallMode?: \"plugin\" | \"local\";\n initialExpertMode?: boolean;\n initialDomains?: Domain[];\n initialAgents?: AgentName[];\n installedSkillIds?: SkillId[];\n};\n\n/**\n * Runs one-time wizard store initialization before the first render.\n * Populates step, approach, install mode, and skill selections from props.\n */\nexport function useWizardInitialization({\n matrix,\n initialStep,\n initialInstallMode,\n initialExpertMode,\n initialDomains,\n initialAgents,\n installedSkillIds,\n}: UseWizardInitializationOptions): void {\n const initialized = useRef(false);\n\n if (!initialized.current) {\n initialized.current = true;\n\n if (initialStep) {\n if (installedSkillIds?.length) {\n useWizardStore\n .getState()\n .populateFromSkillIds(installedSkillIds, matrix.skills, matrix.categories);\n }\n useWizardStore.setState({ step: initialStep, approach: \"scratch\" });\n }\n if (initialInstallMode) {\n useWizardStore.setState({ installMode: initialInstallMode });\n }\n if (initialExpertMode) {\n useWizardStore.setState({ expertMode: initialExpertMode });\n }\n // Restore saved domains from config, overriding the domains\n // derived by populateFromSkillIds\n if (initialDomains?.length) {\n useWizardStore.setState({ selectedDomains: initialDomains });\n }\n // Restore saved agents from config, overriding the default empty array\n if (initialAgents?.length) {\n useWizardStore.setState({ selectedAgents: initialAgents });\n }\n }\n}\n","import { useCallback } from \"react\";\nimport type { Domain, MergedSkillsMatrix, SkillId } from \"../../types/index.js\";\nimport type { WizardState } from \"../../stores/wizard-store.js\";\nimport type { StepBuildProps } from \"../wizard/step-build.js\";\n\ntype UseBuildStepPropsOptions = {\n store: WizardState;\n matrix: MergedSkillsMatrix;\n installedSkillIds?: SkillId[];\n};\n\nexport function useBuildStepProps({\n store,\n matrix,\n installedSkillIds,\n}: UseBuildStepPropsOptions): StepBuildProps {\n const currentDomain = store.getCurrentDomain();\n const defaultDomains: Domain[] = [\"web\"];\n const effectiveDomains =\n store.selectedDomains.length > 0 ? store.selectedDomains : defaultDomains;\n\n const allSelections = store.getAllSelectedTechnologies();\n\n const activeDomain: Domain = currentDomain || effectiveDomains[0] || \"web\";\n\n const onToggle = useCallback(\n (subcategoryId: Parameters<StepBuildProps[\"onToggle\"]>[0], techId: SkillId) => {\n const domain: Domain = store.getCurrentDomain() || \"web\";\n const cat = matrix.categories[subcategoryId];\n store.toggleTechnology(domain, subcategoryId, techId, cat?.exclusive ?? true);\n },\n [store, matrix],\n );\n\n const onContinue = useCallback(() => {\n if (!store.nextDomain()) {\n store.setStep(\"sources\");\n }\n }, [store]);\n\n const onBack = useCallback(() => {\n if (!store.prevDomain()) {\n store.goBack();\n }\n }, [store]);\n\n return {\n matrix,\n domain: activeDomain,\n selectedDomains: effectiveDomains,\n selections: store.domainSelections[activeDomain] || {},\n allSelections,\n showLabels: store.showLabels,\n expertMode: store.expertMode,\n installedSkillIds,\n onToggle,\n onToggleLabels: store.toggleShowLabels,\n onContinue,\n onBack,\n };\n}\n"],"mappings":"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","names":["useCallback","useCallback"]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CLI_COLORS
4
- } from "./chunk-PP7NDFFE.js";
4
+ } from "./chunk-74HSA7C4.js";
5
5
  import {
6
6
  init_esm_shims
7
7
  } from "./chunk-DHET7RCE.js";
@@ -87,4 +87,4 @@ var HelpModal = ({ currentStep }) => {
87
87
  export {
88
88
  HelpModal
89
89
  };
90
- //# sourceMappingURL=chunk-44QCEK7E.js.map
90
+ //# sourceMappingURL=chunk-D7JTL3DJ.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CLI_COLORS
4
- } from "./chunk-PP7NDFFE.js";
4
+ } from "./chunk-74HSA7C4.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-MMD26LKJ.js.map
20
+ //# sourceMappingURL=chunk-DO5OZHSS.js.map
@@ -2,25 +2,25 @@
2
2
  import {
3
3
  WIZARD_STEPS,
4
4
  WizardTabs
5
- } from "./chunk-MHET2RG2.js";
5
+ } from "./chunk-TOWP4T5L.js";
6
6
  import {
7
7
  HelpModal
8
- } from "./chunk-44QCEK7E.js";
8
+ } from "./chunk-D7JTL3DJ.js";
9
9
  import {
10
10
  useWizardStore
11
- } from "./chunk-XRA4LHTJ.js";
11
+ } from "./chunk-UK3AMBR7.js";
12
12
  import {
13
13
  CLI_COLORS,
14
14
  DEFAULT_PLUGIN_NAME
15
- } from "./chunk-PP7NDFFE.js";
15
+ } from "./chunk-74HSA7C4.js";
16
16
  import {
17
17
  init_esm_shims
18
18
  } from "./chunk-DHET7RCE.js";
19
19
 
20
20
  // src/cli/components/wizard/wizard-layout.tsx
21
21
  init_esm_shims();
22
- import { Fragment } from "react";
23
22
  import { Box, Text } from "ink";
23
+ import { Fragment } from "react";
24
24
 
25
25
  // src/cli/components/hooks/use-terminal-dimensions.ts
26
26
  init_esm_shims();
@@ -124,7 +124,7 @@ var WizardLayout = ({
124
124
  const { completedSteps, skippedSteps } = store.getStepProgress();
125
125
  const { rows: terminalHeight } = useTerminalDimensions();
126
126
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, height: terminalHeight, children: [
127
- logo && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { children: logo }) }),
127
+ logo && /* @__PURE__ */ jsx(Box, { flexDirection: "row", marginTop: 1, columnGap: 1, children: /* @__PURE__ */ jsx(Text, { children: logo }) }),
128
128
  /* @__PURE__ */ jsxs(Box, { children: [
129
129
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Marketplace: " }),
130
130
  /* @__PURE__ */ jsx(Text, { bold: true, children: marketplaceLabel || `${DEFAULT_PLUGIN_NAME} (public)` })
@@ -179,4 +179,4 @@ var WizardLayout = ({
179
179
  export {
180
180
  WizardLayout
181
181
  };
182
- //# sourceMappingURL=chunk-ORJPGZVD.js.map
182
+ //# sourceMappingURL=chunk-FKBCYT7B.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/wizard/wizard-layout.tsx","../src/cli/components/hooks/use-terminal-dimensions.ts"],"sourcesContent":["import { Box, Text } from \"ink\";\nimport React, { Fragment } from \"react\";\nimport { CLI_COLORS, DEFAULT_PLUGIN_NAME } from \"../../consts.js\";\nimport { useWizardStore } from \"../../stores/wizard-store.js\";\nimport { useTerminalDimensions } from \"../hooks/use-terminal-dimensions.js\";\nimport { HelpModal } from \"./help-modal.js\";\nimport { WIZARD_STEPS, WizardTabs } from \"./wizard-tabs.js\";\n\ntype KeyHintProps = {\n isVisible?: boolean;\n isActive?: boolean;\n label: string;\n values: string[];\n};\n\nconst DefinitionItem: React.FC<KeyHintProps> = ({\n isVisible = true,\n isActive = false,\n label,\n values,\n}) => {\n if (!isVisible) {\n return null;\n }\n\n return (\n <Text>\n {values.map((value) => (\n <Fragment key={value}>\n <Text\n backgroundColor=\"black\"\n color={isActive ? CLI_COLORS.PRIMARY : CLI_COLORS.UNFOCUSED}\n >\n {\" \"}\n {value}{\" \"}\n </Text>{\" \"}\n </Fragment>\n ))}\n <Text color={isActive ? CLI_COLORS.PRIMARY : undefined}>{label}</Text>\n </Text>\n );\n};\n\nconst HOT_KEYS: { label: string; values: string[] }[] = [\n { label: \"navigate\", values: [\"\\u2190/\\u2192\", \"\\u2191/\\u2193\"] },\n { label: \"select\", values: [\"SPACE\"] },\n { label: \"continue\", values: [\"ENTER\"] },\n { label: \"back\", values: [\"ESC\"] },\n];\n\nconst WizardFooter = () => {\n const store = useWizardStore();\n\n return (\n <Box\n columnGap={2}\n borderTop\n borderRight={false}\n borderBottom\n borderLeft={false}\n borderColor=\"blackBright\"\n borderStyle=\"single\"\n paddingLeft={1}\n paddingRight={1}\n >\n <DefinitionItem\n label=\"Accept defaults\"\n values={[\"A\"]}\n isVisible={store.step === \"build\" && !!store.selectedStackId}\n />\n {HOT_KEYS.map((hotkey) => (\n <DefinitionItem {...hotkey} key={hotkey.label} />\n ))}\n </Box>\n );\n};\n\ntype WizardLayoutProps = {\n version?: string;\n marketplaceLabel?: string;\n logo?: string;\n children: React.ReactNode;\n};\n\nexport const WizardLayout: React.FC<WizardLayoutProps> = ({\n version,\n marketplaceLabel,\n logo,\n children,\n}) => {\n const store = useWizardStore();\n const { completedSteps, skippedSteps } = store.getStepProgress();\n const { rows: terminalHeight } = useTerminalDimensions();\n\n return (\n <Box flexDirection=\"column\" paddingX={1} height={terminalHeight}>\n {logo && (\n <Box flexDirection=\"row\" marginTop={1} columnGap={1}>\n <Text>{logo}</Text>\n </Box>\n )}\n <Box>\n <Text dimColor>Marketplace: </Text>\n <Text bold>{marketplaceLabel || `${DEFAULT_PLUGIN_NAME} (public)`}</Text>\n </Box>\n <WizardTabs\n steps={WIZARD_STEPS}\n currentStep={store.step}\n completedSteps={completedSteps}\n skippedSteps={skippedSteps}\n version={version}\n />\n {store.showHelp ? (\n <HelpModal currentStep={store.step} />\n ) : (\n <>\n <Box flexDirection=\"column\" flexGrow={1} flexBasis={0} marginTop={1}>\n {children}\n </Box>\n <Box paddingX={1} columnGap={2} marginTop={2}>\n <DefinitionItem label=\"Expert mode\" values={[\"E\"]} isActive={store.expertMode} />\n <DefinitionItem\n label=\"Labels\"\n values={[\"D\"]}\n isVisible={store.step === \"build\"}\n isActive={store.showLabels}\n />\n <DefinitionItem\n label=\"Plugin mode\"\n values={[\"P\"]}\n isActive={store.installMode === \"plugin\"}\n />\n <DefinitionItem\n label=\"Settings\"\n values={[\"G\"]}\n isVisible={store.step === \"sources\"}\n isActive={store.showSettings}\n />\n <DefinitionItem label=\"Help\" values={[\"?\"]} />\n </Box>\n <WizardFooter />\n </>\n )}\n </Box>\n );\n};\n","import { useState, useEffect } from \"react\";\nimport { useStdout } from \"ink\";\n\nconst DEFAULT_COLUMNS = 80;\nconst DEFAULT_ROWS = 24;\n\nexport type TerminalDimensions = {\n /** Terminal width in columns */\n columns: number;\n /** Terminal height in rows */\n rows: number;\n};\n\n/**\n * Tracks terminal dimensions reactively. Re-renders on resize.\n *\n * Falls back to DEFAULT_COLUMNS x DEFAULT_ROWS when stdout is not a TTY\n * (e.g., piped output, CI environments, tests).\n */\nexport function useTerminalDimensions(): TerminalDimensions {\n const { stdout } = useStdout();\n\n const [dimensions, setDimensions] = useState<TerminalDimensions>(() => ({\n columns: stdout.columns || DEFAULT_COLUMNS,\n rows: stdout.rows || DEFAULT_ROWS,\n }));\n\n useEffect(() => {\n const handleResize = () => {\n setDimensions({\n columns: stdout.columns || DEFAULT_COLUMNS,\n rows: stdout.rows || DEFAULT_ROWS,\n });\n };\n\n stdout.on(\"resize\", handleResize);\n return () => {\n stdout.off(\"resize\", handleResize);\n };\n }, [stdout]);\n\n return dimensions;\n}\n"],"mappings":"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","names":["Fragment"]}