agents-inc 0.164.0 → 0.166.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 (191) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +8 -9
  3. package/dist/{chunk-X4SSJZLY.js → chunk-25MPBIK3.js} +12 -261
  4. package/dist/chunk-25MPBIK3.js.map +1 -0
  5. package/dist/{chunk-LT5GXEB2.js → chunk-2VUYFTUB.js} +8 -7
  6. package/dist/chunk-2VUYFTUB.js.map +1 -0
  7. package/dist/chunk-4AAOOAIN.js +49 -0
  8. package/dist/chunk-4AAOOAIN.js.map +1 -0
  9. package/dist/{chunk-ARYNOWKF.js → chunk-4NDBUJHK.js} +8 -8
  10. package/dist/{chunk-S63LXWMC.js → chunk-6L75SQZU.js} +2 -2
  11. package/dist/{chunk-7REKOTKG.js → chunk-6LF4CQBY.js} +3 -3
  12. package/dist/{chunk-K5ITEA3I.js → chunk-7JNJ52CD.js} +34 -17
  13. package/dist/chunk-7JNJ52CD.js.map +1 -0
  14. package/dist/{chunk-XPDVAJDH.js → chunk-A4KDXNOT.js} +3519 -1568
  15. package/dist/chunk-A4KDXNOT.js.map +1 -0
  16. package/dist/{chunk-XHPWTL4I.js → chunk-DAIEQJIF.js} +3 -3
  17. package/dist/{chunk-UITEKY2R.js → chunk-ENGYJCTF.js} +28 -4
  18. package/dist/chunk-ENGYJCTF.js.map +1 -0
  19. package/dist/{chunk-G2JJ5BO5.js → chunk-G4JK7WVO.js} +336 -140
  20. package/dist/chunk-G4JK7WVO.js.map +1 -0
  21. package/dist/{chunk-X37UYIE3.js → chunk-G6UREQ4V.js} +436 -739
  22. package/dist/chunk-G6UREQ4V.js.map +1 -0
  23. package/dist/chunk-GE3WKJ6Q.js +375 -0
  24. package/dist/chunk-GE3WKJ6Q.js.map +1 -0
  25. package/dist/{chunk-RGYDFBCT.js → chunk-HHIYT3PI.js} +5 -5
  26. package/dist/{chunk-2CQNPLVF.js → chunk-HWEKHVLE.js} +63 -42
  27. package/dist/chunk-HWEKHVLE.js.map +1 -0
  28. package/dist/{chunk-XVAQG2CV.js → chunk-JOEA52NR.js} +10 -8
  29. package/dist/chunk-JOEA52NR.js.map +1 -0
  30. package/dist/{chunk-3WVH2CTK.js → chunk-JQRJQ4SS.js} +15 -8
  31. package/dist/chunk-JQRJQ4SS.js.map +1 -0
  32. package/dist/{chunk-DSL66I4N.js → chunk-LFWLEDUG.js} +6 -6
  33. package/dist/{chunk-B3Y3QMWI.js → chunk-MAWNVT2C.js} +4 -4
  34. package/dist/{chunk-Q4IRRVXK.js → chunk-MPTNC32T.js} +3 -3
  35. package/dist/chunk-NBXXFIIW.js +73 -0
  36. package/dist/chunk-NBXXFIIW.js.map +1 -0
  37. package/dist/{chunk-B6FSLXYN.js → chunk-PMJWD27C.js} +3 -3
  38. package/dist/{chunk-XATLUKXK.js → chunk-PNB3524H.js} +6 -6
  39. package/dist/chunk-PXWG7KFO.js +39 -0
  40. package/dist/chunk-PXWG7KFO.js.map +1 -0
  41. package/dist/{chunk-4VL3CEOS.js → chunk-QHDPLLK6.js} +3 -3
  42. package/dist/{chunk-AOTCIUGX.js → chunk-RAHSOJQ2.js} +2 -2
  43. package/dist/{chunk-PHCPUQVR.js → chunk-RZDXDMT6.js} +2 -2
  44. package/dist/{chunk-6MV4JOCL.js → chunk-SAYZWCH6.js} +2 -2
  45. package/dist/{chunk-URXIQE7I.js → chunk-SCOQ34YM.js} +2 -2
  46. package/dist/{chunk-NQXO4LR3.js → chunk-SXVHSOTH.js} +16 -11
  47. package/dist/chunk-SXVHSOTH.js.map +1 -0
  48. package/dist/{chunk-QZX7SYKU.js → chunk-T6UWJAB3.js} +2 -2
  49. package/dist/{chunk-ZVXGW2IC.js → chunk-TODCQFRB.js} +4 -6
  50. package/dist/chunk-TODCQFRB.js.map +1 -0
  51. package/dist/{chunk-YRS3QZP2.js → chunk-TTY6DPJN.js} +3 -3
  52. package/dist/{chunk-QGUP3QJF.js → chunk-TZ2YJJKK.js} +22 -18
  53. package/dist/{chunk-QGUP3QJF.js.map → chunk-TZ2YJJKK.js.map} +1 -1
  54. package/dist/chunk-V3I7VQ7N.js +356 -0
  55. package/dist/chunk-V3I7VQ7N.js.map +1 -0
  56. package/dist/{chunk-AVK2HEKB.js → chunk-V5WXIM2F.js} +2 -6
  57. package/dist/{chunk-AVK2HEKB.js.map → chunk-V5WXIM2F.js.map} +1 -1
  58. package/dist/{chunk-NKGYKOB7.js → chunk-VAPTVR3J.js} +7 -8
  59. package/dist/chunk-VAPTVR3J.js.map +1 -0
  60. package/dist/chunk-VRXX4D6K.js +31 -0
  61. package/dist/chunk-VRXX4D6K.js.map +1 -0
  62. package/dist/{chunk-ABAZMMG4.js → chunk-WVUDLKIO.js} +2 -2
  63. package/dist/{chunk-ECURHW3T.js → chunk-WWIFNPJG.js} +13 -12
  64. package/dist/chunk-WWIFNPJG.js.map +1 -0
  65. package/dist/{chunk-J27W6TXS.js → chunk-XQHZPINL.js} +8 -4
  66. package/dist/chunk-XQHZPINL.js.map +1 -0
  67. package/dist/{chunk-V5GLL2JB.js → chunk-XZEVWPX6.js} +7 -7
  68. package/dist/chunk-Y3VIRVVE.js +932 -0
  69. package/dist/chunk-Y3VIRVVE.js.map +1 -0
  70. package/dist/{chunk-NTF4UXGK.js → chunk-Y47EV74W.js} +73 -30
  71. package/dist/chunk-Y47EV74W.js.map +1 -0
  72. package/dist/chunk-ZR4YPKXQ.js +159 -0
  73. package/dist/chunk-ZR4YPKXQ.js.map +1 -0
  74. package/dist/chunk-ZXKBERQU.js +250 -0
  75. package/dist/chunk-ZXKBERQU.js.map +1 -0
  76. package/dist/commands/build/marketplace.js +17 -17
  77. package/dist/commands/build/marketplace.js.map +1 -1
  78. package/dist/commands/build/plugins.js +11 -9
  79. package/dist/commands/build/plugins.js.map +1 -1
  80. package/dist/commands/compile.js +105 -27
  81. package/dist/commands/compile.js.map +1 -1
  82. package/dist/commands/doctor.js +309 -171
  83. package/dist/commands/doctor.js.map +1 -1
  84. package/dist/commands/edit.js +503 -314
  85. package/dist/commands/edit.js.map +1 -1
  86. package/dist/commands/eject.js +40 -23
  87. package/dist/commands/eject.js.map +1 -1
  88. package/dist/commands/init.js +44 -40
  89. package/dist/commands/list.js +44 -10
  90. package/dist/commands/list.js.map +1 -1
  91. package/dist/commands/new/marketplace.js +13 -10
  92. package/dist/commands/new/marketplace.js.map +1 -1
  93. package/dist/commands/search.js +12 -10
  94. package/dist/commands/search.js.map +1 -1
  95. package/dist/commands/share.js +102 -15
  96. package/dist/commands/share.js.map +1 -1
  97. package/dist/commands/uninstall.js +231 -103
  98. package/dist/commands/uninstall.js.map +1 -1
  99. package/dist/commands/update.js +49 -20
  100. package/dist/commands/update.js.map +1 -1
  101. package/dist/components/common/prompt-confirm.js +1 -1
  102. package/dist/components/common/removal-plan-confirm.js +2 -2
  103. package/dist/components/common/select-list.js +3 -3
  104. package/dist/components/common/spinner.js +6 -3
  105. package/dist/components/wizard/category-grid.js +5 -5
  106. package/dist/components/wizard/checkbox-grid.js +4 -4
  107. package/dist/components/wizard/domain-selection.js +9 -9
  108. package/dist/components/wizard/run-wizard-session.js +29 -29
  109. package/dist/components/wizard/scroll-affordance.js +2 -2
  110. package/dist/components/wizard/skill-agent-summary.js +5 -5
  111. package/dist/components/wizard/source-grid.js +6 -6
  112. package/dist/components/wizard/stack-selection.js +8 -8
  113. package/dist/components/wizard/step-agents.js +8 -8
  114. package/dist/components/wizard/step-build.js +8 -8
  115. package/dist/components/wizard/step-confirm.js +9 -9
  116. package/dist/components/wizard/step-sources.js +9 -9
  117. package/dist/components/wizard/step-stack.js +9 -9
  118. package/dist/components/wizard/summary-panel.js +8 -8
  119. package/dist/components/wizard/toast.js +2 -2
  120. package/dist/components/wizard/wizard-layout.js +12 -12
  121. package/dist/components/wizard/wizard-tabs.js +2 -2
  122. package/dist/components/wizard/wizard.js +27 -27
  123. package/dist/hooks/init.js +69 -43
  124. package/dist/hooks/init.js.map +1 -1
  125. package/dist/load-agent-defs-3C27ENC7.js +14 -0
  126. package/dist/recompile-project-agents-DGNBCU3N.js +18 -0
  127. package/dist/{source-loader-GRBVNRAW.js → source-loader-7KZ3MD5O.js} +6 -4
  128. package/dist/src/agents/_templates/agent-body.liquid +55 -0
  129. package/dist/src/agents/_templates/agent.liquid +1 -55
  130. package/dist/src/agents/meta/agent-summoner/output.md +1 -1
  131. package/dist/src/agents/meta/agent-summoner/playbook.md +15 -7
  132. package/dist/stores/wizard-store.js +4 -4
  133. package/package.json +10 -9
  134. package/src/agents/_templates/agent-body.liquid +55 -0
  135. package/src/agents/_templates/agent.liquid +1 -55
  136. package/src/agents/meta/agent-summoner/output.md +1 -1
  137. package/src/agents/meta/agent-summoner/playbook.md +15 -7
  138. package/src/schemas/project-config.schema.json +1 -1
  139. package/src/schemas/project-source-config.schema.json +1 -1
  140. package/dist/chunk-2CQNPLVF.js.map +0 -1
  141. package/dist/chunk-3WVH2CTK.js.map +0 -1
  142. package/dist/chunk-7ANJQLPJ.js +0 -35
  143. package/dist/chunk-7ANJQLPJ.js.map +0 -1
  144. package/dist/chunk-ECURHW3T.js.map +0 -1
  145. package/dist/chunk-FLW25ORT.js +0 -130
  146. package/dist/chunk-FLW25ORT.js.map +0 -1
  147. package/dist/chunk-G2JJ5BO5.js.map +0 -1
  148. package/dist/chunk-J27W6TXS.js.map +0 -1
  149. package/dist/chunk-JCPANDNR.js +0 -35
  150. package/dist/chunk-JCPANDNR.js.map +0 -1
  151. package/dist/chunk-JIERYE6B.js +0 -480
  152. package/dist/chunk-JIERYE6B.js.map +0 -1
  153. package/dist/chunk-JU4ZGJ7A.js +0 -668
  154. package/dist/chunk-JU4ZGJ7A.js.map +0 -1
  155. package/dist/chunk-K5ITEA3I.js.map +0 -1
  156. package/dist/chunk-LT5GXEB2.js.map +0 -1
  157. package/dist/chunk-NKGYKOB7.js.map +0 -1
  158. package/dist/chunk-NQXO4LR3.js.map +0 -1
  159. package/dist/chunk-NTF4UXGK.js.map +0 -1
  160. package/dist/chunk-UITEKY2R.js.map +0 -1
  161. package/dist/chunk-X37UYIE3.js.map +0 -1
  162. package/dist/chunk-X4SSJZLY.js.map +0 -1
  163. package/dist/chunk-XPDVAJDH.js.map +0 -1
  164. package/dist/chunk-XVAQG2CV.js.map +0 -1
  165. package/dist/chunk-ZBPEFQ4G.js +0 -12
  166. package/dist/chunk-ZBPEFQ4G.js.map +0 -1
  167. package/dist/chunk-ZVXGW2IC.js.map +0 -1
  168. package/dist/load-agent-defs-7IM3GBEQ.js +0 -13
  169. package/dist/recompile-project-agents-VR4NLGOU.js +0 -17
  170. /package/dist/{chunk-ARYNOWKF.js.map → chunk-4NDBUJHK.js.map} +0 -0
  171. /package/dist/{chunk-S63LXWMC.js.map → chunk-6L75SQZU.js.map} +0 -0
  172. /package/dist/{chunk-7REKOTKG.js.map → chunk-6LF4CQBY.js.map} +0 -0
  173. /package/dist/{chunk-XHPWTL4I.js.map → chunk-DAIEQJIF.js.map} +0 -0
  174. /package/dist/{chunk-RGYDFBCT.js.map → chunk-HHIYT3PI.js.map} +0 -0
  175. /package/dist/{chunk-DSL66I4N.js.map → chunk-LFWLEDUG.js.map} +0 -0
  176. /package/dist/{chunk-B3Y3QMWI.js.map → chunk-MAWNVT2C.js.map} +0 -0
  177. /package/dist/{chunk-Q4IRRVXK.js.map → chunk-MPTNC32T.js.map} +0 -0
  178. /package/dist/{chunk-B6FSLXYN.js.map → chunk-PMJWD27C.js.map} +0 -0
  179. /package/dist/{chunk-XATLUKXK.js.map → chunk-PNB3524H.js.map} +0 -0
  180. /package/dist/{chunk-4VL3CEOS.js.map → chunk-QHDPLLK6.js.map} +0 -0
  181. /package/dist/{chunk-AOTCIUGX.js.map → chunk-RAHSOJQ2.js.map} +0 -0
  182. /package/dist/{chunk-PHCPUQVR.js.map → chunk-RZDXDMT6.js.map} +0 -0
  183. /package/dist/{chunk-6MV4JOCL.js.map → chunk-SAYZWCH6.js.map} +0 -0
  184. /package/dist/{chunk-URXIQE7I.js.map → chunk-SCOQ34YM.js.map} +0 -0
  185. /package/dist/{chunk-QZX7SYKU.js.map → chunk-T6UWJAB3.js.map} +0 -0
  186. /package/dist/{chunk-YRS3QZP2.js.map → chunk-TTY6DPJN.js.map} +0 -0
  187. /package/dist/{chunk-ABAZMMG4.js.map → chunk-WVUDLKIO.js.map} +0 -0
  188. /package/dist/{chunk-V5GLL2JB.js.map → chunk-XZEVWPX6.js.map} +0 -0
  189. /package/dist/{load-agent-defs-7IM3GBEQ.js.map → load-agent-defs-3C27ENC7.js.map} +0 -0
  190. /package/dist/{recompile-project-agents-VR4NLGOU.js.map → recompile-project-agents-DGNBCU3N.js.map} +0 -0
  191. /package/dist/{source-loader-GRBVNRAW.js.map → source-loader-7KZ3MD5O.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,69 @@ 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.166.0] - 2026-10-04
