agents-inc 0.153.0 → 0.155.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 (218) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/dist/chunk-2H7UWE2G.js +246 -0
  3. package/dist/chunk-2H7UWE2G.js.map +1 -0
  4. package/dist/{chunk-ZNDLUWWD.js → chunk-4A4L3YIW.js} +5 -5
  5. package/dist/chunk-4G2YHZIM.js +132 -0
  6. package/dist/chunk-4G2YHZIM.js.map +1 -0
  7. package/dist/{chunk-WDAO3RUY.js → chunk-5F2YR5XV.js} +3 -3
  8. package/dist/{chunk-JFAUQDZM.js → chunk-5OKLCVM4.js} +3 -3
  9. package/dist/{chunk-REKOWTIB.js → chunk-5QZAK6J7.js} +44 -10
  10. package/dist/chunk-5QZAK6J7.js.map +1 -0
  11. package/dist/{chunk-VECFAKM5.js → chunk-6RLV45HN.js} +10 -10
  12. package/dist/{chunk-VECFAKM5.js.map → chunk-6RLV45HN.js.map} +1 -1
  13. package/dist/{chunk-K7PJDWER.js → chunk-CLFHC2DK.js} +15 -106
  14. package/dist/chunk-CLFHC2DK.js.map +1 -0
  15. package/dist/chunk-D2CAV7MG.js +61 -0
  16. package/dist/chunk-D2CAV7MG.js.map +1 -0
  17. package/dist/{chunk-NXGMM5YN.js → chunk-F2AMBBIA.js} +6 -6
  18. package/dist/{chunk-NXGMM5YN.js.map → chunk-F2AMBBIA.js.map} +1 -1
  19. package/dist/{chunk-HEBPKK5L.js → chunk-F5T6TV4Z.js} +213 -92
  20. package/dist/chunk-F5T6TV4Z.js.map +1 -0
  21. package/dist/{chunk-K3O3YQYG.js → chunk-G47TDUK3.js} +2 -2
  22. package/dist/{chunk-U2DNAHFE.js → chunk-GS2NZ3GD.js} +8 -8
  23. package/dist/chunk-GS2NZ3GD.js.map +1 -0
  24. package/dist/{chunk-ORJ5Y222.js → chunk-H6UTKTLK.js} +176 -256
  25. package/dist/chunk-H6UTKTLK.js.map +1 -0
  26. package/dist/{chunk-M5HSTNYU.js → chunk-HG4JPXJT.js} +11 -29
  27. package/dist/chunk-HG4JPXJT.js.map +1 -0
  28. package/dist/{chunk-VGEBYCQZ.js → chunk-HTYH7CKN.js} +4 -15
  29. package/dist/chunk-HTYH7CKN.js.map +1 -0
  30. package/dist/{chunk-JC7DBPRD.js → chunk-HXKOIWLU.js} +2 -2
  31. package/dist/chunk-HXKOIWLU.js.map +1 -0
  32. package/dist/{chunk-NSJ2S7R5.js → chunk-ILUVL7WM.js} +2 -2
  33. package/dist/{chunk-R54KIGN2.js → chunk-IO5YBKHV.js} +13 -2
  34. package/dist/chunk-IO5YBKHV.js.map +1 -0
  35. package/dist/chunk-IXRZAHZF.js +53 -0
  36. package/dist/chunk-IXRZAHZF.js.map +1 -0
  37. package/dist/chunk-JCPANDNR.js +35 -0
  38. package/dist/chunk-JCPANDNR.js.map +1 -0
  39. package/dist/{chunk-R4SEU3DF.js → chunk-LJCNUP6M.js} +2 -2
  40. package/dist/{chunk-FRWKCE6R.js → chunk-MHRC3KRX.js} +5 -5
  41. package/dist/chunk-MHRC3KRX.js.map +1 -0
  42. package/dist/chunk-MO5FGLVS.js +142 -0
  43. package/dist/chunk-MO5FGLVS.js.map +1 -0
  44. package/dist/{chunk-Q5FLW4JG.js → chunk-OKH64JZE.js} +2 -2
  45. package/dist/{chunk-WG6V6VJV.js → chunk-PCC4LBKB.js} +2 -2
  46. package/dist/chunk-PMUABBPG.js +258 -0
  47. package/dist/chunk-PMUABBPG.js.map +1 -0
  48. package/dist/{chunk-EFU6P3B7.js → chunk-PNMM5HNX.js} +5 -5
  49. package/dist/{chunk-4VYALFQZ.js → chunk-QWVXOCXO.js} +6 -6
  50. package/dist/{chunk-35VOK7XE.js → chunk-RWOYPOMR.js} +4365 -4355
  51. package/dist/chunk-RWOYPOMR.js.map +1 -0
  52. package/dist/{chunk-IO4VEW5U.js → chunk-SY4X2LND.js} +4 -4
  53. package/dist/{chunk-XGMOXMS4.js → chunk-TBOVYLQJ.js} +2 -2
  54. package/dist/{chunk-BMGBA6HJ.js → chunk-TNGMF4UX.js} +20 -20
  55. package/dist/{chunk-BMGBA6HJ.js.map → chunk-TNGMF4UX.js.map} +1 -1
  56. package/dist/{chunk-TY6CAOL7.js → chunk-UMH2YZKG.js} +14 -5
  57. package/dist/chunk-UMH2YZKG.js.map +1 -0
  58. package/dist/{chunk-6CNRMCFM.js → chunk-VPASBA7P.js} +2 -2
  59. package/dist/{chunk-NXQAY5U7.js → chunk-WCAUUBHO.js} +5860 -5126
  60. package/dist/chunk-WCAUUBHO.js.map +1 -0
  61. package/dist/{chunk-V3ENMW7G.js → chunk-WFRTHH3A.js} +3 -3
  62. package/dist/{chunk-2HC2SNRP.js → chunk-WTJ3AY33.js} +1 -5
  63. package/dist/chunk-WTJ3AY33.js.map +1 -0
  64. package/dist/{chunk-F7Z5I6GU.js → chunk-WUQ3MTPF.js} +11 -5
  65. package/dist/{chunk-F7Z5I6GU.js.map → chunk-WUQ3MTPF.js.map} +1 -1
  66. package/dist/{chunk-U2YSGBHA.js → chunk-XAXJWXI4.js} +93 -43
  67. package/dist/chunk-XAXJWXI4.js.map +1 -0
  68. package/dist/{chunk-ACKC577F.js → chunk-Y5Y7XHVU.js} +4 -4
  69. package/dist/{chunk-X5XJ6WKO.js → chunk-YO2LTXB4.js} +107 -16
  70. package/dist/chunk-YO2LTXB4.js.map +1 -0
  71. package/dist/{chunk-FIHTAFST.js → chunk-ZUAZHDKP.js} +64 -65
  72. package/dist/chunk-ZUAZHDKP.js.map +1 -0
  73. package/dist/commands/build/marketplace.js +98 -120
  74. package/dist/commands/build/marketplace.js.map +1 -1
  75. package/dist/commands/build/plugins.js +8 -8
  76. package/dist/commands/compile.js +57 -24
  77. package/dist/commands/compile.js.map +1 -1
  78. package/dist/commands/doctor.js +220 -79
  79. package/dist/commands/doctor.js.map +1 -1
  80. package/dist/commands/edit.js +608 -100
  81. package/dist/commands/edit.js.map +1 -1
  82. package/dist/commands/eject.js +26 -21
  83. package/dist/commands/eject.js.map +1 -1
  84. package/dist/commands/init.js +41 -39
  85. package/dist/commands/list.js +11 -11
  86. package/dist/commands/list.js.map +1 -1
  87. package/dist/commands/new/marketplace.js +243 -0
  88. package/dist/commands/new/marketplace.js.map +1 -0
  89. package/dist/commands/search.js +34 -24
  90. package/dist/commands/search.js.map +1 -1
  91. package/dist/commands/share.js +67 -0
  92. package/dist/commands/share.js.map +1 -0
  93. package/dist/commands/uninstall.js +234 -196
  94. package/dist/commands/uninstall.js.map +1 -1
  95. package/dist/commands/update.js +9 -9
  96. package/dist/commands/update.js.map +1 -1
  97. package/dist/components/common/prompt-confirm.js +1 -1
  98. package/dist/components/common/removal-plan-confirm.js +10 -0
  99. package/dist/components/common/select-list.js +2 -2
  100. package/dist/components/wizard/category-grid.js +5 -5
  101. package/dist/components/wizard/checkbox-grid.js +5 -5
  102. package/dist/components/wizard/domain-selection.js +11 -11
  103. package/dist/components/wizard/run-wizard-session.js +29 -28
  104. package/dist/components/wizard/scroll-affordance.js +2 -2
  105. package/dist/components/wizard/skill-agent-summary.js +6 -6
  106. package/dist/components/wizard/source-grid.js +6 -6
  107. package/dist/components/wizard/stack-selection.js +8 -8
  108. package/dist/components/wizard/step-agents.js +10 -11
  109. package/dist/components/wizard/step-build.js +9 -9
  110. package/dist/components/wizard/step-confirm.js +10 -10
  111. package/dist/components/wizard/step-sources.js +10 -10
  112. package/dist/components/wizard/step-stack.js +9 -9
  113. package/dist/components/wizard/summary-panel.js +9 -9
  114. package/dist/components/wizard/toast.js +2 -2
  115. package/dist/components/wizard/wizard-layout.js +14 -14
  116. package/dist/components/wizard/wizard-tabs.js +2 -2
  117. package/dist/components/wizard/wizard.js +27 -27
  118. package/dist/copy-local-skills-NSUTBVLJ.js +14 -0
  119. package/dist/hooks/init.js +42 -62
  120. package/dist/hooks/init.js.map +1 -1
  121. package/dist/load-agent-defs-X4E57I2U.js +15 -0
  122. package/dist/{loader-4U7UCE2Y.js → loader-G6BFG2VA.js} +3 -3
  123. package/dist/recompile-project-agents-JE4MM72I.js +20 -0
  124. package/dist/{source-loader-ZNSYJJCI.js → source-loader-3DL3NYUX.js} +7 -5
  125. package/dist/source-loader-3DL3NYUX.js.map +1 -0
  126. package/dist/src/agents/developer/ai-developer/critical-reminders.md +1 -1
  127. package/dist/src/agents/developer/ai-developer/critical-requirements.md +1 -1
  128. package/dist/src/agents/developer/ai-developer/identity.md +1 -1
  129. package/dist/src/agents/developer/ai-developer/playbook.md +1 -1
  130. package/dist/src/agents/developer/api-developer/identity.md +1 -1
  131. package/dist/src/agents/developer/cli-developer/identity.md +1 -1
  132. package/dist/src/agents/developer/web-developer/identity.md +1 -1
  133. package/dist/src/agents/meta/codex-keeper/identity.md +1 -1
  134. package/dist/src/agents/meta/convention-keeper/identity.md +1 -1
  135. package/dist/src/agents/meta/skill-summoner/identity.md +1 -1
  136. package/dist/src/agents/researcher/ai-researcher/identity.md +1 -1
  137. package/dist/src/agents/researcher/api-researcher/identity.md +1 -1
  138. package/dist/src/agents/researcher/cli-researcher/identity.md +1 -1
  139. package/dist/src/agents/researcher/web-researcher/identity.md +1 -1
  140. package/dist/src/agents/reviewer/reviewer/playbook.md +1 -1
  141. package/dist/src/agents/tester/ai-tester/identity.md +1 -1
  142. package/dist/src/agents/tester/api-tester/critical-reminders.md +1 -1
  143. package/dist/src/agents/tester/api-tester/critical-requirements.md +1 -1
  144. package/dist/src/agents/tester/api-tester/identity.md +2 -2
  145. package/dist/src/agents/tester/cli-tester/identity.md +1 -1
  146. package/dist/src/agents/tester/web-tester/identity.md +3 -3
  147. package/dist/stores/wizard-store.js +7 -5
  148. package/package.json +10 -7
  149. package/src/agents/developer/ai-developer/critical-reminders.md +1 -1
  150. package/src/agents/developer/ai-developer/critical-requirements.md +1 -1
  151. package/src/agents/developer/ai-developer/identity.md +1 -1
  152. package/src/agents/developer/ai-developer/playbook.md +1 -1
  153. package/src/agents/developer/api-developer/identity.md +1 -1
  154. package/src/agents/developer/cli-developer/identity.md +1 -1
  155. package/src/agents/developer/web-developer/identity.md +1 -1
  156. package/src/agents/meta/codex-keeper/identity.md +1 -1
  157. package/src/agents/meta/convention-keeper/identity.md +1 -1
  158. package/src/agents/meta/skill-summoner/identity.md +1 -1
  159. package/src/agents/researcher/ai-researcher/identity.md +1 -1
  160. package/src/agents/researcher/api-researcher/identity.md +1 -1
  161. package/src/agents/researcher/cli-researcher/identity.md +1 -1
  162. package/src/agents/researcher/web-researcher/identity.md +1 -1
  163. package/src/agents/reviewer/reviewer/playbook.md +1 -1
  164. package/src/agents/tester/ai-tester/identity.md +1 -1
  165. package/src/agents/tester/api-tester/critical-reminders.md +1 -1
  166. package/src/agents/tester/api-tester/critical-requirements.md +1 -1
  167. package/src/agents/tester/api-tester/identity.md +2 -2
  168. package/src/agents/tester/cli-tester/identity.md +1 -1
  169. package/src/agents/tester/web-tester/identity.md +3 -3
  170. package/src/schemas/custom-metadata.schema.json +3 -0
  171. package/src/schemas/metadata.schema.json +3 -0
  172. package/src/schemas/plugin.schema.json +3 -0
  173. package/src/schemas/project-config.schema.json +4 -8
  174. package/src/schemas/project-source-config.schema.json +2 -2
  175. package/dist/chunk-2HC2SNRP.js.map +0 -1
  176. package/dist/chunk-35VOK7XE.js.map +0 -1
  177. package/dist/chunk-FIHTAFST.js.map +0 -1
  178. package/dist/chunk-FRWKCE6R.js.map +0 -1
  179. package/dist/chunk-HEBPKK5L.js.map +0 -1
  180. package/dist/chunk-JC7DBPRD.js.map +0 -1
  181. package/dist/chunk-K7PJDWER.js.map +0 -1
  182. package/dist/chunk-KET3EVGX.js +0 -41
  183. package/dist/chunk-KET3EVGX.js.map +0 -1
  184. package/dist/chunk-M5HSTNYU.js.map +0 -1
  185. package/dist/chunk-NXQAY5U7.js.map +0 -1
  186. package/dist/chunk-OJJLYHM3.js +0 -34
  187. package/dist/chunk-OJJLYHM3.js.map +0 -1
  188. package/dist/chunk-ORJ5Y222.js.map +0 -1
  189. package/dist/chunk-R54KIGN2.js.map +0 -1
  190. package/dist/chunk-REKOWTIB.js.map +0 -1
  191. package/dist/chunk-TY6CAOL7.js.map +0 -1
  192. package/dist/chunk-U2DNAHFE.js.map +0 -1
  193. package/dist/chunk-U2YSGBHA.js.map +0 -1
  194. package/dist/chunk-VGEBYCQZ.js.map +0 -1
  195. package/dist/chunk-X5XJ6WKO.js.map +0 -1
  196. package/dist/copy-local-skills-KF2RAVD3.js +0 -14
  197. package/dist/load-agent-defs-DGXGBU3Q.js +0 -15
  198. package/dist/recompile-project-agents-CJVBAWDJ.js +0 -20
  199. /package/dist/{chunk-ZNDLUWWD.js.map → chunk-4A4L3YIW.js.map} +0 -0
  200. /package/dist/{chunk-WDAO3RUY.js.map → chunk-5F2YR5XV.js.map} +0 -0
  201. /package/dist/{chunk-JFAUQDZM.js.map → chunk-5OKLCVM4.js.map} +0 -0
  202. /package/dist/{chunk-K3O3YQYG.js.map → chunk-G47TDUK3.js.map} +0 -0
  203. /package/dist/{chunk-NSJ2S7R5.js.map → chunk-ILUVL7WM.js.map} +0 -0
  204. /package/dist/{chunk-R4SEU3DF.js.map → chunk-LJCNUP6M.js.map} +0 -0
  205. /package/dist/{chunk-Q5FLW4JG.js.map → chunk-OKH64JZE.js.map} +0 -0
  206. /package/dist/{chunk-WG6V6VJV.js.map → chunk-PCC4LBKB.js.map} +0 -0
  207. /package/dist/{chunk-EFU6P3B7.js.map → chunk-PNMM5HNX.js.map} +0 -0
  208. /package/dist/{chunk-4VYALFQZ.js.map → chunk-QWVXOCXO.js.map} +0 -0
  209. /package/dist/{chunk-IO4VEW5U.js.map → chunk-SY4X2LND.js.map} +0 -0
  210. /package/dist/{chunk-XGMOXMS4.js.map → chunk-TBOVYLQJ.js.map} +0 -0
  211. /package/dist/{chunk-6CNRMCFM.js.map → chunk-VPASBA7P.js.map} +0 -0
  212. /package/dist/{chunk-V3ENMW7G.js.map → chunk-WFRTHH3A.js.map} +0 -0
  213. /package/dist/{chunk-ACKC577F.js.map → chunk-Y5Y7XHVU.js.map} +0 -0
  214. /package/dist/{copy-local-skills-KF2RAVD3.js.map → components/common/removal-plan-confirm.js.map} +0 -0
  215. /package/dist/{load-agent-defs-DGXGBU3Q.js.map → copy-local-skills-NSUTBVLJ.js.map} +0 -0
  216. /package/dist/{loader-4U7UCE2Y.js.map → load-agent-defs-X4E57I2U.js.map} +0 -0
  217. /package/dist/{recompile-project-agents-CJVBAWDJ.js.map → loader-G6BFG2VA.js.map} +0 -0
  218. /package/dist/{source-loader-ZNSYJJCI.js.map → recompile-project-agents-JE4MM72I.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,71 @@ Each release has detailed notes in its own file under [`changelogs/`](./changelo
7
7
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
8
8
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
9
9
 
10
+ ## [0.155.0] - 2026-08-18
11
+
12
+ **The editor and the CLI share one configuration**
13
+
14
+ - **Breaking:** `--source`/`-s` is now `--marketplace`/`-m`, the old spelling rejected rather than aliased, and `CC_SOURCE` is now `CC_MARKETPLACE` (CLI-463)
15
+ - **Breaking:** the config fields say what they hold — `marketplace` to `marketplaceName`, `source` to `marketplace`, `SkillConfig.source` to `origin`; an older `config.ts` is a hard error naming the pairs that moved (CLI-501)
16
+ - `share` and `edit --ui` mint a shared id from the installation in the current directory (CLI-354)
17
+ - `edit --from <id>` applies a configuration built in the editor, destructively and interactively (CLI-462)
18
+ - External skills install: a skill in neither catalogue travels in the payload as content (CLI-507)
19
+ - The round trip leaves what it does not own alone — a skill nobody installed is neither carried nor removed (CLI-508)
20
+ - Carry-back closes the loop, so a shared configuration survives more than one lap (CLI-509)
21
+ - The stale-id refusal names the remedy instead of a cause it cannot observe (CLI-510)
22
+ - `new marketplace` returns, and what it scaffolds works end to end (CLI-454)
23
+ - `build marketplace` emits the `catalog.json` the editor fetches (CLI-504)
24
+ - A locally authored skill installs (CLI-407), keeps its identity through the install (CLI-408), and one writer answers the custom question (CLI-409), completing the install half of custom skill support (D-212)
25
+ - A compiled agent says who compiled it — legs 2 and 3 (CLI-470)
26
+ - Two marketplaces can no longer name one skill: ids are namespaced by their marketplace (CLI-498)
27
+ - `catalog.json` becomes deterministic (CLI-411), carries what the editor needs (CLI-412) and is emitted from one place (CLI-413)
28
+ - One broken skill no longer blinds `doctor` — each skill is judged on its own (CLI-475)
29
+ - A global install refuses project-scoped content at the boundary (D-310)
30
+ - A global skill is removable from a project, with the user's informed consent (CLI-519)
31
+ - Three validators that judged the wrong subject: a parse boundary that reads as guarded (CLI-503), `doctor` judging a shared directory as if it owned it (CLI-505), and one more found in the same pass (CLI-513)
32
+ - The second producer stops re-costing its skips (CLI-511) and stops inheriting the first's refusals (CLI-512)
33
+ - The `FILES` enumeration is complete, and so are five others beside it (CLI-506)
34
+ - The unowned-install row stops claiming agent files `uninstall` refuses (CLI-515)
35
+ - The global split stops carrying the whole stack when no global agent survives (CLI-516)
36
+ - The two screens agree (CLI-517), and they agree on agents (CLI-522)
37
+ - The fan-out line counts projects reached, not rewrites (CLI-518)
38
+ - The removal reporting says what it did (CLI-521)
39
+ - The narrowing probe cannot be fooled by a syntax error (CLI-525), and the page is now checked (CLI-526)
40
+ - The harness stops handing the binary its own environment (CLI-523) and spawns it clean (CLI-524)
41
+ - The plugin suite stops writing into the machine that runs it (CLI-514)
42
+ - Journey 7's arc is actually run, and journey 9 closed with it (CLI-527)
43
+ - The gate that declined to judge, silently, now reports what it skipped (CLI-528)
44
+ - Prose that asserted behaviour the code no longer had (CLI-530), and a documented gap filled a day earlier (CLI-533)
45
+ - The enumeration-drift checker, filed five times in eighteen days, is finally built (CLI-531)
46
+ - The smoke files are finally run by something (CLI-520)
47
+ - Three of the four escape shapes are judgement and one is a selector (CLI-529), and the vacuous-comparison rules reach every workspace (CLI-532)
48
+
49
+ See [changelogs/0.155.0.md](./changelogs/0.155.0.md) for full details.
50
+
51
+ ## [0.154.0] - 2026-08-10
52
+
53
+ **The wizard stops reaching through the lock**
54
+
55
+ - A skill's `metadata.yaml` category now reaches the marketplace it is published to, so `build marketplace` stops reporting every plugin as uncategorized (CLI-481)
56
+ - A skill that reaches no sub-agent says so, at both the wizard and `compile` (CLI-491)
57
+ - Generated files order themselves, so a re-emit is a fixed point and an unchanged agent stops being rewritten (CLI-478)
58
+ - The Sources step's bulk install-mode hotkeys are withdrawn: in a project edit they rewrote inherited global rows the per-row toggle deliberately cannot reach, and really installed plugins (CLI-479)
59
+ - Project configurations carry the project's own name (CLI-482)
60
+ - Uninstall promises exactly what it removes — one plan, rendered and executed (CLI-494)
61
+ - One operation owns removing sub-agents and tidying their directory (CLI-495)
62
+ - The removal plan is honest when the configuration cannot be read: the compiled agents are named as kept, and why (CLI-470)
63
+ - Emptied `.claude/skills/` and `.claude/agents/` are removed, and a project-only install stops pre-creating an empty global agents directory (CLI-480)
64
+ - A sub-agent moved from global to project scope keeps its skill catalogue — it was silently shrinking from seven skills to four (CLI-483)
65
+ - `list` counts the plugin skills a project owns, instead of reporting `Skills: 0` from a project directory (CLI-484)
66
+ - `init` names the configuration that actually holds the agent-skill assignments (CLI-485)
67
+ - A refusal no longer paints over a live loading spinner (CLI-486)
68
+ - A moved source is downloaded once per run, not once per load — a full duplicate tarball for `github:` sources (CLI-487)
69
+ - The home root stops calling the global configuration a project one, in `compile`, `edit` and `doctor` (CLI-488)
70
+ - The plugin-to-eject direction of a mode switch reports the copies it made (CLI-489)
71
+ - `search` names the skill and the source it found, in the columns whose headers claim them (CLI-490)
72
+
73
+ See [changelogs/0.154.0.md](./changelogs/0.154.0.md) for full details.
74
+
10
75
  ## [0.153.0] - 2026-08-09
11
76
 
12
77
  **One marketplace at a time**
@@ -0,0 +1,246 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ SEED_API_URL,
4
+ SEED_USER_AGENT,
5
+ readCarriedSkills
6
+ } from "./chunk-PMUABBPG.js";
7
+ import {
8
+ ERROR_MESSAGES,
9
+ MATRIX_VERSION,
10
+ SEED_VERSION,
11
+ isScopePairCompatible,
12
+ loadProjectConfig,
13
+ readForkedFromMetadata,
14
+ resolveInstallPaths,
15
+ seedModelSchema,
16
+ seedPayloadSchema
17
+ } from "./chunk-WCAUUBHO.js";
18
+ import {
19
+ directoryExists,
20
+ getErrorMessage
21
+ } from "./chunk-XAXJWXI4.js";
22
+ import {
23
+ typedEntries,
24
+ typedValues
25
+ } from "./chunk-VASLW636.js";
26
+ import {
27
+ DEFAULT_PUBLIC_SOURCE_NAME,
28
+ EJECT_SOURCE
29
+ } from "./chunk-IO5YBKHV.js";
30
+
31
+ // src/cli/lib/seed/installation-payload.ts
32
+ import path from "path";
33
+
34
+ // src/cli/lib/seed/config-to-seed.ts
35
+ function isInstalled(entry) {
36
+ return !entry.excluded;
37
+ }
38
+ function needsMarketplaceNamed(origin) {
39
+ return origin !== EJECT_SOURCE && origin !== DEFAULT_PUBLIC_SOURCE_NAME;
40
+ }
41
+ function isSeedModel(model) {
42
+ return seedModelSchema.safeParse(model).success;
43
+ }
44
+ function seedLoadState(preloaded) {
45
+ return preloaded === true ? "preloaded" : "lazy";
46
+ }
47
+ function unnameableOriginMessage(skill) {
48
+ return `${skill.id} comes from the marketplace '${skill.origin}', and this installation does not record where that marketplace is fetched from`;
49
+ }
50
+ function unnameableModelMessage(agent, model) {
51
+ return `${agent} pins model '${model}', which this contract has no word for \u2014 leaving it out would say "keep the sub-agent's own metadata", which is a different instruction`;
52
+ }
53
+ function unwritablePairMessage(skillId, agent) {
54
+ return `${skillId} -> ${agent}: a project-scoped skill never reaches a sub-agent that rests at global scope`;
55
+ }
56
+ function unshareableConfigError(unshareable) {
57
+ return [
58
+ "This installation cannot be shared as it stands \u2014 a shared configuration has no way to carry:",
59
+ ...unshareable.map((line) => ` ${line}`),
60
+ "Sharing it anyway would mint an id that installs something else."
61
+ ].join("\n");
62
+ }
63
+ function isRecordedMarketplace(config, skill) {
64
+ return config.marketplace !== void 0 && skill.origin === config.marketplaceName;
65
+ }
66
+ function readMarketplace(config, skills) {
67
+ const served = skills.filter((skill) => needsMarketplaceNamed(skill.origin));
68
+ if (served.length === 0) return { ref: void 0, unnameable: [] };
69
+ const unaccounted = served.filter((skill) => !isRecordedMarketplace(config, skill));
70
+ if (unaccounted.length > 0) {
71
+ return { ref: void 0, unnameable: unaccounted.map(unnameableOriginMessage) };
72
+ }
73
+ return { ref: config.marketplace, unnameable: [] };
74
+ }
75
+ function unnameableModels(agents) {
76
+ return agents.flatMap(
77
+ (agent) => agent.model !== void 0 && !isSeedModel(agent.model) ? [unnameableModelMessage(agent.name, agent.model)] : []
78
+ );
79
+ }
80
+ function readStack(stack, skillScopes, agentScopes) {
81
+ const assignments = /* @__PURE__ */ new Map();
82
+ const unwritable = [];
83
+ for (const [agent, agentStack] of typedEntries(stack ?? {})) {
84
+ const agentScope = agentScopes.get(agent);
85
+ if (agentScope === void 0) continue;
86
+ for (const categoryAssignments of typedValues(agentStack)) {
87
+ for (const { id, preloaded } of categoryAssignments) {
88
+ const skillScope = skillScopes.get(id);
89
+ if (skillScope === void 0) continue;
90
+ if (!isScopePairCompatible(skillScope, agentScope)) {
91
+ unwritable.push(unwritablePairMessage(id, agent));
92
+ continue;
93
+ }
94
+ const forSkill = assignments.get(id) ?? {};
95
+ forSkill[agent] = seedLoadState(preloaded);
96
+ assignments.set(id, forSkill);
97
+ }
98
+ }
99
+ }
100
+ return { assignments, unwritable };
101
+ }
102
+ function toSeedSkill(skill, assignments) {
103
+ return {
104
+ // "eject" is the project's own copy; anything else is a plugin the marketplace serves, and
105
+ // which marketplace that is has already had to be one the envelope's ref accounts for.
106
+ install: skill.origin === EJECT_SOURCE ? "eject" : "plugin",
107
+ scope: skill.scope,
108
+ assignments: assignments ?? {}
109
+ };
110
+ }
111
+ function toSeedAgent(agent) {
112
+ return {
113
+ on: true,
114
+ scope: agent.scope,
115
+ ...agent.model !== void 0 && isSeedModel(agent.model) && { model: agent.model },
116
+ ...agent.effort !== void 0 && { effort: agent.effort }
117
+ };
118
+ }
119
+ function configToSeedPayload(config, carried) {
120
+ const skills = config.skills.filter(isInstalled);
121
+ const agents = config.agents.filter(isInstalled);
122
+ const skillScopes = new Map(skills.map((skill) => [skill.id, skill.scope]));
123
+ const agentScopes = new Map(agents.map((agent) => [agent.name, agent.scope]));
124
+ const { assignments, unwritable } = readStack(config.stack, skillScopes, agentScopes);
125
+ const marketplace = readMarketplace(config, skills);
126
+ const unshareable = [
127
+ ...marketplace.unnameable,
128
+ ...unnameableModels(agents),
129
+ ...unwritable,
130
+ ...carried.uncarryable
131
+ ];
132
+ if (unshareable.length > 0) throw new Error(unshareableConfigError(unshareable));
133
+ return seedPayloadSchema.parse({
134
+ v: SEED_VERSION,
135
+ matrixVersion: MATRIX_VERSION,
136
+ stackId: null,
137
+ ...marketplace.ref !== void 0 && { marketplace: marketplace.ref },
138
+ skills: Object.fromEntries(
139
+ skills.map((skill) => [skill.id, toSeedSkill(skill, assignments.get(skill.id))])
140
+ ),
141
+ // Absent rather than empty, for the reason the marketplace ref is: an id is the hash of its
142
+ // body, so a key meaning what its absence already means would remint every ordinary payload.
143
+ ...Object.keys(carried.external).length > 0 && { external: carried.external },
144
+ agents: Object.fromEntries(agents.map((agent) => [agent.name, toSeedAgent(agent)]))
145
+ });
146
+ }
147
+
148
+ // src/cli/lib/seed/installation-payload.ts
149
+ async function seedPayloadForInstallation(projectDir) {
150
+ const installed = await readInstalledConfig(projectDir);
151
+ if (!installed.ok) return installed;
152
+ const mapped = mapOrRefuse(await readRoundTrip(installed.config, projectDir));
153
+ if (!mapped.ok) return mapped;
154
+ const { payload } = mapped;
155
+ const skills = Object.keys(payload.skills).length;
156
+ const agents = Object.keys(payload.agents).length;
157
+ if (skills === 0 && agents === 0) return { ok: false, error: ERROR_MESSAGES.NO_INSTALLATION };
158
+ return { ok: true, payload, skills, agents };
159
+ }
160
+ async function readInstalledConfig(projectDir) {
161
+ try {
162
+ const loaded = await loadProjectConfig(projectDir);
163
+ if (!loaded) return { ok: false, error: ERROR_MESSAGES.NO_INSTALLATION };
164
+ return { ok: true, config: loaded.config };
165
+ } catch (error) {
166
+ return { ok: false, error: getErrorMessage(error) };
167
+ }
168
+ }
169
+ async function readRoundTrip(config, projectDir) {
170
+ const owned = (await judgeSkills(config, projectDir)).filter((entry) => entry.owned);
171
+ return {
172
+ config: { ...config, skills: owned.map((entry) => entry.skill) },
173
+ content: await readCarriedSkills(owned.flatMap((entry) => entry.carried ?? []))
174
+ };
175
+ }
176
+ function judgeSkills(config, projectDir) {
177
+ return Promise.all(config.skills.map((skill) => judgeSkill(skill, projectDir)));
178
+ }
179
+ async function skillsAuthoredHere(config, projectDir) {
180
+ const judged = await judgeSkills(config, projectDir);
181
+ return new Set(judged.filter((entry) => !entry.owned).map((entry) => entry.skill.id));
182
+ }
183
+ function travelsAsAnId(skill) {
184
+ return { skill, owned: true, carried: null };
185
+ }
186
+ async function judgeSkill(skill, projectDir) {
187
+ if (skill.origin !== EJECT_SOURCE) return travelsAsAnId(skill);
188
+ if (!isInstalled(skill)) return travelsAsAnId(skill);
189
+ const skillsDir = resolveInstallPaths(projectDir, skill.scope).skillsDir;
190
+ const skillDir = path.join(skillsDir, skill.id);
191
+ if (!await directoryExists(skillDir)) return travelsAsAnId(skill);
192
+ const provenance = await readForkedFromMetadata(skillDir);
193
+ if (provenance === null) return { skill, owned: false, carried: null };
194
+ return { skill, owned: true, carried: { id: skill.id, skillDir, provenance } };
195
+ }
196
+ function mapOrRefuse({ config, content }) {
197
+ try {
198
+ return { ok: true, payload: configToSeedPayload(config, content) };
199
+ } catch (error) {
200
+ return { ok: false, error: getErrorMessage(error) };
201
+ }
202
+ }
203
+
204
+ // src/cli/lib/seed/publish-seed.ts
205
+ import { z } from "zod";
206
+ var mintedSchema = z.object({ id: z.string().min(1) });
207
+ async function publishSeedConfig(payload) {
208
+ const url = `${SEED_API_URL}/configs`;
209
+ let response;
210
+ try {
211
+ response = await fetch(url, {
212
+ method: "POST",
213
+ headers: {
214
+ accept: "application/json",
215
+ "content-type": "application/json",
216
+ // The same signal the fetch half sends: it is the only thing that tells the store a
217
+ // configuration came from the CLI rather than from a browser.
218
+ "user-agent": SEED_USER_AGENT
219
+ },
220
+ body: JSON.stringify(payload)
221
+ });
222
+ } catch {
223
+ return { ok: false, error: `Could not reach ${SEED_API_URL} \u2014 check your connection.` };
224
+ }
225
+ if (!response.ok) {
226
+ return { ok: false, error: `Sharing this configuration failed (HTTP ${response.status}).` };
227
+ }
228
+ let body;
229
+ try {
230
+ body = await response.json();
231
+ } catch {
232
+ return { ok: false, error: "The configuration store returned something that is not JSON." };
233
+ }
234
+ const minted = mintedSchema.safeParse(body);
235
+ if (!minted.success) {
236
+ return { ok: false, error: "The configuration store did not return an id for this share." };
237
+ }
238
+ return { ok: true, id: minted.data.id };
239
+ }
240
+
241
+ export {
242
+ seedPayloadForInstallation,
243
+ skillsAuthoredHere,
244
+ publishSeedConfig
245
+ };
246
+ //# sourceMappingURL=chunk-2H7UWE2G.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/lib/seed/installation-payload.ts","../src/cli/lib/seed/config-to-seed.ts","../src/cli/lib/seed/publish-seed.ts"],"sourcesContent":["import path from \"path\";\n\nimport { configToSeedPayload, isInstalled } from \"./config-to-seed.js\";\nimport { readCarriedSkills } from \"./external-skills.js\";\nimport { EJECT_SOURCE } from \"../../consts.js\";\nimport { loadProjectConfig } from \"../configuration/project-config.js\";\nimport { resolveInstallPaths } from \"../installation/install-base-dir.js\";\nimport { readForkedFromMetadata } from \"../skills/skill-metadata.js\";\nimport { getErrorMessage } from \"../../utils/errors.js\";\nimport { directoryExists } from \"../../utils/fs.js\";\nimport { ERROR_MESSAGES } from \"../../utils/messages.js\";\n\nimport type { ContentReading, OwnedSkillDir } from \"./external-skills.js\";\nimport type { ProjectConfig, SkillConfig, SkillId } from \"../../types/index.js\";\nimport type { SeedPayload } from \"@workspace/matrix/seed\";\n\n/**\n * A payload ready to post, and what it will install — counted the way the sharer's own screens\n * count it, off the payload rather than off the config, so what a caller announces and what it\n * posts cannot disagree.\n */\nexport type InstallationPayload =\n { ok: true; payload: SeedPayload; skills: number; agents: number } | { ok: false; error: string };\n\n/** The config to map, or the sentence explaining why there is none to map. */\ntype InstalledConfig = { ok: true; config: ProjectConfig } | { ok: false; error: string };\n\n/** The mapped payload, or every line naming something the contract cannot carry. */\ntype MappedPayload = { ok: true; payload: SeedPayload } | { ok: false; error: string };\n\n/** The installation as the round trip sees it: what it owns, and what it has to carry with it. */\ntype RoundTrip = { config: ProjectConfig; content: ContentReading };\n\n/**\n * What the round trip makes of one installed entry: whether it travels at all, and whether its\n * bytes travel with it.\n */\ntype SkillJudgement = { skill: SkillConfig; owned: boolean; carried: OwnedSkillDir | null };\n\n/**\n * Read, map, refuse — everything that can fail locally, failing before anybody spends a write.\n *\n * Shared by the two commands that mint an id for what is installed here: `share`, which reports\n * the id, and `edit --ui`, which opens it. They are one operation with two endings, and this is\n * the half that has to be identical — two spellings of \"the installation in this directory\"\n * would mint two different ids for one project, and only one of them would be true.\n *\n * Every failure is a message rather than a throw, because nothing has been written by the time\n * it runs: the caller's only job is to explain, and both callers explain the same way.\n */\nexport async function seedPayloadForInstallation(projectDir: string): Promise<InstallationPayload> {\n const installed = await readInstalledConfig(projectDir);\n if (!installed.ok) return installed;\n\n const mapped = mapOrRefuse(await readRoundTrip(installed.config, projectDir));\n if (!mapped.ok) return mapped;\n\n const { payload } = mapped;\n const skills = Object.keys(payload.skills).length;\n const agents = Object.keys(payload.agents).length;\n\n // The same guard `init --from` applies on arrival, applied here before the write instead of\n // there after it. A sub-agent is installable on its own — it has front-matter, a prompt and a\n // compiled file without owning a single skill — so only a configuration with neither skills\n // nor sub-agents is nothing to carry.\n if (skills === 0 && agents === 0) return { ok: false, error: ERROR_MESSAGES.NO_INSTALLATION };\n\n return { ok: true, payload, skills, agents };\n}\n\n/**\n * The installation this directory has: its own configuration, or the global one it inherits.\n *\n * That is the config every other command reads here, and it carries the entries of BOTH scopes\n * with each entry's own scope on it — which is exactly what the wire keys by. So a payload minted\n * from a project describes the whole of what is installed for it rather than half of it, and the\n * receiver puts each entry back where this one had it.\n *\n * A config file that exists but cannot be loaded is a fault, not an absence, so its own message\n * is reported rather than read as \"not installed\".\n */\nasync function readInstalledConfig(projectDir: string): Promise<InstalledConfig> {\n try {\n const loaded = await loadProjectConfig(projectDir);\n if (!loaded) return { ok: false, error: ERROR_MESSAGES.NO_INSTALLATION };\n return { ok: true, config: loaded.config };\n } catch (error) {\n return { ok: false, error: getErrorMessage(error) };\n }\n}\n\n/**\n * The installation minus what the round trip does not own, plus the content it has to take along.\n *\n * `forkedFrom` decides ownership, and the round trip leaves what it does not own alone. The CLI\n * stamps that key into every skill directory it writes — an ejected catalogue skill, and a skill\n * a payload carried inline — while a skill the user wrote by hand into `.claude/skills/` carries\n * none. So absence of the key is not the whole answer and provenance is: what the CLI put there\n * says so on disk, and what a person put there says nothing because nothing forked it.\n *\n * A user-authored skill is therefore neither carried nor refused — it is simply outside the\n * round trip, and nothing about leaving it home is lossy because it was never in scope. Dropping\n * it here rather than in a command is what keeps `share` and `edit --ui`, which mint an id from\n * the same directory, from disagreeing about a single project.\n *\n * The stack rows naming a dropped skill go with it: `configToSeedPayload` keys assignments by\n * the skills it carries, so a row whose skill is gone is gone too.\n *\n * One walk answers both questions, because they are asked of the same file: whether the CLI put\n * this directory here, and — for the one kind of skill no catalogue can resolve — where its bytes\n * came from, so they can travel a second time.\n */\nasync function readRoundTrip(config: ProjectConfig, projectDir: string): Promise<RoundTrip> {\n const owned = (await judgeSkills(config, projectDir)).filter((entry) => entry.owned);\n\n return {\n config: { ...config, skills: owned.map((entry) => entry.skill) },\n content: await readCarriedSkills(owned.flatMap((entry) => entry.carried ?? [])),\n };\n}\n\n/** Every installed entry, judged. One walk of the disk, whichever half of the trip is asking. */\nfunction judgeSkills(config: ProjectConfig, projectDir: string): Promise<SkillJudgement[]> {\n return Promise.all(config.skills.map((skill) => judgeSkill(skill, projectDir)));\n}\n\n/**\n * The installed skills the round trip does NOT own — the user's own work, sitting in\n * `.claude/skills/` with no `forkedFrom` to say the CLI put it there.\n *\n * The producer already drops these on the way out, so no payload minted here ever named one.\n * This is the same judgement read by the other half: `edit --from` applies a payload\n * destructively, and a payload that made no statement about a skill cannot be read as an\n * instruction to delete it. Both halves ask {@link judgeSkill}, so \"outside the round trip\" has\n * one definition and cannot come to mean two things on one machine.\n */\nexport async function skillsAuthoredHere(\n config: ProjectConfig,\n projectDir: string,\n): Promise<Set<SkillId>> {\n const judged = await judgeSkills(config, projectDir);\n return new Set(judged.filter((entry) => !entry.owned).map((entry) => entry.skill.id));\n}\n\n/** The entry travels and no directory is read for it: a row naming an id is the whole of it. */\nfunction travelsAsAnId(skill: SkillConfig): SkillJudgement {\n return { skill, owned: true, carried: null };\n}\n\n/**\n * How one installed entry answers to the round trip — asked of the disk, offline, exactly as\n * `uninstall` asks it before removing anything.\n *\n * Only a directory that EXISTS and carries no provenance is somebody's own work, and that is the\n * one thing this excludes; each guard below says why the case above it never gets that far.\n */\nasync function judgeSkill(skill: SkillConfig, projectDir: string): Promise<SkillJudgement> {\n // A marketplace skill has no local directory to hold provenance and needs none: the marketplace\n // is what installs it again.\n if (skill.origin !== EJECT_SOURCE) return travelsAsAnId(skill);\n // A tombstone is a statement about something that is NOT installed here, so there are no bytes\n // of it to carry — and a refusal raised over them would refuse a share for a skill it is not\n // sharing.\n if (!isInstalled(skill)) return travelsAsAnId(skill);\n\n const skillsDir = resolveInstallPaths(projectDir, skill.scope).skillsDir;\n const skillDir = path.join(skillsDir, skill.id);\n // A configuration recording an install that is not there is evidence of nothing rather than\n // evidence of authorship, so it travels as the configuration records it.\n if (!(await directoryExists(skillDir))) return travelsAsAnId(skill);\n\n const provenance = await readForkedFromMetadata(skillDir);\n if (provenance === null) return { skill, owned: false, carried: null };\n\n return { skill, owned: true, carried: { id: skill.id, skillDir, provenance } };\n}\n\n/**\n * The mapping, with its refusals turned into this module's own currency.\n *\n * `configToSeedPayload` throws, and what it throws is a list: every line names something this\n * installation has that a shared configuration cannot say. That is a message for the user, not a\n * fault to propagate — the alternative is worse than a failed command, because a payload quietly\n * missing what it could not say still mints an id, and that id installs a configuration nobody\n * chose.\n */\nfunction mapOrRefuse({ config, content }: RoundTrip): MappedPayload {\n try {\n return { ok: true, payload: configToSeedPayload(config, content) };\n } catch (error) {\n return { ok: false, error: getErrorMessage(error) };\n }\n}\n","import { MATRIX_VERSION } from \"@workspace/matrix\";\nimport { SEED_VERSION, seedModelSchema, seedPayloadSchema } from \"@workspace/matrix/seed\";\n\nimport { DEFAULT_PUBLIC_SOURCE_NAME, EJECT_SOURCE } from \"../../consts.js\";\nimport { isScopePairCompatible } from \"../configuration/config-generator.js\";\nimport { typedEntries, typedValues } from \"../../utils/typed-object.js\";\n\nimport type { ContentReading } from \"./external-skills.js\";\nimport type {\n AgentScopeConfig,\n ProjectConfig,\n SkillConfig,\n SkillScope,\n} from \"../../types/config.js\";\nimport type { ModelName } from \"../../types/matrix.js\";\nimport type { SkillId } from \"../../types/skills.js\";\nimport type {\n SeedAgent,\n SeedLoadState,\n SeedModel,\n SeedPayload,\n SeedSkill,\n} from \"@workspace/matrix/seed\";\n\n/** Sub-agent name -> load state for one skill: the shape the wire keys by skill. */\ntype SeedAssignments = Record<string, SeedLoadState>;\n\n/** What one walk of `config.stack` yields: the wire's assignments, and the rows that cannot be one. */\ntype StackReading = {\n assignments: Map<SkillId, SeedAssignments>;\n /** Pairs the config model has nowhere to write, worded for the refusal. */\n unwritable: string[];\n};\n\n/**\n * An installed entry. An excluded one is a tombstone — a statement about something that is NOT\n * installed here — and presence is selection on the wire, so it has nothing to say. Nothing is\n * lost by leaving it home either: `init --from` installs into a clean setup, where there is no\n * global install for a tombstone to mask.\n *\n * Exported because the reader that gathers what an installation CARRIES has to agree with the\n * mapper about what it installs: content read for an entry this payload will not name is content\n * paid for by nobody, and a refusal raised over it would refuse a share for a skill it is not\n * sharing.\n */\nexport function isInstalled(entry: { excluded?: boolean }): boolean {\n return !entry.excluded;\n}\n\n/**\n * Whether a skill from this origin obliges the payload to name a marketplace.\n *\n * Two do not. An ejected copy travels with the project, and the default public catalogue is what\n * an absent ref already means — so neither puts a ref on the wire, and two identical selections\n * cannot mint two different ids over one.\n */\nfunction needsMarketplaceNamed(origin: string): boolean {\n return origin !== EJECT_SOURCE && origin !== DEFAULT_PUBLIC_SOURCE_NAME;\n}\n\n/**\n * Asked of the contract's own enum rather than spelled out here, so widening `MODEL_NAMES`\n * without widening the wire cannot slip past as a silently dropped pin.\n */\nfunction isSeedModel(model: ModelName): model is SeedModel {\n return seedModelSchema.safeParse(model).success;\n}\n\n/** The wire spells as an enum what a stack entry spells as a boolean, and an absent flag is lazy. */\nfunction seedLoadState(preloaded: boolean | undefined): SeedLoadState {\n return preloaded === true ? \"preloaded\" : \"lazy\";\n}\n\nfunction unnameableOriginMessage(skill: SkillConfig): string {\n return `${skill.id} comes from the marketplace '${skill.origin}', and this installation does not record where that marketplace is fetched from`;\n}\n\nfunction unnameableModelMessage(agent: AgentScopeConfig[\"name\"], model: ModelName): string {\n return `${agent} pins model '${model}', which this contract has no word for — leaving it out would say \"keep the sub-agent's own metadata\", which is a different instruction`;\n}\n\nfunction unwritablePairMessage(skillId: SkillId, agent: string): string {\n return `${skillId} -> ${agent}: a project-scoped skill never reaches a sub-agent that rests at global scope`;\n}\n\n/**\n * Names everything the contract cannot carry at once, not one thing at a time: a sharer who fixes\n * one only to be refused for the next learns nothing the first message could not have told them.\n *\n * The refusal exists because the alternative is worse than a failed command. A payload quietly\n * missing what it could not say still mints an id, and that id installs a configuration nobody\n * chose — with nothing on either side saying so.\n */\nfunction unshareableConfigError(unshareable: string[]): string {\n return [\n \"This installation cannot be shared as it stands — a shared configuration has no way to carry:\",\n ...unshareable.map((line) => ` ${line}`),\n \"Sharing it anyway would mint an id that installs something else.\",\n ].join(\"\\n\");\n}\n\n/** The ref a payload has to carry, and the skills no ref this config holds can account for. */\ntype MarketplaceReading = {\n /** Absent when nothing installed here came from a marketplace. */\n ref: string | undefined;\n unnameable: string[];\n};\n\n/**\n * Whether the marketplace this installation reads is the one `skill` came from.\n *\n * A skill's `origin` is a marketplace's NAME, `config.marketplace` is the ref it was fetched from,\n * and `marketplaceName` is what joins them — the name the install read out of that repository's\n * own manifest. Without both halves agreeing, the ref would send the receiver to a repository that\n * never served this skill, which is the same silent swap under a different spelling.\n */\nfunction isRecordedMarketplace(config: ProjectConfig, skill: SkillConfig): boolean {\n return config.marketplace !== undefined && skill.origin === config.marketplaceName;\n}\n\n/** Which marketplace the payload names, decided by the skills that oblige it to name one. */\nfunction readMarketplace(config: ProjectConfig, skills: SkillConfig[]): MarketplaceReading {\n const served = skills.filter((skill) => needsMarketplaceNamed(skill.origin));\n if (served.length === 0) return { ref: undefined, unnameable: [] };\n\n const unaccounted = served.filter((skill) => !isRecordedMarketplace(config, skill));\n if (unaccounted.length > 0) {\n return { ref: undefined, unnameable: unaccounted.map(unnameableOriginMessage) };\n }\n\n return { ref: config.marketplace, unnameable: [] };\n}\n\nfunction unnameableModels(agents: AgentScopeConfig[]): string[] {\n return agents.flatMap((agent) =>\n agent.model !== undefined && !isSeedModel(agent.model)\n ? [unnameableModelMessage(agent.name, agent.model)]\n : [],\n );\n}\n\n/**\n * Turns `config.stack` inside out: it is keyed by sub-agent and this contract is keyed by skill.\n *\n * Rows naming a sub-agent or a skill this configuration does not install are dropped rather than\n * refused, because they are already no part of what is installed — `compile` warns about exactly\n * those and leaves them out of the agents it writes, so carrying them would share more than this\n * project has.\n *\n * A row the config model cannot write is the opposite case and is collected for the refusal: the\n * decoder turns the same pair away on the way back in, so minting it would produce an id that\n * cannot be installed.\n */\nfunction readStack(\n stack: ProjectConfig[\"stack\"],\n skillScopes: Map<SkillId, SkillScope>,\n agentScopes: Map<string, SkillScope>,\n): StackReading {\n const assignments = new Map<SkillId, SeedAssignments>();\n const unwritable: string[] = [];\n\n for (const [agent, agentStack] of typedEntries(stack ?? {})) {\n const agentScope = agentScopes.get(agent);\n if (agentScope === undefined) continue;\n\n for (const categoryAssignments of typedValues(agentStack)) {\n for (const { id, preloaded } of categoryAssignments) {\n const skillScope = skillScopes.get(id);\n if (skillScope === undefined) continue;\n\n if (!isScopePairCompatible(skillScope, agentScope)) {\n unwritable.push(unwritablePairMessage(id, agent));\n continue;\n }\n\n const forSkill = assignments.get(id) ?? {};\n forSkill[agent] = seedLoadState(preloaded);\n assignments.set(id, forSkill);\n }\n }\n }\n\n return { assignments, unwritable };\n}\n\nfunction toSeedSkill(skill: SkillConfig, assignments: SeedAssignments | undefined): SeedSkill {\n return {\n // \"eject\" is the project's own copy; anything else is a plugin the marketplace serves, and\n // which marketplace that is has already had to be one the envelope's ref accounts for.\n install: skill.origin === EJECT_SOURCE ? \"eject\" : \"plugin\",\n scope: skill.scope,\n assignments: assignments ?? {},\n };\n}\n\n/**\n * A sub-agent's own row, and it always says `on`.\n *\n * The web app pins `on` only where a person did, because there an agent switched on by its rows is\n * already implied by them. An installed config is not an inference — every sub-agent it names was\n * chosen and has a compiled file on disk — so saying so outright is what carries the one that owns\n * no skill, which is the only thing the agents map can do that assignments cannot.\n */\nfunction toSeedAgent(agent: AgentScopeConfig): SeedAgent {\n return {\n on: true,\n scope: agent.scope,\n ...(agent.model !== undefined && isSeedModel(agent.model) && { model: agent.model }),\n ...(agent.effort !== undefined && { effort: agent.effort }),\n };\n}\n\n/**\n * Turns the installation this directory records into the payload the config store holds, so the\n * CLI can mint an id rather than only consume one.\n *\n * The inverse of {@link import(\"./seed-to-wizard.js\").seedToWizardResult}, and held to it: a\n * payload minted here and decoded there has to describe the same install. That is why the scope\n * of every skill and sub-agent travels per entry rather than being implied, why the whole stack\n * is carried as per-`(skill, sub-agent)` assignments, and why nothing here leans on a wire default\n * the decoder would have to guess back.\n *\n * What it will not do is share a thinner configuration than the one installed. Anything the\n * contract cannot state is refused by name — see {@link unshareableConfigError} — because the\n * failure mode of the alternative is an id that silently installs something else.\n *\n * `stackId` is always null. A saved config records a stack's expansion and never the id it came\n * from, and the assignments above carry that expansion in full; naming an id this module invented\n * would make the receiver overlay a stack's own preload flags over the curation being shared.\n *\n * `marketplace` is the one field carried for the payload rather than for an entry, because the\n * receiver loads one catalogue before it reads a single skill row — see {@link readMarketplace}.\n *\n * `carried` arrives as an argument because it is read off disk and this mapping touches none:\n * every id above is resolved by the receiver against a catalogue it already has, and the entries\n * in `carried` are the ones that answer to no catalogue in either direction. Stated rather than\n * defaulted, because a producer that forgot the content is exactly the defect this closed.\n */\nexport function configToSeedPayload(config: ProjectConfig, carried: ContentReading): SeedPayload {\n const skills = config.skills.filter(isInstalled);\n const agents = config.agents.filter(isInstalled);\n const skillScopes = new Map(skills.map((skill) => [skill.id, skill.scope]));\n const agentScopes = new Map<string, SkillScope>(agents.map((agent) => [agent.name, agent.scope]));\n\n const { assignments, unwritable } = readStack(config.stack, skillScopes, agentScopes);\n const marketplace = readMarketplace(config, skills);\n\n const unshareable = [\n ...marketplace.unnameable,\n ...unnameableModels(agents),\n ...unwritable,\n ...carried.uncarryable,\n ];\n if (unshareable.length > 0) throw new Error(unshareableConfigError(unshareable));\n\n // Parsed rather than merely assembled, exactly as the web app's encoder is: it makes \"the JSON\n // the store will hold\" literal, and it is the one gate that catches a field whose values have\n // drifted out of the wire's enums since this module last named them.\n return seedPayloadSchema.parse({\n v: SEED_VERSION,\n matrixVersion: MATRIX_VERSION,\n stackId: null,\n ...(marketplace.ref !== undefined && { marketplace: marketplace.ref }),\n skills: Object.fromEntries(\n skills.map((skill) => [skill.id, toSeedSkill(skill, assignments.get(skill.id))]),\n ),\n // Absent rather than empty, for the reason the marketplace ref is: an id is the hash of its\n // body, so a key meaning what its absence already means would remint every ordinary payload.\n ...(Object.keys(carried.external).length > 0 && { external: carried.external }),\n agents: Object.fromEntries(agents.map((agent) => [agent.name, toSeedAgent(agent)])),\n });\n}\n","import { z } from \"zod\";\n\nimport { SEED_API_URL, SEED_USER_AGENT } from \"./fetch-seed.js\";\n\nimport type { SeedPayload } from \"@workspace/matrix/seed\";\n\n/**\n * The store answers a stored configuration with its content-addressed id and nothing else.\n * Revalidated rather than read off the response type, because that type describes the worker this\n * CLI was built against and what answers is whatever is deployed.\n */\nconst mintedSchema = z.object({ id: z.string().min(1) });\n\nexport type PublishSeedResult = { ok: true; id: string } | { ok: false; error: string };\n\n/**\n * Stores a configuration and returns the id it was given.\n *\n * The outbound half of the boundary {@link import(\"./fetch-seed.js\").fetchSeedConfig} owns the\n * inbound half of, and it keeps the same posture: every failure is a message rather than a throw.\n * Nothing local has been written by the time this runs, so there is nothing to roll back and the\n * caller's only job is to explain.\n *\n * The id is the payload's own hash, so re-sharing an unchanged configuration returns the id it\n * had — the store spends no write, and a caller can share as often as it likes.\n */\nexport async function publishSeedConfig(payload: SeedPayload): Promise<PublishSeedResult> {\n const url = `${SEED_API_URL}/configs`;\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"POST\",\n headers: {\n accept: \"application/json\",\n \"content-type\": \"application/json\",\n // The same signal the fetch half sends: it is the only thing that tells the store a\n // configuration came from the CLI rather than from a browser.\n \"user-agent\": SEED_USER_AGENT,\n },\n body: JSON.stringify(payload),\n });\n } catch {\n return { ok: false, error: `Could not reach ${SEED_API_URL} — check your connection.` };\n }\n\n if (!response.ok) {\n return { ok: false, error: `Sharing this 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 minted = mintedSchema.safeParse(body);\n if (!minted.success) {\n // Reporting success here would print an id nobody can install and tell the user to use it.\n return { ok: false, error: \"The configuration store did not return an id for this share.\" };\n }\n\n return { ok: true, id: minted.data.id };\n}\n"],"mappings":"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","names":[]}
@@ -6,16 +6,16 @@ import {
6
6
  HOTKEY_TOGGLE_LABELS,
7
7
  KEY_SPACE,
8
8
  isHotkey
9
- } from "./chunk-2HC2SNRP.js";
9
+ } from "./chunk-WTJ3AY33.js";
10
10
  import {
11
11
  useSectionScroll
12
- } from "./chunk-Q5FLW4JG.js";
12
+ } from "./chunk-OKH64JZE.js";
13
13
  import {
14
14
  getSkillById
15
- } from "./chunk-35VOK7XE.js";
15
+ } from "./chunk-RWOYPOMR.js";
16
16
  import {
17
17
  CLI_COLORS
18
- } from "./chunk-R54KIGN2.js";
18
+ } from "./chunk-IO5YBKHV.js";
19
19
 
