agents-inc 0.156.0 → 0.158.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 (173) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/{chunk-4A4L3YIW.js → chunk-2PN4QXON.js} +6 -7
  3. package/dist/chunk-2PN4QXON.js.map +1 -0
  4. package/dist/{chunk-QWVXOCXO.js → chunk-3NBXQSW2.js} +7 -9
  5. package/dist/{chunk-QWVXOCXO.js.map → chunk-3NBXQSW2.js.map} +1 -1
  6. package/dist/{chunk-WCAUUBHO.js → chunk-3PJLLBUS.js} +14597 -14536
  7. package/dist/chunk-3PJLLBUS.js.map +1 -0
  8. package/dist/{chunk-6RLV45HN.js → chunk-3Q2N7CSY.js} +6 -6
  9. package/dist/chunk-3Q2N7CSY.js.map +1 -0
  10. package/dist/{chunk-LJCNUP6M.js → chunk-4ABNXKAR.js} +2 -2
  11. package/dist/{chunk-D2CAV7MG.js → chunk-4DMFK67G.js} +2 -2
  12. package/dist/{chunk-MO5FGLVS.js → chunk-54QLV3OH.js} +11 -12
  13. package/dist/chunk-54QLV3OH.js.map +1 -0
  14. package/dist/{chunk-5F2YR5XV.js → chunk-6DBE562W.js} +15 -5
  15. package/dist/chunk-6DBE562W.js.map +1 -0
  16. package/dist/{chunk-RWOYPOMR.js → chunk-7Z4N4JWQ.js} +19 -245
  17. package/dist/chunk-7Z4N4JWQ.js.map +1 -0
  18. package/dist/{chunk-H6UTKTLK.js → chunk-DAUY2SB3.js} +98 -34
  19. package/dist/chunk-DAUY2SB3.js.map +1 -0
  20. package/dist/{chunk-2H7UWE2G.js → chunk-DP6YQFVO.js} +13 -12
  21. package/dist/chunk-DP6YQFVO.js.map +1 -0
  22. package/dist/{chunk-ILUVL7WM.js → chunk-DZD25NHW.js} +2 -2
  23. package/dist/{chunk-PCC4LBKB.js → chunk-EEAX7E2X.js} +2 -2
  24. package/dist/{chunk-IO5YBKHV.js → chunk-G6G2FT2G.js} +10 -8
  25. package/dist/chunk-G6G2FT2G.js.map +1 -0
  26. package/dist/{chunk-5QZAK6J7.js → chunk-H25VRR2T.js} +50 -18
  27. package/dist/chunk-H25VRR2T.js.map +1 -0
  28. package/dist/chunk-H2DHTJKW.js +1352 -0
  29. package/dist/chunk-H2DHTJKW.js.map +1 -0
  30. package/dist/{chunk-MHRC3KRX.js → chunk-H4CL23HG.js} +3 -3
  31. package/dist/chunk-H4CL23HG.js.map +1 -0
  32. package/dist/{chunk-F5T6TV4Z.js → chunk-IK6YOXZH.js} +40 -14
  33. package/dist/chunk-IK6YOXZH.js.map +1 -0
  34. package/dist/{chunk-HG4JPXJT.js → chunk-JG3B47P3.js} +23 -10
  35. package/dist/chunk-JG3B47P3.js.map +1 -0
  36. package/dist/{chunk-SY4X2LND.js → chunk-KUZBMEWR.js} +3 -3
  37. package/dist/{chunk-5OKLCVM4.js → chunk-KVVXINCJ.js} +3 -3
  38. package/dist/{chunk-PNMM5HNX.js → chunk-L4FHSOL5.js} +9 -10
  39. package/dist/chunk-L4FHSOL5.js.map +1 -0
  40. package/dist/{chunk-PMUABBPG.js → chunk-LKI3ENGQ.js} +9 -13
  41. package/dist/chunk-LKI3ENGQ.js.map +1 -0
  42. package/dist/{chunk-CLFHC2DK.js → chunk-OQB2CKBT.js} +17 -18
  43. package/dist/chunk-OQB2CKBT.js.map +1 -0
  44. package/dist/{chunk-HXKOIWLU.js → chunk-P6OKOH62.js} +2 -6
  45. package/dist/chunk-P6OKOH62.js.map +1 -0
  46. package/dist/{chunk-TNGMF4UX.js → chunk-PNRLBPGS.js} +12 -12
  47. package/dist/chunk-PNRLBPGS.js.map +1 -0
  48. package/dist/{chunk-Y5Y7XHVU.js → chunk-PZNXBORZ.js} +4 -4
  49. package/dist/{chunk-HTYH7CKN.js → chunk-Q7CAE5NN.js} +3 -3
  50. package/dist/{chunk-G47TDUK3.js → chunk-QNAMXQKF.js} +2 -2
  51. package/dist/{chunk-GS2NZ3GD.js → chunk-SJ4T5YLG.js} +5 -5
  52. package/dist/chunk-SJ4T5YLG.js.map +1 -0
  53. package/dist/{chunk-UMH2YZKG.js → chunk-TPQ5M3BW.js} +4 -6
  54. package/dist/{chunk-UMH2YZKG.js.map → chunk-TPQ5M3BW.js.map} +1 -1
  55. package/dist/{chunk-4G2YHZIM.js → chunk-TUXXBEBB.js} +4 -6
  56. package/dist/{chunk-4G2YHZIM.js.map → chunk-TUXXBEBB.js.map} +1 -1
  57. package/dist/{chunk-WFRTHH3A.js → chunk-TZRXWFF5.js} +3 -3
  58. package/dist/{chunk-TBOVYLQJ.js → chunk-VTIDLRNA.js} +2 -2
  59. package/dist/{chunk-OKH64JZE.js → chunk-WCZNQECQ.js} +2 -2
  60. package/dist/{chunk-ZUAZHDKP.js → chunk-WMVOJY6V.js} +277 -40
  61. package/dist/chunk-WMVOJY6V.js.map +1 -0
  62. package/dist/{chunk-YO2LTXB4.js → chunk-X52QUV4U.js} +115 -10
  63. package/dist/chunk-X52QUV4U.js.map +1 -0
  64. package/dist/{chunk-IXRZAHZF.js → chunk-XNYHMRVJ.js} +4 -4
  65. package/dist/{chunk-VPASBA7P.js → chunk-YXI3BTQQ.js} +2 -2
  66. package/dist/commands/build/marketplace.js +9 -12
  67. package/dist/commands/build/marketplace.js.map +1 -1
  68. package/dist/commands/build/plugins.js +9 -12
  69. package/dist/commands/build/plugins.js.map +1 -1
  70. package/dist/commands/compile.js +15 -19
  71. package/dist/commands/compile.js.map +1 -1
  72. package/dist/commands/doctor.js +75 -54
  73. package/dist/commands/doctor.js.map +1 -1
  74. package/dist/commands/edit.js +154 -109
  75. package/dist/commands/edit.js.map +1 -1
  76. package/dist/commands/eject.js +68 -38
  77. package/dist/commands/eject.js.map +1 -1
  78. package/dist/commands/init.js +36 -39
  79. package/dist/commands/list.js +8 -10
  80. package/dist/commands/list.js.map +1 -1
  81. package/dist/commands/new/marketplace.js +8 -11
  82. package/dist/commands/new/marketplace.js.map +1 -1
  83. package/dist/commands/search.js +10 -13
  84. package/dist/commands/search.js.map +1 -1
  85. package/dist/commands/share.js +8 -10
  86. package/dist/commands/share.js.map +1 -1
  87. package/dist/commands/uninstall.js +39 -28
  88. package/dist/commands/uninstall.js.map +1 -1
  89. package/dist/commands/update.js +7 -10
  90. package/dist/commands/update.js.map +1 -1
  91. package/dist/components/common/removal-plan-confirm.js +2 -2
  92. package/dist/components/common/select-list.js +2 -2
  93. package/dist/components/wizard/category-grid.js +4 -5
  94. package/dist/components/wizard/checkbox-grid.js +4 -4
  95. package/dist/components/wizard/domain-selection.js +9 -11
  96. package/dist/components/wizard/run-wizard-session.js +26 -28
  97. package/dist/components/wizard/scroll-affordance.js +2 -2
  98. package/dist/components/wizard/skill-agent-summary.js +5 -7
  99. package/dist/components/wizard/source-grid.js +5 -6
  100. package/dist/components/wizard/stack-selection.js +7 -9
  101. package/dist/components/wizard/step-agents.js +8 -10
  102. package/dist/components/wizard/step-build.js +7 -9
  103. package/dist/components/wizard/step-confirm.js +9 -11
  104. package/dist/components/wizard/step-sources.js +8 -10
  105. package/dist/components/wizard/step-stack.js +8 -10
  106. package/dist/components/wizard/summary-panel.js +8 -10
  107. package/dist/components/wizard/toast.js +2 -2
  108. package/dist/components/wizard/wizard-layout.js +12 -14
  109. package/dist/components/wizard/wizard-tabs.js +2 -4
  110. package/dist/components/wizard/wizard.js +25 -27
  111. package/dist/hooks/init.js +36 -39
  112. package/dist/hooks/init.js.map +1 -1
  113. package/dist/load-agent-defs-2VYAV5GL.js +13 -0
  114. package/dist/recompile-project-agents-NG5DCPHJ.js +17 -0
  115. package/dist/{source-loader-3DL3NYUX.js → source-loader-CO2FZ3OO.js} +4 -8
  116. package/dist/src/agents/_templates/agent.liquid +2 -2
  117. package/dist/stores/wizard-store.js +6 -6
  118. package/package.json +2 -4
  119. package/src/agents/_templates/agent.liquid +2 -2
  120. package/src/schemas/custom-metadata.schema.json +1 -1
  121. package/src/schemas/marketplace.schema.json +2 -1
  122. package/dist/chunk-2H7UWE2G.js.map +0 -1
  123. package/dist/chunk-4A4L3YIW.js.map +0 -1
  124. package/dist/chunk-5F2YR5XV.js.map +0 -1
  125. package/dist/chunk-5QZAK6J7.js.map +0 -1
  126. package/dist/chunk-6RLV45HN.js.map +0 -1
  127. package/dist/chunk-CLFHC2DK.js.map +0 -1
  128. package/dist/chunk-F2AMBBIA.js +0 -64
  129. package/dist/chunk-F2AMBBIA.js.map +0 -1
  130. package/dist/chunk-F5T6TV4Z.js.map +0 -1
  131. package/dist/chunk-GS2NZ3GD.js.map +0 -1
  132. package/dist/chunk-H6UTKTLK.js.map +0 -1
  133. package/dist/chunk-HG4JPXJT.js.map +0 -1
  134. package/dist/chunk-HXKOIWLU.js.map +0 -1
  135. package/dist/chunk-IO5YBKHV.js.map +0 -1
  136. package/dist/chunk-MHRC3KRX.js.map +0 -1
  137. package/dist/chunk-MO5FGLVS.js.map +0 -1
  138. package/dist/chunk-PMUABBPG.js.map +0 -1
  139. package/dist/chunk-PNMM5HNX.js.map +0 -1
  140. package/dist/chunk-RWOYPOMR.js.map +0 -1
  141. package/dist/chunk-TNGMF4UX.js.map +0 -1
  142. package/dist/chunk-VASLW636.js +0 -23
  143. package/dist/chunk-VASLW636.js.map +0 -1
  144. package/dist/chunk-WCAUUBHO.js.map +0 -1
  145. package/dist/chunk-WUQ3MTPF.js +0 -173
  146. package/dist/chunk-WUQ3MTPF.js.map +0 -1
  147. package/dist/chunk-XAXJWXI4.js +0 -1667
  148. package/dist/chunk-XAXJWXI4.js.map +0 -1
  149. package/dist/chunk-YO2LTXB4.js.map +0 -1
  150. package/dist/chunk-ZUAZHDKP.js.map +0 -1
  151. package/dist/copy-local-skills-NSUTBVLJ.js +0 -14
  152. package/dist/load-agent-defs-X4E57I2U.js +0 -15
  153. package/dist/loader-G6BFG2VA.js +0 -24
  154. package/dist/recompile-project-agents-JE4MM72I.js +0 -20
  155. package/dist/recompile-project-agents-JE4MM72I.js.map +0 -1
  156. package/dist/source-loader-3DL3NYUX.js.map +0 -1
  157. /package/dist/{chunk-LJCNUP6M.js.map → chunk-4ABNXKAR.js.map} +0 -0
  158. /package/dist/{chunk-D2CAV7MG.js.map → chunk-4DMFK67G.js.map} +0 -0
  159. /package/dist/{chunk-ILUVL7WM.js.map → chunk-DZD25NHW.js.map} +0 -0
  160. /package/dist/{chunk-PCC4LBKB.js.map → chunk-EEAX7E2X.js.map} +0 -0
  161. /package/dist/{chunk-SY4X2LND.js.map → chunk-KUZBMEWR.js.map} +0 -0
  162. /package/dist/{chunk-5OKLCVM4.js.map → chunk-KVVXINCJ.js.map} +0 -0
  163. /package/dist/{chunk-Y5Y7XHVU.js.map → chunk-PZNXBORZ.js.map} +0 -0
  164. /package/dist/{chunk-HTYH7CKN.js.map → chunk-Q7CAE5NN.js.map} +0 -0
  165. /package/dist/{chunk-G47TDUK3.js.map → chunk-QNAMXQKF.js.map} +0 -0
  166. /package/dist/{chunk-WFRTHH3A.js.map → chunk-TZRXWFF5.js.map} +0 -0
  167. /package/dist/{chunk-TBOVYLQJ.js.map → chunk-VTIDLRNA.js.map} +0 -0
  168. /package/dist/{chunk-OKH64JZE.js.map → chunk-WCZNQECQ.js.map} +0 -0
  169. /package/dist/{chunk-IXRZAHZF.js.map → chunk-XNYHMRVJ.js.map} +0 -0
  170. /package/dist/{chunk-VPASBA7P.js.map → chunk-YXI3BTQQ.js.map} +0 -0
  171. /package/dist/{copy-local-skills-NSUTBVLJ.js.map → load-agent-defs-2VYAV5GL.js.map} +0 -0
  172. /package/dist/{load-agent-defs-X4E57I2U.js.map → recompile-project-agents-NG5DCPHJ.js.map} +0 -0
  173. /package/dist/{loader-G6BFG2VA.js.map → source-loader-CO2FZ3OO.js.map} +0 -0
@@ -2,31 +2,27 @@
2
2
  import {
3
3
  claimSlug,
4
4
  computeFileHash,
5
+ glob,
5
6
  injectForkedFromMetadata,
7
+ readFile,
6
8
  readLocalSkillMetadata,
9
+ readSkillMetadata,
7
10
  resolveInstallPaths,
8
11
  seedExternalSkillSchema,
9
12
  seedPayloadSchema,
10
13
  validateSkillPath,
14
+ writeFile,
11
15
  writeMetadataYaml
12
- } from "./chunk-WCAUUBHO.js";
13
- import {
14
- glob,
15
- readFile,
16
- readSkillMetadata,
17
- writeFile
18
- } from "./chunk-XAXJWXI4.js";
19
- import {
20
- getCategoryDomain
21
- } from "./chunk-RWOYPOMR.js";
16
+ } from "./chunk-3PJLLBUS.js";
22
17
  import {
18
+ getCategoryDomain,
23
19
  typedEntries
24
- } from "./chunk-VASLW636.js";
20
+ } from "./chunk-7Z4N4JWQ.js";
25
21
  import {
26
22
  GITHUB_SOURCE,
27
23
  LOCAL_SKILLS_PATH,
28
24
  STANDARD_FILES
29
- } from "./chunk-IO5YBKHV.js";
25
+ } from "./chunk-G6G2FT2G.js";
30
26
 
