agents-inc 0.170.2 → 0.172.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/{chunk-PSUTNOQ2.js → chunk-255DGUCH.js} +2 -2
  3. package/dist/chunk-33JICQD5.js +196 -0
  4. package/dist/chunk-33JICQD5.js.map +1 -0
  5. package/dist/{chunk-3ED3KHHD.js → chunk-3R3LHA4Q.js} +2 -2
  6. package/dist/{chunk-YXKTY5WM.js → chunk-5MGRHS2G.js} +94 -58
  7. package/dist/chunk-5MGRHS2G.js.map +1 -0
  8. package/dist/{chunk-PLFANHRZ.js → chunk-5PPB6HT6.js} +2 -2
  9. package/dist/{chunk-66AEVERM.js → chunk-6CRA46YO.js} +14 -14
  10. package/dist/{chunk-ELV5WDS3.js → chunk-6LUTY35Q.js} +25 -14
  11. package/dist/chunk-6LUTY35Q.js.map +1 -0
  12. package/dist/{chunk-AUNRHWH4.js → chunk-7OG635DK.js} +26 -4
  13. package/dist/chunk-7OG635DK.js.map +1 -0
  14. package/dist/{chunk-GEMW5RQ2.js → chunk-7ZOWSCAO.js} +29 -102
  15. package/dist/chunk-7ZOWSCAO.js.map +1 -0
  16. package/dist/{chunk-YGDWHDOY.js → chunk-A3ZAXL5T.js} +4 -5
  17. package/dist/{chunk-YGDWHDOY.js.map → chunk-A3ZAXL5T.js.map} +1 -1
  18. package/dist/chunk-G7NUK3BF.js +253 -0
  19. package/dist/chunk-G7NUK3BF.js.map +1 -0
  20. package/dist/{chunk-THO7LO5B.js → chunk-KCLBLK3F.js} +3 -3
  21. package/dist/{chunk-2YA4DDRM.js → chunk-LUKDDHSI.js} +8 -6
  22. package/dist/{chunk-2YA4DDRM.js.map → chunk-LUKDDHSI.js.map} +1 -1
  23. package/dist/{chunk-OCAVW6N5.js → chunk-MBIOZ5LW.js} +12 -3
  24. package/dist/{chunk-OCAVW6N5.js.map → chunk-MBIOZ5LW.js.map} +1 -1
  25. package/dist/{chunk-7C3PFRBU.js → chunk-MHAQWMNJ.js} +35 -5
  26. package/dist/{chunk-7C3PFRBU.js.map → chunk-MHAQWMNJ.js.map} +1 -1
  27. package/dist/{chunk-HACRR422.js → chunk-MOL2J5HS.js} +2 -2
  28. package/dist/{chunk-BBDGYJHX.js → chunk-N33AKFLR.js} +2 -2
  29. package/dist/{chunk-5BNX73OU.js → chunk-NISWBQNS.js} +52 -31
  30. package/dist/chunk-NISWBQNS.js.map +1 -0
  31. package/dist/{chunk-GAKVZNTX.js → chunk-VILFCMRN.js} +420 -316
  32. package/dist/chunk-VILFCMRN.js.map +1 -0
  33. package/dist/{chunk-IWY5IR4F.js → chunk-Y23MP2OC.js} +8 -8
  34. package/dist/commands/build/marketplace.js +16 -14
  35. package/dist/commands/build/marketplace.js.map +1 -1
  36. package/dist/commands/build/plugins.js +6 -6
  37. package/dist/commands/compile.js +12 -12
  38. package/dist/commands/doctor.js +64 -81
  39. package/dist/commands/doctor.js.map +1 -1
  40. package/dist/commands/edit.js +150 -152
  41. package/dist/commands/edit.js.map +1 -1
  42. package/dist/commands/eject.js +10 -10
  43. package/dist/commands/init.js +19 -16
  44. package/dist/commands/list.js +14 -17
  45. package/dist/commands/list.js.map +1 -1
  46. package/dist/commands/new/marketplace.js +4 -4
  47. package/dist/commands/search.js +15 -13
  48. package/dist/commands/search.js.map +1 -1
  49. package/dist/commands/share.js +11 -11
  50. package/dist/commands/uninstall.js +109 -98
  51. package/dist/commands/uninstall.js.map +1 -1
  52. package/dist/commands/update.js +6 -6
  53. package/dist/components/common/scroll-affordance.js +2 -2
  54. package/dist/components/common/select-list.js +2 -2
  55. package/dist/components/common/skill-agent-summary.js +3 -4
  56. package/dist/components/common/summary-panel.js +11 -146
  57. package/dist/components/common/summary-panel.js.map +1 -1
  58. package/dist/hooks/init.js +22 -19
  59. package/dist/hooks/init.js.map +1 -1
  60. package/dist/load-agent-defs-54VIKN4T.js +14 -0
  61. package/dist/{recompile-project-agents-I33KQJFB.js → recompile-project-agents-5A44NJRD.js} +8 -8
  62. package/dist/{source-loader-TTJVW2ER.js → source-loader-LQNRI5UZ.js} +4 -4
  63. package/dist/src/agents/tester/web-tester/playbook.md +1 -1
  64. package/package.json +1 -1
  65. package/src/agents/tester/web-tester/playbook.md +1 -1
  66. package/dist/chunk-5BNX73OU.js.map +0 -1
  67. package/dist/chunk-AUNRHWH4.js.map +0 -1
  68. package/dist/chunk-ELV5WDS3.js.map +0 -1
  69. package/dist/chunk-GAKVZNTX.js.map +0 -1
  70. package/dist/chunk-GEMW5RQ2.js.map +0 -1
  71. package/dist/chunk-O4LN3HFI.js +0 -69
  72. package/dist/chunk-O4LN3HFI.js.map +0 -1
  73. package/dist/chunk-PMUZR4XO.js +0 -108
  74. package/dist/chunk-PMUZR4XO.js.map +0 -1
  75. package/dist/chunk-RIK45Q4I.js +0 -103
  76. package/dist/chunk-RIK45Q4I.js.map +0 -1
  77. package/dist/chunk-YXKTY5WM.js.map +0 -1
  78. package/dist/components/common/removal-plan-confirm.js +0 -10
  79. package/dist/load-agent-defs-6QUUMGHK.js +0 -14
  80. package/dist/source-loader-TTJVW2ER.js.map +0 -1
  81. /package/dist/{chunk-PSUTNOQ2.js.map → chunk-255DGUCH.js.map} +0 -0
  82. /package/dist/{chunk-3ED3KHHD.js.map → chunk-3R3LHA4Q.js.map} +0 -0
  83. /package/dist/{chunk-PLFANHRZ.js.map → chunk-5PPB6HT6.js.map} +0 -0
  84. /package/dist/{chunk-66AEVERM.js.map → chunk-6CRA46YO.js.map} +0 -0
  85. /package/dist/{chunk-THO7LO5B.js.map → chunk-KCLBLK3F.js.map} +0 -0
  86. /package/dist/{chunk-HACRR422.js.map → chunk-MOL2J5HS.js.map} +0 -0
  87. /package/dist/{chunk-BBDGYJHX.js.map → chunk-N33AKFLR.js.map} +0 -0
  88. /package/dist/{chunk-IWY5IR4F.js.map → chunk-Y23MP2OC.js.map} +0 -0
  89. /package/dist/{components/common/removal-plan-confirm.js.map → load-agent-defs-54VIKN4T.js.map} +0 -0
  90. /package/dist/{recompile-project-agents-I33KQJFB.js.map → recompile-project-agents-5A44NJRD.js.map} +0 -0
  91. /package/dist/{load-agent-defs-6QUUMGHK.js.map → source-loader-LQNRI5UZ.js.map} +0 -0