11
+
12
+ **From a project, `share`, `edit --ui` and every `--from` treat the global install as one every project reads, every write refuses a broken `config.ts` by name, and the audit that followed the journey fixes is applied**
13
+
14
+ - CLI-902: the 14 non-Codex failures from the 2026-10-01 manual journey run are fixed as ruled. From a project, `share` and `edit --ui` carry the global sub-agents' skills, and `share` refuses ejected skills because a share carries plugins only. Dropping a project copy writes nothing global, and `config.ts` rewrites keep `branding`. `edit` prints the trust line, and the editor's preview matches the install.
15
+ - **Every `--from` lists what goes into the project and what into the global install, then asks.** With no terminal, `init --from` prints the lists and carries on. From a project, `--from` never removes or changes anything global, and names what it kept. The plan discloses a dropped project copy. A kept skill joins the configuration's row, or yields a one-skill slot and stays installed, and every refusal comes before the question.
16
+ - **`init --marketplace` takes the bare-init route; `init --ui --marketplace` refuses.** A custom marketplace needs a valid `marketplace.json`, and `eject`, `local` and `external` are reserved at build and at load, recorded names included.
17
+ - **A broken `config.ts` is refused by every write, naming the file and the folder to recreate it from.** `uninstall` still works on one and clears both source folders, and `doctor` labels the config it read.
18
+ - **Commands that need a terminal refuse cleanly without one.** Raw mode is held before the first paint, Ctrl+C on the dashboard exits as cancelled, and the bare dashboard no longer prints help.
19
+ - **The Claude trust check follows git roots and `CLAUDE_CONFIG_DIR`, and `update` says truthfully that it refreshed listings, not plugins.** Skipped registered projects get bare `Skipped <path>: <reason>.` lines, and counts are singular for one.
20
+ - **`bun run dev` builds the local skill index and applies the local migrations.** The editor's Playwright suite runs on its own port. The modulepreload polyfill is off, and the first-paint budget is 352 KiB.
21
+ - CLI-868: `check-finding-citations` resolves a dated plan under `todo/plans/` as a plan, not a missing finding, and a `compile` unit test no longer depends on TMPDIR's length.
22
+
23
+ See [changelogs/0.166.0.md](./changelogs/0.166.0.md) for full details.
24
+
25
+ ## [0.165.0] - 2026-10-01
26
+
27
+ **A new installation's source folder is `.agents-inc/<provider>/`, a configuration can be installed onto OpenAI Codex, and `edit --from` applies a sub-agent's model and effort**
28
+
29
+ **The source folder.** No command moves an installation that already exists.
30
+
31
+ - **New installs write `.agents-inc/claude/`** instead of `.claude-src/`, at both scopes: `<project>/.agents-inc/claude/` and `~/.agents-inc/claude/`. The folder is named for the product and grouped by provider, so a Codex installation is `.agents-inc/codex/` with the same shape. One installation is exactly one provider and the folder is what says which; nothing inside `config.ts` records it.
32
+ - **Nothing in the CLI moves an existing installation.** An installation on `.claude-src/` is read and written where it is, by every command, indefinitely. There is no dual-write, no copy on first run, and no deprecation warning that changes where bytes land. Every message naming the config file names the folder the scope is actually on.
33
+ - **Moving a folder is a manual step, and there is no command for it.** The procedure is in `.ai-docs/reference/concepts/source-folder-layout.md`, along with its two gotchas: `config-types.ts`'s relative import gains a level, and a `.gitignore` with a bare `.agents-inc/` rule hides the moved config, so the next commit records only the deletion. Running `npx agents-inc compile` in the moved scope repairs the first.
34
+ - **`doctor` gains a `Layout` row per scope.** It reports which folder the scope is on, whether both folders are on disk and which one is being read, and any compiled sub-agent whose prompt names the folder this installation does not use. At `$HOME` it also lists the registered projects still on the old name, labelled as the registry rather than an inventory of the machine.
35
+ - **`init`, `edit`, `compile`, `update` and `eject` refuse outright while a scope holds both folders**, because the folder the resolver prefers can be the stale one. The refusal names the folder being read and asks you to merge by hand. `doctor` and `list` keep working in that state. A scope on the old name alone is a supported state, not a fault: no write command prints a line about it, and `doctor`'s `Layout` row reports it as a warning naming the move by hand.
36
+ - **Installed output keeps every path it had.** Claude still installs into `.claude/`, skills into `.claude/skills/` and compiled sub-agents into `.claude/agents/`. A shared payload names no source folder, so the rename reaches no share id.
37
+ - **A marketplace source repository gains no provider segment.** A repo declares itself in `.agents-inc/config.ts`, and one still declaring `.claude-src/config.ts` is read there with no sunset.
38
+ - **`uninstall` removes the `.agents-inc/` parent** once the last provider folder under it is gone, and only when the parent is otherwise empty. If you keep your own files under it, the run says it kept the parent rather than leaving it behind silently.
39
+ - **A compiled `agent-summoner` names the source folder its own installation uses**, substituted at compile time, rather than a literal that is wrong on half of all installs.
40
+
41
+ **OpenAI Codex as a second provider.** You pick the provider in the web app, and the folder on disk is the only record of which one it is.
42
+
43
+ - **The provider is chosen in the editor, not in the terminal.** A `provider` row in the roster footer picks it, and the command the Install dialog hands you becomes `npx agents-inc init --from <id> --provider codex`. The terminal wizard gains no step. `--provider` without `--from` exits `INVALID_ARGS`, naming `--from` and the editor.
44
+ - **Where one scope holds an installation of each provider, `--provider` is the disambiguator.** `edit`, `uninstall`, `share`, `eject`, `compile` and `update` refuse rather than guess, naming both. `list --provider` picks one. `doctor` reports the state instead of refusing.
45
+ - **Codex offers three of the four placements**: plugin + global, eject + global, and eject + project. Eject + project lands at `<repo>/.agents/skills/<id>/`, a committed directory that reaches the model with no plugin, no marketplace and no trust entry. `plugin + project` is refused by name before anything is written, never quietly ejected instead. The refusal also fires when a config is read, so a `.agents-inc/claude/` copied to `.agents-inc/codex/` is caught.
46
+ - **Sixteen of the eighteen sub-agents compile, as Codex agent role definitions in TOML.** `agent-summoner` and `skill-summoner` are left out for v1, and every Codex install says so once. A role file carries no `model`, since every model a sub-agent can name is Claude's and Codex refuses to start a role naming one; each sub-agent runs on your Codex session's model. Six settings have no Codex expression, and each compile says so once.
47
+ - **`doctor` gains a `Placements Offered` row.** It reports a placement no host can fill and a scope holding two installations, the states other commands stop on, and never refuses.
48
+ - **`uninstall` counts the plugins it observed being removed**, on either host, rather than the plugins it asked about.
49
+ - **What Codex cannot do**: install a plugin for one project; restrict a sub-agent's tools (`tools` is a configuration struct, not an allowlist, so the read-only sub-agents can still edit files); preload a named list of skills; or carry a sub-agent's `disallowedTools`, which is dropped without a mention (no shipped sub-agent declares one).
50
+ - CLI-893: a Codex project's role files need that project trusted in your global Codex config, so the install writes that entry and prints exactly what it wrote. Any later `compile` does the same. A `trust_level` you already set, `"untrusted"` included, is left as you set it and reported. Each of the four ways that trust fails is silent: no entry, `untrusted`, a trailing slash on the path, or the entry written inside the project.
51
+ - CLI-895: an ejected skill's copy is deleted from the folder Codex reads. Switching it to plugin or deselecting it in `edit` used to leave the copy, so Codex read the skill twice.
52
+ - CLI-896: `uninstall` on Codex removes the role files it compiled (`.codex/agents/*.toml`) and leaves a role you wrote yourself, as it does `.claude/agents/*.md` on Claude. `compile` prunes a deselected Codex role the same way.
53
+
54
+ **Fixes on either host.**
55
+
56
+ - CLI-898: the editor's output preview lists each skill with the description the install writes, its `SKILL.md` description, rather than the wizard's short label. `build marketplace` puts it in `catalog.json` as `activationDescription`; a catalogue built before this release still previews the label.
57
+ - CLI-899: the loading spinner's last frame no longer survives into the `init` and `edit` wizards. A frame held by Ink's render throttle was painted after the clear, so the full-height wizard lost its top line.
58
+ - `compile` no longer refuses an installation with sub-agents and no skills, which the editor makes and `init --from` installs. A config that declares skills, none of which are on disk, is still refused.
59
+ - `edit --from` applies a configuration whose only difference is a sub-agent's `model` or `effort`, and reports it as its own line. It used to print `No changes made.` and write nothing. Re-selecting a sub-agent in the wizard no longer reverts its model and effort.
60
+ - A local marketplace directory whose path contains a space, such as `/Users/My Name/…`, now reaches `claude` and `codex` instead of being refused by a pattern written for `owner/repo` references.
61
+
62
+ **On Claude Code, a project sub-agent's completion gate runs only in a folder you have trusted.**
63
+
64
+ - Claude Code drops the gate otherwise, without a word anywhere you would look. So `init` and `compile` now say so in one line naming the folder. They print it when a compiled sub-agent in `<project>/.claude/agents/` has at least one hook under `Stop` or `SubagentStop`, and `~/.claude.json` has no record of you accepting that folder's trust dialog. A run from the home directory never prints it. The CLI never accepts the dialog for you.
65
+
66
+ **Withdrawn before release.**
67
+
68
+ - **`npx agents-inc migrate`.** An earlier draft of this release carried a command that performed the move, with `--adopt`, `--dry-run`, a forwarding stub at the old path, a registered-project rewrite and a git-visibility probe. It was never requested and is deleted whole; nothing in the CLI hands out the invocation.
69
+ - A per-agent lint and check gate, including the `check:` map and a Codex gate plugin with its hook trust, was reverted before release on the owner's 2026-10-01 ruling, and 0.163.0's typecheck `Stop` hook is unchanged.
70
+
71
+ See [changelogs/0.165.0.md](./changelogs/0.165.0.md) for full details.
72
+
10
73
  ## [0.164.0] - 2026-09-06