31
27
  // src/cli/lib/seed/external-skills.ts
32
28
  import path from "path";
@@ -255,4 +251,4 @@ export {
255
251
  SEED_USER_AGENT,
256
252
  fetchSeedConfig
257
253
  };
258
- //# sourceMappingURL=chunk-PMUABBPG.js.map
254
+ //# sourceMappingURL=chunk-LKI3ENGQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/lib/seed/external-skills.ts","../src/cli/lib/seed/fetch-seed.ts"],"sourcesContent":["import path from \"path\";\n\nimport { seedExternalSkillSchema } from \"@workspace/matrix/seed\";\n\nimport { GITHUB_SOURCE, LOCAL_SKILLS_PATH, STANDARD_FILES } from \"../../consts.js\";\nimport { resolveInstallPaths } from \"../installation/install-base-dir.js\";\nimport { readSkillMetadata } from \"../loading/loader.js\";\nimport { getCategoryDomain } from \"../matrix/matrix-provider.js\";\nimport { claimSlug } from \"../matrix/skill-resolution.js\";\nimport { validateSkillPath } from \"../skills/skill-copier.js\";\nimport {\n injectForkedFromMetadata,\n readLocalSkillMetadata,\n writeMetadataYaml,\n} from \"../skills/skill-metadata.js\";\nimport { computeFileHash } from \"../versioning.js\";\nimport { glob, readFile, writeFile } from \"../../utils/fs.js\";\nimport { typedEntries } from \"../../utils/typed-object.js\";\n\nimport type { SkillScope } from \"../../types/config.js\";\nimport type { Domain, MergedSkillsMatrix, ResolvedSkill } from \"../../types/matrix.js\";\nimport type { CategoryPath, SkillId, SkillSlug } from \"../../types/skills.js\";\nimport type { ForkedFromMetadata, LocalSkillMetadata } from \"../skills/skill-metadata.js\";\nimport type {\n SeedExternalSkill,\n SeedPayload,\n SeedSkill,\n SeedSkillTree,\n} from \"@workspace/matrix/seed\";\n\n/**\n * One skill the payload carries rather than names, seated and ready to be written.\n *\n * `skillDir` and `domain` are resolved once, where the checks that make them safe already are:\n * the directory has to be the one the catalogue entry points at, because that is what tells the\n * copy step this skill is already where it belongs, and the domain is what the category guard has\n * just proved this catalogue declares.\n */\nexport type ExternalSkillInstall = {\n id: SkillId;\n scope: SkillScope;\n skill: SeedExternalSkill;\n domain: Domain;\n skillDir: string;\n};\n\n/** One external entry with the skill row that selected it — what the payload says about it. */\ntype SelectedExternal = { id: SkillId; skill: SeedExternalSkill; entry: SeedSkill };\n\n/**\n * Names every carried skill a payload asks to install as a plugin, not just the first.\n *\n * There is nothing to install them FROM: the payload is the source, so no marketplace serves\n * these ids. Coercing them to eject would install something the payload did not ask for, under a\n * mode the sharer's own screen still shows as plugin — so the ids are named and the run stops.\n */\nfunction pluginInstallError(ids: SkillId[]): string {\n return [\n \"This configuration cannot be installed: these skills travel inside it, so no marketplace \" +\n \"serves them and they cannot be installed as plugins:\",\n ...ids.map((id) => ` ${id}`),\n \"Re-share it with each skill above set to eject, which is the only way a carried skill installs.\",\n ].join(\"\\n\");\n}\n\n/**\n * The external entries this selection actually names.\n *\n * Presence in `skills` is selection and `external` is only where the bytes are, so content nobody\n * picked installs nothing — seating it would put a skill in the catalogue no part of the\n * configuration asked for.\n */\nfunction selectedExternals(payload: SeedPayload): SelectedExternal[] {\n return typedEntries<SkillId, SeedExternalSkill>(payload.external ?? {}).flatMap(([id, skill]) => {\n const entry = payload.skills[id];\n return entry ? [{ id, skill, entry }] : [];\n });\n}\n\nfunction askedForAsPlugin(externals: SelectedExternal[]): SkillId[] {\n return externals.filter(({ entry }) => entry.install !== \"eject\").map(({ id }) => id);\n}\n\n/**\n * Boundary cast: a carried skill's id is minted at intake and belongs to no generated union,\n * which is exactly what makes it external. It is its own slug too — a skill outside every\n * catalogue has no shorter name anything else would recognise, and the sending side says the same.\n */\nfunction externalSlug(id: SkillId): SkillSlug {\n return id as SkillSlug;\n}\n\n/** The repository's owner as a handle, which is all the authorship a carried skill records. */\nfunction skillAuthor(repo: string): string {\n const [owner = repo] = repo.split(\"/\");\n return `@${owner}`;\n}\n\n/** The repository ref a carried skill's provenance records, in the form every other ref wears. */\nfunction repoRef(repo: string): string {\n return `${GITHUB_SOURCE.GITHUB_PREFIX}${repo}`;\n}\n\n/** Where a carried skill's directory goes: under the skills directory of the scope it names. */\nfunction externalSkillDir(id: SkillId, scope: SkillScope, projectDir: string): string {\n return path.join(resolveInstallPaths(projectDir, scope).skillsDir, id);\n}\n\n/**\n * The catalogue entry a carried skill becomes on arrival.\n *\n * It declares no relationships and is named by none, which is honest: nothing outside the\n * catalogue can say what this skill conflicts with, and nothing inside it knows the skill exists.\n */\nfunction externalCatalogueEntry(install: ExternalSkillInstall): ResolvedSkill {\n return {\n id: install.id,\n slug: externalSlug(install.id),\n displayName: install.skill.displayName,\n description: install.skill.description,\n // Boundary cast held by the guard above it: the category was confirmed against the catalogue\n // the payload names, and this one has just been shown to declare it too.\n category: install.skill.categoryId as CategoryPath,\n author: skillAuthor(install.skill.repo),\n path: `${LOCAL_SKILLS_PATH}/${install.id}/`,\n conflictsWith: [],\n requires: [],\n alternatives: [],\n discourages: [],\n // Local and custom for the same reason: the bytes are on this machine and nowhere else, and\n // the skill was authored outside the vocabulary this catalogue ships.\n local: true,\n custom: true,\n localPath: install.skillDir,\n };\n}\n\n/**\n * Seats one carried skill in the catalogue, or leaves it unseated.\n *\n * A skill is PLACED in the taxonomy and never extends it, so a category this catalogue has no\n * definition for belongs to no domain, renders in no tab and reaches no sub-agent. Left unseated,\n * the id falls to the decode's own skip, which reports it by name.\n */\nfunction seatExternalSkill(\n { id, skill, entry }: SelectedExternal,\n matrix: MergedSkillsMatrix,\n projectDir: string,\n): ExternalSkillInstall[] {\n const domain = getCategoryDomain(skill.categoryId);\n if (domain === undefined) return [];\n\n const install: ExternalSkillInstall = {\n id,\n skill,\n domain,\n scope: entry.scope,\n skillDir: externalSkillDir(id, entry.scope, projectDir),\n };\n\n matrix.skills[id] = externalCatalogueEntry(install);\n // Completes the map over the matrix: an entry the slug map does not carry is invisible to\n // anything that addresses a skill by slug.\n claimSlug(matrix.slugMap, externalSlug(id), id);\n\n return [install];\n}\n\n/**\n * Seats the catalogue entries a payload carries, and says where each one's bytes go.\n *\n * Every other id in a payload is resolved against a catalogue the receiver already has, so one it\n * does not know is skipped and reported. A carried skill answers to no catalogue at all — which is\n * why its whole directory travels — so it has to BECOME a catalogue entry before the decode reads\n * the selection, or it is skipped like any other unknown id and the configuration installs\n * quietly smaller than it was shared.\n *\n * The entries are written into `matrix` in place, exactly as the local-skill merge writes the\n * skills it discovers on disk. That matrix is the object `initializeMatrix` seated, which is what\n * {@link getCategoryDomain} reads — every caller passes the loaded source's own.\n *\n * @throws {Error} If any carried skill asks to be installed as a plugin — see\n * {@link pluginInstallError}.\n */\nexport function registerExternalSkills(\n payload: SeedPayload,\n matrix: MergedSkillsMatrix,\n projectDir: string,\n): ExternalSkillInstall[] {\n const externals = selectedExternals(payload);\n const asPlugin = askedForAsPlugin(externals);\n\n if (asPlugin.length > 0) throw new Error(pluginInstallError(asPlugin));\n\n return externals.flatMap((external) => seatExternalSkill(external, matrix, projectDir));\n}\n\n/**\n * Writes each carried skill's directory where its own entry said, and registers it on disk.\n *\n * Three things have to be true afterwards, and each is a separate act: the files are there, the\n * skill answers to the id the configuration recorded, and a metadata.yaml describes it well\n * enough for the next `edit`, `compile` or `list` to find it again. The seat above lasts one run;\n * this is what makes the install survive.\n */\nexport async function writeExternalSkills(installs: ExternalSkillInstall[]): Promise<void> {\n for (const install of installs) {\n await writeSkillTree(install);\n await writeSkillManifest(install);\n await registerSkillOnDisk(install);\n }\n}\n\n/**\n * The directory as the payload holds it, keys and all.\n *\n * The keys came off the wire, so a payload may ask for any path it likes — including one that\n * climbs out of the skill's own directory. They answer to the same guard a marketplace's own\n * paths do.\n */\nasync function writeSkillTree(install: ExternalSkillInstall): Promise<void> {\n for (const [relativePath, contents] of Object.entries(install.skill.files)) {\n const filePath = path.join(install.skillDir, relativePath);\n validateSkillPath(filePath, install.skillDir, relativePath);\n await writeFile(filePath, contents);\n }\n}\n\n/** The frontmatter block at the head of a SKILL.md, and the `name` field inside it. */\nconst FRONTMATTER_BLOCK = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/;\nconst NAME_FIELD = /^name:.*$/m;\n\n/**\n * Names the skill by the id it installs under.\n *\n * Every loader reads a skill's id off this one field, and a compiled sub-agent references the id\n * the configuration recorded — the one minted at intake, which is also this directory's name. The\n * repository the skill came from knew about neither, so left as it was written the two never\n * meet: the sub-agent names a skill Claude Code knows as something else, and the next load\n * registers an id no configuration carries.\n *\n * Only the name. Everything else in the block is the author's and travels untouched.\n */\nfunction withInstalledName(manifest: string, install: ExternalSkillInstall): string {\n const named = `name: ${install.id}`;\n const block = FRONTMATTER_BLOCK.exec(manifest);\n\n // A manifest with no frontmatter at all describes no skill to Claude Code either. The payload\n // carries both fields one needs, so it is given one rather than installed unreadable.\n if (!block) {\n return `---\\n${named}\\ndescription: ${install.skill.description}\\n---\\n\\n${manifest}`;\n }\n\n const [whole, fields = \"\"] = block;\n const renamed = NAME_FIELD.test(fields)\n ? fields.replace(NAME_FIELD, named)\n : `${named}\\n${fields}`;\n\n // The block is anchored at the head of the file, so what follows it is the rest verbatim.\n return `---\\n${renamed}\\n---${manifest.slice(whole.length)}`;\n}\n\nasync function writeSkillManifest(install: ExternalSkillInstall): Promise<void> {\n const manifestPath = path.join(install.skillDir, STANDARD_FILES.SKILL_MD);\n await writeFile(manifestPath, withInstalledName(await readFile(manifestPath), install));\n}\n\n/**\n * What the payload confirmed, over what the repository shipped, over what neither says.\n *\n * The order is the whole of it. The taxonomy is the placement the user chose against the\n * catalogue this configuration names, and the repository's own idea of where its skill belongs\n * answers to a taxonomy nobody here shares — so that is written over the file. Everything else\n * the repository wrote is kept, because its author knows more about their skill than a default\n * does.\n *\n * The defaults underneath exist because `doctor` validates every installed metadata.yaml, and a\n * file this command writes that that command reports as an error is the CLI disagreeing with\n * itself — in a file the user cannot fix, since the skill is somebody else's repository. Neither\n * is invented: the authorship is the repository's owner, and the usage line is the same words the\n * stack loader already writes for every skill reference it has nothing more specific for.\n */\nfunction externalSkillMetadata(\n install: ExternalSkillInstall,\n shipped: LocalSkillMetadata | null,\n): LocalSkillMetadata {\n return {\n author: skillAuthor(install.skill.repo),\n usageGuidance: `Use when working with ${install.skill.displayName}`,\n ...shipped,\n displayName: install.skill.displayName,\n slug: install.id,\n category: install.skill.categoryId,\n domain: install.domain,\n cliDescription: install.skill.description,\n custom: true,\n };\n}\n\n/**\n * Registers the skill on disk, where the next command will look for it.\n *\n * `forkedFrom` is stamped through the one writer that stamps it, because it is the package's\n * single answer to \"did the CLI put this directory here?\" — `uninstall` reads it to decide what it\n * may delete, and the producer reads it to decide what the round trip owns. A carried skill IS the\n * round trip's; a skill the user wrote by hand is not, and carries nothing.\n *\n * The repository AND the directory inside it, because this is the only record of where these bytes\n * came from. Sharing this installation has to rebuild the entry the payload carried, and a\n * repository alone cannot say which of its directories travelled — see {@link readCarriedSkills}.\n */\nasync function registerSkillOnDisk(install: ExternalSkillInstall): Promise<void> {\n const metadataPath = path.join(install.skillDir, STANDARD_FILES.METADATA_YAML);\n const shipped = await readLocalSkillMetadata(install.skillDir);\n\n await writeMetadataYaml(metadataPath, externalSkillMetadata(install, shipped));\n await injectForkedFromMetadata(\n install.skillDir,\n install.id,\n await computeFileHash(path.join(install.skillDir, STANDARD_FILES.SKILL_MD)),\n { source: repoRef(install.skill.repo), path: install.skill.path },\n );\n}\n\n/**\n * One installed skill directory the round trip owns, and what its install recorded about it.\n *\n * Read by the caller rather than here: whether a directory is the CLI's copy or the user's own\n * work is a question about ownership, which `installation-payload.ts` already asks of every entry\n * before anything else. This module answers only the next one — whether the bytes have to travel.\n */\nexport type OwnedSkillDir = { id: string; skillDir: string; provenance: ForkedFromMetadata };\n\n/**\n * What one installation carries rather than names.\n *\n * The same shape the mapper's other readings wear: what can travel, and the lines naming what\n * cannot. Both halves matter — a payload quietly missing content it could not read still mints an\n * id, and that id installs a configuration nobody chose.\n */\nexport type ContentReading = {\n /** Skill id -> the entry a receiver seats it from. Keyed exactly as the wire keys `external`. */\n external: Record<string, SeedExternalSkill>;\n /** One line per skill whose content this installation cannot carry, for the refusal. */\n uncarryable: string[];\n};\n\n/** What one owned directory turns out to carry: content, a reason it cannot, or nothing. */\ntype CarriedRead =\n | { carries: \"content\"; id: string; skill: SeedExternalSkill }\n | { carries: \"nothing\" }\n | { carries: \"unshareable\"; line: string };\n\nfunction unnameableRepoMessage(id: string): string {\n return `${id} travels inside a shared configuration, and this installation does not record the repository its content came from`;\n}\n\nfunction uncarryableContentMessage(id: string, reason: string): string {\n return `${id} travels inside a shared configuration, and ${reason}`;\n}\n\n/**\n * The inverse of {@link repoRef}: the repository a ref names, or nothing when it names none this\n * contract can state.\n *\n * Provenance is written as a ref because that is the form every other source in a configuration\n * wears; the wire wants GitHub's own `owner/name`. Any other ref is a repository this contract has\n * no word for, which is a refusal rather than something to guess at.\n */\nfunction repoFromRef(ref: string | undefined): string | undefined {\n if (ref === undefined || !ref.startsWith(GITHUB_SOURCE.GITHUB_PREFIX)) return undefined;\n return ref.slice(GITHUB_SOURCE.GITHUB_PREFIX.length);\n}\n\n/** Every file under the skill, keyed by its path relative to it — dotfiles and nesting included. */\nasync function readSkillTree(skillDir: string): Promise<SeedSkillTree> {\n const files = await glob(\"**/*\", skillDir, { dot: true });\n const entries = await Promise.all(\n files.map(async (file) => [file, await readFile(path.join(skillDir, file))] as const),\n );\n return Object.fromEntries(entries);\n}\n\n/**\n * Rebuilds the entry a carried skill arrived as, from the directory the install wrote.\n *\n * The bytes as they stand rather than as they arrived: the manifest was renamed to the id this\n * install recorded, and a user may have edited the skill since. Both are what is installed here,\n * and what is installed here is what a share carries.\n *\n * Validated against the contract's own schema rather than by rules restated here — that is what\n * makes the weight limit, the manifest requirement and every field's shape one definition rather\n * than two that can drift.\n */\nasync function readCarriedSkill({ id, skillDir, provenance }: OwnedSkillDir): Promise<CarriedRead> {\n // The directory is what marks a skill as one no catalogue can resolve. Without it there is a\n // catalogue behind this id, so the receiver installs it from there and the bytes stay home.\n if (provenance.path === undefined) return { carries: \"nothing\" };\n\n const repo = repoFromRef(provenance.source);\n if (repo === undefined) return { carries: \"unshareable\", line: unnameableRepoMessage(id) };\n\n const described = await readSkillMetadata(path.join(skillDir, STANDARD_FILES.METADATA_YAML));\n if (!described.usable) {\n return { carries: \"unshareable\", line: uncarryableContentMessage(id, described.reason) };\n }\n\n const parsed = seedExternalSkillSchema.safeParse({\n displayName: described.metadata.displayName,\n description: described.metadata.cliDescription,\n categoryId: described.metadata.category,\n repo,\n path: provenance.path,\n files: await readSkillTree(skillDir),\n });\n if (!parsed.success) {\n const reason = parsed.error.issues.map((issue) => issue.message).join(\"; \");\n return { carries: \"unshareable\", line: uncarryableContentMessage(id, reason) };\n }\n\n return { carries: \"content\", id, skill: parsed.data };\n}\n\n/**\n * What a set of owned skill directories carries, and what it cannot.\n *\n * The mirror of {@link registerExternalSkills}: that seats an entry a payload carried and writes\n * its bytes, this reads those bytes back into the entry a payload carries. Every other id in a\n * configuration is resolved by the receiver against a catalogue it already has, so its content has\n * nothing to add; an added skill answers to no catalogue in either direction, and a share that\n * named it without carrying it would mint an id that installs a configuration missing it.\n */\nexport async function readCarriedSkills(owned: OwnedSkillDir[]): Promise<ContentReading> {\n const reads = await Promise.all(owned.map(readCarriedSkill));\n\n return {\n external: Object.fromEntries(\n reads.flatMap((read) => (read.carries === \"content\" ? [[read.id, read.skill]] : [])),\n ),\n uncarryable: reads.flatMap((read) => (read.carries === \"unshareable\" ? [read.line] : [])),\n };\n}\n","import { seedPayloadSchema, type SeedPayload } from \"@workspace/matrix/seed\";\n\n/** The config store behind agentsinc.sh. Overridable so tests never touch the network. */\nexport const SEED_API_URL = process.env.AGENTS_INC_API_URL ?? \"https://api.agentsinc.sh\";\n\n/**\n * Identifies this fetch as the CLI rather than a browser.\n *\n * `GET /configs/:id` is the only place either side can observe a config being *installed* rather\n * than merely built, and the worker can only separate that from someone opening a share link if\n * we say so. Without this the conversion signal does not exist.\n */\nexport const SEED_USER_AGENT = \"agents-inc-cli\";\n\nexport type FetchSeedResult = { ok: true; payload: SeedPayload } | { ok: false; error: string };\n\n/**\n * Fetches and validates a shared configuration.\n *\n * Every failure is a message rather than a throw: this runs before anything has been written, so\n * there is nothing to roll back and the caller's job is to explain rather than recover.\n */\nexport async function fetchSeedConfig(id: string): Promise<FetchSeedResult> {\n const url = `${SEED_API_URL}/configs/${encodeURIComponent(id)}`;\n\n let response: Response;\n try {\n response = await fetch(url, {\n headers: { accept: \"application/json\", \"user-agent\": SEED_USER_AGENT },\n });\n } catch {\n return { ok: false, error: `Could not reach ${SEED_API_URL} — check your connection.` };\n }\n\n if (response.status === 404) {\n return { ok: false, error: `No configuration found for id '${id}'.` };\n }\n if (!response.ok) {\n return { ok: false, error: `Fetching configuration failed (HTTP ${response.status}).` };\n }\n\n let body: unknown;\n try {\n body = await response.json();\n } catch {\n return { ok: false, error: \"The configuration store returned something that is not JSON.\" };\n }\n\n const parsed = seedPayloadSchema.safeParse(body);\n if (!parsed.success) {\n // Names the remedy rather than a cause it cannot observe. The payload was validated by the\n // store on the way IN, so a stored payload that no longer parses means the contract moved\n // underneath it — and every way that happens lands in this one `safeParse`: an id minted\n // before a version bump, an id minted after one, and a body that is simply broken. The CLI\n // cannot tell them apart, and the one thing true of all three is that re-sharing fixes it,\n // because the store content-addresses an id and a fresh share mints one under this version.\n return {\n ok: false,\n error: `Configuration '${id}' is not in a format this version of the CLI can install. Shared ids are never migrated — re-share the configuration to mint a current one, or update the CLI if that id came from a newer version.`,\n };\n }\n\n return { ok: true, payload: parsed.data };\n}\n"],"mappings":"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","names":[]}
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getDomainDisplayName
4
- } from "./chunk-5OKLCVM4.js";
4
+ } from "./chunk-KVVXINCJ.js";
5
5
  import {
6
6
  useMeasuredHeight
7
7
  } from "./chunk-TSIU7DME.js";
