@agents-inc/cli 0.60.1 → 0.64.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 (212) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +23 -172
  3. package/dist/{chunk-G6WHCALR.js → chunk-3YNT3NX3.js} +13 -11
  4. package/dist/chunk-3YNT3NX3.js.map +1 -0
  5. package/dist/{chunk-WF6RM73R.js → chunk-4C7CSZC5.js} +27 -149
  6. package/dist/chunk-4C7CSZC5.js.map +1 -0
  7. package/dist/{chunk-EMIUPGPL.js → chunk-4KVBH2X4.js} +33 -14
  8. package/dist/chunk-4KVBH2X4.js.map +1 -0
  9. package/dist/{chunk-KIWFEBKH.js → chunk-52THXN5G.js} +14 -5
  10. package/dist/chunk-52THXN5G.js.map +1 -0
  11. package/dist/{chunk-TZXYBG3R.js → chunk-53URJ5XK.js} +448 -153
  12. package/dist/chunk-53URJ5XK.js.map +1 -0
  13. package/dist/{chunk-MKCHLXMY.js → chunk-6DEK3TDF.js} +10 -10
  14. package/dist/chunk-6DEK3TDF.js.map +1 -0
  15. package/dist/{chunk-SDKCQXWE.js → chunk-6IK2TCK7.js} +13 -6
  16. package/dist/chunk-6IK2TCK7.js.map +1 -0
  17. package/dist/chunk-6VIOO74O.js +51 -0
  18. package/dist/chunk-6VIOO74O.js.map +1 -0
  19. package/dist/{chunk-52XO4ULK.js → chunk-7DI3HGKL.js} +32 -14
  20. package/dist/chunk-7DI3HGKL.js.map +1 -0
  21. package/dist/{chunk-MGNYPVOJ.js → chunk-AQYAVLZK.js} +2 -2
  22. package/dist/{chunk-BNQ5O6LE.js → chunk-AUNBGZS4.js} +2 -2
  23. package/dist/chunk-BGPGQF35.js +248 -0
  24. package/dist/chunk-BGPGQF35.js.map +1 -0
  25. package/dist/chunk-BKL3DF2Q.js +45 -0
  26. package/dist/chunk-BKL3DF2Q.js.map +1 -0
  27. package/dist/{chunk-AX3SZZWA.js → chunk-BKTPEATV.js} +13 -6
  28. package/dist/chunk-BKTPEATV.js.map +1 -0
  29. package/dist/{chunk-H7WJK7NJ.js → chunk-CKPJTMNC.js} +13 -6
  30. package/dist/chunk-CKPJTMNC.js.map +1 -0
  31. package/dist/{chunk-MR6OBL3B.js → chunk-CXRVM7BA.js} +2 -4
  32. package/dist/chunk-CXRVM7BA.js.map +1 -0
  33. package/dist/{chunk-VR3CDXDT.js → chunk-EE5EPS32.js} +2 -2
  34. package/dist/{chunk-6OWHQ7HM.js → chunk-EGMQ3SXN.js} +2 -11
  35. package/dist/{chunk-6OWHQ7HM.js.map → chunk-EGMQ3SXN.js.map} +1 -1
  36. package/dist/{chunk-BMJZBLP7.js → chunk-EZ35IPXZ.js} +10 -7
  37. package/dist/chunk-EZ35IPXZ.js.map +1 -0
  38. package/dist/{chunk-OCEFD7V6.js → chunk-F3REOP7N.js} +3 -3
  39. package/dist/{chunk-C577AJE7.js → chunk-FGLUQSVU.js} +3 -3
  40. package/dist/{chunk-SEJF7CGJ.js → chunk-J4POGAJF.js} +24 -24
  41. package/dist/chunk-J4POGAJF.js.map +1 -0
  42. package/dist/{chunk-O6BA7Q2B.js → chunk-KFDTVSIC.js} +18 -8
  43. package/dist/chunk-KFDTVSIC.js.map +1 -0
  44. package/dist/{chunk-LWXRUR6B.js → chunk-LMZXL5RQ.js} +2 -2
  45. package/dist/{chunk-LWXRUR6B.js.map → chunk-LMZXL5RQ.js.map} +1 -1
  46. package/dist/{chunk-7FMEMXJ4.js → chunk-MOMI77PL.js} +100 -59
  47. package/dist/chunk-MOMI77PL.js.map +1 -0
  48. package/dist/{chunk-BFD5NZQ4.js → chunk-MVYJVKVT.js} +19 -11
  49. package/dist/chunk-MVYJVKVT.js.map +1 -0
  50. package/dist/{chunk-X5EG4EFP.js → chunk-MWGDG4QN.js} +2 -2
  51. package/dist/{chunk-TGLRDEEL.js → chunk-O2HK3NTG.js} +10 -6
  52. package/dist/chunk-O2HK3NTG.js.map +1 -0
  53. package/dist/{chunk-6G3KZSO4.js → chunk-OORWBS6F.js} +45 -52
  54. package/dist/chunk-OORWBS6F.js.map +1 -0
  55. package/dist/{chunk-CIG7IKX3.js → chunk-OV5UJWS5.js} +4 -4
  56. package/dist/{chunk-RO6LX3UV.js → chunk-R46CB36B.js} +5 -5
  57. package/dist/{chunk-52M2XF3W.js → chunk-RG3KDXMR.js} +16 -8
  58. package/dist/chunk-RG3KDXMR.js.map +1 -0
  59. package/dist/{chunk-MMFQNJPE.js → chunk-SXGBPQY6.js} +3 -3
  60. package/dist/chunk-SXGBPQY6.js.map +1 -0
  61. package/dist/{chunk-XUDTFI4M.js → chunk-T5DJCIUP.js} +2 -2
  62. package/dist/{chunk-AJJJE7F7.js → chunk-TQLDQ3XZ.js} +2 -2
  63. package/dist/{chunk-SZRK3VOR.js → chunk-WSMQ5GAP.js} +33 -21
  64. package/dist/chunk-WSMQ5GAP.js.map +1 -0
  65. package/dist/{chunk-WYVDNGJB.js → chunk-XMLCXRTS.js} +3 -3
  66. package/dist/{chunk-K6OLORQL.js → chunk-YEGPTBX5.js} +4 -4
  67. package/dist/{chunk-YHCYKUA3.js → chunk-ZFY5EMDV.js} +5 -5
  68. package/dist/{chunk-BKJHAJQW.js → chunk-ZYUASJUN.js} +7 -4
  69. package/dist/chunk-ZYUASJUN.js.map +1 -0
  70. package/dist/commands/build/marketplace.js +4 -4
  71. package/dist/commands/build/plugins.js +7 -6
  72. package/dist/commands/build/plugins.js.map +1 -1
  73. package/dist/commands/build/stack.js +7 -6
  74. package/dist/commands/build/stack.js.map +1 -1
  75. package/dist/commands/compile.js +79 -138
  76. package/dist/commands/compile.js.map +1 -1
  77. package/dist/commands/config/index.js +7 -6
  78. package/dist/commands/config/index.js.map +1 -1
  79. package/dist/commands/config/path.js +6 -5
  80. package/dist/commands/config/path.js.map +1 -1
  81. package/dist/commands/config/show.js +7 -6
  82. package/dist/commands/diff.js +6 -5
  83. package/dist/commands/diff.js.map +1 -1
  84. package/dist/commands/doctor.js +11 -15
  85. package/dist/commands/doctor.js.map +1 -1
  86. package/dist/commands/edit.js +63 -69
  87. package/dist/commands/edit.js.map +1 -1
  88. package/dist/commands/eject.js +13 -13
  89. package/dist/commands/eject.js.map +1 -1
  90. package/dist/commands/import/skill.js +7 -6
  91. package/dist/commands/import/skill.js.map +1 -1
  92. package/dist/commands/info.js +14 -16
  93. package/dist/commands/info.js.map +1 -1
  94. package/dist/commands/init.js +32 -30
  95. package/dist/commands/list.js +6 -5
  96. package/dist/commands/list.js.map +1 -1
  97. package/dist/commands/new/agent.js +7 -6
  98. package/dist/commands/new/agent.js.map +1 -1
  99. package/dist/commands/new/marketplace.js +28 -11
  100. package/dist/commands/new/marketplace.js.map +1 -1
  101. package/dist/commands/new/skill.js +7 -6
  102. package/dist/commands/outdated.js +6 -5
  103. package/dist/commands/outdated.js.map +1 -1
  104. package/dist/commands/search.js +13 -11
  105. package/dist/commands/search.js.map +1 -1
  106. package/dist/commands/uninstall.js +9 -10
  107. package/dist/commands/uninstall.js.map +1 -1
  108. package/dist/commands/update.js +12 -8
  109. package/dist/commands/update.js.map +1 -1
  110. package/dist/commands/validate.js +20 -42
  111. package/dist/commands/validate.js.map +1 -1
  112. package/dist/components/skill-search/skill-search.js +4 -3
  113. package/dist/components/wizard/category-grid.js +5 -3
  114. package/dist/components/wizard/category-grid.test.js +242 -194
  115. package/dist/components/wizard/category-grid.test.js.map +1 -1
  116. package/dist/components/wizard/checkbox-grid.js +5 -5
  117. package/dist/components/wizard/checkbox-grid.test.js +5 -5
  118. package/dist/components/wizard/domain-selection.js +12 -11
  119. package/dist/components/wizard/help-modal.js +3 -2
  120. package/dist/components/wizard/menu-item.js +1 -1
  121. package/dist/components/wizard/search-modal.js +3 -2
  122. package/dist/components/wizard/search-modal.test.js +3 -2
  123. package/dist/components/wizard/search-modal.test.js.map +1 -1
  124. package/dist/components/wizard/section-progress.js +2 -2
  125. package/dist/components/wizard/section-progress.test.js +3 -3
  126. package/dist/components/wizard/section-progress.test.js.map +1 -1
  127. package/dist/components/wizard/selection-card.js +2 -2
  128. package/dist/components/wizard/source-grid.js +6 -4
  129. package/dist/components/wizard/source-grid.test.js +65 -40
  130. package/dist/components/wizard/source-grid.test.js.map +1 -1
  131. package/dist/components/wizard/stack-selection.js +9 -8
  132. package/dist/components/wizard/step-agents.js +11 -10
  133. package/dist/components/wizard/step-agents.test.js +28 -25
  134. package/dist/components/wizard/step-agents.test.js.map +1 -1
  135. package/dist/components/wizard/step-build.js +12 -10
  136. package/dist/components/wizard/step-build.test.js +28 -34
  137. package/dist/components/wizard/step-build.test.js.map +1 -1
  138. package/dist/components/wizard/step-confirm.js +6 -4
  139. package/dist/components/wizard/step-confirm.test.js +11 -15
  140. package/dist/components/wizard/step-confirm.test.js.map +1 -1
  141. package/dist/components/wizard/step-refine.js +3 -2
  142. package/dist/components/wizard/step-refine.test.js +3 -2
  143. package/dist/components/wizard/step-refine.test.js.map +1 -1
  144. package/dist/components/wizard/step-settings.js +10 -8
  145. package/dist/components/wizard/step-settings.test.js +17 -13
  146. package/dist/components/wizard/step-settings.test.js.map +1 -1
  147. package/dist/components/wizard/step-sources.js +13 -11
  148. package/dist/components/wizard/step-sources.test.js +17 -14
  149. package/dist/components/wizard/step-sources.test.js.map +1 -1
  150. package/dist/components/wizard/step-stack.js +15 -14
  151. package/dist/components/wizard/step-stack.test.js +42 -38
  152. package/dist/components/wizard/step-stack.test.js.map +1 -1
  153. package/dist/components/wizard/view-title.js +2 -2
  154. package/dist/components/wizard/wizard-layout.js +10 -8
  155. package/dist/components/wizard/wizard-tabs.js +2 -2
  156. package/dist/components/wizard/wizard-tabs.test.js +2 -2
  157. package/dist/components/wizard/wizard.js +29 -27
  158. package/dist/hooks/init.js +32 -30
  159. package/dist/hooks/init.js.map +1 -1
  160. package/dist/{loader-2O32KKAQ.js → loader-4YOZCFIP.js} +4 -4
  161. package/dist/plugins/dummy-skill/.claude-plugin/.content-hash +1 -0
  162. package/dist/plugins/dummy-skill/.claude-plugin/plugin.json +13 -0
  163. package/dist/{source-loader-A6B3NDI4.js → source-loader-O5RMYUBW.js} +6 -5
  164. package/dist/{source-manager-Q7IQSGIX.js → source-manager-NKLL6HCL.js} +6 -5
  165. package/dist/stores/matrix-store.js +15 -0
  166. package/dist/stores/matrix-store.js.map +1 -0
  167. package/dist/stores/matrix-store.test.js +146 -0
  168. package/dist/stores/matrix-store.test.js.map +1 -0
  169. package/dist/stores/wizard-store.js +6 -5
  170. package/dist/stores/wizard-store.test.js +159 -107
  171. package/dist/stores/wizard-store.test.js.map +1 -1
  172. package/package.json +1 -1
  173. package/dist/chunk-52M2XF3W.js.map +0 -1
  174. package/dist/chunk-52XO4ULK.js.map +0 -1
  175. package/dist/chunk-6G3KZSO4.js.map +0 -1
  176. package/dist/chunk-7FMEMXJ4.js.map +0 -1
  177. package/dist/chunk-AX3SZZWA.js.map +0 -1
  178. package/dist/chunk-BFD5NZQ4.js.map +0 -1
  179. package/dist/chunk-BKJHAJQW.js.map +0 -1
  180. package/dist/chunk-BMJZBLP7.js.map +0 -1
  181. package/dist/chunk-EMIUPGPL.js.map +0 -1
  182. package/dist/chunk-G6WHCALR.js.map +0 -1
  183. package/dist/chunk-H7WJK7NJ.js.map +0 -1
  184. package/dist/chunk-KIWFEBKH.js.map +0 -1
  185. package/dist/chunk-MKCHLXMY.js.map +0 -1
  186. package/dist/chunk-MMFQNJPE.js.map +0 -1
  187. package/dist/chunk-MR6OBL3B.js.map +0 -1
  188. package/dist/chunk-O6BA7Q2B.js.map +0 -1
  189. package/dist/chunk-SDKCQXWE.js.map +0 -1
  190. package/dist/chunk-SEJF7CGJ.js.map +0 -1
  191. package/dist/chunk-SZRK3VOR.js.map +0 -1
  192. package/dist/chunk-TC3NHO34.js +0 -151
  193. package/dist/chunk-TC3NHO34.js.map +0 -1
  194. package/dist/chunk-TGLRDEEL.js.map +0 -1
  195. package/dist/chunk-TZXYBG3R.js.map +0 -1
  196. package/dist/chunk-WF6RM73R.js.map +0 -1
  197. /package/dist/{chunk-MGNYPVOJ.js.map → chunk-AQYAVLZK.js.map} +0 -0
  198. /package/dist/{chunk-BNQ5O6LE.js.map → chunk-AUNBGZS4.js.map} +0 -0
  199. /package/dist/{chunk-VR3CDXDT.js.map → chunk-EE5EPS32.js.map} +0 -0
  200. /package/dist/{chunk-OCEFD7V6.js.map → chunk-F3REOP7N.js.map} +0 -0
  201. /package/dist/{chunk-C577AJE7.js.map → chunk-FGLUQSVU.js.map} +0 -0
  202. /package/dist/{chunk-X5EG4EFP.js.map → chunk-MWGDG4QN.js.map} +0 -0
  203. /package/dist/{chunk-CIG7IKX3.js.map → chunk-OV5UJWS5.js.map} +0 -0
  204. /package/dist/{chunk-RO6LX3UV.js.map → chunk-R46CB36B.js.map} +0 -0
  205. /package/dist/{chunk-XUDTFI4M.js.map → chunk-T5DJCIUP.js.map} +0 -0
  206. /package/dist/{chunk-AJJJE7F7.js.map → chunk-TQLDQ3XZ.js.map} +0 -0
  207. /package/dist/{chunk-WYVDNGJB.js.map → chunk-XMLCXRTS.js.map} +0 -0
  208. /package/dist/{chunk-K6OLORQL.js.map → chunk-YEGPTBX5.js.map} +0 -0
  209. /package/dist/{chunk-YHCYKUA3.js.map → chunk-ZFY5EMDV.js.map} +0 -0
  210. /package/dist/{loader-2O32KKAQ.js.map → loader-4YOZCFIP.js.map} +0 -0
  211. /package/dist/{source-loader-A6B3NDI4.js.map → source-loader-O5RMYUBW.js.map} +0 -0
  212. /package/dist/{source-manager-Q7IQSGIX.js.map → source-manager-NKLL6HCL.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/lib/__tests__/test-fixtures.ts","../src/cli/lib/__tests__/helpers.ts"],"sourcesContent":["import type { MergedSkillsMatrix, ResolvedSkill, SkillSlug } from \"../../types\";\nimport { createMockSkill, createMockCategory, createMockMatrix } from \"./helpers\";\n\nconst SKILL_FIXTURES = {\n react: createMockSkill(\"web-framework-react\", {\n description: \"React framework for building user interfaces\",\n tags: [\"react\", \"web\", \"ui\", \"component\"],\n }),\n zustand: createMockSkill(\"web-state-zustand\", {\n description: \"Bear necessities state management\",\n tags: [\"state\", \"react\", \"zustand\"],\n }),\n hono: createMockSkill(\"api-framework-hono\", {\n description: \"Lightweight web framework for the edge\",\n tags: [\"api\", \"api\", \"edge\", \"serverless\"],\n }),\n vitest: createMockSkill(\"web-testing-vitest\", {\n description: \"Next generation testing framework\",\n tags: [\"testing\", \"vitest\", \"unit\"],\n }),\n vue: createMockSkill(\"web-framework-vue\", {\n description: \"Progressive JavaScript framework\",\n tags: [\"vue\", \"web\", \"reactive\"],\n }),\n \"auth-patterns\": createMockSkill(\"api-security-auth-patterns\", {\n description: \"Authentication and authorization patterns\",\n tags: [\"auth\", \"security\", \"jwt\", \"oauth\"],\n }),\n drizzle: createMockSkill(\"api-database-drizzle\", {\n description: \"TypeScript ORM for SQL databases\",\n tags: [\"database\", \"orm\", \"sql\"],\n }),\n \"anti-over-engineering\": createMockSkill(\"meta-methodology-anti-over-engineering\", {\n description: \"Surgical implementation, not architectural innovation\",\n tags: [\"methodology\", \"foundational\"],\n }),\n \"scss-modules\": createMockSkill(\"web-styling-scss-modules\", {\n description: \"CSS Modules with SCSS\",\n tags: [\"css\", \"scss\", \"modules\"],\n }),\n} satisfies Partial<Record<SkillSlug, ResolvedSkill>>;\n\nexport type TestSkillName = keyof typeof SKILL_FIXTURES;\n\nexport function getTestSkill(\n name: TestSkillName,\n overrides?: Partial<ResolvedSkill>,\n): ResolvedSkill {\n return { ...SKILL_FIXTURES[name], ...overrides };\n}\n\n// ---------------------------------------------------------------------------\n// Shared base skill fixtures — canonical defaults with no overrides.\n// Use spread for per-test customization: `{ ...TEST_SKILLS.react, slug: \"react\" }`\n// ---------------------------------------------------------------------------\n\nexport const TEST_SKILLS = {\n react: createMockSkill(\"web-framework-react\"),\n vue: createMockSkill(\"web-framework-vue\"),\n zustand: createMockSkill(\"web-state-zustand\", {\n compatibleWith: [\"web-framework-react\"],\n }),\n pinia: createMockSkill(\"web-state-pinia\", {\n compatibleWith: [\"web-framework-vue\"],\n }),\n hono: createMockSkill(\"api-framework-hono\"),\n vitest: createMockSkill(\"web-testing-vitest\"),\n \"scss-modules\": createMockSkill(\"web-styling-scss-modules\"),\n drizzle: createMockSkill(\"api-database-drizzle\"),\n // Methodology skills (DEFAULT_PRESELECTED_SKILLS) — used by createComprehensiveMatrix\n \"investigation-requirements\": createMockSkill(\"meta-methodology-investigation-requirements\", {\n description: \"Never speculate - read actual code first\",\n }),\n \"anti-over-engineering\": createMockSkill(\"meta-methodology-anti-over-engineering\", {\n description: \"Surgical implementation, not architectural innovation\",\n }),\n \"success-criteria\": createMockSkill(\"meta-methodology-success-criteria\", {\n description: \"Explicit, measurable criteria defining done\",\n }),\n \"write-verification\": createMockSkill(\"meta-methodology-write-verification\", {\n description: \"Verify work was actually saved\",\n }),\n \"improvement-protocol\": createMockSkill(\"meta-methodology-improvement-protocol\", {\n description: \"Evidence-based self-improvement\",\n }),\n \"context-management\": createMockSkill(\"meta-methodology-context-management\", {\n description: \"Maintain project continuity across sessions\",\n }),\n} satisfies Partial<Record<SkillSlug, ResolvedSkill>>;\n\n// ---------------------------------------------------------------------------\n// Shared base category fixtures — canonical defaults with no overrides.\n// Use spread for per-test customization: `{ ...TEST_CATEGORIES.framework, required: true }`\n// ---------------------------------------------------------------------------\n\nexport const TEST_CATEGORIES = {\n framework: createMockCategory(\"web-framework\", \"Framework\"),\n clientState: createMockCategory(\"web-client-state\", \"Client State\"),\n styling: createMockCategory(\"web-styling\", \"Styling\"),\n testing: createMockCategory(\"web-testing\", \"Testing\"),\n api: createMockCategory(\"api-api\", \"Backend Framework\"),\n database: createMockCategory(\"api-database\", \"Database\"),\n methodology: createMockCategory(\"shared-methodology\", \"Methodology\"),\n tooling: createMockCategory(\"shared-tooling\", \"Tooling\"),\n};\n\n// ---------------------------------------------------------------------------\n// Shared matrix fixtures — common skill combinations used across test files.\n// Use createMockMatrix overrides for per-test customization:\n// `createMockMatrix(TEST_MATRICES.react.skills, { suggestedStacks: [...] })`\n// ---------------------------------------------------------------------------\n\nexport const TEST_MATRICES: Record<string, MergedSkillsMatrix> = {\n empty: createMockMatrix({}),\n react: createMockMatrix({\n \"web-framework-react\": TEST_SKILLS.react,\n }),\n reactAndZustand: createMockMatrix({\n \"web-framework-react\": TEST_SKILLS.react,\n \"web-state-zustand\": TEST_SKILLS.zustand,\n }),\n reactAndHono: createMockMatrix({\n \"web-framework-react\": TEST_SKILLS.react,\n \"api-framework-hono\": TEST_SKILLS.hono,\n }),\n reactAndScss: createMockMatrix({\n \"web-framework-react\": TEST_SKILLS.react,\n \"web-styling-scss-modules\": TEST_SKILLS[\"scss-modules\"],\n }),\n reactScssAndHono: createMockMatrix({\n \"web-framework-react\": TEST_SKILLS.react,\n \"web-styling-scss-modules\": TEST_SKILLS[\"scss-modules\"],\n \"api-framework-hono\": TEST_SKILLS.hono,\n }),\n reactZustandAndHono: createMockMatrix({\n \"web-framework-react\": TEST_SKILLS.react,\n \"web-state-zustand\": TEST_SKILLS.zustand,\n \"api-framework-hono\": TEST_SKILLS.hono,\n }),\n};\n","import path from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { mkdir, writeFile, readFile } from \"fs/promises\";\nimport { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport { run, Errors } from \"@oclif/core\";\nimport ansis from \"ansis\";\nimport { createJiti } from \"jiti\";\nimport {\n CLAUDE_SRC_DIR,\n DEFAULT_BRANDING,\n DEFAULT_PLUGIN_NAME,\n STANDARD_FILES,\n} from \"../../consts\";\nimport { findSkill, useMatrixStore } from \"../../stores/matrix-store\";\nimport { typedEntries } from \"../../utils/typed-object\";\nimport { computeSkillFolderHash } from \"../versioning\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nexport const CLI_ROOT = path.resolve(__dirname, \"../../../..\");\n\n/** Resolve @agents-inc/cli/config to the source config-exports.ts so jiti can load it in dev. */\nconst CONFIG_EXPORTS_PATH = path.resolve(__dirname, \"../../config-exports.ts\");\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 AgentName,\n AgentScopeConfig,\n CategoryDefinition,\n CategoryPath,\n CompiledAgentData,\n CompileAgentConfig,\n CompileConfig,\n CompileContext,\n Domain,\n DomainSelections,\n ExtractedSkillMetadata,\n Marketplace,\n MarketplacePlugin,\n MergedSkillsMatrix,\n ProjectConfig,\n ResolvedSkill,\n ResolvedStack,\n Skill,\n SkillAssignment,\n SkillConfig,\n SkillDefinition,\n SkillSlug,\n SkillSlugMap,\n SkillId,\n SkillSource,\n SkillSourceType,\n RelationshipDefinitions,\n RawStacksConfig,\n Stack,\n StackAgentConfig,\n Category,\n} from \"../../types\";\nimport type { CompiledStackPlugin } from \"../stacks/stack-plugin-compiler\";\nimport type { WizardResultV2 } from \"../../components/wizard/wizard\";\nimport type { SourceLoadResult } from \"../loading/source-loader\";\nimport type { ResolvedConfig } from \"../configuration/config\";\nimport { useWizardStore } from \"../../stores/wizard-store\";\nimport { resolveAlias, validateSelection } from \"../matrix\";\nimport type { TestProjectConfig, TestSkill } from \"./fixtures/create-test-source\";\nimport { getTestSkill, TEST_SKILLS, TEST_CATEGORIES } from \"./test-fixtures\";\n\nexport { fileExists, directoryExists } from \"./test-fs-utils\";\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\n/**\n * Load a config file using jiti. Handles defineConfig(), satisfies, and plain exports.\n */\nexport async function readTestTsConfig<T>(filePath: string): Promise<T> {\n const jiti = createJiti(import.meta.url, {\n moduleCache: false,\n interopDefault: true,\n alias: { \"@agents-inc/cli/config\": CONFIG_EXPORTS_PATH },\n });\n // Boundary cast: jiti returns unknown, caller provides expected type\n const result = await jiti.import(filePath, { default: true });\n return result as T;\n}\n\n/** Writes a config file with the given object into the given subdirectory (defaults to CLAUDE_SRC_DIR) */\nexport async function writeTestTsConfig(\n projectDir: string,\n config: Record<string, unknown>,\n configSubdir: string = CLAUDE_SRC_DIR,\n): Promise<void> {\n const configDir = path.join(projectDir, configSubdir);\n await mkdir(configDir, { recursive: true });\n const content = `export default ${JSON.stringify(config, null, 2)};`;\n await writeFile(path.join(configDir, STANDARD_FILES.CONFIG_TS), content);\n}\n\nexport function buildProjectConfig(overrides?: Partial<ProjectConfig>): ProjectConfig {\n return {\n name: \"test-project\",\n agents: [{ name: \"web-developer\", scope: \"project\" }],\n skills: buildSkillConfigs([\"web-framework-react\"]),\n ...overrides,\n };\n}\n\nexport function buildWizardResult(\n skills: SkillConfig[],\n overrides?: Partial<WizardResultV2>,\n): WizardResultV2 {\n return {\n skills,\n selectedAgents: [],\n agentConfigs: [],\n selectedStackId: null,\n domainSelections: {} as DomainSelections,\n selectedDomains: [],\n cancelled: false,\n validation: { valid: true, errors: [], warnings: [] },\n ...overrides,\n };\n}\n\n/** Build a SkillConfig array from skill IDs with default scope and source */\nexport function buildSkillConfigs(\n skillIds: SkillId[],\n overrides?: Partial<Omit<SkillConfig, \"id\">>,\n): SkillConfig[] {\n return skillIds.map((id) => ({\n id,\n scope: overrides?.scope ?? \"project\",\n source: overrides?.source ?? \"local\",\n }));\n}\n\nexport function buildAgentConfigs(\n agentNames: AgentName[],\n overrides?: Partial<Omit<AgentScopeConfig, \"name\">>,\n): AgentScopeConfig[] {\n return agentNames.map((name) => ({\n name,\n scope: overrides?.scope ?? \"project\",\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\nimport { createTempDir, cleanupTempDir } from \"./test-fs-utils\";\nexport { createTempDir, cleanupTempDir };\n\nexport interface PluginTestDirs {\n tempDir: string;\n projectDir: string;\n pluginDir: string;\n skillsDir: string;\n agentsDir: string;\n}\n\nexport async function createTestDirs(prefix = \"ai-test-\"): Promise<PluginTestDirs> {\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: PluginTestDirs): Promise<void> {\n await cleanupTempDir(dirs.tempDir);\n}\n\n/**\n * Canonical category for known test skills.\n * createMockSkill() looks up from here when no category override is provided.\n * Custom/novel skills must pass { category } in overrides.\n *\n * Uses a lazy singleton to avoid circular initialization issues:\n * test-fixtures.ts calls createMockSkill() at module level during import,\n * and ESM hoists all imports before evaluating any `const` declarations.\n */\n// eslint-disable-next-line no-var -- `var` avoids TDZ in circular ESM imports (let/const would throw)\nvar _canonicalSkillCategories: Partial<Record<SkillId, CategoryPath>> | undefined;\nfunction getCanonicalSkillCategories(): Partial<Record<SkillId, CategoryPath>> {\n if (!_canonicalSkillCategories) {\n _canonicalSkillCategories = {\n \"web-framework-react\": \"web-framework\",\n \"web-framework-vue\": \"web-framework\",\n \"web-framework-original\": \"web-framework\",\n \"web-framework-simple\": \"web-framework\",\n \"web-framework-arbitrary\": \"web-framework\",\n \"web-framework-unknown\": \"web-framework\",\n \"web-styling-tailwind\": \"web-styling\",\n \"web-styling-scss-modules\": \"web-styling\",\n \"web-styling-custom\": \"web-styling\",\n \"web-state-zustand\": \"web-client-state\",\n \"web-state-pinia\": \"web-client-state\",\n \"web-state-mobx\": \"web-client-state\",\n \"web-testing-vitest\": \"web-testing\",\n \"web-testing-copier\": \"web-testing\",\n \"web-testing-metadata\": \"web-testing\",\n \"web-testing-playwright\": \"web-testing\",\n \"web-testing-cypress-e2e\": \"web-testing\",\n \"web-data-fetching-react-query\": \"web-server-state\",\n \"web-tooling-vite\": \"shared-tooling\",\n \"web-tooling-acme\": \"web-tooling\",\n \"web-tooling-custom\": \"web-tooling\",\n \"web-tooling-nometadata\": \"web-tooling\",\n \"web-tooling-personal\": \"web-tooling\",\n \"web-skill-a\": \"web-framework\",\n \"web-skill-a-v\": \"web-framework\",\n \"web-skill-b\": \"web-framework\",\n \"web-skill-b-v\": \"web-framework\",\n \"web-skill-c\": \"web-framework\",\n \"web-skill-d\": \"web-framework\",\n \"web-skill-setup\": \"web-framework\",\n \"web-skill-usage\": \"web-framework\",\n \"web-local-skill\": \"local\",\n \"api-framework-hono\": \"api-api\",\n \"api-framework-express\": \"api-api\",\n \"api-database-drizzle\": \"api-database\",\n \"api-database-postgres\": \"api-database\",\n \"api-security-auth-patterns\": \"api-security\",\n \"cli-cli-framework-commander\": \"cli-framework\",\n \"infra-setup-env\": \"shared-tooling\",\n \"infra-tooling-linter\": \"unmapped-category\" as CategoryPath,\n \"meta-methodology-success-criteria\": \"shared-methodology\",\n \"meta-methodology-investigation-requirements\": \"shared-methodology\",\n \"meta-methodology-anti-over-engineering\": \"shared-methodology\",\n \"meta-methodology-write-verification\": \"shared-methodology\",\n \"meta-methodology-improvement-protocol\": \"shared-methodology\",\n \"meta-methodology-context-management\": \"shared-methodology\",\n \"meta-company-patterns\": \"local\",\n };\n }\n return _canonicalSkillCategories;\n}\n\nexport function createMockSkill(id: SkillId, overrides?: Partial<ResolvedSkill>): ResolvedSkill {\n const category = overrides?.category ?? getCanonicalSkillCategories()[id];\n\n if (!category) {\n throw new Error(\n `createMockSkill: \"${id}\" not in canonical registry — provide { category } in overrides`,\n );\n }\n\n // Derive slug from skill ID: strip domain-category prefix to get the last segment(s)\n // e.g., \"web-framework-react\" -> \"react\", \"meta-methodology-anti-over-engineering\" -> \"anti-over-engineering\"\n const segments = id.split(\"-\");\n const defaultSlug = (segments.length >= 3 ? segments.slice(2).join(\"-\") : id) as SkillSlug;\n\n // Derive display name from slug: title-case each segment\n const defaultDisplayName = defaultSlug\n .split(\"-\")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \");\n\n return {\n id,\n slug: defaultSlug,\n displayName: defaultDisplayName,\n description: `${id} skill`,\n category,\n tags: [],\n author: \"@test\",\n conflictsWith: [],\n isRecommended: false,\n requires: [],\n alternatives: [],\n discourages: [],\n compatibleWith: [],\n requiresSetup: [],\n providesSetupFor: [],\n path: `skills/${category}/${id}/`,\n ...overrides,\n };\n}\n\nexport function createMockSkillSource(\n type: SkillSourceType,\n overrides?: Partial<SkillSource>,\n): SkillSource {\n const defaults: Record<SkillSourceType, SkillSource> = {\n public: { name: \"public\", type: \"public\", installed: false },\n private: {\n name: \"private-source\",\n type: \"private\",\n url: \"github:org/skills\",\n installed: false,\n },\n local: { name: \"local\", type: \"local\", installed: true, installMode: \"local\" },\n };\n return { ...defaults[type], ...overrides };\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-category-name\"\n const segments = id.split(\"-\");\n const domain = segments[0] ?? \"web\";\n const category = segments[1] ?? \"framework\";\n const name = segments.slice(2).join(\"-\") || \"skill\";\n const directoryPath = `${domain}/${category}/${name}`;\n\n return {\n id,\n directoryPath,\n description: `${id} skill`,\n category: `${domain}-${category}` as CategoryPath,\n author: \"@test\",\n tags: [],\n path: `skills/${directoryPath}/`,\n domain: domain as Domain,\n displayName: name,\n slug: name as SkillSlug,\n ...overrides,\n };\n}\n\nexport function createMockMatrix(\n skills: Record<string, ResolvedSkill>,\n overrides?: Partial<MergedSkillsMatrix>,\n): MergedSkillsMatrix {\n // Boundary cast: empty objects are populated in the loop below\n const autoSlugToId = {} as Record<SkillSlug, SkillId>;\n const autoIdToSlug = {} as Record<SkillId, SkillSlug>;\n for (const [id, skill] of typedEntries(skills)) {\n if (skill.slug) {\n autoSlugToId[skill.slug] = id as SkillId;\n autoIdToSlug[id as SkillId] = skill.slug;\n }\n }\n\n return {\n version: \"1.0.0\",\n categories: {} as Record<Category, import(\"../../types\").CategoryDefinition>,\n skills,\n suggestedStacks: [],\n slugMap: { slugToId: autoSlugToId, idToSlug: autoIdToSlug },\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\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 skillId: SkillId,\n options?: {\n /** Extra fields to merge into metadata.yaml (e.g., forkedFrom, displayName) */\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 skill = findSkill(skillId);\n\n if (!options?.skipMetadata && !skill) {\n throw new Error(\n `writeTestSkill: \"${skillId}\" not found in matrix store — populate the store in beforeEach`,\n );\n }\n\n const skillDir = path.join(skillsDir, skillId);\n await mkdir(skillDir, { recursive: true });\n\n await writeFile(\n path.join(skillDir, STANDARD_FILES.SKILL_MD),\n options?.skillContent ?? createSkillContent(skillId, skill?.description),\n );\n\n if (!options?.skipMetadata && skill) {\n const { slug, category, author } = skill;\n const domain = category.split(\"-\")[0];\n\n const contentHash = await computeSkillFolderHash(skillDir);\n const baseMetadata = {\n author,\n category,\n domain,\n slug,\n contentHash,\n };\n if (options?.extraMetadata) {\n await writeFile(\n path.join(skillDir, STANDARD_FILES.METADATA_YAML),\n stringifyYaml({ ...baseMetadata, ...options.extraMetadata }),\n );\n } else {\n await writeFile(\n path.join(skillDir, STANDARD_FILES.METADATA_YAML),\n stringifyYaml(baseMetadata),\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>/<category>/<name>/`).\n */\nexport async function writeSourceSkill(\n skillsDir: string,\n directoryPath: string,\n config: TestSkill,\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 domain = config.domain;\n const slug = config.slug;\n const metadata: Record<string, unknown> = {\n displayName: config.id,\n slug,\n category: config.category,\n domain,\n author: config.author ?? \"@test\",\n };\n if (config.tags) {\n metadata.tags = config.tags;\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_METADATA_YAML),\n createAgentYamlContent(agentName, options?.description),\n );\n\n return agentDir;\n}\n\nexport function createMockCategory(\n id: Category,\n displayName: string,\n overrides?: Partial<CategoryDefinition>,\n): CategoryDefinition {\n return {\n id,\n displayName,\n description: `${displayName} category`,\n domain: \"web\",\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 skills: {},\n allSkillIds: [],\n philosophy: \"\",\n ...overrides,\n };\n}\n\n/**\n * Builds a comprehensive test matrix with 13 skills across 7 categories,\n * 2 suggested stacks, display name mappings, and relationship data\n * (conflicts, recommends). Includes all 6 DEFAULT_PRESELECTED_SKILLS\n * (methodology) so wizard handleComplete can resolve them.\n * @returns A fully populated MergedSkillsMatrix with realistic test data\n */\nexport function createComprehensiveMatrix(\n overrides?: Partial<MergedSkillsMatrix>,\n): MergedSkillsMatrix {\n // Skill categories use domain-prefixed Category IDs (matching production\n // metadata.yaml and the categories map keys, e.g., \"web-framework\", \"api-api\").\n const skills = {\n \"web-framework-react\": getTestSkill(\"react\", { category: \"web-framework\" }),\n \"web-framework-vue\": getTestSkill(\"vue\", {\n category: \"web-framework\",\n conflictsWith: [{ skillId: \"web-framework-react\", reason: \"Choose one framework\" }],\n }),\n \"web-state-zustand\": getTestSkill(\"zustand\", {\n category: \"web-client-state\",\n }),\n \"web-styling-scss-modules\": getTestSkill(\"scss-modules\", { category: \"web-styling\" }),\n \"api-framework-hono\": getTestSkill(\"hono\", { category: \"api-api\" }),\n \"api-database-drizzle\": getTestSkill(\"drizzle\", { category: \"api-database\" }),\n \"web-testing-vitest\": getTestSkill(\"vitest\", { category: \"web-testing\" }),\n // Methodology skills (DEFAULT_PRESELECTED_SKILLS) — auto-injected by wizard\n \"meta-methodology-investigation-requirements\": TEST_SKILLS[\"investigation-requirements\"],\n \"meta-methodology-anti-over-engineering\": TEST_SKILLS[\"anti-over-engineering\"],\n \"meta-methodology-success-criteria\": TEST_SKILLS[\"success-criteria\"],\n \"meta-methodology-write-verification\": TEST_SKILLS[\"write-verification\"],\n \"meta-methodology-improvement-protocol\": TEST_SKILLS[\"improvement-protocol\"],\n \"meta-methodology-context-management\": TEST_SKILLS[\"context-management\"],\n };\n\n const categories = {\n \"web-framework\": {\n ...TEST_CATEGORIES.framework,\n domain: \"web\",\n exclusive: true,\n required: true,\n },\n \"web-client-state\": { ...TEST_CATEGORIES.clientState, domain: \"web\", order: 1 },\n \"web-styling\": { ...TEST_CATEGORIES.styling, domain: \"web\", order: 2 },\n \"api-api\": { ...TEST_CATEGORIES.api, domain: \"api\", exclusive: true, required: true },\n \"api-database\": { ...TEST_CATEGORIES.database, domain: \"api\", order: 1 },\n \"web-testing\": {\n ...TEST_CATEGORIES.testing,\n domain: \"shared\",\n exclusive: false,\n order: 10,\n },\n \"shared-methodology\": {\n ...TEST_CATEGORIES.methodology,\n domain: \"shared\",\n exclusive: false,\n required: false,\n order: 11,\n },\n } as Record<Category, CategoryDefinition>;\n\n const suggestedStacks: ResolvedStack[] = [\n createMockResolvedStack(\"nextjs-fullstack\", \"Next.js Fullstack\", {\n description: \"Complete Next.js stack with React and Hono\",\n skills: {\n \"web-developer\": {\n \"web-framework\": [\"web-framework-react\"],\n \"web-client-state\": [\"web-state-zustand\"],\n \"web-styling\": [\"web-styling-scss-modules\"],\n },\n \"api-developer\": {\n \"api-api\": [\"api-framework-hono\"],\n \"api-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 skills: {\n \"web-developer\": {\n \"web-framework\": [\"web-framework-vue\"],\n },\n } as ResolvedStack[\"skills\"],\n allSkillIds: [\"web-framework-vue\"],\n philosophy: \"Progressive framework approach\",\n }),\n ];\n\n // Boundary cast: test matrix only contains a subset of all possible slugs\n const slugToId = {\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 \"investigation-requirements\": \"meta-methodology-investigation-requirements\",\n \"anti-over-engineering\": \"meta-methodology-anti-over-engineering\",\n \"success-criteria\": \"meta-methodology-success-criteria\",\n \"write-verification\": \"meta-methodology-write-verification\",\n \"improvement-protocol\": \"meta-methodology-improvement-protocol\",\n \"context-management\": \"meta-methodology-context-management\",\n } as unknown as Record<SkillSlug, SkillId>;\n\n // Boundary cast: Object.fromEntries returns { [k: string]: string }\n const idToSlug = Object.fromEntries(\n typedEntries(slugToId).map(([slug, fullId]) => [fullId, slug]),\n ) as SkillSlugMap[\"idToSlug\"];\n\n return createMockMatrix(skills, {\n categories,\n suggestedStacks,\n slugMap: { slugToId, idToSlug },\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 // Domain-prefixed Category IDs — see createComprehensiveMatrix comment\n const skills = {\n \"web-framework-react\": getTestSkill(\"react\", { category: \"web-framework\" }),\n \"web-state-zustand\": getTestSkill(\"zustand\", { category: \"web-client-state\" }),\n \"api-framework-hono\": getTestSkill(\"hono\", { category: \"api-api\" }),\n \"web-testing-vitest\": getTestSkill(\"vitest\", { category: \"web-testing\" }),\n // Methodology skills (DEFAULT_PRESELECTED_SKILLS) — auto-injected by wizard\n \"meta-methodology-anti-over-engineering\": TEST_SKILLS[\"anti-over-engineering\"],\n \"meta-methodology-context-management\": TEST_SKILLS[\"context-management\"],\n \"meta-methodology-improvement-protocol\": TEST_SKILLS[\"improvement-protocol\"],\n \"meta-methodology-investigation-requirements\": TEST_SKILLS[\"investigation-requirements\"],\n \"meta-methodology-success-criteria\": TEST_SKILLS[\"success-criteria\"],\n \"meta-methodology-write-verification\": TEST_SKILLS[\"write-verification\"],\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 \"web-framework\": {\n ...TEST_CATEGORIES.framework,\n domain: \"web\",\n exclusive: true,\n required: true,\n },\n \"web-client-state\": { ...TEST_CATEGORIES.clientState, domain: \"web\", order: 1 },\n \"api-api\": {\n ...TEST_CATEGORIES.api,\n domain: \"api\",\n exclusive: true,\n required: true,\n },\n \"web-testing\": {\n ...TEST_CATEGORIES.testing,\n displayName: \"Testing Framework\",\n domain: \"shared\",\n exclusive: false,\n },\n \"shared-methodology\": {\n ...TEST_CATEGORIES.methodology,\n domain: \"shared\",\n exclusive: false,\n required: false,\n },\n } as Record<Category, CategoryDefinition>,\n ...overrides,\n });\n}\n\n/**\n * Replicates `handleComplete` from wizard.tsx for the \"customize\" path.\n *\n * Given the wizard store state (after simulated user selections), this\n * builds the same WizardResultV2 that the real wizard produces:\n * 1. Collects all selected technologies from domainSelections\n * 2. Resolves aliases to canonical skill IDs\n * 3. Runs validation\n */\nexport function buildWizardResultFromStore(\n matrix: MergedSkillsMatrix,\n overrides?: Partial<WizardResultV2>,\n): WizardResultV2 {\n const store = useWizardStore.getState();\n\n let allSkills: SkillId[];\n\n if (store.selectedStackId && store.stackAction === \"defaults\") {\n const stack = matrix.suggestedStacks.find((s) => s.id === store.selectedStackId);\n allSkills = [...(stack?.allSkillIds || [])];\n } else {\n const techNames = store.getAllSelectedTechnologies();\n allSkills = techNames.map((tech) => resolveAlias(tech));\n }\n\n const validation = validateSelection(allSkills);\n\n return {\n skills: store.skillConfigs.length > 0 ? store.skillConfigs : buildSkillConfigs(allSkills),\n selectedAgents: store.selectedAgents,\n agentConfigs: store.agentConfigs,\n selectedStackId: store.selectedStackId,\n domainSelections: store.domainSelections,\n selectedDomains: store.selectedDomains,\n cancelled: false,\n validation,\n ...overrides,\n };\n}\n\n/**\n * Simulates a user selecting specific skills via the wizard store.\n *\n * Sets up domainSelections as if the user toggled each skill in the build step,\n * using the matrix to look up the correct domain and category per skill.\n */\nexport function simulateSkillSelections(\n skillIds: SkillId[],\n matrix: MergedSkillsMatrix,\n selectedDomains: string[],\n): void {\n const domainSelections = skillIds.reduce<DomainSelections>((acc, skillId) => {\n const skill = matrix.skills[skillId];\n if (!skill) return acc;\n // Boundary cast: skill.category is a Category at runtime\n const category = skill.category as Category;\n const domain = matrix.categories[category]?.domain;\n if (!domain) return acc;\n const domainObj = acc[domain] ?? {};\n const subcatList = domainObj[category] ?? [];\n if (subcatList.includes(skillId)) return acc;\n return {\n ...acc,\n [domain]: { ...domainObj, [category]: [...subcatList, skillId] },\n };\n }, {});\n\n useWizardStore.setState({\n domainSelections,\n selectedDomains: selectedDomains as Domain[],\n approach: \"scratch\",\n step: \"confirm\",\n });\n}\n\n/**\n * Extracts skill IDs from a stack assignment value, which may be:\n * - A bare string (e.g., \"web-framework-react\")\n * - An object with .id (e.g., { id: \"web-framework-react\", preloaded: true })\n * - An array of strings or objects\n */\nexport function extractSkillIdsFromAssignment(assignment: unknown): string[] {\n if (typeof assignment === \"string\") {\n return [assignment];\n }\n if (Array.isArray(assignment)) {\n return assignment.flatMap((item) => extractSkillIdsFromAssignment(item));\n }\n if (typeof assignment === \"object\" && assignment !== null && \"id\" in assignment) {\n return [String((assignment as { id: string }).id)];\n }\n return [];\n}\n\nexport function buildTestProjectConfig(\n agents: string[],\n skills: Array<string | { id: string }>,\n overrides?: Partial<TestProjectConfig>,\n): TestProjectConfig {\n return {\n name: \"test-project\",\n description: \"Test project\",\n agents,\n skills,\n ...overrides,\n };\n}\n\nexport function createMockSkillDefinition(\n id: SkillId,\n overrides?: Partial<SkillDefinition>,\n): SkillDefinition {\n return {\n id,\n path: `skills/${id}/`,\n description: `${id} skill`,\n ...overrides,\n };\n}\n\n/** Decomposed matrix config returned by createMockMatrixConfig (replaces SkillsMatrixConfig) */\nexport type MockMatrixConfig = {\n categories: Record<string, CategoryDefinition>;\n relationships: RelationshipDefinitions;\n};\n\nexport function createMockMatrixConfig(\n categories: Record<string, CategoryDefinition>,\n overrides?: {\n relationships?: RelationshipDefinitions;\n },\n): MockMatrixConfig {\n return {\n categories,\n relationships: overrides?.relationships ?? {\n conflicts: [],\n discourages: [],\n recommends: [],\n requires: [],\n alternatives: [],\n },\n };\n}\n\nexport function createMockStack(\n id: string,\n config: {\n name: string;\n description?: string;\n agents: Record<string, StackAgentConfig>;\n philosophy?: string;\n },\n): Stack {\n return {\n id,\n name: config.name,\n description: config.description ?? \"\",\n // Boundary cast: test callers may pass arbitrary agent names (e.g., \"nonexistent-agent\")\n agents: config.agents as Stack[\"agents\"],\n philosophy: config.philosophy,\n };\n}\n\nexport function createMockCompileConfig(\n agents: Record<string, CompileAgentConfig>,\n overrides?: Partial<CompileConfig>,\n): CompileConfig {\n return {\n name: \"Test Plugin\",\n description: \"Test description\",\n agents,\n ...overrides,\n };\n}\n\nexport function createMockCompiledStackPlugin(\n overrides?: Partial<CompiledStackPlugin>,\n): CompiledStackPlugin {\n return {\n pluginPath: \"/tmp/cc-stack-123456/test-stack\",\n manifest: { name: \"test-stack\", version: \"1.0.0\" },\n stackName: \"Test Stack\",\n agents: [\"web-developer\"],\n skillPlugins: [\"web-framework-react\"],\n hasHooks: false,\n ...overrides,\n };\n}\n\nexport function createMockSkillAssignment(id: SkillId, preloaded = false): SkillAssignment {\n return { id, preloaded };\n}\n\nexport function createMockRawStacksConfig(): RawStacksConfig {\n return {\n stacks: [\n {\n id: \"nextjs-fullstack\",\n name: \"Next.js Fullstack\",\n description: \"Full-stack Next.js with Hono API\",\n agents: {\n \"web-developer\": {\n \"web-framework\": \"web-framework-react\",\n \"web-styling\": \"web-styling-scss-modules\",\n },\n \"api-developer\": {\n \"api-api\": \"api-framework-hono\",\n \"api-database\": \"api-database-drizzle\",\n },\n },\n },\n {\n id: \"vue-spa\",\n name: \"Vue SPA\",\n description: \"Vue single-page application\",\n agents: {\n \"web-developer\": {\n \"web-framework\": \"web-framework-vue-composition-api\",\n \"web-styling\": \"web-styling-tailwind\",\n },\n },\n },\n ],\n };\n}\n\nexport function createMockRawStacksConfigWithArrays(): RawStacksConfig {\n return {\n stacks: [\n {\n id: \"multi-select-stack\",\n name: \"Multi-Select Stack\",\n description: \"Stack with array-valued categories\",\n agents: {\n \"web-developer\": {\n \"web-framework\": \"web-framework-react\",\n \"shared-methodology\": [\n \"meta-methodology-investigation-requirements\",\n \"meta-methodology-anti-over-engineering\",\n \"meta-methodology-success-criteria\",\n ],\n },\n \"pattern-scout\": {\n \"shared-methodology\": [\n \"meta-methodology-investigation-requirements\",\n \"meta-methodology-anti-over-engineering\",\n ],\n \"shared-research\": \"meta-research-research-methodology\",\n },\n },\n },\n ],\n };\n}\n\nexport function createMockRawStacksConfigWithObjects(): RawStacksConfig {\n return {\n stacks: [\n {\n id: \"object-stack\",\n name: \"Object Stack\",\n description: \"Stack with object-form skill assignments\",\n agents: {\n \"web-developer\": {\n \"web-framework\": [{ id: \"web-framework-react\", preloaded: true }],\n \"web-styling\": \"web-styling-scss-modules\",\n \"shared-methodology\": [\n { id: \"meta-methodology-investigation-requirements\", preloaded: true },\n \"meta-methodology-anti-over-engineering\",\n ],\n },\n },\n },\n ],\n };\n}\n\nexport function createMockMarketplace(plugins: MarketplacePlugin[] = []): Marketplace {\n return {\n name: \"test-marketplace\",\n version: \"1.0.0\",\n owner: { name: \"Test Owner\" },\n plugins,\n };\n}\n\nexport function createMockMarketplacePlugin(\n name: string,\n source: MarketplacePlugin[\"source\"] = \"local\",\n): MarketplacePlugin {\n return {\n name,\n source,\n };\n}\n\n/**\n * Creates a ResolvedSkill with availableSources annotation for multi-source testing.\n * Simulates what multi-source-loader.ts does after tagging.\n */\nexport function createMockMultiSourceSkill(\n id: SkillId,\n sources: SkillSource[],\n overrides?: Partial<ResolvedSkill>,\n): ResolvedSkill {\n const activeSource = sources.find((s) => s.installed) ?? sources[0];\n return createMockSkill(id, {\n availableSources: sources,\n activeSource,\n ...overrides,\n });\n}\n\nexport function createMockCompiledAgentData(overrides?: Partial<AgentConfig>): CompiledAgentData {\n const agent = createMockAgentConfig(\"test-agent\", [], {\n title: \"Test Agent\",\n description: \"A test agent\",\n ...overrides,\n });\n\n return {\n agent,\n intro: \"Test intro\",\n workflow: \"Test workflow\",\n examples: \"Test examples\",\n criticalRequirementsTop: \"\",\n criticalReminders: \"\",\n outputFormat: \"\",\n skills: agent.skills,\n preloadedSkills: [],\n dynamicSkills: [],\n preloadedSkillIds: [],\n };\n}\n\nexport { getTestSkill, TEST_SKILLS, TEST_CATEGORIES, TEST_MATRICES } from \"./test-fixtures\";\nexport type { TestSkillName } from \"./test-fixtures\";\n"],"mappings":"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","names":[]}
