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
@@ -0,0 +1,1169 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ SHARED_CONFIG_APPLY,
4
+ SHARED_CONFIG_ARRIVALS,
5
+ STATUS_MESSAGES,
6
+ addedToGlobalSubAgents,
7
+ buildMarketplacePluginRef,
8
+ claimSlug,
9
+ completedWithFailures,
10
+ copy,
11
+ defaultUsageGuidance,
12
+ directoryExists,
13
+ fetchMarketplace,
14
+ fileExists,
15
+ findConfigLoadFailures,
16
+ findUnusableSavedSkillMetadata,
17
+ getCurrentDate,
18
+ getErrorMessage,
19
+ getStackSkillIds,
20
+ glob,
21
+ hostAt,
22
+ installationConfigsUnreadable,
23
+ isHomeDirectory,
24
+ isLocalOnlySkill,
25
+ loadProjectConfigFromDir,
26
+ parseFrontmatter,
27
+ pluginsInstalled,
28
+ propagatedRecompileSummary,
29
+ providerInUse,
30
+ readFile,
31
+ readForkedFromMetadata,
32
+ readSkillMetadata,
33
+ refuseUnofferedPlacements,
34
+ refuseUnofferedStack,
35
+ refuseUnwritableStack,
36
+ registeredProjectConfigUnreadable,
37
+ registeredProjectGone,
38
+ registeredProjectOnAnotherProvider,
39
+ remove,
40
+ resolveBranding,
41
+ resolveInstallPaths,
42
+ savedSkillMetadataUnusableError,
43
+ scopeBlockedStackAssignment,
44
+ sharedConfigProjectScopeAtHome,
45
+ skillAssignedToNoAgent,
46
+ skillMetadataUnusableDetail,
47
+ skippedUnknownAgents,
48
+ skippedUnknownSkills,
49
+ typedEntries,
50
+ typedKeys,
51
+ typedValues,
52
+ validateSkillPath,
53
+ verbose,
54
+ warn,
55
+ writeFile
56
+ } from "./chunk-GAKVZNTX.js";
57
+ import {
58
+ BUILT_IN_MATRIX,
59
+ getCategoryDomain,
60
+ getSkillDisplayName,
61
+ matrix,
62
+ stripTerminalControls
63
+ } from "./chunk-AUNRHWH4.js";
64
+ import {
65
+ CLI_COLORS,
66
+ CLI_INVOKE_COMMAND,
67
+ DEFAULT_BRANDING,
68
+ EJECT_SOURCE,
69
+ GITHUB_PREFIX,
70
+ GITHUB_SOURCE,
71
+ LOCAL_SKILLS_PATH,
72
+ MIN_TERMINAL_SIZE,
73
+ STANDARD_FILES,
74
+ externalIdsNamingNoDirectory,
75
+ isActiveAt,
76
+ orderDomains,
77
+ seedExternalSkillSchema
78
+ } from "./chunk-7C3PFRBU.js";
79
+
80
+ // src/cli/utils/terminal.ts
81
+ function clearTerminalScreen() {
82
+ process.stdout.write("\x1B[H\x1B[2J\x1B[3J");
83
+ }
84
+ function isTerminalLargeEnough(columns, rows) {
85
+ return columns >= MIN_TERMINAL_SIZE.COLS && rows >= MIN_TERMINAL_SIZE.ROWS;
86
+ }
87
+ function formatTerminalTooSmallMessage(columns) {
88
+ const issue = columns < MIN_TERMINAL_SIZE.COLS ? `too narrow (need ${MIN_TERMINAL_SIZE.COLS})` : `too short (need ${MIN_TERMINAL_SIZE.ROWS})`;
89
+ return `Terminal ${issue}. Please resize.`;
90
+ }
91
+
92
+ // src/cli/lib/exit-codes.ts
93
+ var EXIT_CODES = {
94
+ SUCCESS: 0,
95
+ ERROR: 1,
96
+ INVALID_ARGS: 2,
97
+ NETWORK_ERROR: 3,
98
+ CANCELLED: 4,
99
+ /**
100
+ * Ran to the end but part of the work failed. Unlike ERROR the work landed, so repeating the run
101
+ * is wrong; the output names the remedy. Raised at the end of a command, never mid-flight.
102
+ */
103
+ COMPLETED_WITH_FAILURES: 5
104
+ };
105
+
106
+ // src/cli/lib/seed/external-skills.ts
107
+ import os from "os";
108
+ import path from "path";
109
+ import { mkdtemp } from "fs/promises";
110
+
111
+ // ../compile/src/carried-skill.ts
112
+ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
113
+ var FRONTMATTER_BLOCK = /^---\r?\n([\s\S]*?)\r?\n---/;
114
+ var NAME_FIELD = /^name:.*$/m;
115
+ function withInstalledName(manifest, { id, description }) {
116
+ const named = `name: ${id}`;
117
+ const block = FRONTMATTER_BLOCK.exec(manifest);
118
+ if (!block) {
119
+ return `---
120
+ ${named}
121
+ description: ${description}
122
+ ---
123
+
124
+ ${manifest}`;
125
+ }
126
+ const [whole, fields = ""] = block;
127
+ const renamed = NAME_FIELD.test(fields) ? fields.replace(NAME_FIELD, named) : `${named}
128
+ ${fields}`;
129
+ return `---
130
+ ${renamed}
131
+ ---${manifest.slice(whole.length)}`;
132
+ }
133
+ function carriedSkillAuthor(repo) {
134
+ const [owner = repo] = repo.split("/");
135
+ return `@${owner}`;
136
+ }
137
+ function carriedSkillMetadata({
138
+ id,
139
+ skill,
140
+ domain,
141
+ usageGuidance,
142
+ shipped
143
+ }) {
144
+ return {
145
+ author: carriedSkillAuthor(skill.repo),
146
+ usageGuidance,
147
+ ...shipped,
148
+ displayName: skill.displayName,
149
+ slug: id,
150
+ category: skill.categoryId,
151
+ domain,
152
+ cliDescription: shortLabel(skill.description),
153
+ custom: true
154
+ };
155
+ }
156
+ var CLI_DESCRIPTION_MAX_LENGTH = 60;
157
+ var ELLIPSIS = "\u2026";
158
+ function shortLabel(description) {
159
+ if (description.length <= CLI_DESCRIPTION_MAX_LENGTH) return description;
160
+ const roomLength = CLI_DESCRIPTION_MAX_LENGTH - ELLIPSIS.length;
161
+ const room = description.slice(0, roomLength);
162
+ const endsOnAWord = description[roomLength] === " ";
163
+ return `${endsOnAWord ? room.trimEnd() : toLastWholeWord(room)}${ELLIPSIS}`;
164
+ }
165
+ function toLastWholeWord(text) {
166
+ const lastBreak = text.lastIndexOf(" ");
167
+ return (lastBreak > 0 ? text.slice(0, lastBreak) : text).trimEnd();
168
+ }
169
+ async function installedSkillTree(install) {
170
+ const manifest = withInstalledName(manifestOf(install.skill.files), {
171
+ id: install.id,
172
+ description: install.skill.description
173
+ });
174
+ return {
175
+ ...install.skill.files,
176
+ [STANDARD_FILES.SKILL_MD]: manifest,
177
+ [STANDARD_FILES.METADATA_YAML]: await metadataYaml(install, manifest)
178
+ };
179
+ }
180
+ var METADATA_SCHEMA_COMMENT = "# yaml-language-server: $schema=https://raw.githubusercontent.com/agents-inc/agents-inc/main/packages/cli/src/schemas/metadata.schema.json";
181
+ var NO_LINE_FOLDING = 0;
182
+ async function metadataYaml(install, manifest) {
183
+ const metadata = carriedSkillMetadata({
184
+ ...install,
185
+ shipped: shippedMetadata(install.skill.files)
186
+ });
187
+ const stamp = await provenanceStamp(install, manifest);
188
+ return asMetadataFile(withForkedFrom(metadata, stamp));
189
+ }
190
+ async function provenanceStamp(install, manifest) {
191
+ return {
192
+ skillId: install.id,
193
+ contentHash: await contentHashOf(manifest),
194
+ date: install.date,
195
+ source: `${GITHUB_PREFIX}${install.skill.repo}`,
196
+ path: install.skill.path
197
+ };
198
+ }
199
+ var asMetadataFile = (metadata) => `${METADATA_SCHEMA_COMMENT}
200
+ ${stringifyYaml(metadata, {
201
+ lineWidth: NO_LINE_FOLDING
202
+ })}`;
203
+ function withForkedFrom(metadata, forkedFrom) {
204
+ const { forkedFrom: _replaced, ...rest } = metadata;
205
+ return { ...rest, forkedFrom };
206
+ }
207
+ function shippedMetadata(files) {
208
+ const shipped = files[STANDARD_FILES.METADATA_YAML];
209
+ if (shipped === void 0) return null;
210
+ const parsed = parsedYaml(shipped);
211
+ return isMapping(parsed) && hasReadableProvenance(parsed) ? parsed : null;
212
+ }
213
+ function parsedYaml(text) {
214
+ try {
215
+ return parseYaml(text);
216
+ } catch {
217
+ return void 0;
218
+ }
219
+ }
220
+ var isMapping = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
221
+ var PROVENANCE_REQUIRED = ["skillId", "contentHash", "date"];
222
+ var PROVENANCE_OPTIONAL = ["source", "path"];
223
+ function hasReadableProvenance(metadata) {
224
+ if (!Object.hasOwn(metadata, "forkedFrom")) return true;
225
+ const provenance = metadata.forkedFrom;
226
+ return isMapping(provenance) && PROVENANCE_REQUIRED.every((key) => typeof provenance[key] === "string") && PROVENANCE_OPTIONAL.every(
227
+ (key) => !Object.hasOwn(provenance, key) || typeof provenance[key] === "string"
228
+ );
229
+ }
230
+ function manifestOf(files) {
231
+ const manifest = files[STANDARD_FILES.SKILL_MD];
232
+ if (manifest === void 0) {
233
+ throw new Error(
234
+ `A carried skill's directory holds no ${STANDARD_FILES.SKILL_MD}, which seedSkillTreeSchema refuses`
235
+ );
236
+ }
237
+ return manifest;
238
+ }
239
+ var CONTENT_HASH_LENGTH = 7;
240
+ async function contentHashOf(text) {
241
+ const digest = await crypto.subtle.digest(
242
+ "SHA-256",
243
+ new TextEncoder().encode(text)
244
+ );
245
+ return hexOf(new Uint8Array(digest)).slice(0, CONTENT_HASH_LENGTH);
246
+ }
247
+ var hexOf = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
248
+
249
+ // src/cli/lib/seed/external-skills.ts
250
+ function pluginInstallError(ids) {
251
+ return [
252
+ "This configuration cannot be installed: these skills travel inside it, so no marketplace serves them and they cannot be installed as plugins:",
253
+ ...ids.map((id) => ` ${id}`),
254
+ "Re-share it with each skill above set to eject, which is the only way a carried skill installs."
255
+ ].join("\n");
256
+ }
257
+ function selectedExternals(payload) {
258
+ return typedEntries(payload.external ?? {}).flatMap(([id, skill]) => {
259
+ const entry = payload.skills[id];
260
+ return entry ? [{ id, skill, entry }] : [];
261
+ });
262
+ }
263
+ function askedForAsPlugin(externals) {
264
+ return externals.filter(({ entry }) => entry.install !== "eject").map(({ id }) => id);
265
+ }
266
+ function claimingACatalogueId(externals, matrix2) {
267
+ return externals.filter(({ id }) => heldByEitherCatalogue(id, matrix2)).map(({ id }) => id);
268
+ }
269
+ var CATALOGUE_SKILL_IDS = new Set(typedKeys(BUILT_IN_MATRIX.skills));
270
+ function heldByEitherCatalogue(id, matrix2) {
271
+ return heldByLoadedCatalogue(matrix2.skills[id]) || CATALOGUE_SKILL_IDS.has(id);
272
+ }
273
+ function heldByLoadedCatalogue(incumbent) {
274
+ return incumbent !== void 0 && incumbent.local !== true;
275
+ }
276
+ function catalogueIdCollisionError(ids) {
277
+ return [
278
+ "This configuration cannot be installed: these skills travel inside it under ids a catalogue already owns:",
279
+ ...ids.map((id) => ` ${id}`),
280
+ "A skill id is the directory the skill installs into, so each would be written over the catalogue's own copy of that skill. Re-share it with each skill above added again, which mints an id \u2014 'external-<category>-<name>' \u2014 that no catalogue owns."
281
+ ].join("\n");
282
+ }
283
+ function externalSlug(id) {
284
+ return id;
285
+ }
286
+ function externalSkillDir(id, scope, projectDir) {
287
+ return path.join(resolveInstallPaths(projectDir, scope).skillsDir, id);
288
+ }
289
+ function externalCatalogueEntry(install) {
290
+ return {
291
+ id: install.id,
292
+ slug: externalSlug(install.id),
293
+ displayName: install.skill.displayName,
294
+ description: install.skill.description,
295
+ // Boundary cast: seatExternalSkill has confirmed this catalogue declares the category.
296
+ category: install.skill.categoryId,
297
+ author: carriedSkillAuthor(install.skill.repo),
298
+ path: `${LOCAL_SKILLS_PATH}/${install.id}/`,
299
+ conflictsWith: [],
300
+ requires: [],
301
+ alternatives: [],
302
+ discourages: [],
303
+ local: true,
304
+ custom: true,
305
+ localPath: install.skillDir
306
+ };
307
+ }
308
+ function seatExternalSkill({ id, skill, entry }, matrix2, projectDir) {
309
+ const domain = getCategoryDomain(skill.categoryId);
310
+ if (domain === void 0) return [];
311
+ const install = {
312
+ id,
313
+ skill,
314
+ domain,
315
+ scope: entry.scope,
316
+ skillDir: externalSkillDir(id, entry.scope, projectDir)
317
+ };
318
+ matrix2.skills[id] = externalCatalogueEntry(install);
319
+ claimSlug(matrix2.slugMap, externalSlug(id), id);
320
+ return [install];
321
+ }
322
+ function notADirectoryNameError(ids) {
323
+ return [
324
+ "This configuration cannot be installed: these skills travel inside it under ids that are not a single directory name:",
325
+ ...ids.map((id) => ` ${JSON.stringify(id)}`),
326
+ "A skill id is the directory the skill is written into, so an id holding a path separator, or naming '.' or '..', would be written somewhere other than a directory of its own, and one holding a control character would hand it to the terminal wherever that directory is named. Re-share it with each skill above added again, which mints an id \u2014 'external-<category>-<name>' \u2014 that names one directory."
327
+ ].join("\n");
328
+ }
329
+ function registerExternalSkills(payload, matrix2, projectDir) {
330
+ const notDirectoryNames = externalIdsNamingNoDirectory(payload);
331
+ if (notDirectoryNames.length > 0) throw new Error(notADirectoryNameError(notDirectoryNames));
332
+ const externals = selectedExternals(payload);
333
+ const asPlugin = askedForAsPlugin(externals);
334
+ const colliding = claimingACatalogueId(externals, matrix2);
335
+ if (asPlugin.length > 0) throw new Error(pluginInstallError(asPlugin));
336
+ if (colliding.length > 0) throw new Error(catalogueIdCollisionError(colliding));
337
+ return externals.flatMap((external) => seatExternalSkill(external, matrix2, projectDir));
338
+ }
339
+ async function writeExternalSkills(installs) {
340
+ await refuseUncarriedDestinations(installs);
341
+ const written = await Promise.all(installs.map(writeExternalSkill));
342
+ return changedIds(written);
343
+ }
344
+ var SCRATCH_DIR_PREFIX = "agents-inc-carried-";
345
+ async function carriedSkillsThatDiffer(installs) {
346
+ if (installs.length === 0) return [];
347
+ const scratch = await mkdtemp(path.join(os.tmpdir(), SCRATCH_DIR_PREFIX));
348
+ try {
349
+ const written = await Promise.all(
350
+ installs.map(async (install) => writeExternalSkill(await scratchCopyOf(install, scratch)))
351
+ );
352
+ return changedIds(written);
353
+ } finally {
354
+ await remove(scratch);
355
+ }
356
+ }
357
+ async function scratchCopyOf(install, scratch) {
358
+ const skillDir = path.join(scratch, install.id);
359
+ if (await directoryExists(install.skillDir)) await copy(install.skillDir, skillDir);
360
+ return { ...install, skillDir };
361
+ }
362
+ function changedIds(written) {
363
+ return written.filter(({ changed }) => changed).map(({ id }) => id);
364
+ }
365
+ async function writeExternalSkill(install) {
366
+ const files = typedEntries(await treeToWrite(install));
367
+ refuseEscapingPaths(install.skillDir, files);
368
+ const written = await Promise.all(
369
+ files.map(
370
+ ([relativePath, contents]) => writeIfChanged(path.join(install.skillDir, relativePath), contents)
371
+ )
372
+ );
373
+ return { id: install.id, changed: written.some(Boolean) };
374
+ }
375
+ function refuseEscapingPaths(skillDir, files) {
376
+ for (const [relativePath] of files) {
377
+ validateSkillPath(path.join(skillDir, relativePath), skillDir, relativePath);
378
+ }
379
+ }
380
+ async function treeToWrite(install) {
381
+ const shipped = await withShippedMetadata(install);
382
+ const unchanged = await treeAsInstalled(install, shipped);
383
+ return unchanged ?? carriedTree(install, shipped, getCurrentDate());
384
+ }
385
+ async function treeAsInstalled(install, shipped) {
386
+ const recorded = (await readForkedFromMetadata(install.skillDir))?.date;
387
+ if (recorded === void 0) return null;
388
+ const tree = await carriedTree(install, shipped, recorded);
389
+ const onDisk = await bytesOnDisk(path.join(install.skillDir, STANDARD_FILES.METADATA_YAML));
390
+ return tree[STANDARD_FILES.METADATA_YAML] === onDisk ? tree : null;
391
+ }
392
+ function carriedTree(install, skill, date) {
393
+ return installedSkillTree({
394
+ id: install.id,
395
+ skill,
396
+ domain: install.domain,
397
+ usageGuidance: defaultUsageGuidance(install.skill.categoryId),
398
+ date
399
+ });
400
+ }
401
+ async function withShippedMetadata(install) {
402
+ if (Object.hasOwn(install.skill.files, STANDARD_FILES.METADATA_YAML)) return install.skill;
403
+ const onDisk = await bytesOnDisk(path.join(install.skillDir, STANDARD_FILES.METADATA_YAML));
404
+ if (onDisk === null) return install.skill;
405
+ return {
406
+ ...install.skill,
407
+ files: { ...install.skill.files, [STANDARD_FILES.METADATA_YAML]: onDisk }
408
+ };
409
+ }
410
+ async function bytesOnDisk(filePath) {
411
+ return await fileExists(filePath) ? readFile(filePath) : null;
412
+ }
413
+ async function writeIfChanged(filePath, contents) {
414
+ if (await bytesOnDisk(filePath) === contents) return false;
415
+ await writeFile(filePath, contents);
416
+ return true;
417
+ }
418
+ async function refuseUncarriedDestinations(installs) {
419
+ const judged = await Promise.all(installs.map(judgeDestination));
420
+ const blocked = judged.filter((destination) => destination !== null);
421
+ if (blocked.length > 0) throw new Error(uncarriedDestinationError(blocked));
422
+ }
423
+ async function judgeDestination(install) {
424
+ const { id, skillDir } = install;
425
+ if (!await directoryExists(skillDir)) return null;
426
+ if ((await readForkedFromMetadata(skillDir))?.path !== void 0) return null;
427
+ return { id, skillDir };
428
+ }
429
+ function uncarriedDestinationError(blocked) {
430
+ return [
431
+ "This configuration cannot be installed: these skills travel inside it under ids this installation already keeps a skill directory for, and no shared configuration put those directories there:",
432
+ ...blocked.map(({ id, skillDir }) => ` ${id} -> ${skillDir}`),
433
+ "A skill id is the directory the skill installs into, so each would be written over a skill written here by hand or ejected from a marketplace. Move or rename the directory above, or re-share the configuration with each skill added again, which mints an id \u2014 'external-<category>-<name>' \u2014 that nothing here holds."
434
+ ].join("\n");
435
+ }
436
+ function unnameableRepoMessage(id) {
437
+ return `${id} travels inside a shared configuration, and this installation does not record the repository its content came from`;
438
+ }
439
+ function uncarryableContentMessage(id, reason) {
440
+ return `${id} travels inside a shared configuration, and ${reason}`;
441
+ }
442
+ function repoFromRef(ref) {
443
+ if (ref === void 0 || !ref.startsWith(GITHUB_SOURCE.GITHUB_PREFIX)) return void 0;
444
+ return ref.slice(GITHUB_SOURCE.GITHUB_PREFIX.length);
445
+ }
446
+ var NO_MANIFEST_DESCRIPTION = `no description can be read from its ${STANDARD_FILES.SKILL_MD}`;
447
+ function manifestDescription(files, skillDir) {
448
+ const manifest = files[STANDARD_FILES.SKILL_MD];
449
+ if (manifest === void 0) return null;
450
+ const frontmatter = parseFrontmatter(manifest, path.join(skillDir, STANDARD_FILES.SKILL_MD));
451
+ return frontmatter === null ? null : frontmatter.description;
452
+ }
453
+ async function readSkillTree(skillDir) {
454
+ const files = await glob("**/*", skillDir, { dot: true });
455
+ const entries = await Promise.all(
456
+ files.map(async (file) => [file, await readFile(path.join(skillDir, file))])
457
+ );
458
+ return Object.fromEntries(entries);
459
+ }
460
+ async function readCarriedSkill({ id, skillDir, provenance }) {
461
+ if (provenance.path === void 0) return { carries: "nothing" };
462
+ const repo = repoFromRef(provenance.source);
463
+ if (repo === void 0) return { carries: "unshareable", line: unnameableRepoMessage(id) };
464
+ const described = await readSkillMetadata(path.join(skillDir, STANDARD_FILES.METADATA_YAML));
465
+ if (!described.usable) {
466
+ return { carries: "unshareable", line: uncarryableContentMessage(id, described.reason) };
467
+ }
468
+ const files = await readSkillTree(skillDir);
469
+ const description = manifestDescription(files, skillDir);
470
+ if (description === null) {
471
+ return { carries: "unshareable", line: uncarryableContentMessage(id, NO_MANIFEST_DESCRIPTION) };
472
+ }
473
+ const parsed = seedExternalSkillSchema.safeParse({
474
+ displayName: described.metadata.displayName,
475
+ description,
476
+ categoryId: described.metadata.category,
477
+ repo,
478
+ path: provenance.path,
479
+ files
480
+ });
481
+ if (!parsed.success) {
482
+ const reason = parsed.error.issues.map((issue) => issue.message).join("; ");
483
+ return { carries: "unshareable", line: uncarryableContentMessage(id, reason) };
484
+ }
485
+ return { carries: "content", id, skill: parsed.data };
486
+ }
487
+ async function readCarriedSkills(owned) {
488
+ const reads = await Promise.all(owned.map(readCarriedSkill));
489
+ return {
490
+ external: Object.fromEntries(
491
+ reads.flatMap((read) => read.carries === "content" ? [[read.id, read.skill]] : [])
492
+ ),
493
+ uncarryable: reads.flatMap((read) => read.carries === "unshareable" ? [read.line] : [])
494
+ };
495
+ }
496
+
497
+ // src/cli/lib/seed/seed-apply.ts
498
+ import os2 from "os";
499
+ import { isDeepEqual, omit, partition, pick } from "remeda";
500
+ var NOTHING_KEPT = {
501
+ authoredSkillIds: [],
502
+ unplaceableSkillIds: []
503
+ };
504
+ function reconcileSharedConfig(options) {
505
+ const { decoded, installed } = options;
506
+ if (!installed) return nothingToReconcile(decoded);
507
+ const keptSkills = skillsToKeep(options, installed);
508
+ if (keptSkills.length === 0) return nothingToReconcile(decoded);
509
+ const { result, unassigned } = withKeptEntries(decoded, installed, keptSkills);
510
+ return { result, kept: describeKept(options, keptSkills), unassigned };
511
+ }
512
+ function nothingToReconcile(decoded) {
513
+ return { result: decoded, kept: NOTHING_KEPT, unassigned: [] };
514
+ }
515
+ function skillsToKeep(options, installed) {
516
+ const named = new Set(options.decoded.skills.map((skill) => skill.id));
517
+ return installed.skills.filter(
518
+ (skill) => !skill.excluded && !named.has(skill.id) && mayNotRemove(skill, options)
519
+ );
520
+ }
521
+ function mayNotRemove(skill, options) {
522
+ return options.authoredHere.has(skill.id) || options.unplaceable.has(skill.id);
523
+ }
524
+ function describeKept(options, keptSkills) {
525
+ const idsKeptFor = (reason) => keptSkills.filter((skill) => reasonKept(skill, options) === reason).map((skill) => skill.id);
526
+ return {
527
+ authoredSkillIds: idsKeptFor("authored"),
528
+ unplaceableSkillIds: idsKeptFor("unplaceable")
529
+ };
530
+ }
531
+ function reasonKept(skill, options) {
532
+ return options.authoredHere.has(skill.id) ? "authored" : "unplaceable";
533
+ }
534
+ function withKeptEntries(decoded, installed, keptSkills) {
535
+ const withSkills = {
536
+ ...decoded,
537
+ skills: [...decoded.skills, ...keptSkills],
538
+ selectedDomains: withKeptDomains(decoded.selectedDomains, keptSkills)
539
+ };
540
+ const { assignedStack, unassigned } = withKeptStackRows(decoded.assignedStack, installed.stack, {
541
+ keptSkillIds: new Set(keptSkills.map((skill) => skill.id)),
542
+ survivingAgents: new Set(decoded.selectedAgents)
543
+ });
544
+ return { result: { ...withSkills, assignedStack }, unassigned };
545
+ }
546
+ function withKeptDomains(selected, keptSkills) {
547
+ const keptDomains = keptSkills.flatMap((skill) => domainOfSkill(skill.id) ?? []);
548
+ const missing = keptDomains.filter((domain) => !selected.includes(domain));
549
+ if (missing.length === 0) return selected;
550
+ return orderDomains([...selected, ...new Set(missing)]);
551
+ }
552
+ function domainOfSkill(skillId) {
553
+ const category = matrix.skills[skillId]?.category;
554
+ return category === void 0 ? void 0 : getCategoryDomain(category);
555
+ }
556
+ function withKeptStackRows(assigned, installedStack, filter) {
557
+ const isKept = (_category, row) => filter.keptSkillIds.has(row.id);
558
+ return typedEntries(installedStack ?? {}).filter(([agent]) => filter.survivingAgents.has(agent)).reduce(
559
+ (carried, [agent, agentStack]) => withAgentKeptRows(carried, agent, rowsWhere(agentStack, isKept), assigned[agent]),
560
+ { assignedStack: assigned, unassigned: [] }
561
+ );
562
+ }
563
+ function withAgentKeptRows(carried, agent, keptRows, payloadRows) {
564
+ const filled = filledExclusiveCategories(payloadRows);
565
+ const [displaced, joining] = partition(keptRows, ([category]) => filled.has(category));
566
+ return {
567
+ assignedStack: withAppendedRows(carried.assignedStack, agent, joining),
568
+ unassigned: [
569
+ ...carried.unassigned,
570
+ ...displaced.map(([category, row]) => ({ skillId: row.id, agentName: agent, category }))
571
+ ]
572
+ };
573
+ }
574
+ function withUnplaceableAssignees(decoded, installed, payload, unplaceable) {
575
+ if (!installed) return decoded;
576
+ const assigned = assigneesOfHeld(payload, installed, unplaceable);
577
+ const selected = new Set(decoded.selectedAgents);
578
+ const leftOutThoughAssigned = (agent) => !agent.excluded && assigned.has(agent.name) && !selected.has(agent.name);
579
+ const assignees = installed.agents.filter(leftOutThoughAssigned);
580
+ if (assignees.length === 0) return decoded;
581
+ return {
582
+ ...decoded,
583
+ selectedAgents: [...decoded.selectedAgents, ...assignees.map((agent) => agent.name)],
584
+ agentConfigs: [...decoded.agentConfigs, ...assignees]
585
+ };
586
+ }
587
+ function assigneesOfHeld(payload, installed, unplaceable) {
588
+ const held = new Set(
589
+ installed.skills.filter((skill) => !skill.excluded).map((skill) => skill.id)
590
+ );
591
+ const isHeldUnplaceable = ([id]) => unplaceable.has(id) && held.has(id);
592
+ const isNotSwitchedOff = (name) => payload.agents[name]?.on !== false;
593
+ return new Set(
594
+ typedEntries(payload.skills).filter(isHeldUnplaceable).flatMap(([, skill]) => typedKeys(skill.assignments)).filter(isNotSwitchedOff)
595
+ );
596
+ }
597
+ async function readInstalledGlobal(projectDir) {
598
+ const loaded = await loadProjectConfigFromDir(os2.homedir(), providerInUse(projectDir));
599
+ return loaded?.config ?? null;
600
+ }
601
+ var NO_GLOBAL_ENTRIES = { skillIds: [], agentNames: [] };
602
+ function holdInstalledGlobal(decoded, installed) {
603
+ if (!installed) {
604
+ return {
605
+ result: decoded,
606
+ installed: NO_GLOBAL_ENTRIES,
607
+ keptAsInstalled: NO_GLOBAL_ENTRIES,
608
+ joined: []
609
+ };
610
+ }
611
+ const heldSkills = activeGlobalById(installed.skills, (skill) => skill.id);
612
+ const heldAgents = activeGlobalById(installed.agents, (agent) => agent.name);
613
+ const skills = pairWithHeld(decoded.skills, heldSkills, (skill) => skill.id);
614
+ const agents = pairWithHeld(decoded.agentConfigs, heldAgents, (agent) => agent.name);
615
+ const namedSkillIds = skills.map(({ named }) => named.id);
616
+ const namedAgentNames = agents.map(({ named }) => named.name);
617
+ const { assignedStack, joined } = withHeldRows(decoded.assignedStack, installed.stack, {
618
+ agentNames: namedAgentNames,
619
+ skippedSkillIds: new Set(namedSkillIds)
620
+ });
621
+ return {
622
+ result: {
623
+ ...decoded,
624
+ skills: decoded.skills.map(asHeld(skills)),
625
+ agentConfigs: decoded.agentConfigs.map(asHeld(agents)),
626
+ assignedStack
627
+ },
628
+ installed: { skillIds: namedSkillIds, agentNames: namedAgentNames },
629
+ keptAsInstalled: {
630
+ skillIds: skillsKeptAsInstalled(skills),
631
+ agentNames: agentsKeptAsInstalled(agents, {
632
+ stated: decoded.assignedStack,
633
+ held: assignedStack,
634
+ joined
635
+ })
636
+ },
637
+ joined
638
+ };
639
+ }
640
+ function skillsKeptAsInstalled(pairs) {
641
+ return pairs.filter((pair) => !restatesSkill(pair)).map(({ named }) => named.id);
642
+ }
643
+ function restatesSkill({ named, held }) {
644
+ return named.origin === held.origin;
645
+ }
646
+ function agentsKeptAsInstalled(pairs, rows) {
647
+ const joinedNames = new Set(rows.joined.map(({ agentName }) => agentName));
648
+ const keptAsIs = (pair) => !restatesAgent(pair, rows) && !joinedNames.has(pair.named.name);
649
+ return pairs.filter(keptAsIs).map(({ named }) => named.name);
650
+ }
651
+ function restatesAgent({ named, held }, rows) {
652
+ return named.model === held.model && named.codexModel === held.codexModel && named.effort === held.effort && sameAssignments(rows.stated[named.name], rows.held[named.name]);
653
+ }
654
+ function restoreInstalledGlobal(decoded, installed) {
655
+ const held = holdInstalledGlobal(decoded, installed);
656
+ if (!installed) return held;
657
+ const omitted = omittedGlobal(held.result, installed);
658
+ const { skillIds, agentNames } = held.keptAsInstalled;
659
+ return {
660
+ ...held,
661
+ result: withOmittedGlobal(held.result, installed, omitted),
662
+ keptAsInstalled: {
663
+ skillIds: [...skillIds, ...omitted.skills.map((skill) => skill.id)],
664
+ agentNames: [...agentNames, ...omitted.agents.map((agent) => agent.name)]
665
+ }
666
+ };
667
+ }
668
+ function activeGlobalById(entries, keyOf) {
669
+ return new Map(
670
+ entries.filter((entry) => isActiveAt(entry, "global")).map((entry) => [keyOf(entry), entry])
671
+ );
672
+ }
673
+ function asHeld(pairs) {
674
+ const heldFor = new Map(pairs.map(({ named, held }) => [named, held]));
675
+ return (entry) => heldFor.get(entry) ?? entry;
676
+ }
677
+ function pairWithHeld(decoded, heldByKey, keyOf) {
678
+ return decoded.flatMap((named) => {
679
+ const held = heldByKey.get(keyOf(named));
680
+ return held !== void 0 && isActiveAt(named, "global") ? [{ named, held }] : [];
681
+ });
682
+ }
683
+ function sameAssignments(left, right) {
684
+ return isDeepEqual(assignmentKeys(left), assignmentKeys(right));
685
+ }
686
+ function assignmentKeys(agentStack) {
687
+ return typedEntries(agentStack ?? {}).flatMap(
688
+ ([category, assignments]) => assignments.map((row) => `${category}/${row.id}/${row.preloaded === true}`)
689
+ ).sort();
690
+ }
691
+ function withInstalledRows(assigned, installedStack, agentNames) {
692
+ return { ...omit(assigned, agentNames), ...pick(installedStack ?? {}, agentNames) };
693
+ }
694
+ function withHeldRows(assigned, installedStack, held) {
695
+ const asInstalled = withInstalledRows(assigned, installedStack, held.agentNames);
696
+ const arrivals = held.agentNames.map((agentName) => ({
697
+ agentName,
698
+ rows: arrivingRows(assigned[agentName], asInstalled[agentName], held.skippedSkillIds)
699
+ }));
700
+ return {
701
+ assignedStack: arrivals.reduce(
702
+ (stack, { agentName, rows }) => withAppendedRows(stack, agentName, rows),
703
+ asInstalled
704
+ ),
705
+ joined: arrivals.filter(({ rows }) => rows.length > 0).map(asJoined)
706
+ };
707
+ }
708
+ function asJoined({ agentName, rows }) {
709
+ return { agentName, skillIds: rows.map(([, row]) => row.id) };
710
+ }
711
+ function arrivingRows(stated, installed, skippedSkillIds) {
712
+ const alreadyLoaded = new Set(rowIds(installed));
713
+ const filled = filledExclusiveCategories(installed);
714
+ const arrives = (category, row) => !skippedSkillIds.has(row.id) && !alreadyLoaded.has(row.id) && !filled.has(category);
715
+ return rowsWhere(stated, arrives);
716
+ }
717
+ function rowIds(agentStack) {
718
+ return typedValues(agentStack ?? {}).flatMap(
719
+ (rows) => rows.map((row) => row.id)
720
+ );
721
+ }
722
+ function filledExclusiveCategories(agentStack) {
723
+ const isExclusive = (category) => matrix.categories[category]?.exclusive === true;
724
+ return new Set(
725
+ typedEntries(agentStack ?? {}).filter(([category, rows]) => rows.length > 0 && isExclusive(category)).map(([category]) => category)
726
+ );
727
+ }
728
+ function withAppendedRows(stack, agentName, added) {
729
+ if (added.length === 0) return stack;
730
+ return { ...stack, [agentName]: appendRows(stack[agentName] ?? {}, added) };
731
+ }
732
+ function rowsWhere(agentStack, keep) {
733
+ return typedEntries(agentStack ?? {}).flatMap(
734
+ ([category, rows]) => rows.filter((row) => keep(category, row)).map((row) => [category, row])
735
+ );
736
+ }
737
+ function appendRows(agentStack, added) {
738
+ return added.reduce(
739
+ (rows, [category, row]) => ({ ...rows, [category]: [...rows[category] ?? [], row] }),
740
+ agentStack
741
+ );
742
+ }
743
+ function omittedGlobal(result, installed) {
744
+ const namedSkillIds = new Set(result.skills.map((skill) => skill.id));
745
+ const namedAgentNames = new Set(result.agentConfigs.map((agent) => agent.name));
746
+ return {
747
+ skills: [...activeGlobalById(installed.skills, (skill) => skill.id).values()].filter(
748
+ (skill) => !namedSkillIds.has(skill.id)
749
+ ),
750
+ agents: [...activeGlobalById(installed.agents, (agent) => agent.name).values()].filter(
751
+ (agent) => !namedAgentNames.has(agent.name)
752
+ )
753
+ };
754
+ }
755
+ function withOmittedGlobal(result, installed, omitted) {
756
+ const omittedAgentNames = omitted.agents.map((agent) => agent.name);
757
+ return {
758
+ ...result,
759
+ skills: [...result.skills, ...omitted.skills],
760
+ agentConfigs: [...result.agentConfigs, ...omitted.agents],
761
+ selectedAgents: [.../* @__PURE__ */ new Set([...result.selectedAgents, ...omittedAgentNames])],
762
+ selectedDomains: withKeptDomains(result.selectedDomains, omitted.skills),
763
+ assignedStack: withInstalledRows(result.assignedStack, installed.stack, omittedAgentNames)
764
+ };
765
+ }
766
+ function notInstalledGlobally(entries, held) {
767
+ const installed = new Set(held.installed.skillIds);
768
+ return entries.filter((entry) => !installed.has(entry.id));
769
+ }
770
+ function arrivalsByScope(skills, agents) {
771
+ return {
772
+ project: entriesActiveAt(skills, agents, "project"),
773
+ global: entriesActiveAt(skills, agents, "global")
774
+ };
775
+ }
776
+ function entriesActiveAt(skills, agents, scope) {
777
+ return {
778
+ skillIds: skills.filter((skill) => isActiveAt(skill, scope)).map((skill) => skill.id),
779
+ agentNames: agents.filter((agent) => isActiveAt(agent, scope)).map((agent) => agent.name)
780
+ };
781
+ }
782
+ function installArrivals(held) {
783
+ const installedSkillIds = new Set(held.installed.skillIds);
784
+ const installedAgentNames = new Set(held.installed.agentNames);
785
+ return arrivalsByScope(
786
+ held.result.skills.filter((skill) => !installedSkillIds.has(skill.id)),
787
+ held.result.agentConfigs.filter((agent) => !installedAgentNames.has(agent.name))
788
+ );
789
+ }
790
+ function installPlanLines(held) {
791
+ return [
792
+ ...arrivalLines(installArrivals(held)),
793
+ ...scopeLines(SHARED_CONFIG_ARRIVALS.SKIPPED_HEADING, skippedAsInstalled(held)),
794
+ ...joinedStatement(held)
795
+ ];
796
+ }
797
+ function skippedAsInstalled({ installed, joined }) {
798
+ const joinedNames = new Set(joined.map(({ agentName }) => agentName));
799
+ return {
800
+ skillIds: installed.skillIds,
801
+ agentNames: installed.agentNames.filter((name) => !joinedNames.has(name))
802
+ };
803
+ }
804
+ function joinedStatement(held) {
805
+ return held.joined.length === 0 ? [] : [addedToGlobalSubAgents(held.joined)];
806
+ }
807
+ function skillLabel(skillId) {
808
+ const displayName = getSkillDisplayName(skillId);
809
+ return displayName === skillId ? skillId : `${displayName} (${skillId})`;
810
+ }
811
+ function arrivalLines(arrivals) {
812
+ return [
813
+ ...scopeLines(SHARED_CONFIG_ARRIVALS.PROJECT_HEADING, arrivals.project),
814
+ ...scopeLines(SHARED_CONFIG_ARRIVALS.GLOBAL_HEADING, arrivals.global)
815
+ ];
816
+ }
817
+ function scopeLines(heading, entries) {
818
+ const groups = [
819
+ { label: SHARED_CONFIG_APPLY.SKILLS_HEADING, items: entries.skillIds.map(skillLabel) },
820
+ { label: SHARED_CONFIG_APPLY.AGENTS_HEADING, items: entries.agentNames }
821
+ ].filter((group) => group.items.length > 0);
822
+ if (groups.length === 0) return [];
823
+ return [
824
+ heading,
825
+ ...groups.flatMap((group) => [` ${group.label}`, ...group.items.map((item) => ` ${item}`)])
826
+ ];
827
+ }
828
+ function projectScopedContent(result) {
829
+ return entriesActiveAt(result.skills, result.agentConfigs, "project");
830
+ }
831
+ function installsAtHome(result) {
832
+ const { skillIds, agentNames } = projectScopedContent(result);
833
+ return skillIds.length === 0 && agentNames.length === 0;
834
+ }
835
+ function skippedWithTheirSkills(skipped, selectedAgents) {
836
+ const selected = new Set(selectedAgents);
837
+ return skipped.flatMap(({ skillId, agentNames }) => {
838
+ const left = agentNames.filter((name) => !selected.has(name));
839
+ return left.length === 0 ? [] : [{ skillId, agentNames: left }];
840
+ });
841
+ }
842
+
843
+ // src/cli/base-command.ts
844
+ import { Command } from "@oclif/core";
845
+ import chalk from "chalk";
846
+ import { unique } from "remeda";
847
+
848
+ // src/cli/lib/operations/source/ensure-marketplace.ts
849
+ async function ensureMarketplace(sourceResult, host) {
850
+ const marketplace = await resolveMarketplaceName(sourceResult);
851
+ if (marketplace === null) return { marketplace: null, registered: false };
852
+ const exists = await host.marketplaceExists(marketplace);
853
+ if (!exists) {
854
+ const marketplaceSource = sourceResult.sourceConfig.source.replace(/^github:/, "");
855
+ await host.addMarketplace(marketplaceSource);
856
+ return { marketplace, registered: true };
857
+ }
858
+ try {
859
+ await host.refreshMarketplace(marketplace);
860
+ } catch {
861
+ warn("Could not update marketplace \u2014 continuing with cached version");
862
+ }
863
+ return { marketplace, registered: false };
864
+ }
865
+ async function resolveMarketplaceName(sourceResult) {
866
+ if (sourceResult.marketplace) return sourceResult.marketplace;
867
+ const source = sourceResult.sourceConfig.source;
868
+ try {
869
+ const marketplaceResult = await fetchMarketplace(source);
870
+ sourceResult.marketplace = marketplaceResult.marketplace.name;
871
+ return sourceResult.marketplace;
872
+ } catch (error) {
873
+ warn(`Could not resolve a marketplace from '${source}': ${getErrorMessage(error)}`);
874
+ return null;
875
+ }
876
+ }
877
+
878
+ // src/cli/lib/operations/source/require-marketplace.ts
879
+ async function requireMarketplace(sourceResult, purpose, host) {
880
+ const mpResult = await ensureMarketplace(sourceResult, host);
881
+ if (!mpResult.marketplace) {
882
+ return {
883
+ ok: false,
884
+ error: `Cannot ${purpose}: marketplace could not be resolved from '${sourceResult.sourceConfig.source}'. Plugin install mode requires a marketplace \u2014 fix the marketplace or switch the affected skills to eject mode.`
885
+ };
886
+ }
887
+ return { ok: true, marketplace: mpResult.marketplace, registered: mpResult.registered };
888
+ }
889
+
890
+ // src/cli/lib/operations/skills/install-plugin-skills.ts
891
+ function pluginInstallFailureError(failedCount) {
892
+ return `Failed to install ${failedCount} plugin skill(s). Plugin install intent could not be honored. Verify the skill id matches the marketplace, run '${CLI_INVOKE_COMMAND} update' to refresh the marketplace, or switch affected skills to eject mode.`;
893
+ }
894
+ function unbackedPluginSkillIds(skills, matrix2) {
895
+ return skills.filter((skill) => skill.origin !== EJECT_SOURCE).filter((skill) => isLocalOnlySkill(matrix2.skills[skill.id])).map((skill) => skill.id);
896
+ }
897
+ function unbackedPluginInstallError(ids) {
898
+ return `Cannot install ${ids.length} skill(s) as plugins \u2014 no marketplace carries them: ${ids.join(", ")}. A skill that exists only in this project can only be installed as a local copy. Set its Install mode to eject in the editor, or publish it to a marketplace first.`;
899
+ }
900
+ async function installPluginSkills(skills, marketplace, projectDir) {
901
+ const host = hostAt(projectDir);
902
+ const pluginSkills = skills.filter((s) => s.origin !== EJECT_SOURCE);
903
+ const installed = [];
904
+ const failed = [];
905
+ for (const skill of pluginSkills) {
906
+ const pluginRef = buildMarketplacePluginRef(skill.id, marketplace);
907
+ try {
908
+ await host.installPlugin(pluginRef, skill.scope, projectDir);
909
+ installed.push({ id: skill.id, ref: pluginRef });
910
+ } catch (error) {
911
+ failed.push({ id: skill.id, error: getErrorMessage(error) });
912
+ }
913
+ }
914
+ return { installed, failed };
915
+ }
916
+
917
+ // src/cli/base-command.ts
918
+ var TERMINAL_RESIZE_POLL_MS = 500;
919
+ var terminalColumns = () => process.stdout.columns ?? MIN_TERMINAL_SIZE.COLS;
920
+ var terminalRows = () => process.stdout.rows ?? MIN_TERMINAL_SIZE.ROWS;
921
+ function isTerminalBigEnough() {
922
+ return isTerminalLargeEnough(terminalColumns(), terminalRows());
923
+ }
924
+ function waitForTerminal(isSatisfied) {
925
+ return new Promise((resolve) => {
926
+ const check = () => {
927
+ if (!isSatisfied()) return;
928
+ clearInterval(interval);
929
+ process.stdout.off("resize", check);
930
+ resolve();
931
+ };
932
+ const interval = setInterval(check, TERMINAL_RESIZE_POLL_MS);
933
+ process.stdout.on("resize", check);
934
+ });
935
+ }
936
+ function findUnassignedSkills(config, selected) {
937
+ const assignedIds = new Set(config.stack ? getStackSkillIds(config.stack) : []);
938
+ const selectedIds = new Set(selected.map((skill) => skill.id));
939
+ const globalAgentNames = config.agents.filter((agent) => isActiveAt(agent, "global")).map((agent) => agent.name);
940
+ const unassignedIds = unique(
941
+ config.skills.filter((skill) => !skill.excluded && selectedIds.has(skill.id)).map((skill) => skill.id).filter((skillId) => !assignedIds.has(skillId))
942
+ );
943
+ return unassignedIds.map((skillId) => ({
944
+ skillId,
945
+ blockedBy: hasActiveProjectEntry(config.skills, skillId) ? globalAgentNames : []
946
+ }));
947
+ }
948
+ function hasActiveProjectEntry(skills, skillId) {
949
+ return skills.some((skill) => skill.id === skillId && isActiveAt(skill, "project"));
950
+ }
951
+ var BaseCommand = class extends Command {
952
+ /** Non-empty decides the exit code; see {@link exitIfWorkIncomplete}. */
953
+ incompleteWork = [];
954
+ async init() {
955
+ await super.init();
956
+ await this.ensureTerminalSize();
957
+ }
958
+ async ensureTerminalSize() {
959
+ if (isTerminalBigEnough()) return;
960
+ clearTerminalScreen();
961
+ this.log(chalk.hex(CLI_COLORS.WARNING)(formatTerminalTooSmallMessage(terminalColumns())));
962
+ await waitForTerminal(isTerminalBigEnough);
963
+ }
964
+ handleError(error) {
965
+ const message = getErrorMessage(error);
966
+ this.error(message, { exit: EXIT_CODES.ERROR });
967
+ }
968
+ /**
969
+ * Degrades on an unreadable config: `doctor` and `uninstall` must survive one, and a heading is
970
+ * not worth refusing over. The command's own config reader reports the cause.
971
+ */
972
+ async resolveBrandingName(projectDir) {
973
+ try {
974
+ const branding = await resolveBranding(projectDir);
975
+ return branding.name;
976
+ } catch (error) {
977
+ verbose(`Could not read branding from the config: ${getErrorMessage(error)}`);
978
+ return DEFAULT_BRANDING.NAME;
979
+ }
980
+ }
981
+ logSuccess(message) {
982
+ this.log(`\u2713 ${message}`);
983
+ }
984
+ /** `onRegistered` fires only when the marketplace had to be registered. */
985
+ async requireMarketplaceOrExit(sourceResult, purpose, host, onRegistered) {
986
+ const required = await requireMarketplace(sourceResult, purpose, host);
987
+ if (!required.ok) {
988
+ this.error(required.error, { exit: EXIT_CODES.ERROR });
989
+ }
990
+ if (required.registered) {
991
+ onRegistered?.(required.marketplace);
992
+ }
993
+ return required.marketplace;
994
+ }
995
+ /** Advisory: the selection installs either way. A message can quote a payload's own ids. */
996
+ reportValidationErrors(validation) {
997
+ for (const error of validation.errors) {
998
+ this.warn(stripTerminalControls(error.message));
999
+ }
1000
+ }
1001
+ /** Refuses skills no marketplace carries before the host's CLI is asked. */
1002
+ async installPluginSkillsReported(skills, marketplace, projectDir, matrix2) {
1003
+ const unbacked = unbackedPluginSkillIds(skills, matrix2);
1004
+ if (unbacked.length > 0) {
1005
+ this.error(unbackedPluginInstallError(unbacked), { exit: EXIT_CODES.ERROR });
1006
+ }
1007
+ this.announcePluginInstall();
1008
+ const result = await installPluginSkills(skills, marketplace, projectDir);
1009
+ this.reportPluginInstalls(result);
1010
+ return result;
1011
+ }
1012
+ /** Split out for the eject-to-plugin migration, which runs its own installs. */
1013
+ announcePluginInstall() {
1014
+ this.log(STATUS_MESSAGES.INSTALLING_PLUGINS);
1015
+ }
1016
+ /** Hard-errors on any failure, before a config can record an origin for an uninstalled plugin. */
1017
+ reportPluginInstalls(result) {
1018
+ for (const item of result.installed) {
1019
+ this.log(` Installed ${item.ref}`);
1020
+ }
1021
+ for (const item of result.failed) {
1022
+ this.warn(`Failed to install plugin ${item.id}: ${item.error}`);
1023
+ }
1024
+ if (result.failed.length > 0) {
1025
+ this.error(pluginInstallFailureError(result.failed.length), { exit: EXIT_CODES.ERROR });
1026
+ }
1027
+ this.log(`${pluginsInstalled(result.installed.length)}
1028
+ `);
1029
+ }
1030
+ reportFanOut(report) {
1031
+ for (const projectPath of report.propagated.unreadable) {
1032
+ this.warn(registeredProjectConfigUnreadable(projectPath));
1033
+ }
1034
+ this.reportPropagatedRecompile(report);
1035
+ }
1036
+ reportProjectsOutOfReach(propagated) {
1037
+ for (const projectPath of propagated.gone) {
1038
+ this.warn(registeredProjectGone(projectPath));
1039
+ }
1040
+ for (const projectPath of propagated.notOurs) {
1041
+ this.warn(registeredProjectOnAnotherProvider(projectPath));
1042
+ }
1043
+ }
1044
+ reportPropagatedRecompile(report) {
1045
+ if (report.propagated.updated.length === 0) return;
1046
+ const { rewrittenCount, unchangedCount, failedCount, warnings } = report.recompile;
1047
+ for (const warning of warnings) {
1048
+ this.warn(warning);
1049
+ }
1050
+ this.log(propagatedRecompileSummary(rewrittenCount, unchangedCount, failedCount));
1051
+ }
1052
+ reportUnassignedSkills(config, selected) {
1053
+ for (const { skillId, blockedBy } of findUnassignedSkills(config, selected)) {
1054
+ this.warn(skillAssignedToNoAgent(skillId));
1055
+ if (blockedBy.length > 0) {
1056
+ this.warn(scopeBlockedStackAssignment(blockedBy, skillId));
1057
+ }
1058
+ }
1059
+ }
1060
+ /**
1061
+ * Aborts up front when the project or global config exists but cannot be loaded, so nothing is
1062
+ * installed before the config write fails. A missing config passes.
1063
+ */
1064
+ async ensureConfigReadable(projectDir) {
1065
+ const failures = await findConfigLoadFailures(projectDir);
1066
+ if (failures.length > 0) {
1067
+ this.error(installationConfigsUnreadable(failures), { exit: EXIT_CODES.ERROR });
1068
+ }
1069
+ }
1070
+ /** Only entries the catalogue lacks are checked; one whose files are gone is not refused. */
1071
+ async ensureSavedSkillsReadable(savedSkills, matrix2, projectDir) {
1072
+ const unresolved = savedSkills.filter((saved) => !matrix2.skills[saved.id]);
1073
+ await this.refuseUnusableSavedSkillMetadata(
1074
+ unresolved,
1075
+ projectDir,
1076
+ savedSkillMetadataUnusableError
1077
+ );
1078
+ }
1079
+ async refuseUnusableSavedSkillMetadata(savedSkills, projectDir, refusal) {
1080
+ const unusable = await findUnusableSavedSkillMetadata(savedSkills, projectDir);
1081
+ if (unusable.length === 0) return;
1082
+ for (const entry of unusable) {
1083
+ this.log(skillMetadataUnusableDetail(entry));
1084
+ }
1085
+ this.error(refusal(unusable), { exit: EXIT_CODES.ERROR });
1086
+ }
1087
+ /**
1088
+ * The home directory is the global scope, so project-scoped content there would land in the
1089
+ * global config mislabelled. Needs the decoded selection; callers run it before any write.
1090
+ */
1091
+ refuseProjectScopedContentAtHome(result, projectDir) {
1092
+ if (!isHomeDirectory(projectDir) || installsAtHome(result)) return;
1093
+ const { skillIds, agentNames } = projectScopedContent(result);
1094
+ this.error(sharedConfigProjectScopeAtHome(skillIds, agentNames), { exit: EXIT_CODES.ERROR });
1095
+ }
1096
+ /** Measured against the selection after its restores, so a sub-agent kept is not named skipped. */
1097
+ warnOfSkippedEntries(mapping, selectedAgents) {
1098
+ const { skippedSkillIds, skippedAgentNames, agentsOnlySkippedSkillsCarried } = mapping;
1099
+ const skippedWith = skippedWithTheirSkills(agentsOnlySkippedSkillsCarried, selectedAgents);
1100
+ if (skippedSkillIds.length > 0) this.warn(skippedUnknownSkills(skippedSkillIds, skippedWith));
1101
+ if (skippedAgentNames.length > 0) this.warn(skippedUnknownAgents(skippedAgentNames));
1102
+ }
1103
+ /** The `--from` refusals that need the decoded selection, asked before the confirm. */
1104
+ async refuseSharedConfigBeforeAsking(selection, carried, root, sourceResult) {
1105
+ this.refuseUnofferablePlacementsBeforeWriting(selection.skills, root);
1106
+ try {
1107
+ await refuseUnofferedStack(selection.selectedStackId, sourceResult);
1108
+ await refuseUncarriedDestinations(carried);
1109
+ refuseUnwritableStack(selection.assignedStack, sourceResult.matrix);
1110
+ } catch (error) {
1111
+ this.handleError(error);
1112
+ }
1113
+ }
1114
+ /**
1115
+ * Pre-flight over every row, ejected included: the per-plugin guard in `installPlugin` fires
1116
+ * mid-loop, after earlier plugins are already installed.
1117
+ */
1118
+ refuseUnofferablePlacementsBeforeWriting(skills, root) {
1119
+ try {
1120
+ refuseUnofferedPlacements(skills, hostAt(root));
1121
+ } catch (error) {
1122
+ this.error(getErrorMessage(error), { exit: EXIT_CODES.ERROR });
1123
+ }
1124
+ }
1125
+ get hasIncompleteWork() {
1126
+ return this.incompleteWork.length > 0;
1127
+ }
1128
+ /** Never aborts: every caller is past a mutation that has already landed. */
1129
+ reportIncompleteWork(what, recovery) {
1130
+ this.warn(what);
1131
+ this.recordIncompleteWork(what, recovery);
1132
+ }
1133
+ /** For a caller that has already warned. */
1134
+ recordIncompleteWork(what, recovery) {
1135
+ this.incompleteWork.push({ what, recovery });
1136
+ }
1137
+ /** Every command that records must call this; `this.exit` skips the `Error:` framing. */
1138
+ exitIfWorkIncomplete() {
1139
+ if (!this.hasIncompleteWork) return;
1140
+ this.log(`
1141
+ ${chalk.hex(CLI_COLORS.WARNING)(completedWithFailures(this.incompleteWork))}
1142
+ `);
1143
+ this.exit(EXIT_CODES.COMPLETED_WITH_FAILURES);
1144
+ }
1145
+ };
1146
+
1147
+ export {
1148
+ clearTerminalScreen,
1149
+ EXIT_CODES,
1150
+ registerExternalSkills,
1151
+ writeExternalSkills,
1152
+ carriedSkillsThatDiffer,
1153
+ readCarriedSkills,
1154
+ reconcileSharedConfig,
1155
+ withUnplaceableAssignees,
1156
+ readInstalledGlobal,
1157
+ holdInstalledGlobal,
1158
+ restoreInstalledGlobal,
1159
+ sameAssignments,
1160
+ notInstalledGlobally,
1161
+ arrivalsByScope,
1162
+ installPlanLines,
1163
+ joinedStatement,
1164
+ skillLabel,
1165
+ arrivalLines,
1166
+ installsAtHome,
1167
+ BaseCommand
1168
+ };
1169
+ //# sourceMappingURL=chunk-GEMW5RQ2.js.map