20
20
  // src/cli/components/wizard/category-grid.tsx
21
21
  import { useCallback as useCallback2, useEffect as useEffect2 } from "react";
@@ -266,4 +266,4 @@ var CategoryGrid = ({
266
266
  export {
267
267
  CategoryGrid
268
268
  };
269
- //# sourceMappingURL=chunk-ZNDLUWWD.js.map
269
+ //# sourceMappingURL=chunk-4A4L3YIW.js.map
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ readPluginManifest
4
+ } from "./chunk-WCAUUBHO.js";
5
+ import {
6
+ ensureDir,
7
+ glob,
8
+ verbose,
9
+ warn,
10
+ writeFile
11
+ } from "./chunk-XAXJWXI4.js";
12
+ import {
13
+ DEFAULT_PUBLIC_SOURCE_NAME,
14
+ DEFAULT_VERSION,
15
+ PLUGIN_MANIFEST_DIR,
16
+ PLUGIN_MANIFEST_FILE,
17
+ PUBLIC_CATALOGUE_PACKAGE
18
+ } from "./chunk-IO5YBKHV.js";
19
+
20
+ // src/cli/lib/marketplace-generator.ts
21
+ import path from "path";
22
+ import { countBy, sortBy } from "remeda";
23
+ var PLUGIN_MANIFEST_PATH = `${PLUGIN_MANIFEST_DIR}/${PLUGIN_MANIFEST_FILE}`;
24
+ var MARKETPLACE_SCHEMA_URL = "https://anthropic.com/claude-code/marketplace.schema.json";
25
+ var EXTERNAL_SKILL_NAMESPACE = "external";
26
+ var LOCAL_SKILL_NAMESPACE = "local";
27
+ var RESERVED_MARKETPLACE_NAMES = [
28
+ DEFAULT_PUBLIC_SOURCE_NAME,
29
+ EXTERNAL_SKILL_NAMESPACE,
30
+ LOCAL_SKILL_NAMESPACE
31
+ ];
32
+ var MAX_REPORTED_NAMESPACE_VIOLATIONS = 10;
33
+ function convertManifestToMarketplacePlugin(manifest, pluginRoot, pluginDirName) {
34
+ return {
35
+ name: manifest.name,
36
+ source: `./${pluginRoot}/${pluginDirName}`,
37
+ ...manifest.description !== void 0 && { description: manifest.description },
38
+ ...manifest.version !== void 0 && { version: manifest.version },
39
+ ...manifest.author !== void 0 && { author: manifest.author },
40
+ ...manifest.category !== void 0 && { category: manifest.category },
41
+ ...manifest.keywords !== void 0 && { keywords: manifest.keywords }
42
+ };
43
+ }
44
+ async function generateMarketplace(pluginsDir, options) {
45
+ verbose(`Scanning plugins directory: ${pluginsDir}`);
46
+ const manifestFiles = await glob(`**/${PLUGIN_MANIFEST_PATH}`, pluginsDir);
47
+ verbose(`Found ${manifestFiles.length} plugin manifests`);
48
+ const plugins = [];
49
+ for (const manifestFile of manifestFiles) {
50
+ const separator = manifestFile.indexOf("/");
51
+ const pluginDirName = separator === -1 ? manifestFile : manifestFile.slice(0, separator);
52
+ const pluginDir = path.join(pluginsDir, pluginDirName);
53
+ const manifest = await readPluginManifest(pluginDir);
54
+ if (!manifest) {
55
+ warn(`Could not read plugin manifest: '${manifestFile}'`);
56
+ continue;
57
+ }
58
+ const plugin = convertManifestToMarketplacePlugin(
59
+ manifest,
60
+ options.pluginRoot.replace(/^\.\//, ""),
61
+ pluginDirName
62
+ );
63
+ plugins.push(plugin);
64
+ verbose(` [OK] ${plugin.name}`);
65
+ }
66
+ const sortedPlugins = sortBy(plugins, (p) => p.name);
67
+ return {
68
+ $schema: MARKETPLACE_SCHEMA_URL,
69
+ name: options.name,
70
+ version: options.version ?? DEFAULT_VERSION,
71
+ owner: {
72
+ name: options.ownerName,
73
+ ...options.ownerEmail ? { email: options.ownerEmail } : {}
74
+ },
75
+ metadata: {
76
+ pluginRoot: options.pluginRoot
77
+ },
78
+ plugins: sortedPlugins,
79
+ ...options.description ? { description: options.description } : {}
80
+ };
81
+ }
82
+ function validateMarketplaceName(marketplaceName, packageName) {
83
+ if (!RESERVED_MARKETPLACE_NAMES.includes(marketplaceName)) return null;
84
+ if (isCatalogueOwnReservedName(marketplaceName, packageName)) return null;
85
+ return reservedNameError(marketplaceName);
86
+ }
87
+ function validateSkillIdNamespace(marketplace) {
88
+ if (marketplace.name === DEFAULT_PUBLIC_SOURCE_NAME) return null;
89
+ const foreignIds = marketplace.plugins.filter((plugin) => !carriesNamespace(plugin.name, marketplace.name)).map((plugin) => plugin.name);
90
+ if (foreignIds.length === 0) return null;
91
+ return namespaceViolationError(foreignIds, marketplace.name);
92
+ }
93
+ function isCatalogueOwnReservedName(marketplaceName, packageName) {
94
+ return marketplaceName === DEFAULT_PUBLIC_SOURCE_NAME && packageName === PUBLIC_CATALOGUE_PACKAGE;
95
+ }
96
+ function carriesNamespace(skillId, marketplaceName) {
97
+ return skillId.startsWith(`${marketplaceName}-`);
98
+ }
99
+ function reservedNameError(marketplaceName) {
100
+ return `Marketplace name '${marketplaceName}' is reserved. '${DEFAULT_PUBLIC_SOURCE_NAME}' is the public catalogue's own namespace, and '${EXTERNAL_SKILL_NAMESPACE}' and '${LOCAL_SKILL_NAMESPACE}' hold the skills that belong to no marketplace \u2014 publishing under any of them claims skill ids that are not this marketplace's. Choose a name of your own: set package.json 'name', or pass --name.`;
101
+ }
102
+ function namespaceViolationError(foreignIds, marketplaceName) {
103
+ const listed = foreignIds.slice(0, MAX_REPORTED_NAMESPACE_VIOLATIONS).map((id) => ` ${id} -> ${marketplaceName}-${id}`);
104
+ const unlisted = foreignIds.length - listed.length;
105
+ return [
106
+ `Marketplace '${marketplaceName}' ships ${foreignIds.length} skill id(s) outside its own namespace. Every skill id must begin with the marketplace's name:`,
107
+ ...listed,
108
+ ...unlisted > 0 ? [` ... and ${unlisted} more`] : [],
109
+ `Rename each skill directory and the id in its metadata, re-run 'build plugins', then build the marketplace again.`
110
+ ].join("\n");
111
+ }
112
+ async function writeMarketplace(outputPath, marketplace) {
113
+ await ensureDir(path.dirname(outputPath));
114
+ const content = `${JSON.stringify(marketplace, null, 2)}
115
+ `;
116
+ await writeFile(outputPath, content);
117
+ }
118
+ function getMarketplaceStats(marketplace) {
119
+ return {
120
+ total: marketplace.plugins.length,
121
+ byCategory: countBy(marketplace.plugins, (p) => p.category ?? "uncategorized")
122
+ };
123
+ }
124
+
125
+ export {
126
+ generateMarketplace,
127
+ validateMarketplaceName,
128
+ validateSkillIdNamespace,
129
+ writeMarketplace,
130
+ getMarketplaceStats
131
+ };
132
+ //# sourceMappingURL=chunk-4G2YHZIM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/lib/marketplace-generator.ts"],"sourcesContent":["import path from \"path\";\nimport { countBy, sortBy } from \"remeda\";\n\nimport {\n DEFAULT_PUBLIC_SOURCE_NAME,\n DEFAULT_VERSION,\n PLUGIN_MANIFEST_DIR,\n PLUGIN_MANIFEST_FILE,\n PUBLIC_CATALOGUE_PACKAGE,\n} from \"../consts\";\nimport { writeFile, glob, ensureDir } from \"../utils/fs\";\nimport { verbose, warn } from \"../utils/logger\";\nimport type { Marketplace, MarketplacePlugin, PluginManifest } from \"../types\";\nimport { readPluginManifest } from \"./plugins\";\n\nconst PLUGIN_MANIFEST_PATH = `${PLUGIN_MANIFEST_DIR}/${PLUGIN_MANIFEST_FILE}`;\nconst MARKETPLACE_SCHEMA_URL = \"https://anthropic.com/claude-code/marketplace.schema.json\";\n\n/** Namespace a skill with no marketplace at all is given. */\nconst EXTERNAL_SKILL_NAMESPACE = \"external\";\n\n/** Namespace a skill created in place, rather than fetched from a marketplace, is given. */\nconst LOCAL_SKILL_NAMESPACE = \"local\";\n\n/**\n * Marketplace names that are not an author's to take.\n *\n * The public catalogue's skills ship unprefixed, so every bare id in the catalogue\n * already lives in `agents-inc`; the other two hold the skills that belong to no\n * marketplace. Publishing under any of them claims ids the marketplace does not own.\n */\nconst RESERVED_MARKETPLACE_NAMES: readonly string[] = [\n DEFAULT_PUBLIC_SOURCE_NAME,\n EXTERNAL_SKILL_NAMESPACE,\n LOCAL_SKILL_NAMESPACE,\n];\n\n/** How many offending ids a namespace refusal lists before summarising the rest. */\nconst MAX_REPORTED_NAMESPACE_VIOLATIONS = 10;\n\ntype MarketplaceOptions = {\n name: string;\n version?: string;\n description?: string;\n ownerName: string;\n ownerEmail?: string;\n pluginRoot: string;\n};\n\nfunction convertManifestToMarketplacePlugin(\n manifest: PluginManifest,\n pluginRoot: string,\n pluginDirName: string,\n): MarketplacePlugin {\n return {\n name: manifest.name,\n source: `./${pluginRoot}/${pluginDirName}`,\n ...(manifest.description !== undefined && { description: manifest.description }),\n ...(manifest.version !== undefined && { version: manifest.version }),\n ...(manifest.author !== undefined && { author: manifest.author }),\n ...(manifest.category !== undefined && { category: manifest.category }),\n ...(manifest.keywords !== undefined && { keywords: manifest.keywords }),\n };\n}\n\nexport async function generateMarketplace(\n pluginsDir: string,\n options: MarketplaceOptions,\n): Promise<Marketplace> {\n verbose(`Scanning plugins directory: ${pluginsDir}`);\n\n const manifestFiles = await glob(`**/${PLUGIN_MANIFEST_PATH}`, pluginsDir);\n verbose(`Found ${manifestFiles.length} plugin manifests`);\n\n const plugins: MarketplacePlugin[] = [];\n\n for (const manifestFile of manifestFiles) {\n const separator = manifestFile.indexOf(\"/\");\n const pluginDirName = separator === -1 ? manifestFile : manifestFile.slice(0, separator);\n const pluginDir = path.join(pluginsDir, pluginDirName);\n\n const manifest = await readPluginManifest(pluginDir);\n if (!manifest) {\n warn(`Could not read plugin manifest: '${manifestFile}'`);\n continue;\n }\n\n const plugin = convertManifestToMarketplacePlugin(\n manifest,\n options.pluginRoot.replace(/^\\.\\//, \"\"),\n pluginDirName,\n );\n plugins.push(plugin);\n verbose(` [OK] ${plugin.name}`);\n }\n\n const sortedPlugins = sortBy(plugins, (p) => p.name);\n\n // Field order matters: the object is serialized verbatim into marketplace.json,\n // so conditional fields keep the same positions the previous emissions used.\n return {\n $schema: MARKETPLACE_SCHEMA_URL,\n name: options.name,\n version: options.version ?? DEFAULT_VERSION,\n owner: {\n name: options.ownerName,\n ...(options.ownerEmail ? { email: options.ownerEmail } : {}),\n },\n metadata: {\n pluginRoot: options.pluginRoot,\n },\n plugins: sortedPlugins,\n ...(options.description ? { description: options.description } : {}),\n };\n}\n\n/**\n * Refuses a marketplace name that belongs to a namespace the author does not own,\n * or returns null when the name is theirs to take.\n *\n * `packageName` is package.json's own `name`. It is read for one purpose: the public\n * catalogue publishes under a reserved name legitimately, and\n * {@link PUBLIC_CATALOGUE_PACKAGE} is what distinguishes it from a claimant.\n */\nexport function validateMarketplaceName(\n marketplaceName: string,\n packageName: string,\n): string | null {\n if (!RESERVED_MARKETPLACE_NAMES.includes(marketplaceName)) return null;\n if (isCatalogueOwnReservedName(marketplaceName, packageName)) return null;\n\n return reservedNameError(marketplaceName);\n}\n\n/**\n * Refuses a marketplace whose skill ids fall outside its own namespace, or returns\n * null when every id carries it.\n *\n * The public catalogue is exempt: its skills ship unprefixed by design and\n * `agents-inc` IS the namespace they occupy. Reading that exemption off the NAME is\n * only safe because {@link validateMarketplaceName} runs first and lets nothing but\n * the catalogue's own package hold it — the two checks are a pair, in that order.\n */\nexport function validateSkillIdNamespace(marketplace: Marketplace): string | null {\n if (marketplace.name === DEFAULT_PUBLIC_SOURCE_NAME) return null;\n\n const foreignIds = marketplace.plugins\n .filter((plugin) => !carriesNamespace(plugin.name, marketplace.name))\n .map((plugin) => plugin.name);\n\n if (foreignIds.length === 0) return null;\n\n return namespaceViolationError(foreignIds, marketplace.name);\n}\n\n/**\n * Whether the reserved NAME this build publishes under is the catalogue's own, which\n * takes both halves: the name claimed is the catalogue's, and the package claiming it\n * is the catalogue's package.\n *\n * The load side asks a different question of the same identity: `isPublicCatalogueCheckout`\n * in `configuration/config.ts` asks whether a DIRECTORY is a checkout of that repository,\n * and reads the package name off it rather than being handed one.\n */\nfunction isCatalogueOwnReservedName(marketplaceName: string, packageName: string): boolean {\n return marketplaceName === DEFAULT_PUBLIC_SOURCE_NAME && packageName === PUBLIC_CATALOGUE_PACKAGE;\n}\n\nfunction carriesNamespace(skillId: string, marketplaceName: string): boolean {\n return skillId.startsWith(`${marketplaceName}-`);\n}\n\nfunction reservedNameError(marketplaceName: string): string {\n return (\n `Marketplace name '${marketplaceName}' is reserved. ` +\n `'${DEFAULT_PUBLIC_SOURCE_NAME}' is the public catalogue's own namespace, and ` +\n `'${EXTERNAL_SKILL_NAMESPACE}' and '${LOCAL_SKILL_NAMESPACE}' hold the skills that belong to ` +\n `no marketplace — publishing under any of them claims skill ids that are not this ` +\n `marketplace's. Choose a name of your own: set package.json 'name', or pass --name.`\n );\n}\n\nfunction namespaceViolationError(foreignIds: string[], marketplaceName: string): string {\n const listed = foreignIds\n .slice(0, MAX_REPORTED_NAMESPACE_VIOLATIONS)\n .map((id) => ` ${id} -> ${marketplaceName}-${id}`);\n const unlisted = foreignIds.length - listed.length;\n\n return [\n `Marketplace '${marketplaceName}' ships ${foreignIds.length} skill id(s) outside its own ` +\n `namespace. Every skill id must begin with the marketplace's name:`,\n ...listed,\n ...(unlisted > 0 ? [` ... and ${unlisted} more`] : []),\n `Rename each skill directory and the id in its metadata, re-run 'build plugins', then ` +\n `build the marketplace again.`,\n ].join(\"\\n\");\n}\n\nexport async function writeMarketplace(\n outputPath: string,\n marketplace: Marketplace,\n): Promise<void> {\n await ensureDir(path.dirname(outputPath));\n const content = `${JSON.stringify(marketplace, null, 2)}\\n`;\n await writeFile(outputPath, content);\n}\n\nexport function getMarketplaceStats(marketplace: Marketplace): {\n total: number;\n byCategory: Record<string, number>;\n} {\n return {\n total: marketplace.plugins.length,\n byCategory: countBy(marketplace.plugins, (p) => p.category ?? \"uncategorized\"),\n };\n}\n"],"mappings":"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","names":[]}
@@ -4,11 +4,11 @@ import {
4
4
  } from "./chunk-TSIU7DME.js";
5
5
  import {
6
6
  CategoryGrid
7
- } from "./chunk-ZNDLUWWD.js";
7
+ } from "./chunk-4A4L3YIW.js";
8
8
  import {
9
9
  buildCategoriesForDomain,
10
10
  useWizardStore
11
- } from "./chunk-HEBPKK5L.js";
11
+ } from "./chunk-F5T6TV4Z.js";
12
12
 
13
13
  // src/cli/components/wizard/step-build.tsx
14
14
  import { Box, useInput } from "ink";
@@ -89,4 +89,4 @@ var StepBuild = ({
89
89
  export {
90
90
  StepBuild
91
91
  };
92
- //# sourceMappingURL=chunk-WDAO3RUY.js.map
92
+ //# sourceMappingURL=chunk-5F2YR5XV.js.map
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  isDomain
4
- } from "./chunk-NXQAY5U7.js";
4
+ } from "./chunk-XAXJWXI4.js";
5
5
  import {
6
6
  findStack
7
- } from "./chunk-35VOK7XE.js";
7
+ } from "./chunk-RWOYPOMR.js";
8
8
 
9
9
  // src/cli/components/wizard/utils.ts
10
10
  var DOMAIN_DISPLAY_NAMES = {
@@ -32,4 +32,4 @@ export {
32
32
  getDomainDisplayName,
33
33
  getStackName
34
34
  };
35
- //# sourceMappingURL=chunk-JFAUQDZM.js.map
35
+ //# sourceMappingURL=chunk-5OKLCVM4.js.map