8
8
  import {
9
9
  useRowScroll
10
- } from "./chunk-WFRTHH3A.js";
10
+ } from "./chunk-TZRXWFF5.js";
11
11
  import {
12
12
  KEY_SPACE
13
13
  } from "./chunk-WTJ3AY33.js";
@@ -17,41 +17,40 @@ import {
17
17
  deriveScopeBadges,
18
18
  formatScopeTag,
19
19
  useWizardStore
20
- } from "./chunk-F5T6TV4Z.js";
20
+ } from "./chunk-IK6YOXZH.js";
21
21
  import {
22
+ isAgentName,
22
23
  toTitleCase
23
- } from "./chunk-WCAUUBHO.js";
24
- import {
25
- isAgentName
26
- } from "./chunk-XAXJWXI4.js";
27
- import {
28
- matrix
29
- } from "./chunk-RWOYPOMR.js";
24
+ } from "./chunk-3PJLLBUS.js";
30
25
  import {
26
+ matrix,
31
27
  typedKeys
32
- } from "./chunk-VASLW636.js";
28
+ } from "./chunk-7Z4N4JWQ.js";
33
29
  import {
34
30
  CLI_COLORS,
35
31
  UI_SYMBOLS
36
- } from "./chunk-IO5YBKHV.js";
32
+ } from "./chunk-G6G2FT2G.js";
37
33
 
38
34
  // src/cli/components/wizard/step-agents.tsx
39
35
  import { groupBy, unique } from "remeda";
40
36
  import { Box, Text, useInput } from "ink";
41
37
  import React, { useMemo, useState } from "react";
42
38
  import { jsx, jsxs } from "react/jsx-runtime";