11
74
 
12
75
  **Every shipped sub-agent moves to the 2026-09-03 doctrine, the web skills say when to reach for them, and the editor takes the 2026-09-05 design refresh**
package/README.md CHANGED
@@ -61,7 +61,6 @@ npx agents-inc init
61
61
  | [Editing your config](https://github.com/agents-inc/agents-inc/blob/main/apps/www/src/content/docs/docs/guides/editing-config.md) | Skill mappings, preloaded vs dynamic loading, and config structure |
62
62
  | [Customizing subagents](https://github.com/agents-inc/agents-inc/blob/main/apps/www/src/content/docs/docs/guides/customizing-subagents.md) | Eject and modify partials, templates, and skills |
63
63
  | [Writing custom skills and subagents](https://github.com/agents-inc/agents-inc/blob/main/apps/www/src/content/docs/docs/guides/writing-custom-skills.md) | Author skills and subagents from scratch or iterate on existing ones |
64
- | [Importing third-party skills](https://github.com/agents-inc/agents-inc/blob/main/apps/www/src/content/docs/docs/guides/importing-skills.md) | Install skills from external repositories |
65
64
  | [Creating a marketplace](https://github.com/agents-inc/agents-inc/blob/main/apps/www/src/content/docs/docs/guides/creating-a-marketplace.md) | Build a personal or org-level marketplace with curated skills |
66
65
  | [Documenting your codebase](https://github.com/agents-inc/agents-inc/blob/main/apps/www/src/content/docs/docs/guides/documenting-your-codebase.md) | Generate and maintain AI-focused reference documentation |
67
66
 
@@ -73,13 +72,13 @@ Over 200 skills across 9 domains:
73
72
  | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
74
73
  | Web | React, Vue, Angular, Svelte, SolidJS, Next.js, Remix, Nuxt, SvelteKit, Astro, Qwik, Tailwind, SCSS Modules, Zustand, Redux, Pinia, Vitest, Playwright, Storybook, and more |
75
74
  | API | Hono, Express, Fastify, NestJS, Elysia, Drizzle, Prisma, PostgreSQL, MongoDB, Redis, Stripe, and more |
76
- | AI | Anthropic SDK, OpenAI SDK, Vercel AI SDK, LangChain, LlamaIndex, Pinecone, ChromaDB, and more |
77
- | Mobile | React Native, Expo |
78
- | Desktop | Tauri, Electron, Electron Forge, electron-updater |
75
+ | AI | Anthropic SDK, OpenAI SDK, Vercel AI SDK, LangChain, LlamaIndex, and more |
76
+ | Mobile | React Native, Expo, and more |
77
+ | Desktop | Tauri, Electron, Electron Forge, electron-updater, and more |
79
78
  | CLI | oclif + Ink, Commander, Clack |
80
- | Infra | Docker, GitHub Actions, Cloudflare Workers |
81
- | Shared | Turborepo, ESLint + Prettier, Code Reviewing, Auth Security |
82
- | Meta | Research Methodology, CLI Reviewing |
79
+ | Infra | Docker, GitHub Actions, Cloudflare Workers, and more |
80
+ | Shared | Turborepo, ESLint + Prettier, Auth Security, and more |
81
+ | Meta | Code Reviewing, CLI Reviewing, Research Methodology, and more |
83
82
 
84
83
  Browse the full catalog on the [Plugin Marketplace](https://github.com/agents-inc/skills).
85
84
 
@@ -107,7 +106,7 @@ Each subagent is composed from modular partials (role, workflow, output format)
107
106
  | `edit` | Modify skill selection via the interactive wizard |
108
107
  | `compile` | Recompile subagents after changes |
109
108
  | `update` | Refresh the marketplaces this installation uses (ejected skills are yours) |
110
- | `search` | Search skills across all sources |
109
+ | `search` | Search this installation's marketplace and your local skills |
111
110
 
112
111
  ### Customization
113
112
 
@@ -119,7 +118,7 @@ Each subagent is composed from modular partials (role, workflow, output format)
119
118
 
120
119
  | Command | Description |
121
120
  | ------------------- | ---------------------------------------------- |
122
- | `build marketplace` | Generate `marketplace.json` from source skills |
121
+ | `build marketplace` | Generate `marketplace.json` from built plugins |
123
122
  | `build plugins` | Build skill and agent plugins for distribution |
124
123
 
125
124
  ### Diagnostics
@@ -1,256 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- claimSlug,
4
- computeFileHash,
5
- defaultUsageGuidance,
6
- directoryExists,
7
- glob,
8
- injectForkedFromMetadata,
9
- readFile,
10
- readForkedFromMetadata,
11
- readLocalSkillMetadata,
12
- readSkillMetadata,
13
- resolveInstallPaths,
14
- truncateText,
15
- validateSkillPath,
16
- writeFile,
17
- writeMetadataYaml
18
- } from "./chunk-XPDVAJDH.js";
3
+ truncateText
4
+ } from "./chunk-A4KDXNOT.js";
19
5
  import {
20
- BUILT_IN_MATRIX,
21
- getCategoryDomain,
22
- typedEntries,
23
- typedKeys
24
- } from "./chunk-AVK2HEKB.js";
25
- import {
26
- GITHUB_SOURCE,
27
- LOCAL_SKILLS_PATH,
28
- STANDARD_FILES,
29
- seedExternalSkillSchema,
30
6
  seedPayloadSchema
31
- } from "./chunk-UITEKY2R.js";
32
-
33
- // src/cli/lib/seed/external-skills.ts
34
- import path from "path";
35
- function pluginInstallError(ids) {
36
- return [
37
- "This configuration cannot be installed: these skills travel inside it, so no marketplace serves them and they cannot be installed as plugins:",
38
- ...ids.map((id) => ` ${id}`),
39
- "Re-share it with each skill above set to eject, which is the only way a carried skill installs."
40
- ].join("\n");
41
- }
42
- function selectedExternals(payload) {
43
- return typedEntries(payload.external ?? {}).flatMap(([id, skill]) => {
44
- const entry = payload.skills[id];
45
- return entry ? [{ id, skill, entry }] : [];
46
- });
47
- }
48
- function askedForAsPlugin(externals) {
49
- return externals.filter(({ entry }) => entry.install !== "eject").map(({ id }) => id);
50
- }
51
- function claimingACatalogueId(externals, matrix) {
52
- return externals.filter(({ id }) => heldByEitherCatalogue(id, matrix)).map(({ id }) => id);
53
- }
54
- var CATALOGUE_SKILL_IDS = new Set(typedKeys(BUILT_IN_MATRIX.skills));
55
- function heldByEitherCatalogue(id, matrix) {
56
- return heldByLoadedCatalogue(matrix.skills[id]) || CATALOGUE_SKILL_IDS.has(id);
57
- }
58
- function heldByLoadedCatalogue(incumbent) {
59
- return incumbent !== void 0 && incumbent.local !== true;
60
- }
61
- function catalogueIdCollisionError(ids) {
62
- return [
63
- "This configuration cannot be installed: these skills travel inside it under ids a catalogue already owns:",
64
- ...ids.map((id) => ` ${id}`),
65
- "A skill id is the directory the skill installs into, so each would be written over the catalogue's own copy of that skill. Re-share it with each skill above added again, which mints an id \u2014 'external-<category>-<name>' \u2014 that no catalogue owns."
66
- ].join("\n");
67
- }
68
- function externalSlug(id) {
69
- return id;
70
- }
71
- function skillAuthor(repo) {
72
- const [owner = repo] = repo.split("/");
73
- return `@${owner}`;
74
- }
75
- function repoRef(repo) {
76
- return `${GITHUB_SOURCE.GITHUB_PREFIX}${repo}`;
77
- }
78
- function externalSkillDir(id, scope, projectDir) {
79
- return path.join(resolveInstallPaths(projectDir, scope).skillsDir, id);
80
- }
81
- function externalCatalogueEntry(install) {
82
- return {
83
- id: install.id,
84
- slug: externalSlug(install.id),
85
- displayName: install.skill.displayName,
86
- description: install.skill.description,
87
- // Boundary cast held by the guard above it: the category was confirmed against the catalogue
88
- // the payload names, and this one has just been shown to declare it too.
89
- category: install.skill.categoryId,
90
- author: skillAuthor(install.skill.repo),
91
- path: `${LOCAL_SKILLS_PATH}/${install.id}/`,
92
- conflictsWith: [],
93
- requires: [],
94
- alternatives: [],
95
- discourages: [],
96
- // Local and custom for the same reason: the bytes are on this machine and nowhere else, and
97
- // the skill was authored outside the vocabulary this catalogue ships.
98
- local: true,
99
- custom: true,
100
- localPath: install.skillDir
101
- };
102
- }
103
- function seatExternalSkill({ id, skill, entry }, matrix, projectDir) {
104
- const domain = getCategoryDomain(skill.categoryId);
105
- if (domain === void 0) return [];
106
- const install = {
107
- id,
108
- skill,
109
- domain,
110
- scope: entry.scope,
111
- skillDir: externalSkillDir(id, entry.scope, projectDir)
112
- };
113
- matrix.skills[id] = externalCatalogueEntry(install);
114
- claimSlug(matrix.slugMap, externalSlug(id), id);
115
- return [install];
116
- }
117
- function registerExternalSkills(payload, matrix, projectDir) {
118
- const externals = selectedExternals(payload);
119
- const asPlugin = askedForAsPlugin(externals);
120
- const colliding = claimingACatalogueId(externals, matrix);
121
- if (asPlugin.length > 0) throw new Error(pluginInstallError(asPlugin));
122
- if (colliding.length > 0) throw new Error(catalogueIdCollisionError(colliding));
123
- return externals.flatMap((external) => seatExternalSkill(external, matrix, projectDir));
124
- }
125
- async function writeExternalSkills(installs) {
126
- await refuseUncarriedDestinations(installs);
127
- for (const install of installs) {
128
- await writeSkillTree(install);
129
- await writeSkillManifest(install);
130
- await registerSkillOnDisk(install);
131
- }
132
- }
133
- async function refuseUncarriedDestinations(installs) {
134
- const judged = await Promise.all(installs.map(judgeDestination));
135
- const blocked = judged.filter((destination) => destination !== null);
136
- if (blocked.length > 0) throw new Error(uncarriedDestinationError(blocked));
137
- }
138
- async function judgeDestination(install) {
139
- const { id, skillDir } = install;
140
- if (!await directoryExists(skillDir)) return null;
141
- if ((await readForkedFromMetadata(skillDir))?.path !== void 0) return null;
142
- return { id, skillDir };
143
- }
144
- function uncarriedDestinationError(blocked) {
145
- return [
146
- "This configuration cannot be installed: these skills travel inside it under ids this installation already keeps a skill directory for, and no shared configuration put those directories there:",
147
- ...blocked.map(({ id, skillDir }) => ` ${id} -> ${skillDir}`),
148
- "A skill id is the directory the skill installs into, so each would be written over a skill written here by hand or ejected from a marketplace. Move or rename the directory above, or re-share the configuration with each skill added again, which mints an id \u2014 'external-<category>-<name>' \u2014 that nothing here holds."
149
- ].join("\n");
150
- }
151
- async function writeSkillTree(install) {
152
- for (const [relativePath, contents] of Object.entries(install.skill.files)) {
153
- const filePath = path.join(install.skillDir, relativePath);
154
- validateSkillPath(filePath, install.skillDir, relativePath);
155
- await writeFile(filePath, contents);
156
- }
157
- }
158
- var FRONTMATTER_BLOCK = /^---\r?\n([\s\S]*?)\r?\n---/;
159
- var NAME_FIELD = /^name:.*$/m;
160
- function withInstalledName(manifest, install) {
161
- const named = `name: ${install.id}`;
162
- const block = FRONTMATTER_BLOCK.exec(manifest);
163
- if (!block) {
164
- return `---
165
- ${named}
166
- description: ${install.skill.description}
167
- ---
168
-
169
- ${manifest}`;
170
- }
171
- const [whole, fields = ""] = block;
172
- const renamed = NAME_FIELD.test(fields) ? fields.replace(NAME_FIELD, named) : `${named}
173
- ${fields}`;
174
- return `---
175
- ${renamed}
176
- ---${manifest.slice(whole.length)}`;
177
- }
178
- async function writeSkillManifest(install) {
179
- const manifestPath = path.join(install.skillDir, STANDARD_FILES.SKILL_MD);
180
- await writeFile(manifestPath, withInstalledName(await readFile(manifestPath), install));
181
- }
182
- function externalSkillMetadata(install, shipped) {
183
- return {
184
- author: skillAuthor(install.skill.repo),
185
- usageGuidance: defaultUsageGuidance(install.skill.categoryId),
186
- ...shipped,
187
- displayName: install.skill.displayName,
188
- slug: install.id,
189
- category: install.skill.categoryId,
190
- domain: install.domain,
191
- cliDescription: install.skill.description,
192
- custom: true
193
- };
194
- }
195
- async function registerSkillOnDisk(install) {
196
- const metadataPath = path.join(install.skillDir, STANDARD_FILES.METADATA_YAML);
197
- const shipped = await readLocalSkillMetadata(install.skillDir);
198
- await writeMetadataYaml(metadataPath, externalSkillMetadata(install, shipped));
199
- await injectForkedFromMetadata(
200
- install.skillDir,
201
- install.id,
202
- await computeFileHash(path.join(install.skillDir, STANDARD_FILES.SKILL_MD)),
203
- { source: repoRef(install.skill.repo), path: install.skill.path }
204
- );
205
- }
206
- function unnameableRepoMessage(id) {
207
- return `${id} travels inside a shared configuration, and this installation does not record the repository its content came from`;
208
- }
209
- function uncarryableContentMessage(id, reason) {
210
- return `${id} travels inside a shared configuration, and ${reason}`;
211
- }
212
- function repoFromRef(ref) {
213
- if (ref === void 0 || !ref.startsWith(GITHUB_SOURCE.GITHUB_PREFIX)) return void 0;
214
- return ref.slice(GITHUB_SOURCE.GITHUB_PREFIX.length);
215
- }
216
- async function readSkillTree(skillDir) {
217
- const files = await glob("**/*", skillDir, { dot: true });
218
- const entries = await Promise.all(
219
- files.map(async (file) => [file, await readFile(path.join(skillDir, file))])
220
- );
221
- return Object.fromEntries(entries);
222
- }
223
- async function readCarriedSkill({ id, skillDir, provenance }) {
224
- if (provenance.path === void 0) return { carries: "nothing" };
225
- const repo = repoFromRef(provenance.source);
226
- if (repo === void 0) return { carries: "unshareable", line: unnameableRepoMessage(id) };
227
- const described = await readSkillMetadata(path.join(skillDir, STANDARD_FILES.METADATA_YAML));
228
- if (!described.usable) {
229
- return { carries: "unshareable", line: uncarryableContentMessage(id, described.reason) };
230
- }
231
- const parsed = seedExternalSkillSchema.safeParse({
232
- displayName: described.metadata.displayName,
233
- description: described.metadata.cliDescription,
234
- categoryId: described.metadata.category,
235
- repo,
236
- path: provenance.path,
237
- files: await readSkillTree(skillDir)
238
- });
239
- if (!parsed.success) {
240
- const reason = parsed.error.issues.map((issue) => issue.message).join("; ");
241
- return { carries: "unshareable", line: uncarryableContentMessage(id, reason) };
242
- }
243
- return { carries: "content", id, skill: parsed.data };
244
- }
245
- async function readCarriedSkills(owned) {
246
- const reads = await Promise.all(owned.map(readCarriedSkill));
247
- return {
248
- external: Object.fromEntries(
249
- reads.flatMap((read) => read.carries === "content" ? [[read.id, read.skill]] : [])
250
- ),
251
- uncarryable: reads.flatMap((read) => read.carries === "unshareable" ? [read.line] : [])
252
- };
253
- }
7
+ } from "./chunk-ENGYJCTF.js";
254
8
 
255
9
  // ../../node_modules/hono/dist/utils/cookie.js
256
10
  var validCookieNameRegEx = /^[\w!#$%&'*.^`|~+-]+$/;
@@ -326,11 +80,11 @@ var serialize = (name, value, opt) => {
326
80
  };
327
81
 
328
82
  // ../../node_modules/hono/dist/client/utils.js
329
- var mergePath = (base, path2) => {
83
+ var mergePath = (base, path) => {
330
84
  base = base.replace(/\/+$/, "");
331
85
  base = base + "/";
332
- path2 = path2.replace(/^\/+/, "");
333
- return base + path2;
86
+ path = path.replace(/^\/+/, "");
87
+ return base + path;
334
88
  };
335
89
  var replaceUrlParam = (urlString, params) => {
336
90
  for (const [k, v] of Object.entries(params)) {
@@ -389,18 +143,18 @@ function deepMerge(target, source) {
389
143
  }
390
144
 
391
145
  // ../../node_modules/hono/dist/client/client.js
392
- var createProxy = (callback, path2) => {
146
+ var createProxy = (callback, path) => {
393
147
  const proxy = new Proxy(() => {
394
148
  }, {
395
149
  get(_obj, key) {
396
150
  if (typeof key !== "string" || key === "then") {
397
151
  return void 0;
398
152
  }
399
- return createProxy(callback, [...path2, key]);
153
+ return createProxy(callback, [...path, key]);
400
154
  },
401
155
  apply(_1, _2, args) {
402
156
  return callback({
403
- path: path2,
157
+ path,
404
158
  args
405
159
  });
406
160
  }
@@ -504,8 +258,8 @@ var hc = (baseUrl, options) => createProxy(function proxyCallback(opts) {
504
258
  method = last.replace(/^\$/, "");
505
259
  }
506
260
  }
507
- const path2 = parts.join("/");
508
- const url = mergePath(baseUrl, path2);
261
+ const path = parts.join("/");
262
+ const url = mergePath(baseUrl, path);
509
263
  if (method === "url" || method === "path") {
510
264
  let result = url;
511
265
  if (opts.args[0]) {
@@ -650,11 +404,8 @@ function explanationOf(body) {
650
404
  }
651
405
 
652
406
  export {
653
- registerExternalSkills,
654
- writeExternalSkills,
655
- readCarriedSkills,
656
407
  SEED_API_URL,
657
408
  SEED_USER_AGENT,
658
409
  fetchSeedConfig
659
410
  };
660
- //# sourceMappingURL=chunk-X4SSJZLY.js.map
411
+ //# sourceMappingURL=chunk-25MPBIK3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../node_modules/hono/dist/utils/cookie.js","../../../node_modules/hono/dist/client/utils.js","../../../node_modules/hono/dist/client/client.js","../../api/src/client.ts","../src/cli/lib/seed/fetch-seed.ts"],"sourcesContent":["// src/utils/cookie.ts\nimport { decodeURIComponent_, tryDecode } from \"./url.js\";\nvar algorithm = { name: \"HMAC\", hash: \"SHA-256\" };\nvar getCryptoKey = async (secret) => {\n const secretBuf = typeof secret === \"string\" ? new TextEncoder().encode(secret) : secret;\n return await crypto.subtle.importKey(\"raw\", secretBuf, algorithm, false, [\"sign\", \"verify\"]);\n};\nvar makeSignature = async (value, secret) => {\n const key = await getCryptoKey(secret);\n const signature = await crypto.subtle.sign(algorithm.name, key, new TextEncoder().encode(value));\n return btoa(String.fromCharCode(...new Uint8Array(signature)));\n};\nvar verifySignature = async (base64Signature, value, secret) => {\n try {\n const signatureBinStr = atob(base64Signature);\n const signature = new Uint8Array(signatureBinStr.length);\n for (let i = 0, len = signatureBinStr.length; i < len; i++) {\n signature[i] = signatureBinStr.charCodeAt(i);\n }\n return await crypto.subtle.verify(algorithm, secret, signature, new TextEncoder().encode(value));\n } catch {\n return false;\n }\n};\nvar validCookieNameRegEx = /^[\\w!#$%&'*.^`|~+-]+$/;\nvar relaxedCookieNameRegEx = /^[!#-:<>-[\\]-~]+$/;\nvar validCookieValueRegEx = /^[ !#-:<-[\\]-~]*$/;\nvar trimCookieWhitespace = (value) => {\n let start = 0;\n let end = value.length;\n while (start < end) {\n const charCode = value.charCodeAt(start);\n if (charCode !== 32 && charCode !== 9) {\n break;\n }\n start++;\n }\n while (end > start) {\n const charCode = value.charCodeAt(end - 1);\n if (charCode !== 32 && charCode !== 9) {\n break;\n }\n end--;\n }\n return start === 0 && end === value.length ? value : value.slice(start, end);\n};\nvar parse = (cookie, name) => {\n if (name && cookie.indexOf(name) === -1) {\n return {};\n }\n const pairs = cookie.split(\";\");\n const parsedCookie = /* @__PURE__ */ Object.create(null);\n for (const pairStr of pairs) {\n const valueStartPos = pairStr.indexOf(\"=\");\n if (valueStartPos === -1) {\n continue;\n }\n const cookieName = trimCookieWhitespace(pairStr.substring(0, valueStartPos));\n if (name && name !== cookieName || !relaxedCookieNameRegEx.test(cookieName) || cookieName in parsedCookie) {\n continue;\n }\n let cookieValue = trimCookieWhitespace(pairStr.substring(valueStartPos + 1));\n if (cookieValue.startsWith('\"') && cookieValue.endsWith('\"')) {\n cookieValue = cookieValue.slice(1, -1);\n }\n if (validCookieValueRegEx.test(cookieValue)) {\n parsedCookie[cookieName] = cookieValue.indexOf(\"%\") !== -1 ? tryDecode(cookieValue, decodeURIComponent_) : cookieValue;\n if (name) {\n break;\n }\n }\n }\n return parsedCookie;\n};\nvar parseSigned = async (cookie, secret, name) => {\n const parsedCookie = /* @__PURE__ */ Object.create(null);\n const secretKey = await getCryptoKey(secret);\n for (const [key, value] of Object.entries(parse(cookie, name))) {\n const signatureStartPos = value.lastIndexOf(\".\");\n if (signatureStartPos < 1) {\n continue;\n }\n const signedValue = value.substring(0, signatureStartPos);\n const signature = value.substring(signatureStartPos + 1);\n if (signature.length !== 44 || !signature.endsWith(\"=\")) {\n continue;\n }\n const isVerified = await verifySignature(signature, signedValue, secretKey);\n parsedCookie[key] = isVerified ? signedValue : false;\n }\n return parsedCookie;\n};\nvar _serialize = (name, value, opt = {}) => {\n if (!validCookieNameRegEx.test(name)) {\n throw new Error(\"Invalid cookie name\");\n }\n let cookie = `${name}=${value}`;\n if (name.startsWith(\"__Secure-\") && !opt.secure) {\n throw new Error(\"__Secure- Cookie must have Secure attributes\");\n }\n if (name.startsWith(\"__Host-\")) {\n if (!opt.secure) {\n throw new Error(\"__Host- Cookie must have Secure attributes\");\n }\n if (opt.path !== \"/\") {\n throw new Error('__Host- Cookie must have Path attributes with \"/\"');\n }\n if (opt.domain) {\n throw new Error(\"__Host- Cookie must not have Domain attributes\");\n }\n }\n for (const key of [\"domain\", \"path\", \"sameSite\", \"priority\"]) {\n if (opt[key] && /[;\\r\\n]/.test(opt[key])) {\n throw new Error(`${key} must not contain \";\", \"\\\\r\", or \"\\\\n\"`);\n }\n }\n if (opt && typeof opt.maxAge === \"number\" && opt.maxAge >= 0) {\n if (opt.maxAge > 3456e4) {\n throw new Error(\n \"Cookies Max-Age SHOULD NOT be greater than 400 days (34560000 seconds) in duration.\"\n );\n }\n cookie += `; Max-Age=${opt.maxAge | 0}`;\n }\n if (opt.domain && opt.prefix !== \"host\") {\n cookie += `; Domain=${opt.domain}`;\n }\n if (opt.path) {\n cookie += `; Path=${opt.path}`;\n }\n if (opt.expires) {\n if (opt.expires.getTime() - Date.now() > 3456e7) {\n throw new Error(\n \"Cookies Expires SHOULD NOT be greater than 400 days (34560000 seconds) in the future.\"\n );\n }\n cookie += `; Expires=${opt.expires.toUTCString()}`;\n }\n if (opt.httpOnly) {\n cookie += \"; HttpOnly\";\n }\n if (opt.secure) {\n cookie += \"; Secure\";\n }\n if (opt.sameSite) {\n cookie += `; SameSite=${opt.sameSite.charAt(0).toUpperCase() + opt.sameSite.slice(1)}`;\n }\n if (opt.priority) {\n cookie += `; Priority=${opt.priority.charAt(0).toUpperCase() + opt.priority.slice(1)}`;\n }\n if (opt.partitioned) {\n if (!opt.secure) {\n throw new Error(\"Partitioned Cookie must have Secure attributes\");\n }\n cookie += \"; Partitioned\";\n }\n return cookie;\n};\nvar serialize = (name, value, opt) => {\n value = encodeURIComponent(value);\n return _serialize(name, value, opt);\n};\nvar serializeSigned = async (name, value, secret, opt = {}) => {\n const signature = await makeSignature(value, secret);\n value = `${value}.${signature}`;\n value = encodeURIComponent(value);\n return _serialize(name, value, opt);\n};\nexport {\n parse,\n parseSigned,\n serialize,\n serializeSigned\n};\n","// src/client/utils.ts\nimport { fetchRP, DetailedError } from \"./fetch-result-please.js\";\nvar mergePath = (base, path) => {\n base = base.replace(/\\/+$/, \"\");\n base = base + \"/\";\n path = path.replace(/^\\/+/, \"\");\n return base + path;\n};\nvar replaceUrlParam = (urlString, params) => {\n for (const [k, v] of Object.entries(params)) {\n const reg = new RegExp(\"/:\" + k + \"(?:{[^/]+})?\\\\??(?=/|$)\");\n urlString = urlString.replace(reg, v ? `/${v}` : \"\");\n }\n return urlString;\n};\nvar buildSearchParams = (query) => {\n const searchParams = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v === void 0) {\n continue;\n }\n if (Array.isArray(v)) {\n for (const v2 of v) {\n searchParams.append(k, v2);\n }\n } else {\n searchParams.set(k, v);\n }\n }\n return searchParams;\n};\nvar replaceUrlProtocol = (urlString, protocol) => {\n switch (protocol) {\n case \"ws\":\n return urlString.replace(/^http/, \"ws\");\n case \"http\":\n return urlString.replace(/^ws/, \"http\");\n }\n};\nvar removeIndexString = (urlString) => {\n if (/^https?:\\/\\/[^\\/]+?\\/index(?=\\?|$)/.test(urlString)) {\n return urlString.replace(/\\/index(?=\\?|$)/, \"/\");\n }\n return urlString.replace(/\\/index(?=\\?|$)/, \"\");\n};\nfunction isObject(item) {\n return typeof item === \"object\" && item !== null && !Array.isArray(item);\n}\nfunction deepMerge(target, source) {\n if (!isObject(target) && !isObject(source)) {\n return source;\n }\n const merged = { ...target };\n for (const key in source) {\n const value = source[key];\n if (isObject(merged[key]) && isObject(value)) {\n merged[key] = deepMerge(merged[key], value);\n } else {\n merged[key] = value;\n }\n }\n return merged;\n}\nasync function parseResponse(fetchRes) {\n return fetchRP(fetchRes);\n}\nexport {\n DetailedError,\n buildSearchParams,\n deepMerge,\n mergePath,\n parseResponse,\n removeIndexString,\n replaceUrlParam,\n replaceUrlProtocol\n};\n","// src/client/client.ts\nimport { serialize } from \"../utils/cookie.js\";\nimport {\n buildSearchParams,\n deepMerge,\n mergePath,\n removeIndexString,\n replaceUrlParam,\n replaceUrlProtocol\n} from \"./utils.js\";\nvar createProxy = (callback, path) => {\n const proxy = new Proxy(() => {\n }, {\n get(_obj, key) {\n if (typeof key !== \"string\" || key === \"then\") {\n return void 0;\n }\n return createProxy(callback, [...path, key]);\n },\n apply(_1, _2, args) {\n return callback({\n path,\n args\n });\n }\n });\n return proxy;\n};\nvar ClientRequestImpl = class {\n url;\n method;\n buildSearchParams;\n queryParams = void 0;\n pathParams = {};\n rBody;\n cType = void 0;\n constructor(url, method, options) {\n this.url = url;\n this.method = method;\n this.buildSearchParams = options.buildSearchParams;\n }\n fetch = async (args, opt) => {\n if (args) {\n if (args.query) {\n this.queryParams = this.buildSearchParams(args.query);\n }\n if (args.form) {\n const form = new FormData();\n for (const [k, v] of Object.entries(args.form)) {\n if (v === void 0) {\n continue;\n }\n if (Array.isArray(v)) {\n for (const v2 of v) {\n form.append(k, v2);\n }\n } else {\n form.append(k, v);\n }\n }\n this.rBody = form;\n }\n if (args.json) {\n this.rBody = JSON.stringify(args.json);\n this.cType = \"application/json\";\n }\n if (args.param) {\n this.pathParams = args.param;\n }\n }\n let methodUpperCase = this.method.toUpperCase();\n const headerValues = {\n ...args?.header,\n ...typeof opt?.headers === \"function\" ? await opt.headers() : opt?.headers\n };\n if (args?.cookie) {\n const cookies = [];\n for (const [key, value] of Object.entries(args.cookie)) {\n cookies.push(serialize(key, value, { path: \"/\" }));\n }\n headerValues[\"Cookie\"] = cookies.join(\",\");\n }\n if (this.cType) {\n headerValues[\"Content-Type\"] = this.cType;\n }\n const headers = new Headers(headerValues ?? void 0);\n let url = this.url;\n url = removeIndexString(url);\n url = replaceUrlParam(url, this.pathParams);\n if (this.queryParams) {\n url = url + \"?\" + this.queryParams.toString();\n }\n methodUpperCase = this.method.toUpperCase();\n const setBody = !(methodUpperCase === \"GET\" || methodUpperCase === \"HEAD\");\n return (opt?.fetch || fetch)(url, {\n body: setBody ? this.rBody : void 0,\n method: methodUpperCase,\n headers,\n ...opt?.init\n });\n };\n};\nvar hc = (baseUrl, options) => createProxy(function proxyCallback(opts) {\n const buildSearchParamsOption = options?.buildSearchParams ?? buildSearchParams;\n const parts = [...opts.path];\n const lastParts = parts.slice(-3).reverse();\n if (lastParts[0] === \"toString\") {\n if (lastParts[1] === \"name\") {\n return lastParts[2] || \"\";\n }\n return proxyCallback.toString();\n }\n if (lastParts[0] === \"valueOf\") {\n if (lastParts[1] === \"name\") {\n return lastParts[2] || \"\";\n }\n return proxyCallback;\n }\n let method = \"\";\n if (/^\\$/.test(lastParts[0])) {\n const last = parts.pop();\n if (last) {\n method = last.replace(/^\\$/, \"\");\n }\n }\n const path = parts.join(\"/\");\n const url = mergePath(baseUrl, path);\n if (method === \"url\" || method === \"path\") {\n let result = url;\n if (opts.args[0]) {\n if (opts.args[0].param) {\n result = replaceUrlParam(url, opts.args[0].param);\n }\n if (opts.args[0].query) {\n result = result + \"?\" + buildSearchParamsOption(opts.args[0].query).toString();\n }\n }\n result = removeIndexString(result);\n if (method === \"url\") {\n return new URL(result);\n }\n return result.slice(baseUrl.replace(/\\/+$/, \"\").length).replace(/^\\/?/, \"/\");\n }\n if (method === \"ws\") {\n const webSocketUrl = replaceUrlProtocol(\n opts.args[0] && opts.args[0].param ? replaceUrlParam(url, opts.args[0].param) : url,\n \"ws\"\n );\n const targetUrl = new URL(webSocketUrl);\n const queryParams = opts.args[0]?.query;\n if (queryParams) {\n Object.entries(queryParams).forEach(([key, value]) => {\n if (Array.isArray(value)) {\n value.forEach((item) => targetUrl.searchParams.append(key, item));\n } else {\n targetUrl.searchParams.set(key, value);\n }\n });\n }\n const establishWebSocket = (...args) => {\n if (options?.webSocket !== void 0 && typeof options.webSocket === \"function\") {\n return options.webSocket(...args);\n }\n return new WebSocket(...args);\n };\n return establishWebSocket(targetUrl.toString());\n }\n const req = new ClientRequestImpl(url, method, {\n buildSearchParams: buildSearchParamsOption\n });\n if (method) {\n options ??= {};\n const reqOptions = { ...opts.args[1] };\n const baseHeaders = options.headers;\n const reqHeaders = reqOptions.headers;\n if (baseHeaders && reqHeaders) {\n reqOptions.headers = async () => ({\n ...typeof baseHeaders === \"function\" ? await baseHeaders() : baseHeaders,\n ...typeof reqHeaders === \"function\" ? await reqHeaders() : reqHeaders\n });\n }\n const args = deepMerge(options, reqOptions);\n return req.fetch(opts.args[0], args);\n }\n return req;\n}, []);\nexport {\n hc\n};\n","import { hc } from \"hono/client\"\n\nimport type { AppType } from \"server\"\n\n/**\n * The typed client both front doors reach the worker through.\n *\n * The editor and the CLI shared nothing before this package existed: the\n * editor configured `hc<AppType>` and the CLI hand-wrote a `fetch` at a URL it\n * built itself, so the two agreed about the worker's routes only by\n * coincidence. What they genuinely differ in is small and finite — where the\n * worker is, whether a session cookie rides along, and what identifies the\n * caller — so those are the parameters and everything else is shared.\n *\n * What this package deliberately does NOT own is either consumer's refusal\n * copy. The editor turns a failure into a union member a component renders;\n * the CLI turns one into a sentence a person reads in a terminal. Those are\n * different artefacts for different readers, and a shared \"message\" would be\n * wrong for both.\n *\n * `AppType` is a type and nothing else. `import type` erases before any\n * bundler sees it, so no worker code is reachable from the editor's bundle\n * (which fails its build on a first-paint budget) or from the CLI's.\n */\n\n/**\n * Whether the browser's session cookie rides along.\n *\n * Only the two values either consumer has a use for, rather than the full\n * `RequestCredentials`: `same-origin` is exactly the setting that breaks the\n * editor, because the cookie is set on `.agentsinc.sh` by a different origin\n * than the one the app is served from, and naming it here would make it\n * reachable by a typo.\n */\nexport type ApiCredentials = \"include\" | \"omit\"\n\n/**\n * What every request carries, however it is sent.\n *\n * `credentials` defaults to `\"include\"` and that direction is the point. The\n * session cookie is set by a different origin on the same registrable domain,\n * and without it sign-in appears to work and the very next request is\n * anonymous — with nothing logged on either side. A policy that has to be\n * opted INTO is one a new call site can forget silently; this one has to be\n * opted out of by name, which is a thing a reader can see.\n */\nexport type ApiRequestPolicy = {\n headers?: Record<string, string>\n credentials?: ApiCredentials\n}\n\nexport type ApiClientOptions = ApiRequestPolicy & {\n /** Where the worker is. No default: a wrong guess is a silent wrong API. */\n baseUrl: string\n /**\n * The seam tests reach through. hc calls\n * `(opt?.fetch || fetch)(url, { body, method, headers, ...opt?.init })`, so\n * injecting one is the only way to observe what this package produces without\n * a network — and the exact shape matters, because `init` is spread LAST.\n * That is why `headers` goes through hc's own option rather than through\n * `init`: in `init` it would replace the whole `Headers` object, including\n * the content type hc sets for a JSON body.\n */\n fetch?: typeof globalThis.fetch\n}\n\ntype ResolvedPolicy = {\n headers: Record<string, string>\n credentials: ApiCredentials\n}\n\n/**\n * The one place the defaults are applied, so the typed client and any\n * hand-written fetch cannot drift about what a request carries.\n */\nconst resolvePolicy = ({\n headers = {},\n credentials = \"include\",\n}: ApiRequestPolicy): ResolvedPolicy => ({ headers, credentials })\n\n/**\n * The policy as a `RequestInit`, for the paths `AppType` does not describe.\n *\n * Better Auth mounts its own surface under `/api/auth/*` with a single\n * handler rather than a described route, so there is nothing there for `hc` to\n * type — and a hand-written fetch at those paths is how the editor signs a\n * person in. This is what keeps that call on the same session policy as the\n * typed ones instead of on a second copy of it.\n *\n * Spread it LAST over a caller's own init: a caller may add a method, a body\n * or headers of its own, and may not drop the cookie.\n */\nexport const apiRequestInit = (policy: ApiRequestPolicy): RequestInit =>\n resolvePolicy(policy)\n\n/**\n * Builds the client for one consumer's base URL, headers and session policy.\n *\n * The two configured values go to different places in hc's options, and which\n * goes where is load-bearing. hc spreads `init` LAST over the request it\n * built, so anything named there replaces what hc put in that field — a\n * `headers` in `init` would replace the whole `Headers` object, including the\n * `Content-Type: application/json` hc sets for a `$post({ json })`. Headers go\n * through hc's own `headers` option, which is merged into that object instead,\n * and `init` names `credentials` and nothing else.\n */\nexport const createApiClient = ({\n baseUrl,\n fetch,\n ...policy\n}: ApiClientOptions) => {\n const { headers, credentials } = resolvePolicy(policy)\n\n return hc<AppType>(baseUrl, {\n headers,\n init: { credentials },\n // Spread rather than named. Under `exactOptionalPropertyTypes` hc's\n // optional `fetch` does not accept an explicit `undefined`, so writing\n // `fetch,` here is a TS2379 whenever a caller omits one.\n ...(fetch && { fetch }),\n })\n}\n\n/** The configured client, for consumers that pass one around. */\nexport type ApiClient = ReturnType<typeof createApiClient>\n","import { createApiClient } from \"@workspace/api\";\nimport { seedPayloadSchema, type SeedPayload } from \"@workspace/matrix/seed\";\n\nimport { truncateText } from \"../../utils/string.js\";\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\n/**\n * The worker's routes, typed from the worker itself, configured for a terminal.\n *\n * `credentials: \"omit\"` is named rather than left to the shared default. The default is `include`\n * because the editor's session is a cookie the browser holds; a terminal holds no cookie jar at\n * all, so inheriting that default would claim a session that cannot exist.\n *\n * Built once at module scope, like `SEED_API_URL` above and for the same reason — the base URL is\n * read from the environment once at import, so there is nothing per-call for a client to pick up.\n */\nconst store = createApiClient({\n baseUrl: SEED_API_URL,\n headers: { accept: \"application/json\", \"user-agent\": SEED_USER_AGENT },\n credentials: \"omit\",\n});\n\n/**\n * How much of the store's account of a refused read to quote back.\n *\n * Sized on what this route really says, which is one short line per refusal: `Stored config is\n * unreadable` and `Could not read this config` are both under thirty characters. This holds the\n * longer of them four times over — room for that sentence to grow into a real one, and none for\n * anything that has stopped being a sentence.\n *\n * Far short of the 300-character `EXPLANATION_BUDGET` in `publish-seed.ts`, and for the reason\n * that one gives for its own size rather than in spite of it: a budget is sized on what the store\n * really writes there, and what it writes there is a list of schema issues naming a skill and a\n * sub-agent apiece. Neither number is a policy about terminals; each is a measurement of one\n * route.\n */\nconst EXPLANATION_BUDGET = 120;\n\n/** The content type the store's own refusals arrive as, and the only one worth reading. */\nconst QUOTABLE_TYPE = \"text/plain\";\n\n/** The status the store spends on an id it has never held, which is the one it is asked for. */\nconst NO_SUCH_CONFIG = 404;\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 let response: Response;\n try {\n // Escaped here rather than by the client: hono's `replaceUrlParam` substitutes a path\n // parameter verbatim, so an id carrying a `/` or a `?` would silently address another route.\n response = await store.configs[\":id\"].$get({ param: { id: encodeURIComponent(id) } });\n } catch {\n return { ok: false, error: `Could not reach ${SEED_API_URL} — check your connection.` };\n }\n\n // Ahead of the quoting arm below, and the only status held out of it. The store answers this\n // one `No config under this id`, which says strictly less than the sentence here does — it does\n // not name the id the caller typed, and that is the whole of what a person needs to see.\n if (response.status === NO_SUCH_CONFIG) {\n return { ok: false, error: `No configuration found for id '${id}'.` };\n }\n if (!response.ok) {\n return { ok: false, error: await refusalMessage(response) };\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\n/**\n * A refusal, carrying the store's own account of it wherever there is one to have.\n *\n * The same posture `refusalMessage` in `publish-seed.ts` takes on the write side, and deliberately\n * NOT the same mechanism. That route's 400 arrives as a `@hono/zod-validator` envelope with the\n * refused issues rendered inside it; `getConfig` writes every one of its refusals with `c.text`,\n * so there is no envelope here to parse and a copy of that parser would find nothing in any body\n * this route can produce.\n *\n * The status is never given up. It is what separates a store that is present and holding bytes it\n * can no longer read (500) from one that would not answer at all (503) — and what the body then\n * adds is the store saying which of its own failures this was, in words the status has no room\n * for.\n */\nasync function refusalMessage(response: Response): Promise<string> {\n const refused = `Fetching configuration failed (HTTP ${response.status}).`;\n const explanation = explanationOf(await refusalBody(response));\n\n return explanation === undefined ? refused : `${refused} The store said: ${explanation}`;\n}\n\n/**\n * Whatever the store sent, as far as it can be read — and only where it was the store that sent it.\n *\n * Nothing below this line is worth failing over: the message being assembled is already a failure,\n * and a body that cannot be read is one more thing the status will have to cover on its own.\n * Draining the stream can throw in its own right — a connection dropped mid-body — which is why\n * the `try` is around the read.\n */\nasync function refusalBody(response: Response): Promise<string | undefined> {\n if (!arrivedAsText(response)) return undefined;\n\n try {\n return await response.text();\n } catch {\n return undefined;\n }\n}\n\n/**\n * Whether what arrived is the store speaking, as far as the wire says it is.\n *\n * `getConfig` writes every refusal with `c.text(...)`, so `text/plain` is the shape the store's\n * own words take on this route. What arrives as anything else did not come from it: a proxy, a\n * WAF, a captive portal or a gateway answering in its place — and their answer is markup rather\n * than prose, which quoted into a terminal is a screenful of tags that explains nothing and buries\n * the status that does.\n *\n * This is the plain-text half of what `refusalSchema` does for `publish-seed.ts`. That side has a\n * SHAPE a foreign body fails to be; a sentence has no shape, so the wire's own statement of what\n * it sent is the only discriminator there is. It is not proof — a hostile answer may claim any\n * type it likes — which is what {@link EXPLANATION_BUDGET} is for, and why what survives both is\n * attributed to the store rather than spoken in the CLI's own voice.\n */\nfunction arrivedAsText(response: Response): boolean {\n return (response.headers.get(\"content-type\") ?? \"\").startsWith(QUOTABLE_TYPE);\n}\n\n/** The store's account of a refusal, where what came back really is one. */\nfunction explanationOf(body: string | undefined): string | undefined {\n if (body === undefined) return undefined;\n\n const said = body.trim();\n if (said === \"\") return undefined;\n\n return truncateText(said, EXPLANATION_BUDGET);\n}\n"],"mappings":"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","names":["fetch"]}
@@ -1,26 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ScrollAffordance
4
- } from "./chunk-6MV4JOCL.js";
4
+ } from "./chunk-SAYZWCH6.js";
5
5
  import {
6
6
  getStackName
7
- } from "./chunk-4VL3CEOS.js";
7
+ } from "./chunk-QHDPLLK6.js";
8
8
  import {
9
9
  useMeasuredHeight
10
10
  } from "./chunk-TSIU7DME.js";
11
11
  import {
12
12
  SkillAgentSummary
13
- } from "./chunk-B3Y3QMWI.js";
13
+ } from "./chunk-MAWNVT2C.js";
14
14
  import {
15
15
  useWizardStore
16
- } from "./chunk-K5ITEA3I.js";
16
+ } from "./chunk-7JNJ52CD.js";
17
17
  import {
18
18
  ALL_SKILLS_EJECTED_LABEL,
19
19
  CLI_COLORS,
20
20
  DEFAULT_PUBLIC_SOURCE_NAME,
21
21
  EJECT_SOURCE,
22
22
  formatSourceDisplayName
23
- } from "./chunk-UITEKY2R.js";
23
+ } from "./chunk-ENGYJCTF.js";
24
24
 
25
25
  // src/cli/components/wizard/summary-panel.tsx
26
26
  import { Box, Text } from "ink";
@@ -95,8 +95,9 @@ var SummaryPanel = () => {
95
95
  const skillConfigs = useWizardStore((s) => s.skillConfigs);
96
96
  const agentConfigs = useWizardStore((s) => s.agentConfigs);
97
97
  const selectedStackId = useWizardStore((s) => s.selectedStackId);
98
+ const setupMarketplace = useWizardStore((s) => s.setupMarketplace);
98
99
  const { viewportRef, contentRef, contentMarginTop, hiddenAbove, hiddenBelow } = usePanelScroll();
99
- const sourceNames = formatSkillMarketplaces(skillConfigs);
100
+ const sourceNames = setupMarketplace === null ? formatSkillMarketplaces(skillConfigs) : formatSourceDisplayName(setupMarketplace);
100
101
  const stackName = getStackName(selectedStackId);
101
102
  return /* @__PURE__ */ jsxs(
102
103
  Box,
@@ -121,4 +122,4 @@ var SummaryPanel = () => {
121
122
  export {
122
123
  SummaryPanel
123
124
  };
124
- //# sourceMappingURL=chunk-LT5GXEB2.js.map
125
+ //# sourceMappingURL=chunk-2VUYFTUB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/wizard/summary-panel.tsx","../src/cli/components/hooks/use-panel-scroll.ts"],"sourcesContent":["import { Box, Text } from \"ink\";\nimport React from \"react\";\nimport { unique } from \"remeda\";\nimport {\n ALL_SKILLS_EJECTED_LABEL,\n CLI_COLORS,\n DEFAULT_PUBLIC_SOURCE_NAME,\n EJECT_SOURCE,\n formatSourceDisplayName,\n} from \"../../consts.js\";\nimport { useWizardStore } from \"../../stores/wizard-store.js\";\nimport type { SkillConfig } from \"../../types/config.js\";\nimport { usePanelScroll } from \"../hooks/use-panel-scroll.js\";\nimport { ScrollAffordance } from \"./scroll-affordance.js\";\nimport { SkillAgentSummary } from \"./skill-agent-summary.js\";\nimport { getStackName } from \"./utils.js\";\n\n/**\n * The Marketplace row's value: the distinct marketplaces the summarised skills come from.\n *\n * `SkillConfig.origin` is authoritative for where a skill came from, and holds exactly one\n * of two things: {@link EJECT_SOURCE}, meaning the files were copied locally, or the marketplace's\n * own name as its `marketplace.json` declares it — which is why a project initialised from a\n * different marketplace names that marketplace here without any extra plumbing.\n *\n * Three cases, and the two zero-marketplace ones are NOT the same state:\n * - **No skills at all.** Reachable — the `I` overlay opens from the stack step before anything is\n * selected. Nothing is claimed yet, so the default public marketplace stands in.\n * - **Skills, but every one ejected.** \"eject\" names no marketplace, so there is none to list;\n * naming the default would assert an origin none of those skills has.\n * - **Any marketplace-sourced skill.** Its marketplace is named. Ejected siblings contribute no\n * name but do not suppress the ones that do.\n *\n * Tombstoned (`excluded`) entries count. One still records a real global install and the\n * marketplace it came from, and `computeScopeDiff` renders it as a row in the summary below, so\n * dropping it here would leave the header disowning a row it sits above.\n *\n * Sorted, so the row cannot reorder between renders on config iteration order alone.\n */\nfunction formatSkillMarketplaces(skillConfigs: SkillConfig[]): string {\n if (skillConfigs.length === 0) return formatSourceDisplayName(DEFAULT_PUBLIC_SOURCE_NAME);\n\n const marketplaceNames = unique(\n skillConfigs.map((config) => config.origin).filter((origin) => origin !== EJECT_SOURCE),\n );\n if (marketplaceNames.length === 0) return ALL_SKILLS_EJECTED_LABEL;\n\n return marketplaceNames\n .map((name) => formatSourceDisplayName(name))\n .sort()\n .join(\" · \");\n}\n\ntype PanelHeaderProps = {\n sourceNames: string;\n stackName: string | undefined;\n};\n\n/**\n * Which marketplaces and which stack the summary below was built from.\n *\n * Rendered inside the scrolled content, so its `marginBottom` scrolls away\n * rather than permanently costing the viewport a row.\n *\n * `flexShrink={0}` is load-bearing: Ink defaults every box to `flexShrink: 1`,\n * so on a short terminal Yoga squeezes this block to fit instead of letting the\n * viewport clip it, and the Marketplace and Stack rows overprint into one\n * unreadable line. Clipping is the panel's job; the header must keep its height.\n */\nconst PanelHeader: React.FC<PanelHeaderProps> = ({ sourceNames, stackName }) => (\n <Box\n flexDirection=\"column\"\n flexShrink={0}\n borderStyle=\"single\"\n borderBottom={true}\n borderTop={false}\n borderLeft={false}\n borderRight={false}\n borderColor={CLI_COLORS.NEUTRAL}\n borderBottomDimColor\n paddingBottom={1}\n marginBottom={1}\n >\n <Box flexDirection=\"row\" columnGap={1}>\n <Text color={CLI_COLORS.WARNING} bold>\n Marketplace\n </Text>\n <Text color={CLI_COLORS.NEUTRAL}>{sourceNames}</Text>\n </Box>\n <Box flexDirection=\"row\" columnGap={1}>\n <Text color={CLI_COLORS.WARNING} bold>\n Stack\n </Text>\n <Text color={CLI_COLORS.NEUTRAL}>{stackName ?? \"none\"}</Text>\n </Box>\n </Box>\n);\n\n/**\n * Marketplace/stack header above a clipped, arrow-scrollable\n * `SkillAgentSummary` — the one panel both summary surfaces render: the `I`\n * overlay in `wizard-layout.tsx` and the confirm step.\n *\n * It owns the `↑`/`↓` keys through `usePanelScroll`. A host that needs other\n * keys adds its own `useInput` alongside it — `StepConfirm` claims `Enter` and\n * `Esc` that way.\n *\n * Horizontal padding only. Vertical padding is unshrinkable, so on a short\n * terminal it claims the last row the viewport has and the content measures 0\n * before it has ever been laid out — the panel then paints neither a summary\n * row nor the affordance that would say rows are missing.\n */\nexport const SummaryPanel: React.FC = () => {\n const skillConfigs = useWizardStore((s) => s.skillConfigs);\n const agentConfigs = useWizardStore((s) => s.agentConfigs);\n const selectedStackId = useWizardStore((s) => s.selectedStackId);\n const setupMarketplace = useWizardStore((s) => s.setupMarketplace);\n const { viewportRef, contentRef, contentMarginTop, hiddenAbove, hiddenBelow } = usePanelScroll();\n\n // A project set up from a marketplace named for it is set up from THAT one, whichever the\n // global install's skills it shows come from.\n const sourceNames =\n setupMarketplace === null\n ? formatSkillMarketplaces(skillConfigs)\n : formatSourceDisplayName(setupMarketplace);\n const stackName = getStackName(selectedStackId);\n\n return (\n <Box\n flexDirection=\"column\"\n flexGrow={1}\n flexBasis={0}\n borderStyle=\"single\"\n borderColor={CLI_COLORS.NEUTRAL}\n paddingX={2}\n >\n <Box ref={viewportRef} flexDirection=\"column\" flexGrow={1} flexBasis={0} overflow=\"hidden\">\n <Box ref={contentRef} flexDirection=\"column\" flexShrink={0} marginTop={contentMarginTop}>\n <PanelHeader sourceNames={sourceNames} stackName={stackName} />\n\n <Box width=\"100%\">\n <SkillAgentSummary skillConfigs={skillConfigs} agentConfigs={agentConfigs} />\n </Box>\n </Box>\n </Box>\n <ScrollAffordance hiddenAbove={hiddenAbove} hiddenBelow={hiddenBelow} />\n </Box>\n );\n};\n","import { useEffect, useRef, useState } from \"react\";\nimport type { Ref, RefObject } from \"react\";\nimport { type DOMElement, measureElement, useInput } from \"ink\";\nimport { useMeasuredHeight } from \"./use-measured-height.js\";\n\nexport type UsePanelScrollResult = {\n /** Attach to the clipping box — the one with `overflow=\"hidden\"`. */\n viewportRef: Ref<DOMElement>;\n /** Attach to the scrolled content box inside the viewport. */\n contentRef: RefObject<DOMElement | null>;\n /** Apply as the content box's `marginTop`; negative by the number of lines scrolled off the top. */\n contentMarginTop: number;\n /** Content lines scrolled off the top of the viewport. */\n hiddenAbove: number;\n /** Content lines clipped below the viewport. */\n hiddenBelow: number;\n};\n\n/**\n * Line scrolling for a single clipped viewport, driven by the `↑`/`↓` keys.\n *\n * The caller renders viewport, content and a `ScrollAffordance` sibling; this\n * owns the offset those three agree on. Distinct from `useSectionScroll`, which\n * moves a viewport to follow focus across variable-height sections — here there\n * is no focus, only the arrow keys.\n *\n * A host that needs other keys adds its own `useInput` alongside it, on keys\n * disjoint from `↑`/`↓`.\n */\nexport function usePanelScroll(): UsePanelScrollResult {\n // Measured on the clipping box itself, inside the border and padding, so it\n // is the height the content actually gets.\n const { ref: viewportRef, measuredHeight: viewportHeight } = useMeasuredHeight();\n const contentRef = useRef<DOMElement>(null);\n const [contentHeight, setContentHeight] = useState(0);\n const [scrollOffset, setScrollOffset] = useState(0);\n\n // Ink's measurement idiom: re-measure on every render, no dependency array\n // on purpose. The setState inside is guarded to fire only when the measured\n // height actually changed, so the chain converges instead of looping — a\n // dependency array here would skip re-measures after layout changes React\n // cannot see.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(() => {\n if (!contentRef.current) return;\n const { height } = measureElement(contentRef.current);\n // Yoga under-reports a wrapped subtree measured inside a viewport too\n // short to lay it out, and reports 0 inside a zero-height one, so the\n // tallest reading is the trustworthy one. Believing a squeezed reading\n // would shrink the hidden-line count until the affordance disappeared,\n // which hands its row back and grows the count again — a render loop.\n setContentHeight((prev) => Math.max(prev, height));\n });\n\n // contentHeight only ever sizes the scroll range and the affordance count.\n // It must never gate the clip — which is why it is not returned: a viewport\n // that stops clipping when it looks too small grows to content height, which\n // makes it look big enough to stop clipping — a stable wrong answer that\n // paints the content over the border.\n const maxScroll = Math.max(0, contentHeight - viewportHeight);\n const hiddenBelow = Math.max(0, maxScroll - scrollOffset);\n\n useInput((_input, key) => {\n if (key.upArrow) setScrollOffset((prev) => Math.max(0, prev - 1));\n if (key.downArrow) setScrollOffset((prev) => Math.min(maxScroll, prev + 1));\n });\n\n return {\n viewportRef,\n contentRef,\n contentMarginTop: scrollOffset > 0 ? -scrollOffset : 0,\n hiddenAbove: scrollOffset,\n hiddenBelow,\n };\n}\n"],"mappings":"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","names":[]}