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
@@ -1,39 +1,42 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  loadAgentDefs
4
- } from "./chunk-B6FSLXYN.js";
4
+ } from "./chunk-PMJWD27C.js";
5
5
  import {
6
6
  pruneStaleCompiledAgents,
7
7
  recompileAgents
8
- } from "./chunk-JU4ZGJ7A.js";
8
+ } from "./chunk-GE3WKJ6Q.js";
9
9
  import {
10
+ agentCodec,
10
11
  buildAgentScopeMap,
11
12
  buildMarketplacePluginRef,
12
- claudePluginUninstall,
13
13
  copySkillsToLocalFlattened,
14
14
  discoverAllPluginSkills,
15
15
  ensureDir,
16
16
  getErrorMessage,
17
+ hostAt,
18
+ installBaseDir,
17
19
  isHomeDirectory,
18
- loadProjectConfigFromDir,
20
+ loadInstalledConfig,
19
21
  loadSkillsFromDir,
20
22
  loadSkillsMatrixFromSource,
23
+ providerInUse,
21
24
  remove,
22
25
  removeDirIfEmpty,
23
26
  resolveInstallPaths,
24
- toClaudePluginScope,
27
+ skillsDir,
28
+ skillsPathPrefix,
25
29
  verbose
26
- } from "./chunk-XPDVAJDH.js";
30
+ } from "./chunk-A4KDXNOT.js";
27
31
  import {
28
32
  initializeMatrix,
29
33
  matrix,
30
34
  typedEntries,
31
35
  typedKeys
32
- } from "./chunk-AVK2HEKB.js";
36
+ } from "./chunk-V5WXIM2F.js";
33
37
  import {
34
- LOCAL_SKILLS_PATH,
35
38
  globalInstallRoot
36
- } from "./chunk-UITEKY2R.js";
39
+ } from "./chunk-ENGYJCTF.js";
37
40
 
38
41
  // src/cli/lib/operations/project/remove-compiled-agents.ts
39
42
  import path from "path";
@@ -51,7 +54,7 @@ async function removeCompiledAgents(options) {
51
54
  };
52
55
  }
53
56
  async function pruneCompiledAgents(options) {
54
- await pruneStaleCompiledAgents(options.agentsDir, options.keep);
57
+ await pruneStaleCompiledAgents(options.agentsDir, options.keep, options.codec);
55
58
  await tidyEmptiedAgentsDir(options.agentsDir);
56
59
  }