39
+ function agentIdsNamedByStacks(matrix2) {
40
+ return unique(matrix2.suggestedStacks.flatMap((stack) => typedKeys(stack.skills)));
41
+ }
42
+ function selectableSourceAgents(matrix2) {
43
+ return agentIdsNamedByStacks(matrix2).filter(isAgentName).filter((agentName) => !BUILT_IN_AGENT_IDS.has(agentName));
44
+ }
43
45
  function buildAgentGroups(matrix2) {
44
- const customAgentIds = unique(
45
- matrix2.suggestedStacks.flatMap((stack) => typedKeys(stack.skills))
46
- ).filter((agentName) => !BUILT_IN_AGENT_IDS.has(agentName));
46
+ const customAgentIds = selectableSourceAgents(matrix2);
47
47
  if (customAgentIds.length === 0) return BUILT_IN_AGENT_GROUPS;
48
48
  const customItems = customAgentIds.map((agentId) => {
49
- const explicitDomain = isAgentName(agentId) ? matrix2.agentDefinedDomains?.[agentId] : void 0;
49
+ const explicitDomain = matrix2.agentDefinedDomains?.[agentId];
50
50
  const domainKey = explicitDomain ?? (agentId.split("-")[0] || "custom");
51
51
  return {
52
52
  groupLabel: getDomainDisplayName(domainKey),
53
53
  item: {
54
- // Boundary cast: custom agent names from marketplace stacks are not in the AgentName union
55
54
  id: agentId,
56
55
  label: toTitleCase(agentId),
57
56
  description: "Custom agent"
@@ -228,4 +227,4 @@ var StepAgents = () => {
228
227
  export {
229
228
  StepAgents
230
229
  };
231
- //# sourceMappingURL=chunk-CLFHC2DK.js.map
230
+ //# sourceMappingURL=chunk-OQB2CKBT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/wizard/step-agents.tsx"],"sourcesContent":["import { groupBy, unique } from \"remeda\";\nimport { Box, Text, useInput } from \"ink\";\nimport {\n BUILT_IN_AGENT_GROUPS,\n BUILT_IN_AGENT_IDS,\n deriveScopeBadges,\n formatScopeTag,\n} from \"../../lib/wizard/index.js\";\nimport type { AgentGroup, AgentItem } from \"../../lib/wizard/index.js\";\nimport React, { useMemo, useState } from \"react\";\nimport { CLI_COLORS, UI_SYMBOLS } from \"../../consts.js\";\nimport { matrix } from \"../../lib/matrix/matrix-provider.js\";\nimport { useWizardStore } from \"../../stores/wizard-store.js\";\nimport type { AgentName, MergedSkillsMatrix } from \"../../types/index.js\";\nimport { isAgentName } from \"../../utils/type-guards.js\";\nimport { typedKeys } from \"../../utils/typed-object.js\";\nimport { toTitleCase } from \"../../utils/string.js\";\nimport { useMeasuredHeight } from \"../hooks/use-measured-height.js\";\nimport { useRowScroll } from \"../hooks/use-row-scroll.js\";\nimport { KEY_SPACE } from \"./hotkeys.js\";\nimport { getDomainDisplayName } from \"./utils.js\";\n\ntype FocusId = AgentName | \"continue\";\n\ntype ListRow =\n { type: \"header\"; label: string } | { type: \"spacer\" } | { type: \"agent\"; agent: AgentItem };\n\n/**\n * The sub-agents a loaded source's stacks name, as the strings a stack file spells them.\n *\n * `typedKeys` types them `AgentName` because `ResolvedStack.skills` is keyed by it, but the keys\n * arrive from a marketplace's own YAML and nothing between there and here narrows them — which\n * is exactly the claim the filter below has to test.\n */\nfunction agentIdsNamedByStacks(matrix: MergedSkillsMatrix): string[] {\n return unique(matrix.suggestedStacks.flatMap((stack) => typedKeys(stack.skills)));\n}\n\n/**\n * The rows a loaded source adds to the built-in grid.\n *\n * Narrowed with `isAgentName` and deliberately NOT cast to it. A row here is a name the user can\n * put into `config.agents`, and only the CLI's own `src/agents/` declares a sub-agent a compile\n * pass can honour — that directory is the whole of the roster (owner ruling 2026-08-21) and is\n * what `AGENT_NAMES` is generated from. So a stack naming an agent the CLI does not ship\n * contributes nothing: offering it a row wrote a name that reached `AgentName`,\n * `SelectedAgentName` and `ProjectAgentName` alike and then left `compile` with no definition to\n * compile.\n */\nfunction selectableSourceAgents(matrix: MergedSkillsMatrix): AgentName[] {\n return agentIdsNamedByStacks(matrix)\n .filter(isAgentName)\n .filter((agentName) => !BUILT_IN_AGENT_IDS.has(agentName));\n}\n\nfunction buildAgentGroups(matrix: MergedSkillsMatrix): AgentGroup[] {\n const customAgentIds = selectableSourceAgents(matrix);\n\n if (customAgentIds.length === 0) return BUILT_IN_AGENT_GROUPS;\n\n // Group custom agents by explicit domain (from metadata.yaml) or kebab prefix fallback\n const customItems = customAgentIds.map((agentId) => {\n const explicitDomain = matrix.agentDefinedDomains?.[agentId];\n const domainKey = explicitDomain ?? (agentId.split(\"-\")[0] || \"custom\");\n return {\n groupLabel: getDomainDisplayName(domainKey),\n item: {\n id: agentId,\n label: toTitleCase(agentId),\n description: \"Custom agent\",\n },\n };\n });\n const customGroups: AgentGroup[] = Object.entries(groupBy(customItems, (c) => c.groupLabel)).map(\n ([label, entries]) => ({ label, items: entries.map((entry) => entry.item) }),\n );\n\n return [...BUILT_IN_AGENT_GROUPS, ...customGroups];\n}\n\nfunction buildFlatRows(groups: AgentGroup[]): ListRow[] {\n return groups.flatMap((group, groupIndex): ListRow[] => [\n ...(groupIndex > 0 ? [{ type: \"spacer\" as const }] : []),\n { type: \"header\" as const, label: group.label },\n ...group.items.map((agent): ListRow => ({ type: \"agent\", agent })),\n ]);\n}\n\nfunction buildFocusableIds(groups: AgentGroup[]): FocusId[] {\n return [...groups.flatMap((group) => group.items.map((a) => a.id)), \"continue\"];\n}\n\n/**\n * Asserting row lookup. `buildFocusableIds` always appends \"continue\", so the\n * list is never empty and every caller below wraps within it — a miss means the\n * wrap arithmetic is wrong, which is worth a diagnostic rather than a focus that\n * silently stops moving.\n */\nfunction focusableIdAt(ids: FocusId[], index: number): FocusId {\n const id = ids[index];\n if (id === undefined) throw new Error(`No focusable row at index ${index} of ${ids.length}`);\n return id;\n}\n\nexport const StepAgents: React.FC = () => {\n const selectedAgents = useWizardStore((s) => s.selectedAgents);\n const agentConfigs = useWizardStore((s) => s.agentConfigs);\n\n const agentGroups = useMemo(() => buildAgentGroups(matrix), []);\n const flatRows = useMemo(() => buildFlatRows(agentGroups), [agentGroups]);\n const focusableIds = useMemo(() => buildFocusableIds(agentGroups), [agentGroups]);\n\n const [focusedId, setFocusedId] = useState<FocusId>(() => {\n const stored = useWizardStore.getState().focusedAgentId;\n return stored && focusableIds.includes(stored) ? stored : focusableIdAt(focusableIds, 0);\n });\n const { ref: listRef, measuredHeight: listHeight } = useMeasuredHeight();\n\n const focusedRowIndex =\n focusedId !== \"continue\"\n ? flatRows.findIndex((row) => row.type === \"agent\" && row.agent.id === focusedId)\n : -1;\n // The Agents grid clips silently: `useRowScroll` exposes no hidden-line counts\n // and this view renders no `ScrollAffordance`, by owner decision — see the\n // hook's doc comment.\n const { scrollEnabled, scrollTop } = useRowScroll({\n focusedIndex: Math.max(0, focusedRowIndex),\n itemCount: flatRows.length,\n availableHeight: listHeight,\n });\n\n useInput((input, key) => {\n if (key.escape) {\n useWizardStore.getState().goBack();\n return;\n }\n\n const currentIdx = focusableIds.indexOf(focusedId);\n\n if (key.upArrow || input === \"k\") {\n const nextIdx = currentIdx <= 0 ? focusableIds.length - 1 : currentIdx - 1;\n setFocusedId(focusableIdAt(focusableIds, nextIdx));\n return;\n }\n\n if (key.downArrow || input === \"j\") {\n const nextIdx = currentIdx >= focusableIds.length - 1 ? 0 : currentIdx + 1;\n setFocusedId(focusableIdAt(focusableIds, nextIdx));\n return;\n }\n\n if (key.return) {\n useWizardStore.getState().setStep(\"confirm\");\n return;\n }\n\n if (input === KEY_SPACE && focusedId !== \"continue\") {\n useWizardStore.getState().toggleAgent(focusedId);\n }\n });\n\n // Sync focusedAgentId so the wizard-level S key handler can toggle agent scope\n React.useEffect(() => {\n useWizardStore.getState().setFocusedAgentId(focusedId === \"continue\" ? null : focusedId);\n }, [focusedId]);\n\n const focusedGroupLabel = agentGroups.find((g) => g.items.some((a) => a.id === focusedId))?.label;\n\n const selectedCount = selectedAgents.length;\n const continueLabel =\n selectedCount > 0 ? `Continue with ${selectedCount} agent(s)` : \"Continue without agents\";\n\n const isContinueFocused = focusedId === \"continue\";\n\n const rowElements = flatRows.map((row, index) => {\n switch (row.type) {\n case \"header\":\n return (\n <Box key={`header-${row.label}`} flexShrink={0}>\n {row.label === focusedGroupLabel ? (\n <Text\n color={CLI_COLORS.WHITE}\n backgroundColor={CLI_COLORS.LABEL_BG}\n >{` ${row.label} `}</Text>\n ) : (\n <Text dimColor bold>\n {\" \"}\n {row.label}\n </Text>\n )}\n </Box>\n );\n case \"spacer\":\n return (\n <Box key={`spacer-${index}`} flexShrink={0}>\n <Text> </Text>\n </Box>\n );\n case \"agent\": {\n const isFocused = row.agent.id === focusedId;\n const isSelected = selectedAgents.includes(row.agent.id);\n const checkbox = isSelected ? \"[\\u2713]\" : \"[ ]\";\n const pointer = isFocused ? UI_SYMBOLS.CHEVRON : UI_SYMBOLS.CHEVRON_SPACER;\n const agentConfig = agentConfigs.find((ac) => ac.name === row.agent.id && !ac.excluded);\n const excludedConfig = agentConfigs.find((ac) => ac.name === row.agent.id && ac.excluded);\n const badges = deriveScopeBadges(agentConfig, excludedConfig);\n const scope = badges.scope ?? \"global\";\n const secondaryScope = badges.secondaryScope;\n return (\n <Box key={row.agent.id} flexShrink={0}>\n <Text>\n <Text {...(isFocused && { color: CLI_COLORS.PRIMARY })}>{pointer}</Text>\n <Text\n {...((isSelected || isFocused) && { color: CLI_COLORS.PRIMARY })}\n bold={isFocused}\n >\n {\" \"}\n {checkbox}{\" \"}\n </Text>\n <Text color={scope === \"global\" ? CLI_COLORS.WARNING : CLI_COLORS.TOAST_BG}>\n {formatScopeTag(scope)}\n </Text>\n {secondaryScope && (\n <Text\n color={secondaryScope === \"global\" ? CLI_COLORS.WARNING : CLI_COLORS.TOAST_BG}\n >\n {formatScopeTag(secondaryScope)}\n </Text>\n )}\n <Text\n {...((isSelected || isFocused) && { color: CLI_COLORS.PRIMARY })}\n bold={isFocused}\n >\n {\" \"}\n {row.agent.label}\n </Text>\n <Text dimColor> {row.agent.description}</Text>\n </Text>\n </Box>\n );\n }\n default: {\n const _exhaustive: never = row;\n return _exhaustive;\n }\n }\n });\n\n return (\n <Box flexDirection=\"column\" width=\"100%\" flexGrow={1} flexBasis={0}>\n <Box ref={listRef} flexDirection=\"column\" flexGrow={1} flexBasis={0}>\n <Box\n flexDirection=\"column\"\n flexGrow={1}\n {...(scrollEnabled && { overflow: \"hidden\" as const })}\n >\n <Box\n flexDirection=\"column\"\n marginTop={scrollTop > 0 ? -scrollTop : 0}\n {...(scrollEnabled && { flexShrink: 0 })}\n >\n {rowElements}\n </Box>\n </Box>\n </Box>\n\n <Text {...(isContinueFocused && { color: CLI_COLORS.PRIMARY })} bold={isContinueFocused}>\n {isContinueFocused ? UI_SYMBOLS.CHEVRON : UI_SYMBOLS.CHEVRON_SPACER} {\"\\u2192\"}{\" \"}\n {continueLabel}\n </Text>\n </Box>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,SAAS,cAAc;AAChC,SAAS,KAAK,MAAM,gBAAgB;AAQpC,OAAO,SAAS,SAAS,gBAAgB;AA2K3B,cAKA,YALA;AAlJd,SAAS,sBAAsBA,SAAsC;AACnE,SAAO,OAAOA,QAAO,gBAAgB,QAAQ,CAAC,UAAU,UAAU,MAAM,MAAM,CAAC,CAAC;AAClF;AAaA,SAAS,uBAAuBA,SAAyC;AACvE,SAAO,sBAAsBA,OAAM,EAChC,OAAO,WAAW,EAClB,OAAO,CAAC,cAAc,CAAC,mBAAmB,IAAI,SAAS,CAAC;AAC7D;AAEA,SAAS,iBAAiBA,SAA0C;AAClE,QAAM,iBAAiB,uBAAuBA,OAAM;AAEpD,MAAI,eAAe,WAAW,EAAG,QAAO;AAGxC,QAAM,cAAc,eAAe,IAAI,CAAC,YAAY;AAClD,UAAM,iBAAiBA,QAAO,sBAAsB,OAAO;AAC3D,UAAM,YAAY,mBAAmB,QAAQ,MAAM,GAAG,EAAE,CAAC,KAAK;AAC9D,WAAO;AAAA,MACL,YAAY,qBAAqB,SAAS;AAAA,MAC1C,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,OAAO,YAAY,OAAO;AAAA,QAC1B,aAAa;AAAA,MACf;AAAA,IACF;AAAA,EACF,CAAC;AACD,QAAM,eAA6B,OAAO,QAAQ,QAAQ,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE;AAAA,IAC3F,CAAC,CAAC,OAAO,OAAO,OAAO,EAAE,OAAO,OAAO,QAAQ,IAAI,CAAC,UAAU,MAAM,IAAI,EAAE;AAAA,EAC5E;AAEA,SAAO,CAAC,GAAG,uBAAuB,GAAG,YAAY;AACnD;AAEA,SAAS,cAAc,QAAiC;AACtD,SAAO,OAAO,QAAQ,CAAC,OAAO,eAA0B;AAAA,IACtD,GAAI,aAAa,IAAI,CAAC,EAAE,MAAM,SAAkB,CAAC,IAAI,CAAC;AAAA,IACtD,EAAE,MAAM,UAAmB,OAAO,MAAM,MAAM;AAAA,IAC9C,GAAG,MAAM,MAAM,IAAI,CAAC,WAAoB,EAAE,MAAM,SAAS,MAAM,EAAE;AAAA,EACnE,CAAC;AACH;AAEA,SAAS,kBAAkB,QAAiC;AAC1D,SAAO,CAAC,GAAG,OAAO,QAAQ,CAAC,UAAU,MAAM,MAAM,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,UAAU;AAChF;AAQA,SAAS,cAAc,KAAgB,OAAwB;AAC7D,QAAM,KAAK,IAAI,KAAK;AACpB,MAAI,OAAO,OAAW,OAAM,IAAI,MAAM,6BAA6B,KAAK,OAAO,IAAI,MAAM,EAAE;AAC3F,SAAO;AACT;AAEO,IAAM,aAAuB,MAAM;AACxC,QAAM,iBAAiB,eAAe,CAAC,MAAM,EAAE,cAAc;AAC7D,QAAM,eAAe,eAAe,CAAC,MAAM,EAAE,YAAY;AAEzD,QAAM,cAAc,QAAQ,MAAM,iBAAiB,MAAM,GAAG,CAAC,CAAC;AAC9D,QAAM,WAAW,QAAQ,MAAM,cAAc,WAAW,GAAG,CAAC,WAAW,CAAC;AACxE,QAAM,eAAe,QAAQ,MAAM,kBAAkB,WAAW,GAAG,CAAC,WAAW,CAAC;AAEhF,QAAM,CAAC,WAAW,YAAY,IAAI,SAAkB,MAAM;AACxD,UAAM,SAAS,eAAe,SAAS,EAAE;AACzC,WAAO,UAAU,aAAa,SAAS,MAAM,IAAI,SAAS,cAAc,cAAc,CAAC;AAAA,EACzF,CAAC;AACD,QAAM,EAAE,KAAK,SAAS,gBAAgB,WAAW,IAAI,kBAAkB;AAEvE,QAAM,kBACJ,cAAc,aACV,SAAS,UAAU,CAAC,QAAQ,IAAI,SAAS,WAAW,IAAI,MAAM,OAAO,SAAS,IAC9E;AAIN,QAAM,EAAE,eAAe,UAAU,IAAI,aAAa;AAAA,IAChD,cAAc,KAAK,IAAI,GAAG,eAAe;AAAA,IACzC,WAAW,SAAS;AAAA,IACpB,iBAAiB;AAAA,EACnB,CAAC;AAED,WAAS,CAAC,OAAO,QAAQ;AACvB,QAAI,IAAI,QAAQ;AACd,qBAAe,SAAS,EAAE,OAAO;AACjC;AAAA,IACF;AAEA,UAAM,aAAa,aAAa,QAAQ,SAAS;AAEjD,QAAI,IAAI,WAAW,UAAU,KAAK;AAChC,YAAM,UAAU,cAAc,IAAI,aAAa,SAAS,IAAI,aAAa;AACzE,mBAAa,cAAc,cAAc,OAAO,CAAC;AACjD;AAAA,IACF;AAEA,QAAI,IAAI,aAAa,UAAU,KAAK;AAClC,YAAM,UAAU,cAAc,aAAa,SAAS,IAAI,IAAI,aAAa;AACzE,mBAAa,cAAc,cAAc,OAAO,CAAC;AACjD;AAAA,IACF;AAEA,QAAI,IAAI,QAAQ;AACd,qBAAe,SAAS,EAAE,QAAQ,SAAS;AAC3C;AAAA,IACF;AAEA,QAAI,UAAU,aAAa,cAAc,YAAY;AACnD,qBAAe,SAAS,EAAE,YAAY,SAAS;AAAA,IACjD;AAAA,EACF,CAAC;AAGD,QAAM,UAAU,MAAM;AACpB,mBAAe,SAAS,EAAE,kBAAkB,cAAc,aAAa,OAAO,SAAS;AAAA,EACzF,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,oBAAoB,YAAY,KAAK,CAAC,MAAM,EAAE,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,SAAS,CAAC,GAAG;AAE5F,QAAM,gBAAgB,eAAe;AACrC,QAAM,gBACJ,gBAAgB,IAAI,iBAAiB,aAAa,cAAc;AAElE,QAAM,oBAAoB,cAAc;AAExC,QAAM,cAAc,SAAS,IAAI,CAAC,KAAK,UAAU;AAC/C,YAAQ,IAAI,MAAM;AAAA,MAChB,KAAK;AACH,eACE,oBAAC,OAAgC,YAAY,GAC1C,cAAI,UAAU,oBACb;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,WAAW;AAAA,YAClB,iBAAiB,WAAW;AAAA,YAC5B,cAAI,IAAI,KAAK;AAAA;AAAA,QAAI,IAEnB,qBAAC,QAAK,UAAQ,MAAC,MAAI,MAChB;AAAA;AAAA,UACA,IAAI;AAAA,WACP,KAVM,UAAU,IAAI,KAAK,EAY7B;AAAA,MAEJ,KAAK;AACH,eACE,oBAAC,OAA4B,YAAY,GACvC,8BAAC,QAAK,eAAC,KADC,UAAU,KAAK,EAEzB;AAAA,MAEJ,KAAK,SAAS;AACZ,cAAM,YAAY,IAAI,MAAM,OAAO;AACnC,cAAM,aAAa,eAAe,SAAS,IAAI,MAAM,EAAE;AACvD,cAAM,WAAW,aAAa,aAAa;AAC3C,cAAM,UAAU,YAAY,WAAW,UAAU,WAAW;AAC5D,cAAM,cAAc,aAAa,KAAK,CAAC,OAAO,GAAG,SAAS,IAAI,MAAM,MAAM,CAAC,GAAG,QAAQ;AACtF,cAAM,iBAAiB,aAAa,KAAK,CAAC,OAAO,GAAG,SAAS,IAAI,MAAM,MAAM,GAAG,QAAQ;AACxF,cAAM,SAAS,kBAAkB,aAAa,cAAc;AAC5D,cAAM,QAAQ,OAAO,SAAS;AAC9B,cAAM,iBAAiB,OAAO;AAC9B,eACE,oBAAC,OAAuB,YAAY,GAClC,+BAAC,QACC;AAAA,8BAAC,QAAM,GAAI,aAAa,EAAE,OAAO,WAAW,QAAQ,GAAK,mBAAQ;AAAA,UACjE;AAAA,YAAC;AAAA;AAAA,cACE,IAAK,cAAc,cAAc,EAAE,OAAO,WAAW,QAAQ;AAAA,cAC9D,MAAM;AAAA,cAEL;AAAA;AAAA,gBACA;AAAA,gBAAU;AAAA;AAAA;AAAA,UACb;AAAA,UACA,oBAAC,QAAK,OAAO,UAAU,WAAW,WAAW,UAAU,WAAW,UAC/D,yBAAe,KAAK,GACvB;AAAA,UACC,kBACC;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,mBAAmB,WAAW,WAAW,UAAU,WAAW;AAAA,cAEpE,yBAAe,cAAc;AAAA;AAAA,UAChC;AAAA,UAEF;AAAA,YAAC;AAAA;AAAA,cACE,IAAK,cAAc,cAAc,EAAE,OAAO,WAAW,QAAQ;AAAA,cAC9D,MAAM;AAAA,cAEL;AAAA;AAAA,gBACA,IAAI,MAAM;AAAA;AAAA;AAAA,UACb;AAAA,UACA,qBAAC,QAAK,UAAQ,MAAC;AAAA;AAAA,YAAE,IAAI,MAAM;AAAA,aAAY;AAAA,WACzC,KA5BQ,IAAI,MAAM,EA6BpB;AAAA,MAEJ;AAAA,MACA,SAAS;AACP,cAAM,cAAqB;AAC3B,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF,CAAC;AAED,SACE,qBAAC,OAAI,eAAc,UAAS,OAAM,QAAO,UAAU,GAAG,WAAW,GAC/D;AAAA,wBAAC,OAAI,KAAK,SAAS,eAAc,UAAS,UAAU,GAAG,WAAW,GAChE;AAAA,MAAC;AAAA;AAAA,QACC,eAAc;AAAA,QACd,UAAU;AAAA,QACT,GAAI,iBAAiB,EAAE,UAAU,SAAkB;AAAA,QAEpD;AAAA,UAAC;AAAA;AAAA,YACC,eAAc;AAAA,YACd,WAAW,YAAY,IAAI,CAAC,YAAY;AAAA,YACvC,GAAI,iBAAiB,EAAE,YAAY,EAAE;AAAA,YAErC;AAAA;AAAA,QACH;AAAA;AAAA,IACF,GACF;AAAA,IAEA,qBAAC,QAAM,GAAI,qBAAqB,EAAE,OAAO,WAAW,QAAQ,GAAI,MAAM,mBACnE;AAAA,0BAAoB,WAAW,UAAU,WAAW;AAAA,MAAe;AAAA,MAAE;AAAA,MAAU;AAAA,MAC/E;AAAA,OACH;AAAA,KACF;AAEJ;","names":["matrix"]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CLI_COLORS
4
- } from "./chunk-IO5YBKHV.js";
4
+ } from "./chunk-G6G2FT2G.js";
5
5
 
6
6
  // src/cli/components/wizard/wizard-tabs.tsx
7
7
  import { Box, Text } from "ink";
@@ -18,9 +18,6 @@ var WIZARD_STEP_LABELS = {
18
18
  function wizardTabsFor(flow) {
19
19
  return flow.map((id) => ({ id, label: WIZARD_STEP_LABELS[id] }));
20
20
  }
21
- function formatStepLabel(stepId) {
22
- return WIZARD_STEP_LABELS[stepId];
23
- }
24
21
  var getStepState = (stepId, currentStep, completedSteps, skippedSteps) => {
25
22
  if (completedSteps.includes(stepId)) return "completed";
26
23
  if (stepId === currentStep) return "current";
@@ -166,7 +163,6 @@ var WizardTabs = ({
166
163
 
167
164
  export {
168
165
  wizardTabsFor,
169
- formatStepLabel,
170
166
  WizardTabs
171
167
  };
172
- //# sourceMappingURL=chunk-HXKOIWLU.js.map
168
+ //# sourceMappingURL=chunk-P6OKOH62.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/wizard/wizard-tabs.tsx"],"sourcesContent":["import { Box, Text } from \"ink\";\nimport React from \"react\";\nimport { CLI_COLORS } from \"../../consts.js\";\nimport type { WizardStep } from \"../../stores/wizard-store.js\";\nimport type { Domain } from \"../../types/index.js\";\n\ntype WizardTabStep = {\n id: WizardStep;\n label: string;\n};\n\nexport type TabDropdownItem = {\n id: string;\n label: string;\n};\n\nexport type TabDropdownProps = {\n items: TabDropdownItem[];\n activeId?: string | undefined;\n isSubNav?: boolean | undefined;\n inline?: boolean | undefined;\n};\n\nexport type DomainNavProps = {\n domains: Domain[];\n activeDomain: Domain;\n getDomainLabel: (domain: Domain) => string;\n};\n\nexport type WizardTabsProps = {\n steps: WizardTabStep[];\n currentStep: WizardStep;\n completedSteps: WizardStep[];\n skippedSteps?: WizardStep[] | undefined;\n version?: string | undefined;\n domainNav?: DomainNavProps | undefined;\n dropdowns?: Partial<Record<WizardStep, TabDropdownProps>>;\n};\n\n/**\n * Display label per wizard step. Step ORDER has a single source: WIZARD_STEP_ORDER.\n *\n * The `sources` step is where a user picks where each skill comes from, so that is what\n * its tab is called. `e2e/pages/constants.ts` `WIZARD_TAB_LABELS` mirrors the whole set,\n * so a label moves in both places or in neither.\n */\nconst WIZARD_STEP_LABELS = {\n stack: \"Stack\",\n domains: \"Domains\",\n build: \"Skills\",\n sources: \"Sources\",\n agents: \"Agents\",\n confirm: \"Confirm\",\n} as const satisfies Record<WizardStep, string>;\n\n/**\n * The tabs for a step flow — one per step that flow runs, in its order.\n *\n * A function of the flow rather than a constant over `WIZARD_STEP_ORDER`: a\n * source that ships no stacks gives the wizard no stack step (see\n * `getActiveStepFlow`), and a bar drawn from every step the wizard HAS would\n * paint a Stack tab for a step this run never opens.\n */\nexport function wizardTabsFor(flow: WizardStep[]): WizardTabStep[] {\n return flow.map((id) => ({ id, label: WIZARD_STEP_LABELS[id] }));\n}\n\ntype StepState = \"completed\" | \"current\" | \"pending\" | \"skipped\";\n\nconst getStepState = (\n stepId: WizardStep,\n currentStep: WizardStep,\n completedSteps: WizardStep[],\n skippedSteps: WizardStep[],\n): StepState => {\n if (completedSteps.includes(stepId)) return \"completed\";\n if (stepId === currentStep) return \"current\";\n if (skippedSteps.includes(stepId)) return \"skipped\";\n return \"pending\";\n};\n\ntype TabProps = {\n step: WizardTabStep;\n state: StepState;\n};\n\nconst Tab: React.FC<TabProps> = ({ step, state }) => {\n const label = step.label;\n\n switch (state) {\n case \"current\":\n return (\n <Text color={CLI_COLORS.BLACK} backgroundColor={CLI_COLORS.WARNING} bold>\n {\" \"}\n {label}{\" \"}\n </Text>\n );\n case \"completed\":\n return <Text>{label}</Text>;\n case \"skipped\":\n return <Text dimColor>{label}</Text>;\n case \"pending\":\n return <Text color={CLI_COLORS.UNFOCUSED}>{label}</Text>;\n default: {\n const _exhaustive: never = state;\n return _exhaustive;\n }\n }\n};\n\nconst TabDropdown: React.FC<TabDropdownProps> = ({ items, activeId, isSubNav = false, inline }) => (\n <Box\n {...(inline ? {} : { position: \"absolute\" as const, marginTop: 2 })}\n flexDirection=\"row\"\n columnGap={0}\n paddingLeft={1}\n paddingRight={1}\n borderStyle=\"single\"\n borderColor=\"blackBright\"\n flexWrap=\"wrap\"\n >\n {items.map((item) => {\n const isActive = item.id === activeId;\n return (\n <Text\n key={item.id}\n dimColor={!isSubNav}\n bold={isSubNav && isActive}\n color={isActive ? CLI_COLORS.WARNING : CLI_COLORS.UNFOCUSED}\n >\n {\" \"}\n {item.label}{\" \"}\n </Text>\n );\n })}\n </Box>\n);\n\nconst DomainNav: React.FC<DomainNavProps & { inline?: boolean }> = ({\n domains,\n activeDomain,\n getDomainLabel,\n inline,\n}) => (\n <TabDropdown\n items={domains.map((d) => ({ id: d, label: getDomainLabel(d) }))}\n activeId={activeDomain}\n isSubNav\n inline={inline}\n />\n);\n\nconst DOMAIN_NAV_CHAR_THRESHOLD = 25;\n\nconst getStepJustifyContent = (stepId: WizardStep): \"flex-start\" | \"flex-end\" | \"center\" => {\n if (stepId === \"stack\") return \"flex-start\";\n if (stepId === \"confirm\") return \"flex-end\";\n return \"center\";\n};\n\nconst isDomainNavOverThreshold = (domainNav: DomainNavProps): boolean =>\n domainNav.domains.reduce((sum, d) => sum + domainNav.getDomainLabel(d).length, 0) >\n DOMAIN_NAV_CHAR_THRESHOLD;\n\nexport const WizardTabs: React.FC<WizardTabsProps> = ({\n steps,\n currentStep,\n completedSteps,\n skippedSteps = [],\n version,\n domainNav,\n dropdowns,\n}) => {\n const shouldHoistDomainNav = domainNav ? isDomainNavOverThreshold(domainNav) : false;\n\n return (\n <>\n <Box\n flexDirection=\"row\"\n columnGap={2}\n borderColor=\"blackBright\"\n borderStyle=\"single\"\n paddingX={1}\n alignItems=\"center\"\n marginBottom={shouldHoistDomainNav ? 0 : 3}\n >\n {steps.map((step) => {\n const state = getStepState(step.id, currentStep, completedSteps, skippedSteps);\n\n if (step.id === \"build\" && domainNav && !shouldHoistDomainNav) {\n return (\n <Box\n key={step.id}\n position=\"relative\"\n display=\"flex\"\n justifyContent={getStepJustifyContent(step.id)}\n >\n <Tab step={step} state={state} />\n <DomainNav {...domainNav} />\n </Box>\n );\n }\n\n // Generic dropdown for any step\n const dropdown = dropdowns?.[step.id];\n if (dropdown) {\n return (\n <Box\n key={step.id}\n position=\"relative\"\n display=\"flex\"\n justifyContent={getStepJustifyContent(step.id)}\n >\n <Tab step={step} state={state} />\n <TabDropdown {...dropdown} />\n </Box>\n );\n }\n\n return <Tab key={step.id} step={step} state={state} />;\n })}\n <Box flexGrow={1} justifyContent=\"flex-end\">\n <Text dimColor>{`v${version}`}</Text>\n </Box>\n </Box>\n {domainNav && shouldHoistDomainNav && (\n <Box>\n <DomainNav {...domainNav} inline />\n </Box>\n )}\n </>\n );\n};\n"],"mappings":"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","names":[]}
@@ -1,25 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  StepConfirm
4
- } from "./chunk-LJCNUP6M.js";
4
+ } from "./chunk-4ABNXKAR.js";
5
5
  import {
6
6
  StepSources
7
- } from "./chunk-HTYH7CKN.js";
7
+ } from "./chunk-Q7CAE5NN.js";
8
8
  import {
9
9
  StepStack
10
- } from "./chunk-PCC4LBKB.js";
10
+ } from "./chunk-EEAX7E2X.js";
11
11
  import {
12
12
  WizardLayout
13
- } from "./chunk-HG4JPXJT.js";
13
+ } from "./chunk-JG3B47P3.js";
14
14
  import {
15
15
  DomainSelection
16
- } from "./chunk-QWVXOCXO.js";
16
+ } from "./chunk-3NBXQSW2.js";
17
17
  import {
18
18
  StepAgents
19
- } from "./chunk-CLFHC2DK.js";
19
+ } from "./chunk-OQB2CKBT.js";
20
20
  import {
21
21
  StepBuild
22
- } from "./chunk-5F2YR5XV.js";
22
+ } from "./chunk-6DBE562W.js";
23
23
  import {
24
24
  HOTKEY_ACCEPT_DEFAULTS,
25
25
  HOTKEY_INFO,
@@ -29,20 +29,20 @@ import {
29
29
  } from "./chunk-WTJ3AY33.js";
30
30
  import {
31
31
  useWizardStore
32
- } from "./chunk-F5T6TV4Z.js";
32
+ } from "./chunk-IK6YOXZH.js";
33
33
  import {
34
34
  validateSelection
35
- } from "./chunk-WCAUUBHO.js";
35
+ } from "./chunk-3PJLLBUS.js";
36
36
  import {
37
37
  findStack,
38
38
  getSkillById,
39
39
  matrix
40
- } from "./chunk-RWOYPOMR.js";
40
+ } from "./chunk-7Z4N4JWQ.js";
41
41
  import {
42
42
  CLI_COLORS,
43
43
  EJECT_SOURCE,
44
44
  FALLBACK_DOMAIN
45
- } from "./chunk-IO5YBKHV.js";
45
+ } from "./chunk-G6G2FT2G.js";
46
46
 