@@ -2,12 +2,18 @@
2
2
  import {
3
3
  BUILT_IN_MATRIX,
4
4
  getSkillById,
5
+ getSkillDisplayName,
6
+ inertLine,
5
7
  initializeMatrix,
6
8
  matrix,
7
9
  plural,
8
10
  stripTerminalControls,
9
- toTitleCase
10
- } from "./chunk-AUNRHWH4.js";
11
+ toTitleCase,
12
+ typedEntries,
13
+ typedFromEntries,
14
+ typedKeys,
15
+ typedValues
16
+ } from "./chunk-7OG635DK.js";
11
17
  import {
12
18
  AGENT_NAMES,
13
19
  AUTHOR_HANDLE_PATTERN,
@@ -76,7 +82,7 @@ import {
76
82
  seedAgentScope,
77
83
  sourceDirName,
78
84
  validateSelection
79
- } from "./chunk-7C3PFRBU.js";
85
+ } from "./chunk-MHAQWMNJ.js";
80
86
  import {
81
87
  defaultCategories,
82
88
  defaultRules,
@@ -86,7 +92,7 @@ import {
86
92
  // src/cli/lib/loading/source-loader.ts
87
93
  import os14 from "os";
88
94
  import { partition as partition4, unique as unique6 } from "remeda";
89
- import path28 from "path";
95
+ import path29 from "path";
90
96
 
91
97
  // src/cli/lib/installation/is-home-directory.ts
92
98
  import fs from "fs";
@@ -485,13 +491,15 @@ function sourceRootOf(root) {
485
491
  }
486
492
  var RETIRED_SOURCE_FOLDER = ".claude-src";
487
493
  function retiredSourceFolderIn(roots) {
488
- if (!roots.some(holdsTheRetiredSourceFolder)) return null;
489
- return sourceDirName("claude");
494
+ return [...new Set(roots)].filter(holdsTheRetiredSourceFolder);
490
495
  }
491
496
  function holdsTheRetiredSourceFolder(root) {
492
497
  return directoryExists(path.join(root, RETIRED_SOURCE_FOLDER));
493
498
  }
494
499
  var PROVIDER_OF_A_NEW_INSTALLATION = "claude";
500
+ function isTheProviderOfANewInstallation(provider) {
501
+ return provider === PROVIDER_OF_A_NEW_INSTALLATION;
502
+ }
495
503
  var providerChosenForThisRun;
496
504
  function chooseProviderForThisRun(provider) {
497
505
  providerChosenForThisRun = provider;
@@ -862,6 +870,9 @@ function warnOnce(msg) {
862
870
  sayingEachWarningOnce(() => warn(msg));
863
871
  }
864
872
 
873
+ // src/cli/utils/messages.ts
874
+ import path3 from "path";
875
+
865
876
  // src/cli/lib/validate-kebab-name.ts
866
877
  import { unique } from "remeda";
867
878
  var KEBAB_CASE_CHARACTER = /[a-z0-9-]/;
@@ -974,9 +985,9 @@ function completedWithFailures(failures) {
974
985
  function failureLines({ what, recovery }) {
975
986
  return [` ${what}`, ` ${recovery}`];
976
987
  }
977
- function propagatedRecompileSummary(rewritten, unchanged, failed) {
988
+ function propagatedRecompileSummary(recompiled, failed) {
978
989
  const failureSuffix = failed > 0 ? ` (${failed} failed)` : "";
979
- return `Registered projects: ${rewritten} rewritten, ${unchanged} unchanged${failureSuffix}`;
990
+ return `Registered projects: ${recompiled} recompiled${failureSuffix}`;
980
991
  }
981
992
  function skillAssignedToNoAgent(skillId) {
982
993
  return `Skill '${skillId}' is assigned to no sub-agent \u2014 nothing will load it.`;
@@ -1133,22 +1144,25 @@ function sharedSkillsLeftBehind(skillIds) {
1133
1144
  function sharedConfigExistingInstall(configPath, id, provider) {
1134
1145
  return `An installation already exists at ${configPath}. Run '${CLI_INVOKE_COMMAND} uninstall${providerArgument(provider)}' first \u2014 ${SHARED_CONFIG_GREENFIELD_HINT}. ${applySharedConfigHere(id, true, provider)}`;
1135
1146
  }
1136
- function sharedConfigProjectScopeAtHome(skillIds, agentNames) {
1147
+ function sharedConfigProjectScopeAtHome(skillIds, agentNames, installedHere, provider) {
1137
1148
  return [
1138
1149
  "This configuration cannot be installed here: the home directory is the global scope, and a global installation holds only global-scoped content \u2014 so these project-scoped entries have nowhere to be written:",
1139
1150
  ...skillIds.map((id) => ` skill ${id} (scope: project)`),
1140
1151
  ...agentNames.map((name) => ` sub-agent ${name} (scope: project)`),
1141
- "Run this from inside a project directory, or re-share it with each entry above at global scope."
1152
+ installedHere ? changeTheGlobalInstallFromHome(providerArgument(provider)) : "Run this from inside a project directory, or re-share it with each entry above at global scope."
1142
1153
  ].join("\n");
1143
1154
  }
1155
+ function changeTheGlobalInstallFromHome(named) {
1156
+ return `Run this from inside a project directory, or change the global install from here: run '${CLI_INVOKE_COMMAND} edit${named}', make the change in the editor, then apply the id it gives you with '${CLI_INVOKE_COMMAND} edit --from <id>${named}'.`;
1157
+ }
1144
1158
  function unknownKeys(keys) {
1145
- return keys.map(stripTerminalControls).join(", ");
1159
+ return keys.map(inertLine).join(", ");
1146
1160
  }
1147
- function skippedUnknownSkills(skillIds, skippedWith) {
1161
+ function skippedUnknownSkills(skillIds, installedWithout) {
1148
1162
  return [
1149
1163
  `Skipped ${skillIds.length} skill(s) this catalog does not know: ${unknownKeys(skillIds)}`,
1150
- ...skippedWith.map(
1151
- ({ skillId, agentNames }) => ` sub-agent(s) skipped with ${unknownKeys([skillId])}, carrying nothing else this catalog knows: ${agentNames.join(", ")}`
1164
+ ...installedWithout.map(
1165
+ ({ skillId, agentNames }) => ` sub-agent(s) installed without ${unknownKeys([skillId])}, carrying nothing else this catalog knows: ${agentNames.join(", ")}`
1152
1166
  )
1153
1167
  ].join("\n");
1154
1168
  }
@@ -1159,30 +1173,18 @@ function carriedSkillsWritten(skillIds) {
1159
1173
  return `Wrote ${skillIds.length} skill(s) this configuration carries: ${skillIds.join(", ")}`;
1160
1174
  }
1161
1175
  var SHARED_CONFIG_APPLY = {
1162
- PREVIEW_HEADING: "Applying this configuration will remove:",
1163
- SKILLS_HEADING: "Skills:",
1164
- AGENTS_HEADING: "Sub-agents:",
1165
- NOTHING_REMOVED: "Nothing is removed \u2014 this configuration only adds and re-tunes.",
1166
- OVERWRITTEN_HEADING: "Will be overwritten:",
1167
1176
  CONFIRM: "Apply this configuration?"
1168
1177
  };
1169
- function projectCopyRemoved(subject) {
1170
- return `${subject}: this project's copy is removed; the global one takes over`;
1171
- }
1172
- function projectCopyMovedToGlobal(subject) {
1173
- return `${subject}: moves to the global install, so this project's copy is removed`;
1174
- }
1175
- function registeredProjectsRewritten(projectPaths) {
1178
+ function registeredProjectsRecompiled(projectPaths) {
1176
1179
  return [
1177
- "Each project registered with this global install is rewritten to match:",
1180
+ "Each project registered with this global install is recompiled to match:",
1178
1181
  ...projectPaths.map((projectPath) => ` ${projectPath}`)
1179
1182
  ].join("\n");
1180
1183
  }
1181
- function sharedConfigNeedsTerminal(id, provider) {
1182
- const named = providerArgument(provider);
1184
+ function sharedConfigNeedsTerminal(id, editProvider, initProvider) {
1183
1185
  return [
1184
1186
  `Applying configuration '${id}' removes whatever it leaves out, so it has to be confirmed \u2014 and there is no terminal here to confirm it at.`,
1185
- `Run '${CLI_INVOKE_COMMAND} edit --from ${id}${named}' from a terminal, or '${CLI_INVOKE_COMMAND} init --from ${id}${named}' in a clean directory, which installs without removing anything.`
1187
+ `Run '${CLI_INVOKE_COMMAND} edit --from ${id}${providerArgument(editProvider)}' from a terminal, or '${CLI_INVOKE_COMMAND} init --from ${id}${providerArgument(initProvider)}' in a clean directory, which installs without removing anything.`
1186
1188
  ].join("\n");
1187
1189
  }
1188
1190
  function nothingInstalledToApplyTo(id, provider) {
@@ -1199,21 +1201,38 @@ function addMarketplaceInTheEditor(ref, isFolder) {
1199
1201
  return isFolder ? `${ref} is a folder on this machine, which the editor cannot read \u2014 push it to GitHub, then add it from the editor's Marketplace button.` : `Add ${ref} from the editor's Marketplace button.`;
1200
1202
  }
1201
1203
  var SHARED_CONFIG_ARRIVALS = {
1202
- PROJECT_HEADING: "Into this project:",
1203
- GLOBAL_HEADING: "Into the global install:",
1204
- SKIPPED_HEADING: "Skipped, already in the global install:",
1205
1204
  INSTALL_CONFIRM: "Install this configuration?"
1206
1205
  };
1207
- function keptAsInstalledGlobally(skillIds, agentNames, idAppliedAtHome) {
1206
+ function keptAsInstalledGlobally(skillIds, agentNames, idAppliedAtHome, provider) {
1208
1207
  return [
1209
1208
  "Kept as installed in the global install \u2014 this configuration states them differently or leaves them out, and a project run only adds to the global install:",
1210
1209
  ...skillIds.map((id) => ` skill ${id}`),
1211
1210
  ...agentNames.map((name) => ` sub-agent ${name}`),
1212
1211
  ...idAppliedAtHome === void 0 ? [] : [
1213
- `Change them with '${CLI_INVOKE_COMMAND} edit --from ${idAppliedAtHome}' from your home directory.`
1212
+ `Change them with '${CLI_INVOKE_COMMAND} edit --from ${idAppliedAtHome}${providerArgument(provider)}' from your home directory.`
1214
1213
  ]
1215
1214
  ].join("\n");
1216
1215
  }
1216
+ function skillsMoved(verb, skillIds) {
1217
+ return skillIds.length === 0 ? [] : [`${verb} ${listed(skillIds.map(getSkillDisplayName))}`];
1218
+ }
1219
+ function settingMoved({ setting, from, to }) {
1220
+ return `${setting} ${from ?? "default"} \u2192 ${to ?? "default"}`;
1221
+ }
1222
+ function listed(names) {
1223
+ if (names.length <= 1) return names.join("");
1224
+ return `${names.slice(0, -1).join(", ")} and ${names.at(-1)}`;
1225
+ }
1226
+ var SKILLS_REASSIGNED = "skills reassigned";
1227
+ function subAgentChanged({ name, scope, gains, loses, settings }) {
1228
+ const details = [
1229
+ ...skillsMoved("gains", gains),
1230
+ ...skillsMoved("loses", loses),
1231
+ ...settings.map(settingMoved)
1232
+ ];
1233
+ const where = scope === "global" ? " (global)" : "";
1234
+ return `~ ${name}${where}: ${details.length > 0 ? details.join(", ") : SKILLS_REASSIGNED}`;
1235
+ }
1217
1236
  function addedToGlobalSubAgents(joined) {
1218
1237
  return [
1219
1238
  "Added to sub-agents in the global install \u2014 each keeps everything it already has, since a project run only adds to the global install:",
@@ -1260,17 +1279,18 @@ function uninstallSkillMetadataUnreadable(metadataPath) {
1260
1279
  }
1261
1280
  function recreateConfigFrom({
1262
1281
  scopeRoot,
1263
- providerToName
1282
+ providerToName,
1283
+ initProviderToName
1264
1284
  }) {
1265
- const named = providerArgument(providerToName);
1266
- return `'${CLI_INVOKE_COMMAND} uninstall${named}' from '${scopeRoot}', which still works on a config it cannot read, then '${CLI_INVOKE_COMMAND} init --from <id>${named}' with an id from ${EDITOR_URL}`;
1285
+ return `'${CLI_INVOKE_COMMAND} uninstall${providerArgument(providerToName)}' from '${scopeRoot}', which still works on a config it cannot read, then '${CLI_INVOKE_COMMAND} init --from <id>${providerArgument(initProviderToName)}' with an id from ${EDITOR_URL}`;
1267
1286
  }
1268
1287
  function providerArgument(provider) {
1269
- return provider === void 0 ? "" : ` --provider ${provider}`;
1288
+ if (provider === void 0) return "";
1289
+ return ` --provider ${typeof provider === "string" ? provider : `<${provider.join("|")}>`}`;
1270
1290
  }
1271
- function configUnreadableError(configLoadFailure2) {
1291
+ function configUnreadableError(configLoadFailure3) {
1272
1292
  return [
1273
- configLoadFailure2,
1293
+ configLoadFailure3,
1274
1294
  "This is the marketplace's own config rather than an installation's, so it is fixed in the marketplace repository it comes from."
1275
1295
  ].join("\n");
1276
1296
  }
@@ -1281,17 +1301,8 @@ function unmarkedAgentsKept(agentsDir2, count) {
1281
1301
  const object = count === 1 ? "it" : "them";
1282
1302
  return `Kept ${plural(count, "agent")} in ${agentsDir2}/ \u2014 no ${DEFAULT_PLUGIN_NAME} marker, so this CLI did not compile ${object}.`;
1283
1303
  }
1284
- var UNINSTALL_PLAN = {
1285
- PREVIEW_HEADING: "The following will be removed:",
1286
- PLUGINS_HEADING: "Plugins:",
1287
- CLI_MANAGED_FILES_HEADING: "CLI-managed files:",
1288
- CONFIG_HEADING: "Config:"
1289
- };
1290
- function localSkillsRemoval(skillsDir2) {
1291
- return `${skillsDir2}/ (matching the marketplace)`;
1292
- }
1293
- function compiledAgentsRemoval(agentsDir2) {
1294
- return `${agentsDir2}/ (CLI-compiled)`;
1304
+ function configFileRemoved(filePath) {
1305
+ return `- ${filePath}`;
1295
1306
  }
1296
1307
  function scopeNoun(kind) {
1297
1308
  return kind === "project" ? "this project" : "the global installation";
@@ -1305,8 +1316,8 @@ function sentenceCase(text) {
1305
1316
  function layoutIsCurrent(label, to) {
1306
1317
  return `${label} is on ${to}/`;
1307
1318
  }
1308
- function retiredSourceFolderUnsupported(replacement) {
1309
- return `${RETIRED_SOURCE_FOLDER}/ is no longer supported (agents-inc now serves Claude and Codex). Move it to ${replacement}/.`;
1319
+ function retiredSourceFolderUnsupported(root) {
1320
+ return `${path3.join(root, RETIRED_SOURCE_FOLDER)}/ is no longer supported (agents-inc now serves Claude and Codex). Move it to ${path3.join(root, sourceDirName("claude"))}/.`;
1310
1321
  }
1311
1322
 
1312
1323
  // src/cli/types/generated/source-types.ts
@@ -1930,24 +1941,10 @@ function isSkillAssignment(value) {
1930
1941
  return typeof value.id === "string";
1931
1942
  }
1932
1943
 
1933
- // src/cli/utils/typed-object.ts
1934
- function typedEntries(obj) {
1935
- return Object.entries(obj);
1936
- }
1937
- function typedKeys(obj) {
1938
- return Object.keys(obj);
1939
- }
1940
- function typedFromEntries(entries) {
1941
- return Object.fromEntries(entries);
1942
- }
1943
- function typedValues(obj) {
1944
- return Object.values(obj).filter((value) => value !== void 0);
1945
- }
1946
-
1947
1944
  // src/cli/lib/configuration/config.ts
1948
1945
  import { BlockList, isIPv4 } from "net";
1949
1946
  import os4 from "os";
1950
- import path5 from "path";
1947
+ import path6 from "path";
1951
1948
  import { z as z3 } from "zod";
1952
1949
 
1953
1950
  // src/cli/lib/schemas.ts
@@ -1957,11 +1954,11 @@ import { partition } from "remeda";
1957
1954
  // src/cli/lib/schema-validator.ts
1958
1955
  import "zod";
1959
1956
  function formatZodIssue(issue) {
1960
- const path29 = stripTerminalControls(issue.path.join("."));
1957
+ const path30 = stripTerminalControls(issue.path.join("."));
1961
1958
  if (issue.code === "unrecognized_keys") {
1962
1959
  return `Unrecognized key: "${stripTerminalControls(issue.keys.join('", "'))}"`;
1963
1960
  }
1964
- return path29 ? `${path29}: ${stripTerminalControls(issue.message)}` : stripTerminalControls(issue.message);
1961
+ return path30 ? `${path30}: ${stripTerminalControls(issue.message)}` : stripTerminalControls(issue.message);
1965
1962
  }
1966
1963
  function formatZodErrors(error) {
1967
1964
  return error.issues.map(formatZodIssue);
@@ -2103,12 +2100,12 @@ var RENAMED_SKILL_ENTRY_FIELDS = { source: "origin" };
2103
2100
  function renamedFieldMessage(oldKey, newKey) {
2104
2101
  return `"${oldKey}" was renamed to "${newKey}". Rename the key in this config \u2014 the CLI does not read "${oldKey}" any more and will not fall back to it.`;
2105
2102
  }
2106
- function reportRenamedKeys(record, renames, path29, ctx) {
2103
+ function reportRenamedKeys(record, renames, path30, ctx) {
2107
2104
  for (const [oldKey, newKey] of Object.entries(renames)) {
2108
2105
  if (!(oldKey in record)) continue;
2109
2106
  ctx.addIssue({
2110
2107
  code: "custom",
2111
- path: [...path29, oldKey],
2108
+ path: [...path30, oldKey],
2112
2109
  message: renamedFieldMessage(oldKey, newKey)
2113
2110
  });
2114
2111
  }
@@ -2426,9 +2423,16 @@ var stackConfigValidationSchema = z2.object({
2426
2423
  function formatZodIssues(issues) {
2427
2424
  return issues.map(formatZodIssue).join("; ");
2428
2425
  }
2426
+ var carriedMetadataValidationSchema = customMetadataValidationSchema.extend({
2427
+ displayName: z2.string().min(1),
2428
+ slug: z2.string().regex(KEBAB_CASE_PATTERN).min(1)
2429
+ });
2429
2430
  function validateSkillMetadata(rawMetadata) {
2430
- const schema = isCustomMetadata(rawMetadata) ? customMetadataValidationSchema : metadataValidationSchema;
2431
- return schema.safeParse(rawMetadata);
2431
+ return metadataSchemaFor(rawMetadata).safeParse(rawMetadata);
2432
+ }
2433
+ function metadataSchemaFor(raw) {
2434
+ if (!isCustomMetadata(raw)) return metadataValidationSchema;
2435
+ return isCarriedMetadata(raw) ? carriedMetadataValidationSchema : customMetadataValidationSchema;
2432
2436
  }
2433
2437
  function describeMetadataSchemaFailure(issues, rawMetadata) {
2434
2438
  const [absent, malformed] = partition(issues, (issue) => isAbsentField(issue, rawMetadata));
@@ -2479,6 +2483,9 @@ function validateNestingDepth(value, maxDepth) {
2479
2483
  function isCustomMetadata(raw) {
2480
2484
  return isRecord(raw) && raw.custom === true;
2481
2485
  }
2486
+ function isCarriedMetadata(raw) {
2487
+ return isRecord(raw) && isRecord(raw.forkedFrom) && typeof raw.forkedFrom.path === "string";
2488
+ }
2482
2489
  function warnUnknownFields(parsed, expectedKeys, context) {
2483
2490
  const expectedSet = new Set(expectedKeys);
2484
2491
  const unknownKeys2 = Object.keys(parsed).filter((k) => !expectedSet.has(k));
@@ -2488,11 +2495,11 @@ function warnUnknownFields(parsed, expectedKeys, context) {
2488
2495
  }
2489
2496
 
2490
2497
  // src/cli/lib/configuration/config-loader.ts
2491
- import path3 from "path";
2498
+ import path4 from "path";
2492
2499
  import { fileURLToPath } from "url";
2493
2500
  import { createJiti } from "jiti";
2494
- var CONFIG_EXPORTS_PATH = path3.resolve(
2495
- path3.dirname(fileURLToPath(import.meta.url)),
2501
+ var CONFIG_EXPORTS_PATH = path4.resolve(
2502
+ path4.dirname(fileURLToPath(import.meta.url)),
2496
2503
  "../../config-exports.ts"
2497
2504
  );
2498
2505
  var ConfigSchemaError = class extends Error {
@@ -2515,13 +2522,16 @@ var ConfigDefaultExportError = class extends Error {
2515
2522
  };
2516
2523
  var ES_MODULE_MARKER = "__esModule";
2517
2524
  function unwrapDefaultExport(namespace) {
2518
- if (isRecord(namespace) && namespace.default !== void 0) return namespace.default;
2525
+ if (isRecord(namespace) && "default" in namespace) return namespace.default;
2519
2526
  return namespace;
2520
2527
  }
2521
2528
  function declaresNoDefaultExport(namespace) {
2522
2529
  return isRecord(namespace) && ES_MODULE_MARKER in namespace && !("default" in namespace);
2523
2530
  }
2524
2531
  var NO_VALID_DEFAULT_EXPORT = "the file has no valid default export";
2532
+ function configLoadFailure(configPath, reason) {
2533
+ return `Failed to load config from '${configPath}': ${reason}`;
2534
+ }
2525
2535
  function exportsNothing(value) {
2526
2536
  return value == null || typeof value === "object" && Object.keys(value).length === 0;
2527
2537
  }
@@ -2557,16 +2567,14 @@ async function importConfigModule(configPath) {
2557
2567
  });
2558
2568
  return await jiti.import(configPath);
2559
2569
  } catch (error) {
2560
- throw new Error(`Failed to load config from '${configPath}': ${getErrorMessage(error)}`, {
2561
- cause: error
2562
- });
2570
+ throw new Error(configLoadFailure(configPath, getErrorMessage(error)), { cause: error });
2563
2571
  }
2564
2572
  }
2565
2573
 
2566
2574
  // src/cli/lib/installation/install-base-dir.ts
2567
- import path4 from "path";
2575
+ import path5 from "path";
2568
2576
  function getProjectConfigPath(dir, provider) {
2569
- return path4.join(sourceFolderInUse(dir, provider).dir, STANDARD_FILES.CONFIG_TS);
2577
+ return path5.join(sourceFolderInUse(dir, provider).dir, STANDARD_FILES.CONFIG_TS);
2570
2578
  }
2571
2579
  function getInstalledConfigPath(dir) {
2572
2580
  return getProjectConfigPath(dir, providerInUse(dir));
@@ -2589,6 +2597,9 @@ function providerToNameAt(root, provider) {
2589
2597
  );
2590
2598
  return besideAnother ? provider : void 0;
2591
2599
  }
2600
+ function initProviderToNameAt(root, provider) {
2601
+ return providerToNameAt(root, provider) ?? (isTheProviderOfANewInstallation(provider) ? void 0 : provider);
2602
+ }
2592
2603
 
2593
2604
  // src/cli/lib/configuration/config.ts
2594
2605
  var SOURCE_ENV_VAR = "CC_MARKETPLACE";
@@ -2613,8 +2624,11 @@ async function loadSourceConfig(dir, scope, provider) {
2613
2624
  }
2614
2625
  function refuseInstallationConfig(configPath, scope, scopeRoot, provider) {
2615
2626
  const providerToName = providerToNameAt(scopeRoot, provider);
2627
+ const initProviderToName = initProviderToNameAt(scopeRoot, provider);
2616
2628
  return (reason, cause) => new Error(
2617
- installationConfigsUnreadable([{ configPath, reason, scope, scopeRoot, providerToName }]),
2629
+ installationConfigsUnreadable([
2630
+ { configPath, reason, scope, scopeRoot, providerToName, initProviderToName }
2631
+ ]),
2618
2632
  { cause }
2619
2633
  );
2620
2634
  }
@@ -2638,7 +2652,7 @@ function describesItsOwnFault(error) {
2638
2652
  }
2639
2653
  var unreadableSourceRepoConfig = (reason, cause) => new Error(configUnreadableError(reason), { cause });
2640
2654
  async function readSourceRepoConfig(basePath) {
2641
- const configPath = path5.join(basePath, SOURCE_ROOT_DIR, STANDARD_FILES.CONFIG_TS);
2655
+ const configPath = path6.join(basePath, SOURCE_ROOT_DIR, STANDARD_FILES.CONFIG_TS);
2642
2656
  if (!await fileExists(configPath)) {
2643
2657
  verbose(`Source config not found at ${configPath}`);
2644
2658
  return { kind: "absent" };
@@ -2902,6 +2916,7 @@ function validateSourceFormat(source, carrier) {
2902
2916
  function validateRemoteSource(source, protocol, carrier) {
2903
2917
  const isHttp = protocol === "https://" || protocol === "http://";
2904
2918
  if (isHttp) refuseCredentials(source, protocol, carrier);
2919
+ refuseLineBreaksAndControls(source, carrier, REMOTE_EXAMPLES);
2905
2920
  const pathAfterProtocol = source.slice(protocol.length).trim();
2906
2921
  if (pathAfterProtocol.length < MIN_REMOTE_PATH_LENGTH) {
2907
2922
  throw refusal(
@@ -3041,16 +3056,18 @@ function validateGitShorthand(source, repoPath, carrier) {
3041
3056
  );
3042
3057
  }
3043
3058
  }
3059
+ function refuseLineBreaksAndControls(source, carrier, examples) {
3060
+ const inert = inertLine(source);
3061
+ if (inert === source) return;
3062
+ throw refusal(
3063
+ carrier,
3064
+ `contains invalid characters: "${inert}"`,
3065
+ ["Marketplace values must not contain line breaks or control characters."],
3066
+ examples
3067
+ );
3068
+ }
3044
3069
  function validateLocalPath(source, carrier) {
3045
- const CONTROL_CHAR_PATTERN2 = /[\x00-\x08\x0E-\x1F\x7F]/u;
3046
- if (CONTROL_CHAR_PATTERN2.test(source)) {
3047
- throw refusal(
3048
- carrier,
3049
- `contains invalid characters: "${source}"`,
3050
- ["Marketplace paths must not contain control characters."],
3051
- LOCAL_EXAMPLES
3052
- );
3053
- }
3070
+ refuseLineBreaksAndControls(source, carrier, LOCAL_EXAMPLES);
3054
3071
  if (UNC_PATH_PATTERN.test(source)) {
3055
3072
  throw refusal(
3056
3073
  carrier,
@@ -3068,7 +3085,7 @@ function isDefaultSource(source) {
3068
3085
  }
3069
3086
  var packageIdentitySchema = z3.object({ name: z3.string() });
3070
3087
  async function isPublicCatalogueCheckout(basePath) {
3071
- const raw = await readFileOptional(path5.join(basePath, STANDARD_FILES.PACKAGE_JSON));
3088
+ const raw = await readFileOptional(path6.join(basePath, STANDARD_FILES.PACKAGE_JSON));
3072
3089
  return declaredPackageName(raw) === PUBLIC_CATALOGUE_PACKAGE;
3073
3090
  }
3074
3091
  function declaredPackageName(raw) {
@@ -3099,7 +3116,10 @@ function isLocalSource(source) {
3099
3116
  }
3100
3117
 
3101
3118
  // src/cli/lib/compile-seat.ts
3102
- seatDiagnostics({ warn, verbose });
3119
+ seatDiagnostics({
3120
+ warn: (message) => warn(inertLine(message)),
3121
+ verbose: (message) => verbose(inertLine(message))
3122
+ });
3103
3123
 
3104
3124
  // ../compile/src/seed-to-config.ts
3105
3125
  import { groupBy, indexBy, isDefined, mapValues, omit as omit2, partition as partition2 } from "remeda";
@@ -3273,6 +3293,10 @@ var HAND_WRITTEN_FIELDS = [
3273
3293
  "categoriesFile",
3274
3294
  "rulesFile"
3275
3295
  ];
3296
+ var GLOBAL_IDENTITY_FIELDS = [
3297
+ ...HAND_WRITTEN_FIELDS,
3298
+ "description"
3299
+ ];
3276
3300
  var EXTRACTED_FIELDS = /* @__PURE__ */ new Set([
3277
3301
  "skills",
3278
3302
  "agents",
@@ -3560,7 +3584,7 @@ function stackOfAgents(stack, agents) {
3560
3584
  function mergeInlinedScalarFields(cleaned, cleanedGlobal) {
3561
3585
  return Object.entries(
3562
3586
  canonicalizeFieldOrder({
3563
- ...omit(cleanedGlobal, HAND_WRITTEN_FIELDS),
3587
+ ...omit(cleanedGlobal, GLOBAL_IDENTITY_FIELDS),
3564
3588
  ...cleaned
3565
3589
  })
3566
3590
  ).filter(([key]) => isCopiedScalarField(key));
@@ -4008,9 +4032,9 @@ function seedToInstallSelection(payload, catalog) {
4008
4032
  const rows = installable.flatMap((skill) => assignmentRows(skill, agentMap));
4009
4033
  refuseUnwritableAssignments(rows);
4010
4034
  const placed = rows.filter((row) => row.kind === "placed");
4011
- const selectedAgents = selectedAgentsOf(placed, agentMap);
4035
+ const carried = new Set(agentsCarriedIn(placed, agentMap));
4036
+ const selectedAgents = withAgentsSkippedSkillsName(carried, skipped, agentMap);
4012
4037
  const skills = installable.map(toSeedSkillConfig);
4013
- const selected = new Set(selectedAgents);
4014
4038
  return {
4015
4039
  result: {
4016
4040
  skills,
@@ -4036,7 +4060,7 @@ function seedToInstallSelection(payload, catalog) {
4036
4060
  skippedSkillIds: skipped.map(({ skillId }) => skillId),
4037
4061
  skippedAgentNames: skippedAgentNamesOf(agentMap, rows),
4038
4062
  agentsOnlySkippedSkillsCarried: skipped.flatMap(
4039
- (skill) => agentsOnlyItCarried(skill, agentMap, selected)
4063
+ (skill) => agentsOnlyItCarried(skill, agentMap, carried)
4040
4064
  )
4041
4065
  };
4042
4066
  }
@@ -4046,11 +4070,14 @@ function refuseUnwritableAssignments(rows) {
4046
4070
  throw new Error(unwritableAssignmentsError(unwritable));
4047
4071
  }
4048
4072
  }
4049
- function selectedAgentsOf(placed, agentMap) {
4073
+ function agentsCarriedIn(placed, agentMap) {
4074
+ return [...placed.map((row) => row.agent), ...agentsSwitchedOn(agentMap)];
4075
+ }
4076
+ function withAgentsSkippedSkillsName(carried, skipped, agentMap) {
4050
4077
  return [
4051
4078
  .../* @__PURE__ */ new Set([
4052
- ...placed.map((row) => row.agent),
4053
- ...agentsSwitchedOn(agentMap)
4079
+ ...carried,
4080
+ ...skipped.flatMap((skill) => agentsNamedBy(skill, agentMap))
4054
4081
  ])
4055
4082
  ];
4056
4083
  }
@@ -4058,9 +4085,14 @@ function skippedAgentNamesOf(agentMap, rows) {
4058
4085
  const unknownAssignees = rows.filter((row) => row.kind === "unknownAgent").map((row) => row.agentName);
4059
4086
  return [.../* @__PURE__ */ new Set([...agentMap.unknown, ...unknownAssignees])];
4060
4087
  }
4061
- function agentsOnlyItCarried({ skillId, seedSkill }, agentMap, selected) {
4062
- const agentNames = Object.keys(seedSkill.assignments).filter(isAgentName2).filter((name) => !agentMap.switchedOff.has(name) && !selected.has(name));
4063
- return agentNames.length === 0 ? [] : [{ skillId, agentNames }];
4088
+ function agentsNamedBy({ seedSkill }, agentMap) {
4089
+ return Object.keys(seedSkill.assignments).filter(isAgentName2).filter((name) => !agentMap.switchedOff.has(name));
4090
+ }
4091
+ function agentsOnlyItCarried(skill, agentMap, carried) {
4092
+ const agentNames = agentsNamedBy(skill, agentMap).filter(
4093
+ (name) => !carried.has(name)
4094
+ );
4095
+ return agentNames.length === 0 ? [] : [{ skillId: skill.skillId, agentNames }];
4064
4096
  }
4065
4097
  function readAgentMap(agents) {
4066
4098
  const known = /* @__PURE__ */ new Map();
@@ -4178,10 +4210,10 @@ import { indexBy as indexBy2, omit as omit3, pick, uniqueBy as uniqueBy2 } from
4178
4210
 
4179
4211
  // src/cli/lib/configuration/project-config.ts
4180
4212
  import os5 from "os";
4181
- import path7 from "path";
4213
+ import path8 from "path";
4182
4214
 
4183
4215
  // src/cli/lib/stacks/stacks-loader.ts
4184
- import path6 from "path";
4216
+ import path7 from "path";
4185
4217
  import { flatMap, groupBy as groupBy2, mapValues as mapValues2, partition as partition3, pipe, unique as unique2, uniqueBy } from "remeda";
4186
4218
  var stacksCache = /* @__PURE__ */ new Map();
4187
4219
  function normalizeAgentConfig(agentConfig) {
@@ -4241,7 +4273,7 @@ async function loadStacks(configDir, stacksFile) {
4241
4273
  const cacheKey = `${configDir}:${resolvedStacksFile}`;
4242
4274
  const cached = stacksCache.get(cacheKey);
4243
4275
  if (cached) return cached;
4244
- const stacksPath = path6.join(configDir, resolvedStacksFile);
4276
+ const stacksPath = path7.join(configDir, resolvedStacksFile);
4245
4277
  try {
4246
4278
  const raw = await loadConfig(stacksPath, stacksConfigSchema);
4247
4279
  if (raw == null) {
@@ -4287,7 +4319,7 @@ async function loadStackById(stackId, configDir, source) {
4287
4319
  return defaultStack;
4288
4320
  }
4289
4321
  function stackNotOfferedMessage(stackId, source) {
4290
- return `Stack '${stackId}' is not a stack the source '${source}' offers. A marketplace ships its own stacks; the CLI's built-in ones belong to the default public marketplace alone.`;
4322
+ return `Stack '${inertLine(stackId)}' is not a stack the source '${source}' offers. A marketplace ships its own stacks; the CLI's built-in ones belong to the default public marketplace alone.`;
4291
4323
  }
4292
4324
  function defaultUsageGuidance(category) {
4293
4325
  return `Use when working with ${category}.`;
@@ -4327,6 +4359,7 @@ var ConfigLoadError = class extends Error {
4327
4359
  this.name = "ConfigLoadError";
4328
4360
  this.scope = isHomeDirectory(scopeRoot) ? "global" : "project";
4329
4361
  this.providerToName = providerToNameAt(scopeRoot, provider);
4362
+ this.initProviderToName = initProviderToNameAt(scopeRoot, provider);
4330
4363
  }
4331
4364
  configPath;
4332
4365
  reason;
@@ -4334,6 +4367,8 @@ var ConfigLoadError = class extends Error {
4334
4367
  scope;
4335
4368
  /** Named with `--provider` in a remedy handed out for this installation; beside another only. */
4336
4369
  providerToName;
4370
+ /** Named in the remedy's `init --from` half, which a bare run would answer with another one. */
4371
+ initProviderToName;
4337
4372
  };
4338
4373
  async function loadProjectConfigFromDir(projectDir, provider) {
4339
4374
  return loadProjectConfigAt(getProjectConfigPath(projectDir, provider), projectDir, provider);
@@ -4372,7 +4407,7 @@ function asProjectConfig(parsed, projectDir) {
4372
4407
  const normalizedStack = parsed.stack ? normalizeStackRecord(parsed.stack) : void 0;
4373
4408
  return {
4374
4409
  ...parsed,
4375
- name: parsed.name ?? path7.basename(projectDir),
4410
+ name: parsed.name ?? path8.basename(projectDir),
4376
4411
  skills: parsed.skills ?? [],
4377
4412
  agents: parsed.agents ?? [],
4378
4413
  ...normalizedStack && { stack: normalizedStack }
@@ -4395,10 +4430,10 @@ function configDirsInPlay(projectDir) {
4395
4430
  return isHomeDirectory(projectDir) ? [projectDir] : [projectDir, os5.homedir()];
4396
4431
  }
4397
4432
  async function findConfigLoadFailures(projectDir) {
4398
- const outcomes = await Promise.all(configDirsInPlay(projectDir).map(configLoadFailure));
4433
+ const outcomes = await Promise.all(configDirsInPlay(projectDir).map(configLoadFailure2));
4399
4434
  return outcomes.filter((failure) => failure !== void 0);
4400
4435
  }
4401
- async function configLoadFailure(dir) {
4436
+ async function configLoadFailure2(dir) {
4402
4437
  try {
4403
4438
  await loadInstalledConfig(dir);
4404
4439
  return void 0;
@@ -4499,7 +4534,6 @@ function mergeConfigs(newConfig, existingConfig, options) {
4499
4534
  }
4500
4535
  return merged;
4501
4536
  }
4502
- var GLOBAL_IDENTITY_FIELDS = [...HAND_WRITTEN_FIELDS, "description"];
4503
4537
  function existingConfigForMerge(loaded, projectDir, ownName) {
4504
4538
  const isProjectsOwnFile = loaded.configPath === getProjectConfigPath(projectDir, loaded.provider);
4505
4539
  if (isProjectsOwnFile) return loaded.config;
@@ -4521,13 +4555,13 @@ async function mergeWithExistingConfig(newConfig, context) {
4521
4555
  }
4522
4556
 
4523
4557
  // src/cli/lib/skills/skill-metadata.ts
4524
- import path9 from "path";
4558
+ import path10 from "path";
4525
4559
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
4526
4560
 
4527
4561
  // src/cli/lib/versioning.ts
4528
4562
  import { createHash } from "crypto";
4529
4563
  import { readFile as readFileBytes } from "fs/promises";
4530
- import path8 from "path";
4564
+ import path9 from "path";
4531
4565
  var ISO_DATE_LENGTH = 10;
4532
4566
  function getCurrentDate() {
4533
4567
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, ISO_DATE_LENGTH);
@@ -4544,18 +4578,18 @@ async function computeFileHash(filePath) {
4544
4578
  async function computeSkillFolderHash(skillPath) {
4545
4579
  const contents = [];
4546
4580
  for (const fileName of SKILL_CONTENT_FILES) {
4547
- const filePath = path8.join(skillPath, fileName);
4581
+ const filePath = path9.join(skillPath, fileName);
4548
4582
  if (await fileExists(filePath)) {
4549
4583
  const content = await readFile(filePath);
4550
4584
  contents.push(`${fileName}:${content}`);
4551
4585
  }
4552
4586
  }
4553
4587
  for (const dirName of SKILL_CONTENT_DIRS) {
4554
- const dirPath = path8.join(skillPath, dirName);
4588
+ const dirPath = path9.join(skillPath, dirName);
4555
4589
  if (await fileExists(dirPath)) {
4556
4590
  const files = await glob("**/*", dirPath);
4557
4591
  for (const file of files.sort()) {
4558
- const filePath = path8.join(dirPath, file);
4592
+ const filePath = path9.join(dirPath, file);
4559
4593
  const content = await readFile(filePath);
4560
4594
  contents.push(`${dirName}/${file}:${content}`);
4561
4595
  }
@@ -4576,21 +4610,6 @@ function bumpMajorVersion(version) {
4576
4610
  const major = parseMajorVersion(version);
4577
4611
  return `${major + 1}.0.0`;
4578
4612
  }
4579
- async function readExistingPluginManifest(pluginDir, getManifestPath) {
4580
- const manifestPath = getManifestPath(pluginDir);
4581
- if (!await fileExists(manifestPath)) {
4582
- return null;
4583
- }
4584
- try {
4585
- const version = await readManifestVersion(manifestPath);
4586
- const hashFilePath = contentHashFileBeside(manifestPath);
4587
- const contentHash = await fileExists(hashFilePath) ? (await readFile(hashFilePath)).trim() : void 0;
4588
- return { version, contentHash };
4589
- } catch (error) {
4590
- warn(`Failed to read plugin manifest at '${manifestPath}': ${getErrorMessage(error)}`);
4591
- return null;
4592
- }
4593
- }
4594
4613
  async function readManifestVersion(manifestPath) {
4595
4614
  const content = await readFileSafe(manifestPath, MAX_PLUGIN_FILE_SIZE);
4596
4615
  return pluginManifestSchema.parse(JSON.parse(content)).version ?? DEFAULT_VERSION;
@@ -4599,7 +4618,7 @@ async function computeBytesHash(filePath) {
4599
4618
  return createHash("sha256").update(await readFileBytes(filePath)).digest("hex");
4600
4619
  }
4601
4620
  async function describeShippedFile(pluginDir, file) {
4602
- return `${file}\0${await computeBytesHash(path8.join(pluginDir, file))}\0`;
4621
+ return `${file}\0${await computeBytesHash(path9.join(pluginDir, file))}\0`;
4603
4622
  }
4604
4623
  async function computeShippedHash(pluginDir) {
4605
4624
  const files = await glob("**/*", pluginDir, { dot: true });
@@ -4620,25 +4639,6 @@ function versionAfterRebuild(earlier, shippedHash) {
4620
4639
  if (!earlier) return DEFAULT_VERSION;
4621
4640
  return earlier.shippedHash === shippedHash ? earlier.version : bumpMajorVersion(earlier.version);
4622
4641
  }
4623
- async function determinePluginVersion(newHash, pluginDir, getManifestPath) {
4624
- const existing = await readExistingPluginManifest(pluginDir, getManifestPath);
4625
- if (!existing) {
4626
- return {
4627
- version: DEFAULT_VERSION,
4628
- contentHash: newHash
4629
- };
4630
- }
4631
- if (existing.contentHash !== newHash) {
4632
- return {
4633
- version: bumpMajorVersion(existing.version),
4634
- contentHash: newHash
4635
- };
4636
- }
4637
- return {
4638
- version: existing.version,
4639
- contentHash: newHash
4640
- };
4641
- }
4642
4642
  async function writeContentHash(pluginDir, contentHash, getManifestPath) {
4643
4643
  await writeFile(contentHashFileBeside(getManifestPath(pluginDir)), contentHash);
4644
4644
  }
@@ -4662,7 +4662,7 @@ async function readForkedFromMetadata(skillDir) {
4662
4662
  return metadata?.forkedFrom ?? null;
4663
4663
  }
4664
4664
  async function readLocalSkillMetadata(skillDir) {
4665
- const metadataPath = path9.join(skillDir, STANDARD_FILES.METADATA_YAML);
4665
+ const metadataPath = path10.join(skillDir, STANDARD_FILES.METADATA_YAML);
4666
4666
  if (!await fileExists(metadataPath)) {
4667
4667
  return null;
4668
4668
  }
@@ -4680,7 +4680,7 @@ async function readLocalSkillMetadata(skillDir) {
4680
4680
  return result.data;
4681
4681
  }
4682
4682
  async function injectForkedFromMetadata(destPath, skillId, contentHash, origin = {}) {
4683
- const metadataPath = path9.join(destPath, STANDARD_FILES.METADATA_YAML);
4683
+ const metadataPath = path10.join(destPath, STANDARD_FILES.METADATA_YAML);
4684
4684
  const rawContent = await readFile(metadataPath);
4685
4685
  const { yamlContent } = stripYamlSchemaComment(rawContent);
4686
4686
  const metadata = {
@@ -4718,7 +4718,7 @@ function existingMetadataFields(yamlContent, metadataPath) {
4718
4718
  }
4719
4719
 
4720
4720
  // src/cli/lib/skills/skill-copier.ts
4721
- import path10 from "path";
4721
+ import path11 from "path";
4722
4722
  var NULL_BYTE_PATTERN2 = /\0/;
4723
4723
  function validateSkillPath(resolvedPath, expectedParent, skillPath) {
4724
4724
  if (NULL_BYTE_PATTERN2.test(skillPath)) {
@@ -4726,26 +4726,26 @@ function validateSkillPath(resolvedPath, expectedParent, skillPath) {
4726
4726
  }
4727
4727
  if (!isPathWithin(resolvedPath, expectedParent)) {
4728
4728
  throw new Error(
4729
- `Invalid skill path: '${skillPath}' escapes expected directory '${path10.resolve(expectedParent)}'`
4729
+ `Invalid skill path: '${skillPath}' escapes expected directory '${path11.resolve(expectedParent)}'`
4730
4730
  );
4731
4731
  }
4732
4732
  }
4733
4733
  function resolveSkillPath(basePath, skillPath) {
4734
- const resolved = path10.join(basePath, skillPath);
4734
+ const resolved = path11.join(basePath, skillPath);
4735
4735
  validateSkillPath(resolved, basePath, skillPath);
4736
4736
  return resolved;
4737
4737
  }
4738
4738
  function getSkillSourcePath(skill, rootDir) {
4739
- const srcDir = path10.join(rootDir, SOURCE_SRC_DIR);
4739
+ const srcDir = path11.join(rootDir, SOURCE_SRC_DIR);
4740
4740
  return resolveSkillPath(srcDir, skill.path);
4741
4741
  }
4742
4742
  async function generateSkillHash(skillSourcePath) {
4743
- const skillMdPath = path10.join(skillSourcePath, STANDARD_FILES.SKILL_MD);
4743
+ const skillMdPath = path11.join(skillSourcePath, STANDARD_FILES.SKILL_MD);
4744
4744
  return computeFileHash(skillMdPath);
4745
4745
  }
4746
4746
  async function copySkillTo(skill, sourcePath, destPath, source) {
4747
4747
  const contentHash = await generateSkillHash(sourcePath);
4748
- await ensureDir(path10.dirname(destPath));
4748
+ await ensureDir(path11.dirname(destPath));
4749
4749
  await copy(sourcePath, destPath);
4750
4750
  await injectForkedFromMetadata(destPath, skill.id, contentHash, {
4751
4751
  ...source !== void 0 && { source }
@@ -4812,7 +4812,7 @@ async function copySelectedSkill(skillId, localSkillsDir, sourceResult, selected
4812
4812
  return copySkillToLocalFlattened(skill, localSkillsDir, sourceResult);
4813
4813
  }
4814
4814
  const destPath = getFlattenedSkillDestPath(skill, localSkillsDir);
4815
- if (path10.resolve(localPath) === path10.resolve(destPath)) {
4815
+ if (path11.resolve(localPath) === path11.resolve(destPath)) {
4816
4816
  return resolveLocalCopiedSkill(skill, localPath);
4817
4817
  }
4818
4818
  return copyLocalSkillTo(skill, localPath, destPath);
@@ -4825,33 +4825,33 @@ function localPathOf(skill) {
4825
4825
  }
4826
4826
  async function copyLocalSkillTo(skill, localPath, destPath) {
4827
4827
  const contentHash = await generateSkillHash(localPath);
4828
- await ensureDir(path10.dirname(destPath));
4828
+ await ensureDir(path11.dirname(destPath));
4829
4829
  await copy(localPath, destPath);
4830
4830
  return { skillId: skill.id, sourcePath: localPath, destPath, contentHash };
4831
4831
  }
4832
4832
 
4833
4833
  // src/cli/lib/skills/skill-plugin-compiler.ts
4834
- import path23 from "path";
4834
+ import path24 from "path";
4835
4835
  import { parse as parseYaml4 } from "yaml";
4836
4836
 
4837
4837
  // src/cli/lib/plugins/plugin-manifest.ts
4838
- import path12 from "path";
4838
+ import path13 from "path";
4839
4839
 
4840
4840
  // src/cli/lib/plugins/plugin-finder.ts
4841
4841
  import os6 from "os";
4842
- import path11 from "path";
4842
+ import path12 from "path";
4843
4843
  function getUserPluginsDir() {
4844
- return path11.join(os6.homedir(), CLAUDE_DIR, PLUGINS_SUBDIR);
4844
+ return path12.join(os6.homedir(), CLAUDE_DIR, PLUGINS_SUBDIR);
4845
4845
  }
4846
4846
  function getProjectPluginsDir(projectDir) {
4847
4847
  const dir = projectDir ?? process.cwd();
4848
- return path11.join(dir, CLAUDE_DIR, PLUGINS_SUBDIR);
4848
+ return path12.join(dir, CLAUDE_DIR, PLUGINS_SUBDIR);
4849
4849
  }
4850
4850
  function getPluginAgentsDir(pluginDir) {
4851
- return path11.join(pluginDir, STANDARD_DIRS.AGENTS);
4851
+ return path12.join(pluginDir, STANDARD_DIRS.AGENTS);
4852
4852
  }
4853
4853
  function getPluginManifestPath(pluginDir) {
4854
- return path11.join(pluginDir, PLUGIN_MANIFEST_DIR, PLUGIN_MANIFEST_FILE);
4854
+ return path12.join(pluginDir, PLUGIN_MANIFEST_DIR, PLUGIN_MANIFEST_FILE);
4855
4855
  }
4856
4856
  async function readPluginManifest(pluginDir) {
4857
4857
  const manifestPath = getPluginManifestPath(pluginDir);
@@ -4906,7 +4906,7 @@ function generateAgentPluginManifest(options) {
4906
4906
  }
4907
4907
  async function writePluginManifest(outputDir, manifest) {
4908
4908
  const manifestPath = getPluginManifestPath(outputDir);
4909
- await ensureDir(path12.dirname(manifestPath));
4909
+ await ensureDir(path13.dirname(manifestPath));
4910
4910
  const content = JSON.stringify(manifest, null, 2);
4911
4911
  await writeFile(manifestPath, content);
4912
4912
  return manifestPath;
@@ -4921,12 +4921,24 @@ function parseMarketplacePluginRef(ref) {
4921
4921
  return separator === -1 ? ref : ref.slice(0, separator);
4922
4922
  }
4923
4923
 
4924
+ // src/cli/lib/plugins/earlier-build.ts
4925
+ async function isEarlierBuild(pluginDir) {
4926
+ return fileExists(getPluginManifestPath(pluginDir));
4927
+ }
4928
+ async function holdsContentNoBuildMade(pluginDir) {
4929
+ if (await isEarlierBuild(pluginDir)) return false;
4930
+ return !await isDirectoryEmpty(pluginDir);
4931
+ }
4932
+ async function clearEarlierBuild(pluginDir) {
4933
+ if (await isEarlierBuild(pluginDir)) await remove(pluginDir);
4934
+ }
4935
+
4924
4936
  // src/cli/lib/plugins/plugin-info.ts
4925
4937
  import { readdir } from "fs/promises";
4926
4938
 
4927
4939
  // src/cli/lib/installation/installation.ts
4928
4940
  import os7 from "os";
4929
- import path13 from "path";
4941
+ import path14 from "path";
4930
4942
  var INSTALL_MODE_LABELS = {
4931
4943
  plugin: "Plugin",
4932
4944
  mixed: "Mixed",
@@ -4965,7 +4977,7 @@ async function detectInstallationInDir(dir, scope) {
4965
4977
  configPath,
4966
4978
  agentsDir: agentsDir(provider, scope, dir),
4967
4979
  // Mixed mode uses the local skills dir, as eject does.
4968
- skillsDir: path13.join(
4980
+ skillsDir: path14.join(
4969
4981
  dir,
4970
4982
  CLAUDE_DIR,
4971
4983
  mode === "plugin" ? PLUGINS_SUBDIR : STANDARD_DIRS.SKILLS
@@ -4995,12 +5007,12 @@ async function detectInstallation(projectDir = process.cwd()) {
4995
5007
 
4996
5008
  // src/cli/lib/installation/local-installer.ts
4997
5009
  import os10 from "os";
4998
- import path17 from "path";
5010
+ import path18 from "path";
4999
5011
  import { unique as unique4 } from "remeda";
5000
5012
 
5001
5013
  // src/cli/lib/loading/loader.ts
5002
5014
  import { parse as parseYaml3 } from "yaml";
5003
- import path14 from "path";
5015
+ import path15 from "path";
5004
5016
 
5005
5017
  // src/cli/utils/frontmatter.ts
5006
5018
  import { parse as parseYaml2 } from "yaml";
@@ -5073,7 +5085,7 @@ async function loadAgentsFromDir(agentsDir2, sourceRoot, options) {
5073
5085
  return agents;
5074
5086
  }
5075
5087
  async function readAgentDefinition(agentsDir2, file, sourceRoot, options) {
5076
- const fullPath = path14.join(agentsDir2, file);
5088
+ const fullPath = path15.join(agentsDir2, file);
5077
5089
  try {
5078
5090
  const content = await readFile(fullPath);
5079
5091
  const parsed = agentYamlConfigSchema.safeParse(parseYaml3(content));
@@ -5106,7 +5118,7 @@ function toAgentDefinition(config, file, sourceRoot, options) {
5106
5118
  ...config.hooks !== void 0 && { hooks: config.hooks },
5107
5119
  ...config.experimental !== void 0 && { experimental: config.experimental },
5108
5120
  ...config.outputFormat !== void 0 && { outputFormat: config.outputFormat },
5109
- path: path14.dirname(file),
5121
+ path: path15.dirname(file),
5110
5122
  sourceRoot,
5111
5123
  ...options.agentBaseDir ? { agentBaseDir: options.agentBaseDir } : {},
5112
5124
  ...config.domain ? { domain: config.domain } : {},
@@ -5114,7 +5126,7 @@ function toAgentDefinition(config, file, sourceRoot, options) {
5114
5126
  };
5115
5127
  }
5116
5128
  async function loadAllAgents(projectRoot) {
5117
- return loadAgentsFromDir(path14.join(projectRoot, DIRS.agents), projectRoot, {
5129
+ return loadAgentsFromDir(path15.join(projectRoot, DIRS.agents), projectRoot, {
5118
5130
  includeCustomFlag: true,
5119
5131
  verboseLabel: "agent"
5120
5132
  });
@@ -5128,7 +5140,7 @@ async function loadMergedAgents(sourcePath) {
5128
5140
  }
5129
5141
  async function loadProjectAgents(projectRoot) {
5130
5142
  const sourceFolder = sourceFolderInUse(projectRoot, providerInUse(projectRoot));
5131
- const projectAgentsDir = path14.join(sourceFolder.dir, STANDARD_DIRS.AGENTS);
5143
+ const projectAgentsDir = path15.join(sourceFolder.dir, STANDARD_DIRS.AGENTS);
5132
5144
  if (!await directoryExists2(projectAgentsDir)) {
5133
5145
  verbose(`No project agents directory at ${projectAgentsDir}`);
5134
5146
  return {};
@@ -5160,19 +5172,19 @@ async function loadSkillsFromDir(skillsDir2, options = {}) {
5160
5172
  return { skills, unusableMetadata };
5161
5173
  }
5162
5174
  function locateSkill(skillsDir2, skillFile, pathPrefix) {
5163
- const skillMdPath = path14.join(skillsDir2, skillFile);
5164
- const skillDir = path14.dirname(skillMdPath);
5165
- const relativePath = path14.relative(skillsDir2, skillDir);
5175
+ const skillMdPath = path15.join(skillsDir2, skillFile);
5176
+ const skillDir = path15.dirname(skillMdPath);
5177
+ const relativePath = path15.relative(skillsDir2, skillDir);
5166
5178
  return {
5167
5179
  skillFile,
5168
5180
  skillMdPath,
5169
5181
  skillDir,
5170
- skillDirName: path14.basename(skillDir),
5182
+ skillDirName: path15.basename(skillDir),
5171
5183
  displayPath: pathPrefix ? `${pathPrefix}/${relativePath}/` : `${relativePath}/`
5172
5184
  };
5173
5185
  }
5174
5186
  async function judgeSkillMetadata(skill) {
5175
- const metadataPath = path14.join(skill.skillDir, STANDARD_FILES.METADATA_YAML);
5187
+ const metadataPath = path15.join(skill.skillDir, STANDARD_FILES.METADATA_YAML);
5176
5188
  if (!await fileExists(metadataPath)) {
5177
5189
  warn(
5178
5190
  `Skill '${skill.skillDirName}' in '${skill.displayPath}' is missing ${STANDARD_FILES.METADATA_YAML} \u2014 skipped. Add ${STANDARD_FILES.METADATA_YAML} to register it with the CLI.`
@@ -5214,7 +5226,7 @@ async function readSkillDefinition(skill) {
5214
5226
  }
5215
5227
  }
5216
5228
  async function loadPluginSkills(pluginDir) {
5217
- const { skills } = await loadSkillsFromDir(path14.join(pluginDir, "skills"), {
5229
+ const { skills } = await loadSkillsFromDir(path15.join(pluginDir, "skills"), {
5218
5230
  pathPrefix: "skills",
5219
5231
  requireMetadata: false
5220
5232
  });
@@ -5222,13 +5234,15 @@ async function loadPluginSkills(pluginDir) {
5222
5234
  }
5223
5235
 
5224
5236
  // src/cli/lib/loading/source-fetcher.ts
5237
+ import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
5225
5238
  import { createHash as createHash2 } from "crypto";
5239
+ import { rename } from "fs/promises";
5226
5240
  import { downloadTemplate } from "giget";
5227
5241
  import os8 from "os";
5228
- import path16 from "path";
5242
+ import path17 from "path";
5229
5243
 
5230
5244
  // src/cli/lib/marketplace-generator.ts
5231
- import path15 from "path";
5245
+ import path16 from "path";
5232
5246
  import { countBy, sortBy } from "remeda";
5233
5247
  var PLUGIN_MANIFEST_PATH = `${PLUGIN_MANIFEST_DIR}/${PLUGIN_MANIFEST_FILE}`;
5234
5248
  var MARKETPLACE_SCHEMA_URL = "https://anthropic.com/claude-code/marketplace.schema.json";
@@ -5260,7 +5274,7 @@ async function generateMarketplace(pluginsDir2, options) {
5260
5274
  const rootWithoutDotSlash = options.pluginRoot.replace(/^\.\//, "");
5261
5275
  for (const manifestFile of manifestFiles) {
5262
5276
  const pluginDirName = pluginDirNameOf(manifestFile);
5263
- const pluginDir = path15.join(pluginsDir2, pluginDirName);
5277
+ const pluginDir = path16.join(pluginsDir2, pluginDirName);
5264
5278
  const manifest = await readPluginManifest(pluginDir);
5265
5279
  if (!manifest) {
5266
5280
  warn(`Could not read plugin manifest: '${manifestFile}'`);
@@ -5315,17 +5329,17 @@ function reservedNameError(marketplaceName, wayOut) {
5315
5329
  return `Marketplace name '${marketplaceName}' is reserved. '${DEFAULT_PUBLIC_SOURCE_NAME}' is the public catalogue's own namespace, '${EXTERNAL_SKILL_NAMESPACE}' and '${LOCAL_SKILL_NAMESPACE}' hold the skills that belong to no marketplace, and '${EJECT_SOURCE}' is what an installation records for a skill copied into it rather than installed as a plugin \u2014 a marketplace under any of them is read back as something it is not. Choose a name of your own: ${wayOut}.`;
5316
5330
  }
5317
5331
  function namespaceViolationError(foreignIds, marketplaceName) {
5318
- const listed = foreignIds.slice(0, MAX_REPORTED_NAMESPACE_VIOLATIONS).map((id) => ` ${id} -> ${marketplaceName}-${id}`);
5319
- const unlisted = foreignIds.length - listed.length;
5332
+ const listed2 = foreignIds.slice(0, MAX_REPORTED_NAMESPACE_VIOLATIONS).map((id) => ` ${id} -> ${marketplaceName}-${id}`);
5333
+ const unlisted = foreignIds.length - listed2.length;
5320
5334
  return [
5321
5335
  `Marketplace '${marketplaceName}' ships ${foreignIds.length} skill id(s) outside its own namespace. Every skill id must begin with the marketplace's name:`,
5322
- ...listed,
5336
+ ...listed2,
5323
5337
  ...unlisted > 0 ? [` ... and ${unlisted} more`] : [],
5324
5338
  `Rename each skill directory and the 'name' in its SKILL.md, re-run 'build plugins', then build the marketplace again.`
5325
5339
  ].join("\n");
5326
5340
  }
5327
5341
  async function writeMarketplace(outputPath, marketplace) {
5328
- await ensureDir(path15.dirname(outputPath));
5342
+ await ensureDir(path16.dirname(outputPath));
5329
5343
  const content = `${JSON.stringify(marketplace, null, 2)}
5330
5344
  `;
5331
5345
  await writeFile(outputPath, content);
@@ -5343,6 +5357,29 @@ var MAX_NAME_LENGTH = 200;
5343
5357
  var QUOTED_NAME_LENGTH = 50;
5344
5358
  var SOURCE_PROTO_RE = /^([\w-.]+):/;
5345
5359
  var GIT_URI_RE = /^(?<repo>[\w.-]+\/[\w.-]+)(?<subdir>[^#]+)?(?<ref>#[\w./@-]+)?/;
5360
+ var MarketplaceUnreachableError = class extends Error {
5361
+ constructor(source, reason) {
5362
+ super(`Could not reach ${source}: ${reason}`);
5363
+ this.source = source;
5364
+ this.reason = reason;
5365
+ this.name = "MarketplaceUnreachableError";
5366
+ }
5367
+ source;
5368
+ reason;
5369
+ };
5370
+ var TarballWithheldError = class extends Error {
5371
+ constructor({ status, statusText }) {
5372
+ super(`the marketplace answered ${status} ${statusText}`.trimEnd());
5373
+ this.name = "TarballWithheldError";
5374
+ }
5375
+ };
5376
+ var marketplaceMustAnswer = new AsyncLocalStorage3();
5377
+ function askingEachMarketplaceItself(body) {
5378
+ return marketplaceMustAnswer.run(true, body);
5379
+ }
5380
+ function mustAnswer() {
5381
+ return marketplaceMustAnswer.getStore() === true;
5382
+ }
5346
5383
  function sanitizeSourceForCache(source) {
5347
5384
  const hash = createHash2("sha256").update(source).digest("hex").slice(0, CACHE_HASH_LENGTH);
5348
5385
  const readable = source.replace(/[^a-zA-Z0-9]/g, "-").replace(/--+/g, "-").replace(/^-|-$/g, "").slice(0, CACHE_READABLE_PREFIX_LENGTH);
@@ -5350,7 +5387,7 @@ function sanitizeSourceForCache(source) {
5350
5387
  }
5351
5388
  function getCacheDir(source) {
5352
5389
  const sanitized = sanitizeSourceForCache(source) || "unknown";
5353
- return path16.join(cacheRoot(), "sources", sanitized);
5390
+ return path17.join(cacheRoot(), "sources", sanitized);
5354
5391
  }
5355
5392
  async function fetchFromSource(source, options = {}) {
5356
5393
  const { subdir } = options;
@@ -5361,8 +5398,8 @@ async function fetchFromSource(source, options = {}) {
5361
5398
  return fetchFromRemoteSource(source, subdir);
5362
5399
  }
5363
5400
  async function fetchFromLocalSource(source, subdir) {
5364
- const fullPath = subdir ? path16.join(source, subdir) : source;
5365
- const absolutePath = path16.isAbsolute(fullPath) ? fullPath : path16.resolve(process.cwd(), fullPath);
5401
+ const fullPath = subdir ? path17.join(source, subdir) : source;
5402
+ const absolutePath = path17.isAbsolute(fullPath) ? fullPath : path17.resolve(process.cwd(), fullPath);
5366
5403
  if (!await directoryExists2(absolutePath)) {
5367
5404
  throw new Error(
5368
5405
  `Local marketplace not found: '${absolutePath}'
@@ -5393,8 +5430,8 @@ function getGigetCacheDir(source) {
5393
5430
  return void 0;
5394
5431
  }
5395
5432
  const templateName = uriMatch.groups.repo.replace("/", "-").replace(/[^\da-z-]/gi, "-");
5396
- const gigetCacheRoot = process.env.XDG_CACHE_HOME ? path16.resolve(process.env.XDG_CACHE_HOME, "giget") : path16.resolve(os8.homedir(), ".cache", "giget");
5397
- return path16.join(gigetCacheRoot, providerName, templateName);
5433
+ const gigetCacheRoot = process.env.XDG_CACHE_HOME ? path17.resolve(process.env.XDG_CACHE_HOME, "giget") : path17.resolve(os8.homedir(), ".cache", "giget");
5434
+ return path17.join(gigetCacheRoot, providerName, templateName);
5398
5435
  }
5399
5436
  async function clearGigetCache(source) {
5400
5437
  const gigetDir = getGigetCacheDir(source);
@@ -5421,16 +5458,16 @@ async function fetchFromRemoteSource(source, subdir) {
5421
5458
  };
5422
5459
  }
5423
5460
  announceRefetch(verdict);
5424
- await discardCachedCopy(cacheDir, source);
5461
+ if (!mustAnswer()) await discardCachedCopy(cacheDir, source);
5425
5462
  }
5426
- return downloadFreshCopy(cacheDir, source, fullSource);
5463
+ return mustAnswer() ? downloadAnsweredFor(cacheDir, source, fullSource) : downloadFreshCopy(cacheDir, source, fullSource);
5427
5464
  }
5428
5465
  async function discardCachedCopy(cacheDir, source) {
5429
5466
  await clearGigetCache(source);
5430
5467
  await remove(cacheDir);
5431
5468
  }
5432
5469
  async function downloadFreshCopy(cacheDir, source, fullSource) {
5433
- await ensureDir(path16.dirname(cacheDir));
5470
+ await ensureDir(path17.dirname(cacheDir));
5434
5471
  try {
5435
5472
  const result = await downloadTemplate(fullSource, {
5436
5473
  dir: cacheDir,
@@ -5451,40 +5488,75 @@ async function downloadFreshCopy(cacheDir, source, fullSource) {
5451
5488
  throw createDetailedFetchError(error, source);
5452
5489
  }
5453
5490
  }
5491
+ var STAGED_DOWNLOAD_SUFFIX = ".download";
5492
+ async function downloadAnsweredFor(cacheDir, source, fullSource) {
5493
+ const staged = `${cacheDir}${STAGED_DOWNLOAD_SUFFIX}`;
5494
+ await ensureDir(path17.dirname(cacheDir));
5495
+ try {
5496
+ const result = await downloadTemplate(fullSource, {
5497
+ dir: staged,
5498
+ force: true,
5499
+ forceClean: true,
5500
+ offline: false
5501
+ });
5502
+ const record = await askAfterTheDownload(result, source);
5503
+ await replaceCopy(cacheDir, staged, record);
5504
+ markCopyCurrentForThisRun(cacheDir);
5505
+ return { path: cacheDir, fromCache: false, unreachable: false, source: fullSource };
5506
+ } catch (error) {
5507
+ if (error instanceof MarketplaceUnreachableError) throw error;
5508
+ throw createDetailedFetchError(error, source);
5509
+ } finally {
5510
+ await remove(staged);
5511
+ }
5512
+ }
5513
+ async function replaceCopy(cacheDir, staged, record) {
5514
+ await remove(cacheDir);
5515
+ await rename(staged, cacheDir);
5516
+ await writeFile(fetchRecordPath(cacheDir), JSON.stringify(record));
5517
+ }
5454
5518
  var REVALIDATION_TIMEOUT_MS = 5e3;
5455
5519
  var FETCH_RECORD_SUFFIX = ".etag.json";
5456
5520
  var askedThisRun = /* @__PURE__ */ new Map();
5521
+ var askedItselfThisRun = /* @__PURE__ */ new Map();
5522
+ function verdictsThisRun() {
5523
+ return mustAnswer() ? askedItselfThisRun : askedThisRun;
5524
+ }
5457
5525
  function announceRefetch(verdict) {
5458
5526
  if (verdict === "superseded") {
5459
5527
  log(STATUS_MESSAGES.MARKETPLACE_HAS_NEWER_CONTENT);
5460
5528
  }
5461
5529
  }
5462
5530
  function revalidateCachedCopy(cacheDir, source) {
5463
- const asked = askedThisRun.get(cacheDir);
5531
+ const verdicts = verdictsThisRun();
5532
+ const asked = verdicts.get(cacheDir);
5464
5533
  if (asked) return asked;
5465
5534
  const verdict = classifyCachedCopy(cacheDir, source);
5466
- askedThisRun.set(cacheDir, verdict);
5535
+ verdicts.set(cacheDir, verdict);
5467
5536
  return verdict;
5468
5537
  }
5469
5538
  function markCopyCurrentForThisRun(cacheDir) {
5470
- askedThisRun.set(cacheDir, Promise.resolve("current"));
5539
+ verdictsThisRun().set(cacheDir, Promise.resolve("current"));
5471
5540
  }
5472
5541
  async function classifyCachedCopy(cacheDir, source) {
5473
5542
  const record = await readFetchRecord(cacheDir);
5474
5543
  if (!record) return "unrecorded";
5475
- if (record.etag === void 0) {
5544
+ if (record.etag === void 0 && !mustAnswer()) {
5476
5545
  verbose(`No ETag was recorded for ${record.tar} \u2014 keeping the cached copy`);
5477
5546
  return "current";
5478
5547
  }
5479
5548
  try {
5480
5549
  const live = await fetchEtag(record.tar);
5481
- if (live === void 0) {
5482
- verbose(`${record.tar} answered without an ETag \u2014 keeping the cached copy`);
5483
- return "current";
5550
+ if (live !== void 0 && record.etag !== void 0) {
5551
+ return live === record.etag ? "current" : "superseded";
5484
5552
  }
5485
- return live === record.etag ? "current" : "superseded";
5553
+ if (mustAnswer()) return "unconfirmed";
5554
+ verbose(`${record.tar} answered without an ETag \u2014 keeping the cached copy`);
5555
+ return "current";
5486
5556
  } catch (error) {
5557
+ if (error instanceof TarballWithheldError) throw createDetailedFetchError(error, source);
5487
5558
  verbose(`Could not revalidate ${record.tar}: ${getErrorMessage(error)}`);
5559
+ if (mustAnswer()) throw new MarketplaceUnreachableError(source, getErrorMessage(error));
5488
5560
  warn(sourceUnreachableUsingCache(source));
5489
5561
  return "unreachable";
5490
5562
  }
@@ -5492,10 +5564,12 @@ async function classifyCachedCopy(cacheDir, source) {
5492
5564
  async function fetchEtag(tar) {
5493
5565
  const auth = process.env.GIGET_AUTH;
5494
5566
  const response = await fetch(tar, {
5495
- method: "HEAD",
5567
+ method: mustAnswer() ? "GET" : "HEAD",
5496
5568
  ...auth && { headers: { Authorization: `Bearer ${auth}` } },
5497
5569
  signal: AbortSignal.timeout(REVALIDATION_TIMEOUT_MS)
5498
5570
  });
5571
+ await response.body?.cancel();
5572
+ if (mustAnswer() && !response.ok) throw new TarballWithheldError(response);
5499
5573
  return response.headers.get("etag") ?? void 0;
5500
5574
  }
5501
5575
  function fetchRecordPath(cacheDir) {
@@ -5512,14 +5586,40 @@ async function readFetchRecord(cacheDir) {
5512
5586
  return parsed.data;
5513
5587
  }
5514
5588
  async function recordFetchedCopy(cacheDir, result) {
5515
- const tar = result.tar;
5516
- if (typeof tar !== "string") {
5589
+ const tar = tarballOf(result);
5590
+ if (tar === void 0) {
5517
5591
  verbose("No tarball URL came back from the download \u2014 the copy cannot be revalidated");
5518
5592
  return;
5519
5593
  }
5520
- const etag = await fetchEtag(tar).catch(() => void 0);
5521
- const record = { tar, ...etag !== void 0 && { etag } };
5522
- await writeFile(fetchRecordPath(cacheDir), JSON.stringify(record));
5594
+ const etag = await etagIfAnswered(tar);
5595
+ await writeFile(fetchRecordPath(cacheDir), JSON.stringify(fetchRecord(tar, etag)));
5596
+ }
5597
+ async function etagIfAnswered(tar) {
5598
+ try {
5599
+ return await fetchEtag(tar);
5600
+ } catch (error) {
5601
+ verbose(`Could not ask ${tar} after the download: ${getErrorMessage(error)}`);
5602
+ return void 0;
5603
+ }
5604
+ }
5605
+ async function askAfterTheDownload(result, source) {
5606
+ const tar = tarballOf(result);
5607
+ if (tar === void 0) {
5608
+ throw new Error("the download named no tarball to ask the marketplace about");
5609
+ }
5610
+ try {
5611
+ return fetchRecord(tar, await fetchEtag(tar));
5612
+ } catch (error) {
5613
+ if (error instanceof TarballWithheldError) throw error;
5614
+ throw new MarketplaceUnreachableError(source, getErrorMessage(error));
5615
+ }
5616
+ }
5617
+ function tarballOf(result) {
5618
+ const tar = result.tar;
5619
+ return typeof tar === "string" ? tar : void 0;
5620
+ }
5621
+ function fetchRecord(tar, etag) {
5622
+ return { tar, ...etag !== void 0 && { etag } };
5523
5623
  }
5524
5624
  function parseJsonOrUndefined(raw) {
5525
5625
  try {
@@ -5824,9 +5924,9 @@ function agentNotFoundError(agentName, agents) {
5824
5924
  }
5825
5925
  function describeAvailableAgents(availableAgents) {
5826
5926
  if (availableAgents.length === 0) return "No agents found in scanned directories";
5827
- const listed = availableAgents.slice(0, MAX_LISTED_AVAILABLE_AGENTS).join(", ");
5927
+ const listed2 = availableAgents.slice(0, MAX_LISTED_AVAILABLE_AGENTS).join(", ");
5828
5928
  const unlisted = availableAgents.length - MAX_LISTED_AVAILABLE_AGENTS;
5829
- return unlisted > 0 ? `Available agents: ${listed} (and ${unlisted} more)` : `Available agents: ${listed}`;
5929
+ return unlisted > 0 ? `Available agents: ${listed2} (and ${unlisted} more)` : `Available agents: ${listed2}`;
5830
5930
  }
5831
5931
  function assembleAgentConfig(agentName, definition, agentConfig, resolvedSkills) {
5832
5932
  const model = agentConfig.model ?? definition.model;
@@ -5863,10 +5963,10 @@ function resolveStackProperty(generated, assigned) {
5863
5963
  return generated === void 0 ? void 0 : assigned;
5864
5964
  }
5865
5965
  function resolveDescription(loadedStack, shared) {
5866
- return loadedStack === null ? shared : loadedStack.description;
5966
+ return shared ?? loadedStack?.description;
5867
5967
  }
5868
5968
  function configNameFor(projectDir) {
5869
- return isHomeDirectory(projectDir) ? DEFAULT_PLUGIN_NAME : path17.basename(projectDir);
5969
+ return isHomeDirectory(projectDir) ? DEFAULT_PLUGIN_NAME : path18.basename(projectDir);
5870
5970
  }
5871
5971
  async function loadCuratedStack(projectDir, projectStack) {
5872
5972
  const globalStack = isHomeDirectory(projectDir) ? void 0 : await loadGlobalStack();
@@ -6039,12 +6139,12 @@ async function installationIfPresent(candidate) {
6039
6139
 
6040
6140
  // src/cli/lib/hosts/claude-host.ts
6041
6141
  import os12 from "os";
6042
- import path20 from "path";
6142
+ import path21 from "path";
6043
6143
  import { z as z5 } from "zod";
6044
6144
 
6045
6145
  // src/cli/utils/exec.ts
6046
6146
  import { spawn } from "child_process";
6047
- import path18 from "path";
6147
+ import path19 from "path";
6048
6148
  var MAX_PLUGIN_PATH_LENGTH = 1024;
6049
6149
  var MAX_PLUGIN_NAME_LENGTH = 256;
6050
6150
  var MAX_MARKETPLACE_SOURCE_LENGTH = 1024;
@@ -6052,7 +6152,7 @@ var SAFE_NAME_PATTERN2 = /^[a-zA-Z0-9._@/-]+$/;
6052
6152
  var SAFE_PLUGIN_PATH_PATTERN = /^[a-zA-Z0-9._@/:~-]+$/;
6053
6153
  var OPENS_A_RELATIVE_PATH = /^\.\.?[/\\]/;
6054
6154
  function namesADirectoryOnThisMachine(source) {
6055
- return path18.isAbsolute(source) || OPENS_A_RELATIVE_PATH.test(source);
6155
+ return path19.isAbsolute(source) || OPENS_A_RELATIVE_PATH.test(source);
6056
6156
  }
6057
6157
  var CONTROL_CHAR_PATTERN = /[\x00-\x08\x0E-\x1F\x7F]/u;
6058
6158
  function validatePluginPath(pluginPath) {
@@ -6142,7 +6242,7 @@ async function execCommand(command, args, options) {
6142
6242
  }
6143
6243
 
6144
6244
  // src/cli/lib/plugins/plugin-settings.ts
6145
- import path19 from "path";
6245
+ import path20 from "path";
6146
6246
  import { z as z4 } from "zod";
6147
6247
  var pluginSettingsSchema = z4.object({
6148
6248
  // Absent when no plugin is enabled.
@@ -6163,7 +6263,7 @@ var installedPluginsSchema = z4.object({
6163
6263
  plugins: z4.record(z4.string(), z4.array(pluginInstallationSchema))
6164
6264
  }).passthrough();
6165
6265
  function getInstalledPluginsRegistryPath(pluginsDir2) {
6166
- return path19.join(pluginsDir2, INSTALLED_PLUGINS_FILE);
6266
+ return path20.join(pluginsDir2, INSTALLED_PLUGINS_FILE);
6167
6267
  }
6168
6268
  async function readRegisteredInstallations(pluginsDir2) {
6169
6269
  const registryPath = getInstalledPluginsRegistryPath(pluginsDir2);
@@ -6196,7 +6296,7 @@ async function listPluginInstallsForProject(pluginsDir2, projectDir) {
6196
6296
  });
6197
6297
  }
6198
6298
  async function getEnabledPluginKeys(projectDir) {
6199
- const settingsPath = path19.join(projectDir, CLAUDE_DIR, STANDARD_FILES.SETTINGS_JSON);
6299
+ const settingsPath = path20.join(projectDir, CLAUDE_DIR, STANDARD_FILES.SETTINGS_JSON);
6200
6300
  if (!await fileExists(settingsPath)) {
6201
6301
  verbose(`No settings.json found at '${settingsPath}'`);
6202
6302
  return [];
@@ -6344,7 +6444,7 @@ function namesNothingThere(message) {
6344
6444
  return NOT_INSTALLED_MESSAGES.some((phrase) => message.includes(phrase));
6345
6445
  }
6346
6446
  function claudePluginsRoot(projectDir, options) {
6347
- if (options?.configDir !== void 0) return path20.join(options.configDir, PLUGINS_SUBDIR);
6447
+ if (options?.configDir !== void 0) return path21.join(options.configDir, PLUGINS_SUBDIR);
6348
6448
  return pluginsDir(CLAUDE, "global", projectDir);
6349
6449
  }
6350
6450
  async function claudeListPlugins(projectDir, options) {
@@ -6379,7 +6479,7 @@ function claudeHost() {
6379
6479
 
6380
6480
  // src/cli/lib/hosts/codex-host.ts
6381
6481
  import os13 from "os";
6382
- import path21 from "path";
6482
+ import path22 from "path";
6383
6483
  import { z as z6 } from "zod";
6384
6484
  var CODEX = "codex";
6385
6485
  var PLUGIN_CACHE_SUBDIR = "cache";
@@ -6463,7 +6563,7 @@ var pluginListingSchema = z6.object({
6463
6563
  )
6464
6564
  });
6465
6565
  function codexPluginsRoot(projectDir, options) {
6466
- if (options?.configDir !== void 0) return path21.join(options.configDir, PLUGINS_SUBDIR);
6566
+ if (options?.configDir !== void 0) return path22.join(options.configDir, PLUGINS_SUBDIR);
6467
6567
  return pluginsDir(CODEX, "global", projectDir);
6468
6568
  }
6469
6569
  async function isCodexCLIAvailable(options) {
@@ -6593,7 +6693,7 @@ async function codexListPlugins(projectDir, options) {
6593
6693
  async function codexPluginsOrUnreadable(projectDir, options) {
6594
6694
  const pluginsRoot = codexPluginsRoot(projectDir, options);
6595
6695
  if (pluginsRoot === null) return { kind: "listed", plugins: [] };
6596
- const cacheRoot2 = path21.join(pluginsRoot, PLUGIN_CACHE_SUBDIR);
6696
+ const cacheRoot2 = path22.join(pluginsRoot, PLUGIN_CACHE_SUBDIR);
6597
6697
  let installed;
6598
6698
  try {
6599
6699
  installed = await codexPluginListing(options, projectDir);
@@ -6605,7 +6705,7 @@ async function codexPluginsOrUnreadable(projectDir, options) {
6605
6705
  kind: "listed",
6606
6706
  plugins: installed.map((plugin) => ({
6607
6707
  pluginKey: plugin.pluginId,
6608
- installPath: path21.join(cacheRoot2, plugin.marketplaceName, plugin.name, plugin.version),
6708
+ installPath: path22.join(cacheRoot2, plugin.marketplaceName, plugin.name, plugin.version),
6609
6709
  enabled: plugin.enabled
6610
6710
  }))
6611
6711
  };
@@ -6915,7 +7015,7 @@ function formatInstallationDisplay(info) {
6915
7015
  }
6916
7016
 
6917
7017
  // src/cli/lib/plugins/plugin-validator.ts
6918
- import path22 from "path";
7018
+ import path23 from "path";
6919
7019
 
6920
7020
  // src/cli/lib/validation-result.ts
6921
7021
  function validResult() {
@@ -6947,15 +7047,15 @@ async function validatePluginStructure(pluginPath) {
6947
7047
  if (!await directoryExists2(pluginPath)) {
6948
7048
  return invalidResult(`Plugin directory does not exist: ${pluginPath}`);
6949
7049
  }
6950
- const pluginDir = path22.join(pluginPath, PLUGIN_MANIFEST_DIR);
7050
+ const pluginDir = path23.join(pluginPath, PLUGIN_MANIFEST_DIR);
6951
7051
  if (!await directoryExists2(pluginDir)) {
6952
7052
  errors.push(`Missing ${PLUGIN_MANIFEST_DIR}/ directory`);
6953
7053
  }
6954
- const manifestPath = path22.join(pluginDir, STANDARD_FILES.PLUGIN_JSON);
7054
+ const manifestPath = path23.join(pluginDir, STANDARD_FILES.PLUGIN_JSON);
6955
7055
  if (!await fileExists(manifestPath)) {
6956
7056
  errors.push(`Missing ${PLUGIN_MANIFEST_DIR}/${STANDARD_FILES.PLUGIN_JSON}`);
6957
7057
  }
6958
- const readmePath = path22.join(pluginPath, STANDARD_FILES.README_MD);
7058
+ const readmePath = path23.join(pluginPath, STANDARD_FILES.README_MD);
6959
7059
  if (!await fileExists(readmePath)) {
6960
7060
  warnings.push("Missing README.md (recommended for documentation)");
6961
7061
  }
@@ -6998,15 +7098,15 @@ async function validatePluginManifest(manifestPath) {
6998
7098
  if (!manifest.description) {
6999
7099
  warnings.push("Missing description field (recommended for discoverability)");
7000
7100
  }
7001
- const pluginDir = path22.dirname(path22.dirname(manifestPath));
7101
+ const pluginDir = path23.dirname(path23.dirname(manifestPath));
7002
7102
  if (isNonEmptyString(manifest.skills)) {
7003
- const skillsPath = path22.join(pluginDir, manifest.skills);
7103
+ const skillsPath = path23.join(pluginDir, manifest.skills);
7004
7104
  if (!await directoryExists2(skillsPath)) {
7005
7105
  errors.push(`Skills path does not exist: ${manifest.skills}`);
7006
7106
  }
7007
7107
  }
7008
7108
  if (isNonEmptyString(manifest.agents)) {
7009
- const agentsPath = path22.join(pluginDir, manifest.agents);
7109
+ const agentsPath = path23.join(pluginDir, manifest.agents);
7010
7110
  if (!await directoryExists2(agentsPath)) {
7011
7111
  errors.push(`Agents path does not exist: ${manifest.agents}`);
7012
7112
  }
@@ -7075,14 +7175,14 @@ function prefixResult(result, prefix) {
7075
7175
  async function validateEachFile(pluginPath, scannedDir, files, validate) {
7076
7176
  const results = await Promise.all(
7077
7177
  files.map(async (file) => {
7078
- const filePath = path22.join(scannedDir, file);
7079
- return prefixResult(await validate(filePath), path22.relative(pluginPath, filePath));
7178
+ const filePath = path23.join(scannedDir, file);
7179
+ return prefixResult(await validate(filePath), path23.relative(pluginPath, filePath));
7080
7180
  })
7081
7181
  );
7082
7182
  return mergeValidationResults(results);
7083
7183
  }
7084
7184
  async function validatePluginSkillFiles(pluginPath, skillsRelPath) {
7085
- const skillsDir2 = path22.join(pluginPath, skillsRelPath);
7185
+ const skillsDir2 = path23.join(pluginPath, skillsRelPath);
7086
7186
  if (!await directoryExists2(skillsDir2)) return validResult();
7087
7187
  const files = await glob(`**/${STANDARD_FILES.SKILL_MD}`, skillsDir2);
7088
7188
  if (files.length === 0) {
@@ -7095,7 +7195,7 @@ async function validatePluginSkillFiles(pluginPath, skillsRelPath) {
7095
7195
  return validateEachFile(pluginPath, skillsDir2, files, validateSkillFrontmatter);
7096
7196
  }
7097
7197
  async function validatePluginAgentFiles(pluginPath, agentsRelPath) {
7098
- const agentsDir2 = path22.join(pluginPath, agentsRelPath);
7198
+ const agentsDir2 = path23.join(pluginPath, agentsRelPath);
7099
7199
  if (!await directoryExists2(agentsDir2)) return validResult();
7100
7200
  const files = await glob(agentCodec("claude").listGlob, agentsDir2);
7101
7201
  if (files.length === 0) {
@@ -7166,7 +7266,7 @@ async function listPluginDirNames(pluginsDir2) {
7166
7266
  const dirChecks = await Promise.all(
7167
7267
  allDirs.map(async (dirName) => ({
7168
7268
  dirName,
7169
- isPlugin: await directoryExists2(path22.join(pluginsDir2, dirName, PLUGIN_MANIFEST_DIR))
7269
+ isPlugin: await directoryExists2(path23.join(pluginsDir2, dirName, PLUGIN_MANIFEST_DIR))
7170
7270
  }))
7171
7271
  );
7172
7272
  return dirChecks.filter((c) => c.isPlugin).map((c) => c.dirName);
@@ -7174,7 +7274,7 @@ async function listPluginDirNames(pluginsDir2) {
7174
7274
  async function validateEachPlugin(pluginsDir2, pluginDirs) {
7175
7275
  const results = [];
7176
7276
  for (const pluginName of pluginDirs) {
7177
- const result = await validatePlugin(path22.join(pluginsDir2, pluginName));
7277
+ const result = await validatePlugin(path23.join(pluginsDir2, pluginName));
7178
7278
  results.push({ name: pluginName, result });
7179
7279
  }
7180
7280
  return results;
@@ -7257,11 +7357,11 @@ function sanitizeSkillName(name) {
7257
7357
  return name.replace(/\+/g, "-");
7258
7358
  }
7259
7359
  function isSingleFolderName(name) {
7260
- return name !== "" && name !== "." && name !== ".." && path23.basename(name) === name;
7360
+ return name !== "" && name !== "." && name !== ".." && path24.basename(name) === name;
7261
7361
  }
7262
7362
  async function assertClearable(pluginDir, skillPath, skillName) {
7263
- const skill = `Skill '${path23.basename(skillPath)}'`;
7264
- const file = `File: ${path23.join(skillPath, STANDARD_FILES.SKILL_MD)}`;
7363
+ const skill = `Skill '${path24.basename(skillPath)}'`;
7364
+ const file = `File: ${path24.join(skillPath, STANDARD_FILES.SKILL_MD)}`;
7265
7365
  if (!isSingleFolderName(skillName)) {
7266
7366
  throw new Error(
7267
7367
  `${skill} is named '${skillName}', which is not a single folder name, so its plugin cannot be built inside the output directory. ${file}`
@@ -7274,22 +7374,12 @@ async function assertClearable(pluginDir, skillPath, skillName) {
7274
7374
  }
7275
7375
  if (await holdsContentNoBuildMade(pluginDir)) {
7276
7376
  throw new Error(
7277
- `${skill} would be built into ${pluginDir}, which holds no ${path23.join(PLUGIN_MANIFEST_DIR, PLUGIN_MANIFEST_FILE)} and is not empty, so no earlier build made it and a rebuild would delete what it holds. Move its contents out, or build into another output directory. ${file}`
7377
+ `${skill} would be built into ${pluginDir}, which holds no ${path24.join(PLUGIN_MANIFEST_DIR, PLUGIN_MANIFEST_FILE)} and is not empty, so no earlier build made it and a rebuild would delete what it holds. Move its contents out, or build into another output directory. ${file}`
7278
7378
  );
7279
7379
  }
7280
7380
  }
7281
- async function isEarlierBuild(pluginDir) {
7282
- return fileExists(getPluginManifestPath(pluginDir));
7283
- }
7284
- async function holdsContentNoBuildMade(pluginDir) {
7285
- if (await isEarlierBuild(pluginDir)) return false;
7286
- return !await isDirectoryEmpty(pluginDir);
7287
- }
7288
- async function clearEarlierBuild(pluginDir) {
7289
- if (await isEarlierBuild(pluginDir)) await remove(pluginDir);
7290
- }
7291
7381
  async function readPluginSkillMetadata(skillPath) {
7292
- const metadataPath = path23.join(skillPath, STANDARD_FILES.METADATA_YAML);
7382
+ const metadataPath = path24.join(skillPath, STANDARD_FILES.METADATA_YAML);
7293
7383
  if (!await fileExists(metadataPath)) {
7294
7384
  return null;
7295
7385
  }
@@ -7333,28 +7423,28 @@ This skill is automatically available when installed.
7333
7423
  }
7334
7424
  async function writeSkillFiles(skillPath, pluginSkillDir, skillMdContent) {
7335
7425
  await ensureDir(pluginSkillDir);
7336
- await writeFile(path23.join(pluginSkillDir, STANDARD_FILES.SKILL_MD), skillMdContent);
7426
+ await writeFile(path24.join(pluginSkillDir, STANDARD_FILES.SKILL_MD), skillMdContent);
7337
7427
  verbose(` Copied ${STANDARD_FILES.SKILL_MD}`);
7338
7428
  for (const fileName of SKILL_CONTENT_FILES) {
7339
7429
  if (fileName === STANDARD_FILES.SKILL_MD) continue;
7340
- const sourcePath = path23.join(skillPath, fileName);
7430
+ const sourcePath = path24.join(skillPath, fileName);
7341
7431
  if (await fileExists(sourcePath)) {
7342
7432
  const content = await readFile(sourcePath);
7343
- await writeFile(path23.join(pluginSkillDir, fileName), content);
7433
+ await writeFile(path24.join(pluginSkillDir, fileName), content);
7344
7434
  verbose(` Copied ${fileName}`);
7345
7435
  }
7346
7436
  }
7347
7437
  for (const dirName of SKILL_CONTENT_DIRS) {
7348
- const sourceDir2 = path23.join(skillPath, dirName);
7438
+ const sourceDir2 = path24.join(skillPath, dirName);
7349
7439
  if (await fileExists(sourceDir2)) {
7350
- await copy(sourceDir2, path23.join(pluginSkillDir, dirName));
7440
+ await copy(sourceDir2, path24.join(pluginSkillDir, dirName));
7351
7441
  verbose(` Copied ${dirName}/`);
7352
7442
  }
7353
7443
  }
7354
7444
  }
7355
7445
  async function readSkillMdToBuild(skillPath) {
7356
- const dirBasename = path23.basename(skillPath);
7357
- const skillMdPath = path23.join(skillPath, STANDARD_FILES.SKILL_MD);
7446
+ const dirBasename = path24.basename(skillPath);
7447
+ const skillMdPath = path24.join(skillPath, STANDARD_FILES.SKILL_MD);
7358
7448
  if (!await fileExists(skillMdPath)) {
7359
7449
  throw new Error(
7360
7450
  `Skill '${dirBasename}' is missing required ${STANDARD_FILES.SKILL_MD} file. Expected at: ${skillMdPath}`
@@ -7373,7 +7463,7 @@ async function compileSkillPlugin(options) {
7373
7463
  const { skillPath, outputDir, skillName: overrideName } = options;
7374
7464
  const { skillMdContent, frontmatter } = await readSkillMdToBuild(skillPath);
7375
7465
  const skillName = overrideName ?? sanitizeSkillName(frontmatter.name);
7376
- const pluginDir = path23.join(outputDir, skillName);
7466
+ const pluginDir = path24.join(outputDir, skillName);
7377
7467
  await assertClearable(pluginDir, skillPath, skillName);
7378
7468
  verbose(`Compiling skill plugin: ${skillName} from ${skillPath}`);
7379
7469
  const metadata = await readPluginSkillMetadata(skillPath);
@@ -7390,11 +7480,11 @@ async function compileSkillPlugin(options) {
7390
7480
  await writeContentHash(pluginDir, await computeSkillFolderHash(skillPath), getPluginManifestPath);
7391
7481
  await writeSkillFiles(
7392
7482
  skillPath,
7393
- path23.join(pluginDir, STANDARD_DIRS.SKILLS, skillName),
7483
+ path24.join(pluginDir, STANDARD_DIRS.SKILLS, skillName),
7394
7484
  skillMdContent
7395
7485
  );
7396
7486
  const readme = generateReadme(skillName, frontmatter);
7397
- await writeFile(path23.join(pluginDir, STANDARD_FILES.README_MD), readme);
7487
+ await writeFile(path24.join(pluginDir, STANDARD_FILES.README_MD), readme);
7398
7488
  verbose(" Generated README.md");
7399
7489
  const manifest = manifestAt(
7400
7490
  versionAfterRebuild(earlierBuild, await computeShippedHash(pluginDir))
@@ -7412,7 +7502,7 @@ async function compileAllSkillPlugins(skillsDir2, outputDir) {
7412
7502
  const failed = [];
7413
7503
  const skillMdFiles = await glob(`**/${STANDARD_FILES.SKILL_MD}`, skillsDir2);
7414
7504
  for (const skillMdFile of skillMdFiles) {
7415
- const skillPath = path23.join(skillsDir2, path23.dirname(skillMdFile));
7505
+ const skillPath = path24.join(skillsDir2, path24.dirname(skillMdFile));
7416
7506
  try {
7417
7507
  const result = await compileSkillPlugin({
7418
7508
  skillPath,
@@ -7422,7 +7512,7 @@ async function compileAllSkillPlugins(skillsDir2, outputDir) {
7422
7512
  log(` [OK] ${result.skillName}`);
7423
7513
  } catch (error) {
7424
7514
  const errorMessage = getErrorMessage(error);
7425
- const dirBasename = path23.basename(skillPath);
7515
+ const dirBasename = path24.basename(skillPath);
7426
7516
  failed.push(dirBasename);
7427
7517
  warn(`Failed to compile skill from '${dirBasename}': ${errorMessage}`);
7428
7518
  }
@@ -7438,16 +7528,24 @@ Compiled ${plural(results.length, "skill plugin")}:`);
7438
7528
  }
7439
7529
 
7440
7530
  // src/cli/lib/skills/local-skill-loader.ts
7441
- import path24 from "path";
7442
- async function discoverLocalSkills(projectDir) {
7443
- const localSkillsPath = path24.join(projectDir, LOCAL_SKILLS_PATH);
7531
+ import path25 from "path";
7532
+ function copiedInSkillsAt(root) {
7533
+ const scope = isHomeDirectory(root) ? "global" : "project";
7534
+ const provider = providerInUse(root);
7535
+ return {
7536
+ dir: skillsDir(provider, scope, root),
7537
+ pathPrefix: skillsPathPrefix(provider, scope, root)
7538
+ };
7539
+ }
7540
+ async function discoverLocalSkills(root) {
7541
+ const { dir: localSkillsPath, pathPrefix } = copiedInSkillsAt(root);
7444
7542
  if (!await directoryExists2(localSkillsPath)) {
7445
7543
  verbose(`Local skills directory not found: ${localSkillsPath}`);
7446
7544
  return null;
7447
7545
  }
7448
7546
  const skillDirs = await listDirectories(localSkillsPath);
7449
7547
  const extracted = await Promise.all(
7450
- skillDirs.map((skillDirName) => extractLocalSkill(localSkillsPath, skillDirName))
7548
+ skillDirs.map((skillDirName) => extractLocalSkill(localSkillsPath, pathPrefix, skillDirName))
7451
7549
  );
7452
7550
  const skills = extracted.filter((skill) => skill !== null);
7453
7551
  verbose(`Discovered ${plural(skills.length, "local skill")} from ${localSkillsPath}`);
@@ -7456,10 +7554,10 @@ async function discoverLocalSkills(projectDir) {
7456
7554
  localSkillsPath
7457
7555
  };
7458
7556
  }
7459
- async function extractLocalSkill(localSkillsPath, skillDirName) {
7460
- const skillDir = path24.join(localSkillsPath, skillDirName);
7461
- const metadataPath = path24.join(skillDir, STANDARD_FILES.METADATA_YAML);
7462
- const skillMdPath = path24.join(skillDir, STANDARD_FILES.SKILL_MD);
7557
+ async function extractLocalSkill(localSkillsPath, pathPrefix, skillDirName) {
7558
+ const skillDir = path25.join(localSkillsPath, skillDirName);
7559
+ const metadataPath = path25.join(skillDir, STANDARD_FILES.METADATA_YAML);
7560
+ const skillMdPath = path25.join(skillDir, STANDARD_FILES.SKILL_MD);
7463
7561
  if (!await fileExists(metadataPath)) {
7464
7562
  verbose(`Skipping local skill '${skillDirName}': No metadata.yaml found`);
7465
7563
  return null;
@@ -7488,8 +7586,8 @@ async function extractLocalSkill(localSkillsPath, skillDirName) {
7488
7586
  verbose(`Skipping local skill '${skillDirName}': invalid SKILL.md frontmatter`);
7489
7587
  return null;
7490
7588
  }
7491
- const relativePath = `${LOCAL_SKILLS_PATH}/${skillDirName}/`;
7492
- const absolutePath = path24.join(localSkillsPath, skillDirName) + path24.sep;
7589
+ const relativePath = `${pathPrefix}/${skillDirName}/`;
7590
+ const absolutePath = path25.join(localSkillsPath, skillDirName) + path25.sep;
7493
7591
  const skillId = frontmatter.name;
7494
7592
  const extracted = {
7495
7593
  id: skillId,
@@ -7512,7 +7610,7 @@ async function extractLocalSkill(localSkillsPath, skillDirName) {
7512
7610
  }
7513
7611
 
7514
7612
  // src/cli/lib/skills/unresolved-skill-entries.ts
7515
- import path25 from "path";
7613
+ import path26 from "path";
7516
7614
  async function findUnusableSavedSkillMetadata(savedSkills, projectDir) {
7517
7615
  const verdicts = await Promise.all(
7518
7616
  savedSkills.map((saved) => unusableMetadataOf(saved, projectDir))
@@ -7531,11 +7629,11 @@ function claimsLocalCopy(saved) {
7531
7629
  }
7532
7630
  function installedMetadataPath(saved, projectDir) {
7533
7631
  const { skillsDir: skillsDir2 } = resolveInstallPaths(projectDir, saved.scope);
7534
- return path25.join(skillsDir2, saved.id, STANDARD_FILES.METADATA_YAML);
7632
+ return path26.join(skillsDir2, saved.id, STANDARD_FILES.METADATA_YAML);
7535
7633
  }
7536
7634
 
7537
7635
  // src/cli/lib/skills/local-skill-mover.ts
7538
- import path26 from "path";
7636
+ import path27 from "path";
7539
7637
  var THE_DIRECTORY_ITSELF = ".";
7540
7638
  function validateSkillId(skillId) {
7541
7639
  return !(skillId.length === 0 || skillId === THE_DIRECTORY_ITSELF || skillId.includes("\0") || skillId.includes("..") || skillId.includes("/") || skillId.includes("\\"));
@@ -7545,8 +7643,8 @@ async function deleteLocalSkill(projectDir, skillId, scope = "project") {
7545
7643
  warn(`Invalid skill ID for deletion: '${skillId}'`);
7546
7644
  return;
7547
7645
  }
7548
- const skillsDir2 = path26.resolve(resolveInstallPaths(projectDir, scope).skillsDir);
7549
- const skillPath = path26.resolve(skillsDir2, skillId);
7646
+ const skillsDir2 = path27.resolve(resolveInstallPaths(projectDir, scope).skillsDir);
7647
+ const skillPath = path27.resolve(skillsDir2, skillId);
7550
7648
  if (!isPathWithin(skillPath, skillsDir2)) {
7551
7649
  warn(`Skill ID '${skillId}' resolves outside the skills directory.`);
7552
7650
  return;
@@ -7564,10 +7662,10 @@ async function migrateLocalSkillScope(skillId, fromScope, projectDir) {
7564
7662
  return;
7565
7663
  }
7566
7664
  const toScope = fromScope === "global" ? "project" : "global";
7567
- const fromSkillsDir = path26.resolve(resolveInstallPaths(projectDir, fromScope).skillsDir);
7568
- const toSkillsDir = path26.resolve(resolveInstallPaths(projectDir, toScope).skillsDir);
7569
- const fromPath = path26.resolve(fromSkillsDir, skillId);
7570
- const toPath = path26.resolve(toSkillsDir, skillId);
7665
+ const fromSkillsDir = path27.resolve(resolveInstallPaths(projectDir, fromScope).skillsDir);
7666
+ const toSkillsDir = path27.resolve(resolveInstallPaths(projectDir, toScope).skillsDir);
7667
+ const fromPath = path27.resolve(fromSkillsDir, skillId);
7668
+ const toPath = path27.resolve(toSkillsDir, skillId);
7571
7669
  if (!isPathWithin(fromPath, fromSkillsDir)) {
7572
7670
  warn(`Skill ID '${skillId}' resolves outside the source skills directory.`);
7573
7671
  return;
@@ -7594,7 +7692,7 @@ async function migrateLocalSkillScope(skillId, fromScope, projectDir) {
7594
7692
 
7595
7693
  // src/cli/lib/matrix/matrix-loader.ts
7596
7694
  import { parse as parseYaml5 } from "yaml";
7597
- import path27 from "path";
7695
+ import path28 from "path";
7598
7696
  async function loadSkillCategories(configPath) {
7599
7697
  const data = await loadConfig(configPath, skillCategoriesFileSchema);
7600
7698
  if (!data) {
@@ -7630,13 +7728,13 @@ async function extractAllSkills(skillsDir2) {
7630
7728
  return skills;
7631
7729
  }
7632
7730
  async function extractSkill(skillsDir2, metadataFile) {
7633
- const skillDir = path27.dirname(metadataFile);
7634
- const skillMdPath = path27.join(skillsDir2, skillDir, STANDARD_FILES.SKILL_MD);
7731
+ const skillDir = path28.dirname(metadataFile);
7732
+ const skillMdPath = path28.join(skillsDir2, skillDir, STANDARD_FILES.SKILL_MD);
7635
7733
  if (!await fileExists(skillMdPath)) {
7636
7734
  verbose(`Skipping ${metadataFile}: No ${STANDARD_FILES.SKILL_MD} found`);
7637
7735
  return null;
7638
7736
  }
7639
- const metadata = await readMatrixMetadataOrWarn(metadataFile, path27.join(skillsDir2, metadataFile));
7737
+ const metadata = await readMatrixMetadataOrWarn(metadataFile, path28.join(skillsDir2, metadataFile));
7640
7738
  if (!metadata) return null;
7641
7739
  const frontmatter = parseFrontmatterOrWarn(skillDir, skillMdPath, await readFile(skillMdPath));
7642
7740
  if (!frontmatter) return null;
@@ -7689,7 +7787,7 @@ function parseFrontmatterOrWarn(skillDir, skillMdPath, skillMdContent) {
7689
7787
  const unreadable = unreadableFrontmatter(skillMdContent);
7690
7788
  if (unreadable !== void 0) {
7691
7789
  warn(
7692
- `Skipping '${path27.join(skillDir, STANDARD_FILES.SKILL_MD)}': unparseable ${STANDARD_FILES.SKILL_MD} frontmatter at ${skillMdPath} \u2014 ${unreadable}`
7790
+ `Skipping '${path28.join(skillDir, STANDARD_FILES.SKILL_MD)}': unparseable ${STANDARD_FILES.SKILL_MD} frontmatter at ${skillMdPath} \u2014 ${unreadable}`
7693
7791
  );
7694
7792
  }
7695
7793
  return null;
@@ -9800,7 +9898,7 @@ async function loadAndMergeFromBasePath(basePath, source) {
9800
9898
  const skillsDirRelPath = sourceProjectConfig?.skillsDir ?? SKILLS_DIR_PATH;
9801
9899
  const stacksRelFile = sourceProjectConfig?.stacksFile;
9802
9900
  const { categories, sourceRules } = await loadSourceTaxonomy(basePath);
9803
- const skillsDir2 = path28.join(basePath, skillsDirRelPath);
9901
+ const skillsDir2 = path29.join(basePath, skillsDirRelPath);
9804
9902
  verbose(`Skills from source: ${skillsDir2}`);
9805
9903
  const skills = await extractAllSkills(skillsDir2);
9806
9904
  await refuseCatalogueCollisions(basePath, source, skills);
@@ -9823,7 +9921,7 @@ async function loadSourceTaxonomy(basePath) {
9823
9921
  return { categories, sourceRules };
9824
9922
  }
9825
9923
  async function loadSourceCategories(basePath) {
9826
- const categoriesPath = path28.join(basePath, SKILL_CATEGORIES_PATH);
9924
+ const categoriesPath = path29.join(basePath, SKILL_CATEGORIES_PATH);
9827
9925
  if (!await fileExists(categoriesPath)) return void 0;
9828
9926
  const categories = await loadSkillCategories(categoriesPath);
9829
9927
  verbose(
@@ -9832,7 +9930,7 @@ async function loadSourceCategories(basePath) {
9832
9930
  return categories;
9833
9931
  }
9834
9932
  async function loadSourceRules(basePath) {
9835
- const rulesPath = path28.join(basePath, SKILL_RULES_PATH);
9933
+ const rulesPath = path29.join(basePath, SKILL_RULES_PATH);
9836
9934
  if (!await fileExists(rulesPath)) return void 0;
9837
9935
  const rules = await loadSkillRules(rulesPath);
9838
9936
  verbose(`Loaded source rules: ${rulesPath}`);
@@ -9847,11 +9945,11 @@ async function refuseCatalogueCollisions(basePath, source, skills) {
9847
9945
  throw new Error(catalogueCollisionError(collidingIds, source));
9848
9946
  }
9849
9947
  function catalogueCollisionError(collidingIds, source) {
9850
- const listed = collidingIds.slice(0, MAX_REPORTED_COLLISIONS).map((id) => ` ${id}`);
9851
- const unlisted = collidingIds.length - listed.length;
9948
+ const listed2 = collidingIds.slice(0, MAX_REPORTED_COLLISIONS).map((id) => ` ${id}`);
9949
+ const unlisted = collidingIds.length - listed2.length;
9852
9950
  return [
9853
9951
  `Marketplace '${source}' ships ${collidingIds.length} skill id(s) the public catalogue already owns:`,
9854
- ...listed,
9952
+ ...listed2,
9855
9953
  ...unlisted > 0 ? [` ... and ${unlisted} more`] : [],
9856
9954
  `A skill id is the directory the skill installs into, so these would shadow the public catalogue's own skills. Every id must carry its marketplace's name as a namespace \u2014 '<marketplace>-<id>'. Rename each skill and the 'name' in its SKILL.md, then re-run 'build marketplace', which refuses the same ids before they are published.`
9857
9955
  ].join("\n");
@@ -9940,7 +10038,7 @@ function declaresCategory(matrix2, category) {
9940
10038
  return category !== LOCAL_PSEUDO_CATEGORY && matrix2.categories[category] !== void 0;
9941
10039
  }
9942
10040
  function undeclaredCategoryRefusal(metadata, category) {
9943
- const metadataPath = path28.join(metadata.path, STANDARD_FILES.METADATA_YAML);
10041
+ const metadataPath = path29.join(metadata.path, STANDARD_FILES.METADATA_YAML);
9944
10042
  return `Skipping local skill '${metadata.id}': ${METADATA_KEYS.CATEGORY} '${category}' is not one this installation declares, so the skill belongs in no grid tab and can be given to no sub-agent. Set ${METADATA_KEYS.CATEGORY} in ${metadataPath} to a category that already exists \u2014 a skill is placed in the taxonomy, it does not extend it.`;
9945
10043
  }
9946
10044
  function mergeLocalSkillsIntoMatrix(matrix2, localResult) {
@@ -10076,15 +10174,14 @@ export {
10076
10174
  skippedUnknownAgents,
10077
10175
  carriedSkillsWritten,
10078
10176
  SHARED_CONFIG_APPLY,
10079
- projectCopyRemoved,
10080
- projectCopyMovedToGlobal,
10081
- registeredProjectsRewritten,
10177
+ registeredProjectsRecompiled,
10082
10178
  sharedConfigNeedsTerminal,
10083
10179
  nothingInstalledToApplyTo,
10084
10180
  uninstallNeedsTerminal,
10085
10181
  addMarketplaceInTheEditor,
10086
10182
  SHARED_CONFIG_ARRIVALS,
10087
10183
  keptAsInstalledGlobally,
10184
+ subAgentChanged,
10088
10185
  addedToGlobalSubAgents,
10089
10186
  authoredHereKept,
10090
10187
  unplaceableKept,
@@ -10094,11 +10191,10 @@ export {
10094
10191
  uninstallSkillMetadataUnreadable,
10095
10192
  recreateConfigFrom,
10096
10193
  providerArgument,
10194
+ configUnreadableError,
10097
10195
  registeredProjectsUpdateFailed,
10098
10196
  unmarkedAgentsKept,
10099
- UNINSTALL_PLAN,
10100
- localSkillsRemoval,
10101
- compiledAgentsRemoval,
10197
+ configFileRemoved,
10102
10198
  scopeNoun,
10103
10199
  scopeLabel,
10104
10200
  layoutIsCurrent,
@@ -10106,6 +10202,7 @@ export {
10106
10202
  GlobalPairWriteViolation,
10107
10203
  withGateToken,
10108
10204
  assertGateToken,
10205
+ isPathWithin,
10109
10206
  readFile,
10110
10207
  readFileSafe,
10111
10208
  readFileOptional,
@@ -10135,11 +10232,14 @@ export {
10135
10232
  validateSkillMetadata,
10136
10233
  splitMetadataValidationIssues,
10137
10234
  ConfigDefaultExportError,
10235
+ NO_VALID_DEFAULT_EXPORT,
10236
+ configLoadFailure,
10138
10237
  loadConfig,
10139
10238
  getProjectConfigPath,
10140
10239
  getInstalledConfigPath,
10141
10240
  resolveInstallPaths,
10142
10241
  providerToNameAt,
10242
+ initProviderToNameAt,
10143
10243
  isHomeDirectory,
10144
10244
  SOURCE_ENV_VAR,
10145
10245
  readSourceRepoConfig,
@@ -10150,11 +10250,8 @@ export {
10150
10250
  resolvePrimarySourceEntry,
10151
10251
  MarketplaceRefusedError,
10152
10252
  isLocalSource,
10153
- typedEntries,
10154
- typedKeys,
10155
- typedFromEntries,
10156
- typedValues,
10157
10253
  normalizeStackRecord,
10254
+ loadStackById,
10158
10255
  defaultUsageGuidance,
10159
10256
  resolveAgentConfigToSkills,
10160
10257
  getStackSkillIds,
@@ -10186,8 +10283,8 @@ export {
10186
10283
  generateConfigSource,
10187
10284
  refuseUnwritableStack,
10188
10285
  generateBlankGlobalConfigSource,
10189
- typedEntries2,
10190
- typedKeys2,
10286
+ typedEntries2 as typedEntries,
10287
+ typedKeys2 as typedKeys,
10191
10288
  isScopePairCompatible,
10192
10289
  splitConfigByScope,
10193
10290
  seedToInstallSelection,
@@ -10201,9 +10298,12 @@ export {
10201
10298
  loadSkillsFromDir,
10202
10299
  getCurrentDate,
10203
10300
  computeStringHash,
10204
- determinePluginVersion,
10301
+ computeShippedHash,
10302
+ readEarlierBuild,
10303
+ versionAfterRebuild,
10205
10304
  writeContentHash,
10206
10305
  readForkedFromMetadata,
10306
+ readLocalSkillMetadata,
10207
10307
  validateSkillPath,
10208
10308
  copySkillsToLocalFlattened,
10209
10309
  getUserPluginsDir,
@@ -10216,6 +10316,8 @@ export {
10216
10316
  writePluginManifest,
10217
10317
  buildMarketplacePluginRef,
10218
10318
  parseMarketplacePluginRef,
10319
+ holdsContentNoBuildMade,
10320
+ clearEarlierBuild,
10219
10321
  getInstallationInfo,
10220
10322
  getInstallationsByProvider,
10221
10323
  formatInstallationDisplay,
@@ -10248,6 +10350,8 @@ export {
10248
10350
  validateSkillIdNamespace,
10249
10351
  writeMarketplace,
10250
10352
  getMarketplaceStats,
10353
+ MarketplaceUnreachableError,
10354
+ askingEachMarketplaceItself,
10251
10355
  MarketplaceNameRefusedError,
10252
10356
  fetchMarketplace,
10253
10357
  isLocalOnlySkill,
@@ -10271,4 +10375,4 @@ export {
10271
10375
  executeMigration,
10272
10376
  keepsGlobalInstall
10273
10377
  };
10274
- //# sourceMappingURL=chunk-GAKVZNTX.js.map
10378
+ //# sourceMappingURL=chunk-VILFCMRN.js.map