@@ -1,15 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  cliTheme
4
- } from "./chunk-XUDTFI4M.js";
4
+ } from "./chunk-T5DJCIUP.js";
5
5
  import {
6
6
  useTextInput
7
7
  } from "./chunk-U3IGFMCY.js";
8
+ import {
9
+ HOTKEY_COPY_LINK,
10
+ KEY_LABEL_ENTER,
11
+ KEY_LABEL_ESC,
12
+ KEY_LABEL_SPACE,
13
+ KEY_LABEL_VIM_VERT,
14
+ isHotkey
15
+ } from "./chunk-6VIOO74O.js";
8
16
  import {
9
17
  CLI_COLORS,
10
18
  UI_LAYOUT,
11
19
  UI_SYMBOLS
12
- } from "./chunk-6OWHQ7HM.js";
20
+ } from "./chunk-EGMQ3SXN.js";
13
21
  import {
14
22
  init_esm_shims
15
23
  } from "./chunk-DHET7RCE.js";
@@ -94,7 +102,7 @@ function matchesQuery(skill, query) {
94
102
  const lowerQuery = query.toLowerCase();
95
103
  if (skill.id.toLowerCase().includes(lowerQuery)) return true;
96
104
  if (skill.displayName.toLowerCase().includes(lowerQuery)) return true;
97
- if (skill.slug?.toLowerCase().includes(lowerQuery)) return true;
105
+ if (skill.slug.toLowerCase().includes(lowerQuery)) return true;
98
106
  if (skill.description.toLowerCase().includes(lowerQuery)) return true;
99
107
  if (skill.category.toLowerCase().includes(lowerQuery)) return true;
100
108
  if (skill.tags.some((tag) => tag.toLowerCase().includes(lowerQuery))) return true;
@@ -138,7 +146,7 @@ var SearchInput = ({ value }) => {
138
146
  };
139
147
  var ResultItem = ({ skill, isSelected, isFocused }) => {
140
148
  const checkbox = isSelected ? CHECKBOX_CHECKED : CHECKBOX_UNCHECKED;
141
- const displayName = skill.displayName || skill.id;
149
+ const displayName = skill.displayName;
142
150
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
143
151
  /* @__PURE__ */ jsxs(
144
152
  Text,
@@ -218,23 +226,23 @@ var Footer = ({ hasSelection }) => {
218
226
  marginTop: 1,
219
227
  children: [
220
228
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
221
- /* @__PURE__ */ jsx(Text, { color: CLI_COLORS.UNFOCUSED, children: "j/k" }),
229
+ /* @__PURE__ */ jsx(Text, { color: CLI_COLORS.UNFOCUSED, children: KEY_LABEL_VIM_VERT }),
222
230
  " navigate"
223
231
  ] }),
224
232
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
225
- /* @__PURE__ */ jsx(Text, { color: CLI_COLORS.UNFOCUSED, children: "SPACE" }),
233
+ /* @__PURE__ */ jsx(Text, { color: CLI_COLORS.UNFOCUSED, children: KEY_LABEL_SPACE }),
226
234
  " select"
227
235
  ] }),