47
47
  // src/cli/components/wizard/wizard.tsx
48
48
  import { useCallback as useCallback2, useEffect } from "react";
@@ -313,4 +313,4 @@ export {
313
313
  resolveSelectedSkillIds,
314
314
  Wizard
315
315
  };
316
- //# sourceMappingURL=chunk-TNGMF4UX.js.map
316
+ //# sourceMappingURL=chunk-PNRLBPGS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/wizard/wizard.tsx","../src/cli/components/themes/default.ts","../src/cli/components/hooks/use-build-step-props.ts"],"sourcesContent":["import React, { useCallback, useEffect } from \"react\";\nimport { EJECT_SOURCE } from \"../../consts.js\";\nimport { useApp, useInput } from \"ink\";\nimport { ThemeProvider } from \"@inkjs/ui\";\nimport { useWizardStore, type WizardState } from \"../../stores/wizard-store.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 { StepAgents } from \"./step-agents.js\";\nimport { DomainSelection } from \"./domain-selection.js\";\nimport { validateSelection } from \"../../lib/matrix/index.js\";\nimport { getSkillById } from \"../../lib/matrix/matrix-provider.js\";\nimport { findStack } from \"../../lib/matrix/matrix-provider.js\";\nimport {\n HOTKEY_ACCEPT_DEFAULTS,\n HOTKEY_INFO,\n HOTKEY_SCOPE,\n isHotkey,\n isInfoPanelAvailable,\n} from \"./hotkeys.js\";\nimport type {\n AgentName,\n Domain,\n DomainSelections,\n SelectionValidation,\n SkillId,\n StackAgentConfig,\n} from \"../../types/index.js\";\nimport type { AgentScopeConfig, SkillConfig } from \"../../types/config.js\";\nimport type { StartupMessage } from \"../../utils/logger.js\";\nimport { useBuildStepProps } from \"../hooks/use-build-step-props.js\";\n\nconst TOAST_DURATION_MS = 2000;\n\nexport type WizardResultV2 = {\n skills: SkillConfig[];\n selectedAgents: AgentName[];\n agentConfigs: AgentScopeConfig[];\n /**\n * What each sub-agent holds and what preloads, when the producer knows per `(skill, sub-agent)`.\n * A shared configuration installed with `init --from` does: it assigns each skill to named\n * sub-agents at a named load state, and that curation is the whole of what was shared.\n *\n * The wizard has no per-agent granularity — a skill is selected for the project, not for one\n * sub-agent — so it leaves this undefined and the install pipeline's ownership rules build the\n * stack as they always have.\n */\n assignedStack?: Partial<Record<AgentName, StackAgentConfig>>;\n selectedStackId: string | null;\n /**\n * What the written config should record about itself, when the producer already knows.\n *\n * A shared configuration installed with `init --from` does: its payload carries the sharer's own\n * description, because it names no stack for the install to resolve one out of. The wizard leaves\n * this undefined and the install pipeline takes the applied stack's description instead — there\n * IS a loaded stack on that path, and it is the authority on its own sentence.\n */\n description?: string;\n domainSelections: DomainSelections;\n selectedDomains: Domain[];\n /**\n * Skill ids from the saved config that could not be resolved against the loaded source matrix\n * this session. The wizard could not represent them, so they are absent from `skills` and the\n * merge removes their config entries. Carried out of the wizard so the command can NAME each\n * removal and say why it happened — a removal the user never asked for must never be silent.\n */\n unresolvableSkillIds: SkillId[];\n cancelled: boolean;\n validation: SelectionValidation;\n};\n\nexport type WizardProps = {\n onComplete: (result: WizardResultV2) => void;\n onCancel: () => void;\n version: string;\n logo?: string | undefined;\n startupMessages?: StartupMessage[] | undefined;\n initialAgents?: AgentName[] | undefined;\n installedSkillIds?: SkillId[] | undefined;\n};\n\n/** S-key scope toggle: blocked (with a toast) in global context; no-op without a focused row. */\nfunction toggleFocusedScope<Id>(\n isEditingFromGlobalScope: boolean,\n focusedId: Id | null,\n actions: { toggle: (id: Id) => void; setToastMessage: (message: string) => void },\n): void {\n if (isEditingFromGlobalScope) {\n actions.setToastMessage(\"Scope toggle unavailable in global context\");\n return;\n }\n if (focusedId) {\n actions.toggle(focusedId);\n }\n}\n\n/** Selected skill ids: the chosen stack's full list under stack-defaults, else the wizard selections. */\nexport function resolveSelectedSkillIds(store: WizardState): SkillId[] {\n if (store.selectedStackId && store.stackAction === \"defaults\") {\n const stack = findStack(store.selectedStackId);\n if (!stack) {\n throw new Error(`Stack not found: ${store.selectedStackId}`);\n }\n return [...stack.allSkillIds];\n }\n return store.getAllSelectedTechnologies().map((tech) => getSkillById(tech).id);\n}\n\nexport const Wizard: React.FC<WizardProps> = ({\n onComplete,\n onCancel,\n version,\n logo,\n startupMessages,\n initialAgents,\n installedSkillIds,\n}) => {\n const store = useWizardStore();\n const { exit } = useApp();\n\n const toastMessage = useWizardStore((s) => s.toastMessage);\n\n useEffect(() => {\n if (!toastMessage) return;\n const timer = setTimeout(() => {\n useWizardStore.getState().setToastMessage(null);\n }, TOAST_DURATION_MS);\n return () => clearTimeout(timer);\n }, [toastMessage]);\n\n const buildStepProps = useBuildStepProps({\n store,\n ...(installedSkillIds !== undefined && { installedSkillIds }),\n });\n\n useInput((input, key) => {\n // Closing is never gated on the step: the panel is only ever open on a step\n // that allows it, and gating the close would strand the overlay.\n if (store.showInfo) {\n if (key.escape || isHotkey(input, HOTKEY_INFO)) {\n store.toggleInfo();\n }\n return;\n }\n\n if (isInfoPanelAvailable(store.step) && isHotkey(input, HOTKEY_INFO)) {\n store.toggleInfo();\n return;\n }\n\n if (key.escape) {\n // Steps with their own ESC handling (via useInput in child components):\n // - \"stack\": StackSelection handles ESC via onCancel prop\n // - \"domains\": DomainSelection handles ESC via CheckboxGrid onBack\n // - \"build\": StepBuild handles ESC via its own useInput\n // - \"sources\": StepSources handles ESC via onBack prop\n // - \"confirm\": StepConfirm handles ESC via onBack prop\n // - \"agents\": StepAgents handles ESC via its own useInput\n // All steps handle their own ESC, so this is a no-op.\n return;\n }\n\n if (\n isHotkey(input, HOTKEY_ACCEPT_DEFAULTS) &&\n store.step === \"build\" &&\n store.selectedStackId\n ) {\n store.setStackAction(\"defaults\");\n store.setStep(\"confirm\");\n return;\n }\n\n if (isHotkey(input, HOTKEY_SCOPE) && store.step === \"build\") {\n toggleFocusedScope(store.isEditingFromGlobalScope, store.focusedSkillId, {\n toggle: store.toggleSkillScope,\n setToastMessage: store.setToastMessage,\n });\n return;\n }\n\n if (isHotkey(input, HOTKEY_SCOPE) && store.step === \"agents\") {\n toggleFocusedScope(store.isEditingFromGlobalScope, store.focusedAgentId, {\n toggle: store.toggleAgentScope,\n setToastMessage: store.setToastMessage,\n });\n return;\n }\n });\n\n const handleComplete = useCallback(() => {\n const allSkills = resolveSelectedSkillIds(store);\n\n const skillConfigs: SkillConfig[] = allSkills.map((id) => {\n const existing = store.skillConfigs.find((sc) => sc.id === id && !sc.excluded);\n return existing ?? { id, scope: \"global\" as const, origin: EJECT_SOURCE };\n });\n\n // Append excluded entries so they flow through to config generation\n const excludedConfigs = store.skillConfigs.filter((sc) => sc.excluded);\n const allSkillConfigs = [...skillConfigs, ...excludedConfigs];\n\n const validation = validateSelection(allSkills);\n\n const result: WizardResultV2 = {\n skills: allSkillConfigs,\n selectedAgents: store.selectedAgents,\n agentConfigs: store.agentConfigs,\n selectedStackId: store.selectedStackId,\n domainSelections: store.domainSelections,\n selectedDomains: store.selectedDomains,\n unresolvableSkillIds: store.unresolvableSkillIds,\n cancelled: false,\n validation,\n };\n\n onComplete(result);\n exit();\n }, [store, 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 onCancel={handleCancel} />;\n\n case \"domains\":\n return <DomainSelection />;\n\n case \"build\":\n return <StepBuild {...buildStepProps} />;\n\n case \"sources\": {\n return (\n <StepSources\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 />;\n\n case \"confirm\": {\n return <StepConfirm onComplete={handleComplete} onBack={store.goBack} />;\n }\n\n default: {\n const _exhaustive: never = store.step;\n return _exhaustive;\n }\n }\n };\n\n return (\n <ThemeProvider theme={cliTheme}>\n <WizardLayout version={version} logo={logo} startupMessages={startupMessages}>\n {renderStep()}\n </WizardLayout>\n </ThemeProvider>\n );\n};\n","import { extendTheme, defaultTheme } from \"@inkjs/ui\";\nimport { CLI_COLORS } from \"../../consts.js\";\n\n/** Variant → color mapping shared by StatusMessage, Alert, and Badge; unknown variants fall back per-site. */\nconst VARIANT_COLORS: Partial<Record<string, string>> = {\n error: CLI_COLORS.ERROR,\n warning: CLI_COLORS.WARNING,\n success: CLI_COLORS.SUCCESS,\n info: CLI_COLORS.INFO,\n};\n\n/**\n * @inkjs/ui types the props it hands a style function as `any`, so reading\n * `variant` off them is an unchecked access at four call sites. Taking it as\n * `unknown` here is the boundary: the value is only ever a lookup key, and a\n * variant the map does not name falls back to what the call site passes.\n */\nconst variantColor = (variant: unknown, fallback: string): string =>\n VARIANT_COLORS[String(variant)] ?? fallback;\n\n/**\n * CLI theme matching existing picocolors styling\n *\n * Color scheme:\n * - Cyan: Focus/primary (headings, prompts, selected items)\n * - Green: Success states\n * - Red: Errors\n * - Yellow: Warnings\n * - Blue: Info messages\n */\nexport const cliTheme = extendTheme(defaultTheme, {\n components: {\n Spinner: {\n styles: {\n frame: () => ({ color: CLI_COLORS.PRIMARY }),\n label: () => ({ color: CLI_COLORS.NEUTRAL }),\n },\n },\n Select: {\n styles: {\n focusIndicator: () => ({ color: CLI_COLORS.FOCUS }),\n label: ({ isFocused }) => ({\n color: isFocused ? CLI_COLORS.FOCUS : undefined,\n }),\n },\n },\n MultiSelect: {\n styles: {\n focusIndicator: () => ({ color: CLI_COLORS.FOCUS }),\n label: ({ isFocused, isSelected }) => ({\n color: isFocused ? CLI_COLORS.FOCUS : isSelected ? CLI_COLORS.SUCCESS : undefined,\n }),\n checkboxChecked: () => ({ color: CLI_COLORS.SUCCESS }),\n },\n },\n StatusMessage: {\n styles: {\n container: ({ variant }) => ({\n borderStyle: \"round\",\n borderColor: variantColor(variant, CLI_COLORS.INFO),\n }),\n },\n },\n Alert: {\n styles: {\n container: ({ variant }) => ({\n borderColor: variantColor(variant, CLI_COLORS.INFO),\n }),\n icon: ({ variant }) => ({\n color: variantColor(variant, CLI_COLORS.INFO),\n }),\n },\n },\n TextInput: {\n styles: {\n container: ({ isFocused }) => ({\n borderColor: isFocused ? CLI_COLORS.FOCUS : CLI_COLORS.NEUTRAL,\n }),\n cursor: () => ({ color: CLI_COLORS.PRIMARY }),\n },\n },\n ConfirmInput: {\n styles: {\n highlightedChoice: () => ({ color: CLI_COLORS.PRIMARY }),\n },\n },\n Badge: {\n styles: {\n container: ({ variant }) => ({\n color: variantColor(variant, CLI_COLORS.PRIMARY),\n }),\n },\n },\n },\n});\n","import { useCallback } from \"react\";\nimport { FALLBACK_DOMAIN } from \"../../consts.js\";\nimport type { Domain, SkillId } from \"../../types/index.js\";\nimport type { WizardState } from \"../../stores/wizard-store.js\";\nimport { matrix } from \"../../lib/matrix/matrix-provider.js\";\nimport type { StepBuildProps } from \"../wizard/step-build.js\";\n\ntype UseBuildStepPropsOptions = {\n store: WizardState;\n installedSkillIds?: SkillId[];\n};\n\nexport function useBuildStepProps({\n store,\n installedSkillIds,\n}: UseBuildStepPropsOptions): StepBuildProps {\n const currentDomain = store.getCurrentDomain();\n const defaultDomains: Domain[] = [FALLBACK_DOMAIN];\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] || FALLBACK_DOMAIN;\n\n const onToggle = useCallback(\n (categoryId: Parameters<StepBuildProps[\"onToggle\"]>[0], techId: SkillId) => {\n const domain: Domain = store.getCurrentDomain() || FALLBACK_DOMAIN;\n const cat = matrix.categories[categoryId];\n const exclusive = cat?.exclusive ?? true;\n store.toggleTechnology(domain, categoryId, techId, exclusive);\n },\n [store],\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 domain: activeDomain,\n selectedDomains: effectiveDomains,\n allSelections,\n showLabels: store.showLabels,\n installedSkillIds,\n onToggle,\n onToggleLabels: store.toggleShowLabels,\n onContinue,\n onBack,\n };\n}\n"],"mappings":"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","names":["useCallback","useCallback"]}
@@ -2,15 +2,15 @@
2
2
  import {
3
3
  computeScopeDiff,
4
4
  useWizardStore
5
- } from "./chunk-F5T6TV4Z.js";
5
+ } from "./chunk-IK6YOXZH.js";
6
6
  import {
7
7
  getSkillDisplayName
8
- } from "./chunk-RWOYPOMR.js";
8
+ } from "./chunk-7Z4N4JWQ.js";
9
9
  import {
10
10
  CLI_COLORS,
11
11
  EJECT_SOURCE,
12
12
  UI_SYMBOLS
13
- } from "./chunk-IO5YBKHV.js";
13
+ } from "./chunk-G6G2FT2G.js";
14
14
 
