agents-inc 0.171.0 → 0.173.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.
- package/CHANGELOG.md +31 -0
- package/dist/{chunk-W4KAY26P.js → chunk-2RTBSLSR.js} +12 -3
- package/dist/{chunk-W4KAY26P.js.map → chunk-2RTBSLSR.js.map} +1 -1
- package/dist/{chunk-WM2N2D6Q.js → chunk-3N4FSTVX.js} +12 -6
- package/dist/{chunk-WM2N2D6Q.js.map → chunk-3N4FSTVX.js.map} +1 -1
- package/dist/{chunk-ZC6GC66J.js → chunk-3R3LHA4Q.js} +2 -2
- package/dist/{chunk-24OD7IP4.js → chunk-7OG635DK.js} +26 -4
- package/dist/chunk-7OG635DK.js.map +1 -0
- package/dist/{chunk-WDH6HHA3.js → chunk-A3ZAXL5T.js} +4 -5
- package/dist/{chunk-WDH6HHA3.js.map → chunk-A3ZAXL5T.js.map} +1 -1
- package/dist/{chunk-KMOCMVK5.js → chunk-AARQBW5L.js} +106 -60
- package/dist/chunk-AARQBW5L.js.map +1 -0
- package/dist/{chunk-WII6GOPM.js → chunk-BRZA7ZCX.js} +52 -31
- package/dist/chunk-BRZA7ZCX.js.map +1 -0
- package/dist/{chunk-AWWCRL5A.js → chunk-EHJIQDXR.js} +8 -6
- package/dist/{chunk-AWWCRL5A.js.map → chunk-EHJIQDXR.js.map} +1 -1
- package/dist/{chunk-RDXY4GRO.js → chunk-F437ICEG.js} +2 -2
- package/dist/chunk-G7NUK3BF.js +253 -0
- package/dist/chunk-G7NUK3BF.js.map +1 -0
- package/dist/{chunk-7IVLVEHX.js → chunk-ITQF35UQ.js} +3 -3
- package/dist/{chunk-QIR56IDM.js → chunk-LJQXQTFC.js} +29 -102
- package/dist/chunk-LJQXQTFC.js.map +1 -0
- package/dist/{chunk-A6OWEJGC.js → chunk-MHAQWMNJ.js} +35 -5
- package/dist/{chunk-A6OWEJGC.js.map → chunk-MHAQWMNJ.js.map} +1 -1
- package/dist/{chunk-YLUOGAW7.js → chunk-N33AKFLR.js} +2 -2
- package/dist/{chunk-TNFYBKVD.js → chunk-Q4UNHVPX.js} +25 -14
- package/dist/chunk-Q4UNHVPX.js.map +1 -0
- package/dist/chunk-Q6WRR4BN.js +195 -0
- package/dist/chunk-Q6WRR4BN.js.map +1 -0
- package/dist/{chunk-JGPE4RRE.js → chunk-SPQSNZMM.js} +2 -2
- package/dist/{chunk-EOKNEGRD.js → chunk-VR6QTK5Z.js} +8 -8
- package/dist/{chunk-S2SKVJT2.js → chunk-VSPC74SS.js} +48 -26
- package/dist/chunk-VSPC74SS.js.map +1 -0
- package/dist/{chunk-3YYHL76L.js → chunk-WVFBBZEP.js} +648 -394
- package/dist/chunk-WVFBBZEP.js.map +1 -0
- package/dist/commands/build/marketplace.js +16 -14
- package/dist/commands/build/marketplace.js.map +1 -1
- package/dist/commands/build/plugins.js +6 -6
- package/dist/commands/compile.js +12 -12
- package/dist/commands/doctor.js +87 -86
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/edit.js +150 -152
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/eject.js +25 -30
- package/dist/commands/eject.js.map +1 -1
- package/dist/commands/init.js +19 -16
- package/dist/commands/list.js +14 -17
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/new/marketplace.js +4 -4
- package/dist/commands/search.js +15 -13
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/share.js +11 -11
- package/dist/commands/uninstall.js +145 -130
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.js +6 -6
- package/dist/components/common/scroll-affordance.js +2 -2
- package/dist/components/common/select-list.js +2 -2
- package/dist/components/common/skill-agent-summary.js +3 -4
- package/dist/components/common/summary-panel.js +11 -146
- package/dist/components/common/summary-panel.js.map +1 -1
- package/dist/hooks/init.js +39 -27
- package/dist/hooks/init.js.map +1 -1
- package/dist/load-agent-defs-E67DSYTN.js +14 -0
- package/dist/{recompile-project-agents-IQANWGOP.js → recompile-project-agents-6G6M2RS6.js} +8 -8
- package/dist/{source-loader-YGG7DY2W.js → source-loader-W7XCNKNA.js} +4 -4
- package/dist/src/agents/tester/web-tester/playbook.md +1 -1
- package/package.json +1 -1
- package/src/agents/tester/web-tester/playbook.md +1 -1
- package/dist/chunk-24OD7IP4.js.map +0 -1
- package/dist/chunk-3YYHL76L.js.map +0 -1
- package/dist/chunk-4RQKCV6I.js +0 -103
- package/dist/chunk-4RQKCV6I.js.map +0 -1
- package/dist/chunk-5FM77WKW.js +0 -69
- package/dist/chunk-5FM77WKW.js.map +0 -1
- package/dist/chunk-KMOCMVK5.js.map +0 -1
- package/dist/chunk-QIR56IDM.js.map +0 -1
- package/dist/chunk-R572RLTI.js +0 -108
- package/dist/chunk-R572RLTI.js.map +0 -1
- package/dist/chunk-S2SKVJT2.js.map +0 -1
- package/dist/chunk-TNFYBKVD.js.map +0 -1
- package/dist/chunk-WII6GOPM.js.map +0 -1
- package/dist/components/common/removal-plan-confirm.js +0 -10
- package/dist/load-agent-defs-UBWITIEI.js +0 -14
- package/dist/source-loader-YGG7DY2W.js.map +0 -1
- /package/dist/{chunk-ZC6GC66J.js.map → chunk-3R3LHA4Q.js.map} +0 -0
- /package/dist/{chunk-RDXY4GRO.js.map → chunk-F437ICEG.js.map} +0 -0
- /package/dist/{chunk-7IVLVEHX.js.map → chunk-ITQF35UQ.js.map} +0 -0
- /package/dist/{chunk-YLUOGAW7.js.map → chunk-N33AKFLR.js.map} +0 -0
- /package/dist/{chunk-JGPE4RRE.js.map → chunk-SPQSNZMM.js.map} +0 -0
- /package/dist/{chunk-EOKNEGRD.js.map → chunk-VR6QTK5Z.js.map} +0 -0
- /package/dist/{components/common/removal-plan-confirm.js.map → load-agent-defs-E67DSYTN.js.map} +0 -0
- /package/dist/{recompile-project-agents-IQANWGOP.js.map → recompile-project-agents-6G6M2RS6.js.map} +0 -0
- /package/dist/{load-agent-defs-UBWITIEI.js.map → source-loader-W7XCNKNA.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
|
-
|
|
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-
|
|
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
|
|
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
|
-
|
|
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;
|
|
@@ -736,8 +744,8 @@ async function fileExists(filePath) {
|
|
|
736
744
|
}
|
|
737
745
|
async function directoryExists2(dirPath) {
|
|
738
746
|
try {
|
|
739
|
-
const
|
|
740
|
-
return
|
|
747
|
+
const stat2 = await fs3.stat(dirPath);
|
|
748
|
+
return stat2.isDirectory();
|
|
741
749
|
} catch {
|
|
742
750
|
return false;
|
|
743
751
|
}
|
|
@@ -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-]/;
|
|
@@ -942,6 +953,8 @@ var INFO_MESSAGES = {
|
|
|
942
953
|
CONFIG_TYPES_REFRESHED: `Refreshed ${STANDARD_FILES.CONFIG_TYPES_TS}`,
|
|
943
954
|
EJECTED_SKILLS_USER_OWNED: `Ejected skills are yours to own \u2014 '${CLI_INVOKE_COMMAND} update' does not change them.`,
|
|
944
955
|
NO_PLUGIN_MARKETPLACES: "No plugin marketplaces are configured \u2014 nothing to refresh.",
|
|
956
|
+
/** Under doctor's marketplace row, when its download replaced a saved copy whose files differed. */
|
|
957
|
+
SAVED_COPY_REPLACED: "The saved copy differed from the marketplace and has been replaced",
|
|
945
958
|
/** Printed under the destination already named, so it names files only. */
|
|
946
959
|
AGENT_PARTIALS_CUSTOMIZABLE: `Each sub-agent directory there holds ${AGENT_PARTIAL_FILES.join(", ")} \u2014 edit those to customize that sub-agent.`
|
|
947
960
|
};
|
|
@@ -974,9 +987,9 @@ function completedWithFailures(failures) {
|
|
|
974
987
|
function failureLines({ what, recovery }) {
|
|
975
988
|
return [` ${what}`, ` ${recovery}`];
|
|
976
989
|
}
|
|
977
|
-
function propagatedRecompileSummary(
|
|
990
|
+
function propagatedRecompileSummary(recompiled, failed) {
|
|
978
991
|
const failureSuffix = failed > 0 ? ` (${failed} failed)` : "";
|
|
979
|
-
return `Registered projects: ${
|
|
992
|
+
return `Registered projects: ${recompiled} recompiled${failureSuffix}`;
|
|
980
993
|
}
|
|
981
994
|
function skillAssignedToNoAgent(skillId) {
|
|
982
995
|
return `Skill '${skillId}' is assigned to no sub-agent \u2014 nothing will load it.`;
|
|
@@ -1133,22 +1146,25 @@ function sharedSkillsLeftBehind(skillIds) {
|
|
|
1133
1146
|
function sharedConfigExistingInstall(configPath, id, provider) {
|
|
1134
1147
|
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
1148
|
}
|
|
1136
|
-
function sharedConfigProjectScopeAtHome(skillIds, agentNames) {
|
|
1149
|
+
function sharedConfigProjectScopeAtHome(skillIds, agentNames, installedHere, provider) {
|
|
1137
1150
|
return [
|
|
1138
1151
|
"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
1152
|
...skillIds.map((id) => ` skill ${id} (scope: project)`),
|
|
1140
1153
|
...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."
|
|
1154
|
+
installedHere ? changeTheGlobalInstallFromHome(providerArgument(provider)) : "Run this from inside a project directory, or re-share it with each entry above at global scope."
|
|
1142
1155
|
].join("\n");
|
|
1143
1156
|
}
|
|
1157
|
+
function changeTheGlobalInstallFromHome(named) {
|
|
1158
|
+
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}'.`;
|
|
1159
|
+
}
|
|
1144
1160
|
function unknownKeys(keys) {
|
|
1145
|
-
return keys.map(
|
|
1161
|
+
return keys.map(inertLine).join(", ");
|
|
1146
1162
|
}
|
|
1147
|
-
function skippedUnknownSkills(skillIds,
|
|
1163
|
+
function skippedUnknownSkills(skillIds, installedWithout) {
|
|
1148
1164
|
return [
|
|
1149
1165
|
`Skipped ${skillIds.length} skill(s) this catalog does not know: ${unknownKeys(skillIds)}`,
|
|
1150
|
-
...
|
|
1151
|
-
({ skillId, agentNames }) => ` sub-agent(s)
|
|
1166
|
+
...installedWithout.map(
|
|
1167
|
+
({ skillId, agentNames }) => ` sub-agent(s) installed without ${unknownKeys([skillId])}, carrying nothing else this catalog knows: ${agentNames.join(", ")}`
|
|
1152
1168
|
)
|
|
1153
1169
|
].join("\n");
|
|
1154
1170
|
}
|
|
@@ -1159,30 +1175,18 @@ function carriedSkillsWritten(skillIds) {
|
|
|
1159
1175
|
return `Wrote ${skillIds.length} skill(s) this configuration carries: ${skillIds.join(", ")}`;
|
|
1160
1176
|
}
|
|
1161
1177
|
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
1178
|
CONFIRM: "Apply this configuration?"
|
|
1168
1179
|
};
|
|
1169
|
-
function
|
|
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) {
|
|
1180
|
+
function registeredProjectsRecompiled(projectPaths) {
|
|
1176
1181
|
return [
|
|
1177
|
-
"Each project registered with this global install is
|
|
1182
|
+
"Each project registered with this global install is recompiled to match:",
|
|
1178
1183
|
...projectPaths.map((projectPath) => ` ${projectPath}`)
|
|
1179
1184
|
].join("\n");
|
|
1180
1185
|
}
|
|
1181
|
-
function sharedConfigNeedsTerminal(id,
|
|
1182
|
-
const named = providerArgument(provider);
|
|
1186
|
+
function sharedConfigNeedsTerminal(id, editProvider, initProvider) {
|
|
1183
1187
|
return [
|
|
1184
1188
|
`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}${
|
|
1189
|
+
`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
1190
|
].join("\n");
|
|
1187
1191
|
}
|
|
1188
1192
|
function nothingInstalledToApplyTo(id, provider) {
|
|
@@ -1199,21 +1203,38 @@ function addMarketplaceInTheEditor(ref, isFolder) {
|
|
|
1199
1203
|
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
1204
|
}
|
|
1201
1205
|
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
1206
|
INSTALL_CONFIRM: "Install this configuration?"
|
|
1206
1207
|
};
|
|
1207
|
-
function keptAsInstalledGlobally(skillIds, agentNames, idAppliedAtHome) {
|
|
1208
|
+
function keptAsInstalledGlobally(skillIds, agentNames, idAppliedAtHome, provider) {
|
|
1208
1209
|
return [
|
|
1209
1210
|
"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
1211
|
...skillIds.map((id) => ` skill ${id}`),
|
|
1211
1212
|
...agentNames.map((name) => ` sub-agent ${name}`),
|
|
1212
1213
|
...idAppliedAtHome === void 0 ? [] : [
|
|
1213
|
-
`Change them with '${CLI_INVOKE_COMMAND} edit --from ${idAppliedAtHome}' from your home directory.`
|
|
1214
|
+
`Change them with '${CLI_INVOKE_COMMAND} edit --from ${idAppliedAtHome}${providerArgument(provider)}' from your home directory.`
|
|
1214
1215
|
]
|
|
1215
1216
|
].join("\n");
|
|
1216
1217
|
}
|
|
1218
|
+
function skillsMoved(verb, skillIds) {
|
|
1219
|
+
return skillIds.length === 0 ? [] : [`${verb} ${listed(skillIds.map(getSkillDisplayName))}`];
|
|
1220
|
+
}
|
|
1221
|
+
function settingMoved({ setting, from, to }) {
|
|
1222
|
+
return `${setting} ${from ?? "default"} \u2192 ${to ?? "default"}`;
|
|
1223
|
+
}
|
|
1224
|
+
function listed(names) {
|
|
1225
|
+
if (names.length <= 1) return names.join("");
|
|
1226
|
+
return `${names.slice(0, -1).join(", ")} and ${names.at(-1)}`;
|
|
1227
|
+
}
|
|
1228
|
+
var SKILLS_REASSIGNED = "skills reassigned";
|
|
1229
|
+
function subAgentChanged({ name, scope, gains, loses, settings }) {
|
|
1230
|
+
const details = [
|
|
1231
|
+
...skillsMoved("gains", gains),
|
|
1232
|
+
...skillsMoved("loses", loses),
|
|
1233
|
+
...settings.map(settingMoved)
|
|
1234
|
+
];
|
|
1235
|
+
const where = scope === "global" ? " (global)" : "";
|
|
1236
|
+
return `~ ${name}${where}: ${details.length > 0 ? details.join(", ") : SKILLS_REASSIGNED}`;
|
|
1237
|
+
}
|
|
1217
1238
|
function addedToGlobalSubAgents(joined) {
|
|
1218
1239
|
return [
|
|
1219
1240
|
"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 +1281,18 @@ function uninstallSkillMetadataUnreadable(metadataPath) {
|
|
|
1260
1281
|
}
|
|
1261
1282
|
function recreateConfigFrom({
|
|
1262
1283
|
scopeRoot,
|
|
1263
|
-
providerToName
|
|
1284
|
+
providerToName,
|
|
1285
|
+
initProviderToName
|
|
1264
1286
|
}) {
|
|
1265
|
-
|
|
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}`;
|
|
1287
|
+
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
1288
|
}
|
|
1268
1289
|
function providerArgument(provider) {
|
|
1269
|
-
|
|
1290
|
+
if (provider === void 0) return "";
|
|
1291
|
+
return ` --provider ${typeof provider === "string" ? provider : `<${provider.join("|")}>`}`;
|
|
1270
1292
|
}
|
|
1271
|
-
function configUnreadableError(
|
|
1293
|
+
function configUnreadableError(configLoadFailure3) {
|
|
1272
1294
|
return [
|
|
1273
|
-
|
|
1295
|
+
configLoadFailure3,
|
|
1274
1296
|
"This is the marketplace's own config rather than an installation's, so it is fixed in the marketplace repository it comes from."
|
|
1275
1297
|
].join("\n");
|
|
1276
1298
|
}
|
|
@@ -1281,17 +1303,8 @@ function unmarkedAgentsKept(agentsDir2, count) {
|
|
|
1281
1303
|
const object = count === 1 ? "it" : "them";
|
|
1282
1304
|
return `Kept ${plural(count, "agent")} in ${agentsDir2}/ \u2014 no ${DEFAULT_PLUGIN_NAME} marker, so this CLI did not compile ${object}.`;
|
|
1283
1305
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
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)`;
|
|
1306
|
+
function configFileRemoved(filePath) {
|
|
1307
|
+
return `- ${filePath}`;
|
|
1295
1308
|
}
|
|
1296
1309
|
function scopeNoun(kind) {
|
|
1297
1310
|
return kind === "project" ? "this project" : "the global installation";
|
|
@@ -1305,8 +1318,8 @@ function sentenceCase(text) {
|
|
|
1305
1318
|
function layoutIsCurrent(label, to) {
|
|
1306
1319
|
return `${label} is on ${to}/`;
|
|
1307
1320
|
}
|
|
1308
|
-
function retiredSourceFolderUnsupported(
|
|
1309
|
-
return `${RETIRED_SOURCE_FOLDER}/ is no longer supported (agents-inc now serves Claude and Codex). Move it to ${
|
|
1321
|
+
function retiredSourceFolderUnsupported(root) {
|
|
1322
|
+
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
1323
|
}
|
|
1311
1324
|
|
|
1312
1325
|
// src/cli/types/generated/source-types.ts
|
|
@@ -1930,24 +1943,10 @@ function isSkillAssignment(value) {
|
|
|
1930
1943
|
return typeof value.id === "string";
|
|
1931
1944
|
}
|
|
1932
1945
|
|
|
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
1946
|
// src/cli/lib/configuration/config.ts
|
|
1948
1947
|
import { BlockList, isIPv4 } from "net";
|
|
1949
1948
|
import os4 from "os";
|
|
1950
|
-
import
|
|
1949
|
+
import path6 from "path";
|
|
1951
1950
|
import { z as z3 } from "zod";
|
|
1952
1951
|
|
|
1953
1952
|
// src/cli/lib/schemas.ts
|
|
@@ -1957,11 +1956,11 @@ import { partition } from "remeda";
|
|
|
1957
1956
|
// src/cli/lib/schema-validator.ts
|
|
1958
1957
|
import "zod";
|
|
1959
1958
|
function formatZodIssue(issue) {
|
|
1960
|
-
const
|
|
1959
|
+
const path30 = stripTerminalControls(issue.path.join("."));
|
|
1961
1960
|
if (issue.code === "unrecognized_keys") {
|
|
1962
1961
|
return `Unrecognized key: "${stripTerminalControls(issue.keys.join('", "'))}"`;
|
|
1963
1962
|
}
|
|
1964
|
-
return
|
|
1963
|
+
return path30 ? `${path30}: ${stripTerminalControls(issue.message)}` : stripTerminalControls(issue.message);
|
|
1965
1964
|
}
|
|
1966
1965
|
function formatZodErrors(error) {
|
|
1967
1966
|
return error.issues.map(formatZodIssue);
|
|
@@ -2103,12 +2102,12 @@ var RENAMED_SKILL_ENTRY_FIELDS = { source: "origin" };
|
|
|
2103
2102
|
function renamedFieldMessage(oldKey, newKey) {
|
|
2104
2103
|
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
2104
|
}
|
|
2106
|
-
function reportRenamedKeys(record, renames,
|
|
2105
|
+
function reportRenamedKeys(record, renames, path30, ctx) {
|
|
2107
2106
|
for (const [oldKey, newKey] of Object.entries(renames)) {
|
|
2108
2107
|
if (!(oldKey in record)) continue;
|
|
2109
2108
|
ctx.addIssue({
|
|
2110
2109
|
code: "custom",
|
|
2111
|
-
path: [...
|
|
2110
|
+
path: [...path30, oldKey],
|
|
2112
2111
|
message: renamedFieldMessage(oldKey, newKey)
|
|
2113
2112
|
});
|
|
2114
2113
|
}
|
|
@@ -2426,9 +2425,16 @@ var stackConfigValidationSchema = z2.object({
|
|
|
2426
2425
|
function formatZodIssues(issues) {
|
|
2427
2426
|
return issues.map(formatZodIssue).join("; ");
|
|
2428
2427
|
}
|
|
2428
|
+
var carriedMetadataValidationSchema = customMetadataValidationSchema.extend({
|
|
2429
|
+
displayName: z2.string().min(1),
|
|
2430
|
+
slug: z2.string().regex(KEBAB_CASE_PATTERN).min(1)
|
|
2431
|
+
});
|
|
2429
2432
|
function validateSkillMetadata(rawMetadata) {
|
|
2430
|
-
|
|
2431
|
-
|
|
2433
|
+
return metadataSchemaFor(rawMetadata).safeParse(rawMetadata);
|
|
2434
|
+
}
|
|
2435
|
+
function metadataSchemaFor(raw) {
|
|
2436
|
+
if (!isCustomMetadata(raw)) return metadataValidationSchema;
|
|
2437
|
+
return isCarriedMetadata(raw) ? carriedMetadataValidationSchema : customMetadataValidationSchema;
|
|
2432
2438
|
}
|
|
2433
2439
|
function describeMetadataSchemaFailure(issues, rawMetadata) {
|
|
2434
2440
|
const [absent, malformed] = partition(issues, (issue) => isAbsentField(issue, rawMetadata));
|
|
@@ -2479,6 +2485,9 @@ function validateNestingDepth(value, maxDepth) {
|
|
|
2479
2485
|
function isCustomMetadata(raw) {
|
|
2480
2486
|
return isRecord(raw) && raw.custom === true;
|
|
2481
2487
|
}
|
|
2488
|
+
function isCarriedMetadata(raw) {
|
|
2489
|
+
return isRecord(raw) && isRecord(raw.forkedFrom) && typeof raw.forkedFrom.path === "string";
|
|
2490
|
+
}
|
|
2482
2491
|
function warnUnknownFields(parsed, expectedKeys, context) {
|
|
2483
2492
|
const expectedSet = new Set(expectedKeys);
|
|
2484
2493
|
const unknownKeys2 = Object.keys(parsed).filter((k) => !expectedSet.has(k));
|
|
@@ -2488,11 +2497,11 @@ function warnUnknownFields(parsed, expectedKeys, context) {
|
|
|
2488
2497
|
}
|
|
2489
2498
|
|
|
2490
2499
|
// src/cli/lib/configuration/config-loader.ts
|
|
2491
|
-
import
|
|
2500
|
+
import path4 from "path";
|
|
2492
2501
|
import { fileURLToPath } from "url";
|
|
2493
2502
|
import { createJiti } from "jiti";
|
|
2494
|
-
var CONFIG_EXPORTS_PATH =
|
|
2495
|
-
|
|
2503
|
+
var CONFIG_EXPORTS_PATH = path4.resolve(
|
|
2504
|
+
path4.dirname(fileURLToPath(import.meta.url)),
|
|
2496
2505
|
"../../config-exports.ts"
|
|
2497
2506
|
);
|
|
2498
2507
|
var ConfigSchemaError = class extends Error {
|
|
@@ -2515,13 +2524,16 @@ var ConfigDefaultExportError = class extends Error {
|
|
|
2515
2524
|
};
|
|
2516
2525
|
var ES_MODULE_MARKER = "__esModule";
|
|
2517
2526
|
function unwrapDefaultExport(namespace) {
|
|
2518
|
-
if (isRecord(namespace) &&
|
|
2527
|
+
if (isRecord(namespace) && "default" in namespace) return namespace.default;
|
|
2519
2528
|
return namespace;
|
|
2520
2529
|
}
|
|
2521
2530
|
function declaresNoDefaultExport(namespace) {
|
|
2522
2531
|
return isRecord(namespace) && ES_MODULE_MARKER in namespace && !("default" in namespace);
|
|
2523
2532
|
}
|
|
2524
2533
|
var NO_VALID_DEFAULT_EXPORT = "the file has no valid default export";
|
|
2534
|
+
function configLoadFailure(configPath, reason) {
|
|
2535
|
+
return `Failed to load config from '${configPath}': ${reason}`;
|
|
2536
|
+
}
|
|
2525
2537
|
function exportsNothing(value) {
|
|
2526
2538
|
return value == null || typeof value === "object" && Object.keys(value).length === 0;
|
|
2527
2539
|
}
|
|
@@ -2539,14 +2551,12 @@ async function loadConfig(configPath, schema) {
|
|
|
2539
2551
|
if (declaresNoDefaultExport(namespace)) {
|
|
2540
2552
|
throw new ConfigDefaultExportError(configPath);
|
|
2541
2553
|
}
|
|
2542
|
-
if (schema)
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
}
|
|
2547
|
-
return result.data;
|
|
2554
|
+
if (!schema) return raw;
|
|
2555
|
+
const result = schema.safeParse(raw);
|
|
2556
|
+
if (!result.success) {
|
|
2557
|
+
throw new ConfigSchemaError(configPath, formatZodIssues(result.error.issues));
|
|
2548
2558
|
}
|
|
2549
|
-
return
|
|
2559
|
+
return result.data;
|
|
2550
2560
|
}
|
|
2551
2561
|
async function importConfigModule(configPath) {
|
|
2552
2562
|
try {
|
|
@@ -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(
|
|
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
|
|
2575
|
+
import path5 from "path";
|
|
2568
2576
|
function getProjectConfigPath(dir, provider) {
|
|
2569
|
-
return
|
|
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([
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
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({
|
|
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";
|
|
@@ -3157,7 +3177,16 @@ function brokenUnionLayout(members, bodyIndent) {
|
|
|
3157
3177
|
}
|
|
3158
3178
|
function renderUnionBody(members, layout, bodyIndent) {
|
|
3159
3179
|
const padding = spaces(bodyIndent);
|
|
3160
|
-
|
|
3180
|
+
switch (layout) {
|
|
3181
|
+
case "indented":
|
|
3182
|
+
return `${padding}${flatUnion(members)}`;
|
|
3183
|
+
case "stacked":
|
|
3184
|
+
return members.map((member) => `${padding}| ${member}`).join("\n");
|
|
3185
|
+
default: {
|
|
3186
|
+
const _exhaustive = layout;
|
|
3187
|
+
return _exhaustive;
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3161
3190
|
}
|
|
3162
3191
|
function flatUnion(members) {
|
|
3163
3192
|
return members.join(" | ");
|
|
@@ -3205,9 +3234,18 @@ function renderArray(entries, position) {
|
|
|
3205
3234
|
return ["[", ...lines, `${spaces(position.indent)}]`].join("\n");
|
|
3206
3235
|
}
|
|
3207
3236
|
function renderEntryLine(entry, indent) {
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3237
|
+
switch (entry.kind) {
|
|
3238
|
+
case "comment":
|
|
3239
|
+
return `${spaces(indent)}// ${entry.text}`;
|
|
3240
|
+
case "source":
|
|
3241
|
+
return `${spaces(indent)}${entry.source},`;
|
|
3242
|
+
case "value":
|
|
3243
|
+
return renderValueLine(entry.value, { indent, prefix: "", suffix: "," });
|
|
3244
|
+
default: {
|
|
3245
|
+
const _exhaustive = entry;
|
|
3246
|
+
return _exhaustive;
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3211
3249
|
}
|
|
3212
3250
|
function renderRecord(record, position) {
|
|
3213
3251
|
const properties = Object.entries(record);
|
|
@@ -3227,11 +3265,27 @@ function propertyPosition(key, indent) {
|
|
|
3227
3265
|
function flatArray(entries) {
|
|
3228
3266
|
const items = [];
|
|
3229
3267
|
for (const entry of entries) {
|
|
3230
|
-
|
|
3231
|
-
|
|
3268
|
+
const item = flatEntry(entry);
|
|
3269
|
+
if (item === void 0) return void 0;
|
|
3270
|
+
items.push(item);
|
|
3232
3271
|
}
|
|
3233
3272
|
return flatList(items);
|
|
3234
3273
|
}
|
|
3274
|
+
function flatEntry(entry) {
|
|
3275
|
+
switch (entry.kind) {
|
|
3276
|
+
// A `//` line cannot fold onto one line.
|
|
3277
|
+
case "comment":
|
|
3278
|
+
return void 0;
|
|
3279
|
+
case "source":
|
|
3280
|
+
return entry.source;
|
|
3281
|
+
case "value":
|
|
3282
|
+
return flatValue(entry.value);
|
|
3283
|
+
default: {
|
|
3284
|
+
const _exhaustive = entry;
|
|
3285
|
+
return _exhaustive;
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3235
3289
|
function flatValue(value) {
|
|
3236
3290
|
if (Array.isArray(value)) return flatList(value.map(flatValue));
|
|
3237
3291
|
if (isKeyedRecord(value)) return flatRecord(value);
|
|
@@ -3273,12 +3327,20 @@ var HAND_WRITTEN_FIELDS = [
|
|
|
3273
3327
|
"categoriesFile",
|
|
3274
3328
|
"rulesFile"
|
|
3275
3329
|
];
|
|
3276
|
-
var
|
|
3330
|
+
var GLOBAL_IDENTITY_FIELDS = [
|
|
3331
|
+
...HAND_WRITTEN_FIELDS,
|
|
3332
|
+
"description"
|
|
3333
|
+
];
|
|
3334
|
+
var EXTRACTED_FIELDS = [
|
|
3277
3335
|
"skills",
|
|
3278
3336
|
"agents",
|
|
3279
3337
|
"stack",
|
|
3280
3338
|
"selectedDomains"
|
|
3281
|
-
]
|
|
3339
|
+
];
|
|
3340
|
+
var extractedFields = new Set(EXTRACTED_FIELDS);
|
|
3341
|
+
function isExtractedField(key) {
|
|
3342
|
+
return extractedFields.has(key);
|
|
3343
|
+
}
|
|
3282
3344
|
var CANONICAL_FIELD_ORDER = [
|
|
3283
3345
|
"name",
|
|
3284
3346
|
"description",
|
|
@@ -3479,11 +3541,21 @@ function generateStandaloneConfig(cleaned) {
|
|
|
3479
3541
|
});
|
|
3480
3542
|
}
|
|
3481
3543
|
function standaloneExportField(key, value, emitted) {
|
|
3482
|
-
if (!
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3544
|
+
if (!isExtractedField(key)) return [renderScalarField(key, value)];
|
|
3545
|
+
switch (key) {
|
|
3546
|
+
case "skills":
|
|
3547
|
+
return [` skills${emitted.hasSkills ? "" : ": []"},`];
|
|
3548
|
+
case "agents":
|
|
3549
|
+
return [` agents${emitted.hasAgents ? "" : ": []"},`];
|
|
3550
|
+
case "stack":
|
|
3551
|
+
return emitted.hasStack ? [` stack,`] : [];
|
|
3552
|
+
case "selectedDomains":
|
|
3553
|
+
return [` selectedDomains${emitted.hasSelectedDomains ? "" : ": []"},`];
|
|
3554
|
+
default: {
|
|
3555
|
+
const _exhaustive = key;
|
|
3556
|
+
return _exhaustive;
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3487
3559
|
}
|
|
3488
3560
|
function assembleConfigSource({
|
|
3489
3561
|
imports,
|
|
@@ -3514,7 +3586,7 @@ function buildTypeImports(flags) {
|
|
|
3514
3586
|
].filter((t) => Boolean(t));
|
|
3515
3587
|
}
|
|
3516
3588
|
function isCopiedScalarField(key) {
|
|
3517
|
-
return !
|
|
3589
|
+
return !isExtractedField(key) && key !== "name";
|
|
3518
3590
|
}
|
|
3519
3591
|
function resolveProjectName(cleaned) {
|
|
3520
3592
|
return cleaned.name && cleaned.name !== GLOBAL_CONFIG_NAME ? cleaned.name : DEFAULT_PLUGIN_NAME;
|
|
@@ -3560,7 +3632,7 @@ function stackOfAgents(stack, agents) {
|
|
|
3560
3632
|
function mergeInlinedScalarFields(cleaned, cleanedGlobal) {
|
|
3561
3633
|
return Object.entries(
|
|
3562
3634
|
canonicalizeFieldOrder({
|
|
3563
|
-
...omit(cleanedGlobal,
|
|
3635
|
+
...omit(cleanedGlobal, GLOBAL_IDENTITY_FIELDS),
|
|
3564
3636
|
...cleaned
|
|
3565
3637
|
})
|
|
3566
3638
|
).filter(([key]) => isCopiedScalarField(key));
|
|
@@ -4008,9 +4080,9 @@ function seedToInstallSelection(payload, catalog) {
|
|
|
4008
4080
|
const rows = installable.flatMap((skill) => assignmentRows(skill, agentMap));
|
|
4009
4081
|
refuseUnwritableAssignments(rows);
|
|
4010
4082
|
const placed = rows.filter((row) => row.kind === "placed");
|
|
4011
|
-
const
|
|
4083
|
+
const carried = new Set(agentsCarriedIn(placed, agentMap));
|
|
4084
|
+
const selectedAgents = withAgentsSkippedSkillsName(carried, skipped, agentMap);
|
|
4012
4085
|
const skills = installable.map(toSeedSkillConfig);
|
|
4013
|
-
const selected = new Set(selectedAgents);
|
|
4014
4086
|
return {
|
|
4015
4087
|
result: {
|
|
4016
4088
|
skills,
|
|
@@ -4036,7 +4108,7 @@ function seedToInstallSelection(payload, catalog) {
|
|
|
4036
4108
|
skippedSkillIds: skipped.map(({ skillId }) => skillId),
|
|
4037
4109
|
skippedAgentNames: skippedAgentNamesOf(agentMap, rows),
|
|
4038
4110
|
agentsOnlySkippedSkillsCarried: skipped.flatMap(
|
|
4039
|
-
(skill) => agentsOnlyItCarried(skill, agentMap,
|
|
4111
|
+
(skill) => agentsOnlyItCarried(skill, agentMap, carried)
|
|
4040
4112
|
)
|
|
4041
4113
|
};
|
|
4042
4114
|
}
|
|
@@ -4046,11 +4118,14 @@ function refuseUnwritableAssignments(rows) {
|
|
|
4046
4118
|
throw new Error(unwritableAssignmentsError(unwritable));
|
|
4047
4119
|
}
|
|
4048
4120
|
}
|
|
4049
|
-
function
|
|
4121
|
+
function agentsCarriedIn(placed, agentMap) {
|
|
4122
|
+
return [...placed.map((row) => row.agent), ...agentsSwitchedOn(agentMap)];
|
|
4123
|
+
}
|
|
4124
|
+
function withAgentsSkippedSkillsName(carried, skipped, agentMap) {
|
|
4050
4125
|
return [
|
|
4051
4126
|
.../* @__PURE__ */ new Set([
|
|
4052
|
-
...
|
|
4053
|
-
...
|
|
4127
|
+
...carried,
|
|
4128
|
+
...skipped.flatMap((skill) => agentsNamedBy(skill, agentMap))
|
|
4054
4129
|
])
|
|
4055
4130
|
];
|
|
4056
4131
|
}
|
|
@@ -4058,9 +4133,14 @@ function skippedAgentNamesOf(agentMap, rows) {
|
|
|
4058
4133
|
const unknownAssignees = rows.filter((row) => row.kind === "unknownAgent").map((row) => row.agentName);
|
|
4059
4134
|
return [.../* @__PURE__ */ new Set([...agentMap.unknown, ...unknownAssignees])];
|
|
4060
4135
|
}
|
|
4061
|
-
function
|
|
4062
|
-
|
|
4063
|
-
|
|
4136
|
+
function agentsNamedBy({ seedSkill }, agentMap) {
|
|
4137
|
+
return Object.keys(seedSkill.assignments).filter(isAgentName2).filter((name) => !agentMap.switchedOff.has(name));
|
|
4138
|
+
}
|
|
4139
|
+
function agentsOnlyItCarried(skill, agentMap, carried) {
|
|
4140
|
+
const agentNames = agentsNamedBy(skill, agentMap).filter(
|
|
4141
|
+
(name) => !carried.has(name)
|
|
4142
|
+
);
|
|
4143
|
+
return agentNames.length === 0 ? [] : [{ skillId: skill.skillId, agentNames }];
|
|
4064
4144
|
}
|
|
4065
4145
|
function readAgentMap(agents) {
|
|
4066
4146
|
const known = /* @__PURE__ */ new Map();
|
|
@@ -4178,10 +4258,10 @@ import { indexBy as indexBy2, omit as omit3, pick, uniqueBy as uniqueBy2 } from
|
|
|
4178
4258
|
|
|
4179
4259
|
// src/cli/lib/configuration/project-config.ts
|
|
4180
4260
|
import os5 from "os";
|
|
4181
|
-
import
|
|
4261
|
+
import path8 from "path";
|
|
4182
4262
|
|
|
4183
4263
|
// src/cli/lib/stacks/stacks-loader.ts
|
|
4184
|
-
import
|
|
4264
|
+
import path7 from "path";
|
|
4185
4265
|
import { flatMap, groupBy as groupBy2, mapValues as mapValues2, partition as partition3, pipe, unique as unique2, uniqueBy } from "remeda";
|
|
4186
4266
|
var stacksCache = /* @__PURE__ */ new Map();
|
|
4187
4267
|
function normalizeAgentConfig(agentConfig) {
|
|
@@ -4241,7 +4321,7 @@ async function loadStacks(configDir, stacksFile) {
|
|
|
4241
4321
|
const cacheKey = `${configDir}:${resolvedStacksFile}`;
|
|
4242
4322
|
const cached = stacksCache.get(cacheKey);
|
|
4243
4323
|
if (cached) return cached;
|
|
4244
|
-
const stacksPath =
|
|
4324
|
+
const stacksPath = path7.join(configDir, resolvedStacksFile);
|
|
4245
4325
|
try {
|
|
4246
4326
|
const raw = await loadConfig(stacksPath, stacksConfigSchema);
|
|
4247
4327
|
if (raw == null) {
|
|
@@ -4278,16 +4358,16 @@ async function loadStackById(stackId, configDir, source) {
|
|
|
4278
4358
|
);
|
|
4279
4359
|
return null;
|
|
4280
4360
|
}
|
|
4281
|
-
const defaultStack = defaultStacks.find((s) => s.id === stackId)
|
|
4282
|
-
if (defaultStack) {
|
|
4283
|
-
verbose(`Found default stack: ${defaultStack.name} (${stackId})`);
|
|
4284
|
-
} else {
|
|
4361
|
+
const defaultStack = defaultStacks.find((s) => s.id === stackId);
|
|
4362
|
+
if (!defaultStack) {
|
|
4285
4363
|
verbose(`Stack '${stackId}' not found in the default marketplace or the built-ins`);
|
|
4364
|
+
return null;
|
|
4286
4365
|
}
|
|
4366
|
+
verbose(`Found default stack: ${defaultStack.name} (${stackId})`);
|
|
4287
4367
|
return defaultStack;
|
|
4288
4368
|
}
|
|
4289
4369
|
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.`;
|
|
4370
|
+
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
4371
|
}
|
|
4292
4372
|
function defaultUsageGuidance(category) {
|
|
4293
4373
|
return `Use when working with ${category}.`;
|
|
@@ -4327,6 +4407,7 @@ var ConfigLoadError = class extends Error {
|
|
|
4327
4407
|
this.name = "ConfigLoadError";
|
|
4328
4408
|
this.scope = isHomeDirectory(scopeRoot) ? "global" : "project";
|
|
4329
4409
|
this.providerToName = providerToNameAt(scopeRoot, provider);
|
|
4410
|
+
this.initProviderToName = initProviderToNameAt(scopeRoot, provider);
|
|
4330
4411
|
}
|
|
4331
4412
|
configPath;
|
|
4332
4413
|
reason;
|
|
@@ -4334,6 +4415,8 @@ var ConfigLoadError = class extends Error {
|
|
|
4334
4415
|
scope;
|
|
4335
4416
|
/** Named with `--provider` in a remedy handed out for this installation; beside another only. */
|
|
4336
4417
|
providerToName;
|
|
4418
|
+
/** Named in the remedy's `init --from` half, which a bare run would answer with another one. */
|
|
4419
|
+
initProviderToName;
|
|
4337
4420
|
};
|
|
4338
4421
|
async function loadProjectConfigFromDir(projectDir, provider) {
|
|
4339
4422
|
return loadProjectConfigAt(getProjectConfigPath(projectDir, provider), projectDir, provider);
|
|
@@ -4372,7 +4455,7 @@ function asProjectConfig(parsed, projectDir) {
|
|
|
4372
4455
|
const normalizedStack = parsed.stack ? normalizeStackRecord(parsed.stack) : void 0;
|
|
4373
4456
|
return {
|
|
4374
4457
|
...parsed,
|
|
4375
|
-
name: parsed.name ??
|
|
4458
|
+
name: parsed.name ?? path8.basename(projectDir),
|
|
4376
4459
|
skills: parsed.skills ?? [],
|
|
4377
4460
|
agents: parsed.agents ?? [],
|
|
4378
4461
|
...normalizedStack && { stack: normalizedStack }
|
|
@@ -4395,10 +4478,10 @@ function configDirsInPlay(projectDir) {
|
|
|
4395
4478
|
return isHomeDirectory(projectDir) ? [projectDir] : [projectDir, os5.homedir()];
|
|
4396
4479
|
}
|
|
4397
4480
|
async function findConfigLoadFailures(projectDir) {
|
|
4398
|
-
const outcomes = await Promise.all(configDirsInPlay(projectDir).map(
|
|
4481
|
+
const outcomes = await Promise.all(configDirsInPlay(projectDir).map(configLoadFailure2));
|
|
4399
4482
|
return outcomes.filter((failure) => failure !== void 0);
|
|
4400
4483
|
}
|
|
4401
|
-
async function
|
|
4484
|
+
async function configLoadFailure2(dir) {
|
|
4402
4485
|
try {
|
|
4403
4486
|
await loadInstalledConfig(dir);
|
|
4404
4487
|
return void 0;
|
|
@@ -4411,10 +4494,8 @@ async function loadProjectConfig(projectDir) {
|
|
|
4411
4494
|
const provider = providerInUse(projectDir);
|
|
4412
4495
|
const projectResult = await loadProjectConfigFromDir(projectDir, provider);
|
|
4413
4496
|
if (projectResult) return projectResult;
|
|
4414
|
-
if (
|
|
4415
|
-
|
|
4416
|
-
}
|
|
4417
|
-
return null;
|
|
4497
|
+
if (isHomeDirectory(projectDir)) return null;
|
|
4498
|
+
return loadProjectConfigFromDir(os5.homedir(), provider);
|
|
4418
4499
|
}
|
|
4419
4500
|
function validateProjectConfig(config) {
|
|
4420
4501
|
const result = projectConfigLoaderSchema.safeParse(config);
|
|
@@ -4499,7 +4580,6 @@ function mergeConfigs(newConfig, existingConfig, options) {
|
|
|
4499
4580
|
}
|
|
4500
4581
|
return merged;
|
|
4501
4582
|
}
|
|
4502
|
-
var GLOBAL_IDENTITY_FIELDS = [...HAND_WRITTEN_FIELDS, "description"];
|
|
4503
4583
|
function existingConfigForMerge(loaded, projectDir, ownName) {
|
|
4504
4584
|
const isProjectsOwnFile = loaded.configPath === getProjectConfigPath(projectDir, loaded.provider);
|
|
4505
4585
|
if (isProjectsOwnFile) return loaded.config;
|
|
@@ -4521,13 +4601,13 @@ async function mergeWithExistingConfig(newConfig, context) {
|
|
|
4521
4601
|
}
|
|
4522
4602
|
|
|
4523
4603
|
// src/cli/lib/skills/skill-metadata.ts
|
|
4524
|
-
import
|
|
4604
|
+
import path10 from "path";
|
|
4525
4605
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
4526
4606
|
|
|
4527
4607
|
// src/cli/lib/versioning.ts
|
|
4528
4608
|
import { createHash } from "crypto";
|
|
4529
4609
|
import { readFile as readFileBytes } from "fs/promises";
|
|
4530
|
-
import
|
|
4610
|
+
import path9 from "path";
|
|
4531
4611
|
var ISO_DATE_LENGTH = 10;
|
|
4532
4612
|
function getCurrentDate() {
|
|
4533
4613
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, ISO_DATE_LENGTH);
|
|
@@ -4544,18 +4624,18 @@ async function computeFileHash(filePath) {
|
|
|
4544
4624
|
async function computeSkillFolderHash(skillPath) {
|
|
4545
4625
|
const contents = [];
|
|
4546
4626
|
for (const fileName of SKILL_CONTENT_FILES) {
|
|
4547
|
-
const filePath =
|
|
4627
|
+
const filePath = path9.join(skillPath, fileName);
|
|
4548
4628
|
if (await fileExists(filePath)) {
|
|
4549
4629
|
const content = await readFile(filePath);
|
|
4550
4630
|
contents.push(`${fileName}:${content}`);
|
|
4551
4631
|
}
|
|
4552
4632
|
}
|
|
4553
4633
|
for (const dirName of SKILL_CONTENT_DIRS) {
|
|
4554
|
-
const dirPath =
|
|
4634
|
+
const dirPath = path9.join(skillPath, dirName);
|
|
4555
4635
|
if (await fileExists(dirPath)) {
|
|
4556
4636
|
const files = await glob("**/*", dirPath);
|
|
4557
4637
|
for (const file of files.sort()) {
|
|
4558
|
-
const filePath =
|
|
4638
|
+
const filePath = path9.join(dirPath, file);
|
|
4559
4639
|
const content = await readFile(filePath);
|
|
4560
4640
|
contents.push(`${dirName}/${file}:${content}`);
|
|
4561
4641
|
}
|
|
@@ -4576,21 +4656,6 @@ function bumpMajorVersion(version) {
|
|
|
4576
4656
|
const major = parseMajorVersion(version);
|
|
4577
4657
|
return `${major + 1}.0.0`;
|
|
4578
4658
|
}
|
|
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
4659
|
async function readManifestVersion(manifestPath) {
|
|
4595
4660
|
const content = await readFileSafe(manifestPath, MAX_PLUGIN_FILE_SIZE);
|
|
4596
4661
|
return pluginManifestSchema.parse(JSON.parse(content)).version ?? DEFAULT_VERSION;
|
|
@@ -4599,7 +4664,7 @@ async function computeBytesHash(filePath) {
|
|
|
4599
4664
|
return createHash("sha256").update(await readFileBytes(filePath)).digest("hex");
|
|
4600
4665
|
}
|
|
4601
4666
|
async function describeShippedFile(pluginDir, file) {
|
|
4602
|
-
return `${file}\0${await computeBytesHash(
|
|
4667
|
+
return `${file}\0${await computeBytesHash(path9.join(pluginDir, file))}\0`;
|
|
4603
4668
|
}
|
|
4604
4669
|
async function computeShippedHash(pluginDir) {
|
|
4605
4670
|
const files = await glob("**/*", pluginDir, { dot: true });
|
|
@@ -4620,25 +4685,6 @@ function versionAfterRebuild(earlier, shippedHash) {
|
|
|
4620
4685
|
if (!earlier) return DEFAULT_VERSION;
|
|
4621
4686
|
return earlier.shippedHash === shippedHash ? earlier.version : bumpMajorVersion(earlier.version);
|
|
4622
4687
|
}
|
|
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
4688
|
async function writeContentHash(pluginDir, contentHash, getManifestPath) {
|
|
4643
4689
|
await writeFile(contentHashFileBeside(getManifestPath(pluginDir)), contentHash);
|
|
4644
4690
|
}
|
|
@@ -4662,7 +4708,7 @@ async function readForkedFromMetadata(skillDir) {
|
|
|
4662
4708
|
return metadata?.forkedFrom ?? null;
|
|
4663
4709
|
}
|
|
4664
4710
|
async function readLocalSkillMetadata(skillDir) {
|
|
4665
|
-
const metadataPath =
|
|
4711
|
+
const metadataPath = path10.join(skillDir, STANDARD_FILES.METADATA_YAML);
|
|
4666
4712
|
if (!await fileExists(metadataPath)) {
|
|
4667
4713
|
return null;
|
|
4668
4714
|
}
|
|
@@ -4680,7 +4726,7 @@ async function readLocalSkillMetadata(skillDir) {
|
|
|
4680
4726
|
return result.data;
|
|
4681
4727
|
}
|
|
4682
4728
|
async function injectForkedFromMetadata(destPath, skillId, contentHash, origin = {}) {
|
|
4683
|
-
const metadataPath =
|
|
4729
|
+
const metadataPath = path10.join(destPath, STANDARD_FILES.METADATA_YAML);
|
|
4684
4730
|
const rawContent = await readFile(metadataPath);
|
|
4685
4731
|
const { yamlContent } = stripYamlSchemaComment(rawContent);
|
|
4686
4732
|
const metadata = {
|
|
@@ -4718,7 +4764,7 @@ function existingMetadataFields(yamlContent, metadataPath) {
|
|
|
4718
4764
|
}
|
|
4719
4765
|
|
|
4720
4766
|
// src/cli/lib/skills/skill-copier.ts
|
|
4721
|
-
import
|
|
4767
|
+
import path11 from "path";
|
|
4722
4768
|
var NULL_BYTE_PATTERN2 = /\0/;
|
|
4723
4769
|
function validateSkillPath(resolvedPath, expectedParent, skillPath) {
|
|
4724
4770
|
if (NULL_BYTE_PATTERN2.test(skillPath)) {
|
|
@@ -4726,26 +4772,26 @@ function validateSkillPath(resolvedPath, expectedParent, skillPath) {
|
|
|
4726
4772
|
}
|
|
4727
4773
|
if (!isPathWithin(resolvedPath, expectedParent)) {
|
|
4728
4774
|
throw new Error(
|
|
4729
|
-
`Invalid skill path: '${skillPath}' escapes expected directory '${
|
|
4775
|
+
`Invalid skill path: '${skillPath}' escapes expected directory '${path11.resolve(expectedParent)}'`
|
|
4730
4776
|
);
|
|
4731
4777
|
}
|
|
4732
4778
|
}
|
|
4733
4779
|
function resolveSkillPath(basePath, skillPath) {
|
|
4734
|
-
const resolved =
|
|
4780
|
+
const resolved = path11.join(basePath, skillPath);
|
|
4735
4781
|
validateSkillPath(resolved, basePath, skillPath);
|
|
4736
4782
|
return resolved;
|
|
4737
4783
|
}
|
|
4738
4784
|
function getSkillSourcePath(skill, rootDir) {
|
|
4739
|
-
const srcDir =
|
|
4785
|
+
const srcDir = path11.join(rootDir, SOURCE_SRC_DIR);
|
|
4740
4786
|
return resolveSkillPath(srcDir, skill.path);
|
|
4741
4787
|
}
|
|
4742
4788
|
async function generateSkillHash(skillSourcePath) {
|
|
4743
|
-
const skillMdPath =
|
|
4789
|
+
const skillMdPath = path11.join(skillSourcePath, STANDARD_FILES.SKILL_MD);
|
|
4744
4790
|
return computeFileHash(skillMdPath);
|
|
4745
4791
|
}
|
|
4746
4792
|
async function copySkillTo(skill, sourcePath, destPath, source) {
|
|
4747
4793
|
const contentHash = await generateSkillHash(sourcePath);
|
|
4748
|
-
await ensureDir(
|
|
4794
|
+
await ensureDir(path11.dirname(destPath));
|
|
4749
4795
|
await copy(sourcePath, destPath);
|
|
4750
4796
|
await injectForkedFromMetadata(destPath, skill.id, contentHash, {
|
|
4751
4797
|
...source !== void 0 && { source }
|
|
@@ -4812,7 +4858,7 @@ async function copySelectedSkill(skillId, localSkillsDir, sourceResult, selected
|
|
|
4812
4858
|
return copySkillToLocalFlattened(skill, localSkillsDir, sourceResult);
|
|
4813
4859
|
}
|
|
4814
4860
|
const destPath = getFlattenedSkillDestPath(skill, localSkillsDir);
|
|
4815
|
-
if (
|
|
4861
|
+
if (path11.resolve(localPath) === path11.resolve(destPath)) {
|
|
4816
4862
|
return resolveLocalCopiedSkill(skill, localPath);
|
|
4817
4863
|
}
|
|
4818
4864
|
return copyLocalSkillTo(skill, localPath, destPath);
|
|
@@ -4825,33 +4871,33 @@ function localPathOf(skill) {
|
|
|
4825
4871
|
}
|
|
4826
4872
|
async function copyLocalSkillTo(skill, localPath, destPath) {
|
|
4827
4873
|
const contentHash = await generateSkillHash(localPath);
|
|
4828
|
-
await ensureDir(
|
|
4874
|
+
await ensureDir(path11.dirname(destPath));
|
|
4829
4875
|
await copy(localPath, destPath);
|
|
4830
4876
|
return { skillId: skill.id, sourcePath: localPath, destPath, contentHash };
|
|
4831
4877
|
}
|
|
4832
4878
|
|
|
4833
4879
|
// src/cli/lib/skills/skill-plugin-compiler.ts
|
|
4834
|
-
import
|
|
4880
|
+
import path24 from "path";
|
|
4835
4881
|
import { parse as parseYaml4 } from "yaml";
|
|
4836
4882
|
|
|
4837
4883
|
// src/cli/lib/plugins/plugin-manifest.ts
|
|
4838
|
-
import
|
|
4884
|
+
import path13 from "path";
|
|
4839
4885
|
|
|
4840
4886
|
// src/cli/lib/plugins/plugin-finder.ts
|
|
4841
4887
|
import os6 from "os";
|
|
4842
|
-
import
|
|
4888
|
+
import path12 from "path";
|
|
4843
4889
|
function getUserPluginsDir() {
|
|
4844
|
-
return
|
|
4890
|
+
return path12.join(os6.homedir(), CLAUDE_DIR, PLUGINS_SUBDIR);
|
|
4845
4891
|
}
|
|
4846
4892
|
function getProjectPluginsDir(projectDir) {
|
|
4847
4893
|
const dir = projectDir ?? process.cwd();
|
|
4848
|
-
return
|
|
4894
|
+
return path12.join(dir, CLAUDE_DIR, PLUGINS_SUBDIR);
|
|
4849
4895
|
}
|
|
4850
4896
|
function getPluginAgentsDir(pluginDir) {
|
|
4851
|
-
return
|
|
4897
|
+
return path12.join(pluginDir, STANDARD_DIRS.AGENTS);
|
|
4852
4898
|
}
|
|
4853
4899
|
function getPluginManifestPath(pluginDir) {
|
|
4854
|
-
return
|
|
4900
|
+
return path12.join(pluginDir, PLUGIN_MANIFEST_DIR, PLUGIN_MANIFEST_FILE);
|
|
4855
4901
|
}
|
|
4856
4902
|
async function readPluginManifest(pluginDir) {
|
|
4857
4903
|
const manifestPath = getPluginManifestPath(pluginDir);
|
|
@@ -4906,7 +4952,7 @@ function generateAgentPluginManifest(options) {
|
|
|
4906
4952
|
}
|
|
4907
4953
|
async function writePluginManifest(outputDir, manifest) {
|
|
4908
4954
|
const manifestPath = getPluginManifestPath(outputDir);
|
|
4909
|
-
await ensureDir(
|
|
4955
|
+
await ensureDir(path13.dirname(manifestPath));
|
|
4910
4956
|
const content = JSON.stringify(manifest, null, 2);
|
|
4911
4957
|
await writeFile(manifestPath, content);
|
|
4912
4958
|
return manifestPath;
|
|
@@ -4921,12 +4967,24 @@ function parseMarketplacePluginRef(ref) {
|
|
|
4921
4967
|
return separator === -1 ? ref : ref.slice(0, separator);
|
|
4922
4968
|
}
|
|
4923
4969
|
|
|
4970
|
+
// src/cli/lib/plugins/earlier-build.ts
|
|
4971
|
+
async function isEarlierBuild(pluginDir) {
|
|
4972
|
+
return fileExists(getPluginManifestPath(pluginDir));
|
|
4973
|
+
}
|
|
4974
|
+
async function holdsContentNoBuildMade(pluginDir) {
|
|
4975
|
+
if (await isEarlierBuild(pluginDir)) return false;
|
|
4976
|
+
return !await isDirectoryEmpty(pluginDir);
|
|
4977
|
+
}
|
|
4978
|
+
async function clearEarlierBuild(pluginDir) {
|
|
4979
|
+
if (await isEarlierBuild(pluginDir)) await remove(pluginDir);
|
|
4980
|
+
}
|
|
4981
|
+
|
|
4924
4982
|
// src/cli/lib/plugins/plugin-info.ts
|
|
4925
4983
|
import { readdir } from "fs/promises";
|
|
4926
4984
|
|
|
4927
4985
|
// src/cli/lib/installation/installation.ts
|
|
4928
4986
|
import os7 from "os";
|
|
4929
|
-
import
|
|
4987
|
+
import path14 from "path";
|
|
4930
4988
|
var INSTALL_MODE_LABELS = {
|
|
4931
4989
|
plugin: "Plugin",
|
|
4932
4990
|
mixed: "Mixed",
|
|
@@ -4965,7 +5023,7 @@ async function detectInstallationInDir(dir, scope) {
|
|
|
4965
5023
|
configPath,
|
|
4966
5024
|
agentsDir: agentsDir(provider, scope, dir),
|
|
4967
5025
|
// Mixed mode uses the local skills dir, as eject does.
|
|
4968
|
-
skillsDir:
|
|
5026
|
+
skillsDir: path14.join(
|
|
4969
5027
|
dir,
|
|
4970
5028
|
CLAUDE_DIR,
|
|
4971
5029
|
mode === "plugin" ? PLUGINS_SUBDIR : STANDARD_DIRS.SKILLS
|
|
@@ -4995,12 +5053,12 @@ async function detectInstallation(projectDir = process.cwd()) {
|
|
|
4995
5053
|
|
|
4996
5054
|
// src/cli/lib/installation/local-installer.ts
|
|
4997
5055
|
import os10 from "os";
|
|
4998
|
-
import
|
|
5056
|
+
import path18 from "path";
|
|
4999
5057
|
import { unique as unique4 } from "remeda";
|
|
5000
5058
|
|
|
5001
5059
|
// src/cli/lib/loading/loader.ts
|
|
5002
5060
|
import { parse as parseYaml3 } from "yaml";
|
|
5003
|
-
import
|
|
5061
|
+
import path15 from "path";
|
|
5004
5062
|
|
|
5005
5063
|
// src/cli/utils/frontmatter.ts
|
|
5006
5064
|
import { parse as parseYaml2 } from "yaml";
|
|
@@ -5073,7 +5131,7 @@ async function loadAgentsFromDir(agentsDir2, sourceRoot, options) {
|
|
|
5073
5131
|
return agents;
|
|
5074
5132
|
}
|
|
5075
5133
|
async function readAgentDefinition(agentsDir2, file, sourceRoot, options) {
|
|
5076
|
-
const fullPath =
|
|
5134
|
+
const fullPath = path15.join(agentsDir2, file);
|
|
5077
5135
|
try {
|
|
5078
5136
|
const content = await readFile(fullPath);
|
|
5079
5137
|
const parsed = agentYamlConfigSchema.safeParse(parseYaml3(content));
|
|
@@ -5106,7 +5164,7 @@ function toAgentDefinition(config, file, sourceRoot, options) {
|
|
|
5106
5164
|
...config.hooks !== void 0 && { hooks: config.hooks },
|
|
5107
5165
|
...config.experimental !== void 0 && { experimental: config.experimental },
|
|
5108
5166
|
...config.outputFormat !== void 0 && { outputFormat: config.outputFormat },
|
|
5109
|
-
path:
|
|
5167
|
+
path: path15.dirname(file),
|
|
5110
5168
|
sourceRoot,
|
|
5111
5169
|
...options.agentBaseDir ? { agentBaseDir: options.agentBaseDir } : {},
|
|
5112
5170
|
...config.domain ? { domain: config.domain } : {},
|
|
@@ -5114,7 +5172,7 @@ function toAgentDefinition(config, file, sourceRoot, options) {
|
|
|
5114
5172
|
};
|
|
5115
5173
|
}
|
|
5116
5174
|
async function loadAllAgents(projectRoot) {
|
|
5117
|
-
return loadAgentsFromDir(
|
|
5175
|
+
return loadAgentsFromDir(path15.join(projectRoot, DIRS.agents), projectRoot, {
|
|
5118
5176
|
includeCustomFlag: true,
|
|
5119
5177
|
verboseLabel: "agent"
|
|
5120
5178
|
});
|
|
@@ -5128,7 +5186,7 @@ async function loadMergedAgents(sourcePath) {
|
|
|
5128
5186
|
}
|
|
5129
5187
|
async function loadProjectAgents(projectRoot) {
|
|
5130
5188
|
const sourceFolder = sourceFolderInUse(projectRoot, providerInUse(projectRoot));
|
|
5131
|
-
const projectAgentsDir =
|
|
5189
|
+
const projectAgentsDir = path15.join(sourceFolder.dir, STANDARD_DIRS.AGENTS);
|
|
5132
5190
|
if (!await directoryExists2(projectAgentsDir)) {
|
|
5133
5191
|
verbose(`No project agents directory at ${projectAgentsDir}`);
|
|
5134
5192
|
return {};
|
|
@@ -5160,19 +5218,19 @@ async function loadSkillsFromDir(skillsDir2, options = {}) {
|
|
|
5160
5218
|
return { skills, unusableMetadata };
|
|
5161
5219
|
}
|
|
5162
5220
|
function locateSkill(skillsDir2, skillFile, pathPrefix) {
|
|
5163
|
-
const skillMdPath =
|
|
5164
|
-
const skillDir =
|
|
5165
|
-
const relativePath =
|
|
5221
|
+
const skillMdPath = path15.join(skillsDir2, skillFile);
|
|
5222
|
+
const skillDir = path15.dirname(skillMdPath);
|
|
5223
|
+
const relativePath = path15.relative(skillsDir2, skillDir);
|
|
5166
5224
|
return {
|
|
5167
5225
|
skillFile,
|
|
5168
5226
|
skillMdPath,
|
|
5169
5227
|
skillDir,
|
|
5170
|
-
skillDirName:
|
|
5228
|
+
skillDirName: path15.basename(skillDir),
|
|
5171
5229
|
displayPath: pathPrefix ? `${pathPrefix}/${relativePath}/` : `${relativePath}/`
|
|
5172
5230
|
};
|
|
5173
5231
|
}
|
|
5174
5232
|
async function judgeSkillMetadata(skill) {
|
|
5175
|
-
const metadataPath =
|
|
5233
|
+
const metadataPath = path15.join(skill.skillDir, STANDARD_FILES.METADATA_YAML);
|
|
5176
5234
|
if (!await fileExists(metadataPath)) {
|
|
5177
5235
|
warn(
|
|
5178
5236
|
`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 +5272,7 @@ async function readSkillDefinition(skill) {
|
|
|
5214
5272
|
}
|
|
5215
5273
|
}
|
|
5216
5274
|
async function loadPluginSkills(pluginDir) {
|
|
5217
|
-
const { skills } = await loadSkillsFromDir(
|
|
5275
|
+
const { skills } = await loadSkillsFromDir(path15.join(pluginDir, "skills"), {
|
|
5218
5276
|
pathPrefix: "skills",
|
|
5219
5277
|
requireMetadata: false
|
|
5220
5278
|
});
|
|
@@ -5222,13 +5280,18 @@ async function loadPluginSkills(pluginDir) {
|
|
|
5222
5280
|
}
|
|
5223
5281
|
|
|
5224
5282
|
// src/cli/lib/loading/source-fetcher.ts
|
|
5283
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
5225
5284
|
import { createHash as createHash2 } from "crypto";
|
|
5285
|
+
import { readFileSync } from "fs";
|
|
5286
|
+
import { rename, stat } from "fs/promises";
|
|
5226
5287
|
import { downloadTemplate } from "giget";
|
|
5227
5288
|
import os8 from "os";
|
|
5228
|
-
import
|
|
5289
|
+
import path17 from "path";
|
|
5290
|
+
import { isDeepEqual } from "remeda";
|
|
5291
|
+
import { z as z4 } from "zod";
|
|
5229
5292
|
|
|
5230
5293
|
// src/cli/lib/marketplace-generator.ts
|
|
5231
|
-
import
|
|
5294
|
+
import path16 from "path";
|
|
5232
5295
|
import { countBy, sortBy } from "remeda";
|
|
5233
5296
|
var PLUGIN_MANIFEST_PATH = `${PLUGIN_MANIFEST_DIR}/${PLUGIN_MANIFEST_FILE}`;
|
|
5234
5297
|
var MARKETPLACE_SCHEMA_URL = "https://anthropic.com/claude-code/marketplace.schema.json";
|
|
@@ -5260,7 +5323,7 @@ async function generateMarketplace(pluginsDir2, options) {
|
|
|
5260
5323
|
const rootWithoutDotSlash = options.pluginRoot.replace(/^\.\//, "");
|
|
5261
5324
|
for (const manifestFile of manifestFiles) {
|
|
5262
5325
|
const pluginDirName = pluginDirNameOf(manifestFile);
|
|
5263
|
-
const pluginDir =
|
|
5326
|
+
const pluginDir = path16.join(pluginsDir2, pluginDirName);
|
|
5264
5327
|
const manifest = await readPluginManifest(pluginDir);
|
|
5265
5328
|
if (!manifest) {
|
|
5266
5329
|
warn(`Could not read plugin manifest: '${manifestFile}'`);
|
|
@@ -5315,17 +5378,17 @@ function reservedNameError(marketplaceName, wayOut) {
|
|
|
5315
5378
|
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
5379
|
}
|
|
5317
5380
|
function namespaceViolationError(foreignIds, marketplaceName) {
|
|
5318
|
-
const
|
|
5319
|
-
const unlisted = foreignIds.length -
|
|
5381
|
+
const listed2 = foreignIds.slice(0, MAX_REPORTED_NAMESPACE_VIOLATIONS).map((id) => ` ${id} -> ${marketplaceName}-${id}`);
|
|
5382
|
+
const unlisted = foreignIds.length - listed2.length;
|
|
5320
5383
|
return [
|
|
5321
5384
|
`Marketplace '${marketplaceName}' ships ${foreignIds.length} skill id(s) outside its own namespace. Every skill id must begin with the marketplace's name:`,
|
|
5322
|
-
...
|
|
5385
|
+
...listed2,
|
|
5323
5386
|
...unlisted > 0 ? [` ... and ${unlisted} more`] : [],
|
|
5324
5387
|
`Rename each skill directory and the 'name' in its SKILL.md, re-run 'build plugins', then build the marketplace again.`
|
|
5325
5388
|
].join("\n");
|
|
5326
5389
|
}
|
|
5327
5390
|
async function writeMarketplace(outputPath, marketplace) {
|
|
5328
|
-
await ensureDir(
|
|
5391
|
+
await ensureDir(path16.dirname(outputPath));
|
|
5329
5392
|
const content = `${JSON.stringify(marketplace, null, 2)}
|
|
5330
5393
|
`;
|
|
5331
5394
|
await writeFile(outputPath, content);
|
|
@@ -5343,6 +5406,30 @@ var MAX_NAME_LENGTH = 200;
|
|
|
5343
5406
|
var QUOTED_NAME_LENGTH = 50;
|
|
5344
5407
|
var SOURCE_PROTO_RE = /^([\w-.]+):/;
|
|
5345
5408
|
var GIT_URI_RE = /^(?<repo>[\w.-]+\/[\w.-]+)(?<subdir>[^#]+)?(?<ref>#[\w./@-]+)?/;
|
|
5409
|
+
var MarketplaceUnreachableError = class extends Error {
|
|
5410
|
+
constructor(source, reason) {
|
|
5411
|
+
super(`Could not reach ${source}: ${reason}`);
|
|
5412
|
+
this.source = source;
|
|
5413
|
+
this.reason = reason;
|
|
5414
|
+
this.name = "MarketplaceUnreachableError";
|
|
5415
|
+
}
|
|
5416
|
+
source;
|
|
5417
|
+
reason;
|
|
5418
|
+
};
|
|
5419
|
+
var TarballWithheldError = class extends Error {
|
|
5420
|
+
constructor({ status, statusText }) {
|
|
5421
|
+
super(`the marketplace answered ${status} ${statusText}`.trimEnd());
|
|
5422
|
+
this.name = "TarballWithheldError";
|
|
5423
|
+
}
|
|
5424
|
+
};
|
|
5425
|
+
var marketplaceMustAnswer = new AsyncLocalStorage3();
|
|
5426
|
+
function askingEachMarketplaceItself(body) {
|
|
5427
|
+
const differingCopiesReplaced = /* @__PURE__ */ new Set();
|
|
5428
|
+
return marketplaceMustAnswer.run(differingCopiesReplaced, () => body(differingCopiesReplaced));
|
|
5429
|
+
}
|
|
5430
|
+
function mustAnswer() {
|
|
5431
|
+
return marketplaceMustAnswer.getStore() !== void 0;
|
|
5432
|
+
}
|
|
5346
5433
|
function sanitizeSourceForCache(source) {
|
|
5347
5434
|
const hash = createHash2("sha256").update(source).digest("hex").slice(0, CACHE_HASH_LENGTH);
|
|
5348
5435
|
const readable = source.replace(/[^a-zA-Z0-9]/g, "-").replace(/--+/g, "-").replace(/^-|-$/g, "").slice(0, CACHE_READABLE_PREFIX_LENGTH);
|
|
@@ -5350,7 +5437,7 @@ function sanitizeSourceForCache(source) {
|
|
|
5350
5437
|
}
|
|
5351
5438
|
function getCacheDir(source) {
|
|
5352
5439
|
const sanitized = sanitizeSourceForCache(source) || "unknown";
|
|
5353
|
-
return
|
|
5440
|
+
return path17.join(cacheRoot(), "sources", sanitized);
|
|
5354
5441
|
}
|
|
5355
5442
|
async function fetchFromSource(source, options = {}) {
|
|
5356
5443
|
const { subdir } = options;
|
|
@@ -5361,8 +5448,8 @@ async function fetchFromSource(source, options = {}) {
|
|
|
5361
5448
|
return fetchFromRemoteSource(source, subdir);
|
|
5362
5449
|
}
|
|
5363
5450
|
async function fetchFromLocalSource(source, subdir) {
|
|
5364
|
-
const fullPath = subdir ?
|
|
5365
|
-
const absolutePath =
|
|
5451
|
+
const fullPath = subdir ? path17.join(source, subdir) : source;
|
|
5452
|
+
const absolutePath = path17.isAbsolute(fullPath) ? fullPath : path17.resolve(process.cwd(), fullPath);
|
|
5366
5453
|
if (!await directoryExists2(absolutePath)) {
|
|
5367
5454
|
throw new Error(
|
|
5368
5455
|
`Local marketplace not found: '${absolutePath}'
|
|
@@ -5393,8 +5480,10 @@ function getGigetCacheDir(source) {
|
|
|
5393
5480
|
return void 0;
|
|
5394
5481
|
}
|
|
5395
5482
|
const templateName = uriMatch.groups.repo.replace("/", "-").replace(/[^\da-z-]/gi, "-");
|
|
5396
|
-
|
|
5397
|
-
|
|
5483
|
+
return path17.join(gigetCacheRoot(), providerName, templateName);
|
|
5484
|
+
}
|
|
5485
|
+
function gigetCacheRoot() {
|
|
5486
|
+
return process.env.XDG_CACHE_HOME ? path17.resolve(process.env.XDG_CACHE_HOME, "giget") : path17.resolve(os8.homedir(), ".cache", "giget");
|
|
5398
5487
|
}
|
|
5399
5488
|
async function clearGigetCache(source) {
|
|
5400
5489
|
const gigetDir = getGigetCacheDir(source);
|
|
@@ -5421,25 +5510,26 @@ async function fetchFromRemoteSource(source, subdir) {
|
|
|
5421
5510
|
};
|
|
5422
5511
|
}
|
|
5423
5512
|
announceRefetch(verdict);
|
|
5424
|
-
await discardCachedCopy(cacheDir, source);
|
|
5513
|
+
if (!mustAnswer()) await discardCachedCopy(cacheDir, source);
|
|
5425
5514
|
}
|
|
5426
|
-
|
|
5515
|
+
const differingCopiesReplaced = marketplaceMustAnswer.getStore();
|
|
5516
|
+
return differingCopiesReplaced ? downloadAnsweredFor(cacheDir, source, fullSource, differingCopiesReplaced) : downloadFreshCopy(cacheDir, source, fullSource);
|
|
5427
5517
|
}
|
|
5428
5518
|
async function discardCachedCopy(cacheDir, source) {
|
|
5429
5519
|
await clearGigetCache(source);
|
|
5430
5520
|
await remove(cacheDir);
|
|
5431
5521
|
}
|
|
5432
5522
|
async function downloadFreshCopy(cacheDir, source, fullSource) {
|
|
5433
|
-
await ensureDir(
|
|
5523
|
+
await ensureDir(path17.dirname(cacheDir));
|
|
5434
5524
|
try {
|
|
5435
|
-
const result = await
|
|
5525
|
+
const { result, savedTarball } = await gigetDownload(source, fullSource, {
|
|
5436
5526
|
dir: cacheDir,
|
|
5437
5527
|
force: true,
|
|
5438
5528
|
// Always force when downloading to avoid "already exists" error
|
|
5439
5529
|
offline: false
|
|
5440
5530
|
});
|
|
5441
5531
|
verbose(`Downloaded to: ${result.dir}`);
|
|
5442
|
-
await recordFetchedCopy(cacheDir, result);
|
|
5532
|
+
await recordFetchedCopy(cacheDir, result, savedTarball);
|
|
5443
5533
|
markCopyCurrentForThisRun(cacheDir);
|
|
5444
5534
|
return {
|
|
5445
5535
|
path: result.dir,
|
|
@@ -5451,51 +5541,184 @@ async function downloadFreshCopy(cacheDir, source, fullSource) {
|
|
|
5451
5541
|
throw createDetailedFetchError(error, source);
|
|
5452
5542
|
}
|
|
5453
5543
|
}
|
|
5544
|
+
var STAGED_DOWNLOAD_SUFFIX = ".download";
|
|
5545
|
+
var ONLY_A_SAVED_COPY_CAME_BACK = "the download failed and only a saved copy came back";
|
|
5546
|
+
var SET_ASIDE_SUFFIX = ".set-aside";
|
|
5547
|
+
async function downloadAnsweredFor(cacheDir, source, fullSource, differingCopiesReplaced) {
|
|
5548
|
+
const staged = `${cacheDir}${STAGED_DOWNLOAD_SUFFIX}`;
|
|
5549
|
+
const downloadStaged = () => gigetDownload(source, fullSource, {
|
|
5550
|
+
dir: staged,
|
|
5551
|
+
force: true,
|
|
5552
|
+
forceClean: true,
|
|
5553
|
+
offline: false
|
|
5554
|
+
});
|
|
5555
|
+
await ensureDir(path17.dirname(cacheDir));
|
|
5556
|
+
try {
|
|
5557
|
+
const { result, savedTarball } = await downloadStaged();
|
|
5558
|
+
const record = await askAfterTheDownload(result, source);
|
|
5559
|
+
if (savedTarball) await downloadPast(savedTarball, record, downloadStaged, source);
|
|
5560
|
+
const differed = await savedCopyDiffers(cacheDir, staged);
|
|
5561
|
+
await replaceCopy(cacheDir, staged, record);
|
|
5562
|
+
if (differed) differingCopiesReplaced.add(source);
|
|
5563
|
+
markCopyCurrentForThisRun(cacheDir);
|
|
5564
|
+
return { path: cacheDir, fromCache: false, unreachable: false, source: fullSource };
|
|
5565
|
+
} catch (error) {
|
|
5566
|
+
if (error instanceof MarketplaceUnreachableError) throw error;
|
|
5567
|
+
throw createDetailedFetchError(error, source);
|
|
5568
|
+
} finally {
|
|
5569
|
+
await remove(staged);
|
|
5570
|
+
}
|
|
5571
|
+
}
|
|
5572
|
+
async function downloadPast(savedTarball, record, download, source) {
|
|
5573
|
+
if (!savedUnder(savedTarball, record)) {
|
|
5574
|
+
throw new MarketplaceUnreachableError(source, ONLY_A_SAVED_COPY_CAME_BACK);
|
|
5575
|
+
}
|
|
5576
|
+
await withTarballSetAside(savedTarball.path, download);
|
|
5577
|
+
}
|
|
5578
|
+
async function withTarballSetAside(tarball, download) {
|
|
5579
|
+
const record = gigetRecordOf(tarball);
|
|
5580
|
+
await rename(record, setAsideOf(record));
|
|
5581
|
+
await rename(tarball, setAsideOf(tarball));
|
|
5582
|
+
try {
|
|
5583
|
+
await download();
|
|
5584
|
+
} catch (error) {
|
|
5585
|
+
await rename(setAsideOf(tarball), tarball);
|
|
5586
|
+
await rename(setAsideOf(record), record);
|
|
5587
|
+
throw error;
|
|
5588
|
+
}
|
|
5589
|
+
await Promise.all([remove(setAsideOf(tarball)), remove(setAsideOf(record))]);
|
|
5590
|
+
}
|
|
5591
|
+
function setAsideOf(file) {
|
|
5592
|
+
return `${file}${SET_ASIDE_SUFFIX}`;
|
|
5593
|
+
}
|
|
5594
|
+
async function savedCopyDiffers(cacheDir, downloaded) {
|
|
5595
|
+
if (!await directoryExists2(cacheDir)) return false;
|
|
5596
|
+
try {
|
|
5597
|
+
return !await holdSameFiles(cacheDir, downloaded);
|
|
5598
|
+
} catch (error) {
|
|
5599
|
+
verbose(`Could not read the saved copy at ${cacheDir}: ${getErrorMessage(error)}`);
|
|
5600
|
+
return true;
|
|
5601
|
+
}
|
|
5602
|
+
}
|
|
5603
|
+
async function holdSameFiles(left, right) {
|
|
5604
|
+
const [leftFiles, rightFiles] = await Promise.all([everyFileIn(left), everyFileIn(right)]);
|
|
5605
|
+
return isDeepEqual(leftFiles, rightFiles) && leftFiles.every((file) => sameBytesAt(file, left, right));
|
|
5606
|
+
}
|
|
5607
|
+
var EVERY_FILE = "**/*";
|
|
5608
|
+
function everyFileIn(dir) {
|
|
5609
|
+
return glob(EVERY_FILE, dir, { dot: true });
|
|
5610
|
+
}
|
|
5611
|
+
function sameBytesAt(file, left, right) {
|
|
5612
|
+
return readFileSync(path17.join(left, file)).equals(readFileSync(path17.join(right, file)));
|
|
5613
|
+
}
|
|
5614
|
+
async function replaceCopy(cacheDir, staged, record) {
|
|
5615
|
+
await remove(cacheDir);
|
|
5616
|
+
await rename(staged, cacheDir);
|
|
5617
|
+
await writeFile(fetchRecordPath(cacheDir), JSON.stringify(record));
|
|
5618
|
+
}
|
|
5619
|
+
var GIGET_TARBALLS = "**/*.tar.gz";
|
|
5620
|
+
var gigetTarballRecordSchema = z4.object({ etag: z4.string().min(1).optional() });
|
|
5621
|
+
async function gigetDownload(source, fullSource, options) {
|
|
5622
|
+
const providerDir = gigetProviderDir(source);
|
|
5623
|
+
const timesBefore = await tarballTimes(providerDir);
|
|
5624
|
+
const result = await downloadTemplate(fullSource, options);
|
|
5625
|
+
const tarball = path17.join(providerDir, gigetTarballOf(result));
|
|
5626
|
+
const writtenByThisDownload = timesBefore.get(tarball) !== await modifiedAt(tarball);
|
|
5627
|
+
if (writtenByThisDownload) return { result };
|
|
5628
|
+
verbose(`giget extracted the tarball it saved earlier, not a download: ${tarball}`);
|
|
5629
|
+
return { result, savedTarball: { path: tarball, etag: await etagSavedWith(tarball) } };
|
|
5630
|
+
}
|
|
5631
|
+
function gigetProviderDir(source) {
|
|
5632
|
+
const provider = SOURCE_PROTO_RE.exec(source)?.[1];
|
|
5633
|
+
if (provider === void 0) throw new Error(`No provider named in '${source}'`);
|
|
5634
|
+
return path17.join(gigetCacheRoot(), provider);
|
|
5635
|
+
}
|
|
5636
|
+
async function tarballTimes(providerDir) {
|
|
5637
|
+
const tarballs = await glob(GIGET_TARBALLS, providerDir);
|
|
5638
|
+
const paths = tarballs.map((tarball) => path17.join(providerDir, tarball));
|
|
5639
|
+
return new Map(await Promise.all(paths.map(async (at) => [at, await modifiedAt(at)])));
|
|
5640
|
+
}
|
|
5641
|
+
async function modifiedAt(file) {
|
|
5642
|
+
return await fileExists(file) ? (await stat(file)).mtimeMs : void 0;
|
|
5643
|
+
}
|
|
5644
|
+
function gigetTarballOf(result) {
|
|
5645
|
+
const name = result.name;
|
|
5646
|
+
const version = result.version;
|
|
5647
|
+
if (typeof name !== "string") throw new Error("the download named no template");
|
|
5648
|
+
const stem = typeof version === "string" && version !== "" ? version : name;
|
|
5649
|
+
return path17.join(name, `${stem}.tar.gz`);
|
|
5650
|
+
}
|
|
5651
|
+
function gigetRecordOf(tarball) {
|
|
5652
|
+
return `${tarball}.json`;
|
|
5653
|
+
}
|
|
5654
|
+
async function etagSavedWith(tarballPath) {
|
|
5655
|
+
const raw = await readFileOptional(gigetRecordOf(tarballPath));
|
|
5656
|
+
const parsed = gigetTarballRecordSchema.safeParse(parseJsonOrUndefined(raw));
|
|
5657
|
+
return parsed.success ? parsed.data.etag : void 0;
|
|
5658
|
+
}
|
|
5659
|
+
function savedUnder(savedTarball, record) {
|
|
5660
|
+
return savedTarball.etag !== void 0 && savedTarball.etag === record.etag;
|
|
5661
|
+
}
|
|
5454
5662
|
var REVALIDATION_TIMEOUT_MS = 5e3;
|
|
5455
5663
|
var FETCH_RECORD_SUFFIX = ".etag.json";
|
|
5456
5664
|
var askedThisRun = /* @__PURE__ */ new Map();
|
|
5665
|
+
var askedItselfThisRun = /* @__PURE__ */ new Map();
|
|
5666
|
+
function verdictsThisRun() {
|
|
5667
|
+
return mustAnswer() ? askedItselfThisRun : askedThisRun;
|
|
5668
|
+
}
|
|
5457
5669
|
function announceRefetch(verdict) {
|
|
5458
5670
|
if (verdict === "superseded") {
|
|
5459
5671
|
log(STATUS_MESSAGES.MARKETPLACE_HAS_NEWER_CONTENT);
|
|
5460
5672
|
}
|
|
5461
5673
|
}
|
|
5462
5674
|
function revalidateCachedCopy(cacheDir, source) {
|
|
5463
|
-
const
|
|
5675
|
+
const verdicts = verdictsThisRun();
|
|
5676
|
+
const asked = verdicts.get(cacheDir);
|
|
5464
5677
|
if (asked) return asked;
|
|
5465
5678
|
const verdict = classifyCachedCopy(cacheDir, source);
|
|
5466
|
-
|
|
5679
|
+
verdicts.set(cacheDir, verdict);
|
|
5467
5680
|
return verdict;
|
|
5468
5681
|
}
|
|
5469
5682
|
function markCopyCurrentForThisRun(cacheDir) {
|
|
5470
|
-
|
|
5683
|
+
verdictsThisRun().set(cacheDir, Promise.resolve("current"));
|
|
5471
5684
|
}
|
|
5472
5685
|
async function classifyCachedCopy(cacheDir, source) {
|
|
5473
5686
|
const record = await readFetchRecord(cacheDir);
|
|
5474
5687
|
if (!record) return "unrecorded";
|
|
5475
|
-
if (record.etag === void 0) {
|
|
5688
|
+
if (record.etag === void 0 && !mustAnswer()) {
|
|
5476
5689
|
verbose(`No ETag was recorded for ${record.tar} \u2014 keeping the cached copy`);
|
|
5477
5690
|
return "current";
|
|
5478
5691
|
}
|
|
5479
5692
|
try {
|
|
5480
|
-
|
|
5481
|
-
if (live === void 0) {
|
|
5482
|
-
verbose(`${record.tar} answered without an ETag \u2014 keeping the cached copy`);
|
|
5483
|
-
return "current";
|
|
5484
|
-
}
|
|
5485
|
-
return live === record.etag ? "current" : "superseded";
|
|
5693
|
+
return judgeAnswer(record, await fetchEtag(record.tar));
|
|
5486
5694
|
} catch (error) {
|
|
5695
|
+
if (error instanceof TarballWithheldError) throw createDetailedFetchError(error, source);
|
|
5487
5696
|
verbose(`Could not revalidate ${record.tar}: ${getErrorMessage(error)}`);
|
|
5697
|
+
if (mustAnswer()) throw new MarketplaceUnreachableError(source, getErrorMessage(error));
|
|
5488
5698
|
warn(sourceUnreachableUsingCache(source));
|
|
5489
5699
|
return "unreachable";
|
|
5490
5700
|
}
|
|
5491
5701
|
}
|
|
5702
|
+
function judgeAnswer(record, live) {
|
|
5703
|
+
if (movedOn(record, live)) return "superseded";
|
|
5704
|
+
if (mustAnswer()) return "answered";
|
|
5705
|
+
if (live === void 0) {
|
|
5706
|
+
verbose(`${record.tar} answered without an ETag \u2014 keeping the cached copy`);
|
|
5707
|
+
}
|
|
5708
|
+
return "current";
|
|
5709
|
+
}
|
|
5710
|
+
function movedOn(record, live) {
|
|
5711
|
+
return live !== void 0 && record.etag !== void 0 && live !== record.etag;
|
|
5712
|
+
}
|
|
5492
5713
|
async function fetchEtag(tar) {
|
|
5493
5714
|
const auth = process.env.GIGET_AUTH;
|
|
5494
5715
|
const response = await fetch(tar, {
|
|
5495
|
-
method: "HEAD",
|
|
5716
|
+
method: mustAnswer() ? "GET" : "HEAD",
|
|
5496
5717
|
...auth && { headers: { Authorization: `Bearer ${auth}` } },
|
|
5497
5718
|
signal: AbortSignal.timeout(REVALIDATION_TIMEOUT_MS)
|
|
5498
5719
|
});
|
|
5720
|
+
await response.body?.cancel();
|
|
5721
|
+
if (mustAnswer() && !response.ok) throw new TarballWithheldError(response);
|
|
5499
5722
|
return response.headers.get("etag") ?? void 0;
|
|
5500
5723
|
}
|
|
5501
5724
|
function fetchRecordPath(cacheDir) {
|
|
@@ -5511,15 +5734,41 @@ async function readFetchRecord(cacheDir) {
|
|
|
5511
5734
|
}
|
|
5512
5735
|
return parsed.data;
|
|
5513
5736
|
}
|
|
5514
|
-
async function recordFetchedCopy(cacheDir, result) {
|
|
5515
|
-
const tar = result
|
|
5516
|
-
if (
|
|
5737
|
+
async function recordFetchedCopy(cacheDir, result, savedTarball) {
|
|
5738
|
+
const tar = tarballOf(result);
|
|
5739
|
+
if (tar === void 0) {
|
|
5517
5740
|
verbose("No tarball URL came back from the download \u2014 the copy cannot be revalidated");
|
|
5518
5741
|
return;
|
|
5519
5742
|
}
|
|
5520
|
-
const etag =
|
|
5521
|
-
|
|
5522
|
-
|
|
5743
|
+
const etag = savedTarball ? savedTarball.etag : await etagIfAnswered(tar);
|
|
5744
|
+
await writeFile(fetchRecordPath(cacheDir), JSON.stringify(fetchRecord(tar, etag)));
|
|
5745
|
+
}
|
|
5746
|
+
async function etagIfAnswered(tar) {
|
|
5747
|
+
try {
|
|
5748
|
+
return await fetchEtag(tar);
|
|
5749
|
+
} catch (error) {
|
|
5750
|
+
verbose(`Could not ask ${tar} after the download: ${getErrorMessage(error)}`);
|
|
5751
|
+
return void 0;
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5754
|
+
async function askAfterTheDownload(result, source) {
|
|
5755
|
+
const tar = tarballOf(result);
|
|
5756
|
+
if (tar === void 0) {
|
|
5757
|
+
throw new Error("the download named no tarball to ask the marketplace about");
|
|
5758
|
+
}
|
|
5759
|
+
try {
|
|
5760
|
+
return fetchRecord(tar, await fetchEtag(tar));
|
|
5761
|
+
} catch (error) {
|
|
5762
|
+
if (error instanceof TarballWithheldError) throw error;
|
|
5763
|
+
throw new MarketplaceUnreachableError(source, getErrorMessage(error));
|
|
5764
|
+
}
|
|
5765
|
+
}
|
|
5766
|
+
function tarballOf(result) {
|
|
5767
|
+
const tar = result.tar;
|
|
5768
|
+
return typeof tar === "string" ? tar : void 0;
|
|
5769
|
+
}
|
|
5770
|
+
function fetchRecord(tar, etag) {
|
|
5771
|
+
return { tar, ...etag !== void 0 && { etag } };
|
|
5523
5772
|
}
|
|
5524
5773
|
function parseJsonOrUndefined(raw) {
|
|
5525
5774
|
try {
|
|
@@ -5824,9 +6073,9 @@ function agentNotFoundError(agentName, agents) {
|
|
|
5824
6073
|
}
|
|
5825
6074
|
function describeAvailableAgents(availableAgents) {
|
|
5826
6075
|
if (availableAgents.length === 0) return "No agents found in scanned directories";
|
|
5827
|
-
const
|
|
6076
|
+
const listed2 = availableAgents.slice(0, MAX_LISTED_AVAILABLE_AGENTS).join(", ");
|
|
5828
6077
|
const unlisted = availableAgents.length - MAX_LISTED_AVAILABLE_AGENTS;
|
|
5829
|
-
return unlisted > 0 ? `Available agents: ${
|
|
6078
|
+
return unlisted > 0 ? `Available agents: ${listed2} (and ${unlisted} more)` : `Available agents: ${listed2}`;
|
|
5830
6079
|
}
|
|
5831
6080
|
function assembleAgentConfig(agentName, definition, agentConfig, resolvedSkills) {
|
|
5832
6081
|
const model = agentConfig.model ?? definition.model;
|
|
@@ -5863,10 +6112,10 @@ function resolveStackProperty(generated, assigned) {
|
|
|
5863
6112
|
return generated === void 0 ? void 0 : assigned;
|
|
5864
6113
|
}
|
|
5865
6114
|
function resolveDescription(loadedStack, shared) {
|
|
5866
|
-
return
|
|
6115
|
+
return shared ?? loadedStack?.description;
|
|
5867
6116
|
}
|
|
5868
6117
|
function configNameFor(projectDir) {
|
|
5869
|
-
return isHomeDirectory(projectDir) ? DEFAULT_PLUGIN_NAME :
|
|
6118
|
+
return isHomeDirectory(projectDir) ? DEFAULT_PLUGIN_NAME : path18.basename(projectDir);
|
|
5870
6119
|
}
|
|
5871
6120
|
async function loadCuratedStack(projectDir, projectStack) {
|
|
5872
6121
|
const globalStack = isHomeDirectory(projectDir) ? void 0 : await loadGlobalStack();
|
|
@@ -6039,12 +6288,12 @@ async function installationIfPresent(candidate) {
|
|
|
6039
6288
|
|
|
6040
6289
|
// src/cli/lib/hosts/claude-host.ts
|
|
6041
6290
|
import os12 from "os";
|
|
6042
|
-
import
|
|
6043
|
-
import { z as
|
|
6291
|
+
import path21 from "path";
|
|
6292
|
+
import { z as z6 } from "zod";
|
|
6044
6293
|
|
|
6045
6294
|
// src/cli/utils/exec.ts
|
|
6046
6295
|
import { spawn } from "child_process";
|
|
6047
|
-
import
|
|
6296
|
+
import path19 from "path";
|
|
6048
6297
|
var MAX_PLUGIN_PATH_LENGTH = 1024;
|
|
6049
6298
|
var MAX_PLUGIN_NAME_LENGTH = 256;
|
|
6050
6299
|
var MAX_MARKETPLACE_SOURCE_LENGTH = 1024;
|
|
@@ -6052,7 +6301,7 @@ var SAFE_NAME_PATTERN2 = /^[a-zA-Z0-9._@/-]+$/;
|
|
|
6052
6301
|
var SAFE_PLUGIN_PATH_PATTERN = /^[a-zA-Z0-9._@/:~-]+$/;
|
|
6053
6302
|
var OPENS_A_RELATIVE_PATH = /^\.\.?[/\\]/;
|
|
6054
6303
|
function namesADirectoryOnThisMachine(source) {
|
|
6055
|
-
return
|
|
6304
|
+
return path19.isAbsolute(source) || OPENS_A_RELATIVE_PATH.test(source);
|
|
6056
6305
|
}
|
|
6057
6306
|
var CONTROL_CHAR_PATTERN = /[\x00-\x08\x0E-\x1F\x7F]/u;
|
|
6058
6307
|
function validatePluginPath(pluginPath) {
|
|
@@ -6142,28 +6391,28 @@ async function execCommand(command, args, options) {
|
|
|
6142
6391
|
}
|
|
6143
6392
|
|
|
6144
6393
|
// src/cli/lib/plugins/plugin-settings.ts
|
|
6145
|
-
import
|
|
6146
|
-
import { z as
|
|
6147
|
-
var pluginSettingsSchema =
|
|
6394
|
+
import path20 from "path";
|
|
6395
|
+
import { z as z5 } from "zod";
|
|
6396
|
+
var pluginSettingsSchema = z5.object({
|
|
6148
6397
|
// Absent when no plugin is enabled.
|
|
6149
|
-
enabledPlugins:
|
|
6398
|
+
enabledPlugins: z5.record(z5.string(), z5.unknown()).exactOptional()
|
|
6150
6399
|
}).passthrough();
|
|
6151
6400
|
var installationFields = {
|
|
6152
|
-
installPath:
|
|
6153
|
-
version:
|
|
6154
|
-
installedAt:
|
|
6401
|
+
installPath: z5.string(),
|
|
6402
|
+
version: z5.string(),
|
|
6403
|
+
installedAt: z5.string()
|
|
6155
6404
|
};
|
|
6156
|
-
var pluginInstallationSchema =
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6405
|
+
var pluginInstallationSchema = z5.discriminatedUnion("scope", [
|
|
6406
|
+
z5.object({ scope: z5.literal("project"), projectPath: z5.string(), ...installationFields }),
|
|
6407
|
+
z5.object({ scope: z5.literal("user"), ...installationFields }),
|
|
6408
|
+
z5.object({ scope: z5.literal("local"), ...installationFields })
|
|
6160
6409
|
]);
|
|
6161
|
-
var installedPluginsSchema =
|
|
6162
|
-
version:
|
|
6163
|
-
plugins:
|
|
6410
|
+
var installedPluginsSchema = z5.object({
|
|
6411
|
+
version: z5.number(),
|
|
6412
|
+
plugins: z5.record(z5.string(), z5.array(pluginInstallationSchema))
|
|
6164
6413
|
}).passthrough();
|
|
6165
6414
|
function getInstalledPluginsRegistryPath(pluginsDir2) {
|
|
6166
|
-
return
|
|
6415
|
+
return path20.join(pluginsDir2, INSTALLED_PLUGINS_FILE);
|
|
6167
6416
|
}
|
|
6168
6417
|
async function readRegisteredInstallations(pluginsDir2) {
|
|
6169
6418
|
const registryPath = getInstalledPluginsRegistryPath(pluginsDir2);
|
|
@@ -6196,7 +6445,7 @@ async function listPluginInstallsForProject(pluginsDir2, projectDir) {
|
|
|
6196
6445
|
});
|
|
6197
6446
|
}
|
|
6198
6447
|
async function getEnabledPluginKeys(projectDir) {
|
|
6199
|
-
const settingsPath =
|
|
6448
|
+
const settingsPath = path20.join(projectDir, CLAUDE_DIR, STANDARD_FILES.SETTINGS_JSON);
|
|
6200
6449
|
if (!await fileExists(settingsPath)) {
|
|
6201
6450
|
verbose(`No settings.json found at '${settingsPath}'`);
|
|
6202
6451
|
return [];
|
|
@@ -6264,12 +6513,12 @@ async function isClaudeCLIAvailable(options) {
|
|
|
6264
6513
|
return false;
|
|
6265
6514
|
}
|
|
6266
6515
|
}
|
|
6267
|
-
var marketplaceInfoListSchema =
|
|
6268
|
-
|
|
6269
|
-
name:
|
|
6270
|
-
source:
|
|
6271
|
-
repo:
|
|
6272
|
-
path:
|
|
6516
|
+
var marketplaceInfoListSchema = z6.array(
|
|
6517
|
+
z6.object({
|
|
6518
|
+
name: z6.string(),
|
|
6519
|
+
source: z6.string(),
|
|
6520
|
+
repo: z6.string().exactOptional(),
|
|
6521
|
+
path: z6.string().exactOptional()
|
|
6273
6522
|
})
|
|
6274
6523
|
);
|
|
6275
6524
|
async function claudePluginMarketplaceList(options) {
|
|
@@ -6344,7 +6593,7 @@ function namesNothingThere(message) {
|
|
|
6344
6593
|
return NOT_INSTALLED_MESSAGES.some((phrase) => message.includes(phrase));
|
|
6345
6594
|
}
|
|
6346
6595
|
function claudePluginsRoot(projectDir, options) {
|
|
6347
|
-
if (options?.configDir !== void 0) return
|
|
6596
|
+
if (options?.configDir !== void 0) return path21.join(options.configDir, PLUGINS_SUBDIR);
|
|
6348
6597
|
return pluginsDir(CLAUDE, "global", projectDir);
|
|
6349
6598
|
}
|
|
6350
6599
|
async function claudeListPlugins(projectDir, options) {
|
|
@@ -6379,8 +6628,8 @@ function claudeHost() {
|
|
|
6379
6628
|
|
|
6380
6629
|
// src/cli/lib/hosts/codex-host.ts
|
|
6381
6630
|
import os13 from "os";
|
|
6382
|
-
import
|
|
6383
|
-
import { z as
|
|
6631
|
+
import path22 from "path";
|
|
6632
|
+
import { z as z7 } from "zod";
|
|
6384
6633
|
var CODEX = "codex";
|
|
6385
6634
|
var PLUGIN_CACHE_SUBDIR = "cache";
|
|
6386
6635
|
var STANDING_WARNING = /could not create PATH aliases/;
|
|
@@ -6443,27 +6692,27 @@ async function readJsonFromCodex(schema, args, options, attempt, askedFrom) {
|
|
|
6443
6692
|
}
|
|
6444
6693
|
return validated.data;
|
|
6445
6694
|
}
|
|
6446
|
-
var marketplaceRegistrySchema =
|
|
6447
|
-
marketplaces:
|
|
6448
|
-
|
|
6449
|
-
name:
|
|
6450
|
-
marketplaceSource:
|
|
6695
|
+
var marketplaceRegistrySchema = z7.object({
|
|
6696
|
+
marketplaces: z7.array(
|
|
6697
|
+
z7.object({
|
|
6698
|
+
name: z7.string(),
|
|
6699
|
+
marketplaceSource: z7.object({ sourceType: z7.string(), source: z7.string() })
|
|
6451
6700
|
})
|
|
6452
6701
|
)
|
|
6453
6702
|
});
|
|
6454
|
-
var pluginListingSchema =
|
|
6455
|
-
installed:
|
|
6456
|
-
|
|
6457
|
-
pluginId:
|
|
6458
|
-
name:
|
|
6459
|
-
marketplaceName:
|
|
6460
|
-
version:
|
|
6461
|
-
enabled:
|
|
6703
|
+
var pluginListingSchema = z7.object({
|
|
6704
|
+
installed: z7.array(
|
|
6705
|
+
z7.object({
|
|
6706
|
+
pluginId: z7.string(),
|
|
6707
|
+
name: z7.string(),
|
|
6708
|
+
marketplaceName: z7.string(),
|
|
6709
|
+
version: z7.string(),
|
|
6710
|
+
enabled: z7.boolean()
|
|
6462
6711
|
})
|
|
6463
6712
|
)
|
|
6464
6713
|
});
|
|
6465
6714
|
function codexPluginsRoot(projectDir, options) {
|
|
6466
|
-
if (options?.configDir !== void 0) return
|
|
6715
|
+
if (options?.configDir !== void 0) return path22.join(options.configDir, PLUGINS_SUBDIR);
|
|
6467
6716
|
return pluginsDir(CODEX, "global", projectDir);
|
|
6468
6717
|
}
|
|
6469
6718
|
async function isCodexCLIAvailable(options) {
|
|
@@ -6593,7 +6842,7 @@ async function codexListPlugins(projectDir, options) {
|
|
|
6593
6842
|
async function codexPluginsOrUnreadable(projectDir, options) {
|
|
6594
6843
|
const pluginsRoot = codexPluginsRoot(projectDir, options);
|
|
6595
6844
|
if (pluginsRoot === null) return { kind: "listed", plugins: [] };
|
|
6596
|
-
const cacheRoot2 =
|
|
6845
|
+
const cacheRoot2 = path22.join(pluginsRoot, PLUGIN_CACHE_SUBDIR);
|
|
6597
6846
|
let installed;
|
|
6598
6847
|
try {
|
|
6599
6848
|
installed = await codexPluginListing(options, projectDir);
|
|
@@ -6605,7 +6854,7 @@ async function codexPluginsOrUnreadable(projectDir, options) {
|
|
|
6605
6854
|
kind: "listed",
|
|
6606
6855
|
plugins: installed.map((plugin) => ({
|
|
6607
6856
|
pluginKey: plugin.pluginId,
|
|
6608
|
-
installPath:
|
|
6857
|
+
installPath: path22.join(cacheRoot2, plugin.marketplaceName, plugin.name, plugin.version),
|
|
6609
6858
|
enabled: plugin.enabled
|
|
6610
6859
|
}))
|
|
6611
6860
|
};
|
|
@@ -6915,7 +7164,7 @@ function formatInstallationDisplay(info) {
|
|
|
6915
7164
|
}
|
|
6916
7165
|
|
|
6917
7166
|
// src/cli/lib/plugins/plugin-validator.ts
|
|
6918
|
-
import
|
|
7167
|
+
import path23 from "path";
|
|
6919
7168
|
|
|
6920
7169
|
// src/cli/lib/validation-result.ts
|
|
6921
7170
|
function validResult() {
|
|
@@ -6947,15 +7196,15 @@ async function validatePluginStructure(pluginPath) {
|
|
|
6947
7196
|
if (!await directoryExists2(pluginPath)) {
|
|
6948
7197
|
return invalidResult(`Plugin directory does not exist: ${pluginPath}`);
|
|
6949
7198
|
}
|
|
6950
|
-
const pluginDir =
|
|
7199
|
+
const pluginDir = path23.join(pluginPath, PLUGIN_MANIFEST_DIR);
|
|
6951
7200
|
if (!await directoryExists2(pluginDir)) {
|
|
6952
7201
|
errors.push(`Missing ${PLUGIN_MANIFEST_DIR}/ directory`);
|
|
6953
7202
|
}
|
|
6954
|
-
const manifestPath =
|
|
7203
|
+
const manifestPath = path23.join(pluginDir, STANDARD_FILES.PLUGIN_JSON);
|
|
6955
7204
|
if (!await fileExists(manifestPath)) {
|
|
6956
7205
|
errors.push(`Missing ${PLUGIN_MANIFEST_DIR}/${STANDARD_FILES.PLUGIN_JSON}`);
|
|
6957
7206
|
}
|
|
6958
|
-
const readmePath =
|
|
7207
|
+
const readmePath = path23.join(pluginPath, STANDARD_FILES.README_MD);
|
|
6959
7208
|
if (!await fileExists(readmePath)) {
|
|
6960
7209
|
warnings.push("Missing README.md (recommended for documentation)");
|
|
6961
7210
|
}
|
|
@@ -6998,15 +7247,15 @@ async function validatePluginManifest(manifestPath) {
|
|
|
6998
7247
|
if (!manifest.description) {
|
|
6999
7248
|
warnings.push("Missing description field (recommended for discoverability)");
|
|
7000
7249
|
}
|
|
7001
|
-
const pluginDir =
|
|
7250
|
+
const pluginDir = path23.dirname(path23.dirname(manifestPath));
|
|
7002
7251
|
if (isNonEmptyString(manifest.skills)) {
|
|
7003
|
-
const skillsPath =
|
|
7252
|
+
const skillsPath = path23.join(pluginDir, manifest.skills);
|
|
7004
7253
|
if (!await directoryExists2(skillsPath)) {
|
|
7005
7254
|
errors.push(`Skills path does not exist: ${manifest.skills}`);
|
|
7006
7255
|
}
|
|
7007
7256
|
}
|
|
7008
7257
|
if (isNonEmptyString(manifest.agents)) {
|
|
7009
|
-
const agentsPath =
|
|
7258
|
+
const agentsPath = path23.join(pluginDir, manifest.agents);
|
|
7010
7259
|
if (!await directoryExists2(agentsPath)) {
|
|
7011
7260
|
errors.push(`Agents path does not exist: ${manifest.agents}`);
|
|
7012
7261
|
}
|
|
@@ -7075,14 +7324,14 @@ function prefixResult(result, prefix) {
|
|
|
7075
7324
|
async function validateEachFile(pluginPath, scannedDir, files, validate) {
|
|
7076
7325
|
const results = await Promise.all(
|
|
7077
7326
|
files.map(async (file) => {
|
|
7078
|
-
const filePath =
|
|
7079
|
-
return prefixResult(await validate(filePath),
|
|
7327
|
+
const filePath = path23.join(scannedDir, file);
|
|
7328
|
+
return prefixResult(await validate(filePath), path23.relative(pluginPath, filePath));
|
|
7080
7329
|
})
|
|
7081
7330
|
);
|
|
7082
7331
|
return mergeValidationResults(results);
|
|
7083
7332
|
}
|
|
7084
7333
|
async function validatePluginSkillFiles(pluginPath, skillsRelPath) {
|
|
7085
|
-
const skillsDir2 =
|
|
7334
|
+
const skillsDir2 = path23.join(pluginPath, skillsRelPath);
|
|
7086
7335
|
if (!await directoryExists2(skillsDir2)) return validResult();
|
|
7087
7336
|
const files = await glob(`**/${STANDARD_FILES.SKILL_MD}`, skillsDir2);
|
|
7088
7337
|
if (files.length === 0) {
|
|
@@ -7095,7 +7344,7 @@ async function validatePluginSkillFiles(pluginPath, skillsRelPath) {
|
|
|
7095
7344
|
return validateEachFile(pluginPath, skillsDir2, files, validateSkillFrontmatter);
|
|
7096
7345
|
}
|
|
7097
7346
|
async function validatePluginAgentFiles(pluginPath, agentsRelPath) {
|
|
7098
|
-
const agentsDir2 =
|
|
7347
|
+
const agentsDir2 = path23.join(pluginPath, agentsRelPath);
|
|
7099
7348
|
if (!await directoryExists2(agentsDir2)) return validResult();
|
|
7100
7349
|
const files = await glob(agentCodec("claude").listGlob, agentsDir2);
|
|
7101
7350
|
if (files.length === 0) {
|
|
@@ -7166,7 +7415,7 @@ async function listPluginDirNames(pluginsDir2) {
|
|
|
7166
7415
|
const dirChecks = await Promise.all(
|
|
7167
7416
|
allDirs.map(async (dirName) => ({
|
|
7168
7417
|
dirName,
|
|
7169
|
-
isPlugin: await directoryExists2(
|
|
7418
|
+
isPlugin: await directoryExists2(path23.join(pluginsDir2, dirName, PLUGIN_MANIFEST_DIR))
|
|
7170
7419
|
}))
|
|
7171
7420
|
);
|
|
7172
7421
|
return dirChecks.filter((c) => c.isPlugin).map((c) => c.dirName);
|
|
@@ -7174,7 +7423,7 @@ async function listPluginDirNames(pluginsDir2) {
|
|
|
7174
7423
|
async function validateEachPlugin(pluginsDir2, pluginDirs) {
|
|
7175
7424
|
const results = [];
|
|
7176
7425
|
for (const pluginName of pluginDirs) {
|
|
7177
|
-
const result = await validatePlugin(
|
|
7426
|
+
const result = await validatePlugin(path23.join(pluginsDir2, pluginName));
|
|
7178
7427
|
results.push({ name: pluginName, result });
|
|
7179
7428
|
}
|
|
7180
7429
|
return results;
|
|
@@ -7257,11 +7506,11 @@ function sanitizeSkillName(name) {
|
|
|
7257
7506
|
return name.replace(/\+/g, "-");
|
|
7258
7507
|
}
|
|
7259
7508
|
function isSingleFolderName(name) {
|
|
7260
|
-
return name !== "" && name !== "." && name !== ".." &&
|
|
7509
|
+
return name !== "" && name !== "." && name !== ".." && path24.basename(name) === name;
|
|
7261
7510
|
}
|
|
7262
7511
|
async function assertClearable(pluginDir, skillPath, skillName) {
|
|
7263
|
-
const skill = `Skill '${
|
|
7264
|
-
const file = `File: ${
|
|
7512
|
+
const skill = `Skill '${path24.basename(skillPath)}'`;
|
|
7513
|
+
const file = `File: ${path24.join(skillPath, STANDARD_FILES.SKILL_MD)}`;
|
|
7265
7514
|
if (!isSingleFolderName(skillName)) {
|
|
7266
7515
|
throw new Error(
|
|
7267
7516
|
`${skill} is named '${skillName}', which is not a single folder name, so its plugin cannot be built inside the output directory. ${file}`
|
|
@@ -7274,22 +7523,12 @@ async function assertClearable(pluginDir, skillPath, skillName) {
|
|
|
7274
7523
|
}
|
|
7275
7524
|
if (await holdsContentNoBuildMade(pluginDir)) {
|
|
7276
7525
|
throw new Error(
|
|
7277
|
-
`${skill} would be built into ${pluginDir}, which holds no ${
|
|
7526
|
+
`${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
7527
|
);
|
|
7279
7528
|
}
|
|
7280
7529
|
}
|
|
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
7530
|
async function readPluginSkillMetadata(skillPath) {
|
|
7292
|
-
const metadataPath =
|
|
7531
|
+
const metadataPath = path24.join(skillPath, STANDARD_FILES.METADATA_YAML);
|
|
7293
7532
|
if (!await fileExists(metadataPath)) {
|
|
7294
7533
|
return null;
|
|
7295
7534
|
}
|
|
@@ -7333,28 +7572,28 @@ This skill is automatically available when installed.
|
|
|
7333
7572
|
}
|
|
7334
7573
|
async function writeSkillFiles(skillPath, pluginSkillDir, skillMdContent) {
|
|
7335
7574
|
await ensureDir(pluginSkillDir);
|
|
7336
|
-
await writeFile(
|
|
7575
|
+
await writeFile(path24.join(pluginSkillDir, STANDARD_FILES.SKILL_MD), skillMdContent);
|
|
7337
7576
|
verbose(` Copied ${STANDARD_FILES.SKILL_MD}`);
|
|
7338
7577
|
for (const fileName of SKILL_CONTENT_FILES) {
|
|
7339
7578
|
if (fileName === STANDARD_FILES.SKILL_MD) continue;
|
|
7340
|
-
const sourcePath =
|
|
7579
|
+
const sourcePath = path24.join(skillPath, fileName);
|
|
7341
7580
|
if (await fileExists(sourcePath)) {
|
|
7342
7581
|
const content = await readFile(sourcePath);
|
|
7343
|
-
await writeFile(
|
|
7582
|
+
await writeFile(path24.join(pluginSkillDir, fileName), content);
|
|
7344
7583
|
verbose(` Copied ${fileName}`);
|
|
7345
7584
|
}
|
|
7346
7585
|
}
|
|
7347
7586
|
for (const dirName of SKILL_CONTENT_DIRS) {
|
|
7348
|
-
const sourceDir2 =
|
|
7587
|
+
const sourceDir2 = path24.join(skillPath, dirName);
|
|
7349
7588
|
if (await fileExists(sourceDir2)) {
|
|
7350
|
-
await copy(sourceDir2,
|
|
7589
|
+
await copy(sourceDir2, path24.join(pluginSkillDir, dirName));
|
|
7351
7590
|
verbose(` Copied ${dirName}/`);
|
|
7352
7591
|
}
|
|
7353
7592
|
}
|
|
7354
7593
|
}
|
|
7355
7594
|
async function readSkillMdToBuild(skillPath) {
|
|
7356
|
-
const dirBasename =
|
|
7357
|
-
const skillMdPath =
|
|
7595
|
+
const dirBasename = path24.basename(skillPath);
|
|
7596
|
+
const skillMdPath = path24.join(skillPath, STANDARD_FILES.SKILL_MD);
|
|
7358
7597
|
if (!await fileExists(skillMdPath)) {
|
|
7359
7598
|
throw new Error(
|
|
7360
7599
|
`Skill '${dirBasename}' is missing required ${STANDARD_FILES.SKILL_MD} file. Expected at: ${skillMdPath}`
|
|
@@ -7373,7 +7612,7 @@ async function compileSkillPlugin(options) {
|
|
|
7373
7612
|
const { skillPath, outputDir, skillName: overrideName } = options;
|
|
7374
7613
|
const { skillMdContent, frontmatter } = await readSkillMdToBuild(skillPath);
|
|
7375
7614
|
const skillName = overrideName ?? sanitizeSkillName(frontmatter.name);
|
|
7376
|
-
const pluginDir =
|
|
7615
|
+
const pluginDir = path24.join(outputDir, skillName);
|
|
7377
7616
|
await assertClearable(pluginDir, skillPath, skillName);
|
|
7378
7617
|
verbose(`Compiling skill plugin: ${skillName} from ${skillPath}`);
|
|
7379
7618
|
const metadata = await readPluginSkillMetadata(skillPath);
|
|
@@ -7390,11 +7629,11 @@ async function compileSkillPlugin(options) {
|
|
|
7390
7629
|
await writeContentHash(pluginDir, await computeSkillFolderHash(skillPath), getPluginManifestPath);
|
|
7391
7630
|
await writeSkillFiles(
|
|
7392
7631
|
skillPath,
|
|
7393
|
-
|
|
7632
|
+
path24.join(pluginDir, STANDARD_DIRS.SKILLS, skillName),
|
|
7394
7633
|
skillMdContent
|
|
7395
7634
|
);
|
|
7396
7635
|
const readme = generateReadme(skillName, frontmatter);
|
|
7397
|
-
await writeFile(
|
|
7636
|
+
await writeFile(path24.join(pluginDir, STANDARD_FILES.README_MD), readme);
|
|
7398
7637
|
verbose(" Generated README.md");
|
|
7399
7638
|
const manifest = manifestAt(
|
|
7400
7639
|
versionAfterRebuild(earlierBuild, await computeShippedHash(pluginDir))
|
|
@@ -7412,7 +7651,7 @@ async function compileAllSkillPlugins(skillsDir2, outputDir) {
|
|
|
7412
7651
|
const failed = [];
|
|
7413
7652
|
const skillMdFiles = await glob(`**/${STANDARD_FILES.SKILL_MD}`, skillsDir2);
|
|
7414
7653
|
for (const skillMdFile of skillMdFiles) {
|
|
7415
|
-
const skillPath =
|
|
7654
|
+
const skillPath = path24.join(skillsDir2, path24.dirname(skillMdFile));
|
|
7416
7655
|
try {
|
|
7417
7656
|
const result = await compileSkillPlugin({
|
|
7418
7657
|
skillPath,
|
|
@@ -7422,7 +7661,7 @@ async function compileAllSkillPlugins(skillsDir2, outputDir) {
|
|
|
7422
7661
|
log(` [OK] ${result.skillName}`);
|
|
7423
7662
|
} catch (error) {
|
|
7424
7663
|
const errorMessage = getErrorMessage(error);
|
|
7425
|
-
const dirBasename =
|
|
7664
|
+
const dirBasename = path24.basename(skillPath);
|
|
7426
7665
|
failed.push(dirBasename);
|
|
7427
7666
|
warn(`Failed to compile skill from '${dirBasename}': ${errorMessage}`);
|
|
7428
7667
|
}
|
|
@@ -7438,16 +7677,23 @@ Compiled ${plural(results.length, "skill plugin")}:`);
|
|
|
7438
7677
|
}
|
|
7439
7678
|
|
|
7440
7679
|
// src/cli/lib/skills/local-skill-loader.ts
|
|
7441
|
-
import
|
|
7442
|
-
|
|
7443
|
-
const
|
|
7680
|
+
import path25 from "path";
|
|
7681
|
+
function copiedInSkillsAt(root, provider) {
|
|
7682
|
+
const scope = isHomeDirectory(root) ? "global" : "project";
|
|
7683
|
+
return {
|
|
7684
|
+
dir: skillsDir(provider, scope, root),
|
|
7685
|
+
pathPrefix: skillsPathPrefix(provider, scope, root)
|
|
7686
|
+
};
|
|
7687
|
+
}
|
|
7688
|
+
async function discoverLocalSkills(root, provider = providerInUse(root)) {
|
|
7689
|
+
const { dir: localSkillsPath, pathPrefix } = copiedInSkillsAt(root, provider);
|
|
7444
7690
|
if (!await directoryExists2(localSkillsPath)) {
|
|
7445
7691
|
verbose(`Local skills directory not found: ${localSkillsPath}`);
|
|
7446
7692
|
return null;
|
|
7447
7693
|
}
|
|
7448
7694
|
const skillDirs = await listDirectories(localSkillsPath);
|
|
7449
7695
|
const extracted = await Promise.all(
|
|
7450
|
-
skillDirs.map((skillDirName) => extractLocalSkill(localSkillsPath, skillDirName))
|
|
7696
|
+
skillDirs.map((skillDirName) => extractLocalSkill(localSkillsPath, pathPrefix, skillDirName))
|
|
7451
7697
|
);
|
|
7452
7698
|
const skills = extracted.filter((skill) => skill !== null);
|
|
7453
7699
|
verbose(`Discovered ${plural(skills.length, "local skill")} from ${localSkillsPath}`);
|
|
@@ -7456,10 +7702,10 @@ async function discoverLocalSkills(projectDir) {
|
|
|
7456
7702
|
localSkillsPath
|
|
7457
7703
|
};
|
|
7458
7704
|
}
|
|
7459
|
-
async function extractLocalSkill(localSkillsPath, skillDirName) {
|
|
7460
|
-
const skillDir =
|
|
7461
|
-
const metadataPath =
|
|
7462
|
-
const skillMdPath =
|
|
7705
|
+
async function extractLocalSkill(localSkillsPath, pathPrefix, skillDirName) {
|
|
7706
|
+
const skillDir = path25.join(localSkillsPath, skillDirName);
|
|
7707
|
+
const metadataPath = path25.join(skillDir, STANDARD_FILES.METADATA_YAML);
|
|
7708
|
+
const skillMdPath = path25.join(skillDir, STANDARD_FILES.SKILL_MD);
|
|
7463
7709
|
if (!await fileExists(metadataPath)) {
|
|
7464
7710
|
verbose(`Skipping local skill '${skillDirName}': No metadata.yaml found`);
|
|
7465
7711
|
return null;
|
|
@@ -7488,8 +7734,8 @@ async function extractLocalSkill(localSkillsPath, skillDirName) {
|
|
|
7488
7734
|
verbose(`Skipping local skill '${skillDirName}': invalid SKILL.md frontmatter`);
|
|
7489
7735
|
return null;
|
|
7490
7736
|
}
|
|
7491
|
-
const relativePath = `${
|
|
7492
|
-
const absolutePath =
|
|
7737
|
+
const relativePath = `${pathPrefix}/${skillDirName}/`;
|
|
7738
|
+
const absolutePath = path25.join(localSkillsPath, skillDirName) + path25.sep;
|
|
7493
7739
|
const skillId = frontmatter.name;
|
|
7494
7740
|
const extracted = {
|
|
7495
7741
|
id: skillId,
|
|
@@ -7512,7 +7758,7 @@ async function extractLocalSkill(localSkillsPath, skillDirName) {
|
|
|
7512
7758
|
}
|
|
7513
7759
|
|
|
7514
7760
|
// src/cli/lib/skills/unresolved-skill-entries.ts
|
|
7515
|
-
import
|
|
7761
|
+
import path26 from "path";
|
|
7516
7762
|
async function findUnusableSavedSkillMetadata(savedSkills, projectDir) {
|
|
7517
7763
|
const verdicts = await Promise.all(
|
|
7518
7764
|
savedSkills.map((saved) => unusableMetadataOf(saved, projectDir))
|
|
@@ -7531,11 +7777,11 @@ function claimsLocalCopy(saved) {
|
|
|
7531
7777
|
}
|
|
7532
7778
|
function installedMetadataPath(saved, projectDir) {
|
|
7533
7779
|
const { skillsDir: skillsDir2 } = resolveInstallPaths(projectDir, saved.scope);
|
|
7534
|
-
return
|
|
7780
|
+
return path26.join(skillsDir2, saved.id, STANDARD_FILES.METADATA_YAML);
|
|
7535
7781
|
}
|
|
7536
7782
|
|
|
7537
7783
|
// src/cli/lib/skills/local-skill-mover.ts
|
|
7538
|
-
import
|
|
7784
|
+
import path27 from "path";
|
|
7539
7785
|
var THE_DIRECTORY_ITSELF = ".";
|
|
7540
7786
|
function validateSkillId(skillId) {
|
|
7541
7787
|
return !(skillId.length === 0 || skillId === THE_DIRECTORY_ITSELF || skillId.includes("\0") || skillId.includes("..") || skillId.includes("/") || skillId.includes("\\"));
|
|
@@ -7545,8 +7791,8 @@ async function deleteLocalSkill(projectDir, skillId, scope = "project") {
|
|
|
7545
7791
|
warn(`Invalid skill ID for deletion: '${skillId}'`);
|
|
7546
7792
|
return;
|
|
7547
7793
|
}
|
|
7548
|
-
const skillsDir2 =
|
|
7549
|
-
const skillPath =
|
|
7794
|
+
const skillsDir2 = path27.resolve(resolveInstallPaths(projectDir, scope).skillsDir);
|
|
7795
|
+
const skillPath = path27.resolve(skillsDir2, skillId);
|
|
7550
7796
|
if (!isPathWithin(skillPath, skillsDir2)) {
|
|
7551
7797
|
warn(`Skill ID '${skillId}' resolves outside the skills directory.`);
|
|
7552
7798
|
return;
|
|
@@ -7564,10 +7810,10 @@ async function migrateLocalSkillScope(skillId, fromScope, projectDir) {
|
|
|
7564
7810
|
return;
|
|
7565
7811
|
}
|
|
7566
7812
|
const toScope = fromScope === "global" ? "project" : "global";
|
|
7567
|
-
const fromSkillsDir =
|
|
7568
|
-
const toSkillsDir =
|
|
7569
|
-
const fromPath =
|
|
7570
|
-
const toPath =
|
|
7813
|
+
const fromSkillsDir = path27.resolve(resolveInstallPaths(projectDir, fromScope).skillsDir);
|
|
7814
|
+
const toSkillsDir = path27.resolve(resolveInstallPaths(projectDir, toScope).skillsDir);
|
|
7815
|
+
const fromPath = path27.resolve(fromSkillsDir, skillId);
|
|
7816
|
+
const toPath = path27.resolve(toSkillsDir, skillId);
|
|
7571
7817
|
if (!isPathWithin(fromPath, fromSkillsDir)) {
|
|
7572
7818
|
warn(`Skill ID '${skillId}' resolves outside the source skills directory.`);
|
|
7573
7819
|
return;
|
|
@@ -7594,7 +7840,7 @@ async function migrateLocalSkillScope(skillId, fromScope, projectDir) {
|
|
|
7594
7840
|
|
|
7595
7841
|
// src/cli/lib/matrix/matrix-loader.ts
|
|
7596
7842
|
import { parse as parseYaml5 } from "yaml";
|
|
7597
|
-
import
|
|
7843
|
+
import path28 from "path";
|
|
7598
7844
|
async function loadSkillCategories(configPath) {
|
|
7599
7845
|
const data = await loadConfig(configPath, skillCategoriesFileSchema);
|
|
7600
7846
|
if (!data) {
|
|
@@ -7630,20 +7876,21 @@ async function extractAllSkills(skillsDir2) {
|
|
|
7630
7876
|
return skills;
|
|
7631
7877
|
}
|
|
7632
7878
|
async function extractSkill(skillsDir2, metadataFile) {
|
|
7633
|
-
const skillDir =
|
|
7634
|
-
const skillMdPath =
|
|
7879
|
+
const skillDir = path28.dirname(metadataFile);
|
|
7880
|
+
const skillMdPath = path28.join(skillsDir2, skillDir, STANDARD_FILES.SKILL_MD);
|
|
7635
7881
|
if (!await fileExists(skillMdPath)) {
|
|
7636
7882
|
verbose(`Skipping ${metadataFile}: No ${STANDARD_FILES.SKILL_MD} found`);
|
|
7637
7883
|
return null;
|
|
7638
7884
|
}
|
|
7639
|
-
const metadata = await readMatrixMetadataOrWarn(metadataFile,
|
|
7885
|
+
const metadata = await readMatrixMetadataOrWarn(metadataFile, path28.join(skillsDir2, metadataFile));
|
|
7640
7886
|
if (!metadata) return null;
|
|
7641
7887
|
const frontmatter = parseFrontmatterOrWarn(skillDir, skillMdPath, await readFile(skillMdPath));
|
|
7642
7888
|
if (!frontmatter) return null;
|
|
7643
7889
|
if (!metadata.displayName) {
|
|
7644
|
-
|
|
7645
|
-
`
|
|
7890
|
+
warn(
|
|
7891
|
+
`Skipping '${metadataFile}': invalid ${STANDARD_FILES.METADATA_YAML} \u2014 missing required field: ${METADATA_KEYS.DISPLAY_NAME}`
|
|
7646
7892
|
);
|
|
7893
|
+
return null;
|
|
7647
7894
|
}
|
|
7648
7895
|
const skillId = frontmatter.name;
|
|
7649
7896
|
const extracted = {
|
|
@@ -7689,7 +7936,7 @@ function parseFrontmatterOrWarn(skillDir, skillMdPath, skillMdContent) {
|
|
|
7689
7936
|
const unreadable = unreadableFrontmatter(skillMdContent);
|
|
7690
7937
|
if (unreadable !== void 0) {
|
|
7691
7938
|
warn(
|
|
7692
|
-
`Skipping '${
|
|
7939
|
+
`Skipping '${path28.join(skillDir, STANDARD_FILES.SKILL_MD)}': unparseable ${STANDARD_FILES.SKILL_MD} frontmatter at ${skillMdPath} \u2014 ${unreadable}`
|
|
7693
7940
|
);
|
|
7694
7941
|
}
|
|
7695
7942
|
return null;
|
|
@@ -9580,7 +9827,7 @@ async function loadCatalogue(options) {
|
|
|
9580
9827
|
...catalogue,
|
|
9581
9828
|
marketplaceSkillIds: new Set(typedKeys(catalogue.matrix.skills))
|
|
9582
9829
|
};
|
|
9583
|
-
result.matrix = await mergeLocalSkills(result.matrix, resolvedProjectDir);
|
|
9830
|
+
result.matrix = await mergeLocalSkills(result.matrix, resolvedProjectDir, provider);
|
|
9584
9831
|
await seatGlobalInstallationSkills(result.matrix, source, purpose);
|
|
9585
9832
|
if (!options.skipExtraSources) {
|
|
9586
9833
|
await loadSkillsFromAllSources(
|
|
@@ -9742,12 +9989,12 @@ function goesOnWithoutManifest(source, purpose) {
|
|
|
9742
9989
|
function configuredLabel(sourceConfig) {
|
|
9743
9990
|
return sourceConfig.marketplace === void 0 ? {} : { marketplace: sourceConfig.marketplace };
|
|
9744
9991
|
}
|
|
9745
|
-
async function mergeLocalSkills(matrix2, projectDir) {
|
|
9746
|
-
const withGlobal = isHomeDirectory(projectDir) ? matrix2 : await mergeDiscoveredLocalSkills(matrix2, os14.homedir(), "global");
|
|
9747
|
-
return mergeDiscoveredLocalSkills(withGlobal, projectDir, "project");
|
|
9992
|
+
async function mergeLocalSkills(matrix2, projectDir, provider) {
|
|
9993
|
+
const withGlobal = isHomeDirectory(projectDir) ? matrix2 : await mergeDiscoveredLocalSkills(matrix2, os14.homedir(), "global", provider);
|
|
9994
|
+
return mergeDiscoveredLocalSkills(withGlobal, projectDir, "project", provider);
|
|
9748
9995
|
}
|
|
9749
|
-
async function mergeDiscoveredLocalSkills(matrix2, dir, label) {
|
|
9750
|
-
const discovered = await discoverLocalSkills(dir);
|
|
9996
|
+
async function mergeDiscoveredLocalSkills(matrix2, dir, label, provider) {
|
|
9997
|
+
const discovered = await discoverLocalSkills(dir, provider);
|
|
9751
9998
|
if (!discovered || discovered.skills.length === 0) return matrix2;
|
|
9752
9999
|
verbose(
|
|
9753
10000
|
`Found ${discovered.skills.length} ${label} local skill(s) in ${discovered.localSkillsPath}`
|
|
@@ -9800,7 +10047,7 @@ async function loadAndMergeFromBasePath(basePath, source) {
|
|
|
9800
10047
|
const skillsDirRelPath = sourceProjectConfig?.skillsDir ?? SKILLS_DIR_PATH;
|
|
9801
10048
|
const stacksRelFile = sourceProjectConfig?.stacksFile;
|
|
9802
10049
|
const { categories, sourceRules } = await loadSourceTaxonomy(basePath);
|
|
9803
|
-
const skillsDir2 =
|
|
10050
|
+
const skillsDir2 = path29.join(basePath, skillsDirRelPath);
|
|
9804
10051
|
verbose(`Skills from source: ${skillsDir2}`);
|
|
9805
10052
|
const skills = await extractAllSkills(skillsDir2);
|
|
9806
10053
|
await refuseCatalogueCollisions(basePath, source, skills);
|
|
@@ -9823,7 +10070,7 @@ async function loadSourceTaxonomy(basePath) {
|
|
|
9823
10070
|
return { categories, sourceRules };
|
|
9824
10071
|
}
|
|
9825
10072
|
async function loadSourceCategories(basePath) {
|
|
9826
|
-
const categoriesPath =
|
|
10073
|
+
const categoriesPath = path29.join(basePath, SKILL_CATEGORIES_PATH);
|
|
9827
10074
|
if (!await fileExists(categoriesPath)) return void 0;
|
|
9828
10075
|
const categories = await loadSkillCategories(categoriesPath);
|
|
9829
10076
|
verbose(
|
|
@@ -9832,7 +10079,7 @@ async function loadSourceCategories(basePath) {
|
|
|
9832
10079
|
return categories;
|
|
9833
10080
|
}
|
|
9834
10081
|
async function loadSourceRules(basePath) {
|
|
9835
|
-
const rulesPath =
|
|
10082
|
+
const rulesPath = path29.join(basePath, SKILL_RULES_PATH);
|
|
9836
10083
|
if (!await fileExists(rulesPath)) return void 0;
|
|
9837
10084
|
const rules = await loadSkillRules(rulesPath);
|
|
9838
10085
|
verbose(`Loaded source rules: ${rulesPath}`);
|
|
@@ -9847,11 +10094,11 @@ async function refuseCatalogueCollisions(basePath, source, skills) {
|
|
|
9847
10094
|
throw new Error(catalogueCollisionError(collidingIds, source));
|
|
9848
10095
|
}
|
|
9849
10096
|
function catalogueCollisionError(collidingIds, source) {
|
|
9850
|
-
const
|
|
9851
|
-
const unlisted = collidingIds.length -
|
|
10097
|
+
const listed2 = collidingIds.slice(0, MAX_REPORTED_COLLISIONS).map((id) => ` ${id}`);
|
|
10098
|
+
const unlisted = collidingIds.length - listed2.length;
|
|
9852
10099
|
return [
|
|
9853
10100
|
`Marketplace '${source}' ships ${collidingIds.length} skill id(s) the public catalogue already owns:`,
|
|
9854
|
-
...
|
|
10101
|
+
...listed2,
|
|
9855
10102
|
...unlisted > 0 ? [` ... and ${unlisted} more`] : [],
|
|
9856
10103
|
`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
10104
|
].join("\n");
|
|
@@ -9940,7 +10187,7 @@ function declaresCategory(matrix2, category) {
|
|
|
9940
10187
|
return category !== LOCAL_PSEUDO_CATEGORY && matrix2.categories[category] !== void 0;
|
|
9941
10188
|
}
|
|
9942
10189
|
function undeclaredCategoryRefusal(metadata, category) {
|
|
9943
|
-
const metadataPath =
|
|
10190
|
+
const metadataPath = path29.join(metadata.path, STANDARD_FILES.METADATA_YAML);
|
|
9944
10191
|
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
10192
|
}
|
|
9946
10193
|
function mergeLocalSkillsIntoMatrix(matrix2, localResult) {
|
|
@@ -10076,15 +10323,14 @@ export {
|
|
|
10076
10323
|
skippedUnknownAgents,
|
|
10077
10324
|
carriedSkillsWritten,
|
|
10078
10325
|
SHARED_CONFIG_APPLY,
|
|
10079
|
-
|
|
10080
|
-
projectCopyMovedToGlobal,
|
|
10081
|
-
registeredProjectsRewritten,
|
|
10326
|
+
registeredProjectsRecompiled,
|
|
10082
10327
|
sharedConfigNeedsTerminal,
|
|
10083
10328
|
nothingInstalledToApplyTo,
|
|
10084
10329
|
uninstallNeedsTerminal,
|
|
10085
10330
|
addMarketplaceInTheEditor,
|
|
10086
10331
|
SHARED_CONFIG_ARRIVALS,
|
|
10087
10332
|
keptAsInstalledGlobally,
|
|
10333
|
+
subAgentChanged,
|
|
10088
10334
|
addedToGlobalSubAgents,
|
|
10089
10335
|
authoredHereKept,
|
|
10090
10336
|
unplaceableKept,
|
|
@@ -10094,11 +10340,10 @@ export {
|
|
|
10094
10340
|
uninstallSkillMetadataUnreadable,
|
|
10095
10341
|
recreateConfigFrom,
|
|
10096
10342
|
providerArgument,
|
|
10343
|
+
configUnreadableError,
|
|
10097
10344
|
registeredProjectsUpdateFailed,
|
|
10098
10345
|
unmarkedAgentsKept,
|
|
10099
|
-
|
|
10100
|
-
localSkillsRemoval,
|
|
10101
|
-
compiledAgentsRemoval,
|
|
10346
|
+
configFileRemoved,
|
|
10102
10347
|
scopeNoun,
|
|
10103
10348
|
scopeLabel,
|
|
10104
10349
|
layoutIsCurrent,
|
|
@@ -10106,6 +10351,7 @@ export {
|
|
|
10106
10351
|
GlobalPairWriteViolation,
|
|
10107
10352
|
withGateToken,
|
|
10108
10353
|
assertGateToken,
|
|
10354
|
+
isPathWithin,
|
|
10109
10355
|
readFile,
|
|
10110
10356
|
readFileSafe,
|
|
10111
10357
|
readFileOptional,
|
|
@@ -10135,11 +10381,14 @@ export {
|
|
|
10135
10381
|
validateSkillMetadata,
|
|
10136
10382
|
splitMetadataValidationIssues,
|
|
10137
10383
|
ConfigDefaultExportError,
|
|
10384
|
+
NO_VALID_DEFAULT_EXPORT,
|
|
10385
|
+
configLoadFailure,
|
|
10138
10386
|
loadConfig,
|
|
10139
10387
|
getProjectConfigPath,
|
|
10140
10388
|
getInstalledConfigPath,
|
|
10141
10389
|
resolveInstallPaths,
|
|
10142
10390
|
providerToNameAt,
|
|
10391
|
+
initProviderToNameAt,
|
|
10143
10392
|
isHomeDirectory,
|
|
10144
10393
|
SOURCE_ENV_VAR,
|
|
10145
10394
|
readSourceRepoConfig,
|
|
@@ -10150,11 +10399,8 @@ export {
|
|
|
10150
10399
|
resolvePrimarySourceEntry,
|
|
10151
10400
|
MarketplaceRefusedError,
|
|
10152
10401
|
isLocalSource,
|
|
10153
|
-
typedEntries,
|
|
10154
|
-
typedKeys,
|
|
10155
|
-
typedFromEntries,
|
|
10156
|
-
typedValues,
|
|
10157
10402
|
normalizeStackRecord,
|
|
10403
|
+
loadStackById,
|
|
10158
10404
|
defaultUsageGuidance,
|
|
10159
10405
|
resolveAgentConfigToSkills,
|
|
10160
10406
|
getStackSkillIds,
|
|
@@ -10183,11 +10429,12 @@ export {
|
|
|
10183
10429
|
flatUnion,
|
|
10184
10430
|
renderTypeImportLine,
|
|
10185
10431
|
spaces,
|
|
10432
|
+
EXTRACTED_FIELDS,
|
|
10186
10433
|
generateConfigSource,
|
|
10187
10434
|
refuseUnwritableStack,
|
|
10188
10435
|
generateBlankGlobalConfigSource,
|
|
10189
|
-
typedEntries2,
|
|
10190
|
-
typedKeys2,
|
|
10436
|
+
typedEntries2 as typedEntries,
|
|
10437
|
+
typedKeys2 as typedKeys,
|
|
10191
10438
|
isScopePairCompatible,
|
|
10192
10439
|
splitConfigByScope,
|
|
10193
10440
|
seedToInstallSelection,
|
|
@@ -10201,9 +10448,12 @@ export {
|
|
|
10201
10448
|
loadSkillsFromDir,
|
|
10202
10449
|
getCurrentDate,
|
|
10203
10450
|
computeStringHash,
|
|
10204
|
-
|
|
10451
|
+
computeShippedHash,
|
|
10452
|
+
readEarlierBuild,
|
|
10453
|
+
versionAfterRebuild,
|
|
10205
10454
|
writeContentHash,
|
|
10206
10455
|
readForkedFromMetadata,
|
|
10456
|
+
readLocalSkillMetadata,
|
|
10207
10457
|
validateSkillPath,
|
|
10208
10458
|
copySkillsToLocalFlattened,
|
|
10209
10459
|
getUserPluginsDir,
|
|
@@ -10216,6 +10466,8 @@ export {
|
|
|
10216
10466
|
writePluginManifest,
|
|
10217
10467
|
buildMarketplacePluginRef,
|
|
10218
10468
|
parseMarketplacePluginRef,
|
|
10469
|
+
holdsContentNoBuildMade,
|
|
10470
|
+
clearEarlierBuild,
|
|
10219
10471
|
getInstallationInfo,
|
|
10220
10472
|
getInstallationsByProvider,
|
|
10221
10473
|
formatInstallationDisplay,
|
|
@@ -10248,6 +10500,8 @@ export {
|
|
|
10248
10500
|
validateSkillIdNamespace,
|
|
10249
10501
|
writeMarketplace,
|
|
10250
10502
|
getMarketplaceStats,
|
|
10503
|
+
MarketplaceUnreachableError,
|
|
10504
|
+
askingEachMarketplaceItself,
|
|
10251
10505
|
MarketplaceNameRefusedError,
|
|
10252
10506
|
fetchMarketplace,
|
|
10253
10507
|
isLocalOnlySkill,
|
|
@@ -10271,4 +10525,4 @@ export {
|
|
|
10271
10525
|
executeMigration,
|
|
10272
10526
|
keepsGlobalInstall
|
|
10273
10527
|
};
|
|
10274
|
-
//# sourceMappingURL=chunk-
|
|
10528
|
+
//# sourceMappingURL=chunk-WVFBBZEP.js.map
|