228
236
  hasSelection && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
229
- /* @__PURE__ */ jsx(Text, { color: CLI_COLORS.SUCCESS, children: "ENTER" }),
237
+ /* @__PURE__ */ jsx(Text, { color: CLI_COLORS.SUCCESS, children: KEY_LABEL_ENTER }),
230
238
  " import"
231
239
  ] }),
232
240
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
233
- /* @__PURE__ */ jsx(Text, { color: CLI_COLORS.UNFOCUSED, children: "c" }),
241
+ /* @__PURE__ */ jsx(Text, { color: CLI_COLORS.UNFOCUSED, children: HOTKEY_COPY_LINK.label }),
234
242
  " copy link"
235
243
  ] }),
236
244
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
237
- /* @__PURE__ */ jsx(Text, { color: CLI_COLORS.WARNING, children: "ESC" }),
245
+ /* @__PURE__ */ jsx(Text, { color: CLI_COLORS.WARNING, children: KEY_LABEL_ESC }),
238
246
  " cancel"
239
247
  ] })
240
248
  ]
@@ -309,7 +317,7 @@ var SkillSearch = ({
309
317
  toggleSelection(focusedSkill.id);
310
318
  return;
311
319
  }
312
- if ((input === "c" || input === "C") && focusedSkill) {
320
+ if (isHotkey(input, HOTKEY_COPY_LINK) && focusedSkill) {
313
321
  void copySkillLink(focusedSkill);
314
322
  return;
315
323
  }
@@ -344,4 +352,4 @@ var SkillSearch = ({
344
352
  export {
345
353
  SkillSearch
346
354
  };
347
- //# sourceMappingURL=chunk-BFD5NZQ4.js.map
355
+ //# sourceMappingURL=chunk-MVYJVKVT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/skill-search/skill-search.tsx","../src/cli/components/hooks/use-filtered-results.ts"],"sourcesContent":["import React, { useState, useCallback } from \"react\";\nimport { Box, Text, useApp, useInput } from \"ink\";\nimport { ThemeProvider } from \"@inkjs/ui\";\nimport { cliTheme } from \"../themes/default.js\";\nimport type { ResolvedSkill, SkillId } from \"../../types/index.js\";\nimport { CLI_COLORS, UI_SYMBOLS, UI_LAYOUT } from \"../../consts.js\";\nimport {\n HOTKEY_COPY_LINK,\n KEY_LABEL_ENTER,\n KEY_LABEL_ESC,\n KEY_LABEL_SPACE,\n KEY_LABEL_VIM_VERT,\n isHotkey,\n} from \"../wizard/hotkeys.js\";\nimport { useTextInput } from \"../hooks/use-text-input.js\";\nimport { useFilteredResults } from \"../hooks/use-filtered-results.js\";\n\nexport type SourcedSkill = ResolvedSkill & {\n sourceName: string;\n sourceUrl?: string;\n};\n\nexport type SkillSearchResult = {\n selectedSkills: SourcedSkill[];\n cancelled: boolean;\n};\n\nexport type SkillSearchProps = {\n skills: SourcedSkill[];\n sourceCount: number;\n initialQuery?: string;\n onComplete: (result: SkillSearchResult) => void;\n onCancel: () => void;\n};\n\nconst {\n MAX_VISIBLE_RESULTS,\n DESCRIPTION_WIDTH,\n COPIED_MESSAGE_TIMEOUT_MS,\n FALLBACK_MESSAGE_TIMEOUT_MS,\n} = UI_LAYOUT;\nconst { CHECKBOX_CHECKED, CHECKBOX_UNCHECKED } = UI_SYMBOLS;\n\nfunction matchesQuery(skill: SourcedSkill, query: string): boolean {\n if (!query.trim()) return true;\n\n const lowerQuery = query.toLowerCase();\n\n if (skill.id.toLowerCase().includes(lowerQuery)) return true;\n if (skill.displayName.toLowerCase().includes(lowerQuery)) return true;\n if (skill.slug.toLowerCase().includes(lowerQuery)) return true;\n if (skill.description.toLowerCase().includes(lowerQuery)) return true;\n if (skill.category.toLowerCase().includes(lowerQuery)) return true;\n if (skill.tags.some((tag) => tag.toLowerCase().includes(lowerQuery))) return true;\n\n return skill.sourceName.toLowerCase().includes(lowerQuery);\n}\n\nfunction truncate(text: string, maxLength: number): string {\n if (text.length <= maxLength) return text;\n return `${text.slice(0, maxLength - 3)}...`;\n}\n\ntype HeaderProps = {\n sourceCount: number;\n};\n\nconst Header: React.FC<HeaderProps> = ({ sourceCount }) => {\n return (\n <Box\n flexDirection=\"row\"\n justifyContent=\"space-between\"\n borderStyle=\"single\"\n borderBottom={false}\n borderLeft={false}\n borderRight={false}\n paddingX={1}\n >\n <Text bold color={CLI_COLORS.PRIMARY}>\n Search Skills\n </Text>\n <Text dimColor>\n {sourceCount} source{sourceCount !== 1 ? \"s\" : \"\"}\n </Text>\n </Box>\n );\n};\n\ntype SearchInputProps = {\n value: string;\n};\n\nconst SearchInput: React.FC<SearchInputProps> = ({ value }) => {\n return (\n <Box paddingX={1} paddingY={1}>\n <Text color={CLI_COLORS.FOCUS}>{\">\"} </Text>\n <Text>{value}</Text>\n <Text color={CLI_COLORS.FOCUS}>_</Text>\n </Box>\n );\n};\n\ntype ResultItemProps = {\n skill: SourcedSkill;\n isSelected: boolean;\n isFocused: boolean;\n};\n\nconst ResultItem: React.FC<ResultItemProps> = ({ skill, isSelected, isFocused }) => {\n const checkbox = isSelected ? CHECKBOX_CHECKED : CHECKBOX_UNCHECKED;\n const displayName = skill.displayName;\n return (\n <Box flexDirection=\"row\" gap={1}>\n <Text\n color={isFocused ? CLI_COLORS.FOCUS : isSelected ? CLI_COLORS.SUCCESS : undefined}\n bold={isFocused}\n backgroundColor={isFocused ? \"#333333\" : undefined}\n >\n {\" \"}\n {checkbox}{\" \"}\n </Text>\n <Box width={14}>\n <Text dimColor>{truncate(skill.sourceName, 12)}</Text>\n </Box>\n <Box width={24}>\n <Text color={isFocused ? CLI_COLORS.FOCUS : undefined} bold={isFocused || isSelected}>\n {truncate(displayName, 22)}\n </Text>\n </Box>\n <Text dimColor>{truncate(skill.description, DESCRIPTION_WIDTH)}</Text>\n </Box>\n );\n};\n\ntype ResultsListProps = {\n results: SourcedSkill[];\n selectedIds: Set<SkillId>;\n focusedIndex: number;\n scrollOffset: number;\n};\n\nconst ResultsList: React.FC<ResultsListProps> = ({\n results,\n selectedIds,\n focusedIndex,\n scrollOffset,\n}) => {\n const visibleResults = results.slice(scrollOffset, scrollOffset + MAX_VISIBLE_RESULTS);\n\n if (results.length === 0) {\n return (\n <Box paddingX={1} paddingY={1}>\n <Text dimColor>No skills found matching your search.</Text>\n </Box>\n );\n }\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderTop={false}\n borderBottom={false}\n paddingX={1}\n >\n {visibleResults.map((skill, index) => {\n const actualIndex = scrollOffset + index;\n return (\n <ResultItem\n key={skill.id}\n skill={skill}\n isSelected={selectedIds.has(skill.id)}\n isFocused={actualIndex === focusedIndex}\n />\n );\n })}\n </Box>\n );\n};\n\ntype StatusBarProps = {\n resultCount: number;\n selectedCount: number;\n};\n\nconst StatusBar: React.FC<StatusBarProps> = ({ resultCount, selectedCount }) => {\n return (\n <Box paddingX={1}>\n <Text dimColor>\n {resultCount} result{resultCount !== 1 ? \"s\" : \"\"}\n {selectedCount > 0 && <Text color={CLI_COLORS.SUCCESS}> | {selectedCount} selected</Text>}\n </Text>\n </Box>\n );\n};\n\ntype FooterProps = {\n hasSelection: boolean;\n};\n\nconst Footer: React.FC<FooterProps> = ({ hasSelection }) => {\n return (\n <Box\n flexDirection=\"row\"\n justifyContent=\"center\"\n gap={2}\n borderStyle=\"single\"\n borderTop={false}\n borderLeft={false}\n borderRight={false}\n paddingX={1}\n marginTop={1}\n >\n <Text dimColor>\n <Text color={CLI_COLORS.UNFOCUSED}>{KEY_LABEL_VIM_VERT}</Text> navigate\n </Text>\n <Text dimColor>\n <Text color={CLI_COLORS.UNFOCUSED}>{KEY_LABEL_SPACE}</Text> select\n </Text>\n {hasSelection && (\n <Text dimColor>\n <Text color={CLI_COLORS.SUCCESS}>{KEY_LABEL_ENTER}</Text> import\n </Text>\n )}\n <Text dimColor>\n <Text color={CLI_COLORS.UNFOCUSED}>{HOTKEY_COPY_LINK.label}</Text> copy link\n </Text>\n <Text dimColor>\n <Text color={CLI_COLORS.WARNING}>{KEY_LABEL_ESC}</Text> cancel\n </Text>\n </Box>\n );\n};\n\nexport const SkillSearch: React.FC<SkillSearchProps> = ({\n skills,\n sourceCount,\n initialQuery = \"\",\n onComplete,\n onCancel,\n}) => {\n const { exit } = useApp();\n\n const { value: query, handleInput: handleTextInput } = useTextInput(initialQuery);\n const [selectedIds, setSelectedIds] = useState<Set<SkillId>>(new Set());\n const [copiedMessage, setCopiedMessage] = useState<string | null>(null);\n\n const {\n filteredResults,\n safeFocusedIndex,\n focusedItem: focusedSkill,\n scrollOffset,\n moveUp,\n moveDown,\n } = useFilteredResults({\n items: skills,\n query,\n filterFn: matchesQuery,\n maxVisible: MAX_VISIBLE_RESULTS,\n });\n\n const toggleSelection = useCallback((skillId: SkillId) => {\n setSelectedIds((prev) => {\n const next = new Set(prev);\n if (next.has(skillId)) {\n next.delete(skillId);\n } else {\n next.add(skillId);\n }\n return next;\n });\n }, []);\n\n const copySkillLink = useCallback(async (skill: SourcedSkill) => {\n const link = skill.sourceUrl ? `${skill.sourceUrl}/${skill.id}` : skill.id;\n\n try {\n // OSC 52 terminal clipboard escape sequence\n const encoded = Buffer.from(link).toString(\"base64\");\n process.stdout.write(`\\x1b]52;c;${encoded}\\x07`);\n setCopiedMessage(`Copied: ${link}`);\n setTimeout(() => setCopiedMessage(null), COPIED_MESSAGE_TIMEOUT_MS);\n } catch {\n setCopiedMessage(`Link: ${link}`);\n setTimeout(() => setCopiedMessage(null), FALLBACK_MESSAGE_TIMEOUT_MS);\n }\n }, []);\n\n useInput((input, key) => {\n if (key.escape) {\n onCancel();\n exit();\n return;\n }\n\n if (key.return) {\n if (selectedIds.size > 0) {\n const selectedSkills = filteredResults.filter((s) => selectedIds.has(s.id));\n onComplete({\n selectedSkills,\n cancelled: false,\n });\n exit();\n }\n return;\n }\n\n if (input === \" \" && focusedSkill) {\n toggleSelection(focusedSkill.id);\n return;\n }\n\n if (isHotkey(input, HOTKEY_COPY_LINK) && focusedSkill) {\n void copySkillLink(focusedSkill);\n return;\n }\n\n const isUp = key.upArrow || input === \"k\";\n const isDown = key.downArrow || input === \"j\";\n\n if (isUp) {\n moveUp();\n }\n\n if (isDown) {\n moveDown();\n }\n\n handleTextInput(input, key);\n });\n\n return (\n <ThemeProvider theme={cliTheme}>\n <Box flexDirection=\"column\">\n <Header sourceCount={sourceCount} />\n <SearchInput value={query} />\n <ResultsList\n results={filteredResults}\n selectedIds={selectedIds}\n focusedIndex={safeFocusedIndex}\n scrollOffset={scrollOffset}\n />\n <StatusBar resultCount={filteredResults.length} selectedCount={selectedIds.size} />\n {copiedMessage && (\n <Box paddingX={1}>\n <Text color={CLI_COLORS.SUCCESS}>{copiedMessage}</Text>\n </Box>\n )}\n <Footer hasSelection={selectedIds.size > 0} />\n </Box>\n </ThemeProvider>\n );\n};\n","import { useState, useMemo, useEffect, useCallback, useRef } from \"react\";\n\ntype UseFilteredResultsOptions<T> = {\n /** Items to filter */\n items: T[];\n /** Current search query */\n query: string;\n /** Filter predicate — returns true if item matches the query */\n filterFn: (item: T, query: string) => boolean;\n /** Number of visible rows in the scrollable viewport */\n maxVisible: number;\n};\n\ntype UseFilteredResultsResult<T> = {\n /** Items that pass the filter */\n filteredResults: T[];\n /** Index clamped to valid range */\n safeFocusedIndex: number;\n /** The item at safeFocusedIndex, or undefined when empty */\n focusedItem: T | undefined;\n /** First visible row offset for windowed rendering */\n scrollOffset: number;\n /** Move focus up one row (clamped, adjusts scroll) */\n moveUp: () => void;\n /** Move focus down one row (clamped, adjusts scroll) */\n moveDown: () => void;\n};\n\n/**\n * Manages filtered list state: query-based filtering, focus tracking,\n * and scroll-offset synchronisation for windowed rendering.\n * Focus and scroll reset automatically when the query changes.\n */\nexport function useFilteredResults<T>({\n items,\n query,\n filterFn,\n maxVisible,\n}: UseFilteredResultsOptions<T>): UseFilteredResultsResult<T> {\n const [focusedIndex, setFocusedIndex] = useState(0);\n const [scrollOffset, setScrollOffset] = useState(0);\n\n useEffect(() => {\n setFocusedIndex(0);\n setScrollOffset(0);\n }, [query]);\n\n const filteredResults = useMemo(() => {\n return items.filter((item) => filterFn(item, query));\n }, [items, query, filterFn]);\n\n const safeFocusedIndex = Math.min(focusedIndex, Math.max(0, filteredResults.length - 1));\n const focusedItem = filteredResults[safeFocusedIndex] as T | undefined;\n\n // Refs for stable callback access (matches use-keyboard-navigation pattern)\n const safeFocusedIndexRef = useRef(safeFocusedIndex);\n const scrollOffsetRef = useRef(scrollOffset);\n const resultCountRef = useRef(filteredResults.length);\n\n useEffect(() => {\n safeFocusedIndexRef.current = safeFocusedIndex;\n }, [safeFocusedIndex]);\n\n useEffect(() => {\n scrollOffsetRef.current = scrollOffset;\n }, [scrollOffset]);\n\n useEffect(() => {\n resultCountRef.current = filteredResults.length;\n }, [filteredResults.length]);\n\n const moveUp = useCallback(() => {\n const current = safeFocusedIndexRef.current;\n if (current <= 0) return;\n const newIndex = current - 1;\n setFocusedIndex(newIndex);\n if (newIndex < scrollOffsetRef.current) {\n setScrollOffset(newIndex);\n }\n }, []);\n\n const moveDown = useCallback(() => {\n const current = safeFocusedIndexRef.current;\n if (current >= resultCountRef.current - 1) return;\n const newIndex = current + 1;\n setFocusedIndex(newIndex);\n if (newIndex >= scrollOffsetRef.current + maxVisible) {\n setScrollOffset(newIndex - maxVisible + 1);\n }\n }, [maxVisible]);\n\n return {\n filteredResults,\n safeFocusedIndex,\n focusedItem,\n scrollOffset,\n moveUp,\n moveDown,\n };\n}\n"],"mappings":"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","names":["useState","useCallback","useState","useCallback"]}
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  CLI_COLORS,
4
4
  UI_SYMBOLS
5
- } from "./chunk-6OWHQ7HM.js";
5
+ } from "./chunk-EGMQ3SXN.js";
6
6
  import {
7
7
  init_esm_shims
8
8
  } from "./chunk-DHET7RCE.js";
@@ -38,4 +38,4 @@ var SelectionCard = ({
38
38
  export {
39
39
  SelectionCard
40
40
  };
41
- //# sourceMappingURL=chunk-X5EG4EFP.js.map
41
+ //# sourceMappingURL=chunk-MWGDG4QN.js.map
@@ -2,16 +2,19 @@
2
2
  import {
3
3
  getDomainDisplayName,
4
4
  orderDomains
5
- } from "./chunk-BKJHAJQW.js";
5
+ } from "./chunk-ZYUASJUN.js";
6
6
  import {
7
7
  CheckboxGrid
8
- } from "./chunk-CIG7IKX3.js";
8
+ } from "./chunk-OV5UJWS5.js";
9
9
  import {
10
10
  useWizardStore
11
- } from "./chunk-WF6RM73R.js";
11
+ } from "./chunk-4C7CSZC5.js";
12
12
  import {
13
13
  typedEntries
14
14
  } from "./chunk-T4EXUIBY.js";
15
+ import {
16
+ useMatrixStore
17
+ } from "./chunk-BKL3DF2Q.js";
15
18
  import {
16
19
  init_esm_shims
17
20
  } from "./chunk-DHET7RCE.js";
@@ -28,8 +31,9 @@ var BUILT_IN_DOMAIN_DESCRIPTIONS = {
28
31
  mobile: "Mobile applications",
29
32
  shared: "Shared utilities and methodology"
30
33
  };
31
- var DomainSelection = ({ matrix }) => {
34
+ var DomainSelection = () => {
32
35
  const { selectedDomains, toggleDomain, setStep, setApproach, selectStack } = useWizardStore();
36
+ const matrix = useMatrixStore((s) => s.getMatrix());
33
37
  const availableDomains = useMemo(() => {
34
38
  const matrixDomains = unique(
35
39
  typedEntries(matrix.categories).map(([, cat]) => cat?.domain).filter((d) => d != null)
@@ -38,7 +42,7 @@ var DomainSelection = ({ matrix }) => {
38
42
  return ordered.map((domain) => ({
39
43
  id: domain,
40
44
  label: getDomainDisplayName(domain),
41
- description: BUILT_IN_DOMAIN_DESCRIPTIONS[domain] ?? `${getDomainDisplayName(domain)} skills`
45
+ description: BUILT_IN_DOMAIN_DESCRIPTIONS[domain]
42
46
  }));
43
47
  }, [matrix]);
44
48
  const handleBack = () => {
@@ -62,4 +66,4 @@ var DomainSelection = ({ matrix }) => {
62
66
  export {
63
67
  DomainSelection
64
68
  };
65
- //# sourceMappingURL=chunk-TGLRDEEL.js.map
69
+ //# sourceMappingURL=chunk-O2HK3NTG.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 { useMatrixStore } from \"../../stores/matrix-store.js\";\nimport type { Domain } from \"../../types/index.js\";\nimport { typedEntries } from \"../../utils/typed-object.js\";\nimport { CheckboxGrid, type CheckboxItem } from \"./checkbox-grid.js\";\nimport { getDomainDisplayName, orderDomains } 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\nexport const DomainSelection: React.FC = () => {\n const { selectedDomains, toggleDomain, setStep, setApproach, selectStack } = useWizardStore();\n const matrix = useMatrixStore((s) => s.getMatrix());\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),\n );\n\n const ordered = orderDomains(matrixDomains);\n\n return ordered.map((domain) => ({\n id: domain,\n label: getDomainDisplayName(domain),\n description: BUILT_IN_DOMAIN_DESCRIPTIONS[domain],\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 emptyMessage=\"Please select at least one domain\"\n />\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA,SAAgB,eAAe;AAC/B,SAAS,cAAc;AA0CnB;AAlCJ,IAAM,+BAAuD;AAAA,EAC3D,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,QAAQ;AACV;AAEO,IAAM,kBAA4B,MAAM;AAC7C,QAAM,EAAE,iBAAiB,cAAc,SAAS,aAAa,YAAY,IAAI,eAAe;AAC5F,QAAM,SAAS,eAAe,CAAC,MAAM,EAAE,UAAU,CAAC;AAElD,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,IAAI;AAAA,IACzC;AAEA,UAAM,UAAU,aAAa,aAAa;AAE1C,WAAO,QAAQ,IAAI,CAAC,YAAY;AAAA,MAC9B,IAAI;AAAA,MACJ,OAAO,qBAAqB,MAAM;AAAA,MAClC,aAAa,6BAA6B,MAAM;AAAA,IAClD,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,cAAa;AAAA;AAAA,EACf;AAEJ;","names":[]}
@@ -1,44 +1,54 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ WizardLayout
4
+ } from "./chunk-4KVBH2X4.js";
2
5
  import {
3
6
  StepSettings
4
- } from "./chunk-52M2XF3W.js";
7
+ } from "./chunk-RG3KDXMR.js";
5
8
  import {
6
9
  StepSources
7
- } from "./chunk-SZRK3VOR.js";
10
+ } from "./chunk-WSMQ5GAP.js";
8
11
  import {
9
12
  StepStack
10
- } from "./chunk-MKCHLXMY.js";
11
- import {
12
- WizardLayout
13
- } from "./chunk-EMIUPGPL.js";
13
+ } from "./chunk-6DEK3TDF.js";
14
14
  import {
15
15
  StepAgents
16
- } from "./chunk-SEJF7CGJ.js";
16
+ } from "./chunk-J4POGAJF.js";
17
17
  import {
18
18
  StepBuild
19
- } from "./chunk-TC3NHO34.js";
19
+ } from "./chunk-BGPGQF35.js";
20
20
  import {
21
21
  StepConfirm
22
- } from "./chunk-KIWFEBKH.js";
22
+ } from "./chunk-52THXN5G.js";
23
23
  import {
24
24
  getStackName
25
- } from "./chunk-BKJHAJQW.js";
25
+ } from "./chunk-ZYUASJUN.js";
26
26
  import {
27
27
  useWizardStore
28
- } from "./chunk-WF6RM73R.js";
28
+ } from "./chunk-4C7CSZC5.js";
29
29
  import {
30
30
  cliTheme
31
- } from "./chunk-XUDTFI4M.js";
31
+ } from "./chunk-T5DJCIUP.js";
32
+ import {
33
+ HOTKEY_ACCEPT_DEFAULTS,
34
+ HOTKEY_HELP,
35
+ HOTKEY_SCOPE,
36
+ HOTKEY_SETTINGS,
37
+ isHotkey
38
+ } from "./chunk-6VIOO74O.js";
32
39
  import {
33
40
  resolveAlias,
34
41
  validateSelection
35
- } from "./chunk-TZXYBG3R.js";
42
+ } from "./chunk-53URJ5XK.js";
43
+ import {
44
+ getMatrix
45
+ } from "./chunk-BKL3DF2Q.js";
36
46
  import {
37
47
  warn
38
- } from "./chunk-LWXRUR6B.js";
48
+ } from "./chunk-LMZXL5RQ.js";
39
49
  import {
40
50
  CLI_COLORS
41
- } from "./chunk-6OWHQ7HM.js";
51
+ } from "./chunk-EGMQ3SXN.js";
42
52
  import {
43
53
  init_esm_shims
44
54
  } from "./chunk-DHET7RCE.js";