15
15
  // src/cli/components/wizard/skill-agent-summary.tsx
16
16
  import { Box, Text } from "ink";
@@ -106,4 +106,4 @@ var SkillAgentSummary = ({
106
106
  export {
107
107
  SkillAgentSummary
108
108
  };
109
- //# sourceMappingURL=chunk-Y5Y7XHVU.js.map
109
+ //# sourceMappingURL=chunk-PZNXBORZ.js.map
@@ -2,13 +2,13 @@
2
2
  import {
3
3
  SourceGrid,
4
4
  isRowInert
5
- } from "./chunk-GS2NZ3GD.js";
5
+ } from "./chunk-SJ4T5YLG.js";
6
6
  import {
7
7
  useMeasuredHeight
8
8
  } from "./chunk-TSIU7DME.js";
9
9
  import {
10
10
  useWizardStore
11
- } from "./chunk-F5T6TV4Z.js";
11
+ } from "./chunk-IK6YOXZH.js";
12
12
 
13
13
  // src/cli/components/wizard/step-sources.tsx
14
14
  import { Box, useInput } from "ink";
@@ -45,4 +45,4 @@ var StepSources = ({ onContinue, onBack }) => {
45
45
  export {
46
46
  StepSources
47
47
  };
48
- //# sourceMappingURL=chunk-HTYH7CKN.js.map
48
+ //# sourceMappingURL=chunk-Q7CAE5NN.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  MIN_TERMINAL_SIZE
4
- } from "./chunk-IO5YBKHV.js";
4
+ } from "./chunk-G6G2FT2G.js";
5
5
 
6
6
  // src/cli/utils/terminal.ts
7
7
  function clearTerminalScreen() {
@@ -20,4 +20,4 @@ export {
20
20
  isTerminalLargeEnough,
21
21
  formatTerminalTooSmallMessage
22
22
  };
23
- //# sourceMappingURL=chunk-G47TDUK3.js.map
23
+ //# sourceMappingURL=chunk-QNAMXQKF.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ScrollAffordance
4
- } from "./chunk-VPASBA7P.js";
4
+ } from "./chunk-YXI3BTQQ.js";
5
5
  import {
6
6
  useFocusedListItem
7
7
  } from "./chunk-J27W6TXS.js";
@@ -10,15 +10,15 @@ import {
10
10
  } from "./chunk-WTJ3AY33.js";
11
11
  import {
12
12
  useSectionScroll
13
- } from "./chunk-OKH64JZE.js";
13
+ } from "./chunk-WCZNQECQ.js";
14
14
  import {
15
15
  getSkillById
16
- } from "./chunk-RWOYPOMR.js";
16
+ } from "./chunk-7Z4N4JWQ.js";
17
17
  import {
18
18
  CLI_COLORS,
19
19
  INSTALL_MODE_CELL_LABELS,
20
20
  UI_SYMBOLS
21
- } from "./chunk-IO5YBKHV.js";
21
+ } from "./chunk-G6G2FT2G.js";
22
22
 