57
60
  async function removeCompiledAgentFile(agentsDir, name) {
@@ -68,21 +71,7 @@ async function tidyEmptiedAgentsDir(agentsDir) {
68
71
 
69
72
  // src/cli/lib/operations/project/compile-agents.ts
70
73
  async function compileAgents(options) {
71
- let resolvedAgents = options.agents;
72
- let resolvedAgentScopeMap = options.agentScopeMap;
73
- if (options.scopeFilter) {
74
- const loadedConfig = await loadProjectConfigFromDir(options.projectDir);
75
- if (!resolvedAgentScopeMap && loadedConfig?.config) {
76
- resolvedAgentScopeMap = buildAgentScopeMap(loadedConfig.config);
77
- }
78
- const filteredAgents = loadedConfig?.config.agents.filter((a) => !a.excluded && a.scope === options.scopeFilter).map((a) => a.name);
79
- if (resolvedAgents && filteredAgents) {
80
- const filterSet = new Set(filteredAgents);
81
- resolvedAgents = resolvedAgents.filter((a) => filterSet.has(a));
82
- } else if (filteredAgents) {
83
- resolvedAgents = filteredAgents;
84
- }
85
- }
74
+ const { agents: resolvedAgents, agentScopeMap: resolvedAgentScopeMap } = options.scopeFilter ? await rosterForScope(options, options.scopeFilter) : { agents: options.agents, agentScopeMap: options.agentScopeMap };
86
75
  const recompileResult = await recompileAgents({
87
76
  pluginDir: options.pluginDir ?? options.projectDir,
88
77
  sourcePath: options.sourcePath,
@@ -100,25 +89,51 @@ async function compileAgents(options) {
100
89
  warnings: recompileResult.warnings
101
90
  };
102
91
  }
92
+ async function rosterForScope(options, scope) {
93
+ const config = (await loadInstalledConfig(options.projectDir))?.config;
94
+ const activeAtScope = config?.agents.filter((agent) => !agent.excluded && agent.scope === scope).map((agent) => agent.name);
95
+ return {
96
+ agents: requestedAgentsAmong(options.agents, activeAtScope),
97
+ agentScopeMap: options.agentScopeMap ?? (config ? buildAgentScopeMap(config) : void 0)
98
+ };
99
+ }
100
+ function requestedAgentsAmong(requested, activeAtScope) {
101
+ if (activeAtScope === void 0) return requested;
102
+ if (requested === void 0) return activeAtScope;
103
+ const atScope = new Set(activeAtScope);
104
+ return requested.filter((name) => atScope.has(name));
105
+ }
103
106
  async function pruneStaleAgentsForPass(options, recompileResult) {
104
107
  if (options.scopeFilter || !options.outputDir) return;
105
108
  const compiledForDir = /* @__PURE__ */ new Set([
106
109
  ...recompileResult.compiled,
107
110
  ...recompileResult.failed
108
111
  ]);
109
- await pruneCompiledAgents({ agentsDir: options.outputDir, keep: compiledForDir });
112
+ await pruneCompiledAgents({
113
+ agentsDir: options.outputDir,
114
+ keep: compiledForDir,
115
+ // The host this installation compiles for — a Codex role is a `.toml`, and a prune that only
116
+ // knew `.md` left a deselected role in place for Codex to keep offering (CLI-896's sibling).
117
+ codec: agentCodec(providerInUse(options.projectDir))
118
+ });
110
119
  }
111
120
 
112
121
  // src/cli/lib/operations/skills/discover-skills.ts
113
- import path2 from "path";
114
122
  var NO_LOCAL_SKILLS = { skills: {}, unusableMetadata: [] };
115
- async function discoverLocalProjectSkills(rootDir) {
116
- const localSkillsDir = path2.join(rootDir, LOCAL_SKILLS_PATH);
117
- return loadSkillsFromDir(localSkillsDir, {
118
- pathPrefix: LOCAL_SKILLS_PATH,
123
+ async function discoverLocalProjectSkills(projectDir, scope) {
124
+ const provider = providerInUse(installBaseDir(projectDir, scope));
125
+ return loadSkillsFromDir(skillsDir(provider, scope, projectDir), {
126
+ pathPrefix: skillsPathPrefix(provider, scope, projectDir),
119
127
  requireMetadata: true
120
128
  });
121
129
  }
130
+ function globalSkillsRoot() {
131
+ const home = globalInstallRoot();
132
+ return skillsDir(providerInUse(home), "global", home);
133
+ }
134
+ function projectSkillsRoot(projectDir, scope) {
135
+ return skillsDir(providerInUse(installBaseDir(projectDir, scope)), scope, projectDir);
136
+ }
122
137
  function mergeSkills(...skillSources) {
123
138
  const merged = {};
124
139
  for (const source of skillSources) {
@@ -137,17 +152,20 @@ async function discoverInstalledSkills(projectDir) {
137
152
  if (globalPluginSkillCount > 0) {
138
153
  verbose(` Found ${globalPluginSkillCount} skills from global plugins`);
139
154
  }
140
- const globalLocal = isGlobalProject ? NO_LOCAL_SKILLS : await discoverLocalProjectSkills(globalInstallRoot());
155
+ const globalLocal = isGlobalProject ? NO_LOCAL_SKILLS : await discoverLocalProjectSkills(projectDir, "global");
141
156
  const globalLocalSkillCount = typedKeys(globalLocal.skills).length;
142
157
  if (globalLocalSkillCount > 0) {
143
- verbose(` Found ${globalLocalSkillCount} global local skills from ~/.claude/skills/`);
158
+ verbose(` Found ${globalLocalSkillCount} global local skills from ${globalSkillsRoot()}`);
144
159
  }
145
160
  const pluginSkills = await discoverAllPluginSkills(projectDir);
146
161
  const pluginSkillCount = typedKeys(pluginSkills).length;
147
162
  verbose(` Found ${pluginSkillCount} skills from installed plugins`);
148
- const local = await discoverLocalProjectSkills(projectDir);
163
+ const localScope = isGlobalProject ? "global" : "project";
164
+ const local = await discoverLocalProjectSkills(projectDir, localScope);
149
165
  const localSkillCount = typedKeys(local.skills).length;
150
- verbose(` Found ${localSkillCount} local skills from .claude/skills/`);
166
+ verbose(
167
+ ` Found ${localSkillCount} local skills from ${projectSkillsRoot(projectDir, localScope)}`
168
+ );
151
169
  const allSkills = mergeSkills(globalPluginSkills, globalLocal.skills, pluginSkills, local.skills);
152
170
  const totalSkillCount = typedKeys(allSkills).length;
153
171
  return {
@@ -162,12 +180,12 @@ async function discoverInstalledSkills(projectDir) {
162
180
  }
163
181
 
164
182
  // src/cli/lib/operations/skills/copy-local-skills.ts
165
- async function copyScopedLocalSkills(scopeSkills, skillsDir, sourceResult) {
183
+ async function copyScopedLocalSkills(scopeSkills, skillsDir2, sourceResult) {
166
184
  if (scopeSkills.length === 0) return [];
167
- await ensureDir(skillsDir);
185
+ await ensureDir(skillsDir2);
168
186
  return copySkillsToLocalFlattened(
169
187
  scopeSkills.map((s) => s.id),
170
- skillsDir,
188
+ skillsDir2,
171
189
  sourceResult
172
190
  );
173
191
  }
@@ -194,16 +212,18 @@ async function copyLocalSkills(skills, projectDir, sourceResult) {
194
212
  }
195
213
 
196
214
  // src/cli/lib/operations/skills/uninstall-plugin-skills.ts
215
+ var SCOPE_OF_AN_UNRECORDED_SKILL = "project";
197
216
  async function uninstallPluginSkills(skillIds, oldSkills, marketplace, projectDir) {
217
+ const host = hostAt(projectDir);
198
218
  const uninstalled = [];
199
219
  const failed = [];
200
220
  for (const skillId of skillIds) {
201
221
  const oldSkill = oldSkills.find((s) => s.id === skillId);
202
- const pluginScope = toClaudePluginScope(oldSkill?.scope);
222
+ const pluginScope = oldSkill?.scope ?? SCOPE_OF_AN_UNRECORDED_SKILL;
203
223
  const pluginRef = buildMarketplacePluginRef(skillId, marketplace);
204
224
  try {
205
- await claudePluginUninstall(pluginRef, pluginScope, projectDir);
206
- uninstalled.push(skillId);
225
+ const outcome = await host.uninstallPlugin(pluginRef, pluginScope, projectDir);
226
+ if (outcome === "removed") uninstalled.push(skillId);
207
227
  } catch (error) {
208
228
  failed.push({ id: skillId, error: getErrorMessage(error) });
209
229
  }
@@ -216,6 +236,7 @@ async function withCatalogueSeatedFor(projectDir, body) {
216
236
  const callersCatalogue = matrix;
217
237
  const { matrix: seated } = await loadSkillsMatrixFromSource({
218
238
  projectDir,
239
+ purpose: "upkeep",
219
240
  skipExtraSources: true,
220
241
  matrixOnly: true
221
242
  });
@@ -273,4 +294,4 @@ export {
273
294
  recompileRegisteredProjectAgents,
274
295
  recompilePropagatedProjectAgents
275
296
  };
276
- //# sourceMappingURL=chunk-2CQNPLVF.js.map
297
+ //# sourceMappingURL=chunk-HWEKHVLE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/lib/operations/project/remove-compiled-agents.ts","../src/cli/lib/operations/project/compile-agents.ts","../src/cli/lib/operations/skills/discover-skills.ts","../src/cli/lib/operations/skills/copy-local-skills.ts","../src/cli/lib/operations/skills/uninstall-plugin-skills.ts","../src/cli/lib/loading/catalogue-seat.ts","../src/cli/lib/operations/project/recompile-project-agents.ts"],"sourcesContent":["import path from \"path\";\nimport { pruneStaleCompiledAgents } from \"../../agents/list-compiled-agents.js\";\nimport type { AgentCodec } from \"../../installation/install-layout.js\";\nimport { getErrorMessage } from \"../../../utils/errors.js\";\nimport { remove, removeDirIfEmpty } from \"../../../utils/fs.js\";\nimport type { AgentName } from \"../../../types/index.js\";\n\nconst AGENT_FILE_EXTENSION = \".md\";\n\nexport type RemoveCompiledAgentsOptions = {\n /** The scope's `.claude/agents/` directory holding the compiled files. */\n agentsDir: string;\n agents: readonly AgentName[];\n};\n\nexport type PruneCompiledAgentsOptions = {\n agentsDir: string;\n /**\n * The COMPLETE set of agents that belong in `agentsDir` — every other\n * CLI-compiled agent there is stale. Only an authoritative pass can say this.\n */\n keep: ReadonlySet<AgentName>;\n /** The host's agent file format — Claude's `.md` when absent. */\n codec?: AgentCodec;\n};\n\nexport type RemoveCompiledAgentsResult = {\n /** Agents whose compiled file this call disposed of; an absent file counts. */\n removed: AgentName[];\n /** Agents whose file survived an error, for the caller to report. */\n failed: Array<{ name: AgentName; error: string }>;\n};\n\ntype RemovalOutcome = { name: AgentName; ok: true } | { name: AgentName; ok: false; error: string };\n\n/**\n * Deletes the named agents' compiled `.md` files from `agentsDir`, then takes the\n * directory itself when nothing at all is left in it — the shape `deleteLocalSkill`\n * models for skills.\n *\n * A scope's `.claude/agents/` is an artefact of what it holds, so the removal that\n * empties it owns the tidy and no caller has to remember. An agent with no file on\n * disk is nothing to do rather than an error.\n */\nexport async function removeCompiledAgents(\n options: RemoveCompiledAgentsOptions,\n): Promise<RemoveCompiledAgentsResult> {\n const outcomes = await Promise.all(\n options.agents.map((name) => removeCompiledAgentFile(options.agentsDir, name)),\n );\n await tidyEmptiedAgentsDir(options.agentsDir);\n\n return {\n removed: outcomes.flatMap((outcome) => (outcome.ok ? [outcome.name] : [])),\n failed: outcomes.flatMap((outcome) =>\n outcome.ok ? [] : [{ name: outcome.name, error: outcome.error }],\n ),\n };\n}\n\n/**\n * Deletes every CLI-compiled agent in `agentsDir` outside `keep`, then takes the\n * directory on the same terms as `removeCompiledAgents`.\n *\n * Hand-authored agents are preserved by `pruneStaleCompiledAgents`'s `isAgentName`\n * predicate, so a directory this leaves empty is one nothing at all remains in.\n */\nexport async function pruneCompiledAgents(options: PruneCompiledAgentsOptions): Promise<void> {\n await pruneStaleCompiledAgents(options.agentsDir, options.keep, options.codec);\n await tidyEmptiedAgentsDir(options.agentsDir);\n}\n\nasync function removeCompiledAgentFile(\n agentsDir: string,\n name: AgentName,\n): Promise<RemovalOutcome> {\n try {\n await remove(path.join(agentsDir, `${name}${AGENT_FILE_EXTENSION}`));\n return { name, ok: true };\n } catch (error) {\n return { name, ok: false, error: getErrorMessage(error) };\n }\n}\n\n/**\n * Emptiness is FILESYSTEM emptiness, never roster emptiness: a hand-authored agent\n * or any user-owned file keeps the directory alive whatever a config says. The\n * agents directory only — `.claude/` above it is uninstall's decision, not a\n * removal path's.\n */\nasync function tidyEmptiedAgentsDir(agentsDir: string): Promise<void> {\n await removeDirIfEmpty(agentsDir);\n}\n","import { pruneCompiledAgents } from \"./remove-compiled-agents.js\";\nimport { recompileAgents } from \"../../agents/index.js\";\nimport { loadInstalledConfig } from \"../../configuration/index.js\";\nimport { buildAgentScopeMap } from \"../../installation/index.js\";\nimport { agentCodec, providerInUse } from \"../../installation/install-layout.js\";\nimport type { AgentName, SkillDefinitionMap, SkillScope } from \"../../../types/index.js\";\n\nexport type CompileAgentsOptions = {\n projectDir: string;\n sourcePath: string;\n pluginDir?: string;\n skills?: SkillDefinitionMap;\n agentScopeMap?: Map<AgentName, SkillScope>;\n agents?: AgentName[];\n /** When set, loads config and filters agents to only those matching this scope. */\n scopeFilter?: SkillScope;\n outputDir?: string;\n};\n\nexport type CompilationResult = {\n compiled: AgentName[];\n /**\n * The subset of `compiled` whose file this pass actually wrote. A pass whose\n * `rewritten` is empty changed nothing on disk, which is exactly what the\n * recompile summary reports and what the count it replaced could not say.\n */\n rewritten: AgentName[];\n failed: AgentName[];\n warnings: string[];\n};\n\n/**\n * Compiles agent markdown files from templates + skill content.\n *\n * Thin wrapper around recompileAgents() that standardizes options.\n * The caller invokes this once (edit, update) or twice with scopeFilter (compile).\n */\nexport async function compileAgents(options: CompileAgentsOptions): Promise<CompilationResult> {\n const { agents: resolvedAgents, agentScopeMap: resolvedAgentScopeMap } = options.scopeFilter\n ? await rosterForScope(options, options.scopeFilter)\n : { agents: options.agents, agentScopeMap: options.agentScopeMap };\n\n const recompileResult = await recompileAgents({\n pluginDir: options.pluginDir ?? options.projectDir,\n sourcePath: options.sourcePath,\n ...(resolvedAgents !== undefined && { agents: resolvedAgents }),\n ...(options.skills !== undefined && { skills: options.skills }),\n projectDir: options.projectDir,\n ...(options.outputDir !== undefined && { outputDir: options.outputDir }),\n ...(resolvedAgentScopeMap !== undefined && { agentScopeMap: resolvedAgentScopeMap }),\n });\n\n await pruneStaleAgentsForPass(options, recompileResult);\n\n return {\n compiled: recompileResult.compiled,\n rewritten: recompileResult.rewritten,\n failed: recompileResult.failed,\n warnings: recompileResult.warnings,\n };\n}\n\n/**\n * What a scope-filtered pass compiles, read off the installed config: the requested agents\n * narrowed to those active at `scope` (all of those when none were requested), and the caller's\n * scope routing — auto-built from the config when the caller passed none. With no installed\n * config, both are the caller's as passed.\n */\nasync function rosterForScope(\n options: CompileAgentsOptions,\n scope: SkillScope,\n): Promise<{\n agents: AgentName[] | undefined;\n agentScopeMap: Map<AgentName, SkillScope> | undefined;\n}> {\n const config = (await loadInstalledConfig(options.projectDir))?.config;\n const activeAtScope = config?.agents\n .filter((agent) => !agent.excluded && agent.scope === scope)\n .map((agent) => agent.name);\n\n return {\n agents: requestedAgentsAmong(options.agents, activeAtScope),\n agentScopeMap: options.agentScopeMap ?? (config ? buildAgentScopeMap(config) : undefined),\n };\n}\n\n/** The requested agents that appear in `activeAtScope`; either list alone when the other is absent. */\nfunction requestedAgentsAmong(\n requested: AgentName[] | undefined,\n activeAtScope: AgentName[] | undefined,\n): AgentName[] | undefined {\n if (activeAtScope === undefined) return requested;\n if (requested === undefined) return activeAtScope;\n\n const atScope = new Set(activeAtScope);\n return requested.filter((name) => atScope.has(name));\n}\n\n/**\n * Compiled-agent writes are additive, so a deselected or stale agent's `.md`\n * lingers after recompile. An authoritative, scope-UNfiltered pass owns its\n * entire `outputDir` (its resolved roster is the full set for that directory),\n * so it prunes built-in agents no longer compiled there. A scope-FILTERED pass\n * (the hasBoth two-pass compile, or the registered-project recompile)\n * sees only one scope's agents and must never delete another scope's files, so\n * it skips pruning. Removing the stale files and tidying the directory they\n * emptied is one operation's job, not this pass's.\n */\nasync function pruneStaleAgentsForPass(\n options: CompileAgentsOptions,\n recompileResult: CompilationResult,\n): Promise<void> {\n if (options.scopeFilter || !options.outputDir) return;\n\n const compiledForDir = new Set<AgentName>([\n ...recompileResult.compiled,\n ...recompileResult.failed,\n ]);\n await pruneCompiledAgents({\n agentsDir: options.outputDir,\n keep: compiledForDir,\n // The host this installation compiles for — a Codex role is a `.toml`, and a prune that only\n // knew `.md` left a deselected role in place for Codex to keep offering (CLI-896's sibling).\n codec: agentCodec(providerInUse(options.projectDir)),\n });\n}\n","import { discoverAllPluginSkills } from \"../../plugins/index.js\";\nimport { isHomeDirectory } from \"../../installation/is-home-directory.js\";\n// loadSkillsFromDir lives in loading/ (a leaf) so both this module and\n// plugins/plugin-discovery can share it without an operations↔plugins cycle.\nimport { loadSkillsFromDir, type LoadedSkills } from \"../../loading/index.js\";\nimport { verbose } from \"../../../utils/logger.js\";\nimport { globalInstallRoot } from \"../../../consts.js\";\nimport {\n installBaseDir,\n providerInUse,\n skillsDir,\n skillsPathPrefix,\n} from \"../../installation/install-layout.js\";\nimport { typedEntries, typedKeys } from \"../../../utils/typed-object.js\";\nimport type { UnusableSkillMetadata } from \"../../loading/index.js\";\nimport type {\n SkillDefinition,\n SkillDefinitionMap,\n SkillId,\n SkillScope,\n} from \"../../../types/index.js\";\n\nexport type DiscoveredSkills = {\n allSkills: SkillDefinitionMap;\n totalSkillCount: number;\n pluginSkillCount: number;\n localSkillCount: number;\n globalPluginSkillCount: number;\n globalLocalSkillCount: number;\n /**\n * Installed skills whose metadata.yaml exists but describes no skill, from either\n * scope. Nothing was loaded for them; `compile` refuses the run over any entry\n * here rather than compile agents around a skill its metadata does not describe.\n */\n unusableMetadata: UnusableSkillMetadata[];\n};\n\n/** The result of a local-skill scan that was not performed. */\nconst NO_LOCAL_SKILLS: LoadedSkills = { skills: {}, unusableMetadata: [] };\n\n/**\n * Discovers the skills copied in rather than installed as plugins, at one scope of one project.\n *\n * **It composed `<rootDir>/.claude/skills` until 2026-09-22, for every provider**, which made this\n * the last Claude literal on the eject READ path: a Codex installation copies its skills to\n * `$CODEX_HOME/skills` and `<repo>/.agents/skills` — the WRITE path routes through the layout\n * already — and this read went on looking in a directory that host never writes to. Nothing failed:\n * the scan answered \"no skills\", which is the same answer an empty installation gives.\n *\n * The scope is a required argument rather than a defaulted one, for the reason C2 deleted\n * `DEFAULT_PROVIDER`: a default is invisible at the call site, and the two call sites below mean\n * different things by it. The provider is READ off the scope root the same way\n * `resolveInstallPaths` reads it, from one probe, so the directory and the prefix cannot name two\n * different installations.\n */\nexport async function discoverLocalProjectSkills(\n projectDir: string,\n scope: SkillScope,\n): Promise<LoadedSkills> {\n const provider = providerInUse(installBaseDir(projectDir, scope));\n\n return loadSkillsFromDir(skillsDir(provider, scope, projectDir), {\n pathPrefix: skillsPathPrefix(provider, scope, projectDir),\n requireMetadata: true,\n });\n}\n\n/** Where the global scope's copied-in skills are, for the diagnostic line that names them. */\nfunction globalSkillsRoot(): string {\n const home = globalInstallRoot();\n return skillsDir(providerInUse(home), \"global\", home);\n}\n\n/** The same, for whichever scope the project pass asked. */\nfunction projectSkillsRoot(projectDir: string, scope: SkillScope): string {\n return skillsDir(providerInUse(installBaseDir(projectDir, scope)), scope, projectDir);\n}\n\n/** Merges skill maps — later sources take precedence over earlier ones. */\nexport function mergeSkills(...skillSources: SkillDefinitionMap[]): SkillDefinitionMap {\n const merged: SkillDefinitionMap = {};\n\n for (const source of skillSources) {\n for (const [id, skill] of typedEntries<SkillId, SkillDefinition | undefined>(source)) {\n if (skill) {\n merged[id] = skill;\n }\n }\n }\n\n return merged;\n}\n\n/**\n * Discovers all installed skills for a project directory using a 4-way merge: the global scope's\n * plugins and copied-in skills, then the project's own of each, so the project wins on conflict.\n *\n * **Every one of the four is resolved per PROVIDER rather than spelled as a Claude path.** The\n * plugin halves go through `hostAt(...).listPlugins` and the copied-in halves through\n * {@link discoverLocalProjectSkills}; on Claude that is `~/.claude/{plugins,skills}` and\n * `<project>/.claude/{plugins,skills}` exactly as it always was, and on Codex it is the Codex\n * state root and the repository's own `.agents/skills`.\n *\n * Pure function — no user-facing logging. Callers add their own log messages.\n * Uses verbose() for diagnostic output only.\n */\nexport async function discoverInstalledSkills(projectDir: string): Promise<DiscoveredSkills> {\n const isGlobalProject = isHomeDirectory(projectDir);\n\n // 1. Global plugins\n const globalPluginSkills = isGlobalProject\n ? {}\n : await discoverAllPluginSkills(globalInstallRoot());\n const globalPluginSkillCount = typedKeys<SkillId>(globalPluginSkills).length;\n if (globalPluginSkillCount > 0) {\n verbose(` Found ${globalPluginSkillCount} skills from global plugins`);\n }\n\n // 2. Global local skills\n const globalLocal = isGlobalProject\n ? NO_LOCAL_SKILLS\n : await discoverLocalProjectSkills(projectDir, \"global\");\n const globalLocalSkillCount = typedKeys<SkillId>(globalLocal.skills).length;\n if (globalLocalSkillCount > 0) {\n verbose(` Found ${globalLocalSkillCount} global local skills from ${globalSkillsRoot()}`);\n }\n\n // 3. Project plugins\n const pluginSkills = await discoverAllPluginSkills(projectDir);\n const pluginSkillCount = typedKeys<SkillId>(pluginSkills).length;\n verbose(` Found ${pluginSkillCount} skills from installed plugins`);\n\n // 4. Project local skills.\n //\n // At the HOME root the two scopes are one installation, so the project pass asks the GLOBAL\n // scope — which is the same directory on Claude and is `$CODEX_HOME/skills` rather than\n // `<home>/.agents/skills` on Codex. Asking both and merging would double the local count on\n // every Claude run for no gain.\n const localScope: SkillScope = isGlobalProject ? \"global\" : \"project\";\n const local = await discoverLocalProjectSkills(projectDir, localScope);\n const localSkillCount = typedKeys<SkillId>(local.skills).length;\n verbose(\n ` Found ${localSkillCount} local skills from ${projectSkillsRoot(projectDir, localScope)}`,\n );\n\n // Merge: global first, project second — project wins on conflict\n const allSkills = mergeSkills(globalPluginSkills, globalLocal.skills, pluginSkills, local.skills);\n const totalSkillCount = typedKeys<SkillId>(allSkills).length;\n\n return {\n allSkills,\n totalSkillCount,\n pluginSkillCount: globalPluginSkillCount + pluginSkillCount,\n localSkillCount: globalLocalSkillCount + localSkillCount,\n globalPluginSkillCount,\n globalLocalSkillCount,\n unusableMetadata: [...globalLocal.unusableMetadata, ...local.unusableMetadata],\n };\n}\n","import { resolveInstallPaths } from \"../../installation/index.js\";\nimport { copySkillsToLocalFlattened, type CopiedSkill } from \"../../skills/index.js\";\nimport { ensureDir } from \"../../../utils/fs.js\";\nimport type { SkillConfig } from \"../../../types/config.js\";\nimport type { SourceLoadResult } from \"../../loading/source-loader.js\";\n\nexport type SkillCopyResult = {\n projectCopied: CopiedSkill[];\n globalCopied: CopiedSkill[];\n totalCopied: number;\n};\n\n/** Copies one scope's local skills into its skills directory. */\nasync function copyScopedLocalSkills(\n scopeSkills: SkillConfig[],\n skillsDir: string,\n sourceResult: SourceLoadResult,\n): Promise<CopiedSkill[]> {\n if (scopeSkills.length === 0) return [];\n\n await ensureDir(skillsDir);\n\n return copySkillsToLocalFlattened(\n scopeSkills.map((s) => s.id),\n skillsDir,\n sourceResult,\n );\n}\n\n/**\n * Copies local-source skills to their scope-appropriate directories.\n *\n * Splits skills by scope (project vs global), resolves install paths,\n * ensures directories exist, and copies from source.\n */\nexport async function copyLocalSkills(\n skills: SkillConfig[],\n projectDir: string,\n sourceResult: SourceLoadResult,\n): Promise<SkillCopyResult> {\n const projectLocalSkills = skills.filter((s) => s.scope !== \"global\");\n const globalLocalSkills = skills.filter((s) => s.scope === \"global\");\n\n const projectPaths = resolveInstallPaths(projectDir, \"project\");\n const globalPaths = resolveInstallPaths(projectDir, \"global\");\n\n const projectCopied = await copyScopedLocalSkills(\n projectLocalSkills,\n projectPaths.skillsDir,\n sourceResult,\n );\n const globalCopied = await copyScopedLocalSkills(\n globalLocalSkills,\n globalPaths.skillsDir,\n sourceResult,\n );\n\n return {\n projectCopied,\n globalCopied,\n totalCopied: projectCopied.length + globalCopied.length,\n };\n}\n","import { hostAt } from \"../../hosts/host-for.js\";\nimport { buildMarketplacePluginRef } from \"../../plugins/index.js\";\nimport { getErrorMessage } from \"../../../utils/errors.js\";\nimport type { SkillId } from \"../../../types/index.js\";\nimport type { SkillConfig, SkillScope } from \"../../../types/config.js\";\n\n/**\n * Where a plugin is looked for when the old config has no entry naming the skill at all.\n *\n * The scope translation swallowed this until C3 — an absent scope and a project one mapped to the\n * same Claude scope word — so the behaviour is unchanged and only the silence is gone. It is the\n * safe direction of the two: a project-scoped removal cannot take a plugin away from every other\n * project under one HOME.\n */\nconst SCOPE_OF_AN_UNRECORDED_SKILL: SkillScope = \"project\";\n\nexport type PluginUninstallResult = {\n /**\n * The skills whose plugin registration this run really dropped.\n *\n * OBSERVED rather than asked for: a host's `uninstallPlugin` answers `removed` or\n * `absent`, and until that answer was read this list held every id the caller named — so a\n * plugin the user had already removed themselves was reported as one `edit` had just taken\n * away. Neither is a failure, which is why the distinction cannot be an exception and has to be\n * the return value.\n */\n uninstalled: SkillId[];\n failed: Array<{ id: SkillId; error: string }>;\n};\n\n/**\n * Uninstalls skill plugins through the host this installation belongs to, using the scope the\n * old config filed each one under.\n *\n * Each plugin reference is qualified as `{skillId}@{marketplace}` to match\n * the form used at install time — bare skill IDs will not match the registry.\n */\nexport async function uninstallPluginSkills(\n skillIds: SkillId[],\n oldSkills: SkillConfig[],\n marketplace: string,\n projectDir: string,\n): Promise<PluginUninstallResult> {\n const host = hostAt(projectDir);\n const uninstalled: SkillId[] = [];\n const failed: PluginUninstallResult[\"failed\"] = [];\n\n for (const skillId of skillIds) {\n const oldSkill = oldSkills.find((s) => s.id === skillId);\n const pluginScope = oldSkill?.scope ?? SCOPE_OF_AN_UNRECORDED_SKILL;\n const pluginRef = buildMarketplacePluginRef(skillId, marketplace);\n try {\n const outcome = await host.uninstallPlugin(pluginRef, pluginScope, projectDir);\n if (outcome === \"removed\") uninstalled.push(skillId);\n } catch (error) {\n failed.push({ id: skillId, error: getErrorMessage(error) });\n }\n }\n\n return { uninstalled, failed };\n}\n","import type { MergedSkillsMatrix } from \"../../types\";\nimport { initializeMatrix, matrix } from \"../matrix/matrix-provider.js\";\nimport { loadSkillsMatrixFromSource } from \"./source-loader.js\";\n\n/**\n * Runs `body` with the skills catalogue seated for `projectDir`, hands it the catalogue\n * that was seated, and puts the caller's own seat back afterwards.\n *\n * A registered project's own local skills, and the skills of the marketplace ITS config\n * names, are in that project's catalogue and in nobody else's. Two things a fan-out does to\n * such a project read a catalogue, and both used to read the TRIGGERING command's:\n *\n * - The RENDER of a compiled agent, which reaches the catalogue through the module-level\n * singleton (`matrix` in `matrix-provider.ts`) that `statedUsageFor` and `liveCategoryOf`\n * in `stacks/stacks-loader.ts` read directly, so no argument reaches it. Rendered against\n * somebody else's seat, each of the project's own skills loses the `usageGuidance` it\n * states and falls back to the per-category placeholder.\n * - The DERIVATION of that project's `config-types.ts` unions, which looks each configured\n * skill's category up in the catalogue (`deriveCategories` / `deriveDomains`). Against\n * somebody else's, a skill only this project's catalogue carries resolves to nothing and\n * its category and domain leave the project's `Category` and `Domain` unions.\n *\n * Both have the same ending: the project's own next `compile` writes the value back from its\n * own catalogue, so the two commands undo each other for as long as they are both run.\n *\n * The catalogue is PASSED to the body as well as seated, so a derivation that can take one as\n * an argument does — a body reading the singleton it just asked to be seated cannot say which\n * seat it got, which is the distinction the whole helper exists to keep. `compile.ts`'s\n * `seatMatrixForPass` returns its catalogue for the same reason.\n *\n * The seat is restored rather than left standing because this is a side trip: the caller has\n * its own pass to finish — `init` and `edit` compile their own agents after the gate returns —\n * and would otherwise render them against the last registered project's catalogue.\n *\n * The load sits outside the `try` deliberately. It seats nothing when it throws, so there is\n * no seat to restore, and a project whose catalogue cannot be loaded is left to the caller's\n * own failure handling rather than silently processed against somebody else's — which is the\n * whole defect this exists to stop. Both callers count such a project as one they could not\n * reach and carry on with the rest.\n *\n * The load is an `\"upkeep\"` one: a fan-out rewrites a project that is already installed, so a\n * marketplace that has since lost its manifest does not leave the project naming global content\n * a change elsewhere just removed.\n */\nexport async function withCatalogueSeatedFor<T>(\n projectDir: string,\n body: (catalogue: MergedSkillsMatrix) => Promise<T>,\n): Promise<T> {\n const callersCatalogue = matrix;\n const { matrix: seated } = await loadSkillsMatrixFromSource({\n projectDir,\n purpose: \"upkeep\",\n skipExtraSources: true,\n matrixOnly: true,\n });\n try {\n return await body(seated);\n } finally {\n initializeMatrix(callersCatalogue);\n }\n}\n","import { compileAgents, type CompilationResult } from \"./compile-agents.js\";\nimport { loadAgentDefs } from \"./load-agent-defs.js\";\nimport { discoverInstalledSkills } from \"../skills/index.js\";\nimport { resolveInstallPaths } from \"../../installation/index.js\";\nimport { withCatalogueSeatedFor } from \"../../loading/catalogue-seat.js\";\nimport { getErrorMessage } from \"../../../utils/errors.js\";\n\n/**\n * Recompiles the PROJECT-scoped agents of a registered project after a global\n * config change was propagated into that project's `config.ts`.\n *\n * Propagation (`propagateGlobalChangesToProjects`) rewrites a registered\n * project's `config.ts` / `config-types.ts` but never touches its compiled\n * `.claude/agents/*.md`, so the compiled agents keep emitting whatever skill\n * reference form the OLD global data dictated. Run this per propagated project,\n * after propagation, to bring the compiled artifacts back in step.\n *\n * Scope: project only. The global agents were already recompiled by the\n * triggering operation's own pass; repeating a global pass per project would\n * rewrite `~/.claude/agents` once per registered project for no gain.\n *\n * Both inputs a compiled agent's bytes depend on are resolved from THIS\n * project's directory, and neither is inherited from the command that triggered\n * the fan-out: `skills` is passed explicitly, because without it\n * `recompileAgents` falls back to `discoverAllPluginSkills`, which sees plugin\n * skills only and would silently strip every global-local and project-local\n * skill; and the catalogue is seated by {@link withCatalogueSeatedFor}, because\n * the render reads it from a module-level singleton no argument reaches.\n *\n * Agent partials always come from the CLI itself (`getLocalAgentDefinitions`\n * returns `sourcePath: PROJECT_ROOT`), so no per-project marketplace source\n * resolution is needed.\n */\nexport async function recompileRegisteredProjectAgents(\n projectDir: string,\n): Promise<CompilationResult> {\n return withCatalogueSeatedFor(projectDir, async () => {\n const { allSkills } = await discoverInstalledSkills(projectDir);\n const { sourcePath } = await loadAgentDefs();\n\n return compileAgents({\n projectDir,\n sourcePath,\n skills: allSkills,\n scopeFilter: \"project\",\n outputDir: resolveInstallPaths(projectDir, \"project\").agentsDir,\n });\n });\n}\n\nexport type PropagatedRecompileSummary = {\n /** Projects where at least one compiled agent's content actually moved. */\n rewrittenCount: number;\n /**\n * Projects the fan-out reached whose agents all came back byte-identical. They\n * were visited and left alone, which is a different fact from being recompiled\n * and the one the old single count could not tell apart.\n */\n unchangedCount: number;\n failedCount: number;\n /** Per-project warnings in processing order — the caller surfaces them via warn(). */\n warnings: string[];\n};\n\n/**\n * Runs {@link recompileRegisteredProjectAgents} over every propagated project,\n * isolating per-project failures: one project's unreadable config or broken\n * template must not abort the loop or leave the remaining projects stale.\n * Projects are processed sequentially so the collected warnings keep a\n * deterministic per-project order.\n */\nexport async function recompilePropagatedProjectAgents(\n projectDirs: string[],\n): Promise<PropagatedRecompileSummary> {\n let rewrittenCount = 0;\n let unchangedCount = 0;\n let failedCount = 0;\n const warnings: string[] = [];\n\n for (const dir of projectDirs) {\n try {\n const result = await recompileRegisteredProjectAgents(dir);\n if (result.failed.length > 0) {\n failedCount++;\n warnings.push(...result.warnings);\n continue;\n }\n if (result.rewritten.length > 0) rewrittenCount++;\n else unchangedCount++;\n } catch (error) {\n failedCount++;\n warnings.push(`Could not recompile agents in ${dir}: ${getErrorMessage(error)}`);\n }\n }\n\n return { rewrittenCount, unchangedCount, failedCount, warnings };\n}\n"],"mappings":"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","names":["skillsDir"]}
@@ -1,16 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli/components/hooks/use-keyboard-navigation.ts
4
- import { useState, useCallback, useRef, useEffect } from "react";
4
+ import { useState, useCallback, useRef } from "react";
5
5
  import { useInput } from "ink";
6
6
  function useKeyboardNavigation(itemCount, handlers = {}, options = {}) {
7
7
  const { onEnter, onEscape } = handlers;
8
8
  const { wrap = true, vimKeys = true, active = true } = options;
9
- const [focusedIndex, setFocusedIndex] = useState(0);
9
+ const [focusedIndex, setRenderedIndex] = useState(0);
10
10
  const focusedIndexRef = useRef(focusedIndex);
11
- useEffect(() => {
12
- focusedIndexRef.current = focusedIndex;
13
- }, [focusedIndex]);
11
+ const setFocusedIndex = useCallback((action) => {
12
+ const next = typeof action === "function" ? action(focusedIndexRef.current) : action;
13
+ focusedIndexRef.current = next;
14
+ setRenderedIndex(next);
15
+ }, []);
14
16
  const moveUp = useCallback(() => {
15
17
  setFocusedIndex((prev) => {
16
18
  if (wrap) {
@@ -18,7 +20,7 @@ function useKeyboardNavigation(itemCount, handlers = {}, options = {}) {
18
20
  }
19
21
  return Math.max(0, prev - 1);
20
22
  });
21
- }, [itemCount, wrap]);
23
+ }, [itemCount, wrap, setFocusedIndex]);
22
24
  const moveDown = useCallback(() => {
23
25
  setFocusedIndex((prev) => {
24
26
  if (wrap) {
@@ -26,7 +28,7 @@ function useKeyboardNavigation(itemCount, handlers = {}, options = {}) {
26
28
  }
27
29
  return Math.min(itemCount - 1, prev + 1);
28
30
  });
29
- }, [itemCount, wrap]);
31
+ }, [itemCount, wrap, setFocusedIndex]);
30
32
  useInput(
31
33
  useCallback(
32
34
  (input, key) => {
@@ -56,4 +58,4 @@ function useKeyboardNavigation(itemCount, handlers = {}, options = {}) {
56
58
  export {
57
59
  useKeyboardNavigation
58
60
  };
59
- //# sourceMappingURL=chunk-XVAQG2CV.js.map
61
+ //# sourceMappingURL=chunk-JOEA52NR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/hooks/use-keyboard-navigation.ts"],"sourcesContent":["import type { Dispatch, SetStateAction } from \"react\";\nimport { useState, useCallback, useRef } from \"react\";\nimport { useInput, type Key } from \"ink\";\n\ntype KeyboardNavigationHandlers = {\n onEnter?: (focusedIndex: number) => void;\n onEscape?: () => void;\n};\n\ntype KeyboardNavigationOptions = {\n wrap?: boolean;\n vimKeys?: boolean;\n active?: boolean;\n};\n\nexport function useKeyboardNavigation(\n itemCount: number,\n handlers: KeyboardNavigationHandlers = {},\n options: KeyboardNavigationOptions = {},\n): {\n focusedIndex: number;\n setFocusedIndex: Dispatch<SetStateAction<number>>;\n} {\n const { onEnter, onEscape } = handlers;\n const { wrap = true, vimKeys = true, active = true } = options;\n\n const [focusedIndex, setRenderedIndex] = useState(0);\n // The focus the handlers read, written the moment it moves rather than once the move renders.\n // Keys that arrive in one chunk — a fast typist, a paste — are handled one after another with no\n // render between them, so a copy synced by an effect still held the focus from before the burst\n // when Enter read it: Down, Down, Enter on the dashboard ran Edit.\n const focusedIndexRef = useRef(focusedIndex);\n\n const setFocusedIndex = useCallback<Dispatch<SetStateAction<number>>>((action) => {\n const next = typeof action === \"function\" ? action(focusedIndexRef.current) : action;\n focusedIndexRef.current = next;\n setRenderedIndex(next);\n }, []);\n\n const moveUp = useCallback(() => {\n setFocusedIndex((prev) => {\n if (wrap) {\n return (prev - 1 + itemCount) % itemCount;\n }\n return Math.max(0, prev - 1);\n });\n }, [itemCount, wrap, setFocusedIndex]);\n\n const moveDown = useCallback(() => {\n setFocusedIndex((prev) => {\n if (wrap) {\n return (prev + 1) % itemCount;\n }\n return Math.min(itemCount - 1, prev + 1);\n });\n }, [itemCount, wrap, setFocusedIndex]);\n\n useInput(\n useCallback(\n (input: string, key: Key) => {\n if (key.escape) {\n onEscape?.();\n return;\n }\n\n if (key.return) {\n onEnter?.(focusedIndexRef.current);\n return;\n }\n\n if (key.upArrow || (vimKeys && input === \"k\")) {\n moveUp();\n return;\n }\n\n if (key.downArrow || (vimKeys && input === \"j\")) {\n moveDown();\n }\n },\n [onEnter, onEscape, vimKeys, moveUp, moveDown],\n ),\n { isActive: active },\n );\n\n return { focusedIndex, setFocusedIndex };\n}\n"],"mappings":";;;AACA,SAAS,UAAU,aAAa,cAAc;AAC9C,SAAS,gBAA0B;AAa5B,SAAS,sBACd,WACA,WAAuC,CAAC,GACxC,UAAqC,CAAC,GAItC;AACA,QAAM,EAAE,SAAS,SAAS,IAAI;AAC9B,QAAM,EAAE,OAAO,MAAM,UAAU,MAAM,SAAS,KAAK,IAAI;AAEvD,QAAM,CAAC,cAAc,gBAAgB,IAAI,SAAS,CAAC;AAKnD,QAAM,kBAAkB,OAAO,YAAY;AAE3C,QAAM,kBAAkB,YAA8C,CAAC,WAAW;AAChF,UAAM,OAAO,OAAO,WAAW,aAAa,OAAO,gBAAgB,OAAO,IAAI;AAC9E,oBAAgB,UAAU;AAC1B,qBAAiB,IAAI;AAAA,EACvB,GAAG,CAAC,CAAC;AAEL,QAAM,SAAS,YAAY,MAAM;AAC/B,oBAAgB,CAAC,SAAS;AACxB,UAAI,MAAM;AACR,gBAAQ,OAAO,IAAI,aAAa;AAAA,MAClC;AACA,aAAO,KAAK,IAAI,GAAG,OAAO,CAAC;AAAA,IAC7B,CAAC;AAAA,EACH,GAAG,CAAC,WAAW,MAAM,eAAe,CAAC;AAErC,QAAM,WAAW,YAAY,MAAM;AACjC,oBAAgB,CAAC,SAAS;AACxB,UAAI,MAAM;AACR,gBAAQ,OAAO,KAAK;AAAA,MACtB;AACA,aAAO,KAAK,IAAI,YAAY,GAAG,OAAO,CAAC;AAAA,IACzC,CAAC;AAAA,EACH,GAAG,CAAC,WAAW,MAAM,eAAe,CAAC;AAErC;AAAA,IACE;AAAA,MACE,CAAC,OAAe,QAAa;AAC3B,YAAI,IAAI,QAAQ;AACd,qBAAW;AACX;AAAA,QACF;AAEA,YAAI,IAAI,QAAQ;AACd,oBAAU,gBAAgB,OAAO;AACjC;AAAA,QACF;AAEA,YAAI,IAAI,WAAY,WAAW,UAAU,KAAM;AAC7C,iBAAO;AACP;AAAA,QACF;AAEA,YAAI,IAAI,aAAc,WAAW,UAAU,KAAM;AAC/C,mBAAS;AAAA,QACX;AAAA,MACF;AAAA,MACA,CAAC,SAAS,UAAU,SAAS,QAAQ,QAAQ;AAAA,IAC/C;AAAA,IACA,EAAE,UAAU,OAAO;AAAA,EACrB;AAEA,SAAO,EAAE,cAAc,gBAAgB;AACzC;","names":[]}
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  useRowScroll
4
- } from "./chunk-YRS3QZP2.js";
4
+ } from "./chunk-TTY6DPJN.js";
5
5
  import {
6
6
  KEY_SPACE
7
7
  } from "./chunk-WTJ3AY33.js";
8
8
  import {
9
9
  CLI_COLORS,
10
10
  UI_SYMBOLS
11
- } from "./chunk-UITEKY2R.js";
11
+ } from "./chunk-ENGYJCTF.js";
12
12
 
13
13
  // src/cli/components/wizard/checkbox-grid.tsx
14
14
  import { Box, Text, useInput } from "ink";
15
- import { useState } from "react";
15
+ import { useRef, useState } from "react";
16
16
  import { jsx, jsxs } from "react/jsx-runtime";
17
17
  var CheckboxGrid = ({
18
18
  subtitle,
@@ -25,18 +25,25 @@ var CheckboxGrid = ({
25
25
  emptyMessage = "Please select at least one item"
26
26
  }) => {
27
27
  const totalItems = items.length + 1;
28
- const [focusedIndex, setFocusedIndex] = useState(0);
28
+ const [focusedIndex, setRenderedIndex] = useState(0);
29
+ const focusedIndexRef = useRef(focusedIndex);
30
+ const focus = (index) => {
31
+ focusedIndexRef.current = index;
32
+ setRenderedIndex(index);
33
+ };
29
34
  useInput((input, key) => {
30
35
  if (key.escape) {
31
36
  onBack();
32
37
  return;
33
38
  }
34
39
  if (key.upArrow || input === "k") {
35
- setFocusedIndex((prev) => prev <= 0 ? totalItems - 1 : prev - 1);
40
+ const prev = focusedIndexRef.current;
41
+ focus(prev <= 0 ? totalItems - 1 : prev - 1);
36
42
  return;
37
43
  }
38
44
  if (key.downArrow || input === "j") {
39
- setFocusedIndex((prev) => prev >= totalItems - 1 ? 0 : prev + 1);
45
+ const prev = focusedIndexRef.current;
46
+ focus(prev >= totalItems - 1 ? 0 : prev + 1);
40
47
  return;
41
48
  }
42
49
  if (key.return) {
@@ -44,7 +51,7 @@ var CheckboxGrid = ({
44
51
  return;
45
52
  }
46
53
  if (input === KEY_SPACE) {
47
- const item = items[focusedIndex];
54
+ const item = items[focusedIndexRef.current];
48
55
  if (item) {
49
56
  onToggle(item.id);
50
57
  }
@@ -108,4 +115,4 @@ var CheckboxGrid = ({
108
115
  export {
109
116
  CheckboxGrid
110
117
  };
111
- //# sourceMappingURL=chunk-3WVH2CTK.js.map
118
+ //# sourceMappingURL=chunk-JQRJQ4SS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/wizard/checkbox-grid.tsx"],"sourcesContent":["import { Box, Text, useInput } from \"ink\";\nimport React, { useRef, useState } from \"react\";\nimport { CLI_COLORS, UI_SYMBOLS } from \"../../consts.js\";\nimport { useRowScroll } from \"../hooks/use-row-scroll.js\";\nimport { KEY_SPACE } from \"./hotkeys.js\";\n\nexport type CheckboxItem<T extends string = string> = {\n id: T;\n label: string;\n description: string;\n};\n\nexport type CheckboxGridProps<T extends string = string> = {\n subtitle?: string;\n items: CheckboxItem<T>[];\n selectedIds: T[];\n /** Available height in terminal lines for the scrollable viewport. 0 = no constraint. */\n availableHeight?: number;\n onToggle: (id: T) => void;\n onContinue: () => void;\n onBack: () => void;\n /** Message shown when nothing is selected */\n emptyMessage?: string;\n};\n\nexport const CheckboxGrid = <T extends string = string>({\n subtitle,\n items,\n selectedIds,\n availableHeight = 0,\n onToggle,\n onContinue,\n onBack,\n emptyMessage = \"Please select at least one item\",\n}: CheckboxGridProps<T>): React.ReactElement => {\n // Items + continue option at the end\n const totalItems = items.length + 1;\n const [focusedIndex, setRenderedIndex] = useState(0);\n // The focus Space reads, written the moment it moves rather than once the move renders: a Space\n // that follows a move in the same chunk is handled before that move renders.\n const focusedIndexRef = useRef(focusedIndex);\n\n const focus = (index: number): void => {\n focusedIndexRef.current = index;\n setRenderedIndex(index);\n };\n\n useInput((input, key) => {\n if (key.escape) {\n onBack();\n return;\n }\n\n if (key.upArrow || input === \"k\") {\n const prev = focusedIndexRef.current;\n focus(prev <= 0 ? totalItems - 1 : prev - 1);\n return;\n }\n\n if (key.downArrow || input === \"j\") {\n const prev = focusedIndexRef.current;\n focus(prev >= totalItems - 1 ? 0 : prev + 1);\n return;\n }\n\n if (key.return) {\n onContinue();\n return;\n }\n\n if (input === KEY_SPACE) {\n const item = items[focusedIndexRef.current];\n if (item) {\n onToggle(item.id);\n }\n }\n });\n\n // The Domains grid clips silently: `useRowScroll` exposes no hidden-line\n // counts and this view renders no `ScrollAffordance`, by owner decision — see\n // the hook's doc comment.\n // When focus is on continue (past items), scroll to show last items\n const effectiveRow = focusedIndex >= items.length ? items.length - 1 : focusedIndex;\n const { scrollEnabled, scrollTop } = useRowScroll({\n focusedIndex: effectiveRow,\n itemCount: items.length,\n availableHeight,\n });\n\n const itemElements = items.map((item, index) => {\n const isFocused = index === focusedIndex;\n const isSelected = selectedIds.includes(item.id);\n const checkbox = isSelected ? \"[\\u2713]\" : \"[ ]\";\n const pointer = isFocused ? UI_SYMBOLS.CHEVRON : UI_SYMBOLS.CHEVRON_SPACER;\n\n return (\n <Text key={item.id}>\n <Text {...(isFocused && { color: CLI_COLORS.PRIMARY })}>{pointer}</Text>\n <Text {...((isSelected || isFocused) && { color: CLI_COLORS.PRIMARY })} bold={isFocused}>\n {\" \"}\n {checkbox} {item.label}\n </Text>\n {item.description && (\n <Text dimColor>\n {\" \"}\n {item.description}\n </Text>\n )}\n </Text>\n );\n });\n\n const footerElement =\n selectedIds.length > 0 ? (\n <Text>\n {\"\\n\"}Selected: <Text color={CLI_COLORS.WARNING}>{selectedIds.join(\", \")}</Text>\n </Text>\n ) : emptyMessage ? (\n <Text dimColor>\n {\"\\n\"}\n {emptyMessage}\n </Text>\n ) : null;\n\n return (\n <Box flexDirection=\"column\">\n {subtitle && <Text dimColor>{subtitle}</Text>}\n <Box\n flexDirection=\"column\"\n {...(scrollEnabled && { height: availableHeight, overflow: \"hidden\" as const })}\n >\n <Box\n flexDirection=\"column\"\n marginTop={scrollTop > 0 ? -scrollTop : 0}\n {...(scrollEnabled && { flexShrink: 0 })}\n >\n {itemElements}\n </Box>\n </Box>\n {footerElement}\n </Box>\n );\n};\n"],"mappings":";;;;;;;;;;;;;AAAA,SAAS,KAAK,MAAM,gBAAgB;AACpC,SAAgB,QAAQ,gBAAgB;AAgGhC,cACA,YADA;AAxED,IAAM,eAAe,CAA4B;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AACjB,MAAgD;AAE9C,QAAM,aAAa,MAAM,SAAS;AAClC,QAAM,CAAC,cAAc,gBAAgB,IAAI,SAAS,CAAC;AAGnD,QAAM,kBAAkB,OAAO,YAAY;AAE3C,QAAM,QAAQ,CAAC,UAAwB;AACrC,oBAAgB,UAAU;AAC1B,qBAAiB,KAAK;AAAA,EACxB;AAEA,WAAS,CAAC,OAAO,QAAQ;AACvB,QAAI,IAAI,QAAQ;AACd,aAAO;AACP;AAAA,IACF;AAEA,QAAI,IAAI,WAAW,UAAU,KAAK;AAChC,YAAM,OAAO,gBAAgB;AAC7B,YAAM,QAAQ,IAAI,aAAa,IAAI,OAAO,CAAC;AAC3C;AAAA,IACF;AAEA,QAAI,IAAI,aAAa,UAAU,KAAK;AAClC,YAAM,OAAO,gBAAgB;AAC7B,YAAM,QAAQ,aAAa,IAAI,IAAI,OAAO,CAAC;AAC3C;AAAA,IACF;AAEA,QAAI,IAAI,QAAQ;AACd,iBAAW;AACX;AAAA,IACF;AAEA,QAAI,UAAU,WAAW;AACvB,YAAM,OAAO,MAAM,gBAAgB,OAAO;AAC1C,UAAI,MAAM;AACR,iBAAS,KAAK,EAAE;AAAA,MAClB;AAAA,IACF;AAAA,EACF,CAAC;AAMD,QAAM,eAAe,gBAAgB,MAAM,SAAS,MAAM,SAAS,IAAI;AACvE,QAAM,EAAE,eAAe,UAAU,IAAI,aAAa;AAAA,IAChD,cAAc;AAAA,IACd,WAAW,MAAM;AAAA,IACjB;AAAA,EACF,CAAC;AAED,QAAM,eAAe,MAAM,IAAI,CAAC,MAAM,UAAU;AAC9C,UAAM,YAAY,UAAU;AAC5B,UAAM,aAAa,YAAY,SAAS,KAAK,EAAE;AAC/C,UAAM,WAAW,aAAa,aAAa;AAC3C,UAAM,UAAU,YAAY,WAAW,UAAU,WAAW;AAE5D,WACE,qBAAC,QACC;AAAA,0BAAC,QAAM,GAAI,aAAa,EAAE,OAAO,WAAW,QAAQ,GAAK,mBAAQ;AAAA,MACjE,qBAAC,QAAM,IAAK,cAAc,cAAc,EAAE,OAAO,WAAW,QAAQ,GAAI,MAAM,WAC3E;AAAA;AAAA,QACA;AAAA,QAAS;AAAA,QAAE,KAAK;AAAA,SACnB;AAAA,MACC,KAAK,eACJ,qBAAC,QAAK,UAAQ,MACX;AAAA;AAAA,QACA,KAAK;AAAA,SACR;AAAA,SAVO,KAAK,EAYhB;AAAA,EAEJ,CAAC;AAED,QAAM,gBACJ,YAAY,SAAS,IACnB,qBAAC,QACE;AAAA;AAAA,IAAK;AAAA,IAAU,oBAAC,QAAK,OAAO,WAAW,SAAU,sBAAY,KAAK,IAAI,GAAE;AAAA,KAC3E,IACE,eACF,qBAAC,QAAK,UAAQ,MACX;AAAA;AAAA,IACA;AAAA,KACH,IACE;AAEN,SACE,qBAAC,OAAI,eAAc,UAChB;AAAA,gBAAY,oBAAC,QAAK,UAAQ,MAAE,oBAAS;AAAA,IACtC;AAAA,MAAC;AAAA;AAAA,QACC,eAAc;AAAA,QACb,GAAI,iBAAiB,EAAE,QAAQ,iBAAiB,UAAU,SAAkB;AAAA,QAE7E;AAAA,UAAC;AAAA;AAAA,YACC,eAAc;AAAA,YACd,WAAW,YAAY,IAAI,CAAC,YAAY;AAAA,YACvC,GAAI,iBAAiB,EAAE,YAAY,EAAE;AAAA,YAErC;AAAA;AAAA,QACH;AAAA;AAAA,IACF;AAAA,IACC;AAAA,KACH;AAEJ;","names":[]}
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getDomainDisplayName
4
- } from "./chunk-4VL3CEOS.js";
4
+ } from "./chunk-QHDPLLK6.js";
5
5
  import {
6
6
  CheckboxGrid
7
- } from "./chunk-3WVH2CTK.js";
7
+ } from "./chunk-JQRJQ4SS.js";
8
8
  import {
9
9
  useWizardStore
10
- } from "./chunk-K5ITEA3I.js";
10
+ } from "./chunk-7JNJ52CD.js";
11
11
  import {
12
12
  matrix,
13
13
  typedEntries
14
- } from "./chunk-AVK2HEKB.js";
14
+ } from "./chunk-V5WXIM2F.js";
15
15
  import {
16
16
  DOMAIN_DESCRIPTIONS,
17
17
  orderDomains
18
- } from "./chunk-UITEKY2R.js";
18
+ } from "./chunk-ENGYJCTF.js";
19
19
 
20
20
  // src/cli/components/wizard/domain-selection.tsx
21
21
  import { useMemo } from "react";
@@ -56,4 +56,4 @@ var DomainSelection = () => {
56
56
  export {
57
57
  DomainSelection
58
58
  };
59
- //# sourceMappingURL=chunk-DSL66I4N.js.map
59
+ //# sourceMappingURL=chunk-LFWLEDUG.js.map
@@ -2,15 +2,15 @@
2
2
  import {
3
3
  computeScopeDiff,
4
4
  useWizardStore
5
- } from "./chunk-K5ITEA3I.js";
5
+ } from "./chunk-7JNJ52CD.js";
6
6
  import {
7
7
  getSkillDisplayName
8
- } from "./chunk-AVK2HEKB.js";
8
+ } from "./chunk-V5WXIM2F.js";
9
9
  import {
10
10
  CLI_COLORS,
11
11
  EJECT_SOURCE,
12
12
  UI_SYMBOLS
13
- } from "./chunk-UITEKY2R.js";
13
+ } from "./chunk-ENGYJCTF.js";
14
14
 
15
15
  // src/cli/components/wizard/skill-agent-summary.tsx
16
16
  import { Box, Text } from "ink";
@@ -106,4 +106,4 @@ var SkillAgentSummary = ({
106
106
  export {
107
107
  SkillAgentSummary
108
108
  };
109
- //# sourceMappingURL=chunk-B3Y3QMWI.js.map
109
+ //# sourceMappingURL=chunk-MAWNVT2C.js.map
@@ -4,11 +4,11 @@ import {
4
4
  } from "./chunk-TSIU7DME.js";
5
5
  import {
6
6
  CategoryGrid
7
- } from "./chunk-NQXO4LR3.js";
7
+ } from "./chunk-SXVHSOTH.js";
8
8
  import {
9
9
  buildCategoriesForDomain,
10
10
  useWizardStore
11
- } from "./chunk-K5ITEA3I.js";
11
+ } from "./chunk-7JNJ52CD.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-Q4IRRVXK.js.map
92
+ //# sourceMappingURL=chunk-MPTNC32T.js.map
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ chooseProviderForThisRun,
4
+ detectInstallations
5
+ } from "./chunk-A4KDXNOT.js";
6
+ import {
7
+ CLI_INVOKE_COMMAND,
8
+ EDITOR_URL,
9
+ PROVIDERS
10
+ } from "./chunk-ENGYJCTF.js";
11
+
12
+ // src/cli/lib/installation/provider-flag.ts
13
+ import os from "os";
14
+ import { Flags } from "@oclif/core";
15
+ function providerFlag() {
16
+ return Flags.string({
17
+ description: "Which provider's installation this run is about",
18
+ helpValue: `<${PROVIDERS.join("|")}>`,
19
+ options: [...PROVIDERS]
20
+ });
21
+ }
22
+ function providerNamedBy(flagValue) {
23
+ if (flagValue === void 0) return void 0;
24
+ return PROVIDERS.find((provider) => provider === flagValue);
25
+ }
26
+ function providerNeedsAConfigurationToInstall() {
27
+ return `--provider names which installation to create and needs the configuration to create it from, so it is only meaningful beside --from. Build one at ${EDITOR_URL}, then run '${CLI_INVOKE_COMMAND} init --from <id> --provider <provider>'.`;
28
+ }
29
+ function ambiguousInstallation(subject, providers, scopeRoot) {
30
+ return `${scopeRoot} holds ${providers.length} installations \u2014 ${providers.join(" and ")} \u2014 so ${subject} cannot tell which one you mean. Name it with --provider <${providers.join("|")}>. Nothing has been changed.`;
31
+ }
32
+ async function installationsInPlay(cwd) {
33
+ const found = await detectInstallations(cwd);
34
+ const inTheProject = atScope(found, "project");
35
+ if (inTheProject.length > 0) return { scopeRoot: cwd, providers: providersOf(inTheProject) };
36
+ return { scopeRoot: os.homedir(), providers: providersOf(atScope(found, "global")) };
37
+ }
38
+ function atScope(found, scope) {
39
+ return found.filter((installation) => installation.scope === scope);
40
+ }
41
+ function providersOf(installations) {
42
+ return installations.map((installation) => installation.provider);
43
+ }
44
+ async function refuseAnAmbiguousInstallation(cwd, subject, named, onAmbiguous) {
45
+ if (named !== void 0) {
46
+ chooseProviderForThisRun(named);
47
+ return;
48
+ }
49
+ const { scopeRoot, providers } = await installationsInPlay(cwd);
50
+ if (providers.length <= 1) return;
51
+ onAmbiguous(ambiguousInstallation(subject, providers, scopeRoot));
52
+ }
53
+ async function ambiguityFinding(cwd) {
54
+ const { scopeRoot, providers } = await installationsInPlay(cwd);
55
+ if (providers.length <= 1) return null;
56
+ return ambiguousInstallation("every command that acts on one", providers, scopeRoot);
57
+ }
58
+ async function otherInstallationsInThisScope(cwd, showing) {
59
+ const { scopeRoot, providers } = await installationsInPlay(cwd);
60
+ const others = providers.filter((provider) => provider !== showing);
61
+ if (others.length === 0) return null;
62
+ return `${scopeRoot} holds ${providers.length} installations \u2014 ${providers.join(" and ")} \u2014 and this is the ${showing} one. Show another with --provider <${providers.join("|")}>.`;
63
+ }
64
+
65
+ export {
66
+ providerFlag,
67
+ providerNamedBy,
68
+ providerNeedsAConfigurationToInstall,
69
+ refuseAnAmbiguousInstallation,
70
+ ambiguityFinding,
71
+ otherInstallationsInThisScope
72
+ };
73
+ //# sourceMappingURL=chunk-NBXXFIIW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/lib/installation/provider-flag.ts"],"sourcesContent":["/**\n * `--provider`: the one flag that names an installation this release cannot read off a folder.\n *\n * **The folder is the record, and this flag is not a second one.** There is no `provider` field in\n * `config.ts`, nothing in a shared payload carries one and a share id encodes none — which is what\n * keeps one saved configuration installable onto either provider and every existing id working.\n * So after an install the folder answers, `providerInUse` reads it, and no command needs a flag.\n * Two questions survive that, and both are the absence of a folder rather than a doubt about one:\n *\n * 1. **A greenfield `init --from <id> --provider codex`** — the folder does not exist yet, so\n * there is nothing to read. This is the flag's whole purpose, and it is why the flag is\n * meaningless without `--from`: the provider is chosen in the web app, so the only run that can\n * be told one is the run that installs what the app produced (D15). A wizard walked to the end\n * and then refused would have spent the user's time before saying so, and there is no wizard\n * step to choose a provider in.\n * 2. **A scope holding BOTH folders** — the disk has two answers, `providerInUse` resolves them by\n * roster order, and a command that acted on the roster's first answer would act on a user's\n * other installation with nothing on screen saying so. `edit`, `uninstall`, `share`, `eject`,\n * `compile` and `update` refuse instead, naming this flag; `list` takes it too and, given no\n * flag, names both installations above the one it is showing —\n * {@link otherInstallationsInThisScope}. **`compile`, `update` and `list` were outside this from C7b\n * until 2026-09-23**, on the reading that a command reading the provider off the FOLDER needs\n * no flag. True of a folder, false of a SCOPE: a scope holds up to one installation per\n * provider, and `providerInUse` then answers by roster order — so the two that write rewrote\n * one of a user's two in silence and the one that reports showed one of them.\n *\n * **The refusal and the selection are one module, deliberately.** A refusal naming a flag that\n * does nothing is worse than neither: the user reads the sentence, types the flag, and the command\n * refuses again or — worse — acts on whichever installation it was going to act on anyway.\n * {@link refuseAnAmbiguousInstallation} is the refusal and {@link chooseProviderForThisRun} is\n * what the flag it names does, and neither can be added without the other.\n *\n * **It was hidden through C6 and is on the help screens from C7b.** Between C4 and C5 a Codex\n * install compiled no sub-agents, so the flag was a user-reachable surface for a provider that\n * was not finished. Hiding it was never disabling it — oclif parses a hidden flag exactly as it\n * parses any other, which is what let the e2e suite drive it throughout and is why no behavioural\n * spec reddened when the hiding came off. The one surface that can tell a shipped flag from an\n * undiscoverable one is help output, so that is where the claim lives:\n * `e2e/commands/provider-is-on-every-help-screen.e2e.test.ts` asserts each command that declares\n * it advertises it with both provider names, and that `doctor` — which reports on every\n * installation in the scope rather than acting on one — does not.\n */\n\nimport os from \"os\";\n\nimport { Flags } from \"@oclif/core\";\n\nimport { PROVIDERS, EDITOR_URL, CLI_INVOKE_COMMAND, type Provider } from \"../../consts.js\";\nimport { detectInstallations, type DetectedInstallation } from \"./detect-installations.js\";\nimport { chooseProviderForThisRun } from \"./install-layout.js\";\n\n/**\n * The flag itself, declared once and built into each command's own `static flags`.\n *\n * `options` is what makes the roster closed: oclif refuses an unlisted value with exit 2 and a\n * message naming every provider there is, so a user who typed one wrong is told the spelling of\n * the one they wanted rather than that something was invalid.\n *\n * A FACTORY rather than a shared constant, for the reason `agentCodec` and\n * `claudeOfferedPlacements` each give: every command declaring it would otherwise hold one object\n * by identity, oclif writes onto a flag definition while building a command's flag map, and\n * nothing in the types says either thing.\n */\nexport function providerFlag() {\n return Flags.string({\n description: \"Which provider's installation this run is about\",\n helpValue: `<${PROVIDERS.join(\"|\")}>`,\n options: [...PROVIDERS],\n });\n}\n\n/**\n * The parsed flag as a {@link Provider}, or `undefined` for a run that named none.\n *\n * oclif has already refused anything outside the roster by the time this is reached — `options`\n * above is checked during `parse` — so the guard here is a narrowing rather than a second\n * validation, and it is written as a membership test rather than a cast because a cast would\n * survive the roster growing a member this switch has never seen.\n */\nexport function providerNamedBy(flagValue: string | undefined): Provider | undefined {\n if (flagValue === undefined) return undefined;\n return PROVIDERS.find((provider) => provider === flagValue);\n}\n\n/**\n * What `init` says when it is handed a provider and no configuration to install.\n *\n * Both halves are load-bearing. `--from` is what the flag needs, and the EDITOR is where an id\n * comes from — a user told only that `--from` is missing has no way to produce one, and the CLI\n * has no step that mints it. The `/editor` segment rather than the apex, because the apex is a\n * landing page that knows nothing about a configuration.\n */\nexport function providerNeedsAConfigurationToInstall(): string {\n return (\n `--provider names which installation to create and needs the configuration to create it ` +\n `from, so it is only meaningful beside --from. Build one at ${EDITOR_URL}, then run ` +\n `'${CLI_INVOKE_COMMAND} init --from <id> --provider <provider>'.`\n );\n}\n\n/**\n * What a command says when the scope it is about holds an installation of each provider.\n *\n * It names both, because \"there is more than one\" leaves the user to go and look; and it names the\n * flag, because the sentence is only worth printing if it says how to answer.\n */\nfunction ambiguousInstallation(\n subject: string,\n providers: readonly Provider[],\n scopeRoot: string,\n): string {\n return (\n `${scopeRoot} holds ${providers.length} installations — ${providers.join(\" and \")} — so ` +\n `${subject} cannot tell which one you mean. Name it with --provider <${providers.join(\"|\")}>. ` +\n `Nothing has been changed.`\n );\n}\n\n/** One scope's installations: the directory they are under, and which providers are there. */\ntype InstallationsInPlay = {\n scopeRoot: string;\n providers: Provider[];\n};\n\n/**\n * The installations a run from `cwd` could act on, and which providers they belong to.\n *\n * The project's own first and the global only when the project has none, which is the fallback\n * every command already walks — a project installation is the subject of a command run inside it,\n * and the global is what a run with no project installation is about. Answering over both at once\n * would refuse a Claude project on a machine whose HOME happens to carry two installations, which\n * is a fact about the machine rather than about this run.\n */\nasync function installationsInPlay(cwd: string): Promise<InstallationsInPlay> {\n const found = await detectInstallations(cwd);\n const inTheProject = atScope(found, \"project\");\n\n if (inTheProject.length > 0) return { scopeRoot: cwd, providers: providersOf(inTheProject) };\n // `os.homedir()` rather than a path derived from a config file, because it is the root\n // `detectInstallations` itself looked under — so the directory the message names is the\n // directory that was read, under a pinned HOME as much as under a real one.\n return { scopeRoot: os.homedir(), providers: providersOf(atScope(found, \"global\")) };\n}\n\n/** The installations one scope root holds. */\nfunction atScope(\n found: readonly DetectedInstallation[],\n scope: DetectedInstallation[\"scope\"],\n): DetectedInstallation[] {\n return found.filter((installation) => installation.scope === scope);\n}\n\n/** Which providers a set of installations belongs to, in the roster order discovery answered in. */\nfunction providersOf(installations: readonly DetectedInstallation[]): Provider[] {\n return installations.map((installation) => installation.provider);\n}\n\n/**\n * Settles which installation a command acts on, and refuses rather than guessing.\n *\n * Three endings, and the middle one is the whole point:\n *\n * - a run TOLD a provider commits to it for every path it builds, through\n * {@link chooseProviderForThisRun}. That is what makes the refusal below actionable.\n * - a scope holding two installations and no flag is refused by `onAmbiguous`, which never\n * returns: half of this — a refusal naming a flag that selects nothing — is worse than neither.\n * - anything else is left exactly as it was, so a scope holding one installation needs no flag and\n * every existing invocation keeps working.\n *\n * `onAmbiguous` is the command's own `this.error`, so the exit code and the rendering are oclif's\n * rather than this module's; it is typed `never` so a caller cannot forget to stop.\n */\nexport async function refuseAnAmbiguousInstallation(\n cwd: string,\n subject: string,\n named: Provider | undefined,\n onAmbiguous: (message: string) => never,\n): Promise<void> {\n if (named !== undefined) {\n chooseProviderForThisRun(named);\n return;\n }\n\n const { scopeRoot, providers } = await installationsInPlay(cwd);\n if (providers.length <= 1) return;\n\n onAmbiguous(ambiguousInstallation(subject, providers, scopeRoot));\n}\n\n/**\n * The same question asked without refusing, for `doctor` — which changes nothing and therefore\n * reports rather than stops.\n *\n * A read-only command that refused would leave a user whose scope holds two installations unable\n * to LOOK at either, which is the one state they most need to see.\n */\nexport async function ambiguityFinding(cwd: string): Promise<string | null> {\n const { scopeRoot, providers } = await installationsInPlay(cwd);\n if (providers.length <= 1) return null;\n\n return ambiguousInstallation(\"every command that acts on one\", providers, scopeRoot);\n}\n\n/**\n * The same question for a command that REPORTS one of the installations — `list`.\n *\n * A third sentence rather than either of the two above, because `list` is in neither of their\n * positions. It writes nothing, so refusing would leave a user unable to LOOK at what they have;\n * and it does not describe the whole scope the way `doctor` does, so \"every command that acts on\n * one\" would be a sentence about other commands printed above a report of one installation. What\n * it owes is the fact its own report omits: there is a second installation here, this is not it,\n * and here is how to ask for the other.\n *\n * `null` where the scope holds one installation, so the ordinary case prints nothing at all.\n */\nexport async function otherInstallationsInThisScope(\n cwd: string,\n showing: Provider,\n): Promise<string | null> {\n const { scopeRoot, providers } = await installationsInPlay(cwd);\n const others = providers.filter((provider) => provider !== showing);\n if (others.length === 0) return null;\n\n return (\n `${scopeRoot} holds ${providers.length} installations — ${providers.join(\" and \")} — and this ` +\n `is the ${showing} one. Show another with --provider <${providers.join(\"|\")}>.`\n );\n}\n"],"mappings":";;;;;;;;;;;;AA2CA,OAAO,QAAQ;AAEf,SAAS,aAAa;AAkBf,SAAS,eAAe;AAC7B,SAAO,MAAM,OAAO;AAAA,IAClB,aAAa;AAAA,IACb,WAAW,IAAI,UAAU,KAAK,GAAG,CAAC;AAAA,IAClC,SAAS,CAAC,GAAG,SAAS;AAAA,EACxB,CAAC;AACH;AAUO,SAAS,gBAAgB,WAAqD;AACnF,MAAI,cAAc,OAAW,QAAO;AACpC,SAAO,UAAU,KAAK,CAAC,aAAa,aAAa,SAAS;AAC5D;AAUO,SAAS,uCAA+C;AAC7D,SACE,qJAC8D,UAAU,eACpE,kBAAkB;AAE1B;AAQA,SAAS,sBACP,SACA,WACA,WACQ;AACR,SACE,GAAG,SAAS,UAAU,UAAU,MAAM,yBAAoB,UAAU,KAAK,OAAO,CAAC,cAC9E,OAAO,6DAA6D,UAAU,KAAK,GAAG,CAAC;AAG9F;AAiBA,eAAe,oBAAoB,KAA2C;AAC5E,QAAM,QAAQ,MAAM,oBAAoB,GAAG;AAC3C,QAAM,eAAe,QAAQ,OAAO,SAAS;AAE7C,MAAI,aAAa,SAAS,EAAG,QAAO,EAAE,WAAW,KAAK,WAAW,YAAY,YAAY,EAAE;AAI3F,SAAO,EAAE,WAAW,GAAG,QAAQ,GAAG,WAAW,YAAY,QAAQ,OAAO,QAAQ,CAAC,EAAE;AACrF;AAGA,SAAS,QACP,OACA,OACwB;AACxB,SAAO,MAAM,OAAO,CAAC,iBAAiB,aAAa,UAAU,KAAK;AACpE;AAGA,SAAS,YAAY,eAA4D;AAC/E,SAAO,cAAc,IAAI,CAAC,iBAAiB,aAAa,QAAQ;AAClE;AAiBA,eAAsB,8BACpB,KACA,SACA,OACA,aACe;AACf,MAAI,UAAU,QAAW;AACvB,6BAAyB,KAAK;AAC9B;AAAA,EACF;AAEA,QAAM,EAAE,WAAW,UAAU,IAAI,MAAM,oBAAoB,GAAG;AAC9D,MAAI,UAAU,UAAU,EAAG;AAE3B,cAAY,sBAAsB,SAAS,WAAW,SAAS,CAAC;AAClE;AASA,eAAsB,iBAAiB,KAAqC;AAC1E,QAAM,EAAE,WAAW,UAAU,IAAI,MAAM,oBAAoB,GAAG;AAC9D,MAAI,UAAU,UAAU,EAAG,QAAO;AAElC,SAAO,sBAAsB,kCAAkC,WAAW,SAAS;AACrF;AAcA,eAAsB,8BACpB,KACA,SACwB;AACxB,QAAM,EAAE,WAAW,UAAU,IAAI,MAAM,oBAAoB,GAAG;AAC9D,QAAM,SAAS,UAAU,OAAO,CAAC,aAAa,aAAa,OAAO;AAClE,MAAI,OAAO,WAAW,EAAG,QAAO;AAEhC,SACE,GAAG,SAAS,UAAU,UAAU,MAAM,yBAAoB,UAAU,KAAK,OAAO,CAAC,2BACvE,OAAO,uCAAuC,UAAU,KAAK,GAAG,CAAC;AAE/E;","names":[]}
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getAgentDefinitions
4
- } from "./chunk-JU4ZGJ7A.js";
4
+ } from "./chunk-ZR4YPKXQ.js";
5
5
  import {
6
6
  loadMergedAgents
7
- } from "./chunk-XPDVAJDH.js";
7
+ } from "./chunk-A4KDXNOT.js";
8
8
 
9
9
  // src/cli/lib/operations/project/load-agent-defs.ts
10
10
  async function loadAgentDefs() {
@@ -20,4 +20,4 @@ async function loadAgentDefs() {
20
20
  export {
21
21
  loadAgentDefs
22
22
  };
23
- //# sourceMappingURL=chunk-B6FSLXYN.js.map
23
+ //# sourceMappingURL=chunk-PMJWD27C.js.map