@@ -53,7 +63,6 @@ import { ThemeProvider } from "@inkjs/ui";
53
63
  init_esm_shims();
54
64
  import { useRef } from "react";
55
65
  function useWizardInitialization({
56
- matrix,
57
66
  initialStep,
58
67
  initialDomains,
59
68
  initialAgents,
@@ -67,12 +76,7 @@ function useWizardInitialization({
67
76
  initialized.current = true;
68
77
  if (initialStep) {
69
78
  if (installedSkillIds?.length) {
70
- useWizardStore.getState().populateFromSkillIds(
71
- installedSkillIds,
72
- matrix.skills,
73
- matrix.categories,
74
- installedSkillConfigs
75
- );
79
+ useWizardStore.getState().populateFromSkillIds(installedSkillIds, installedSkillConfigs);
76
80
  }
77
81
  useWizardStore.setState({ step: initialStep, approach: "scratch" });
78
82
  }
@@ -96,7 +100,6 @@ init_esm_shims();
96
100
  import { useCallback } from "react";
97
101
  function useBuildStepProps({
98
102
  store,
99
- matrix,
100
103
  installedSkillIds
101
104
  }) {
102
105
  const currentDomain = store.getCurrentDomain();
@@ -107,10 +110,10 @@ function useBuildStepProps({
107
110
  const onToggle = useCallback(
108
111
  (categoryId, techId) => {
109
112
  const domain = store.getCurrentDomain() || "web";
110
- const cat = matrix.categories[categoryId];
113
+ const cat = getMatrix().categories[categoryId];
111
114
  store.toggleTechnology(domain, categoryId, techId, cat?.exclusive ?? true);
112
115
  },
113
- [store, matrix]
116
+ [store]
114
117
  );
115
118
  const onContinue = useCallback(() => {
116
119
  if (!store.nextDomain()) {
@@ -123,7 +126,6 @@ function useBuildStepProps({
123
126
  }
124
127
  }, [store]);
125
128
  return {
126
- matrix,
127
129
  domain: activeDomain,
128
130
  selectedDomains: effectiveDomains,
129
131
  selections: store.domainSelections[activeDomain] || {},
@@ -142,7 +144,6 @@ import { jsx, jsxs } from "react/jsx-runtime";
142
144
  var MIN_TERMINAL_WIDTH = 80;
143
145
  var MIN_TERMINAL_HEIGHT = 15;
144
146
  var Wizard = ({
145
- matrix,
146
147
  onComplete,
147
148
  onCancel,
148
149
  version,
@@ -166,7 +167,6 @@ var Wizard = ({
166
167
  const isNarrowTerminal = terminalWidth < MIN_TERMINAL_WIDTH;
167
168
  const isShortTerminal = terminalHeight < MIN_TERMINAL_HEIGHT;
168
169
  useWizardInitialization({
169
- matrix,
170
170
  initialStep,
171
171
  initialDomains,
172
172
  initialAgents,
@@ -175,21 +175,21 @@ var Wizard = ({
175
175
  lockedSkillIds,
176
176
  lockedAgentNames
177
177
  });
178
- const buildStepProps = useBuildStepProps({ store, matrix, installedSkillIds });
178
+ const buildStepProps = useBuildStepProps({ store, installedSkillIds });
179
179
  useInput((input, key) => {
180
180
  if (store.showSettings) {
181
- if (input === "s" || input === "S") {
181
+ if (isHotkey(input, HOTKEY_SETTINGS)) {
182
182
  store.toggleSettings();
183
183
  }
184
184
  return;
185
185
  }
186
186
  if (store.showHelp) {
187
- if (key.escape || input === "?") {
187
+ if (key.escape || isHotkey(input, HOTKEY_HELP)) {
188
188
  store.toggleHelp();
189
189
  }
190
190
  return;
191
191
  }
192
- if (input === "?") {
192
+ if (isHotkey(input, HOTKEY_HELP)) {
193
193
  store.toggleHelp();
194
194
  return;
195
195
  }
@@ -199,26 +199,26 @@ var Wizard = ({
199
199
  }
200
200
  return;
201
201
  }
202
- if ((input === "a" || input === "A") && store.step === "build" && store.selectedStackId) {
202
+ if (isHotkey(input, HOTKEY_ACCEPT_DEFAULTS) && store.step === "build" && store.selectedStackId) {
203
203
  store.setStackAction("defaults");
204
204
  store.setStep("confirm");
205
205
  return;
206
206
  }
207
- if ((input === "s" || input === "S") && store.step === "build") {
207
+ if (isHotkey(input, HOTKEY_SCOPE) && store.step === "build") {
208
208
  const focused = store.focusedSkillId;
209
209
  if (focused) {
210
210
  store.toggleSkillScope(focused);
211
211
  }
212
212
  return;
213
213
  }
214
- if ((input === "s" || input === "S") && store.step === "agents") {
214
+ if (isHotkey(input, HOTKEY_SCOPE) && store.step === "agents") {
215
215
  const focused = store.focusedAgentId;
216
216
  if (focused) {
217
217
  store.toggleAgentScope(focused);
218
218
  }
219
219
  return;
220
220
  }
221
- if ((input === "s" || input === "S") && store.step === "sources") {
221
+ if (isHotkey(input, HOTKEY_SETTINGS) && store.step === "sources") {
222
222
  store.toggleSettings();
223
223
  return;
224
224
  }
@@ -226,26 +226,20 @@ var Wizard = ({
226
226
  const handleComplete = useCallback2(() => {
227
227
  let allSkills;
228
228
  if (store.selectedStackId && store.stackAction === "defaults") {
229
- const stack = matrix.suggestedStacks.find((s) => s.id === store.selectedStackId);
229
+ const stack = getMatrix().suggestedStacks.find((s) => s.id === store.selectedStackId);
230
230
  if (!stack) {
231
231
  warn(`Stack not found in matrix: '${store.selectedStackId}'`);
232
232
  }
233
233
  allSkills = [...stack?.allSkillIds || []];
234
234
  } else {
235
235
  const techNames = store.getAllSelectedTechnologies();
236
- allSkills = techNames.map((tech) => resolveAlias(tech, matrix));
237
- }
238
- const methodologySkills = store.getDefaultMethodologySkills();
239
- for (const skill of methodologySkills) {
240
- if (!allSkills.includes(skill)) {
241
- allSkills.push(skill);
242
- }
236
+ allSkills = techNames.map((tech) => resolveAlias(tech));
243
237
  }
244
238
  const skillConfigs = allSkills.map((id) => {
245
239
  const existing = store.skillConfigs.find((sc) => sc.id === id);
246
- return existing ?? { id, scope: "project", source: "local" };
240
+ return existing ?? { id, scope: "global", source: "local" };
247
241
  });
248
- const validation = validateSelection(allSkills, matrix);
242
+ const validation = validateSelection(allSkills);
249
243
  const result = {
250
244
  skills: skillConfigs,
251
245
  selectedAgents: store.selectedAgents,
@@ -258,7 +252,7 @@ var Wizard = ({
258
252
  };
259
253
  onComplete(result);
260
254
  exit();
261
- }, [store, matrix, onComplete, exit]);
255
+ }, [store, onComplete, exit]);
262
256
  const handleCancel = useCallback2(() => {
263
257
  onCancel();
264
258
  exit();
@@ -266,7 +260,7 @@ var Wizard = ({
266
260
  const renderStep = () => {
267
261
  switch (store.step) {
268
262
  case "stack":
269
- return /* @__PURE__ */ jsx(StepStack, { matrix, onCancel: handleCancel });
263
+ return /* @__PURE__ */ jsx(StepStack, { onCancel: handleCancel });
270
264
  case "build":
271
265
  return /* @__PURE__ */ jsx(StepBuild, { ...buildStepProps });
272
266
  case "sources": {
@@ -282,7 +276,6 @@ var Wizard = ({
282
276
  return /* @__PURE__ */ jsx(
283
277
  StepSources,
284
278
  {
285
- matrix,
286
279
  projectDir,
287
280
  onContinue: () => {
288
281
  if (!initialAgents?.length) {
@@ -295,9 +288,9 @@ var Wizard = ({
295
288
  );
296
289
  }
297
290
  case "agents":
298
- return /* @__PURE__ */ jsx(StepAgents, { matrix });
291
+ return /* @__PURE__ */ jsx(StepAgents, {});
299
292
  case "confirm": {
300
- const stackName = getStackName(store.selectedStackId, matrix);
293
+ const stackName = getStackName(store.selectedStackId);
301
294
  const selectedSkills = store.getAllSelectedTechnologies();
302
295
  return /* @__PURE__ */ jsx(
303
296
  StepConfirm,
@@ -342,4 +335,4 @@ var Wizard = ({
342
335
  export {
343
336
  Wizard
344
337
  };
345
- //# sourceMappingURL=chunk-6G3KZSO4.js.map
338
+ //# sourceMappingURL=chunk-OORWBS6F.js.map