23
23
  // src/cli/components/wizard/source-grid.tsx
24
24
  import { useCallback, useMemo } from "react";
@@ -285,4 +285,4 @@ export {
285
285
  isRowInert,
286
286
  SourceGrid
287
287
  };
288
- //# sourceMappingURL=chunk-GS2NZ3GD.js.map
288
+ //# sourceMappingURL=chunk-SJ4T5YLG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/wizard/source-grid.tsx"],"sourcesContent":["import React, { useCallback, useMemo } from \"react\";\nimport { Box, Text, useInput, type Key } from \"ink\";\nimport type { InstallMode, SkillId, SkillScope } from \"../../types/index.js\";\nimport { CLI_COLORS, INSTALL_MODE_CELL_LABELS, UI_SYMBOLS } from \"../../consts.js\";\nimport { getSkillById } from \"../../lib/matrix/matrix-provider.js\";\nimport { useFocusedListItem } from \"../hooks/use-focused-list-item.js\";\nimport { useSectionScroll } from \"../hooks/use-section-scroll.js\";\nimport { KEY_SPACE } from \"./hotkeys.js\";\nimport { ScrollAffordance } from \"./scroll-affordance.js\";\n\n/**\n * Skill-name cell: the 24 columns the name itself gets, plus the fixed 2-column marker cell that\n * shares the box (see `rowStatusMarker`). Widening by exactly the marker width keeps every name\n * that fitted before the marker was reserved fitting still.\n */\nconst SKILL_NAME_WIDTH = 26;\nconst INSTALL_MODE_COL_WIDTH = 18;\n\n/**\n * Scope gutter: the column of `Global` / `Project` row headers down the left of a scope-grouped\n * grid. Present only in the grouped layout — a single-scope grid has nothing to head, and renders\n * flat with no gutter at all. The header row reserves the width but prints no caption in it: the\n * labels below already name the column, so a caption over them would only repeat them.\n */\nconst SCOPE_COL_WIDTH = 11;\n\n/** Row-header label each scope block is headed by. */\nconst SCOPE_ROW_HEADERS: Record<SkillScope, string> = {\n global: \"Global\",\n project: \"Project\",\n};\n\n/** Blank marker cell — the same two columns a `${glyph} ` marker costs, so names never shift. */\nconst ROW_MARKER_BLANK = \" \";\n\n/**\n * Sources always clips-and-signals rather than bleeding, even at the very short viewports the wizard\n * still renders at (the Sources step is squeezed by the tab bar, dropdown card and footer): a single\n * clipped row plus the overflow affordance beats a bled grid. Distinct from the grid views that fall\n * back to a flex layout below SCROLL_VIEWPORT.MIN_VIEWPORT_ROWS.\n */\nconst SOURCE_GRID_MIN_VIEWPORT_ROWS = 1;\n\n/**\n * One cell of a row's install-mode control. There is no `id`: the two cells ARE the choice, and\n * the `SkillConfig.origin` each one writes is the store's to resolve — which is what stops this\n * surface from writing an origin value nothing installs from.\n */\nexport type SourceOption = {\n mode: Exclude<InstallMode, \"mixed\">;\n selected: boolean;\n};\n\nexport type SourceRow = {\n skillId: SkillId;\n /** The row's two install-mode cells, in render order. */\n options: SourceOption[];\n scope?: SkillScope;\n readOnly?: boolean;\n /**\n * Saved skill deselected this session — still shown so the user can see what saving removes,\n * but inert. Distinct from `readOnly`, which means \"locked because it is installed globally\".\n */\n disabled?: boolean;\n /**\n * Skill selected this session but absent from the hydration snapshot. Renders inline in\n * its normal position with the info panel's added marker (`+`) in green, and stays a normal\n * selectable/editable row — unlike `disabled`/`readOnly`, an added row is NOT inert.\n */\n added?: boolean;\n};\n\n/** Rows the user cannot act on: globally locked (`readOnly`) or pending removal (`disabled`). */\nexport function isRowInert(row: SourceRow): boolean {\n return !!row.readOnly || !!row.disabled;\n}\n\n/**\n * Status marker before the skill name. Lock = \"installed globally, not yours to change here\".\n * The removal marker + red colour and the added marker + green colour mirror the info panel's\n * removed/added-diff treatment (DIFF_PREFIX/DIFF_COLOR in skill-agent-summary.tsx) so a\n * pending-removal or added row reads the same on both surfaces. The marker (not the colour) is\n * what carries the meaning in no-color terminals.\n *\n * ALWAYS two columns wide — glyph plus one separating space, or `ROW_MARKER_BLANK` on a row with no\n * status — so every skill name starts in the same column whether or not its row is marked. Same\n * contract as DIFF_PREFIX, which gives even `unchanged` a 2-char prefix. `UI_SYMBOLS.LOCK` is\n * double-width and still renders a locked row one column wider; known and accepted.\n */\nfunction rowStatusMarker(row: SourceRow): string {\n if (row.readOnly) return `${UI_SYMBOLS.LOCK} `;\n if (row.disabled) return `${UI_SYMBOLS.REMOVED} `;\n if (row.added) return `${UI_SYMBOLS.ADDED} `;\n return ROW_MARKER_BLANK;\n}\n\n/**\n * Diff colour for a row's skill name, matching the info panel's palette (DIFF_COLOR in\n * skill-agent-summary.tsx): red = pending removal, green = added. `undefined` means the row carries\n * no diff status, so each caller supplies its own default.\n */\nfunction rowDiffColor(row: SourceRow): string | undefined {\n if (row.disabled) return CLI_COLORS.ERROR;\n if (row.added) return CLI_COLORS.SUCCESS;\n return undefined;\n}\n\n/** Skill-name colour for an unfocused row: its diff colour, or the neutral default. */\nfunction rowLabelColor(row: SourceRow): string {\n return rowDiffColor(row) ?? CLI_COLORS.NEUTRAL;\n}\n\n/**\n * Skill-name colour for the FOCUSED row, which renders on the dark LABEL_BG highlight. A diff row\n * keeps its diff colour — focus is not a reason to lose the added/removed signal. An ordinary row\n * deliberately stays plain WHITE rather than falling through to `rowLabelColor`: NEUTRAL grey on\n * LABEL_BG is the low-contrast pairing, so reusing it here would dim every ordinary focused row to\n * fix rows that have no diff to show in the first place.\n */\nfunction focusedRowLabelColor(row: SourceRow): string {\n return rowDiffColor(row) ?? CLI_COLORS.WHITE;\n}\n\nexport type SourceGridProps = {\n rows: SourceRow[];\n /** Available height in terminal lines for the scrollable viewport. 0 = no constraint. */\n availableHeight?: number;\n onSelect: (skillId: SkillId, mode: Exclude<InstallMode, \"mixed\">) => void;\n /** Optional initial focus row (default: 0). Use with React `key` to reset. */\n defaultFocusedRow?: number;\n /** Optional initial focus col (default: 0). Use with React `key` to reset. */\n defaultFocusedCol?: number;\n /** Optional callback fired whenever the focused position changes */\n onFocusChange?: (row: number, col: number) => void;\n};\n\ntype SourceSectionProps = {\n row: SourceRow;\n isFocused: boolean;\n focusedOptionIndex: number;\n};\n\nconst SourceTag: React.FC<{\n option: SourceOption;\n isFocused: boolean;\n readOnly?: boolean | undefined;\n disabled?: boolean | undefined;\n}> = ({ option, isFocused, readOnly, disabled }) => {\n if (readOnly || disabled) {\n // Inert rows carry selection in the same vocabulary editable ones do — weight, plus brightness\n // on the locked row whose cells are otherwise all dimmed. Never a glyph: a check appears nowhere\n // else in the grid, and on a pending-removal row it would tick the source the row is losing. The\n // prefix slot stays a blank spacer so the source labels line up with every other row's.\n return (\n <Box width={INSTALL_MODE_COL_WIDTH}>\n <Text\n {...(disabled && { color: CLI_COLORS.ERROR })}\n bold={option.selected}\n dimColor={!disabled && !option.selected}\n >\n {`${UI_SYMBOLS.CHEVRON_SPACER} `}\n {INSTALL_MODE_CELL_LABELS[option.mode]}\n </Text>\n </Box>\n );\n }\n\n const textColor = option.selected ? CLI_COLORS.PRIMARY : CLI_COLORS.WHITE;\n const isBold = isFocused || option.selected;\n const prefix = isFocused ? `${UI_SYMBOLS.CHEVRON} ` : `${UI_SYMBOLS.CHEVRON_SPACER} `;\n\n return (\n <Box width={INSTALL_MODE_COL_WIDTH}>\n <Text color={textColor} bold={isBold} dimColor={!option.selected && !isFocused}>\n {prefix}\n {INSTALL_MODE_CELL_LABELS[option.mode]}\n </Text>\n </Box>\n );\n};\n\nconst SourceSection: React.FC<SourceSectionProps> = ({ row, isFocused, focusedOptionIndex }) => {\n const isInert = isRowInert(row);\n const effectiveFocused = isInert ? false : isFocused;\n // Unlike removal, an added row stays a normal editable row — only its marker/colour differ.\n const statusMarker = rowStatusMarker(row);\n\n return (\n <Box flexDirection=\"row\">\n <Box width={SKILL_NAME_WIDTH}>\n {effectiveFocused ? (\n // The marker sits INSIDE the highlight, and the marker's own separating space is the only\n // one before the name — so the band is the same width on every focused row and the name\n // does not shift a column when focus arrives.\n <Text color={focusedRowLabelColor(row)} backgroundColor={CLI_COLORS.LABEL_BG}>\n {statusMarker}\n {`${getSkillById(row.skillId).displayName} `}\n </Text>\n ) : (\n <Text color={rowLabelColor(row)} dimColor={!!row.readOnly}>\n {statusMarker}\n {getSkillById(row.skillId).displayName}\n </Text>\n )}\n </Box>\n\n <Box flexDirection=\"row\" flexWrap=\"wrap\">\n {row.options.map((option, index) => (\n <SourceTag\n key={option.mode}\n option={option}\n isFocused={effectiveFocused && index === focusedOptionIndex}\n readOnly={row.readOnly}\n disabled={row.disabled}\n />\n ))}\n </Box>\n </Box>\n );\n};\n\ntype ScopeGroup = {\n scope: SkillScope;\n rows: { row: SourceRow; originalIndex: number }[];\n};\n\n/**\n * One row's cell in the scope gutter. Only the first row of a block prints the block's label; every\n * other row reserves the same width empty, which is what indents the rest of the block under its\n * header.\n */\nconst ScopeRowHeader: React.FC<{ scope: SkillScope; isGroupStart: boolean }> = ({\n scope,\n isGroupStart,\n}) => (\n <Box width={SCOPE_COL_WIDTH}>\n {isGroupStart && (\n <Text color={CLI_COLORS.WARNING} bold>\n {SCOPE_ROW_HEADERS[scope]}\n </Text>\n )}\n </Box>\n);\n\n/** First focusable row index at or cyclically after `startRow`; `startRow` when every row is inert. */\nfunction firstFocusableRowIndex(rows: SourceRow[], startRow: number): number {\n const cyclicRowOrder = rows.map((_, offset) => (startRow + offset) % rows.length);\n return (\n cyclicRowOrder.find((row) => {\n const candidate = rows[row];\n return candidate && !isRowInert(candidate);\n }) ?? startRow\n );\n}\n\n/** Index of the last focusable (non-inert) row, or -1 when every row is inert. */\nfunction lastFocusableRowIndex(rows: SourceRow[]): number {\n for (let i = rows.length - 1; i >= 0; i--) {\n const candidate = rows[i];\n if (candidate && !isRowInert(candidate)) return i;\n }\n return -1;\n}\n\n/**\n * Groups rows by scope so the two blocks render separated by a blank line, each headed by its scope\n * in the gutter. Returns an empty array when all rows share the same scope: one block needs no\n * header to tell it from another, so that grid renders flat with no gutter at all.\n */\nfunction groupRowsByScope(rows: SourceRow[]): ScopeGroup[] {\n const indexed = rows.map((row, i) => ({ row, originalIndex: i }));\n const globalRows = indexed.filter(({ row }) => row.scope === \"global\");\n const projectRows = indexed.filter(({ row }) => row.scope !== \"global\");\n\n if (globalRows.length === 0 || projectRows.length === 0) return [];\n\n return [\n { scope: \"global\", rows: globalRows },\n { scope: \"project\", rows: projectRows },\n ];\n}\n\nexport const SourceGrid: React.FC<SourceGridProps> = ({\n rows,\n availableHeight = 0,\n onSelect,\n defaultFocusedRow = 0,\n defaultFocusedCol = 0,\n onFocusChange,\n}) => {\n const getColCount = useCallback((row: number): number => rows[row]?.options.length ?? 0, [rows]);\n\n const skipRow = useCallback(\n (row: number): boolean => {\n const candidate = rows[row];\n return !!candidate && isRowInert(candidate);\n },\n [rows],\n );\n\n const effectiveDefaultRow = firstFocusableRowIndex(rows, defaultFocusedRow);\n\n const { focusedRow, focusedCol, moveFocus } = useFocusedListItem(rows.length, getColCount, {\n wrap: true,\n ...(onFocusChange !== undefined && { onChange: onFocusChange }),\n initialRow: effectiveDefaultRow,\n initialCol: defaultFocusedCol,\n skipRow,\n });\n\n const {\n setSectionRef,\n setViewportRef,\n setContentRef,\n scrollEnabled,\n scrollTopPx,\n hiddenAbove,\n hiddenBelow,\n scrollBy,\n } = useSectionScroll({\n sectionCount: rows.length,\n focusedIndex: focusedRow,\n availableHeight,\n minViewportRows: SOURCE_GRID_MIN_VIEWPORT_ROWS,\n });\n\n const lastFocusableRow = useMemo(() => lastFocusableRowIndex(rows), [rows]);\n\n useInput(\n useCallback(\n (input: string, key: Key) => {\n if (input === KEY_SPACE) {\n const currentRow = rows[focusedRow];\n if (!currentRow || isRowInert(currentRow)) return;\n const currentOption = currentRow.options[focusedCol];\n if (currentOption) {\n onSelect(currentRow.skillId, currentOption.mode);\n }\n return;\n }\n\n // With no focusable row (every row inert — a run of locked-global rows plus the\n // pending-removal row) there is no focus to move or to anchor overscroll on, so\n // vertical keys drive the viewport directly. Otherwise, overscroll only kicks in\n // once focus sits on the last focusable row (below it are only inert rows).\n const noFocusableRow = lastFocusableRow === -1;\n const atLastFocusableRow = focusedRow === lastFocusableRow;\n\n if (key.leftArrow) {\n moveFocus(\"left\");\n } else if (key.rightArrow) {\n moveFocus(\"right\");\n } else if (key.upArrow) {\n if (scrollEnabled && noFocusableRow && hiddenAbove > 0) {\n scrollBy(-1);\n } else {\n moveFocus(\"up\");\n }\n } else if (key.downArrow) {\n // Keep scrolling the viewport to reveal trailing inert rows (e.g. the\n // pending-removal row) instead of wrapping. Focus never lands on them, so the\n // viewport travels past the last focusable row (or scrolls on its own when\n // nothing is focusable).\n if (scrollEnabled && (noFocusableRow || atLastFocusableRow) && hiddenBelow > 0) {\n scrollBy(1);\n } else {\n moveFocus(\"down\");\n }\n }\n },\n [\n rows,\n focusedRow,\n focusedCol,\n onSelect,\n moveFocus,\n scrollEnabled,\n lastFocusableRow,\n hiddenAbove,\n hiddenBelow,\n scrollBy,\n ],\n ),\n );\n\n if (rows.length === 0) {\n return (\n <Box flexDirection=\"column\">\n <Text dimColor>No skills to display.</Text>\n </Box>\n );\n }\n\n const noShrink = scrollEnabled ? { flexShrink: 0 } : {};\n\n const scopeGroups = groupRowsByScope(rows);\n\n const sectionElements =\n scopeGroups.length > 0\n ? scopeGroups.map((group) => (\n <Box key={group.scope} flexDirection=\"column\" marginTop={1} {...noShrink}>\n {group.rows.map(({ row, originalIndex }, rowIndexInGroup) => (\n <Box\n key={`${row.skillId}-${row.scope ?? \"default\"}`}\n flexDirection=\"row\"\n ref={(el) => setSectionRef(originalIndex, el)}\n {...noShrink}\n >\n <ScopeRowHeader scope={group.scope} isGroupStart={rowIndexInGroup === 0} />\n <SourceSection\n row={row}\n isFocused={originalIndex === focusedRow}\n focusedOptionIndex={focusedCol}\n />\n </Box>\n ))}\n </Box>\n ))\n : rows.map((row, rowIndex) => (\n <Box\n key={`${row.skillId}-${row.scope ?? \"default\"}`}\n ref={(el) => setSectionRef(rowIndex, el)}\n {...noShrink}\n >\n <SourceSection\n row={row}\n isFocused={rowIndex === focusedRow}\n focusedOptionIndex={focusedCol}\n />\n </Box>\n ));\n\n return (\n <Box\n flexDirection=\"column\"\n {...(scrollEnabled\n ? { height: availableHeight, overflow: \"hidden\" as const }\n : { flexGrow: 1 })}\n >\n <Box ref={setViewportRef} flexDirection=\"column\" overflow=\"hidden\" flexGrow={1}>\n <Box\n ref={setContentRef}\n flexDirection=\"column\"\n marginTop={scrollTopPx > 0 ? -scrollTopPx : 0}\n {...noShrink}\n >\n {sectionElements}\n </Box>\n </Box>\n <ScrollAffordance hiddenAbove={hiddenAbove} hiddenBelow={hiddenBelow} />\n </Box>\n );\n};\n"],"mappings":"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","names":[]}
@@ -2,16 +2,14 @@
2
2
  import {
3
3
  buildMarketplacePluginRef,
4
4
  claudePluginInstall,
5
+ getErrorMessage,
5
6
  isLocalOnlySkill,
6
7
  toClaudePluginScope
7
- } from "./chunk-WCAUUBHO.js";
8
- import {
9
- getErrorMessage
10
- } from "./chunk-XAXJWXI4.js";
8
+ } from "./chunk-3PJLLBUS.js";
11
9
  import {
12
10
  CLI_INVOKE_COMMAND,
13
11
  EJECT_SOURCE
14
- } from "./chunk-IO5YBKHV.js";
12
+ } from "./chunk-G6G2FT2G.js";
15
13
 
