agents-inc 0.170.0 → 0.170.2

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 (163) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/{chunk-BMRUNMPV.js → chunk-2YA4DDRM.js} +35 -35
  3. package/dist/chunk-2YA4DDRM.js.map +1 -0
  4. package/dist/{chunk-44TE3RKH.js → chunk-3ED3KHHD.js} +5 -4
  5. package/dist/chunk-3ED3KHHD.js.map +1 -0
  6. package/dist/{chunk-3G7X2WKI.js → chunk-5BNX73OU.js} +18 -38
  7. package/dist/chunk-5BNX73OU.js.map +1 -0
  8. package/dist/{chunk-7RDMTR64.js → chunk-66AEVERM.js} +785 -256
  9. package/dist/chunk-66AEVERM.js.map +1 -0
  10. package/dist/{chunk-PJWGTNOX.js → chunk-7C3PFRBU.js} +144 -186
  11. package/dist/chunk-7C3PFRBU.js.map +1 -0
  12. package/dist/{chunk-52HDHJ7I.js → chunk-AUNRHWH4.js} +32 -21
  13. package/dist/chunk-AUNRHWH4.js.map +1 -0
  14. package/dist/{chunk-KOLDW6SF.js → chunk-BBDGYJHX.js} +2 -2
  15. package/dist/chunk-BBDGYJHX.js.map +1 -0
  16. package/dist/{chunk-SZYQISA2.js → chunk-ELV5WDS3.js} +43 -29
  17. package/dist/chunk-ELV5WDS3.js.map +1 -0
  18. package/dist/{chunk-MQ6CGZNJ.js → chunk-GAKVZNTX.js} +1879 -2019
  19. package/dist/chunk-GAKVZNTX.js.map +1 -0
  20. package/dist/chunk-GEMW5RQ2.js +1169 -0
  21. package/dist/chunk-GEMW5RQ2.js.map +1 -0
  22. package/dist/{chunk-NQW25DCF.js → chunk-GHHJYNAN.js} +1 -7
  23. package/dist/chunk-GHHJYNAN.js.map +1 -0
  24. package/dist/{chunk-WTWMGH2C.js → chunk-HACRR422.js} +2 -2
  25. package/dist/chunk-HACRR422.js.map +1 -0
  26. package/dist/{chunk-F2BHT5C4.js → chunk-IWY5IR4F.js} +129 -151
  27. package/dist/chunk-IWY5IR4F.js.map +1 -0
  28. package/dist/{chunk-JFLVEFFP.js → chunk-KIK5EZYD.js} +1 -1
  29. package/dist/chunk-KIK5EZYD.js.map +1 -0
  30. package/dist/{chunk-USVBYQSQ.js → chunk-O4LN3HFI.js} +16 -6
  31. package/dist/chunk-O4LN3HFI.js.map +1 -0
  32. package/dist/{chunk-RIPSGNLU.js → chunk-OCAVW6N5.js} +3 -3
  33. package/dist/chunk-OCAVW6N5.js.map +1 -0
  34. package/dist/{chunk-4AAOOAIN.js → chunk-P6CYS4JK.js} +2 -2
  35. package/dist/chunk-P6CYS4JK.js.map +1 -0
  36. package/dist/chunk-PLFANHRZ.js +35 -0
  37. package/dist/chunk-PLFANHRZ.js.map +1 -0
  38. package/dist/{chunk-6ZRH5RHC.js → chunk-PMUZR4XO.js} +4 -4
  39. package/dist/chunk-PMUZR4XO.js.map +1 -0
  40. package/dist/{chunk-F5EBEATU.js → chunk-PSUTNOQ2.js} +34 -46
  41. package/dist/chunk-PSUTNOQ2.js.map +1 -0
  42. package/dist/chunk-RIK45Q4I.js +103 -0
  43. package/dist/chunk-RIK45Q4I.js.map +1 -0
  44. package/dist/{chunk-HED2DAXZ.js → chunk-THO7LO5B.js} +5 -5
  45. package/dist/chunk-THO7LO5B.js.map +1 -0
  46. package/dist/{chunk-OYJQUA3D.js → chunk-YGDWHDOY.js} +31 -13
  47. package/dist/chunk-YGDWHDOY.js.map +1 -0
  48. package/dist/{chunk-NCWDK6L7.js → chunk-YXKTY5WM.js} +254 -379
  49. package/dist/chunk-YXKTY5WM.js.map +1 -0
  50. package/dist/{chunk-NEZ4OAOL.js → chunk-ZEGM3MVR.js} +3 -5
  51. package/dist/chunk-ZEGM3MVR.js.map +1 -0
  52. package/dist/commands/build/marketplace.js +46 -46
  53. package/dist/commands/build/marketplace.js.map +1 -1
  54. package/dist/commands/build/plugins.js +25 -28
  55. package/dist/commands/build/plugins.js.map +1 -1
  56. package/dist/commands/compile.js +87 -178
  57. package/dist/commands/compile.js.map +1 -1
  58. package/dist/commands/doctor.js +510 -360
  59. package/dist/commands/doctor.js.map +1 -1
  60. package/dist/commands/edit.js +451 -357
  61. package/dist/commands/edit.js.map +1 -1
  62. package/dist/commands/eject.js +144 -125
  63. package/dist/commands/eject.js.map +1 -1
  64. package/dist/commands/init.js +20 -27
  65. package/dist/commands/list.js +39 -55
  66. package/dist/commands/list.js.map +1 -1
  67. package/dist/commands/new/marketplace.js +6 -11
  68. package/dist/commands/new/marketplace.js.map +1 -1
  69. package/dist/commands/search.js +35 -32
  70. package/dist/commands/search.js.map +1 -1
  71. package/dist/commands/share.js +17 -77
  72. package/dist/commands/share.js.map +1 -1
  73. package/dist/commands/uninstall.js +159 -222
  74. package/dist/commands/uninstall.js.map +1 -1
  75. package/dist/commands/update.js +9 -32
  76. package/dist/commands/update.js.map +1 -1
  77. package/dist/components/common/prompt-confirm.js +2 -2
  78. package/dist/components/common/removal-plan-confirm.js +2 -2
  79. package/dist/components/common/scroll-affordance.js +2 -2
  80. package/dist/components/common/select-list.js +2 -2
  81. package/dist/components/common/skill-agent-summary.js +4 -4
  82. package/dist/components/common/spinner.js +2 -4
  83. package/dist/components/common/summary-panel.js +5 -5
  84. package/dist/components/common/summary-panel.js.map +1 -1
  85. package/dist/config-exports.js +7 -3
  86. package/dist/config-exports.js.map +1 -1
  87. package/dist/hooks/init.js +18 -19
  88. package/dist/hooks/init.js.map +1 -1
  89. package/dist/index.js.map +1 -1
  90. package/dist/load-agent-defs-6QUUMGHK.js +14 -0
  91. package/dist/recompile-project-agents-I33KQJFB.js +65 -0
  92. package/dist/recompile-project-agents-I33KQJFB.js.map +1 -0
  93. package/dist/{source-loader-C5SQXZXI.js → source-loader-TTJVW2ER.js} +5 -5
  94. package/dist/src/agents/developer/ai-developer/playbook.md +2 -5
  95. package/dist/src/agents/developer/api-developer/playbook.md +2 -5
  96. package/dist/src/agents/developer/cli-developer/playbook.md +2 -5
  97. package/dist/src/agents/developer/web-developer/playbook.md +2 -5
  98. package/dist/src/agents/meta/agent-summoner/output.md +2 -2
  99. package/dist/src/agents/meta/agent-summoner/playbook.md +1 -13
  100. package/dist/src/agents/meta/codex-keeper/critical-requirements.md +2 -5
  101. package/dist/src/agents/meta/convention-keeper/critical-requirements.md +2 -4
  102. package/dist/src/agents/tester/ai-tester/critical-requirements.md +1 -2
  103. package/dist/src/agents/tester/ai-tester/playbook.md +2 -0
  104. package/dist/src/agents/tester/api-tester/critical-requirements.md +1 -2
  105. package/dist/src/agents/tester/api-tester/playbook.md +2 -0
  106. package/dist/src/agents/tester/cli-tester/critical-requirements.md +1 -2
  107. package/dist/src/agents/tester/cli-tester/playbook.md +2 -0
  108. package/dist/src/agents/tester/web-tester/critical-requirements.md +1 -2
  109. package/dist/src/agents/tester/web-tester/playbook.md +2 -0
  110. package/package.json +9 -8
  111. package/src/agents/developer/ai-developer/playbook.md +2 -5
  112. package/src/agents/developer/api-developer/playbook.md +2 -5
  113. package/src/agents/developer/cli-developer/playbook.md +2 -5
  114. package/src/agents/developer/web-developer/playbook.md +2 -5
  115. package/src/agents/meta/agent-summoner/output.md +2 -2
  116. package/src/agents/meta/agent-summoner/playbook.md +1 -13
  117. package/src/agents/meta/codex-keeper/critical-requirements.md +2 -5
  118. package/src/agents/meta/convention-keeper/critical-requirements.md +2 -4
  119. package/src/agents/tester/ai-tester/critical-requirements.md +1 -2
  120. package/src/agents/tester/ai-tester/playbook.md +2 -0
  121. package/src/agents/tester/api-tester/critical-requirements.md +1 -2
  122. package/src/agents/tester/api-tester/playbook.md +2 -0
  123. package/src/agents/tester/cli-tester/critical-requirements.md +1 -2
  124. package/src/agents/tester/cli-tester/playbook.md +2 -0
  125. package/src/agents/tester/web-tester/critical-requirements.md +1 -2
  126. package/src/agents/tester/web-tester/playbook.md +2 -0
  127. package/src/schemas/project-config.schema.json +0 -3
  128. package/src/schemas/project-source-config.schema.json +0 -6
  129. package/dist/chunk-3G7X2WKI.js.map +0 -1
  130. package/dist/chunk-44TE3RKH.js.map +0 -1
  131. package/dist/chunk-4AAOOAIN.js.map +0 -1
  132. package/dist/chunk-52HDHJ7I.js.map +0 -1
  133. package/dist/chunk-6HNXYYVF.js +0 -373
  134. package/dist/chunk-6HNXYYVF.js.map +0 -1
  135. package/dist/chunk-6ZRH5RHC.js.map +0 -1
  136. package/dist/chunk-7RDMTR64.js.map +0 -1
  137. package/dist/chunk-BMRUNMPV.js.map +0 -1
  138. package/dist/chunk-CLPUWOSA.js +0 -46
  139. package/dist/chunk-CLPUWOSA.js.map +0 -1
  140. package/dist/chunk-F2BHT5C4.js.map +0 -1
  141. package/dist/chunk-F5EBEATU.js.map +0 -1
  142. package/dist/chunk-HED2DAXZ.js.map +0 -1
  143. package/dist/chunk-JFLVEFFP.js.map +0 -1
  144. package/dist/chunk-KOLDW6SF.js.map +0 -1
  145. package/dist/chunk-MQ6CGZNJ.js.map +0 -1
  146. package/dist/chunk-NCWDK6L7.js.map +0 -1
  147. package/dist/chunk-NEZ4OAOL.js.map +0 -1
  148. package/dist/chunk-NIHUOWE4.js +0 -106
  149. package/dist/chunk-NIHUOWE4.js.map +0 -1
  150. package/dist/chunk-NQW25DCF.js.map +0 -1
  151. package/dist/chunk-OYJQUA3D.js.map +0 -1
  152. package/dist/chunk-PJWGTNOX.js.map +0 -1
  153. package/dist/chunk-RIPSGNLU.js.map +0 -1
  154. package/dist/chunk-SZYQISA2.js.map +0 -1
  155. package/dist/chunk-USVBYQSQ.js.map +0 -1
  156. package/dist/chunk-V3KTSP3Q.js +0 -940
  157. package/dist/chunk-V3KTSP3Q.js.map +0 -1
  158. package/dist/chunk-WTWMGH2C.js.map +0 -1
  159. package/dist/load-agent-defs-YMDWBFDP.js +0 -14
  160. package/dist/recompile-project-agents-SZ76WNTW.js +0 -18
  161. package/dist/source-loader-C5SQXZXI.js.map +0 -1
  162. /package/dist/{load-agent-defs-YMDWBFDP.js.map → load-agent-defs-6QUUMGHK.js.map} +0 -0
  163. /package/dist/{recompile-project-agents-SZ76WNTW.js.map → source-loader-TTJVW2ER.js.map} +0 -0