16
14
  // src/cli/lib/operations/skills/install-plugin-skills.ts
17
15
  function pluginInstallFailureError(failedCount) {
@@ -46,4 +44,4 @@ export {
46
44
  unbackedPluginInstallError,
47
45
  installPluginSkills
48
46
  };
49
- //# sourceMappingURL=chunk-UMH2YZKG.js.map
47
+ //# sourceMappingURL=chunk-TPQ5M3BW.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/lib/operations/skills/install-plugin-skills.ts"],"sourcesContent":["import { claudePluginInstall } from \"../../../utils/exec.js\";\nimport { isLocalOnlySkill } from \"../../loading/multi-source-loader.js\";\nimport { buildMarketplacePluginRef, toClaudePluginScope } from \"../../plugins/index.js\";\nimport { getErrorMessage } from \"../../../utils/errors.js\";\nimport { CLI_INVOKE_COMMAND, EJECT_SOURCE } from \"../../../consts.js\";\nimport type { MergedSkillsMatrix, SkillId } from \"../../../types/index.js\";\nimport type { SkillConfig } from \"../../../types/config.js\";\n\nexport type PluginInstallResult = {\n installed: Array<{ id: SkillId; ref: string }>;\n failed: Array<{ id: SkillId; error: string }>;\n};\n\n/**\n * Hard-error message for failed plugin installs. Plugin install intent is\n * inviolable (never fall back to eject) — callers MUST error with this BEFORE\n * config is written, or config.ts gains orphan entries claiming the skill\n * is installed.\n */\nexport function pluginInstallFailureError(failedCount: number): string {\n return `Failed to install ${failedCount} plugin skill(s). Plugin install intent could not be honored. Verify the skill id matches the marketplace, run '${CLI_INVOKE_COMMAND} update' to refresh the marketplace, or switch affected skills to eject mode.`;\n}\n\n/**\n * Skills asking to be installed as plugins that no marketplace carries.\n *\n * A skill that exists only in this project cannot be pulled from anywhere, so the ask is\n * refusable BEFORE anything is attempted — which is what separates it from an install\n * that was tried and failed. The Sources grid reads {@link isLocalOnlySkill} to decide\n * whether to offer the plugin cell at all, so this guard shares it: a caller reaching\n * past that surface must meet the same rule it did.\n */\nexport function unbackedPluginSkillIds(\n skills: SkillConfig[],\n matrix: MergedSkillsMatrix,\n): SkillId[] {\n return skills\n .filter((skill) => skill.origin !== EJECT_SOURCE)\n .filter((skill) => isLocalOnlySkill(matrix.skills[skill.id]))\n .map((skill) => skill.id);\n}\n\n/**\n * The refusal text for {@link unbackedPluginSkillIds}. Deliberately NOT\n * {@link pluginInstallFailureError}: refreshing a marketplace and checking an id against\n * it are impossible instructions for a skill the user wrote themselves, so the two\n * failures owe different advice.\n */\nexport function unbackedPluginInstallError(ids: SkillId[]): string {\n return `Cannot install ${ids.length} skill(s) as plugins — no marketplace carries them: ${ids.join(\", \")}. A skill that exists only in this project can only be installed as a local copy. Set it to Local on the Sources step, or publish it to a marketplace first.`;\n}\n\n/**\n * Installs skill plugins via the Claude CLI, routing by scope.\n *\n * For each skill, constructs the plugin ref as `{skillId}@{marketplace}`\n * and invokes `claudePluginInstall` with the correct scope.\n */\nexport async function installPluginSkills(\n skills: SkillConfig[],\n marketplace: string,\n projectDir: string,\n): Promise<PluginInstallResult> {\n const pluginSkills = skills.filter((s) => s.origin !== EJECT_SOURCE);\n const installed: PluginInstallResult[\"installed\"] = [];\n const failed: PluginInstallResult[\"failed\"] = [];\n\n for (const skill of pluginSkills) {\n const pluginRef = buildMarketplacePluginRef(skill.id, marketplace);\n const pluginScope = toClaudePluginScope(skill.scope);\n try {\n await claudePluginInstall(pluginRef, pluginScope, projectDir);\n installed.push({ id: skill.id, ref: pluginRef });\n } catch (error) {\n failed.push({ id: skill.id, error: getErrorMessage(error) });\n }\n }\n\n return { installed, failed };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAmBO,SAAS,0BAA0B,aAA6B;AACrE,SAAO,qBAAqB,WAAW,mHAAmH,kBAAkB;AAC9K;AAWO,SAAS,uBACd,QACA,QACW;AACX,SAAO,OACJ,OAAO,CAAC,UAAU,MAAM,WAAW,YAAY,EAC/C,OAAO,CAAC,UAAU,iBAAiB,OAAO,OAAO,MAAM,EAAE,CAAC,CAAC,EAC3D,IAAI,CAAC,UAAU,MAAM,EAAE;AAC5B;AAQO,SAAS,2BAA2B,KAAwB;AACjE,SAAO,kBAAkB,IAAI,MAAM,4DAAuD,IAAI,KAAK,IAAI,CAAC;AAC1G;AAQA,eAAsB,oBACpB,QACA,aACA,YAC8B;AAC9B,QAAM,eAAe,OAAO,OAAO,CAAC,MAAM,EAAE,WAAW,YAAY;AACnE,QAAM,YAA8C,CAAC;AACrD,QAAM,SAAwC,CAAC;AAE/C,aAAW,SAAS,cAAc;AAChC,UAAM,YAAY,0BAA0B,MAAM,IAAI,WAAW;AACjE,UAAM,cAAc,oBAAoB,MAAM,KAAK;AACnD,QAAI;AACF,YAAM,oBAAoB,WAAW,aAAa,UAAU;AAC5D,gBAAU,KAAK,EAAE,IAAI,MAAM,IAAI,KAAK,UAAU,CAAC;AAAA,IACjD,SAAS,OAAO;AACd,aAAO,KAAK,EAAE,IAAI,MAAM,IAAI,OAAO,gBAAgB,KAAK,EAAE,CAAC;AAAA,IAC7D;AAAA,EACF;AAEA,SAAO,EAAE,WAAW,OAAO;AAC7B;","names":[]}
1
+ {"version":3,"sources":["../src/cli/lib/operations/skills/install-plugin-skills.ts"],"sourcesContent":["import { claudePluginInstall } from \"../../../utils/exec.js\";\nimport { isLocalOnlySkill } from \"../../loading/multi-source-loader.js\";\nimport { buildMarketplacePluginRef, toClaudePluginScope } from \"../../plugins/index.js\";\nimport { getErrorMessage } from \"../../../utils/errors.js\";\nimport { CLI_INVOKE_COMMAND, EJECT_SOURCE } from \"../../../consts.js\";\nimport type { MergedSkillsMatrix, SkillId } from \"../../../types/index.js\";\nimport type { SkillConfig } from \"../../../types/config.js\";\n\nexport type PluginInstallResult = {\n installed: Array<{ id: SkillId; ref: string }>;\n failed: Array<{ id: SkillId; error: string }>;\n};\n\n/**\n * Hard-error message for failed plugin installs. Plugin install intent is\n * inviolable (never fall back to eject) — callers MUST error with this BEFORE\n * config is written, or config.ts gains orphan entries claiming the skill\n * is installed.\n */\nexport function pluginInstallFailureError(failedCount: number): string {\n return `Failed to install ${failedCount} plugin skill(s). Plugin install intent could not be honored. Verify the skill id matches the marketplace, run '${CLI_INVOKE_COMMAND} update' to refresh the marketplace, or switch affected skills to eject mode.`;\n}\n\n/**\n * Skills asking to be installed as plugins that no marketplace carries.\n *\n * A skill that exists only in this project cannot be pulled from anywhere, so the ask is\n * refusable BEFORE anything is attempted — which is what separates it from an install\n * that was tried and failed. The Sources grid reads {@link isLocalOnlySkill} to decide\n * whether to offer the plugin cell at all, so this guard shares it: a caller reaching\n * past that surface must meet the same rule it did.\n */\nexport function unbackedPluginSkillIds(\n skills: SkillConfig[],\n matrix: MergedSkillsMatrix,\n): SkillId[] {\n return skills\n .filter((skill) => skill.origin !== EJECT_SOURCE)\n .filter((skill) => isLocalOnlySkill(matrix.skills[skill.id]))\n .map((skill) => skill.id);\n}\n\n/**\n * The refusal text for {@link unbackedPluginSkillIds}. Deliberately NOT\n * {@link pluginInstallFailureError}: refreshing a marketplace and checking an id against\n * it are impossible instructions for a skill the user wrote themselves, so the two\n * failures owe different advice.\n */\nexport function unbackedPluginInstallError(ids: SkillId[]): string {\n return `Cannot install ${ids.length} skill(s) as plugins — no marketplace carries them: ${ids.join(\", \")}. A skill that exists only in this project can only be installed as a local copy. Set it to Local on the Sources step, or publish it to a marketplace first.`;\n}\n\n/**\n * Installs skill plugins via the Claude CLI, routing by scope.\n *\n * For each skill, constructs the plugin ref as `{skillId}@{marketplace}`\n * and invokes `claudePluginInstall` with the correct scope.\n */\nexport async function installPluginSkills(\n skills: SkillConfig[],\n marketplace: string,\n projectDir: string,\n): Promise<PluginInstallResult> {\n const pluginSkills = skills.filter((s) => s.origin !== EJECT_SOURCE);\n const installed: PluginInstallResult[\"installed\"] = [];\n const failed: PluginInstallResult[\"failed\"] = [];\n\n for (const skill of pluginSkills) {\n const pluginRef = buildMarketplacePluginRef(skill.id, marketplace);\n const pluginScope = toClaudePluginScope(skill.scope);\n try {\n await claudePluginInstall(pluginRef, pluginScope, projectDir);\n installed.push({ id: skill.id, ref: pluginRef });\n } catch (error) {\n failed.push({ id: skill.id, error: getErrorMessage(error) });\n }\n }\n\n return { installed, failed };\n}\n"],"mappings":";;;;;;;;;;;;;;AAmBO,SAAS,0BAA0B,aAA6B;AACrE,SAAO,qBAAqB,WAAW,mHAAmH,kBAAkB;AAC9K;AAWO,SAAS,uBACd,QACA,QACW;AACX,SAAO,OACJ,OAAO,CAAC,UAAU,MAAM,WAAW,YAAY,EAC/C,OAAO,CAAC,UAAU,iBAAiB,OAAO,OAAO,MAAM,EAAE,CAAC,CAAC,EAC3D,IAAI,CAAC,UAAU,MAAM,EAAE;AAC5B;AAQO,SAAS,2BAA2B,KAAwB;AACjE,SAAO,kBAAkB,IAAI,MAAM,4DAAuD,IAAI,KAAK,IAAI,CAAC;AAC1G;AAQA,eAAsB,oBACpB,QACA,aACA,YAC8B;AAC9B,QAAM,eAAe,OAAO,OAAO,CAAC,MAAM,EAAE,WAAW,YAAY;AACnE,QAAM,YAA8C,CAAC;AACrD,QAAM,SAAwC,CAAC;AAE/C,aAAW,SAAS,cAAc;AAChC,UAAM,YAAY,0BAA0B,MAAM,IAAI,WAAW;AACjE,UAAM,cAAc,oBAAoB,MAAM,KAAK;AACnD,QAAI;AACF,YAAM,oBAAoB,WAAW,aAAa,UAAU;AAC5D,gBAAU,KAAK,EAAE,IAAI,MAAM,IAAI,KAAK,UAAU,CAAC;AAAA,IACjD,SAAS,OAAO;AACd,aAAO,KAAK,EAAE,IAAI,MAAM,IAAI,OAAO,gBAAgB,KAAK,EAAE,CAAC;AAAA,IAC7D;AAAA,EACF;AAEA,SAAO,EAAE,WAAW,OAAO;AAC7B;","names":[]}