@@ -2,18 +2,17 @@
2
2
  import {
3
3
  compileAgents,
4
4
  withCatalogueSeatedFor
5
- } from "./chunk-F2BHT5C4.js";
5
+ } from "./chunk-IWY5IR4F.js";
6
6
  import {
7
7
  loadAgentDefs
8
- } from "./chunk-HED2DAXZ.js";
8
+ } from "./chunk-THO7LO5B.js";
9
9
  import {
10
10
  ConfigLoadError,
11
11
  GlobalPairWriteViolation,
12
+ INDENT_STEP,
12
13
  assertGateToken,
14
+ brokenUnionLayout,
13
15
  buildAndMergeConfig,
14
- buildConfigTypesBackgroundData,
15
- deriveCategories,
16
- deriveDomains,
17
16
  detectGlobalInstallation,
18
17
  detectInstallation,
19
18
  detectProjectInstallation,
@@ -22,14 +21,14 @@ import {
22
21
  enableBuffering,
23
22
  ensureDir,
24
23
  fileExists,
24
+ flatUnion,
25
25
  generateBlankGlobalConfigSource,
26
- generateBlankGlobalConfigTypesSource,
27
26
  generateConfigSource,
28
- generateConfigTypesSource,
29
27
  getErrorMessage,
30
28
  getInstalledConfigPath,
31
29
  getProjectConfigPath,
32
30
  isHomeDirectory,
31
+ isRecord,
33
32
  loadInstalledConfig,
34
33
  loadProjectConfig,
35
34
  loadProjectConfigFromDir,
@@ -37,59 +36,66 @@ import {
37
36
  mergeConfigs,
38
37
  normalizeStackRecord,
39
38
  providerInUse,
39
+ quoteText,
40
40
  readFile,
41
- regenerateConfigTypes,
41
+ renderKey,
42
+ renderTypeImportLine,
43
+ renderUnionBody,
42
44
  resolveInstallPaths,
43
45
  sourceFolderInUse,
46
+ spaces,
44
47
  splitConfigByScope,
45
- typedEntries as typedEntries2,
48
+ typedEntries,
49
+ typedEntries2,
50
+ typedKeys,
51
+ typedKeys2,
52
+ unionLayout,
46
53
  verbose,
47
54
  warn,
48
55
  withGateToken,
49
56
  writeFile
50
- } from "./chunk-MQ6CGZNJ.js";
57
+ } from "./chunk-GAKVZNTX.js";
51
58
  import {
52
- matrix,
53
- typedEntries,
54
- typedKeys
55
- } from "./chunk-52HDHJ7I.js";
59
+ matrix
60
+ } from "./chunk-AUNRHWH4.js";
56
61
  import {
57
62
  CLI_INVOKE_COMMAND,
63
+ CODEX_MODEL_NAMES,
64
+ EFFORT_NAMES,
58
65
  GLOBAL_CONFIG_NAME,
59
66
  LOCAL_PSEUDO_CATEGORY,
67
+ MODEL_NAMES,
60
68
  PROVIDERS,
61
69
  STANDARD_FILES,
70
+ activeAgentNames,
62
71
  activeProjectAgentNames,
72
+ bytewise,
73
+ globalInstallRoot,
63
74
  isActiveAt,
75
+ isExclusiveCategory,
64
76
  isGlobalTombstone,
65
77
  skillDomain
66
- } from "./chunk-PJWGTNOX.js";
78
+ } from "./chunk-7C3PFRBU.js";
67
79
 
68
80
  // src/cli/lib/operations/source/load-source.ts
69
81
  async function loadSource(options) {
70
82
  const { caller, sourceFlag, projectDir, captureStartupMessages } = options;
71
- if (captureStartupMessages) {
72
- enableBuffering();
73
- }
74
- let sourceResult;
83
+ const load = () => loadSkillsMatrixFromSource({
84
+ ...caller !== void 0 && { caller },
85
+ ...sourceFlag !== void 0 && { sourceFlag },
86
+ projectDir
87
+ });
88
+ if (!captureStartupMessages) return { sourceResult: await load(), startupMessages: [] };
89
+ return capturingStartupMessages(load);
90
+ }
91
+ async function capturingStartupMessages(load) {
92
+ enableBuffering();
75
93
  try {
76
- sourceResult = await loadSkillsMatrixFromSource({
77
- ...caller !== void 0 && { caller },
78
- ...sourceFlag !== void 0 && { sourceFlag },
79
- projectDir
80
- });
81
- } catch (error) {
82
- if (captureStartupMessages) {
83
- disableBuffering();
84
- }
85
- throw error;
86
- }
87
- let startupMessages = [];
88
- if (captureStartupMessages) {
89
- startupMessages = drainBuffer();
94
+ const sourceResult = await load();
95
+ return { sourceResult, startupMessages: drainBuffer() };
96
+ } finally {
90
97
  disableBuffering();
91
98
  }
92
- return { sourceResult, startupMessages };
93
99
  }
94
100
  function sayCapturedWarnings(messages) {
95
101
  for (const { text } of messages) warn(text);
@@ -123,184 +129,451 @@ async function detectBothInstallations(projectDir) {
123
129
  return { global, project, hasBoth: !!global && !!project };
124
130
  }
125
131
 
126
- // src/cli/lib/config-gate/index.ts
127
- import os2 from "os";
128
- import path2 from "path";
132
+ // src/cli/lib/config-gate/propagate.ts
133
+ import fs from "fs";
134
+ import { isDeepEqual as isDeepEqual2, unique as unique2 } from "remeda";
129
135
 
130
- // src/cli/lib/config-gate/classify.ts
131
- import { isDeepEqual } from "remeda";
132
- var NO_CHANGES = {
133
- skills: { added: [], removed: [], sourceChanged: [], otherChanged: [] },
134
- agents: { added: [], removed: [], changed: [] },
135
- stackChanged: false,
136
- selectedDomainsChanged: false,
137
- scalarsChanged: [],
138
- projectsChanged: false
139
- };
140
- var EXTRACTED_FIELDS = /* @__PURE__ */ new Set(["skills", "agents", "stack", "selectedDomains"]);
141
- var REGISTRATION_FIELD = "projects";
142
- function normalize(config) {
143
- return JSON.parse(JSON.stringify(config));
136
+ // ../compile/src/config-types-source.ts
137
+ import { groupBy, unique } from "remeda";
138
+ var EMPTY_UNION_TYPE = "never";
139
+ function formatLiteralUnion(members) {
140
+ return flatUnion(members.map(quoteText));
144
141
  }
145
- function field(config, key) {
146
- return config[key];
142
+ var PROJECT_CONFIG_TYPES_BEFORE = `export type InstallMode = 'eject' | 'plugin' | 'mixed'
143
+
144
+ export type SkillConfig = {
145
+ id: SkillId
146
+ scope: 'project' | 'global'
147
+ origin: string
148
+ excluded?: boolean
147
149
  }
148
- function skillsById(config) {
149
- const skills = config.skills ?? [];
150
- return new Map(skills.map((skill) => [skill.id, skill]));
150
+
151
+ export type AgentScopeConfig = {
152
+ name: AgentName
153
+ scope: 'project' | 'global'
154
+ model?: ${formatLiteralUnion(MODEL_NAMES)}
155
+ codexModel?: ${formatLiteralUnion(CODEX_MODEL_NAMES)}
156
+ effort?: ${formatLiteralUnion(EFFORT_NAMES)}
157
+ excluded?: boolean
151
158
  }
152
- function agentsByName(config) {
153
- const agents = config.agents ?? [];
154
- return new Map(agents.map((agent) => [agent.name, agent]));
159
+
160
+ export type SkillAssignment<S extends SkillId = SkillId> = S | { id: S; preloaded: boolean }
161
+ `;
162
+ var PROJECT_CONFIG_INTERFACE_AFTER = `export interface ProjectConfig {
163
+ /** Project/plugin name (kebab-case) */
164
+ name: string
165
+
166
+ /** Project description */
167
+ description?: string
168
+
169
+ /** Per-agent configuration with scope */
170
+ agents: AgentScopeConfig[]
171
+
172
+ /** Per-skill configuration with scope and provenance */
173
+ skills: SkillConfig[]
174
+
175
+ /** Author handle (e.g., "@vince") */
176
+ author?: string
177
+
178
+ /** Stack configuration: agent -> category -> skill assignment */
179
+ stack?: Partial<Record<ProjectAgentName, StackAgentConfig>>
180
+
181
+ /** The marketplace this install reads skills from, as a path or URL */
182
+ marketplace?: string
183
+
184
+ /** The name that marketplace's manifest gives it, which plugins are registered under */
185
+ marketplaceName?: string
186
+
187
+ /** Selected domains */
188
+ selectedDomains?: Domain[]
189
+
190
+ /** Branding overrides for white-labeling the CLI */
191
+ branding?: { name?: string }
192
+
193
+ /** Custom skills directory override (default: "src/skills") */
194
+ skillsDir?: string
195
+
196
+ /** Custom stacks file path override (default: "config/stacks.ts") */
197
+ stacksFile?: string
198
+
199
+ /** Custom categories file path override (default: "config/skill-categories.ts") */
200
+ categoriesFile?: string
201
+
202
+ /** Custom rules file path override (default: "config/skill-rules.ts") */
203
+ rulesFile?: string
204
+
205
+ /** Tracked project installation paths (global config only) */
206
+ projects?: string[]
155
207
  }
156
- function scalarKeys(prev, next) {
157
- const isScalarKey = (key) => !EXTRACTED_FIELDS.has(key) && key !== REGISTRATION_FIELD;
158
- const nextKeys = Object.keys(next).filter(isScalarKey);
159
- const extraPrevKeys = Object.keys(prev).filter(
160
- (key) => isScalarKey(key) && !nextKeys.includes(key)
161
- );
162
- return [...nextKeys, ...extraPrevKeys];
208
+ `;
209
+ var STACK_AGENT_CONFIG_LOOSE_LINE = "export type StackAgentConfig = Partial<Record<Category, SkillAssignment | SkillAssignment[]>>";
210
+ function assembleConfigTypesSource(parts) {
211
+ const importSection = parts.importBlock ? `${parts.importBlock}
212
+
213
+ ` : "";
214
+ return `// AUTO-GENERATED by agents-inc \u2014 DO NOT EDIT
215
+
216
+ ${importSection}${renderAlias("SkillId", parts.skillId)}
217
+
218
+ ${renderAlias("AgentName", parts.agentName)}
219
+
220
+ ${renderAlias("SelectedAgentName", parts.selectedAgentName)}
221
+
222
+ ${renderAlias("ProjectAgentName", parts.projectAgentName)}
223
+
224
+ ${renderAlias("Domain", parts.domain)}
225
+
226
+ ${renderAlias("Category", parts.category)}
227
+
228
+ ${PROJECT_CONFIG_TYPES_BEFORE}
229
+ ${parts.stackAgentConfig}
230
+
231
+ ${PROJECT_CONFIG_INTERFACE_AFTER}`;
163
232
  }
164
- function diffSkills(prev, next) {
165
- const before = skillsById(prev);
166
- const after = skillsById(next);
167
- const added = [...after.keys()].filter((id) => !before.has(id));
168
- const removed = [...before.keys()].filter((id) => !after.has(id));
169
- const sourceChanged = [];
170
- const otherChanged = [];
171
- for (const [id, afterEntry] of after) {
172
- const beforeEntry = before.get(id);
173
- if (!beforeEntry || isDeepEqual(beforeEntry, afterEntry)) continue;
174
- if (beforeEntry.origin !== afterEntry.origin) sourceChanged.push(id);
175
- else otherChanged.push(id);
233
+ function literalUnion(members) {
234
+ return members.length === 0 ? typeReference(EMPTY_UNION_TYPE) : { kind: "plain", members: members.map(quoteText) };
235
+ }
236
+ function typeReference(name) {
237
+ return { kind: "plain", members: [name] };
238
+ }
239
+ function renderAlias(name, rhs) {
240
+ const head = `export type ${name} =`;
241
+ if (rhs.kind === "sectioned") {
242
+ return `${head}
243
+ ${renderSectionedUnion(rhs.custom, rhs.marketplace)}`;
176
244
  }
177
- return { added, removed, sourceChanged, otherChanged };
245
+ const layout = unionLayout(rhs.members, {
246
+ headWidth: head.length + " ".length,
247
+ tailWidth: 0,
248
+ bodyIndent: INDENT_STEP
249
+ });
250
+ return layout === "inline" ? `${head} ${flatUnion(rhs.members)}` : `${head}
251
+ ${renderUnionBody(rhs.members, layout, INDENT_STEP)}`;
252
+ }
253
+ function renderSectionedUnion(custom, marketplace) {
254
+ if (marketplace.length > 0) {
255
+ return [
256
+ sectionHeading("Custom"),
257
+ renderUnionBody(custom, "stacked", INDENT_STEP),
258
+ sectionHeading("Marketplace"),
259
+ renderUnionBody(marketplace, "stacked", INDENT_STEP)
260
+ ].join("\n");
261
+ }
262
+ return [
263
+ sectionHeading("Custom"),
264
+ renderUnionBody(
265
+ custom,
266
+ brokenUnionLayout(custom, INDENT_STEP),
267
+ INDENT_STEP
268
+ )
269
+ ].join("\n");
178
270
  }
179
- function diffAgents(prev, next) {
180
- const before = agentsByName(prev);
181
- const after = agentsByName(next);
271
+ function sectionHeading(text) {
272
+ return `${spaces(INDENT_STEP)}// ${text}`;
273
+ }
274
+ function generateStackAgentConfig(skillsByCategory, catalog) {
275
+ if (skillsByCategory.size === 0) {
276
+ return STACK_AGENT_CONFIG_LOOSE_LINE;
277
+ }
278
+ const properties = [...skillsByCategory.entries()].sort(([a], [b]) => bytewise(a, b)).map(([category, skills]) => renderStackProperty(category, skills, catalog));
279
+ return ["export type StackAgentConfig = {", ...properties, "}"].join("\n");
280
+ }
281
+ function renderStackProperty(category, skills, catalog) {
282
+ const members = [...skills].sort().map(quoteText);
283
+ const tail = `>${isExclusiveCategory(catalog, category) ? "" : "[]"}`;
284
+ const head = `${spaces(INDENT_STEP)}${renderKey(category)}?: SkillAssignment<`;
285
+ const bodyIndent = INDENT_STEP * 2;
286
+ const layout = unionLayout(members, {
287
+ headWidth: head.length,
288
+ tailWidth: tail.length,
289
+ bodyIndent
290
+ });
291
+ if (layout === "inline") return `${head}${flatUnion(members)}${tail}`;
292
+ return [
293
+ head,
294
+ renderUnionBody(members, layout, bodyIndent),
295
+ `${spaces(INDENT_STEP)}${tail}`
296
+ ].join("\n");
297
+ }
298
+ function buildSkillsByCategory(skillIds, categories, catalog) {
299
+ const categorySet = new Set(categories);
300
+ const eligible = [...new Set(skillIds)].map((id) => ({ id, category: catalog.skills[id]?.category })).filter(
301
+ (entry) => entry.category !== void 0 && entry.category !== LOCAL_PSEUDO_CATEGORY && // Boundary cast: `string` on the narrow catalogue shape; `has` is the runtime check.
302
+ categorySet.has(entry.category)
303
+ );
304
+ return new Map(
305
+ typedEntries2(groupBy(eligible, (entry) => entry.category)).map(
306
+ ([category, entries]) => [category, entries.map((entry) => entry.id)]
307
+ )
308
+ );
309
+ }
310
+ function isUndeclaredCategory(category, catalog) {
311
+ return catalog.categories[category] === void 0;
312
+ }
313
+ function isCustomSkill(skillId, catalog) {
314
+ const declared = catalog.skills[skillId];
315
+ return declared === void 0 || declared.custom === true;
316
+ }
317
+ function isCustomAgent(agentName, declared, flaggedCustom) {
318
+ return !declared.has(agentName) || flaggedCustom.has(agentName);
319
+ }
320
+ function isCustomDomain(domain, declared) {
321
+ return !declared.has(domain);
322
+ }
323
+ function generateConfigTypesSource(catalog, agentNames, customAgentNames = [], extras, config) {
324
+ const extra = extraMembers(extras);
325
+ const { skillIds, agents, domains, categories } = config ? configMembers(config, extra, catalog) : catalogueMembers(agentNames, extra, catalog);
326
+ const declaredAgents = new Set(agentNames);
327
+ const flaggedCustomAgents = new Set(customAgentNames);
328
+ const declaredDomains = new Set(extractDomains(catalog));
329
+ const skillIdLine = formatMaybeSectionedUnion(
330
+ skillIds,
331
+ (id) => isCustomSkill(id, catalog)
332
+ );
333
+ const agentNameLine = formatMaybeSectionedUnion(
334
+ agents,
335
+ (name) => isCustomAgent(name, declaredAgents, flaggedCustomAgents)
336
+ );
337
+ const domainLine = formatMaybeSectionedUnion(
338
+ domains,
339
+ (domain) => isCustomDomain(domain, declaredDomains)
340
+ );
341
+ const categoryLine = formatMaybeSectionedUnion(
342
+ categories,
343
+ (category) => isUndeclaredCategory(category, catalog)
344
+ );
345
+ const configAgents = config?.agents ?? [];
346
+ const selectedAgentNameLine = narrowedUnion(
347
+ activeAgentNames(configAgents),
348
+ "AgentName"
349
+ );
350
+ const projectAgentNameLine = narrowedUnion(
351
+ activeProjectAgentNames(configAgents),
352
+ "SelectedAgentName"
353
+ );
354
+ const skillsByCategory = buildSkillsByCategory(skillIds, categories, catalog);
355
+ const stackAgentConfigType = generateStackAgentConfig(
356
+ skillsByCategory,
357
+ catalog
358
+ );
359
+ return assembleConfigTypesSource({
360
+ skillId: skillIdLine,
361
+ agentName: agentNameLine,
362
+ selectedAgentName: selectedAgentNameLine,
363
+ projectAgentName: projectAgentNameLine,
364
+ domain: domainLine,
365
+ category: categoryLine,
366
+ stackAgentConfig: stackAgentConfigType
367
+ });
368
+ }
369
+ function extraMembers(extras) {
182
370
  return {
183
- added: [...after.keys()].filter((name) => !before.has(name)),
184
- removed: [...before.keys()].filter((name) => !after.has(name)),
185
- changed: [...after.keys()].filter((name) => {
186
- const beforeEntry = before.get(name);
187
- return beforeEntry !== void 0 && !isDeepEqual(beforeEntry, after.get(name));
188
- })
371
+ skillIds: extras?.extraSkillIds ?? [],
372
+ agents: extras?.extraAgentNames ?? [],
373
+ domains: extras?.extraDomains ?? [],
374
+ categories: extras?.extraCategories ?? []
189
375
  };
190
376
  }
191
- function classifyGlobalChange(prev, next) {
192
- const before = prev ? normalize(prev) : {};
193
- const after = normalize(next);
194
- const moved = (key) => !isDeepEqual(field(before, key), field(after, key));
377
+ function configMembers(config, extra, catalog) {
378
+ const configSkillIds = config.skills.map((s) => s.id);
379
+ const categories = sortedUnion(
380
+ deriveCategories(configSkillIds, catalog),
381
+ extra.categories
382
+ );
195
383
  return {
196
- skills: diffSkills(before, after),
197
- agents: diffAgents(before, after),
198
- stackChanged: moved("stack"),
199
- selectedDomainsChanged: moved("selectedDomains"),
200
- scalarsChanged: scalarKeys(before, after).filter(moved),
201
- projectsChanged: moved(REGISTRATION_FIELD)
384
+ skillIds: sortedUnion(configSkillIds, extra.skillIds),
385
+ agents: sortedUnion(
386
+ config.agents.map((a) => a.name),
387
+ extra.agents
388
+ ),
389
+ // selectedDomains may carry domains with no skills in this scope.
390
+ domains: sortedUnion(
391
+ deriveDomains(categories, catalog),
392
+ config.selectedDomains ?? [],
393
+ extra.domains
394
+ ),
395
+ categories
202
396
  };
203
397
  }
204
- function movesTypesOrCompiledAgents(changes) {
205
- const { skills, agents } = changes;
206
- return skills.added.length > 0 || skills.removed.length > 0 || skills.sourceChanged.length > 0 || skills.otherChanged.length > 0 || agents.added.length > 0 || agents.removed.length > 0 || agents.changed.length > 0 || changes.stackChanged || changes.selectedDomainsChanged;
398
+ function catalogueMembers(agentNames, extra, catalog) {
399
+ return {
400
+ skillIds: sortedUnion(typedKeys2(catalog.skills), extra.skillIds),
401
+ agents: sortedUnion(agentNames, extra.agents),
402
+ domains: sortedUnion(extractDomains(catalog), extra.domains),
403
+ categories: sortedUnion(
404
+ typedKeys2(catalog.categories),
405
+ extra.categories
406
+ )
407
+ };
207
408
  }
208
- function consequenceTier(changes) {
209
- if (movesTypesOrCompiledAgents(changes)) return "T1";
210
- if (changes.scalarsChanged.length > 0) return "T2";
211
- if (changes.projectsChanged) return "T3";
212
- return "T4";
409
+ function sortedUnion(...lists) {
410
+ return unique(lists.flat()).sort();
213
411
  }
214
- function tierNeedsDeps(tier) {
215
- return tier === "T1" || tier === "T2";
412
+ function narrowedUnion(members, fallback) {
413
+ return members.length > 0 ? literalUnion(members) : typeReference(fallback);
216
414
  }
217
- function tierRegeneratesTypes(tier) {
218
- return tier === "T1";
415
+ function extractDomains(catalog) {
416
+ return deriveDomains(typedKeys2(catalog.categories), catalog).sort();
219
417
  }
220
- function tierPropagates(tier) {
221
- return tier === "T1" || tier === "T2";
418
+ var isNonLocalCategory = (category) => Boolean(category) && category !== LOCAL_PSEUDO_CATEGORY;
419
+ function deriveCategories(skillIds, catalog) {
420
+ return unique(
421
+ skillIds.map((id) => catalog.skills[id]?.category).filter(isNonLocalCategory)
422
+ );
222
423
  }
223
-
224
- // src/cli/lib/config-gate/deps.ts
225
- function isLoaded(deps) {
226
- return "matrix" in deps;
424
+ function deriveDomains(categories, catalog) {
425
+ return unique(
426
+ categories.map((cat) => catalog.categories[cat]?.domain).filter((domain) => domain !== void 0)
427
+ );
227
428
  }
228
- async function resolveGateDeps(deps, tier) {
229
- if (!tierNeedsDeps(tier)) return null;
230
- if (isLoaded(deps)) return deps;
231
- const [matrix2, agents] = await Promise.all([
232
- deps.loadMatrix(),
233
- tierRegeneratesTypes(tier) ? deps.loadAgents() : NO_AGENTS
234
- ]);
235
- return { matrix: matrix2, agents };
429
+ function formatMaybeSectionedUnion(members, isCustom) {
430
+ if (members.length === 0) {
431
+ return typeReference(EMPTY_UNION_TYPE);
432
+ }
433
+ const custom = members.filter(isCustom);
434
+ const marketplace = members.filter((m) => !isCustom(m));
435
+ if (custom.length === 0) {
436
+ return literalUnion(members);
437
+ }
438
+ return {
439
+ kind: "sectioned",
440
+ custom: custom.map(quoteText),
441
+ marketplace: marketplace.map(quoteText)
442
+ };
236
443
  }
237
- var NO_AGENTS = Promise.resolve({});
238
-
239
- // src/cli/lib/config-gate/pair-writer.ts
240
- import os from "os";
241
- import path from "path";
242
- function globalPairPaths(provider) {
243
- const dir = sourceFolderInUse(os.homedir(), provider).dir;
444
+ function generateProjectConfigTypesSource(options) {
445
+ const importPath = `${options.globalTypesImportPath}/config-types`;
446
+ const skillIdUnion = formatExtendedUnion(
447
+ "GlobalSkillId",
448
+ options.projectSkillIds
449
+ );
450
+ const agentNameUnion = formatExtendedUnion(
451
+ "GlobalAgentName",
452
+ options.projectAgentNames
453
+ );
454
+ const domainUnion = formatExtendedUnion(
455
+ "GlobalDomain",
456
+ options.projectDomains
457
+ );
458
+ const categoryUnion = formatExtendedUnion(
459
+ "GlobalCategory",
460
+ options.projectCategories ?? []
461
+ );
462
+ const selectedAgentNameUnion = narrowedUnion(
463
+ options.selectedAgentNames ?? [],
464
+ "AgentName"
465
+ );
466
+ const projectAgentNameUnion = narrowedUnion(
467
+ options.projectScopedAgentNames ?? [],
468
+ "SelectedAgentName"
469
+ );
470
+ const importBlock = renderTypeImportLine(
471
+ [
472
+ "SkillId as GlobalSkillId",
473
+ "AgentName as GlobalAgentName",
474
+ "Domain as GlobalDomain",
475
+ "Category as GlobalCategory"
476
+ ],
477
+ importPath
478
+ );
479
+ return assembleConfigTypesSource({
480
+ importBlock,
481
+ skillId: skillIdUnion,
482
+ agentName: agentNameUnion,
483
+ selectedAgentName: selectedAgentNameUnion,
484
+ projectAgentName: projectAgentNameUnion,
485
+ domain: domainUnion,
486
+ category: categoryUnion,
487
+ stackAgentConfig: STACK_AGENT_CONFIG_LOOSE_LINE
488
+ });
489
+ }
490
+ function generateBlankGlobalConfigTypesSource() {
491
+ return assembleConfigTypesSource({
492
+ skillId: typeReference(EMPTY_UNION_TYPE),
493
+ agentName: typeReference(EMPTY_UNION_TYPE),
494
+ selectedAgentName: typeReference(EMPTY_UNION_TYPE),
495
+ projectAgentName: typeReference("SelectedAgentName"),
496
+ domain: typeReference(EMPTY_UNION_TYPE),
497
+ category: typeReference(EMPTY_UNION_TYPE),
498
+ stackAgentConfig: STACK_AGENT_CONFIG_LOOSE_LINE
499
+ });
500
+ }
501
+ function formatExtendedUnion(globalTypeName, projectMembers) {
502
+ if (projectMembers.length === 0) {
503
+ return typeReference(globalTypeName);
504
+ }
244
505
  return {
245
- configPath: path.join(dir, STANDARD_FILES.CONFIG_TS),
246
- typesPath: path.join(dir, STANDARD_FILES.CONFIG_TYPES_TS)
506
+ kind: "plain",
507
+ members: [globalTypeName, ...[...projectMembers].sort().map(quoteText)]
247
508
  };
248
509
  }
249
- function typesPathFor(configPath) {
250
- return path.join(path.dirname(configPath), STANDARD_FILES.CONFIG_TYPES_TS);
510
+
511
+ // src/cli/lib/configuration/config-types-io.ts
512
+ import path from "path";
513
+ async function getGlobalConfigTypesPath(provider) {
514
+ const globalConfigTypesPath = path.join(
515
+ sourceFolderInUse(globalInstallRoot(), provider).dir,
516
+ STANDARD_FILES.CONFIG_TYPES_TS
517
+ );
518
+ if (await fileExists(globalConfigTypesPath)) {
519
+ return globalConfigTypesPath;
520
+ }
521
+ return null;
251
522
  }
252
- async function writeIfChanged(filePath, source) {
253
- assertGateToken(filePath);
254
- if (await fileExists(filePath) && await readFile(filePath) === source) return false;
255
- await writeFile(filePath, source);
256
- return true;
523
+ function computeGlobalTypesImportPath(projectDir, provider) {
524
+ const projectSourceDir = sourceFolderInUse(projectDir, provider).dir;
525
+ const globalSourceDir = sourceFolderInUse(globalInstallRoot(), provider).dir;
526
+ const relativePath = path.relative(projectSourceDir, globalSourceDir);
527
+ return relativePath.split(path.sep).join("/");
257
528
  }
258
- function renderStandaloneTypes(config, matrix2, agents, extras) {
259
- const data = buildConfigTypesBackgroundData(matrix2, agents);
260
- return generateConfigTypesSource(
529
+ function buildConfigTypesBackgroundData(matrix2, agents) {
530
+ const agentNames = typedKeys(agents);
531
+ return {
532
+ matrix: matrix2,
533
+ agentNames,
534
+ customAgentNames: agentNames.filter((name) => agents[name]?.custom === true)
535
+ };
536
+ }
537
+ async function regenerateConfigTypes(projectDir, backgroundData, extras) {
538
+ const provider = providerInUse(projectDir);
539
+ const configTypesPath = path.join(
540
+ sourceFolderInUse(projectDir, provider).dir,
541
+ STANDARD_FILES.CONFIG_TYPES_TS
542
+ );
543
+ if (isHomeDirectory(projectDir)) throw new GlobalPairWriteViolation(configTypesPath);
544
+ const [data, globalConfigTypes, loadedConfig] = await Promise.all([
545
+ backgroundData,
546
+ getGlobalConfigTypesPath(provider),
547
+ loadProjectConfigFromDir(projectDir, provider)
548
+ ]);
549
+ const source = globalConfigTypes ? renderTypesExtendingGlobal(projectDir, provider, loadedConfig?.config.agents, extras) : generateConfigTypesSource(
261
550
  data.matrix,
262
551
  data.agentNames,
263
552
  data.customAgentNames,
264
553
  extras,
265
- config
266
- );
267
- }
268
- async function writeGlobalConfigHalf(config, configPath) {
269
- return writeIfChanged(configPath, generateConfigSource(config, matrix));
270
- }
271
- async function writeGlobalTypesHalf(config, configPath, matrix2, agents, extras) {
272
- return writeIfChanged(
273
- typesPathFor(configPath),
274
- renderStandaloneTypes(config, matrix2, agents, extras)
275
- );
276
- }
277
- async function writeGlobalPair(config, configPath, matrix2, agents) {
278
- const configWritten = await writeIfChanged(configPath, generateConfigSource(config, matrix2));
279
- const typesWritten = await writeIfChanged(
280
- typesPathFor(configPath),
281
- renderStandaloneTypes(config, matrix2, agents)
554
+ loadedConfig?.config
282
555
  );
283
- return configWritten || typesWritten;
284
- }
285
- async function ensureBlankPair(provider) {
286
- const { configPath, typesPath } = globalPairPaths(provider);
287
- if (await fileExists(configPath)) {
288
- verbose("Global config already exists, skipping blank creation");
289
- return false;
290
- }
291
- await ensureDir(path.dirname(configPath));
292
- await writeIfChanged(configPath, generateBlankGlobalConfigSource());
293
- await writeIfChanged(typesPath, generateBlankGlobalConfigTypesSource());
294
- verbose(`Created blank global config at ${path.dirname(configPath)}`);
295
- return true;
556
+ await writeFile(configTypesPath, source);
557
+ verbose(`Regenerated ${STANDARD_FILES.CONFIG_TYPES_TS}`);
558
+ }
559
+ function renderTypesExtendingGlobal(projectDir, provider, agents, extras) {
560
+ const selectedAgentNames = agents ? activeAgentNames(agents) : void 0;
561
+ const projectScopedAgentNames = agents ? activeProjectAgentNames(agents) : void 0;
562
+ const source = generateProjectConfigTypesSource({
563
+ globalTypesImportPath: computeGlobalTypesImportPath(projectDir, provider),
564
+ projectSkillIds: extras?.extraSkillIds ?? [],
565
+ projectAgentNames: extras?.extraAgentNames ?? [],
566
+ projectDomains: extras?.extraDomains ?? [],
567
+ projectCategories: extras?.extraCategories ?? [],
568
+ ...selectedAgentNames?.length ? { selectedAgentNames } : {},
569
+ ...projectScopedAgentNames?.length ? { projectScopedAgentNames } : {}
570
+ });
571
+ verbose("Using project config-types.ts that imports from global");
572
+ return source;
296
573
  }
297
574
 
298
- // src/cli/lib/config-gate/propagate.ts
299
- import fs from "fs";
300
- import { isDeepEqual as isDeepEqual3, unique } from "remeda";
301
-
302
575
  // ../compile/src/global-config.ts
303
- import { isDeepEqual as isDeepEqual2 } from "remeda";
576
+ import { isDeepEqual } from "remeda";
304
577
  function additiveMergeStack(existing, incoming) {
305
578
  const merged = existing ? structuredClone(existing) : {};
306
579
  if (!incoming) return { stack: merged, changed: false };
@@ -347,15 +620,15 @@ function appendMissingAssignments(existingAssignments, incomingAssignments) {
347
620
  return changed;
348
621
  }
349
622
  function mergeGlobalConfigs(existing, incoming) {
350
- const existingSkillIds = new Set(existing.skills.map((s) => s.id));
351
- const existingAgentNames = new Set(existing.agents.map((a) => a.name));
352
- const incomingActiveSkills = incoming.skills.filter((s) => !s.excluded);
353
- const incomingActiveAgents = incoming.agents.filter((a) => !a.excluded);
354
- const newSkills = incomingActiveSkills.filter(
355
- (s) => !existingSkillIds.has(s.id)
623
+ const newSkills = activeEntriesNotIn(
624
+ incoming.skills,
625
+ existing.skills,
626
+ (s) => s.id
356
627
  );
357
- const newAgents = incomingActiveAgents.filter(
358
- (a) => !existingAgentNames.has(a.name)
628
+ const newAgents = activeEntriesNotIn(
629
+ incoming.agents,
630
+ existing.agents,
631
+ (a) => a.name
359
632
  );
360
633
  const mergedSkills = [...existing.skills, ...newSkills];
361
634
  const mergedAgents = [...existing.agents, ...newAgents];
@@ -370,7 +643,7 @@ function mergeGlobalConfigs(existing, incoming) {
370
643
  ])
371
644
  ];
372
645
  const addsEntries = newSkills.length > 0 || newAgents.length > 0;
373
- const changed = addsEntries || stackChanged || !isDeepEqual2(existing.selectedDomains ?? [], mergedSelectedDomains);
646
+ const changed = addsEntries || stackChanged || !isDeepEqual(existing.selectedDomains ?? [], mergedSelectedDomains);
374
647
  return {
375
648
  config: {
376
649
  ...existing,
@@ -383,6 +656,10 @@ function mergeGlobalConfigs(existing, incoming) {
383
656
  changed
384
657
  };
385
658
  }
659
+ function activeEntriesNotIn(incoming, existing, keyOf) {
660
+ const held = new Set(existing.map(keyOf));
661
+ return incoming.filter((entry) => !entry.excluded && !held.has(keyOf(entry)));
662
+ }
386
663
  function marketplaceIdentityFilledFrom(existing, arriving) {
387
664
  const marketplaceName = existing.marketplaceName ?? arriving?.marketplaceName;
388
665
  const marketplace = existing.marketplace ?? arriving?.marketplace;
@@ -437,21 +714,18 @@ function normalizeProjectPath(projectDir) {
437
714
  async function registerProjectPath(globalConfig, projectDir) {
438
715
  const normalizedPath = normalizeProjectPath(projectDir);
439
716
  const existing = globalConfig.projects ?? [];
440
- const staleChecks = await Promise.all(
441
- existing.map(async (p) => ({
442
- path: p,
443
- hasConfig: await fileExists(getInstalledConfigPath(p))
444
- }))
445
- );
446
- const valid = staleChecks.filter((c) => c.hasConfig).map((c) => c.path);
717
+ const valid = await projectsStillInstalled(existing);
447
718
  if (valid.includes(normalizedPath)) {
448
719
  const changed = valid.length !== existing.length;
449
720
  return { config: changed ? { ...globalConfig, projects: valid } : globalConfig, changed };
450
721
  }
451
722
  return { config: { ...globalConfig, projects: [...valid, normalizedPath] }, changed: true };
452
723
  }
453
- function isProjectScopedEntry(entry) {
454
- return entry.scope === "project";
724
+ async function projectsStillInstalled(projects) {
725
+ const installed = await Promise.all(
726
+ projects.map((project) => fileExists(getInstalledConfigPath(project)))
727
+ );
728
+ return projects.filter((_, index) => installed[index] === true);
455
729
  }
456
730
  function globalHasActiveSkill(globalConfig, id) {
457
731
  return globalConfig.skills.some((s) => s.id === id && isActiveAt(s, "global"));
@@ -461,12 +735,12 @@ function globalHasActiveAgent(globalConfig, name) {
461
735
  }
462
736
  function retainProjectOwnedSkills(skills, globalConfig) {
463
737
  return skills.filter(
464
- (entry) => isProjectScopedEntry(entry) || isGlobalTombstone(entry) && globalHasActiveSkill(globalConfig, entry.id)
738
+ (entry) => entry.scope === "project" || isGlobalTombstone(entry) && globalHasActiveSkill(globalConfig, entry.id)
465
739
  );
466
740
  }
467
741
  function retainProjectOwnedAgents(agents, globalConfig) {
468
742
  return agents.filter(
469
- (entry) => isProjectScopedEntry(entry) || isGlobalTombstone(entry) && globalHasActiveAgent(globalConfig, entry.name)
743
+ (entry) => entry.scope === "project" || isGlobalTombstone(entry) && globalHasActiveAgent(globalConfig, entry.name)
470
744
  );
471
745
  }
472
746
  function categoryOfSkill(id, matrix2) {
@@ -474,7 +748,7 @@ function categoryOfSkill(id, matrix2) {
474
748
  if (category === void 0 || category === LOCAL_PSEUDO_CATEGORY) return void 0;
475
749
  return category;
476
750
  }
477
- function isExclusiveCategory(category, matrix2) {
751
+ function isExclusiveCategory2(category, matrix2) {
478
752
  return matrix2.categories[category]?.exclusive === true;
479
753
  }
480
754
  function activeProjectCategories(projectOwnedSkills, matrix2) {
@@ -488,7 +762,7 @@ function buildProjectCollisionTest(projectOwnedSkills, matrix2) {
488
762
  );
489
763
  const occupiedExclusiveCategories = new Set(
490
764
  [...activeProjectCategories(projectOwnedSkills, matrix2)].filter(
491
- (category) => isExclusiveCategory(category, matrix2)
765
+ (category) => isExclusiveCategory2(category, matrix2)
492
766
  )
493
767
  );
494
768
  return (id) => {
@@ -543,7 +817,7 @@ function computeRemovedGlobalSkillIds(priorProjectSkills, globalConfig) {
543
817
  );
544
818
  return new Set(
545
819
  priorProjectSkills.filter(
546
- (s) => s.scope === "global" && !s.excluded && !activeGlobalIds.has(s.id) && !projectOwnedIds.has(s.id)
820
+ (s) => isActiveAt(s, "global") && !activeGlobalIds.has(s.id) && !projectOwnedIds.has(s.id)
547
821
  ).map((s) => s.id)
548
822
  );
549
823
  }
@@ -585,7 +859,7 @@ function inlinedProjectView(reconciledSplit, effectiveGlobal) {
585
859
  ...reconciledSplit,
586
860
  skills: [...effectiveGlobal.skills, ...reconciledSplit.skills],
587
861
  agents: [...effectiveGlobal.agents, ...reconciledSplit.agents],
588
- selectedDomains: unique([
862
+ selectedDomains: unique2([
589
863
  ...effectiveGlobal.selectedDomains ?? [],
590
864
  ...reconciledSplit.selectedDomains ?? []
591
865
  ]),
@@ -678,6 +952,23 @@ async function propagateToRegisteredProject(projectPath, provider, globalConfig,
678
952
  return error instanceof ConfigLoadError ? "unreadable" : "failed";
679
953
  }
680
954
  }
955
+ async function projectsAFanOutReaches(projects, provider) {
956
+ const reached = await Promise.all(
957
+ projects.map(
958
+ async (projectPath) => await wouldBeRewritten(projectPath, provider) ? [projectPath] : []
959
+ )
960
+ );
961
+ return reached.flat();
962
+ }
963
+ async function wouldBeRewritten(projectPath, provider) {
964
+ if (!await holdsAnInstallation(projectPath)) return false;
965
+ if (await whyThisProjectIsNotOurs(projectPath, provider) !== null) return false;
966
+ try {
967
+ return await loadInstalledConfig(projectPath) !== null;
968
+ } catch {
969
+ return false;
970
+ }
971
+ }
681
972
  async function holdsAnInstallation(projectPath) {
682
973
  const configs = await Promise.all(
683
974
  PROVIDERS.map((installed) => fileExists(getProjectConfigPath(projectPath, installed)))
@@ -713,7 +1004,7 @@ async function resolveEffectiveGlobalConfig(globalSplit, existingGlobalConfig, p
713
1004
  function matchGlobalToSession(globalSplit, existingGlobalConfig) {
714
1005
  if (!existingGlobalConfig) return { config: globalSplit, changed: true };
715
1006
  const config = mergeConfigs(globalSplit, existingGlobalConfig, { authoritativeScope: "all" });
716
- return { config, changed: !isDeepEqual3(config, existingGlobalConfig) };
1007
+ return { config, changed: !isDeepEqual2(config, existingGlobalConfig) };
717
1008
  }
718
1009
  function stackCategories(stack) {
719
1010
  return Object.values(stack ?? {}).flatMap((agentStack) => typedKeys(agentStack ?? {}));
@@ -721,19 +1012,197 @@ function stackCategories(stack) {
721
1012
  function buildProjectTypesExtras(finalConfig, matrix2) {
722
1013
  const activeSkills = finalConfig.skills.filter((s) => !s.excluded);
723
1014
  const activeAgents = finalConfig.agents.filter((a) => !a.excluded);
724
- const extraSkillIds = unique(activeSkills.map((s) => s.id));
725
- const extraAgentNames = unique(activeAgents.map((a) => a.name));
726
- const extraCategories = unique([
1015
+ const extraSkillIds = unique2(activeSkills.map((s) => s.id));
1016
+ const extraAgentNames = unique2(activeAgents.map((a) => a.name));
1017
+ const extraCategories = unique2([
727
1018
  ...deriveCategories(extraSkillIds, matrix2),
728
1019
  ...stackCategories(finalConfig.stack)
729
1020
  ]);
730
- const extraDomains = unique([
1021
+ const extraDomains = unique2([
731
1022
  ...deriveDomains(extraCategories, matrix2),
732
1023
  ...finalConfig.selectedDomains ?? []
733
1024
  ]);
734
1025
  return { extraSkillIds, extraAgentNames, extraDomains, extraCategories };
735
1026
  }
736
1027
 
1028
+ // src/cli/lib/config-gate/index.ts
1029
+ import os2 from "os";
1030
+ import path3 from "path";
1031
+
1032
+ // src/cli/lib/config-gate/classify.ts
1033
+ import { isDeepEqual as isDeepEqual3 } from "remeda";
1034
+ var NO_CHANGES = {
1035
+ skills: { added: [], removed: [], sourceChanged: [], otherChanged: [] },
1036
+ agents: { added: [], removed: [], changed: [] },
1037
+ stackChanged: false,
1038
+ selectedDomainsChanged: false,
1039
+ scalarsChanged: [],
1040
+ projectsChanged: false
1041
+ };
1042
+ var EXTRACTED_FIELDS = /* @__PURE__ */ new Set(["skills", "agents", "stack", "selectedDomains"]);
1043
+ var REGISTRATION_FIELD = "projects";
1044
+ var EMPTY_MEANS_ABSENT = /* @__PURE__ */ new Set(["stack", "selectedDomains"]);
1045
+ function isEmptyCollection(value) {
1046
+ return Array.isArray(value) ? value.length === 0 : isRecord(value) && Object.keys(value).length === 0;
1047
+ }
1048
+ function normalize(config) {
1049
+ const record = JSON.parse(JSON.stringify(config));
1050
+ return Object.fromEntries(
1051
+ Object.entries(record).filter(
1052
+ ([key, value]) => !(EMPTY_MEANS_ABSENT.has(key) && isEmptyCollection(value))
1053
+ )
1054
+ );
1055
+ }
1056
+ function skillsById(config) {
1057
+ const skills = config.skills ?? [];
1058
+ return new Map(skills.map((skill) => [skill.id, skill]));
1059
+ }
1060
+ function agentsByName(config) {
1061
+ const agents = config.agents ?? [];
1062
+ return new Map(agents.map((agent) => [agent.name, agent]));
1063
+ }
1064
+ function scalarKeys(prev, next) {
1065
+ const isScalarKey = (key) => !EXTRACTED_FIELDS.has(key) && key !== REGISTRATION_FIELD;
1066
+ const nextKeys = Object.keys(next).filter(isScalarKey);
1067
+ const extraPrevKeys = Object.keys(prev).filter(
1068
+ (key) => isScalarKey(key) && !nextKeys.includes(key)
1069
+ );
1070
+ return [...nextKeys, ...extraPrevKeys];
1071
+ }
1072
+ function diffSkills(prev, next) {
1073
+ const before = skillsById(prev);
1074
+ const after = skillsById(next);
1075
+ const added = [...after.keys()].filter((id) => !before.has(id));
1076
+ const removed = [...before.keys()].filter((id) => !after.has(id));
1077
+ const sourceChanged = [];
1078
+ const otherChanged = [];
1079
+ for (const [id, afterEntry] of after) {
1080
+ const beforeEntry = before.get(id);
1081
+ if (!beforeEntry || isDeepEqual3(beforeEntry, afterEntry)) continue;
1082
+ if (beforeEntry.origin !== afterEntry.origin) sourceChanged.push(id);
1083
+ else otherChanged.push(id);
1084
+ }
1085
+ return { added, removed, sourceChanged, otherChanged };
1086
+ }
1087
+ function diffAgents(prev, next) {
1088
+ const before = agentsByName(prev);
1089
+ const after = agentsByName(next);
1090
+ return {
1091
+ added: [...after.keys()].filter((name) => !before.has(name)),
1092
+ removed: [...before.keys()].filter((name) => !after.has(name)),
1093
+ changed: [...after.keys()].filter((name) => {
1094
+ const beforeEntry = before.get(name);
1095
+ return beforeEntry !== void 0 && !isDeepEqual3(beforeEntry, after.get(name));
1096
+ })
1097
+ };
1098
+ }
1099
+ function classifyGlobalChange(prev, next) {
1100
+ const before = prev ? normalize(prev) : {};
1101
+ const after = normalize(next);
1102
+ const moved = (key) => !isDeepEqual3(before[key], after[key]);
1103
+ return {
1104
+ skills: diffSkills(before, after),
1105
+ agents: diffAgents(before, after),
1106
+ stackChanged: moved("stack"),
1107
+ selectedDomainsChanged: moved("selectedDomains"),
1108
+ scalarsChanged: scalarKeys(before, after).filter(moved),
1109
+ projectsChanged: moved(REGISTRATION_FIELD)
1110
+ };
1111
+ }
1112
+ function movesTypesOrCompiledAgents(changes) {
1113
+ const { skills, agents } = changes;
1114
+ return skills.added.length > 0 || skills.removed.length > 0 || skills.sourceChanged.length > 0 || skills.otherChanged.length > 0 || agents.added.length > 0 || agents.removed.length > 0 || agents.changed.length > 0 || changes.stackChanged || changes.selectedDomainsChanged;
1115
+ }
1116
+ function consequenceTier(changes) {
1117
+ if (movesTypesOrCompiledAgents(changes)) return "T1";
1118
+ if (changes.scalarsChanged.length > 0) return "T2";
1119
+ if (changes.projectsChanged) return "T3";
1120
+ return "T4";
1121
+ }
1122
+ function tierNeedsDeps(tier) {
1123
+ return tier === "T1" || tier === "T2";
1124
+ }
1125
+ function tierRegeneratesTypes(tier) {
1126
+ return tier === "T1";
1127
+ }
1128
+ function tierPropagates(tier) {
1129
+ return tier === "T1" || tier === "T2";
1130
+ }
1131
+
1132
+ // src/cli/lib/config-gate/deps.ts
1133
+ function isLoaded(deps) {
1134
+ return "matrix" in deps;
1135
+ }
1136
+ async function resolveGateDeps(deps, tier) {
1137
+ if (!tierNeedsDeps(tier)) return null;
1138
+ if (isLoaded(deps)) return deps;
1139
+ const [matrix2, agents] = await Promise.all([
1140
+ deps.loadMatrix(),
1141
+ tierRegeneratesTypes(tier) ? deps.loadAgents() : NO_AGENTS
1142
+ ]);
1143
+ return { matrix: matrix2, agents };
1144
+ }
1145
+ var NO_AGENTS = Promise.resolve({});
1146
+
1147
+ // src/cli/lib/config-gate/pair-writer.ts
1148
+ import os from "os";
1149
+ import path2 from "path";
1150
+ function globalPairPaths(provider) {
1151
+ const dir = sourceFolderInUse(os.homedir(), provider).dir;
1152
+ return {
1153
+ configPath: path2.join(dir, STANDARD_FILES.CONFIG_TS),
1154
+ typesPath: path2.join(dir, STANDARD_FILES.CONFIG_TYPES_TS)
1155
+ };
1156
+ }
1157
+ function typesPathFor(configPath) {
1158
+ return path2.join(path2.dirname(configPath), STANDARD_FILES.CONFIG_TYPES_TS);
1159
+ }
1160
+ async function writeIfChanged(filePath, source) {
1161
+ assertGateToken(filePath);
1162
+ if (await fileExists(filePath) && await readFile(filePath) === source) return false;
1163
+ await writeFile(filePath, source);
1164
+ return true;
1165
+ }
1166
+ function renderStandaloneTypes(config, matrix2, agents, extras) {
1167
+ const data = buildConfigTypesBackgroundData(matrix2, agents);
1168
+ return generateConfigTypesSource(
1169
+ data.matrix,
1170
+ data.agentNames,
1171
+ data.customAgentNames,
1172
+ extras,
1173
+ config
1174
+ );
1175
+ }
1176
+ async function writeGlobalConfigHalf(config, configPath) {
1177
+ return writeIfChanged(configPath, generateConfigSource(config, matrix));
1178
+ }
1179
+ async function writeGlobalTypesHalf(config, configPath, matrix2, agents, extras) {
1180
+ return writeIfChanged(
1181
+ typesPathFor(configPath),
1182
+ renderStandaloneTypes(config, matrix2, agents, extras)
1183
+ );
1184
+ }
1185
+ async function writeGlobalPair(config, configPath, matrix2, agents) {
1186
+ const configWritten = await writeIfChanged(configPath, generateConfigSource(config, matrix2));
1187
+ const typesWritten = await writeIfChanged(
1188
+ typesPathFor(configPath),
1189
+ renderStandaloneTypes(config, matrix2, agents)
1190
+ );
1191
+ return configWritten || typesWritten;
1192
+ }
1193
+ async function ensureBlankPair(provider) {
1194
+ const { configPath, typesPath } = globalPairPaths(provider);
1195
+ if (await fileExists(configPath)) {
1196
+ verbose("Global config already exists, skipping blank creation");
1197
+ return false;
1198
+ }
1199
+ await ensureDir(path2.dirname(configPath));
1200
+ await writeIfChanged(configPath, generateBlankGlobalConfigSource());
1201
+ await writeIfChanged(typesPath, generateBlankGlobalConfigTypesSource());
1202
+ verbose(`Created blank global config at ${path2.dirname(configPath)}`);
1203
+ return true;
1204
+ }
1205
+
737
1206
  // src/cli/lib/config-gate/recompile.ts
738
1207
  var NOTHING_RECOMPILED = {
739
1208
  rewrittenCount: 0,
@@ -741,10 +1210,55 @@ var NOTHING_RECOMPILED = {
741
1210
  failedCount: 0,
742
1211
  warnings: []
743
1212
  };
744
- async function recompilePropagated(projectDirs) {
1213
+ async function recompilePropagated(projectDirs, pairsRewritten) {
745
1214
  if (projectDirs.length === 0) return NOTHING_RECOMPILED;
746
- const { recompilePropagatedProjectAgents: recompilePropagatedProjectAgents2 } = await import("./recompile-project-agents-SZ76WNTW.js");
747
- return recompilePropagatedProjectAgents2(projectDirs);
1215
+ const { recompilePropagatedProjectAgents } = await import("./recompile-project-agents-I33KQJFB.js");
1216
+ return recompilePropagatedProjectAgents(projectDirs, pairsRewritten);
1217
+ }
1218
+ function pairRewriteSummary(projectDirs, pairsRewritten) {
1219
+ const rewrittenCount = projectDirs.filter((dir) => pairsRewritten.has(dir)).length;
1220
+ return {
1221
+ rewrittenCount,
1222
+ unchangedCount: projectDirs.length - rewrittenCount,
1223
+ failedCount: 0,
1224
+ warnings: []
1225
+ };
1226
+ }
1227
+
1228
+ // src/cli/lib/config-gate/pair-snapshot.ts
1229
+ async function readHalf(filePath) {
1230
+ try {
1231
+ return await fileExists(filePath) ? await readFile(filePath) : null;
1232
+ } catch {
1233
+ return Symbol(filePath);
1234
+ }
1235
+ }
1236
+ async function readPair(projectDir, provider) {
1237
+ const configPath = getProjectConfigPath(projectDir, provider);
1238
+ return Promise.all([readHalf(configPath), readHalf(typesPathFor(configPath))]);
1239
+ }
1240
+ async function snapshotProjectPairs(projectDirs, provider) {
1241
+ const pairs = await Promise.all(
1242
+ projectDirs.map(async (dir) => [dir, await readPair(dir, provider)])
1243
+ );
1244
+ return new Map(pairs);
1245
+ }
1246
+ async function pairsRewrittenSince(before, projectDirs, provider) {
1247
+ const judged = await Promise.all(
1248
+ projectDirs.map(async (dir) => ({
1249
+ dir,
1250
+ rewritten: !samePair(snapshotOf(before, dir), await readPair(dir, provider))
1251
+ }))
1252
+ );
1253
+ return new Set(judged.filter(({ rewritten }) => rewritten).map(({ dir }) => dir));
1254
+ }
1255
+ function snapshotOf(before, dir) {
1256
+ const pair = before.get(dir);
1257
+ if (pair === void 0) throw new Error(`No snapshot of ${dir} was taken before the fan-out`);
1258
+ return pair;
1259
+ }
1260
+ function samePair([config, types], [configNow, typesNow]) {
1261
+ return config === configNow && types === typesNow;
748
1262
  }
749
1263
 
750
1264
  // src/cli/lib/config-gate/index.ts
@@ -761,28 +1275,40 @@ function movedGlobalAgentInputs(written) {
761
1275
  function report(globalWritten, changes, propagated = NOTHING_PROPAGATED, recompile = NOTHING_RECOMPILED) {
762
1276
  return { globalWritten, changes, propagated, recompile };
763
1277
  }
1278
+ async function fanOut(projects, provider, propagate, recompiles) {
1279
+ const pairsBefore = await snapshotProjectPairs(projects, provider);
1280
+ const propagated = await propagate();
1281
+ const pairsRewritten = await pairsRewrittenSince(pairsBefore, propagated.updated, provider);
1282
+ const recompile = recompiles ? await recompilePropagated(propagated.updated, pairsRewritten) : pairRewriteSummary(propagated.updated, pairsRewritten);
1283
+ return { propagated, recompile };
1284
+ }
1285
+ function sayPropagated(propagated) {
1286
+ if (propagated.updated.length > 0) {
1287
+ verbose(`Propagated global changes to ${propagated.updated.length} project(s)`);
1288
+ }
1289
+ }
764
1290
  async function applyConsequences(globalConfig, changes, deps, provider, currentProjectDir) {
765
1291
  const tier = consequenceTier(changes);
766
- if (!tierPropagates(tier) || !globalConfig.projects?.length) {
1292
+ const projects = globalConfig.projects ?? [];
1293
+ if (!tierPropagates(tier) || projects.length === 0) {
767
1294
  return { propagated: NOTHING_PROPAGATED, recompile: NOTHING_RECOMPILED };
768
1295
  }
769
- const propagated = await propagateGlobalChangesToProjects(
770
- globalConfig,
771
- deps.agents,
1296
+ const regenerateTypes = tierRegeneratesTypes(tier);
1297
+ const reached = await fanOut(
1298
+ projects,
772
1299
  provider,
773
- currentProjectDir,
774
- { regenerateTypes: tierRegeneratesTypes(tier) }
1300
+ () => propagateGlobalChangesToProjects(globalConfig, deps.agents, provider, currentProjectDir, {
1301
+ regenerateTypes
1302
+ }),
1303
+ regenerateTypes
775
1304
  );
776
- if (propagated.updated.length > 0) {
777
- verbose(`Propagated global changes to ${propagated.updated.length} project(s)`);
778
- }
779
- const recompile = tierRegeneratesTypes(tier) ? await recompilePropagated(propagated.updated) : NOTHING_RECOMPILED;
780
- return { propagated, recompile };
1305
+ sayPropagated(reached.propagated);
1306
+ return reached;
781
1307
  }
782
1308
  function lazyGateDeps(projectDir) {
783
1309
  return {
784
1310
  loadMatrix: async () => {
785
- const { loadSkillsMatrixFromSource: loadSkillsMatrixFromSource2 } = await import("./source-loader-C5SQXZXI.js");
1311
+ const { loadSkillsMatrixFromSource: loadSkillsMatrixFromSource2 } = await import("./source-loader-TTJVW2ER.js");
786
1312
  const { matrix: matrix2 } = await loadSkillsMatrixFromSource2({
787
1313
  projectDir,
788
1314
  skipExtraSources: true,
@@ -791,27 +1317,33 @@ function lazyGateDeps(projectDir) {
791
1317
  return matrix2;
792
1318
  },
793
1319
  loadAgents: async () => {
794
- const { loadAgentDefs: loadAgentDefs2 } = await import("./load-agent-defs-YMDWBFDP.js");
1320
+ const { loadAgentDefs: loadAgentDefs2 } = await import("./load-agent-defs-6QUUMGHK.js");
795
1321
  return (await loadAgentDefs2()).agents;
796
1322
  }
797
1323
  };
798
1324
  }
799
1325
  async function writeConfigByScope(args) {
800
1326
  return withGateToken(async () => {
801
- const { finalConfig, matrix: matrix2, agents, projectDir, projectConfigPath } = args;
1327
+ const { matrix: matrix2, agents, projectDir } = args;
802
1328
  const homeDir = os2.homedir();
803
1329
  const deps = { matrix: matrix2, agents };
804
- if (!isHomeDirectory(projectDir)) {
805
- return writeFromProjectContext(args, homeDir, deps);
806
- }
807
- const provider = providerInUse(homeDir);
808
- const priorGlobal = await loadProjectConfigFromDir(homeDir, provider);
809
- const changes = classifyGlobalChange(priorGlobal?.config, finalConfig);
810
- const globalWritten = await writeGlobalPair(finalConfig, projectConfigPath, matrix2, agents);
811
- const { propagated, recompile } = await applyConsequences(finalConfig, changes, deps, provider);
812
- return report(globalWritten, changes, propagated, recompile);
1330
+ return isHomeDirectory(projectDir) ? writeFromHome(args, homeDir, deps) : writeFromProjectContext(args, homeDir, deps);
813
1331
  });
814
1332
  }
1333
+ async function writeFromHome(args, homeDir, deps) {
1334
+ const { finalConfig, projectConfigPath } = args;
1335
+ const provider = providerInUse(homeDir);
1336
+ const priorGlobal = await loadProjectConfigFromDir(homeDir, provider);
1337
+ const changes = classifyGlobalChange(priorGlobal?.config, finalConfig);
1338
+ const globalWritten = await writeGlobalPair(
1339
+ finalConfig,
1340
+ projectConfigPath,
1341
+ deps.matrix,
1342
+ deps.agents
1343
+ );
1344
+ const { propagated, recompile } = await applyConsequences(finalConfig, changes, deps, provider);
1345
+ return report(globalWritten, changes, propagated, recompile);
1346
+ }
815
1347
  async function writeFromProjectContext(args, homeDir, deps) {
816
1348
  const { finalConfig, projectDir } = args;
817
1349
  const { global: globalConfig, project: projectSplitConfig } = splitConfigByScope(finalConfig);
@@ -853,7 +1385,7 @@ async function writeGlobalPairWhenChanged(effective, globalConfigPath, deps) {
853
1385
  verbose("Global config unchanged, skipping write");
854
1386
  return false;
855
1387
  }
856
- await ensureDir(path2.dirname(globalConfigPath));
1388
+ await ensureDir(path3.dirname(globalConfigPath));
857
1389
  const written = await writeGlobalPair(
858
1390
  effective.config,
859
1391
  globalConfigPath,
@@ -870,7 +1402,7 @@ async function writeProjectPairWhenOwed(project, provider, reconciledProjectConf
870
1402
  );
871
1403
  return;
872
1404
  }
873
- await ensureDir(path2.dirname(project.projectConfigPath));
1405
+ await ensureDir(path3.dirname(project.projectConfigPath));
874
1406
  await writeProjectConfigPair(
875
1407
  project.projectDir,
876
1408
  provider,
@@ -909,16 +1441,14 @@ async function reconcileTypesFromDisk(projectDir, config, deps, options) {
909
1441
  const globalWritten = await writeScopeConfigTypes(projectDir, config, deps, options?.extras);
910
1442
  if (!isHomeDirectory(projectDir)) return report(false, changes);
911
1443
  if (!config.projects?.length) return report(globalWritten, changes);
912
- const propagated = await propagateGlobalChangesToProjects(
913
- config,
914
- deps.agents,
915
- providerInUse(projectDir),
916
- options?.currentProjectDir
1444
+ const provider = providerInUse(projectDir);
1445
+ const { propagated, recompile } = await fanOut(
1446
+ config.projects,
1447
+ provider,
1448
+ () => propagateGlobalChangesToProjects(config, deps.agents, provider, options?.currentProjectDir),
1449
+ true
917
1450
  );
918
- if (propagated.updated.length > 0) {
919
- verbose(`Propagated global changes to ${propagated.updated.length} project(s)`);
920
- }
921
- const recompile = await recompilePropagated(propagated.updated);
1451
+ sayPropagated(propagated);
922
1452
  return report(globalWritten, changes, propagated, recompile);
923
1453
  });
924
1454
  }
@@ -981,12 +1511,12 @@ async function propagateGlobalRemoval(preRemovalGlobalConfig, deps, provider) {
981
1511
  skills: [],
982
1512
  agents: []
983
1513
  });
984
- const propagated = await pruneGlobalEntriesFromRegisteredProjects(
985
- preRemovalGlobalConfig,
986
- deps.agents,
987
- provider
1514
+ const { propagated, recompile } = await fanOut(
1515
+ preRemovalGlobalConfig.projects ?? [],
1516
+ provider,
1517
+ () => pruneGlobalEntriesFromRegisteredProjects(preRemovalGlobalConfig, deps.agents, provider),
1518
+ true
988
1519
  );
989
- const recompile = await recompilePropagated(propagated.updated);
990
1520
  return report(false, changes, propagated, recompile);
991
1521
  });
992
1522
  }
@@ -1002,7 +1532,7 @@ async function writeProjectPartial(projectDir, partial, options = {}) {
1002
1532
  throw new GlobalPairWriteViolation(getInstalledConfigPath(projectDir));
1003
1533
  }
1004
1534
  const configPath = getInstalledConfigPath(projectDir);
1005
- await ensureDir(path2.dirname(configPath));
1535
+ await ensureDir(path3.dirname(configPath));
1006
1536
  await writeFile(
1007
1537
  configPath,
1008
1538
  generateConfigSource(
@@ -1013,11 +1543,11 @@ async function writeProjectPartial(projectDir, partial, options = {}) {
1013
1543
  }
1014
1544
 
1015
1545
  // src/cli/lib/operations/project/write-project-config.ts
1016
- import path3 from "path";
1546
+ import path4 from "path";
1017
1547
  async function writeProjectConfig(options) {
1018
1548
  const { selection, sourceResult, projectDir, sourceFlag } = options;
1019
1549
  const projectPaths = resolveInstallPaths(projectDir, "project");
1020
- await ensureDir(path3.dirname(projectPaths.configPath));
1550
+ await ensureDir(path4.dirname(projectPaths.configPath));
1021
1551
  const { agents } = options.agentDefs ?? await loadAgentDefs();
1022
1552
  const mergeResult = await buildAndMergeConfig(
1023
1553
  selection,
@@ -1038,9 +1568,7 @@ async function writeProjectConfig(options) {
1038
1568
  projectDir,
1039
1569
  projectConfigPath: projectPaths.configPath,
1040
1570
  projectInstallationExists: isProjectContext,
1041
- // The same word the merger above took. It decides the config ROW there and the GLOBAL
1042
- // config a project write commits here, and the two files disagree the moment it does not
1043
- // reach both: a row preserved for a skill whose directory the removal diff deleted.
1571
+ // Must match the merger's, or the global config and the project rows disagree.
1044
1572
  ...options.authoritativeScope !== void 0 && {
1045
1573
  authoritativeScope: options.authoritativeScope
1046
1574
  }
@@ -1102,6 +1630,7 @@ export {
1102
1630
  sayCapturedWarnings,
1103
1631
  detectProject,
1104
1632
  detectBothInstallations,
1633
+ projectsAFanOutReaches,
1105
1634
  movedGlobalAgentInputs,
1106
1635
  lazyGateDeps,
1107
1636
  reconcileTypesFromDisk,
@@ -1113,4 +1642,4 @@ export {
1113
1642
  compileAgentsAllScopes,
1114
1643
  compileProjectScope
1115
1644
  };
1116
- //# sourceMappingURL=chunk-7RDMTR64.js.map
1645
+ //# sourceMappingURL=chunk-66AEVERM.js.map