agents-inc 0.174.0 → 0.176.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 +115 -0
- package/dist/{chunk-APWMMZOK.js → chunk-3F5LT433.js} +157 -53
- package/dist/chunk-3F5LT433.js.map +1 -0
- package/dist/chunk-5CFHP6Z5.js +109 -0
- package/dist/chunk-5CFHP6Z5.js.map +1 -0
- package/dist/{chunk-3PJZ4QNJ.js → chunk-B7AVWSK2.js} +21 -11
- package/dist/chunk-B7AVWSK2.js.map +1 -0
- package/dist/{chunk-H5E6IO2L.js → chunk-C2BSCEDN.js} +9 -11
- package/dist/chunk-C2BSCEDN.js.map +1 -0
- package/dist/{chunk-R7GKQBZJ.js → chunk-CKBWEWHZ.js} +96 -52
- package/dist/chunk-CKBWEWHZ.js.map +1 -0
- package/dist/{chunk-IXVGBUWI.js → chunk-EC3JXT23.js} +5 -5
- package/dist/{chunk-E45IT3HC.js → chunk-ENET4IF6.js} +998 -472
- package/dist/chunk-ENET4IF6.js.map +1 -0
- package/dist/{chunk-FQF6VHO3.js → chunk-FACDJH6O.js} +12 -105
- package/dist/chunk-FACDJH6O.js.map +1 -0
- package/dist/{chunk-TVEDNGVP.js → chunk-H6WUOW5F.js} +2 -2
- package/dist/{chunk-7BHDKEGK.js → chunk-JNRACLNS.js} +3 -3
- package/dist/{chunk-IAQFZFWD.js → chunk-JTMSFBCA.js} +231 -24
- package/dist/chunk-JTMSFBCA.js.map +1 -0
- package/dist/{chunk-EFXVYQGT.js → chunk-MQZNDK5F.js} +39 -40
- package/dist/chunk-MQZNDK5F.js.map +1 -0
- package/dist/{chunk-QCX66KBM.js → chunk-SN64WNWE.js} +3 -3
- package/dist/{chunk-WUSEZFER.js → chunk-U46LYYRA.js} +3 -2
- package/dist/{chunk-WUSEZFER.js.map → chunk-U46LYYRA.js.map} +1 -1
- package/dist/{chunk-6PGXTLA4.js → chunk-U672FZVC.js} +2 -2
- package/dist/{chunk-AIBVAYHS.js → chunk-UE4Z3YMB.js} +2 -2
- package/dist/chunk-UE4Z3YMB.js.map +1 -0
- package/dist/{chunk-REHVGFJE.js → chunk-XMQIWPIR.js} +84 -32
- package/dist/chunk-XMQIWPIR.js.map +1 -0
- package/dist/{chunk-I3WF6L43.js → chunk-XYBHH3M5.js} +2 -2
- package/dist/{chunk-ECPFYHDX.js → chunk-ZBI6GFJD.js} +11 -11
- package/dist/chunk-ZBI6GFJD.js.map +1 -0
- package/dist/commands/build/marketplace.js +5 -4
- package/dist/commands/build/marketplace.js.map +1 -1
- package/dist/commands/build/plugins.js +7 -6
- package/dist/commands/build/plugins.js.map +1 -1
- package/dist/commands/compile.js +20 -16
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/doctor.js +106 -87
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/edit.js +76 -48
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/eject.js +31 -18
- package/dist/commands/eject.js.map +1 -1
- package/dist/commands/init.js +17 -17
- package/dist/commands/list.js +10 -9
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/new/marketplace.js +5 -4
- package/dist/commands/new/marketplace.js.map +1 -1
- package/dist/commands/search.js +14 -9
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/share.js +20 -22
- package/dist/commands/share.js.map +1 -1
- package/dist/commands/uninstall.js +24 -19
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.js +110 -31
- package/dist/commands/update.js.map +1 -1
- package/dist/components/common/select-list.js +2 -2
- package/dist/components/common/skill-agent-summary.js +4 -3
- package/dist/components/common/summary-panel.js +5 -4
- package/dist/hooks/init.js +24 -25
- package/dist/hooks/init.js.map +1 -1
- package/dist/load-agent-defs-5BSECHE6.js +14 -0
- package/dist/{recompile-project-agents-KYK44C24.js → recompile-project-agents-U3UGHPWG.js} +8 -8
- package/dist/{source-loader-G5I4RNUP.js → source-loader-E2Y5MMN5.js} +4 -4
- package/package.json +1 -1
- package/dist/chunk-3PJZ4QNJ.js.map +0 -1
- package/dist/chunk-AIBVAYHS.js.map +0 -1
- package/dist/chunk-APWMMZOK.js.map +0 -1
- package/dist/chunk-E45IT3HC.js.map +0 -1
- package/dist/chunk-ECPFYHDX.js.map +0 -1
- package/dist/chunk-EFXVYQGT.js.map +0 -1
- package/dist/chunk-FQF6VHO3.js.map +0 -1
- package/dist/chunk-H5E6IO2L.js.map +0 -1
- package/dist/chunk-IAQFZFWD.js.map +0 -1
- package/dist/chunk-ORO2T4K2.js +0 -96
- package/dist/chunk-ORO2T4K2.js.map +0 -1
- package/dist/chunk-R7GKQBZJ.js.map +0 -1
- package/dist/chunk-REHVGFJE.js.map +0 -1
- package/dist/load-agent-defs-5U5KAURA.js +0 -14
- /package/dist/{chunk-IXVGBUWI.js.map → chunk-EC3JXT23.js.map} +0 -0
- /package/dist/{chunk-TVEDNGVP.js.map → chunk-H6WUOW5F.js.map} +0 -0
- /package/dist/{chunk-7BHDKEGK.js.map → chunk-JNRACLNS.js.map} +0 -0
- /package/dist/{chunk-QCX66KBM.js.map → chunk-SN64WNWE.js.map} +0 -0
- /package/dist/{chunk-6PGXTLA4.js.map → chunk-U672FZVC.js.map} +0 -0
- /package/dist/{chunk-I3WF6L43.js.map → chunk-XYBHH3M5.js.map} +0 -0
- /package/dist/{load-agent-defs-5U5KAURA.js.map → load-agent-defs-5BSECHE6.js.map} +0 -0
- /package/dist/{recompile-project-agents-KYK44C24.js.map → recompile-project-agents-U3UGHPWG.js.map} +0 -0
- /package/dist/{source-loader-G5I4RNUP.js.map → source-loader-E2Y5MMN5.js.map} +0 -0
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
typedFromEntries,
|
|
14
14
|
typedKeys,
|
|
15
15
|
typedValues
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-UE4Z3YMB.js";
|
|
17
17
|
import {
|
|
18
18
|
AGENT_NAMES,
|
|
19
19
|
AUTHOR_HANDLE_PATTERN,
|
|
@@ -82,7 +82,7 @@ import {
|
|
|
82
82
|
seedAgentScope,
|
|
83
83
|
sourceDirName,
|
|
84
84
|
validateSelection
|
|
85
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-U46LYYRA.js";
|
|
86
86
|
import {
|
|
87
87
|
defaultCategories,
|
|
88
88
|
defaultRules,
|
|
@@ -91,8 +91,8 @@ import {
|
|
|
91
91
|
|
|
92
92
|
// src/cli/lib/loading/source-loader.ts
|
|
93
93
|
import os14 from "os";
|
|
94
|
-
import { partition as
|
|
95
|
-
import
|
|
94
|
+
import { partition as partition6, unique as unique6 } from "remeda";
|
|
95
|
+
import path28 from "path";
|
|
96
96
|
|
|
97
97
|
// src/cli/lib/installation/is-home-directory.ts
|
|
98
98
|
import fs from "fs";
|
|
@@ -105,33 +105,6 @@ function isHomeDirectory(dir) {
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
// src/cli/lib/metadata-keys.ts
|
|
109
|
-
var METADATA_KEYS = {
|
|
110
|
-
DISPLAY_NAME: "displayName",
|
|
111
|
-
CLI_DESCRIPTION: "cliDescription",
|
|
112
|
-
CATEGORY: "category",
|
|
113
|
-
FORKED_FROM: "forkedFrom",
|
|
114
|
-
CONTENT_HASH: "contentHash",
|
|
115
|
-
USAGE_GUIDANCE: "usageGuidance",
|
|
116
|
-
CUSTOM: "custom"
|
|
117
|
-
};
|
|
118
|
-
var LOCAL_DEFAULTS = {
|
|
119
|
-
AUTHOR: "@dummy-author"
|
|
120
|
-
};
|
|
121
|
-
var SKILL_CONTENT_FILES = [STANDARD_FILES.SKILL_MD, STANDARD_FILES.REFERENCE_MD];
|
|
122
|
-
var SKILL_CONTENT_DIRS = [STANDARD_DIRS.EXAMPLES, STANDARD_DIRS.SCRIPTS];
|
|
123
|
-
|
|
124
|
-
// src/cli/utils/errors.ts
|
|
125
|
-
function getErrorMessage(error) {
|
|
126
|
-
return stripTerminalControls(error instanceof Error ? error.message : String(error));
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// src/cli/utils/fs.ts
|
|
130
|
-
import fs3 from "fs-extra";
|
|
131
|
-
import fg from "fast-glob";
|
|
132
|
-
import os3 from "os";
|
|
133
|
-
import path2 from "path";
|
|
134
|
-
|
|
135
108
|
// src/cli/lib/installation/install-layout.ts
|
|
136
109
|
import fs2 from "fs";
|
|
137
110
|
import os2 from "os";
|
|
@@ -508,11 +481,38 @@ function chooseProviderForThisRun(provider) {
|
|
|
508
481
|
function forgetTheProviderChosenForThisRun() {
|
|
509
482
|
providerChosenForThisRun = void 0;
|
|
510
483
|
}
|
|
484
|
+
function declaresNoContent(config) {
|
|
485
|
+
return config.skills.length === 0 && config.agents.length === 0;
|
|
486
|
+
}
|
|
487
|
+
var declaresNothingAsRead = /* @__PURE__ */ new Map();
|
|
488
|
+
function recordWhetherItDeclaresNothing(configPath, declaresNothing) {
|
|
489
|
+
const stamp = stampOf(configPath);
|
|
490
|
+
if (stamp !== null) declaresNothingAsRead.set(configPath, { stamp, declaresNothing });
|
|
491
|
+
}
|
|
492
|
+
function declaresNothingAsLastRead(configPath) {
|
|
493
|
+
const read = declaresNothingAsRead.get(configPath);
|
|
494
|
+
if (read === void 0 || read.stamp !== stampOf(configPath)) return void 0;
|
|
495
|
+
return read.declaresNothing;
|
|
496
|
+
}
|
|
497
|
+
function stampOf(file) {
|
|
498
|
+
try {
|
|
499
|
+
const stats = fs2.statSync(file, { throwIfNoEntry: false });
|
|
500
|
+
return stats === void 0 ? null : `${stats.mtimeMs}:${stats.size}`;
|
|
501
|
+
} catch {
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
511
505
|
function providerInUse(root) {
|
|
512
506
|
if (providerChosenForThisRun !== void 0) return providerChosenForThisRun;
|
|
513
507
|
if (root === void 0) return PROVIDER_OF_A_NEW_INSTALLATION;
|
|
514
508
|
return providerOnDisk(root);
|
|
515
509
|
}
|
|
510
|
+
function inheritedGlobalProvider(projectDir, own = providerInUse(projectDir)) {
|
|
511
|
+
return holdsAnInstallation(projectDir, own) ? own : providerInUse(os2.homedir());
|
|
512
|
+
}
|
|
513
|
+
function providerAtScope(projectDir, scope) {
|
|
514
|
+
return scope === "global" ? inheritedGlobalProvider(projectDir) : providerInUse(projectDir);
|
|
515
|
+
}
|
|
516
516
|
function providerOnDisk(root) {
|
|
517
517
|
const live = PROVIDERS.find((provider) => holdsAnInstallation(root, provider));
|
|
518
518
|
if (live !== void 0) return live;
|
|
@@ -520,7 +520,8 @@ function providerOnDisk(root) {
|
|
|
520
520
|
return started ?? PROVIDER_OF_A_NEW_INSTALLATION;
|
|
521
521
|
}
|
|
522
522
|
function holdsAnInstallation(root, provider) {
|
|
523
|
-
|
|
523
|
+
const dir = sourceDir(root, provider);
|
|
524
|
+
return holdsAConfig(dir) && declaresNothingAsLastRead(configIn(dir)) !== true;
|
|
524
525
|
}
|
|
525
526
|
function holdsASourceFolder(root, provider) {
|
|
526
527
|
return holdsAnything(sourceDir(root, provider));
|
|
@@ -676,7 +677,10 @@ function advisesOnPermissions(provider) {
|
|
|
676
677
|
}
|
|
677
678
|
}
|
|
678
679
|
function holdsAConfig(dir) {
|
|
679
|
-
return fs2.existsSync(
|
|
680
|
+
return fs2.existsSync(configIn(dir));
|
|
681
|
+
}
|
|
682
|
+
function configIn(dir) {
|
|
683
|
+
return path.join(dir, STANDARD_FILES.CONFIG_TS);
|
|
680
684
|
}
|
|
681
685
|
function holdsAnything(dir) {
|
|
682
686
|
return directoryExists(dir) && isNotEmpty(dir);
|
|
@@ -696,6 +700,29 @@ function directoryExists(dir) {
|
|
|
696
700
|
}
|
|
697
701
|
}
|
|
698
702
|
|
|
703
|
+
// src/cli/lib/metadata-keys.ts
|
|
704
|
+
var METADATA_KEYS = {
|
|
705
|
+
DISPLAY_NAME: "displayName",
|
|
706
|
+
CATEGORY: "category",
|
|
707
|
+
CUSTOM: "custom"
|
|
708
|
+
};
|
|
709
|
+
var LOCAL_DEFAULTS = {
|
|
710
|
+
AUTHOR: "@dummy-author"
|
|
711
|
+
};
|
|
712
|
+
var SKILL_CONTENT_FILES = [STANDARD_FILES.SKILL_MD, STANDARD_FILES.REFERENCE_MD];
|
|
713
|
+
var SKILL_CONTENT_DIRS = [STANDARD_DIRS.EXAMPLES, STANDARD_DIRS.SCRIPTS];
|
|
714
|
+
|
|
715
|
+
// src/cli/utils/errors.ts
|
|
716
|
+
function getErrorMessage(error) {
|
|
717
|
+
return stripTerminalControls(error instanceof Error ? error.message : String(error));
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// src/cli/utils/fs.ts
|
|
721
|
+
import fs3 from "fs-extra";
|
|
722
|
+
import fg from "fast-glob";
|
|
723
|
+
import os3 from "os";
|
|
724
|
+
import path2 from "path";
|
|
725
|
+
|
|
699
726
|
// src/cli/lib/config-gate/gate-token.ts
|
|
700
727
|
import { AsyncLocalStorage } from "async_hooks";
|
|
701
728
|
var gateToken = new AsyncLocalStorage();
|
|
@@ -876,6 +903,7 @@ function warnOnce(msg) {
|
|
|
876
903
|
|
|
877
904
|
// src/cli/utils/messages.ts
|
|
878
905
|
import path3 from "path";
|
|
906
|
+
import { partition } from "remeda";
|
|
879
907
|
|
|
880
908
|
// src/cli/lib/validate-kebab-name.ts
|
|
881
909
|
import { unique } from "remeda";
|
|
@@ -957,6 +985,16 @@ var INFO_MESSAGES = {
|
|
|
957
985
|
CONFIG_TYPES_REFRESHED: `Refreshed ${STANDARD_FILES.CONFIG_TYPES_TS}`,
|
|
958
986
|
EJECTED_SKILLS_USER_OWNED: `Ejected skills are yours to own \u2014 '${CLI_INVOKE_COMMAND} update' does not change them.`,
|
|
959
987
|
NO_PLUGIN_MARKETPLACES: "No plugin marketplaces are configured \u2014 nothing to refresh.",
|
|
988
|
+
/**
|
|
989
|
+
* The owner's words (2026-10-24), on stderr before the command's own output. The lines after it
|
|
990
|
+
* are each true of a run this one is not (owner, 2026-10-26: "A line worded for each").
|
|
991
|
+
*/
|
|
992
|
+
GLOBAL_INSTALLATION_STANDS_IN: "This project's config declares nothing, so this works on your global installation.",
|
|
993
|
+
NO_GLOBAL_INSTALLATION_STANDS_IN: "This project's config declares nothing, and there is no global installation for this to work on.",
|
|
994
|
+
DECLARES_NOTHING_TO_UNINSTALL: "This project's config declares nothing, so there is nothing to uninstall in this project.",
|
|
995
|
+
DECLARES_NOTHING_TO_EJECT_FROM: "This project's config declares nothing; eject works on this project, not on a global installation.",
|
|
996
|
+
DECLARES_NOTHING_TO_INIT_OVER: "This project's config declares nothing, so init treats this project as holding no installation.",
|
|
997
|
+
DECLARES_NOTHING_TO_SEARCH_FROM: "This project's config declares nothing; search reads the marketplace it names, else the global installation's, else the default one.",
|
|
960
998
|
/** Under doctor's marketplace row, when its download replaced a saved copy whose files differed. */
|
|
961
999
|
SAVED_COPY_REPLACED: "The saved copy differed from the marketplace and has been replaced",
|
|
962
1000
|
/** Printed under the destination already named, so it names files only. */
|
|
@@ -971,8 +1009,8 @@ function pluginsInstalled(count) {
|
|
|
971
1009
|
function localSkillsCopied(count) {
|
|
972
1010
|
return `Copied ${count} local skill(s)`;
|
|
973
1011
|
}
|
|
974
|
-
function recompileSummary(
|
|
975
|
-
return `${plural(
|
|
1012
|
+
function recompileSummary(rewritten2, unchanged, subject) {
|
|
1013
|
+
return `${plural(rewritten2, subject)} rewritten, ${unchanged} unchanged`;
|
|
976
1014
|
}
|
|
977
1015
|
function agentsNotCompiled(agentNames) {
|
|
978
1016
|
return `${agentNames.length} sub-agent(s) did not compile: ${agentNames.join(", ")}`;
|
|
@@ -995,21 +1033,18 @@ function propagatedRecompileSummary(recompiled, failed) {
|
|
|
995
1033
|
const failureSuffix = failed > 0 ? ` (${failed} failed)` : "";
|
|
996
1034
|
return `Registered projects: ${recompiled} recompiled${failureSuffix}`;
|
|
997
1035
|
}
|
|
998
|
-
function skillAssignedToNoAgent(skillId) {
|
|
999
|
-
return `Skill '${skillId}' is assigned to no sub-agent \u2014 nothing will load it.`;
|
|
1036
|
+
function skillAssignedToNoAgent(skillId, namedBy) {
|
|
1037
|
+
return `Skill '${skillName(skillId, namedBy)}' is assigned to no sub-agent \u2014 nothing will load it.`;
|
|
1000
1038
|
}
|
|
1001
|
-
function scopeBlockedStackAssignment(agentNames, skillId) {
|
|
1039
|
+
function scopeBlockedStackAssignment(agentNames, skillId, namedBy) {
|
|
1002
1040
|
const subject = agentNames.length === 1 ? "Sub-agent" : "Sub-agents";
|
|
1003
1041
|
const named = agentNames.map((name) => `'${name}'`).join(", ");
|
|
1004
|
-
return `${subject} ${named} cannot carry project-scoped skill '${skillId}' \u2014 global-scoped sub-agents only carry global-scoped skills.`;
|
|
1042
|
+
return `${subject} ${named} cannot carry project-scoped skill '${skillName(skillId, namedBy)}' \u2014 global-scoped sub-agents only carry global-scoped skills.`;
|
|
1005
1043
|
}
|
|
1006
1044
|
function globalScopedAgentsHint(count) {
|
|
1007
1045
|
const [subject, object] = count === 1 ? ["agent is", "it"] : ["agents are", "them"];
|
|
1008
1046
|
return `${count} ${subject} global-scoped \u2014 run '${CLI_INVOKE_COMMAND} compile' from your home directory to recompile ${object}.`;
|
|
1009
1047
|
}
|
|
1010
|
-
function marketplacesRefreshed(count) {
|
|
1011
|
-
return `Refreshed ${plural(count, "marketplace listing")}. Installed plugins were not changed.`;
|
|
1012
|
-
}
|
|
1013
1048
|
function marketplaceRefreshFailed(marketplace, reason) {
|
|
1014
1049
|
return `Could not update marketplace ${marketplace}: ${reason}`;
|
|
1015
1050
|
}
|
|
@@ -1022,6 +1057,13 @@ function sourceRefusedUsingCache(source, status) {
|
|
|
1022
1057
|
function marketplacesRefreshFailed(marketplaces) {
|
|
1023
1058
|
return `${plural(marketplaces.length, "marketplace")} could not be updated: ${marketplaces.join(", ")}`;
|
|
1024
1059
|
}
|
|
1060
|
+
function codexPluginLeftSwitchedOn(pluginKey, configFile, reason) {
|
|
1061
|
+
return `Codex switched plugin ${pluginKey} back on when it was updated, and it could not be switched off again in ${configFile}: ${reason}. To switch it off, set enabled = false in that file, in the table it already has for ${pluginKey}.`;
|
|
1062
|
+
}
|
|
1063
|
+
function pluginsLeftSwitchedOn(pluginKeys) {
|
|
1064
|
+
const are = pluginKeys.length === 1 ? "is" : "are";
|
|
1065
|
+
return `${plural(pluginKeys.length, "plugin")} you had switched off ${are} switched on again: ${pluginKeys.join(", ")}`;
|
|
1066
|
+
}
|
|
1025
1067
|
function marketplaceOwnerHasNoName(packageJsonPath) {
|
|
1026
1068
|
return [
|
|
1027
1069
|
`A marketplace's owner must have a name, and no name could be read from 'author' in ${packageJsonPath}.`,
|
|
@@ -1089,6 +1131,10 @@ function registeredProjectOnAnotherProvider(projectPath) {
|
|
|
1089
1131
|
function registeredProjectUpdateFailed(projectPath) {
|
|
1090
1132
|
return `Skipped ${projectPath}: updating it failed.`;
|
|
1091
1133
|
}
|
|
1134
|
+
function unregisteredSkillsSkipped(count, folder) {
|
|
1135
|
+
const are = count === 1 ? "isn't" : "aren't";
|
|
1136
|
+
return `Skipped ${plural(count, "skill")} in '${folder}' that ${are} registered with the CLI (no ${STANDARD_FILES.METADATA_YAML}).`;
|
|
1137
|
+
}
|
|
1092
1138
|
function skillMetadataUnusableDetail({
|
|
1093
1139
|
skillDirName,
|
|
1094
1140
|
metadataPath,
|
|
@@ -1137,26 +1183,26 @@ function applySharedConfigHere(id, installedHere, provider) {
|
|
|
1137
1183
|
const named = providerArgument(provider);
|
|
1138
1184
|
return id === void 0 ? `Then ${verb} what it gives you with '${command} <id>${named}'.` : `To ${verb} it here instead, run '${command} ${id}${named}'.`;
|
|
1139
1185
|
}
|
|
1140
|
-
function shareRefusesEjectedSkills(skillIds) {
|
|
1186
|
+
function shareRefusesEjectedSkills(skillIds, namedBy) {
|
|
1141
1187
|
return [
|
|
1142
1188
|
"This installation holds ejected skills, and ejected skills cannot be shared:",
|
|
1143
|
-
...skillIds.map((id) => ` ${id}`),
|
|
1189
|
+
...skillIds.map((id) => ` ${skillName(id, namedBy)}`),
|
|
1144
1190
|
"Only plugins can be shared."
|
|
1145
1191
|
].join("\n");
|
|
1146
1192
|
}
|
|
1147
1193
|
function sharedIdNamesAFolder(folder) {
|
|
1148
1194
|
return `This id names the marketplace folder ${folder}, so it installs only where that folder exists.`;
|
|
1149
1195
|
}
|
|
1150
|
-
function sharedSkillsLeftBehind(skillIds) {
|
|
1151
|
-
return `A shared id names one marketplace, and these skill(s) come from another, so they will not install elsewhere: ${skillIds
|
|
1196
|
+
function sharedSkillsLeftBehind(skillIds, namedBy) {
|
|
1197
|
+
return `A shared id names one marketplace, and these skill(s) come from another, so they will not install elsewhere: ${skillNames(skillIds, namedBy)}`;
|
|
1152
1198
|
}
|
|
1153
1199
|
function sharedConfigExistingInstall(configPath, id, provider) {
|
|
1154
1200
|
return `An installation already exists at ${configPath}. Run '${CLI_INVOKE_COMMAND} uninstall${providerArgument(provider)}' first \u2014 ${SHARED_CONFIG_GREENFIELD_HINT}. ${applySharedConfigHere(id, true, provider)}`;
|
|
1155
1201
|
}
|
|
1156
|
-
function sharedConfigProjectScopeAtHome(skillIds, agentNames, installedHere, provider) {
|
|
1202
|
+
function sharedConfigProjectScopeAtHome(skillIds, agentNames, installedHere, provider, namedBy) {
|
|
1157
1203
|
return [
|
|
1158
1204
|
"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:",
|
|
1159
|
-
...skillIds.map((id) => ` skill ${id} (scope: project)`),
|
|
1205
|
+
...skillIds.map((id) => ` skill ${skillName(id, namedBy)} (scope: project)`),
|
|
1160
1206
|
...agentNames.map((name) => ` sub-agent ${name} (scope: project)`),
|
|
1161
1207
|
installedHere ? changeTheGlobalInstallFromHome(providerArgument(provider)) : "Run this from inside a project directory, or re-share it with each entry above at global scope."
|
|
1162
1208
|
].join("\n");
|
|
@@ -1167,23 +1213,45 @@ function changeTheGlobalInstallFromHome(named) {
|
|
|
1167
1213
|
function unknownKeys(keys) {
|
|
1168
1214
|
return keys.map(inertLine).join(", ");
|
|
1169
1215
|
}
|
|
1170
|
-
|
|
1216
|
+
var SKIP_CAUSE = {
|
|
1217
|
+
UNLOADED: "could not load",
|
|
1218
|
+
UNKNOWN: "does not know"
|
|
1219
|
+
};
|
|
1220
|
+
function skippedSkills(skillIds, installedWithout, unloadedSkillIds) {
|
|
1221
|
+
const [unloaded, unknown] = partition(skillIds, (id) => unloadedSkillIds.has(id));
|
|
1171
1222
|
return [
|
|
1172
|
-
|
|
1173
|
-
...installedWithout
|
|
1174
|
-
({ skillId, agentNames }) => ` sub-agent(s) installed without ${unknownKeys([skillId])}, carrying nothing else this catalog knows: ${agentNames.join(", ")}`
|
|
1175
|
-
)
|
|
1223
|
+
...skippedFor(SKIP_CAUSE.UNLOADED, unloaded, installedWithout),
|
|
1224
|
+
...skippedFor(SKIP_CAUSE.UNKNOWN, unknown, installedWithout)
|
|
1176
1225
|
].join("\n");
|
|
1177
1226
|
}
|
|
1227
|
+
function skippedFor(cause, skillIds, installedWithout) {
|
|
1228
|
+
if (skillIds.length === 0) return [];
|
|
1229
|
+
return [
|
|
1230
|
+
`Skipped ${skillIds.length} skill(s) this catalog ${cause}: ${unknownKeys(skillIds)}`,
|
|
1231
|
+
...installedWithout.filter(({ skillId }) => skillIds.includes(skillId)).map(subAgentsInstalledWithout)
|
|
1232
|
+
];
|
|
1233
|
+
}
|
|
1234
|
+
function subAgentsInstalledWithout({ skillId, agentNames }) {
|
|
1235
|
+
return ` sub-agent(s) installed without ${unknownKeys([skillId])}, carrying nothing else this catalog knows: ${agentNames.join(", ")}`;
|
|
1236
|
+
}
|
|
1178
1237
|
function skippedUnknownAgents(agentNames) {
|
|
1179
1238
|
return `Skipped ${agentNames.length} unknown sub-agent(s): ${unknownKeys(agentNames)}`;
|
|
1180
1239
|
}
|
|
1181
|
-
function carriedSkillsWritten(skillIds) {
|
|
1182
|
-
return `Wrote ${skillIds.length} skill(s) this configuration carries: ${skillIds
|
|
1240
|
+
function carriedSkillsWritten(skillIds, namedBy) {
|
|
1241
|
+
return `Wrote ${skillIds.length} skill(s) this configuration carries: ${skillNames(skillIds, namedBy)}`;
|
|
1242
|
+
}
|
|
1243
|
+
function copiedSkillsRemovedAfterRefusedSave(count) {
|
|
1244
|
+
return `Removed the ${plural(count, "skill")} it copied, since the configuration could not be saved`;
|
|
1245
|
+
}
|
|
1246
|
+
function carriedSkillsRemovedAfterRefusedSave(count) {
|
|
1247
|
+
return `Removed the ${plural(count, "skill")} this configuration carried, since the configuration could not be saved`;
|
|
1183
1248
|
}
|
|
1184
1249
|
var SHARED_CONFIG_APPLY = {
|
|
1185
1250
|
CONFIRM: "Apply this configuration?"
|
|
1186
1251
|
};
|
|
1252
|
+
function idNamesAnotherMarketplace(named, used) {
|
|
1253
|
+
return `This id names the marketplace ${inertLine(named)}; its skills come from this installation's marketplace, ${used}.`;
|
|
1254
|
+
}
|
|
1187
1255
|
function registeredProjectsRecompiled(projectPaths) {
|
|
1188
1256
|
return [
|
|
1189
1257
|
"Each project registered with this global install is recompiled to match:",
|
|
@@ -1212,10 +1280,16 @@ function addMarketplaceInTheEditor(ref, isFolder) {
|
|
|
1212
1280
|
var SHARED_CONFIG_ARRIVALS = {
|
|
1213
1281
|
INSTALL_CONFIRM: "Install this configuration?"
|
|
1214
1282
|
};
|
|
1215
|
-
function
|
|
1283
|
+
function skillName(id, namedBy) {
|
|
1284
|
+
return getSkillEntryDisplayName(namedBy[id] ?? { id });
|
|
1285
|
+
}
|
|
1286
|
+
function skillNames(skillIds, namedBy) {
|
|
1287
|
+
return skillIds.map((id) => skillName(id, namedBy)).join(", ");
|
|
1288
|
+
}
|
|
1289
|
+
function keptAsInstalledGlobally(skillIds, agentNames, idAppliedAtHome, provider, namedBy) {
|
|
1216
1290
|
return [
|
|
1217
1291
|
"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:",
|
|
1218
|
-
...skillIds.map((id) => ` skill ${id}`),
|
|
1292
|
+
...skillIds.map((id) => ` skill ${skillName(id, namedBy)}`),
|
|
1219
1293
|
...agentNames.map((name) => ` sub-agent ${name}`),
|
|
1220
1294
|
...idAppliedAtHome === void 0 ? [] : [
|
|
1221
1295
|
`Change them with '${CLI_INVOKE_COMMAND} edit --from ${idAppliedAtHome}${providerArgument(provider)}' from your home directory.`
|
|
@@ -1223,16 +1297,12 @@ function keptAsInstalledGlobally(skillIds, agentNames, idAppliedAtHome, provider
|
|
|
1223
1297
|
].join("\n");
|
|
1224
1298
|
}
|
|
1225
1299
|
function skillsMoved(verb, skillIds, namedBy) {
|
|
1226
|
-
const names = skillIds.map((id) =>
|
|
1227
|
-
return names.length === 0 ? [] : [`${verb} ${
|
|
1300
|
+
const names = skillIds.map((id) => skillName(id, namedBy));
|
|
1301
|
+
return names.length === 0 ? [] : [`${verb} ${inAnEnglishList(names)}`];
|
|
1228
1302
|
}
|
|
1229
1303
|
function settingMoved({ setting, from, to }) {
|
|
1230
1304
|
return `${setting} ${from ?? "default"} \u2192 ${to ?? "default"}`;
|
|
1231
1305
|
}
|
|
1232
|
-
function listed(names) {
|
|
1233
|
-
if (names.length <= 1) return names.join("");
|
|
1234
|
-
return `${names.slice(0, -1).join(", ")} and ${names.at(-1)}`;
|
|
1235
|
-
}
|
|
1236
1306
|
var SKILLS_REASSIGNED = "skills reassigned";
|
|
1237
1307
|
function subAgentChanged({
|
|
1238
1308
|
name,
|
|
@@ -1250,29 +1320,29 @@ function subAgentChanged({
|
|
|
1250
1320
|
const where = scope === "global" ? " (global)" : "";
|
|
1251
1321
|
return `~ ${name}${where}: ${details.length > 0 ? details.join(", ") : SKILLS_REASSIGNED}`;
|
|
1252
1322
|
}
|
|
1253
|
-
function addedToGlobalSubAgents(joined) {
|
|
1323
|
+
function addedToGlobalSubAgents(joined, namedBy) {
|
|
1254
1324
|
return [
|
|
1255
1325
|
"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:",
|
|
1256
1326
|
...joined.map(
|
|
1257
|
-
({ agentName, skillIds }) => ` sub-agent ${agentName}: + ${skillIds
|
|
1327
|
+
({ agentName, skillIds }) => ` sub-agent ${agentName}: + ${skillNames(skillIds, namedBy)}`
|
|
1258
1328
|
)
|
|
1259
1329
|
].join("\n");
|
|
1260
1330
|
}
|
|
1261
|
-
function authoredHereKept(skillIds) {
|
|
1331
|
+
function authoredHereKept(skillIds, namedBy) {
|
|
1262
1332
|
return [
|
|
1263
1333
|
"Kept \u2014 written here rather than installed, so a shared configuration never carried them:",
|
|
1264
|
-
...skillIds.map((id) => ` skill ${id}`)
|
|
1334
|
+
...skillIds.map((id) => ` skill ${skillName(id, namedBy)}`)
|
|
1265
1335
|
].join("\n");
|
|
1266
1336
|
}
|
|
1267
|
-
function unplaceableKept(skillIds) {
|
|
1337
|
+
function unplaceableKept(skillIds, namedBy) {
|
|
1268
1338
|
return [
|
|
1269
1339
|
"Kept \u2014 named by this configuration, and this catalogue cannot place them:",
|
|
1270
|
-
...skillIds.map((id) => ` skill ${id}`),
|
|
1340
|
+
...skillIds.map((id) => ` skill ${skillName(id, namedBy)}`),
|
|
1271
1341
|
`Run '${CLI_INVOKE_COMMAND} update' to refresh this installation's marketplace, then apply the configuration again.`
|
|
1272
1342
|
].join("\n");
|
|
1273
1343
|
}
|
|
1274
|
-
function keptUnassigned(skillId, agentName, category) {
|
|
1275
|
-
return `${skillId}: kept, no longer assigned to ${agentName}'s ${category}`;
|
|
1344
|
+
function keptUnassigned({ skillId, agentName, category }, namedBy) {
|
|
1345
|
+
return `${skillName(skillId, namedBy)}: kept, no longer assigned to ${agentName}'s ${category}`;
|
|
1276
1346
|
}
|
|
1277
1347
|
function doctorReportsTheSame(count) {
|
|
1278
1348
|
return `'${CLI_INVOKE_COMMAND} doctor' reports the same ${count === 1 ? "file" : "files"}, alongside whatever else is wrong here.`;
|
|
@@ -1967,16 +2037,16 @@ import { z as z3 } from "zod";
|
|
|
1967
2037
|
|
|
1968
2038
|
// src/cli/lib/schemas.ts
|
|
1969
2039
|
import { z as z2 } from "zod";
|
|
1970
|
-
import { partition } from "remeda";
|
|
2040
|
+
import { partition as partition2 } from "remeda";
|
|
1971
2041
|
|
|
1972
2042
|
// src/cli/lib/schema-validator.ts
|
|
1973
2043
|
import "zod";
|
|
1974
2044
|
function formatZodIssue(issue) {
|
|
1975
|
-
const path30 = stripTerminalControls(issue.path.join("."));
|
|
1976
2045
|
if (issue.code === "unrecognized_keys") {
|
|
1977
|
-
return `Unrecognized key: "${
|
|
2046
|
+
return `Unrecognized key: "${inertLine(issue.keys.join('", "'))}"`;
|
|
1978
2047
|
}
|
|
1979
|
-
|
|
2048
|
+
const path29 = inertLine(issue.path.join("."));
|
|
2049
|
+
return path29 ? `${path29}: ${inertLine(issue.message)}` : inertLine(issue.message);
|
|
1980
2050
|
}
|
|
1981
2051
|
function formatZodErrors(error) {
|
|
1982
2052
|
return error.issues.map(formatZodIssue);
|
|
@@ -2118,12 +2188,12 @@ var RENAMED_SKILL_ENTRY_FIELDS = { source: "origin" };
|
|
|
2118
2188
|
function renamedFieldMessage(oldKey, newKey) {
|
|
2119
2189
|
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.`;
|
|
2120
2190
|
}
|
|
2121
|
-
function reportRenamedKeys(record, renames,
|
|
2191
|
+
function reportRenamedKeys(record, renames, path29, ctx) {
|
|
2122
2192
|
for (const [oldKey, newKey] of Object.entries(renames)) {
|
|
2123
2193
|
if (!(oldKey in record)) continue;
|
|
2124
2194
|
ctx.addIssue({
|
|
2125
2195
|
code: "custom",
|
|
2126
|
-
path: [...
|
|
2196
|
+
path: [...path29, oldKey],
|
|
2127
2197
|
message: renamedFieldMessage(oldKey, newKey)
|
|
2128
2198
|
});
|
|
2129
2199
|
}
|
|
@@ -2454,7 +2524,7 @@ function metadataSchemaFor(raw) {
|
|
|
2454
2524
|
return isCarriedMetadata(raw) ? carriedMetadataValidationSchema : customMetadataValidationSchema;
|
|
2455
2525
|
}
|
|
2456
2526
|
function describeMetadataSchemaFailure(issues, rawMetadata) {
|
|
2457
|
-
const [absent, malformed] =
|
|
2527
|
+
const [absent, malformed] = partition2(issues, (issue) => isAbsentField(issue, rawMetadata));
|
|
2458
2528
|
return [
|
|
2459
2529
|
...absent.length > 0 ? [nameMissingFields(absent)] : [],
|
|
2460
2530
|
...malformed.map(formatZodIssue)
|
|
@@ -2481,7 +2551,7 @@ function formatOverLengthWarning(issue, cliDescription) {
|
|
|
2481
2551
|
return `cliDescription is ${cliDescription.length} characters \u2014 exceeds the recommended maximum of ${CLI_DESCRIPTION_MAX_LENGTH}`;
|
|
2482
2552
|
}
|
|
2483
2553
|
function splitMetadataValidationIssues(error, rawMetadata) {
|
|
2484
|
-
const [overLength, hardFailures] =
|
|
2554
|
+
const [overLength, hardFailures] = partition2(error.issues, isOverLengthCliDescription);
|
|
2485
2555
|
const cliDescription = readCliDescription(rawMetadata);
|
|
2486
2556
|
const warnings = overLength.map((issue) => formatOverLengthWarning(issue, cliDescription));
|
|
2487
2557
|
return { errors: hardFailures.map(formatZodIssue), warnings };
|
|
@@ -2539,6 +2609,14 @@ var ConfigDefaultExportError = class extends Error {
|
|
|
2539
2609
|
}
|
|
2540
2610
|
configPath;
|
|
2541
2611
|
};
|
|
2612
|
+
var ConfigEvaluationError = class extends Error {
|
|
2613
|
+
constructor(configPath, reason, options) {
|
|
2614
|
+
super(configLoadFailure(configPath, reason), options);
|
|
2615
|
+
this.configPath = configPath;
|
|
2616
|
+
this.name = "ConfigEvaluationError";
|
|
2617
|
+
}
|
|
2618
|
+
configPath;
|
|
2619
|
+
};
|
|
2542
2620
|
var ES_MODULE_MARKER = "__esModule";
|
|
2543
2621
|
function unwrapDefaultExport(namespace) {
|
|
2544
2622
|
if (isRecord(namespace) && "default" in namespace) return namespace.default;
|
|
@@ -2584,7 +2662,7 @@ async function importConfigModule(configPath) {
|
|
|
2584
2662
|
});
|
|
2585
2663
|
return await jiti.import(configPath);
|
|
2586
2664
|
} catch (error) {
|
|
2587
|
-
throw new
|
|
2665
|
+
throw new ConfigEvaluationError(configPath, getErrorMessage(error), { cause: error });
|
|
2588
2666
|
}
|
|
2589
2667
|
}
|
|
2590
2668
|
|
|
@@ -2598,7 +2676,7 @@ function getInstalledConfigPath(dir) {
|
|
|
2598
2676
|
}
|
|
2599
2677
|
function resolveInstallPaths(projectDir, scope = "project") {
|
|
2600
2678
|
const baseDir = installBaseDir(projectDir, scope);
|
|
2601
|
-
const provider =
|
|
2679
|
+
const provider = providerAtScope(projectDir, scope);
|
|
2602
2680
|
return {
|
|
2603
2681
|
skillsDir: skillsDir(provider, scope, projectDir),
|
|
2604
2682
|
agentsDir: agentsDir(provider, scope, projectDir),
|
|
@@ -2620,7 +2698,7 @@ function initProviderToNameAt(root, provider) {
|
|
|
2620
2698
|
|
|
2621
2699
|
// src/cli/lib/configuration/config.ts
|
|
2622
2700
|
var SOURCE_ENV_VAR = "CC_MARKETPLACE";
|
|
2623
|
-
async function
|
|
2701
|
+
async function readStoredConfig(dir, scope, provider) {
|
|
2624
2702
|
const scopeLabel2 = scope === "project" ? "Project" : "Global";
|
|
2625
2703
|
const configPath = storedConfigPath(dir, provider);
|
|
2626
2704
|
if (!await fileExists(configPath)) {
|
|
@@ -2637,7 +2715,17 @@ async function loadSourceConfig(dir, scope, provider) {
|
|
|
2637
2715
|
if (installation === null) throw unreadable(NO_VALID_DEFAULT_EXPORT);
|
|
2638
2716
|
const settings = readSettings(configPath, installation);
|
|
2639
2717
|
verbose(`Loaded ${scope} config from ${dir}`);
|
|
2640
|
-
return
|
|
2718
|
+
return {
|
|
2719
|
+
settings,
|
|
2720
|
+
declaresNothing: declaresNoContent({
|
|
2721
|
+
skills: installation.skills ?? [],
|
|
2722
|
+
agents: installation.agents ?? []
|
|
2723
|
+
})
|
|
2724
|
+
};
|
|
2725
|
+
}
|
|
2726
|
+
async function loadSourceConfig(dir, scope, provider) {
|
|
2727
|
+
const stored = await readStoredConfig(dir, scope, provider);
|
|
2728
|
+
return stored === null ? null : stored.settings;
|
|
2641
2729
|
}
|
|
2642
2730
|
function refuseInstallationConfig(configPath, scope, scopeRoot, provider) {
|
|
2643
2731
|
const providerToName = providerToNameAt(scopeRoot, provider);
|
|
@@ -2688,17 +2776,29 @@ async function loadSourceRepoConfig(basePath) {
|
|
|
2688
2776
|
async function loadProjectSourceConfig(projectDir) {
|
|
2689
2777
|
return loadSourceConfig(projectDir, isHomeDirectory(projectDir) ? "global" : "project");
|
|
2690
2778
|
}
|
|
2691
|
-
async function loadGlobalSourceConfig() {
|
|
2692
|
-
return loadSourceConfig(os4.homedir(), "global");
|
|
2779
|
+
async function loadGlobalSourceConfig(provider) {
|
|
2780
|
+
return loadSourceConfig(os4.homedir(), "global", provider);
|
|
2693
2781
|
}
|
|
2694
2782
|
async function loadEffectiveSourceConfig(projectDir, provider) {
|
|
2695
2783
|
const ownDir = ownProjectDir(projectDir);
|
|
2696
2784
|
const own = ownDir === null ? null : await readStoredRung(ownDir, "project", provider);
|
|
2697
|
-
|
|
2785
|
+
if (own !== null && !readsAsNoConfig(own)) return own;
|
|
2786
|
+
return readStoredRung(os4.homedir(), "global", provider ?? inheritedBy(projectDir));
|
|
2787
|
+
}
|
|
2788
|
+
function readsAsNoConfig(own) {
|
|
2789
|
+
return own.declaresNothing && own.config.marketplace === void 0;
|
|
2790
|
+
}
|
|
2791
|
+
function inheritedBy(projectDir) {
|
|
2792
|
+
return inheritedGlobalProvider(projectDir ?? os4.homedir());
|
|
2698
2793
|
}
|
|
2699
2794
|
async function readStoredRung(dir, origin, provider) {
|
|
2700
|
-
const
|
|
2701
|
-
return
|
|
2795
|
+
const stored = await readStoredConfig(dir, origin, provider);
|
|
2796
|
+
return stored && {
|
|
2797
|
+
config: stored.settings,
|
|
2798
|
+
declaresNothing: stored.declaresNothing,
|
|
2799
|
+
origin,
|
|
2800
|
+
configPath: storedConfigPath(dir, provider)
|
|
2801
|
+
};
|
|
2702
2802
|
}
|
|
2703
2803
|
function storedConfigPath(dir, provider) {
|
|
2704
2804
|
return provider === void 0 ? getInstalledConfigPath(dir) : getProjectConfigPath(dir, provider);
|
|
@@ -2732,7 +2832,7 @@ async function resolveSource(request) {
|
|
|
2732
2832
|
if (envSource !== void 0) {
|
|
2733
2833
|
return { source: envSource, sourceOrigin: "env", ...labelFor(envSource) };
|
|
2734
2834
|
}
|
|
2735
|
-
if (effective?.config.marketplace) {
|
|
2835
|
+
if (caller === "stored" && effective?.config.marketplace) {
|
|
2736
2836
|
assertStoredSourceUsable(effective.config.marketplace, effective.configPath);
|
|
2737
2837
|
verbose(`Marketplace from ${effective.origin} config: ${effective.config.marketplace}`);
|
|
2738
2838
|
return {
|
|
@@ -2793,7 +2893,7 @@ ${getErrorMessage(error)}`
|
|
|
2793
2893
|
async function resolveBranding(projectDir) {
|
|
2794
2894
|
const [own, global] = await Promise.all([
|
|
2795
2895
|
loadOwnProjectSourceConfig(projectDir),
|
|
2796
|
-
loadGlobalSourceConfig()
|
|
2896
|
+
loadGlobalSourceConfig(inheritedBy(projectDir))
|
|
2797
2897
|
]);
|
|
2798
2898
|
return {
|
|
2799
2899
|
name: own?.branding?.name ?? global?.branding?.name ?? DEFAULT_BRANDING.NAME
|
|
@@ -3139,7 +3239,7 @@ seatDiagnostics({
|
|
|
3139
3239
|
});
|
|
3140
3240
|
|
|
3141
3241
|
// ../compile/src/seed-to-config.ts
|
|
3142
|
-
import { groupBy, indexBy, isDefined, mapValues, omit as omit2, partition as
|
|
3242
|
+
import { groupBy, indexBy, isDefined, mapValues, omit as omit2, partition as partition3 } from "remeda";
|
|
3143
3243
|
|
|
3144
3244
|
// ../compile/src/config-source.ts
|
|
3145
3245
|
import { omit } from "remeda";
|
|
@@ -3171,9 +3271,6 @@ function valueEntries(values) {
|
|
|
3171
3271
|
function commentEntry(text) {
|
|
3172
3272
|
return { kind: "comment", text };
|
|
3173
3273
|
}
|
|
3174
|
-
function sourceEntry(source) {
|
|
3175
|
-
return { kind: "source", source };
|
|
3176
|
-
}
|
|
3177
3274
|
function renderValueLine(value, position) {
|
|
3178
3275
|
return line(
|
|
3179
3276
|
renderValue(value, position),
|
|
@@ -3254,8 +3351,6 @@ function renderEntryLine(entry, indent) {
|
|
|
3254
3351
|
switch (entry.kind) {
|
|
3255
3352
|
case "comment":
|
|
3256
3353
|
return `${spaces(indent)}// ${entry.text}`;
|
|
3257
|
-
case "source":
|
|
3258
|
-
return `${spaces(indent)}${entry.source},`;
|
|
3259
3354
|
case "value":
|
|
3260
3355
|
return renderValueLine(entry.value, { indent, prefix: "", suffix: "," });
|
|
3261
3356
|
default: {
|
|
@@ -3293,8 +3388,6 @@ function flatEntry(entry) {
|
|
|
3293
3388
|
// A `//` line cannot fold onto one line.
|
|
3294
3389
|
case "comment":
|
|
3295
3390
|
return void 0;
|
|
3296
|
-
case "source":
|
|
3297
|
-
return entry.source;
|
|
3298
3391
|
case "value":
|
|
3299
3392
|
return flatValue(entry.value);
|
|
3300
3393
|
default: {
|
|
@@ -3479,62 +3572,16 @@ function generateConfigSource(config, catalog, options) {
|
|
|
3479
3572
|
}
|
|
3480
3573
|
function generateProjectConfig(config, catalog, options) {
|
|
3481
3574
|
const cleaned = cleanForEmission(config, catalog, { dropProjects: true });
|
|
3482
|
-
if (options.globalConfig) {
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
cleanForEmission(options.globalConfig, catalog, { dropProjects: true })
|
|
3486
|
-
);
|
|
3487
|
-
}
|
|
3488
|
-
if (options.globalImportPath !== void 0) {
|
|
3489
|
-
return generateProjectConfigWithGlobalImport(
|
|
3490
|
-
cleaned,
|
|
3491
|
-
options.globalImportPath
|
|
3575
|
+
if (!options.globalConfig) {
|
|
3576
|
+
throw new Error(
|
|
3577
|
+
"A project config needs the global config it extends: pass `globalConfig` to inline its entries."
|
|
3492
3578
|
);
|
|
3493
3579
|
}
|
|
3494
|
-
|
|
3495
|
-
|
|
3580
|
+
return generateProjectConfigWithInlinedGlobal(
|
|
3581
|
+
cleaned,
|
|
3582
|
+
cleanForEmission(options.globalConfig, catalog, { dropProjects: true })
|
|
3496
3583
|
);
|
|
3497
3584
|
}
|
|
3498
|
-
function generateProjectConfigWithGlobalImport(cleaned, globalImportPath) {
|
|
3499
|
-
const { skills, agents, stack, selectedDomains } = extractConfigArrays(cleaned);
|
|
3500
|
-
const emitted = {
|
|
3501
|
-
hasSkills: true,
|
|
3502
|
-
hasAgents: true,
|
|
3503
|
-
hasStack: hasStackEntries(stack),
|
|
3504
|
-
hasSelectedDomains: selectedDomains.length > 0
|
|
3505
|
-
};
|
|
3506
|
-
const scalarFields = Object.entries(cleaned).filter(([key]) => isCopiedScalarField(key)).map(([key, value]) => renderScalarField(key, value));
|
|
3507
|
-
return assembleConfigSource({
|
|
3508
|
-
imports: [
|
|
3509
|
-
`import globalConfig from ${quoteText(`${globalImportPath}/config`)}`,
|
|
3510
|
-
renderTypeImportLine(buildTypeImports(emitted), CONFIG_TYPES_SPECIFIER)
|
|
3511
|
-
],
|
|
3512
|
-
declarations: [
|
|
3513
|
-
...entryDeclaration("skills", "SkillConfig[]", [
|
|
3514
|
-
sourceEntry("...globalConfig.skills"),
|
|
3515
|
-
...valueEntries(skills)
|
|
3516
|
-
]),
|
|
3517
|
-
...entryDeclaration("agents", "AgentScopeConfig[]", [
|
|
3518
|
-
sourceEntry("...globalConfig.agents"),
|
|
3519
|
-
...valueEntries(agents)
|
|
3520
|
-
]),
|
|
3521
|
-
...emitted.hasStack ? declaration("stack", STACK_TYPE, stack) : [],
|
|
3522
|
-
...emitted.hasSelectedDomains ? entryDeclaration("selectedDomains", "Domain[]", [
|
|
3523
|
-
sourceEntry("...(globalConfig.selectedDomains ?? [])"),
|
|
3524
|
-
...valueEntries(selectedDomains)
|
|
3525
|
-
]) : []
|
|
3526
|
-
],
|
|
3527
|
-
exportFields: [
|
|
3528
|
-
` ...globalConfig,`,
|
|
3529
|
-
renderScalarField("name", resolveProjectName(cleaned)),
|
|
3530
|
-
` skills,`,
|
|
3531
|
-
` agents,`,
|
|
3532
|
-
...emitted.hasStack ? [` stack,`] : [],
|
|
3533
|
-
...emitted.hasSelectedDomains ? [` selectedDomains,`] : [],
|
|
3534
|
-
...scalarFields
|
|
3535
|
-
]
|
|
3536
|
-
});
|
|
3537
|
-
}
|
|
3538
3585
|
function generateStandaloneConfig(cleaned) {
|
|
3539
3586
|
const { skills, agents, stack, selectedDomains } = extractConfigArrays(cleaned);
|
|
3540
3587
|
const emitted = {
|
|
@@ -3650,18 +3697,25 @@ function stackOfAgents(stack, agents) {
|
|
|
3650
3697
|
function mergeInlinedScalarFields(cleaned, cleanedGlobal) {
|
|
3651
3698
|
return Object.entries(
|
|
3652
3699
|
canonicalizeFieldOrder({
|
|
3653
|
-
...
|
|
3700
|
+
...globalScalarsLentTo(cleaned, cleanedGlobal),
|
|
3654
3701
|
...cleaned
|
|
3655
3702
|
})
|
|
3656
3703
|
).filter(([key]) => isCopiedScalarField(key));
|
|
3657
3704
|
}
|
|
3705
|
+
function globalScalarsLentTo(cleaned, cleanedGlobal) {
|
|
3706
|
+
const lent = omit(cleanedGlobal, GLOBAL_IDENTITY_FIELDS);
|
|
3707
|
+
return recordsAnotherMarketplace(cleaned, cleanedGlobal) ? omit(lent, ["marketplaceName"]) : lent;
|
|
3708
|
+
}
|
|
3709
|
+
function recordsAnotherMarketplace(cleaned, cleanedGlobal) {
|
|
3710
|
+
return cleaned.marketplace !== void 0 && cleaned.marketplace !== cleanedGlobal.marketplace;
|
|
3711
|
+
}
|
|
3658
3712
|
function generateProjectConfigWithInlinedGlobal(cleaned, cleanedGlobal) {
|
|
3659
|
-
const
|
|
3713
|
+
const partition7 = partitionInlinedConfigEntries(cleaned, cleanedGlobal);
|
|
3660
3714
|
const emitted = {
|
|
3661
|
-
hasSkills:
|
|
3662
|
-
hasAgents:
|
|
3663
|
-
hasStack: hasStackEntries(
|
|
3664
|
-
hasSelectedDomains:
|
|
3715
|
+
hasSkills: partition7.globalSkills.length > 0 || partition7.projectSkills.length > 0,
|
|
3716
|
+
hasAgents: partition7.globalAgents.length > 0 || partition7.projectAgents.length > 0,
|
|
3717
|
+
hasStack: hasStackEntries(partition7.filteredStack),
|
|
3718
|
+
hasSelectedDomains: partition7.selectedDomains.length > 0
|
|
3665
3719
|
};
|
|
3666
3720
|
const scalarFields = mergeInlinedScalarFields(cleaned, cleanedGlobal);
|
|
3667
3721
|
return assembleConfigSource({
|
|
@@ -3672,15 +3726,15 @@ function generateProjectConfigWithInlinedGlobal(cleaned, cleanedGlobal) {
|
|
|
3672
3726
|
...emitted.hasSkills ? entryDeclaration(
|
|
3673
3727
|
"skills",
|
|
3674
3728
|
"SkillConfig[]",
|
|
3675
|
-
scopeSectionEntries(
|
|
3729
|
+
scopeSectionEntries(partition7.globalSkills, partition7.projectSkills)
|
|
3676
3730
|
) : [],
|
|
3677
3731
|
...emitted.hasAgents ? entryDeclaration(
|
|
3678
3732
|
"agents",
|
|
3679
3733
|
"AgentScopeConfig[]",
|
|
3680
|
-
scopeSectionEntries(
|
|
3734
|
+
scopeSectionEntries(partition7.globalAgents, partition7.projectAgents)
|
|
3681
3735
|
) : [],
|
|
3682
|
-
...emitted.hasStack ? declaration("stack", STACK_TYPE,
|
|
3683
|
-
...emitted.hasSelectedDomains ? declaration("selectedDomains", "Domain[]",
|
|
3736
|
+
...emitted.hasStack ? declaration("stack", STACK_TYPE, partition7.filteredStack) : [],
|
|
3737
|
+
...emitted.hasSelectedDomains ? declaration("selectedDomains", "Domain[]", partition7.selectedDomains) : []
|
|
3684
3738
|
],
|
|
3685
3739
|
exportFields: [
|
|
3686
3740
|
renderScalarField("name", resolveProjectName(cleaned)),
|
|
@@ -3835,7 +3889,7 @@ function assertScopeConfigsGiven(skillConfigs, agentConfigs) {
|
|
|
3835
3889
|
}
|
|
3836
3890
|
}
|
|
3837
3891
|
function resolveValidSkills(selectedSkillIds, skillConfigs, catalog) {
|
|
3838
|
-
const [found, absent] =
|
|
3892
|
+
const [found, absent] = partition3(
|
|
3839
3893
|
selectedSkillIds.map((skillId) => ({
|
|
3840
3894
|
skillId,
|
|
3841
3895
|
skill: catalog.skills[skillId]
|
|
@@ -3885,7 +3939,7 @@ function skillIdsByCategory(skills) {
|
|
|
3885
3939
|
);
|
|
3886
3940
|
}
|
|
3887
3941
|
function buildSkillScopeMap(skillConfigs) {
|
|
3888
|
-
const [excludedConfigs, activeConfigs] =
|
|
3942
|
+
const [excludedConfigs, activeConfigs] = partition3(
|
|
3889
3943
|
skillConfigs,
|
|
3890
3944
|
(s) => Boolean(s.excluded)
|
|
3891
3945
|
);
|
|
@@ -4030,11 +4084,11 @@ function mapCategoriesDroppingEmpty(agentStack, transform) {
|
|
|
4030
4084
|
);
|
|
4031
4085
|
}
|
|
4032
4086
|
function splitConfigByScope(config) {
|
|
4033
|
-
const [globalSkills, projectSkills] =
|
|
4087
|
+
const [globalSkills, projectSkills] = partition3(
|
|
4034
4088
|
config.skills,
|
|
4035
4089
|
(s) => isActiveAt(s, "global")
|
|
4036
4090
|
);
|
|
4037
|
-
const [globalAgents, projectAgents] =
|
|
4091
|
+
const [globalAgents, projectAgents] = partition3(
|
|
4038
4092
|
config.agents,
|
|
4039
4093
|
(a) => isActiveAt(a, "global")
|
|
4040
4094
|
);
|
|
@@ -4094,9 +4148,9 @@ function splitAgentStack(agentStack, globalSkillIds) {
|
|
|
4094
4148
|
}
|
|
4095
4149
|
function seedToInstallSelection(payload, catalog) {
|
|
4096
4150
|
const agentMap = readAgentMap(payload.agents);
|
|
4151
|
+
refuseUnwritableAssignments(payload.skills, agentMap);
|
|
4097
4152
|
const { installable, skipped } = resolveSeedSkills(payload.skills, catalog);
|
|
4098
4153
|
const rows = installable.flatMap((skill) => assignmentRows(skill, agentMap));
|
|
4099
|
-
refuseUnwritableAssignments(rows);
|
|
4100
4154
|
const placed = rows.filter((row) => row.kind === "placed");
|
|
4101
4155
|
const carried = new Set(agentsCarriedIn(placed, agentMap));
|
|
4102
4156
|
const selectedAgents = withAgentsSkippedSkillsName(carried, skipped, agentMap);
|
|
@@ -4130,12 +4184,23 @@ function seedToInstallSelection(payload, catalog) {
|
|
|
4130
4184
|
)
|
|
4131
4185
|
};
|
|
4132
4186
|
}
|
|
4133
|
-
function refuseUnwritableAssignments(
|
|
4134
|
-
const unwritable =
|
|
4187
|
+
function refuseUnwritableAssignments(seedSkills, agentMap) {
|
|
4188
|
+
const unwritable = unwritableAssignmentsOf(seedSkills, agentMap);
|
|
4135
4189
|
if (unwritable.length > 0) {
|
|
4136
4190
|
throw new Error(unwritableAssignmentsError(unwritable));
|
|
4137
4191
|
}
|
|
4138
4192
|
}
|
|
4193
|
+
function unwritableAssignmentsOf(seedSkills, agentMap) {
|
|
4194
|
+
return Object.entries(seedSkills).flatMap(
|
|
4195
|
+
([skillId, seedSkill]) => agentsNamedBy(seedSkill, agentMap).filter((agent) => !isWritablePair(seedSkill, agent, agentMap)).map((agent) => ({ skillId, agent }))
|
|
4196
|
+
);
|
|
4197
|
+
}
|
|
4198
|
+
function isWritablePair(seedSkill, agent, agentMap) {
|
|
4199
|
+
return isScopePairCompatible(
|
|
4200
|
+
seedSkill.scope,
|
|
4201
|
+
seedAgentScope(agentMap.known.get(agent))
|
|
4202
|
+
);
|
|
4203
|
+
}
|
|
4139
4204
|
function agentsCarriedIn(placed, agentMap) {
|
|
4140
4205
|
return [...placed.map((row) => row.agent), ...agentsSwitchedOn(agentMap)];
|
|
4141
4206
|
}
|
|
@@ -4143,7 +4208,7 @@ function withAgentsSkippedSkillsName(carried, skipped, agentMap) {
|
|
|
4143
4208
|
return [
|
|
4144
4209
|
.../* @__PURE__ */ new Set([
|
|
4145
4210
|
...carried,
|
|
4146
|
-
...skipped.flatMap((skill) => agentsNamedBy(skill, agentMap))
|
|
4211
|
+
...skipped.flatMap((skill) => agentsNamedBy(skill.seedSkill, agentMap))
|
|
4147
4212
|
])
|
|
4148
4213
|
];
|
|
4149
4214
|
}
|
|
@@ -4151,25 +4216,25 @@ function skippedAgentNamesOf(agentMap, rows) {
|
|
|
4151
4216
|
const unknownAssignees = rows.filter((row) => row.kind === "unknownAgent").map((row) => row.agentName);
|
|
4152
4217
|
return [.../* @__PURE__ */ new Set([...agentMap.unknown, ...unknownAssignees])];
|
|
4153
4218
|
}
|
|
4154
|
-
function agentsNamedBy(
|
|
4219
|
+
function agentsNamedBy(seedSkill, agentMap) {
|
|
4155
4220
|
return Object.keys(seedSkill.assignments).filter(isAgentName2).filter((name) => !agentMap.switchedOff.has(name));
|
|
4156
4221
|
}
|
|
4157
4222
|
function agentsOnlyItCarried(skill, agentMap, carried) {
|
|
4158
|
-
const agentNames = agentsNamedBy(skill, agentMap).filter(
|
|
4223
|
+
const agentNames = agentsNamedBy(skill.seedSkill, agentMap).filter(
|
|
4159
4224
|
(name) => !carried.has(name)
|
|
4160
4225
|
);
|
|
4161
4226
|
return agentNames.length === 0 ? [] : [{ skillId: skill.skillId, agentNames }];
|
|
4162
4227
|
}
|
|
4163
4228
|
function readAgentMap(agents) {
|
|
4164
4229
|
const known = /* @__PURE__ */ new Map();
|
|
4165
|
-
const
|
|
4230
|
+
const switchedOff2 = /* @__PURE__ */ new Set();
|
|
4166
4231
|
const unknown = [];
|
|
4167
4232
|
for (const [name, entry] of Object.entries(agents)) {
|
|
4168
4233
|
if (!isAgentName2(name)) unknown.push(name);
|
|
4169
|
-
else if (entry.on === false)
|
|
4234
|
+
else if (entry.on === false) switchedOff2.add(name);
|
|
4170
4235
|
else known.set(name, entry);
|
|
4171
4236
|
}
|
|
4172
|
-
return { known, switchedOff, unknown };
|
|
4237
|
+
return { known, switchedOff: switchedOff2, unknown };
|
|
4173
4238
|
}
|
|
4174
4239
|
function agentsSwitchedOn(agentMap) {
|
|
4175
4240
|
return [...agentMap.known].filter(([, entry]) => entry.on === true).map(([name]) => name);
|
|
@@ -4219,10 +4284,6 @@ function assignmentRows(skill, agentMap) {
|
|
|
4219
4284
|
function assignmentRow(skill, agentName, load, agentMap) {
|
|
4220
4285
|
if (agentMap.switchedOff.has(agentName)) return void 0;
|
|
4221
4286
|
if (!isAgentName2(agentName)) return { kind: "unknownAgent", agentName };
|
|
4222
|
-
const agentScope = seedAgentScope(agentMap.known.get(agentName));
|
|
4223
|
-
if (!isScopePairCompatible(skill.seedSkill.scope, agentScope)) {
|
|
4224
|
-
return { kind: "unwritable", skillId: skill.id, agent: agentName };
|
|
4225
|
-
}
|
|
4226
4287
|
return {
|
|
4227
4288
|
kind: "placed",
|
|
4228
4289
|
agent: agentName,
|
|
@@ -4281,7 +4342,7 @@ import path8 from "path";
|
|
|
4281
4342
|
|
|
4282
4343
|
// src/cli/lib/stacks/stacks-loader.ts
|
|
4283
4344
|
import path7 from "path";
|
|
4284
|
-
import { flatMap, groupBy as groupBy2, mapValues as mapValues2, partition as
|
|
4345
|
+
import { flatMap, groupBy as groupBy2, mapValues as mapValues2, partition as partition4, pipe, unique as unique2, uniqueBy } from "remeda";
|
|
4285
4346
|
var stacksCache = /* @__PURE__ */ new Map();
|
|
4286
4347
|
function normalizeAgentConfig(agentConfig) {
|
|
4287
4348
|
return mapValues2(agentConfig, (value) => {
|
|
@@ -4312,7 +4373,7 @@ function placeAssignments(agentConfig) {
|
|
|
4312
4373
|
);
|
|
4313
4374
|
}
|
|
4314
4375
|
function rekeyToLiveCategories(agentConfig) {
|
|
4315
|
-
const [settled, moved] =
|
|
4376
|
+
const [settled, moved] = partition4(
|
|
4316
4377
|
placeAssignments(agentConfig),
|
|
4317
4378
|
(placed) => placed.liveCategory === placed.storedCategory
|
|
4318
4379
|
);
|
|
@@ -4335,6 +4396,12 @@ function normalizeStackRecord(rawStack) {
|
|
|
4335
4396
|
(agentConfig) => rekeyToLiveCategories(normalizeAgentConfig(agentConfig))
|
|
4336
4397
|
);
|
|
4337
4398
|
}
|
|
4399
|
+
var StacksLoadError = class extends Error {
|
|
4400
|
+
constructor(stacksPath, reason, options) {
|
|
4401
|
+
super(`Failed to load stacks from '${stacksPath}': ${reason}`, options);
|
|
4402
|
+
this.name = "StacksLoadError";
|
|
4403
|
+
}
|
|
4404
|
+
};
|
|
4338
4405
|
async function loadStacks(configDir, stacksFile) {
|
|
4339
4406
|
const resolvedStacksFile = stacksFile ?? STACKS_FILE_PATH;
|
|
4340
4407
|
const cacheKey = `${configDir}:${resolvedStacksFile}`;
|
|
@@ -4358,14 +4425,12 @@ async function loadStacks(configDir, stacksFile) {
|
|
|
4358
4425
|
verbose(`Loaded ${stacks.length} stacks from ${stacksPath}`);
|
|
4359
4426
|
return stacks;
|
|
4360
4427
|
} catch (error) {
|
|
4361
|
-
|
|
4362
|
-
throw new Error(`Failed to load stacks from '${stacksPath}': ${errorMessage}`, {
|
|
4363
|
-
cause: error
|
|
4364
|
-
});
|
|
4428
|
+
throw new StacksLoadError(stacksPath, getErrorMessage(error), { cause: error });
|
|
4365
4429
|
}
|
|
4366
4430
|
}
|
|
4367
4431
|
async function loadStackById(stackId, configDir, source) {
|
|
4368
|
-
const
|
|
4432
|
+
const declared = await loadSourceRepoConfig(configDir);
|
|
4433
|
+
const stacks = await loadStacks(configDir, declared?.stacksFile);
|
|
4369
4434
|
const stack = stacks.find((s) => s.id === stackId);
|
|
4370
4435
|
if (stack) {
|
|
4371
4436
|
verbose(`Found stack: ${stack.name} (${stackId})`);
|
|
@@ -4385,8 +4450,15 @@ async function loadStackById(stackId, configDir, source) {
|
|
|
4385
4450
|
verbose(`Found default stack: ${defaultStack.name} (${stackId})`);
|
|
4386
4451
|
return defaultStack;
|
|
4387
4452
|
}
|
|
4388
|
-
function stackNotOfferedMessage(stackId, source) {
|
|
4389
|
-
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
|
|
4453
|
+
function stackNotOfferedMessage(stackId, source, shippedStackIds) {
|
|
4454
|
+
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. ${whatItShips(shippedStackIds)}`;
|
|
4455
|
+
}
|
|
4456
|
+
function whatItShips(stackIds) {
|
|
4457
|
+
if (stackIds.length === 0) return "It ships no stacks.";
|
|
4458
|
+
return withClosingFullStop(`It ships: ${inAnEnglishList(stackIds.map(inertLine))}`);
|
|
4459
|
+
}
|
|
4460
|
+
function withClosingFullStop(sentence) {
|
|
4461
|
+
return sentence.endsWith(".") ? sentence : `${sentence}.`;
|
|
4390
4462
|
}
|
|
4391
4463
|
function defaultUsageGuidance(category) {
|
|
4392
4464
|
return `Use when working with ${category}.`;
|
|
@@ -4437,6 +4509,11 @@ var ConfigLoadError = class extends Error {
|
|
|
4437
4509
|
/** Named in the remedy's `init --from` half, which a bare run would answer with another one. */
|
|
4438
4510
|
initProviderToName;
|
|
4439
4511
|
};
|
|
4512
|
+
async function configDeclaresNothing(projectDir, provider) {
|
|
4513
|
+
const configPath = getProjectConfigPath(projectDir, provider);
|
|
4514
|
+
const parsed = await parseConfigFile(configPath, refusalAt(configPath, projectDir, provider));
|
|
4515
|
+
return declaresNoContent({ skills: parsed.skills ?? [], agents: parsed.agents ?? [] });
|
|
4516
|
+
}
|
|
4440
4517
|
async function loadProjectConfigFromDir(projectDir, provider) {
|
|
4441
4518
|
return loadProjectConfigAt(getProjectConfigPath(projectDir, provider), projectDir, provider);
|
|
4442
4519
|
}
|
|
@@ -4447,8 +4524,7 @@ async function loadProjectConfigAt(configPath, projectDir, provider) {
|
|
|
4447
4524
|
verbose(`${scopeLabel2} config not found at ${configPath}`);
|
|
4448
4525
|
return null;
|
|
4449
4526
|
}
|
|
4450
|
-
const
|
|
4451
|
-
const parsed = await parseConfigFile(configPath, refuse);
|
|
4527
|
+
const parsed = await parseConfigFile(configPath, refusalAt(configPath, projectDir, provider));
|
|
4452
4528
|
const config = asProjectConfig(parsed, projectDir);
|
|
4453
4529
|
warnOfDefaultedFields(parsed, configPath);
|
|
4454
4530
|
verbose(`Loaded ${scope} config from ${configPath}`);
|
|
@@ -4458,6 +4534,9 @@ async function loadProjectConfigAt(configPath, projectDir, provider) {
|
|
|
4458
4534
|
provider
|
|
4459
4535
|
};
|
|
4460
4536
|
}
|
|
4537
|
+
function refusalAt(configPath, projectDir, provider) {
|
|
4538
|
+
return (reason) => new ConfigLoadError(configPath, reason, projectDir, provider);
|
|
4539
|
+
}
|
|
4461
4540
|
async function parseConfigFile(configPath, refuse) {
|
|
4462
4541
|
let raw;
|
|
4463
4542
|
try {
|
|
@@ -4493,29 +4572,39 @@ function warnOfDefaultedFields(parsed, configPath) {
|
|
|
4493
4572
|
async function loadInstalledConfig(dir) {
|
|
4494
4573
|
return loadProjectConfigFromDir(dir, providerInUse(dir));
|
|
4495
4574
|
}
|
|
4496
|
-
function
|
|
4497
|
-
|
|
4575
|
+
function configsInPlay(projectDir) {
|
|
4576
|
+
const own = { dir: projectDir, provider: providerInUse(projectDir) };
|
|
4577
|
+
if (isHomeDirectory(projectDir)) return [own];
|
|
4578
|
+
return [own, { dir: os5.homedir(), provider: inheritedGlobalProvider(projectDir) }];
|
|
4498
4579
|
}
|
|
4499
4580
|
async function findConfigLoadFailures(projectDir) {
|
|
4500
|
-
const outcomes = await Promise.all(
|
|
4581
|
+
const outcomes = await Promise.all(configsInPlay(projectDir).map(configLoadFailure2));
|
|
4501
4582
|
return outcomes.filter((failure) => failure !== void 0);
|
|
4502
4583
|
}
|
|
4503
|
-
async function configLoadFailure2(
|
|
4584
|
+
async function configLoadFailure2({
|
|
4585
|
+
dir,
|
|
4586
|
+
provider
|
|
4587
|
+
}) {
|
|
4504
4588
|
try {
|
|
4505
|
-
await
|
|
4589
|
+
await loadProjectConfigFromDir(dir, provider);
|
|
4506
4590
|
return void 0;
|
|
4507
4591
|
} catch (error) {
|
|
4508
4592
|
if (error instanceof ConfigLoadError) return error;
|
|
4509
4593
|
throw error;
|
|
4510
4594
|
}
|
|
4511
4595
|
}
|
|
4512
|
-
async function loadProjectConfig(projectDir) {
|
|
4513
|
-
const provider = providerInUse(projectDir);
|
|
4596
|
+
async function loadProjectConfig(projectDir, provider = providerInUse(projectDir)) {
|
|
4514
4597
|
const projectResult = await loadProjectConfigFromDir(projectDir, provider);
|
|
4515
4598
|
if (projectResult) return projectResult;
|
|
4516
4599
|
if (isHomeDirectory(projectDir)) return null;
|
|
4517
4600
|
return loadProjectConfigFromDir(os5.homedir(), provider);
|
|
4518
4601
|
}
|
|
4602
|
+
async function loadConfigInPlay(projectDir) {
|
|
4603
|
+
const own = await loadInstalledConfig(projectDir);
|
|
4604
|
+
if (isHomeDirectory(projectDir)) return own;
|
|
4605
|
+
if (own !== null && !declaresNoContent(own.config)) return own;
|
|
4606
|
+
return loadInstalledConfig(os5.homedir());
|
|
4607
|
+
}
|
|
4519
4608
|
function validateProjectConfig(config) {
|
|
4520
4609
|
const result = projectConfigLoaderSchema.safeParse(config);
|
|
4521
4610
|
if (!result.success) {
|
|
@@ -4561,6 +4650,16 @@ function editDrops(incoming, existing, identity, authoritativeScope) {
|
|
|
4561
4650
|
incomingIdentities.has(identity.identityOf(entry)) || dualScopeIdentities.has(identity.identityOf(entry)) || authoritativeScope !== void 0 && isWithinSessionAuthority(entry, authoritativeScope)
|
|
4562
4651
|
);
|
|
4563
4652
|
}
|
|
4653
|
+
function marketplaceNameBeside(merged, existing) {
|
|
4654
|
+
if (merged.marketplace === existing.marketplace) {
|
|
4655
|
+
return existing.marketplaceName ?? merged.marketplaceName;
|
|
4656
|
+
}
|
|
4657
|
+
if (wouldBorrowAnotherMarketplaceName(merged, existing)) return DEFAULT_PUBLIC_SOURCE_NAME;
|
|
4658
|
+
return merged.marketplaceName;
|
|
4659
|
+
}
|
|
4660
|
+
function wouldBorrowAnotherMarketplaceName(merged, existing) {
|
|
4661
|
+
return merged.marketplace === DEFAULT_SOURCE && merged.marketplaceName === void 0 && existing.marketplaceName !== void 0;
|
|
4662
|
+
}
|
|
4564
4663
|
function mergeConfigs(newConfig, existingConfig, options) {
|
|
4565
4664
|
const merged = { ...newConfig };
|
|
4566
4665
|
if (existingConfig.name) {
|
|
@@ -4590,8 +4689,9 @@ function mergeConfigs(newConfig, existingConfig, options) {
|
|
|
4590
4689
|
if (existingConfig.author) {
|
|
4591
4690
|
merged.author = existingConfig.author;
|
|
4592
4691
|
}
|
|
4593
|
-
|
|
4594
|
-
|
|
4692
|
+
const marketplaceName = marketplaceNameBeside(merged, existingConfig);
|
|
4693
|
+
if (marketplaceName !== void 0) {
|
|
4694
|
+
merged.marketplaceName = marketplaceName;
|
|
4595
4695
|
}
|
|
4596
4696
|
Object.assign(merged, pick(existingConfig, HAND_WRITTEN_FIELDS));
|
|
4597
4697
|
if (existingConfig.projects && !newConfig.projects) {
|
|
@@ -4870,6 +4970,21 @@ async function copySkillsToLocalFlattened(selectedSkillIds, localSkillsDir, sour
|
|
|
4870
4970
|
(skillId) => copySelectedSkill(skillId, localSkillsDir, sourceResult, sourceSelections?.[skillId])
|
|
4871
4971
|
);
|
|
4872
4972
|
}
|
|
4973
|
+
async function copyMarketplaceSkillsToLocalFlattened(marketplaceSkillIds, localSkillsDir, sourceResult) {
|
|
4974
|
+
return copyEachSkill(
|
|
4975
|
+
marketplaceSkillIds,
|
|
4976
|
+
(skillId) => copySkillToLocalFlattened(
|
|
4977
|
+
marketplaceSkillOf(skillId, sourceResult),
|
|
4978
|
+
localSkillsDir,
|
|
4979
|
+
sourceResult
|
|
4980
|
+
)
|
|
4981
|
+
);
|
|
4982
|
+
}
|
|
4983
|
+
function marketplaceSkillOf(skillId, sourceResult) {
|
|
4984
|
+
const skill = sourceResult.marketplaceSkills[skillId];
|
|
4985
|
+
if (!skill) throw new Error(`Not one of the marketplace's skills: ${skillId}`);
|
|
4986
|
+
return skill;
|
|
4987
|
+
}
|
|
4873
4988
|
async function copySelectedSkill(skillId, localSkillsDir, sourceResult, selectedSource) {
|
|
4874
4989
|
const skill = getSkillById(skillId);
|
|
4875
4990
|
const localPath = picksRemoteSource(selectedSource) ? void 0 : localPathOf(skill);
|
|
@@ -4896,7 +5011,7 @@ async function copyLocalSkillTo(skill, localPath, destPath) {
|
|
|
4896
5011
|
}
|
|
4897
5012
|
|
|
4898
5013
|
// src/cli/lib/skills/skill-plugin-compiler.ts
|
|
4899
|
-
import
|
|
5014
|
+
import path23 from "path";
|
|
4900
5015
|
import { parse as parseYaml4 } from "yaml";
|
|
4901
5016
|
|
|
4902
5017
|
// src/cli/lib/plugins/plugin-manifest.ts
|
|
@@ -5003,7 +5118,6 @@ import { readdir } from "fs/promises";
|
|
|
5003
5118
|
|
|
5004
5119
|
// src/cli/lib/installation/installation.ts
|
|
5005
5120
|
import os7 from "os";
|
|
5006
|
-
import path14 from "path";
|
|
5007
5121
|
var INSTALL_MODE_LABELS = {
|
|
5008
5122
|
plugin: "Plugin",
|
|
5009
5123
|
mixed: "Mixed",
|
|
@@ -5013,9 +5127,6 @@ var INSTALL_MODE_DESCRIPTIONS = {
|
|
|
5013
5127
|
plugin: `${INSTALL_MODE_LABELS.plugin} (native install)`,
|
|
5014
5128
|
eject: `${INSTALL_MODE_LABELS.eject} (local copy)`
|
|
5015
5129
|
};
|
|
5016
|
-
function declaresNoContent(config) {
|
|
5017
|
-
return config.skills.length === 0 && config.agents.length === 0;
|
|
5018
|
-
}
|
|
5019
5130
|
function deriveInstallMode(skills) {
|
|
5020
5131
|
if (skills.length === 0) return "eject";
|
|
5021
5132
|
const hasEject = skills.some((s) => s.origin === EJECT_SOURCE);
|
|
@@ -5023,8 +5134,7 @@ function deriveInstallMode(skills) {
|
|
|
5023
5134
|
if (hasEject && hasPlugin) return "mixed";
|
|
5024
5135
|
return hasEject ? "eject" : "plugin";
|
|
5025
5136
|
}
|
|
5026
|
-
async function detectInstallationInDir(dir, scope) {
|
|
5027
|
-
const provider = providerInUse(dir);
|
|
5137
|
+
async function detectInstallationInDir(dir, scope, provider = providerInUse(dir)) {
|
|
5028
5138
|
const configPath = getProjectConfigPath(dir, provider);
|
|
5029
5139
|
if (!await fileExists(configPath)) {
|
|
5030
5140
|
return null;
|
|
@@ -5036,17 +5146,10 @@ async function detectInstallationInDir(dir, scope) {
|
|
|
5036
5146
|
if (declaresNoContent(loaded.config)) {
|
|
5037
5147
|
return null;
|
|
5038
5148
|
}
|
|
5039
|
-
const mode = deriveInstallMode(loaded.config.skills);
|
|
5040
5149
|
return {
|
|
5041
|
-
mode,
|
|
5150
|
+
mode: deriveInstallMode(loaded.config.skills),
|
|
5042
5151
|
configPath,
|
|
5043
5152
|
agentsDir: agentsDir(provider, scope, dir),
|
|
5044
|
-
// Mixed mode uses the local skills dir, as eject does.
|
|
5045
|
-
skillsDir: path14.join(
|
|
5046
|
-
dir,
|
|
5047
|
-
CLAUDE_DIR,
|
|
5048
|
-
mode === "plugin" ? PLUGINS_SUBDIR : STANDARD_DIRS.SKILLS
|
|
5049
|
-
),
|
|
5050
5153
|
projectDir: dir
|
|
5051
5154
|
};
|
|
5052
5155
|
}
|
|
@@ -5061,23 +5164,23 @@ async function holdsItsOwnInstallation(dir) {
|
|
|
5061
5164
|
throw error;
|
|
5062
5165
|
}
|
|
5063
5166
|
}
|
|
5064
|
-
async function detectGlobalInstallation() {
|
|
5065
|
-
return detectInstallationInDir(os7.homedir(), "global");
|
|
5167
|
+
async function detectGlobalInstallation(projectDir) {
|
|
5168
|
+
return detectInstallationInDir(os7.homedir(), "global", inheritedGlobalProvider(projectDir));
|
|
5066
5169
|
}
|
|
5067
5170
|
async function detectInstallation(projectDir = process.cwd()) {
|
|
5068
5171
|
const projectInstallation = await detectProjectInstallation(projectDir);
|
|
5069
5172
|
if (projectInstallation) return projectInstallation;
|
|
5070
|
-
return detectGlobalInstallation();
|
|
5173
|
+
return detectGlobalInstallation(projectDir);
|
|
5071
5174
|
}
|
|
5072
5175
|
|
|
5073
5176
|
// src/cli/lib/installation/local-installer.ts
|
|
5074
5177
|
import os10 from "os";
|
|
5075
|
-
import
|
|
5178
|
+
import path17 from "path";
|
|
5076
5179
|
import { unique as unique4 } from "remeda";
|
|
5077
5180
|
|
|
5078
5181
|
// src/cli/lib/loading/loader.ts
|
|
5079
5182
|
import { parse as parseYaml3 } from "yaml";
|
|
5080
|
-
import
|
|
5183
|
+
import path14 from "path";
|
|
5081
5184
|
|
|
5082
5185
|
// src/cli/utils/frontmatter.ts
|
|
5083
5186
|
import { parse as parseYaml2 } from "yaml";
|
|
@@ -5150,7 +5253,7 @@ async function loadAgentsFromDir(agentsDir2, sourceRoot, options) {
|
|
|
5150
5253
|
return agents;
|
|
5151
5254
|
}
|
|
5152
5255
|
async function readAgentDefinition(agentsDir2, file, sourceRoot, options) {
|
|
5153
|
-
const fullPath =
|
|
5256
|
+
const fullPath = path14.join(agentsDir2, file);
|
|
5154
5257
|
try {
|
|
5155
5258
|
const content = await readFile(fullPath);
|
|
5156
5259
|
const parsed = agentYamlConfigSchema.safeParse(parseYaml3(content));
|
|
@@ -5183,7 +5286,7 @@ function toAgentDefinition(config, file, sourceRoot, options) {
|
|
|
5183
5286
|
...config.hooks !== void 0 && { hooks: config.hooks },
|
|
5184
5287
|
...config.experimental !== void 0 && { experimental: config.experimental },
|
|
5185
5288
|
...config.outputFormat !== void 0 && { outputFormat: config.outputFormat },
|
|
5186
|
-
path:
|
|
5289
|
+
path: path14.dirname(file),
|
|
5187
5290
|
sourceRoot,
|
|
5188
5291
|
...options.agentBaseDir ? { agentBaseDir: options.agentBaseDir } : {},
|
|
5189
5292
|
...config.domain ? { domain: config.domain } : {},
|
|
@@ -5191,7 +5294,7 @@ function toAgentDefinition(config, file, sourceRoot, options) {
|
|
|
5191
5294
|
};
|
|
5192
5295
|
}
|
|
5193
5296
|
async function loadAllAgents(projectRoot) {
|
|
5194
|
-
return loadAgentsFromDir(
|
|
5297
|
+
return loadAgentsFromDir(path14.join(projectRoot, DIRS.agents), projectRoot, {
|
|
5195
5298
|
includeCustomFlag: true,
|
|
5196
5299
|
verboseLabel: "agent"
|
|
5197
5300
|
});
|
|
@@ -5203,9 +5306,9 @@ async function loadMergedAgents(sourcePath) {
|
|
|
5203
5306
|
]);
|
|
5204
5307
|
return { ...cliAgents, ...sourceAgents };
|
|
5205
5308
|
}
|
|
5206
|
-
async function loadProjectAgents(projectRoot) {
|
|
5207
|
-
const sourceFolder = sourceFolderInUse(projectRoot,
|
|
5208
|
-
const projectAgentsDir =
|
|
5309
|
+
async function loadProjectAgents(projectRoot, provider = providerInUse(projectRoot)) {
|
|
5310
|
+
const sourceFolder = sourceFolderInUse(projectRoot, provider);
|
|
5311
|
+
const projectAgentsDir = path14.join(sourceFolder.dir, STANDARD_DIRS.AGENTS);
|
|
5209
5312
|
if (!await directoryExists2(projectAgentsDir)) {
|
|
5210
5313
|
verbose(`No project agents directory at ${projectAgentsDir}`);
|
|
5211
5314
|
return {};
|
|
@@ -5217,9 +5320,10 @@ async function loadProjectAgents(projectRoot) {
|
|
|
5217
5320
|
});
|
|
5218
5321
|
}
|
|
5219
5322
|
async function loadSkillsFromDir(skillsDir2, options = {}) {
|
|
5220
|
-
const { pathPrefix = "", requireMetadata = false } = options;
|
|
5323
|
+
const { pathPrefix = "", requireMetadata = false, folderLabel = `${pathPrefix}/` } = options;
|
|
5221
5324
|
const skills = {};
|
|
5222
5325
|
const unusableMetadata = [];
|
|
5326
|
+
let unregisteredCount = 0;
|
|
5223
5327
|
if (!await directoryExists2(skillsDir2)) {
|
|
5224
5328
|
return { skills, unusableMetadata };
|
|
5225
5329
|
}
|
|
@@ -5229,32 +5333,32 @@ async function loadSkillsFromDir(skillsDir2, options = {}) {
|
|
|
5229
5333
|
if (requireMetadata) {
|
|
5230
5334
|
const verdict = await judgeSkillMetadata(skill);
|
|
5231
5335
|
if (verdict.kind === "unusable") unusableMetadata.push(verdict.refusal);
|
|
5336
|
+
if (verdict.kind === "unregistered") unregisteredCount++;
|
|
5232
5337
|
if (verdict.kind !== "admitted") continue;
|
|
5233
5338
|
}
|
|
5234
5339
|
const definition = await readSkillDefinition(skill);
|
|
5235
5340
|
if (definition) skills[definition.id] = definition;
|
|
5236
5341
|
}
|
|
5342
|
+
if (unregisteredCount > 0) warn(unregisteredSkillsSkipped(unregisteredCount, folderLabel));
|
|
5237
5343
|
return { skills, unusableMetadata };
|
|
5238
5344
|
}
|
|
5239
5345
|
function locateSkill(skillsDir2, skillFile, pathPrefix) {
|
|
5240
|
-
const skillMdPath =
|
|
5241
|
-
const skillDir =
|
|
5242
|
-
const relativePath =
|
|
5346
|
+
const skillMdPath = path14.join(skillsDir2, skillFile);
|
|
5347
|
+
const skillDir = path14.dirname(skillMdPath);
|
|
5348
|
+
const relativePath = path14.relative(skillsDir2, skillDir);
|
|
5243
5349
|
return {
|
|
5244
5350
|
skillFile,
|
|
5245
5351
|
skillMdPath,
|
|
5246
5352
|
skillDir,
|
|
5247
|
-
skillDirName:
|
|
5353
|
+
skillDirName: path14.basename(skillDir),
|
|
5248
5354
|
displayPath: pathPrefix ? `${pathPrefix}/${relativePath}/` : `${relativePath}/`
|
|
5249
5355
|
};
|
|
5250
5356
|
}
|
|
5251
5357
|
async function judgeSkillMetadata(skill) {
|
|
5252
|
-
const metadataPath =
|
|
5358
|
+
const metadataPath = path14.join(skill.skillDir, STANDARD_FILES.METADATA_YAML);
|
|
5253
5359
|
if (!await fileExists(metadataPath)) {
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
);
|
|
5257
|
-
return { kind: "skipped" };
|
|
5360
|
+
verbose(` No ${STANDARD_FILES.METADATA_YAML} in '${skill.skillDirName}'`);
|
|
5361
|
+
return { kind: "unregistered" };
|
|
5258
5362
|
}
|
|
5259
5363
|
const read = await readSkillMetadata(metadataPath);
|
|
5260
5364
|
if (!read.usable) {
|
|
@@ -5291,7 +5395,7 @@ async function readSkillDefinition(skill) {
|
|
|
5291
5395
|
}
|
|
5292
5396
|
}
|
|
5293
5397
|
async function loadPluginSkills(pluginDir) {
|
|
5294
|
-
const { skills } = await loadSkillsFromDir(
|
|
5398
|
+
const { skills } = await loadSkillsFromDir(path14.join(pluginDir, "skills"), {
|
|
5295
5399
|
pathPrefix: "skills",
|
|
5296
5400
|
requireMetadata: false
|
|
5297
5401
|
});
|
|
@@ -5300,17 +5404,18 @@ async function loadPluginSkills(pluginDir) {
|
|
|
5300
5404
|
|
|
5301
5405
|
// src/cli/lib/loading/source-fetcher.ts
|
|
5302
5406
|
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
5303
|
-
import { createHash as createHash2 } from "crypto";
|
|
5407
|
+
import { createHash as createHash2, randomUUID } from "crypto";
|
|
5304
5408
|
import { readFileSync } from "fs";
|
|
5305
|
-
import { rename, stat } from "fs/promises";
|
|
5409
|
+
import { open, rename, stat } from "fs/promises";
|
|
5410
|
+
import { setTimeout as sleep } from "timers/promises";
|
|
5306
5411
|
import { downloadTemplate } from "giget";
|
|
5307
5412
|
import os8 from "os";
|
|
5308
|
-
import
|
|
5413
|
+
import path16 from "path";
|
|
5309
5414
|
import { isDeepEqual } from "remeda";
|
|
5310
5415
|
import { z as z4 } from "zod";
|
|
5311
5416
|
|
|
5312
5417
|
// src/cli/lib/marketplace-generator.ts
|
|
5313
|
-
import
|
|
5418
|
+
import path15 from "path";
|
|
5314
5419
|
import { countBy, sortBy } from "remeda";
|
|
5315
5420
|
var PLUGIN_MANIFEST_PATH = `${PLUGIN_MANIFEST_DIR}/${PLUGIN_MANIFEST_FILE}`;
|
|
5316
5421
|
var MARKETPLACE_SCHEMA_URL = "https://anthropic.com/claude-code/marketplace.schema.json";
|
|
@@ -5342,7 +5447,7 @@ async function generateMarketplace(pluginsDir2, options) {
|
|
|
5342
5447
|
const rootWithoutDotSlash = options.pluginRoot.replace(/^\.\//, "");
|
|
5343
5448
|
for (const manifestFile of manifestFiles) {
|
|
5344
5449
|
const pluginDirName = pluginDirNameOf(manifestFile);
|
|
5345
|
-
const pluginDir =
|
|
5450
|
+
const pluginDir = path15.join(pluginsDir2, pluginDirName);
|
|
5346
5451
|
const manifest = await readPluginManifest(pluginDir);
|
|
5347
5452
|
if (!manifest) {
|
|
5348
5453
|
warn(`Could not read plugin manifest: '${manifestFile}'`);
|
|
@@ -5397,17 +5502,17 @@ function reservedNameError(marketplaceName, wayOut) {
|
|
|
5397
5502
|
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}.`;
|
|
5398
5503
|
}
|
|
5399
5504
|
function namespaceViolationError(foreignIds, marketplaceName) {
|
|
5400
|
-
const
|
|
5401
|
-
const unlisted = foreignIds.length -
|
|
5505
|
+
const listed = foreignIds.slice(0, MAX_REPORTED_NAMESPACE_VIOLATIONS).map((id) => ` ${id} -> ${marketplaceName}-${id}`);
|
|
5506
|
+
const unlisted = foreignIds.length - listed.length;
|
|
5402
5507
|
return [
|
|
5403
5508
|
`Marketplace '${marketplaceName}' ships ${foreignIds.length} skill id(s) outside its own namespace. Every skill id must begin with the marketplace's name:`,
|
|
5404
|
-
...
|
|
5509
|
+
...listed,
|
|
5405
5510
|
...unlisted > 0 ? [` ... and ${unlisted} more`] : [],
|
|
5406
5511
|
`Rename each skill directory and the 'name' in its SKILL.md, re-run 'build plugins', then build the marketplace again.`
|
|
5407
5512
|
].join("\n");
|
|
5408
5513
|
}
|
|
5409
5514
|
async function writeMarketplace(outputPath, marketplace) {
|
|
5410
|
-
await ensureDir(
|
|
5515
|
+
await ensureDir(path15.dirname(outputPath));
|
|
5411
5516
|
const content = `${JSON.stringify(marketplace, null, 2)}
|
|
5412
5517
|
`;
|
|
5413
5518
|
await writeFile(outputPath, content);
|
|
@@ -5443,6 +5548,9 @@ var TarballWithheldError = class extends Error {
|
|
|
5443
5548
|
this.status = status;
|
|
5444
5549
|
}
|
|
5445
5550
|
};
|
|
5551
|
+
function isUnanswered(answer) {
|
|
5552
|
+
return answer === "unreachable" || answer instanceof TarballWithheldError;
|
|
5553
|
+
}
|
|
5446
5554
|
var marketplaceMustAnswer = new AsyncLocalStorage3();
|
|
5447
5555
|
function askingEachMarketplaceItself(body) {
|
|
5448
5556
|
const differingCopiesReplaced = /* @__PURE__ */ new Set();
|
|
@@ -5481,7 +5589,7 @@ function sanitizeSourceForCache(source) {
|
|
|
5481
5589
|
}
|
|
5482
5590
|
function getCacheDir(source) {
|
|
5483
5591
|
const sanitized = sanitizeSourceForCache(source) || "unknown";
|
|
5484
|
-
return
|
|
5592
|
+
return path16.join(cacheRoot(), "sources", sanitized);
|
|
5485
5593
|
}
|
|
5486
5594
|
async function fetchFromSource(source, options = {}) {
|
|
5487
5595
|
const { subdir } = options;
|
|
@@ -5492,8 +5600,8 @@ async function fetchFromSource(source, options = {}) {
|
|
|
5492
5600
|
return fetchFromRemoteSource(source, subdir);
|
|
5493
5601
|
}
|
|
5494
5602
|
async function fetchFromLocalSource(source, subdir) {
|
|
5495
|
-
const fullPath = subdir ?
|
|
5496
|
-
const absolutePath =
|
|
5603
|
+
const fullPath = subdir ? path16.join(source, subdir) : source;
|
|
5604
|
+
const absolutePath = path16.isAbsolute(fullPath) ? fullPath : path16.resolve(process.cwd(), fullPath);
|
|
5497
5605
|
if (!await directoryExists2(absolutePath)) {
|
|
5498
5606
|
throw new Error(
|
|
5499
5607
|
`Local marketplace not found: '${absolutePath}'
|
|
@@ -5524,18 +5632,16 @@ function getGigetCacheDir(source) {
|
|
|
5524
5632
|
return void 0;
|
|
5525
5633
|
}
|
|
5526
5634
|
const templateName = uriMatch.groups.repo.replace("/", "-").replace(/[^\da-z-]/gi, "-");
|
|
5527
|
-
return
|
|
5635
|
+
return path16.join(gigetCacheRoot(), providerName, templateName);
|
|
5528
5636
|
}
|
|
5529
5637
|
function gigetCacheRoot() {
|
|
5530
|
-
return process.env.XDG_CACHE_HOME ?
|
|
5638
|
+
return process.env.XDG_CACHE_HOME ? path16.resolve(process.env.XDG_CACHE_HOME, "giget") : path16.resolve(os8.homedir(), ".cache", "giget");
|
|
5531
5639
|
}
|
|
5532
5640
|
async function clearGigetCache(source) {
|
|
5533
5641
|
const gigetDir = getGigetCacheDir(source);
|
|
5534
|
-
if (!gigetDir) return;
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
await remove(gigetDir);
|
|
5538
|
-
}
|
|
5642
|
+
if (!gigetDir || !await directoryExists2(gigetDir)) return;
|
|
5643
|
+
verbose(`Clearing giget cache: ${gigetDir}`);
|
|
5644
|
+
await holdingGigetFolder(path16.dirname(gigetDir), () => remove(gigetDir));
|
|
5539
5645
|
}
|
|
5540
5646
|
async function fetchFromRemoteSource(source, subdir) {
|
|
5541
5647
|
const cacheDir = getCacheDir(source);
|
|
@@ -5544,38 +5650,95 @@ async function fetchFromRemoteSource(source, subdir) {
|
|
|
5544
5650
|
verbose(`Cache directory: ${cacheDir}`);
|
|
5545
5651
|
if (await directoryExists2(cacheDir)) {
|
|
5546
5652
|
const verdict = await revalidateCachedCopy(cacheDir, source);
|
|
5547
|
-
if (keepsTheCopy(verdict))
|
|
5548
|
-
verbose(`Using cached marketplace: ${cacheDir}`);
|
|
5549
|
-
return {
|
|
5550
|
-
path: cacheDir,
|
|
5551
|
-
fromCache: true,
|
|
5552
|
-
unreachable: verdict === "unreachable",
|
|
5553
|
-
source: fullSource
|
|
5554
|
-
};
|
|
5555
|
-
}
|
|
5653
|
+
if (keepsTheCopy(verdict)) return readSavedCopy(cacheDir, fullSource, verdict);
|
|
5556
5654
|
announceRefetch(verdict);
|
|
5557
|
-
if (!mustAnswer())
|
|
5655
|
+
if (!mustAnswer()) return refreshSavedCopy(cacheDir, source, fullSource, verdict);
|
|
5558
5656
|
}
|
|
5559
5657
|
const differingCopiesReplaced = marketplaceMustAnswer.getStore();
|
|
5560
5658
|
return differingCopiesReplaced ? downloadAnsweredFor(cacheDir, source, fullSource, differingCopiesReplaced) : downloadFreshCopy(cacheDir, source, fullSource);
|
|
5561
5659
|
}
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5660
|
+
function readSavedCopy(cacheDir, fullSource, verdict) {
|
|
5661
|
+
verbose(`Using cached marketplace: ${cacheDir}`);
|
|
5662
|
+
return {
|
|
5663
|
+
path: cacheDir,
|
|
5664
|
+
fromCache: true,
|
|
5665
|
+
unreachable: verdict === "unreachable",
|
|
5666
|
+
source: fullSource
|
|
5667
|
+
};
|
|
5668
|
+
}
|
|
5669
|
+
async function refreshSavedCopy(cacheDir, source, fullSource, verdict) {
|
|
5670
|
+
if (verdict === "superseded") await clearGigetCache(source);
|
|
5671
|
+
const staged = `${cacheDir}${STAGED_DOWNLOAD_SUFFIX}`;
|
|
5672
|
+
try {
|
|
5673
|
+
const download = await downloadUnlessItFails(source, fullSource, staged);
|
|
5674
|
+
if (isUnanswered(download)) {
|
|
5675
|
+
const failed = verdictOnFailedDownload(verdict, download, source);
|
|
5676
|
+
return keepSavedCopyUnlessStopped(cacheDir, fullSource, failed);
|
|
5677
|
+
}
|
|
5678
|
+
const downloaded = await verdictOnDownload(download, source);
|
|
5679
|
+
if (downloaded !== "current") {
|
|
5680
|
+
return keepSavedCopyUnlessStopped(cacheDir, fullSource, downloaded);
|
|
5681
|
+
}
|
|
5682
|
+
await moveIntoPlace(cacheDir, staged);
|
|
5683
|
+
await recordFetchedCopy(cacheDir, download.result, download.savedTarball);
|
|
5684
|
+
settleForThisRun(cacheDir, "current");
|
|
5685
|
+
return { path: cacheDir, fromCache: false, unreachable: false, source: fullSource };
|
|
5686
|
+
} catch (error) {
|
|
5687
|
+
throw createDetailedFetchError(error, source);
|
|
5688
|
+
} finally {
|
|
5689
|
+
await remove(staged);
|
|
5690
|
+
}
|
|
5691
|
+
}
|
|
5692
|
+
function keepSavedCopyUnlessStopped(cacheDir, fullSource, verdict) {
|
|
5693
|
+
if (verdict instanceof TarballWithheldError) throw verdict;
|
|
5694
|
+
settleForThisRun(cacheDir, verdict);
|
|
5695
|
+
return readSavedCopy(cacheDir, fullSource, verdict);
|
|
5696
|
+
}
|
|
5697
|
+
async function downloadUnlessItFails(source, fullSource, staged) {
|
|
5698
|
+
try {
|
|
5699
|
+
return await downloadStaged(source, fullSource, staged);
|
|
5700
|
+
} catch (error) {
|
|
5701
|
+
verbose(`Could not download ${fullSource}: ${getErrorMessage(error)}`);
|
|
5702
|
+
return refusalIn(error) ?? "unreachable";
|
|
5703
|
+
}
|
|
5704
|
+
}
|
|
5705
|
+
var GIGET_DOWNLOAD_REFUSED = /^Failed to download \S+: (?<status>\d{3}) (?<statusText>.*)$/;
|
|
5706
|
+
function refusalIn(error) {
|
|
5707
|
+
const answered = GIGET_DOWNLOAD_REFUSED.exec(getErrorMessage(error))?.groups;
|
|
5708
|
+
if (answered?.status === void 0 || answered.statusText === void 0) return void 0;
|
|
5709
|
+
const status = Number(answered.status);
|
|
5710
|
+
if (postureOn(status) === void 0) return void 0;
|
|
5711
|
+
return new TarballWithheldError({ status, statusText: answered.statusText });
|
|
5712
|
+
}
|
|
5713
|
+
function verdictOnFailedDownload(verdict, failure, source) {
|
|
5714
|
+
if (verdict === "unrecorded") return verdictOnUnanswered(failure, source);
|
|
5715
|
+
warn(updateNotFetchedUsingCache(source));
|
|
5716
|
+
return "outdated";
|
|
5717
|
+
}
|
|
5718
|
+
function updateNotFetchedUsingCache(source) {
|
|
5719
|
+
return `Could not fetch the update from ${source} \u2014 using the cached copy, which is out of date.`;
|
|
5720
|
+
}
|
|
5721
|
+
async function verdictOnDownload({ result, savedTarball }, source) {
|
|
5722
|
+
return savedTarball ? verdictOnSavedTarball(result, savedTarball, source) : "current";
|
|
5565
5723
|
}
|
|
5566
5724
|
async function downloadFreshCopy(cacheDir, source, fullSource) {
|
|
5567
|
-
await ensureDir(
|
|
5725
|
+
await ensureDir(path16.dirname(cacheDir));
|
|
5568
5726
|
try {
|
|
5569
|
-
const
|
|
5727
|
+
const download = await gigetDownload(source, fullSource, {
|
|
5570
5728
|
dir: cacheDir,
|
|
5571
5729
|
force: true,
|
|
5572
5730
|
// Always force when downloading to avoid "already exists" error
|
|
5573
5731
|
offline: false
|
|
5574
5732
|
});
|
|
5733
|
+
const { result, savedTarball } = download;
|
|
5575
5734
|
verbose(`Downloaded to: ${result.dir}`);
|
|
5576
|
-
|
|
5735
|
+
const downloaded = await verdictOnDownload(download, source);
|
|
5736
|
+
if (downloaded instanceof TarballWithheldError) {
|
|
5737
|
+
await remove(cacheDir);
|
|
5738
|
+
throw downloaded;
|
|
5739
|
+
}
|
|
5577
5740
|
await recordFetchedCopy(cacheDir, result, savedTarball);
|
|
5578
|
-
|
|
5741
|
+
settleForThisRun(cacheDir, "current");
|
|
5579
5742
|
return {
|
|
5580
5743
|
path: result.dir,
|
|
5581
5744
|
fromCache: false,
|
|
@@ -5586,25 +5749,27 @@ async function downloadFreshCopy(cacheDir, source, fullSource) {
|
|
|
5586
5749
|
throw createDetailedFetchError(error, source);
|
|
5587
5750
|
}
|
|
5588
5751
|
}
|
|
5589
|
-
async function
|
|
5752
|
+
async function verdictOnSavedTarball(result, savedTarball, source) {
|
|
5590
5753
|
const tar = tarballOf(result);
|
|
5591
|
-
if (tar === void 0) return;
|
|
5754
|
+
if (tar === void 0) return "current";
|
|
5592
5755
|
const answer = await answerAbout(tar);
|
|
5593
|
-
if (answer
|
|
5594
|
-
if (answer === "
|
|
5756
|
+
if (isUnanswered(answer)) return verdictOnUnanswered(answer, source);
|
|
5757
|
+
if (answer.etag === savedTarball.etag) return "current";
|
|
5758
|
+
warn(updateNotFetchedUsingCache(source));
|
|
5759
|
+
return "outdated";
|
|
5760
|
+
}
|
|
5761
|
+
function verdictOnUnanswered(unanswered, source) {
|
|
5762
|
+
if (unanswered === "unreachable") {
|
|
5595
5763
|
warn(sourceUnreachableUsingCache(source));
|
|
5596
|
-
return;
|
|
5597
|
-
}
|
|
5598
|
-
if (stopsTheLoad(answer)) {
|
|
5599
|
-
await remove(cacheDir);
|
|
5600
|
-
throw answer;
|
|
5764
|
+
return "unreachable";
|
|
5601
5765
|
}
|
|
5602
|
-
|
|
5766
|
+
if (stopsTheLoad(unanswered)) return unanswered;
|
|
5767
|
+
announceTheCopyKept(unanswered, source);
|
|
5768
|
+
return "refused";
|
|
5603
5769
|
}
|
|
5604
5770
|
async function answerAbout(tar) {
|
|
5605
5771
|
try {
|
|
5606
|
-
await fetchEtag(tar);
|
|
5607
|
-
return "answered";
|
|
5772
|
+
return { etag: await fetchEtag(tar) };
|
|
5608
5773
|
} catch (error) {
|
|
5609
5774
|
if (error instanceof TarballWithheldError) return error;
|
|
5610
5775
|
verbose(`Could not ask ${tar} about the tarball giget saved: ${getErrorMessage(error)}`);
|
|
@@ -5616,21 +5781,16 @@ var ONLY_A_SAVED_COPY_CAME_BACK = "the download failed and only a saved copy cam
|
|
|
5616
5781
|
var SET_ASIDE_SUFFIX = ".set-aside";
|
|
5617
5782
|
async function downloadAnsweredFor(cacheDir, source, fullSource, differingCopiesReplaced) {
|
|
5618
5783
|
const staged = `${cacheDir}${STAGED_DOWNLOAD_SUFFIX}`;
|
|
5619
|
-
const
|
|
5620
|
-
|
|
5621
|
-
force: true,
|
|
5622
|
-
forceClean: true,
|
|
5623
|
-
offline: false
|
|
5624
|
-
});
|
|
5625
|
-
await ensureDir(path17.dirname(cacheDir));
|
|
5784
|
+
const download = () => downloadStaged(source, fullSource, staged);
|
|
5785
|
+
await ensureDir(path16.dirname(cacheDir));
|
|
5626
5786
|
try {
|
|
5627
|
-
const { result, savedTarball } = await
|
|
5787
|
+
const { result, savedTarball } = await download();
|
|
5628
5788
|
const record = await askAfterTheDownload(result, source);
|
|
5629
|
-
if (savedTarball) await downloadPast(savedTarball, record,
|
|
5789
|
+
if (savedTarball) await downloadPast(savedTarball, record, download, source);
|
|
5630
5790
|
const differed = await savedCopyDiffers(cacheDir, staged);
|
|
5631
5791
|
await replaceCopy(cacheDir, staged, record);
|
|
5632
5792
|
if (differed) differingCopiesReplaced.add(source);
|
|
5633
|
-
|
|
5793
|
+
settleForThisRun(cacheDir, "current");
|
|
5634
5794
|
return { path: cacheDir, fromCache: false, unreachable: false, source: fullSource };
|
|
5635
5795
|
} catch (error) {
|
|
5636
5796
|
if (error instanceof MarketplaceUnreachableError) throw error;
|
|
@@ -5639,11 +5799,22 @@ async function downloadAnsweredFor(cacheDir, source, fullSource, differingCopies
|
|
|
5639
5799
|
await remove(staged);
|
|
5640
5800
|
}
|
|
5641
5801
|
}
|
|
5802
|
+
function downloadStaged(source, fullSource, staged) {
|
|
5803
|
+
return gigetDownload(source, fullSource, {
|
|
5804
|
+
dir: staged,
|
|
5805
|
+
force: true,
|
|
5806
|
+
forceClean: true,
|
|
5807
|
+
offline: false
|
|
5808
|
+
});
|
|
5809
|
+
}
|
|
5642
5810
|
async function downloadPast(savedTarball, record, download, source) {
|
|
5643
5811
|
if (!savedUnder(savedTarball, record)) {
|
|
5644
5812
|
throw new MarketplaceUnreachableError(source, ONLY_A_SAVED_COPY_CAME_BACK);
|
|
5645
5813
|
}
|
|
5646
|
-
await
|
|
5814
|
+
await holdingGigetFolder(
|
|
5815
|
+
gigetProviderDir(source),
|
|
5816
|
+
() => withTarballSetAside(savedTarball.path, download)
|
|
5817
|
+
);
|
|
5647
5818
|
}
|
|
5648
5819
|
async function withTarballSetAside(tarball, download) {
|
|
5649
5820
|
const record = gigetRecordOf(tarball);
|
|
@@ -5679,44 +5850,170 @@ function everyFileIn(dir) {
|
|
|
5679
5850
|
return glob(EVERY_FILE, dir, { dot: true });
|
|
5680
5851
|
}
|
|
5681
5852
|
function sameBytesAt(file, left, right) {
|
|
5682
|
-
return readFileSync(
|
|
5853
|
+
return readFileSync(path16.join(left, file)).equals(readFileSync(path16.join(right, file)));
|
|
5683
5854
|
}
|
|
5684
5855
|
async function replaceCopy(cacheDir, staged, record) {
|
|
5856
|
+
await moveIntoPlace(cacheDir, staged);
|
|
5857
|
+
await writeFile(fetchRecordPath(cacheDir), JSON.stringify(record));
|
|
5858
|
+
}
|
|
5859
|
+
async function moveIntoPlace(cacheDir, staged) {
|
|
5685
5860
|
await remove(cacheDir);
|
|
5686
5861
|
await rename(staged, cacheDir);
|
|
5687
|
-
await writeFile(fetchRecordPath(cacheDir), JSON.stringify(record));
|
|
5688
5862
|
}
|
|
5689
5863
|
var GIGET_TARBALLS = "**/*.tar.gz";
|
|
5690
5864
|
var gigetTarballRecordSchema = z4.object({ etag: z4.string().min(1).optional() });
|
|
5691
5865
|
async function gigetDownload(source, fullSource, options) {
|
|
5692
5866
|
const providerDir = gigetProviderDir(source);
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5867
|
+
return holdingGigetFolder(
|
|
5868
|
+
providerDir,
|
|
5869
|
+
() => downloadOverMarkedRecords(providerDir, fullSource, options)
|
|
5870
|
+
);
|
|
5871
|
+
}
|
|
5872
|
+
async function downloadOverMarkedRecords(providerDir, fullSource, options) {
|
|
5873
|
+
const marks = await markSavedRecords(providerDir);
|
|
5874
|
+
try {
|
|
5875
|
+
const result = await downloadTemplate(fullSource, options);
|
|
5876
|
+
const tarball = path16.join(providerDir, gigetTarballOf(result));
|
|
5877
|
+
if (await writtenByThisDownload(marks.get(tarball))) return { result };
|
|
5878
|
+
verbose(`giget extracted the tarball it saved earlier, not a download: ${tarball}`);
|
|
5879
|
+
return { result, savedTarball: { path: tarball, etag: await etagSavedWith(tarball) } };
|
|
5880
|
+
} finally {
|
|
5881
|
+
await Promise.all([...marks.values()].map(unmark));
|
|
5882
|
+
}
|
|
5700
5883
|
}
|
|
5701
5884
|
function gigetProviderDir(source) {
|
|
5702
5885
|
const provider = SOURCE_PROTO_RE.exec(source)?.[1];
|
|
5703
5886
|
if (provider === void 0) throw new Error(`No provider named in '${source}'`);
|
|
5704
|
-
return
|
|
5887
|
+
return path16.join(gigetCacheRoot(), provider);
|
|
5888
|
+
}
|
|
5889
|
+
var GIGET_FOLDER_LOCK = "agents-inc.lock";
|
|
5890
|
+
var EXCLUSIVE_CREATE = "wx";
|
|
5891
|
+
var LOCK_RETRY_MS = 100;
|
|
5892
|
+
var LOCK_ABANDONED_MS = 10 * 60 * 1e3;
|
|
5893
|
+
var gigetFolderHeld = new AsyncLocalStorage3();
|
|
5894
|
+
var holdersInThisProcess = /* @__PURE__ */ new Set();
|
|
5895
|
+
async function holdingGigetFolder(folder, body) {
|
|
5896
|
+
if (gigetFolderHeld.getStore() === folder) return body();
|
|
5897
|
+
const lock = lockFor(folder);
|
|
5898
|
+
try {
|
|
5899
|
+
await lockGigetFolder(folder, lock);
|
|
5900
|
+
return await gigetFolderHeld.run(folder, body);
|
|
5901
|
+
} finally {
|
|
5902
|
+
await letGo(lock);
|
|
5903
|
+
}
|
|
5904
|
+
}
|
|
5905
|
+
function lockFor(folder) {
|
|
5906
|
+
const holder = `${process.pid} ${randomUUID()}`;
|
|
5907
|
+
holdersInThisProcess.add(holder);
|
|
5908
|
+
return { path: path16.join(folder, GIGET_FOLDER_LOCK), holder };
|
|
5705
5909
|
}
|
|
5706
|
-
async function
|
|
5910
|
+
async function letGo({ path: lockPath, holder }) {
|
|
5911
|
+
try {
|
|
5912
|
+
await removeLockHeldBy(lockPath, holder);
|
|
5913
|
+
} finally {
|
|
5914
|
+
holdersInThisProcess.delete(holder);
|
|
5915
|
+
}
|
|
5916
|
+
}
|
|
5917
|
+
async function lockGigetFolder(folder, lock) {
|
|
5918
|
+
await ensureDir(folder);
|
|
5919
|
+
while (!await created(lock)) {
|
|
5920
|
+
const holder = await readFileOptional(lock.path);
|
|
5921
|
+
if (await abandoned(lock.path, holder)) {
|
|
5922
|
+
verbose(`Taking over the lock on ${folder}: its run ended, or held it past any download`);
|
|
5923
|
+
await removeLockHeldBy(lock.path, holder);
|
|
5924
|
+
} else {
|
|
5925
|
+
await sleep(LOCK_RETRY_MS);
|
|
5926
|
+
}
|
|
5927
|
+
}
|
|
5928
|
+
}
|
|
5929
|
+
async function created({ path: lockPath, holder }) {
|
|
5930
|
+
try {
|
|
5931
|
+
await (await open(lockPath, EXCLUSIVE_CREATE)).close();
|
|
5932
|
+
} catch (error) {
|
|
5933
|
+
if (failedWith(error, "EEXIST")) return false;
|
|
5934
|
+
throw error;
|
|
5935
|
+
}
|
|
5936
|
+
await writeFile(lockPath, holder);
|
|
5937
|
+
return true;
|
|
5938
|
+
}
|
|
5939
|
+
async function abandoned(lockPath, holder) {
|
|
5940
|
+
return holderEnded(holder) || await outlastedAnyDownload(lockPath);
|
|
5941
|
+
}
|
|
5942
|
+
function holderEnded(holder) {
|
|
5943
|
+
const pid = Number.parseInt(holder, 10);
|
|
5944
|
+
if (pid === process.pid) return !holdersInThisProcess.has(holder);
|
|
5945
|
+
return pid > 0 && !processRunning(pid);
|
|
5946
|
+
}
|
|
5947
|
+
async function outlastedAnyDownload(lockPath) {
|
|
5948
|
+
const age = await ageOf(lockPath);
|
|
5949
|
+
return age !== void 0 && age > LOCK_ABANDONED_MS;
|
|
5950
|
+
}
|
|
5951
|
+
function processRunning(pid) {
|
|
5952
|
+
try {
|
|
5953
|
+
process.kill(pid, 0);
|
|
5954
|
+
return true;
|
|
5955
|
+
} catch (error) {
|
|
5956
|
+
return !failedWith(error, "ESRCH");
|
|
5957
|
+
}
|
|
5958
|
+
}
|
|
5959
|
+
async function ageOf(file) {
|
|
5960
|
+
try {
|
|
5961
|
+
return Date.now() - (await stat(file)).mtimeMs;
|
|
5962
|
+
} catch (error) {
|
|
5963
|
+
if (failedWith(error, "ENOENT")) return void 0;
|
|
5964
|
+
throw error;
|
|
5965
|
+
}
|
|
5966
|
+
}
|
|
5967
|
+
async function removeLockHeldBy(lockPath, holder) {
|
|
5968
|
+
if (await readFileOptional(lockPath) === holder) await remove(lockPath);
|
|
5969
|
+
}
|
|
5970
|
+
function failedWith(error, code) {
|
|
5971
|
+
return isRecord(error) && error.code === code;
|
|
5972
|
+
}
|
|
5973
|
+
var RECORD_MARK = "\n";
|
|
5974
|
+
var NO_RECORD = "{}";
|
|
5975
|
+
var UNMARKED_SUFFIX = ".unmarked";
|
|
5976
|
+
async function markSavedRecords(providerDir) {
|
|
5707
5977
|
const tarballs = await glob(GIGET_TARBALLS, providerDir);
|
|
5708
|
-
const paths = tarballs.map((tarball) =>
|
|
5709
|
-
return new Map(await Promise.all(paths.map(async (at) => [at, await
|
|
5978
|
+
const paths = tarballs.map((tarball) => path16.join(providerDir, tarball));
|
|
5979
|
+
return new Map(await Promise.all(paths.map(async (at) => [at, await markRecordOf(at)])));
|
|
5980
|
+
}
|
|
5981
|
+
async function markRecordOf(tarball) {
|
|
5982
|
+
const record = gigetRecordOf(tarball);
|
|
5983
|
+
await putBackLeftUnmarked(record);
|
|
5984
|
+
const held = await fileExists(record);
|
|
5985
|
+
const marked = `${RECORD_MARK}${await readFileOptional(record, NO_RECORD)}`;
|
|
5986
|
+
if (held) await rename(record, unmarkedOf(record));
|
|
5987
|
+
await writeFile(record, marked);
|
|
5988
|
+
return { record, marked, held };
|
|
5710
5989
|
}
|
|
5711
|
-
async function
|
|
5712
|
-
|
|
5990
|
+
async function putBackLeftUnmarked(record) {
|
|
5991
|
+
if (await fileExists(unmarkedOf(record))) await rename(unmarkedOf(record), record);
|
|
5992
|
+
}
|
|
5993
|
+
function unmarkedOf(record) {
|
|
5994
|
+
return `${record}${UNMARKED_SUFFIX}`;
|
|
5995
|
+
}
|
|
5996
|
+
async function writtenByThisDownload(mark) {
|
|
5997
|
+
return mark === void 0 || await rewritten(mark);
|
|
5998
|
+
}
|
|
5999
|
+
async function rewritten({ record, marked }) {
|
|
6000
|
+
return await readFileOptional(record, marked) !== marked;
|
|
6001
|
+
}
|
|
6002
|
+
async function unmark(mark) {
|
|
6003
|
+
const { record, held } = mark;
|
|
6004
|
+
if (await rewritten(mark)) {
|
|
6005
|
+
if (held) await remove(unmarkedOf(record));
|
|
6006
|
+
return;
|
|
6007
|
+
}
|
|
6008
|
+
if (held) await rename(unmarkedOf(record), record);
|
|
6009
|
+
else await remove(record);
|
|
5713
6010
|
}
|
|
5714
6011
|
function gigetTarballOf(result) {
|
|
5715
6012
|
const name = result.name;
|
|
5716
6013
|
const version = result.version;
|
|
5717
6014
|
if (typeof name !== "string") throw new Error("the download named no template");
|
|
5718
6015
|
const stem = typeof version === "string" && version !== "" ? version : name;
|
|
5719
|
-
return
|
|
6016
|
+
return path16.join(name, `${stem}.tar.gz`);
|
|
5720
6017
|
}
|
|
5721
6018
|
function gigetRecordOf(tarball) {
|
|
5722
6019
|
return `${tarball}.json`;
|
|
@@ -5730,7 +6027,7 @@ function savedUnder(savedTarball, record) {
|
|
|
5730
6027
|
return savedTarball.etag !== void 0 && savedTarball.etag === record.etag;
|
|
5731
6028
|
}
|
|
5732
6029
|
function keepsTheCopy(verdict) {
|
|
5733
|
-
return verdict === "current" || verdict === "unreachable" || verdict === "refused";
|
|
6030
|
+
return verdict === "current" || verdict === "unreachable" || verdict === "refused" || verdict === "outdated";
|
|
5734
6031
|
}
|
|
5735
6032
|
var REVALIDATION_TIMEOUT_MS = 5e3;
|
|
5736
6033
|
var FETCH_RECORD_SUFFIX = ".etag.json";
|
|
@@ -5752,16 +6049,12 @@ function revalidateCachedCopy(cacheDir, source) {
|
|
|
5752
6049
|
verdicts.set(cacheDir, verdict);
|
|
5753
6050
|
return verdict;
|
|
5754
6051
|
}
|
|
5755
|
-
function
|
|
5756
|
-
verdictsThisRun().set(cacheDir, Promise.resolve(
|
|
6052
|
+
function settleForThisRun(cacheDir, verdict) {
|
|
6053
|
+
verdictsThisRun().set(cacheDir, Promise.resolve(verdict));
|
|
5757
6054
|
}
|
|
5758
6055
|
async function classifyCachedCopy(cacheDir, source) {
|
|
5759
6056
|
const record = await readFetchRecord(cacheDir);
|
|
5760
6057
|
if (!record) return "unrecorded";
|
|
5761
|
-
if (record.etag === void 0 && !mustAnswer()) {
|
|
5762
|
-
verbose(`No ETag was recorded for ${record.tar} \u2014 keeping the cached copy`);
|
|
5763
|
-
return "current";
|
|
5764
|
-
}
|
|
5765
6058
|
try {
|
|
5766
6059
|
return judgeAnswer(record, await fetchEtag(record.tar));
|
|
5767
6060
|
} catch (error) {
|
|
@@ -5780,6 +6073,7 @@ function stopOrKeepTheCopy(refusal2, source) {
|
|
|
5780
6073
|
function judgeAnswer(record, live) {
|
|
5781
6074
|
if (movedOn(record, live)) return "superseded";
|
|
5782
6075
|
if (mustAnswer()) return "answered";
|
|
6076
|
+
if (record.etag === void 0 && live !== void 0) return "unstamped";
|
|
5783
6077
|
if (live === void 0) {
|
|
5784
6078
|
verbose(`${record.tar} answered without an ETag \u2014 keeping the cached copy`);
|
|
5785
6079
|
}
|
|
@@ -6102,7 +6396,7 @@ function markInstalledAsPlugin(skill, primarySourceName, primarySourceType) {
|
|
|
6102
6396
|
}
|
|
6103
6397
|
async function collectPluginSkillIds(projectDir) {
|
|
6104
6398
|
const projectSkillIds = await pluginSkillIdsIn(projectDir);
|
|
6105
|
-
const globalSkillIds = isHomeDirectory(projectDir) ? [] : await pluginSkillIdsIn(os9.homedir());
|
|
6399
|
+
const globalSkillIds = isHomeDirectory(projectDir) ? [] : await pluginSkillIdsIn(os9.homedir(), inheritedGlobalProvider(projectDir));
|
|
6106
6400
|
if (globalSkillIds.length > 0) {
|
|
6107
6401
|
verbose(`Found ${globalSkillIds.length} global plugin skill(s)`);
|
|
6108
6402
|
return unique3([...projectSkillIds, ...globalSkillIds]);
|
|
@@ -6112,8 +6406,8 @@ async function collectPluginSkillIds(projectDir) {
|
|
|
6112
6406
|
}
|
|
6113
6407
|
return projectSkillIds;
|
|
6114
6408
|
}
|
|
6115
|
-
async function pluginSkillIdsIn(dir) {
|
|
6116
|
-
return typedKeys(await discoverAllPluginSkills(dir));
|
|
6409
|
+
async function pluginSkillIdsIn(dir, provider) {
|
|
6410
|
+
return typedKeys(await discoverAllPluginSkills(dir, provider));
|
|
6117
6411
|
}
|
|
6118
6412
|
function setActiveSources(matrix2) {
|
|
6119
6413
|
for (const [, skill] of typedEntries(matrix2.skills)) {
|
|
@@ -6164,9 +6458,9 @@ function agentNotFoundError(agentName, agents) {
|
|
|
6164
6458
|
}
|
|
6165
6459
|
function describeAvailableAgents(availableAgents) {
|
|
6166
6460
|
if (availableAgents.length === 0) return "No agents found in scanned directories";
|
|
6167
|
-
const
|
|
6461
|
+
const listed = availableAgents.slice(0, MAX_LISTED_AVAILABLE_AGENTS).join(", ");
|
|
6168
6462
|
const unlisted = availableAgents.length - MAX_LISTED_AVAILABLE_AGENTS;
|
|
6169
|
-
return unlisted > 0 ? `Available agents: ${
|
|
6463
|
+
return unlisted > 0 ? `Available agents: ${listed} (and ${unlisted} more)` : `Available agents: ${listed}`;
|
|
6170
6464
|
}
|
|
6171
6465
|
function assembleAgentConfig(agentName, definition, agentConfig, resolvedSkills) {
|
|
6172
6466
|
const model = agentConfig.model ?? definition.model;
|
|
@@ -6206,14 +6500,18 @@ function resolveDescription(loadedStack, shared) {
|
|
|
6206
6500
|
return shared ?? loadedStack?.description;
|
|
6207
6501
|
}
|
|
6208
6502
|
function configNameFor(projectDir) {
|
|
6209
|
-
return isHomeDirectory(projectDir) ? DEFAULT_PLUGIN_NAME :
|
|
6503
|
+
return isHomeDirectory(projectDir) ? DEFAULT_PLUGIN_NAME : path17.basename(projectDir);
|
|
6210
6504
|
}
|
|
6211
6505
|
async function loadCuratedStack(projectDir, projectStack) {
|
|
6212
|
-
const globalStack = isHomeDirectory(projectDir) ? void 0 : await loadGlobalStack();
|
|
6506
|
+
const globalStack = isHomeDirectory(projectDir) ? void 0 : await loadGlobalStack(projectDir);
|
|
6213
6507
|
return { ...globalStack, ...projectStack };
|
|
6214
6508
|
}
|
|
6215
|
-
async function loadGlobalStack() {
|
|
6216
|
-
|
|
6509
|
+
async function loadGlobalStack(projectDir) {
|
|
6510
|
+
const inherited = await loadProjectConfigFromDir(
|
|
6511
|
+
os10.homedir(),
|
|
6512
|
+
inheritedGlobalProvider(projectDir)
|
|
6513
|
+
);
|
|
6514
|
+
return inherited?.config.stack;
|
|
6217
6515
|
}
|
|
6218
6516
|
async function buildInstallConfig(selection, sourceResult, projectDir) {
|
|
6219
6517
|
const skillIds = unique4(selection.skills.map((s) => s.id));
|
|
@@ -6252,7 +6550,10 @@ async function loadSelectedStack(stackId, sourceResult) {
|
|
|
6252
6550
|
verbose(
|
|
6253
6551
|
`buildInstallConfig: loadedStack=${loadedStack ? `found (id='${loadedStack.id}')` : "NOT FOUND"}`
|
|
6254
6552
|
);
|
|
6255
|
-
if (!loadedStack)
|
|
6553
|
+
if (!loadedStack) {
|
|
6554
|
+
const shippedStackIds = sourceResult.matrix.suggestedStacks.map((stack) => stack.id);
|
|
6555
|
+
throw new Error(stackNotOfferedMessage(stackId, source, shippedStackIds));
|
|
6556
|
+
}
|
|
6256
6557
|
return loadedStack;
|
|
6257
6558
|
}
|
|
6258
6559
|
function stackToPreserve(loadedStack, curatedStack) {
|
|
@@ -6360,31 +6661,61 @@ async function describeScope(kind, root, provider) {
|
|
|
6360
6661
|
|
|
6361
6662
|
// src/cli/lib/installation/detect-installations.ts
|
|
6362
6663
|
async function detectInstallations(cwd) {
|
|
6664
|
+
const configs = await configsRead(cwd);
|
|
6665
|
+
return configs.filter((config) => !config.declaresNothingInAProject).map(asInstallation);
|
|
6666
|
+
}
|
|
6667
|
+
async function configsDeclaringNothing(cwd) {
|
|
6668
|
+
const configs = await configsRead(cwd);
|
|
6669
|
+
return configs.filter((config) => config.declaresNothingInAProject).map(asInstallation);
|
|
6670
|
+
}
|
|
6671
|
+
async function holdsAConfigDeclaringNothing(cwd) {
|
|
6672
|
+
return (await configsDeclaringNothing(cwd)).length > 0;
|
|
6673
|
+
}
|
|
6674
|
+
async function configsRead(cwd) {
|
|
6363
6675
|
const everywhereOneCouldBe = scopeRootsInPlay(cwd).flatMap(everyProviderUnder);
|
|
6364
|
-
const found = await Promise.all(everywhereOneCouldBe.map(
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
);
|
|
6676
|
+
const found = await Promise.all(everywhereOneCouldBe.map(configIfPresent));
|
|
6677
|
+
const onDisk = found.filter((config) => config !== null);
|
|
6678
|
+
return Promise.all(onDisk.map(withWhetherItDeclaresNothing));
|
|
6368
6679
|
}
|
|
6369
6680
|
function everyProviderUnder({ kind, root }) {
|
|
6370
6681
|
return PROVIDERS.map((provider) => ({
|
|
6371
6682
|
provider,
|
|
6372
6683
|
scope: kind,
|
|
6684
|
+
root,
|
|
6373
6685
|
configPath: getProjectConfigPath(root, provider)
|
|
6374
6686
|
}));
|
|
6375
6687
|
}
|
|
6376
|
-
async function
|
|
6688
|
+
async function configIfPresent(candidate) {
|
|
6377
6689
|
return await fileExists(candidate.configPath) ? candidate : null;
|
|
6378
6690
|
}
|
|
6691
|
+
async function withWhetherItDeclaresNothing(config) {
|
|
6692
|
+
return { ...config, declaresNothingInAProject: await declaresNothingInAProject(config) };
|
|
6693
|
+
}
|
|
6694
|
+
async function declaresNothingInAProject(config) {
|
|
6695
|
+
if (config.scope === "global") return false;
|
|
6696
|
+
const asRead = declaresNothingAsLastRead(config.configPath);
|
|
6697
|
+
if (asRead !== void 0) return asRead;
|
|
6698
|
+
try {
|
|
6699
|
+
const declaresNothing = await configDeclaresNothing(config.root, config.provider);
|
|
6700
|
+
recordWhetherItDeclaresNothing(config.configPath, declaresNothing);
|
|
6701
|
+
return declaresNothing;
|
|
6702
|
+
} catch (error) {
|
|
6703
|
+
if (error instanceof ConfigLoadError) return false;
|
|
6704
|
+
throw error;
|
|
6705
|
+
}
|
|
6706
|
+
}
|
|
6707
|
+
function asInstallation({ provider, scope, configPath }) {
|
|
6708
|
+
return { provider, scope, configPath };
|
|
6709
|
+
}
|
|
6379
6710
|
|
|
6380
6711
|
// src/cli/lib/hosts/claude-host.ts
|
|
6381
6712
|
import os12 from "os";
|
|
6382
|
-
import
|
|
6713
|
+
import path20 from "path";
|
|
6383
6714
|
import { z as z6 } from "zod";
|
|
6384
6715
|
|
|
6385
6716
|
// src/cli/utils/exec.ts
|
|
6386
6717
|
import { spawn } from "child_process";
|
|
6387
|
-
import
|
|
6718
|
+
import path18 from "path";
|
|
6388
6719
|
var MAX_PLUGIN_PATH_LENGTH = 1024;
|
|
6389
6720
|
var MAX_PLUGIN_NAME_LENGTH = 256;
|
|
6390
6721
|
var MAX_MARKETPLACE_SOURCE_LENGTH = 1024;
|
|
@@ -6392,7 +6723,7 @@ var SAFE_NAME_PATTERN2 = /^[a-zA-Z0-9._@/-]+$/;
|
|
|
6392
6723
|
var SAFE_PLUGIN_PATH_PATTERN = /^[a-zA-Z0-9._@/:~-]+$/;
|
|
6393
6724
|
var OPENS_A_RELATIVE_PATH = /^\.\.?[/\\]/;
|
|
6394
6725
|
function namesADirectoryOnThisMachine(source) {
|
|
6395
|
-
return
|
|
6726
|
+
return path18.isAbsolute(source) || OPENS_A_RELATIVE_PATH.test(source);
|
|
6396
6727
|
}
|
|
6397
6728
|
var CONTROL_CHAR_PATTERN = /[\x00-\x08\x0E-\x1F\x7F]/u;
|
|
6398
6729
|
function validatePluginPath(pluginPath) {
|
|
@@ -6482,7 +6813,7 @@ async function execCommand(command, args, options) {
|
|
|
6482
6813
|
}
|
|
6483
6814
|
|
|
6484
6815
|
// src/cli/lib/plugins/plugin-settings.ts
|
|
6485
|
-
import
|
|
6816
|
+
import path19 from "path";
|
|
6486
6817
|
import { z as z5 } from "zod";
|
|
6487
6818
|
var pluginSettingsSchema = z5.object({
|
|
6488
6819
|
// Absent when no plugin is enabled.
|
|
@@ -6503,7 +6834,7 @@ var installedPluginsSchema = z5.object({
|
|
|
6503
6834
|
plugins: z5.record(z5.string(), z5.array(pluginInstallationSchema))
|
|
6504
6835
|
}).passthrough();
|
|
6505
6836
|
function getInstalledPluginsRegistryPath(pluginsDir2) {
|
|
6506
|
-
return
|
|
6837
|
+
return path19.join(pluginsDir2, INSTALLED_PLUGINS_FILE);
|
|
6507
6838
|
}
|
|
6508
6839
|
async function readRegisteredInstallations(pluginsDir2) {
|
|
6509
6840
|
const registryPath = getInstalledPluginsRegistryPath(pluginsDir2);
|
|
@@ -6536,7 +6867,7 @@ async function listPluginInstallsForProject(pluginsDir2, projectDir) {
|
|
|
6536
6867
|
});
|
|
6537
6868
|
}
|
|
6538
6869
|
async function getEnabledPluginKeys(projectDir) {
|
|
6539
|
-
const settingsPath =
|
|
6870
|
+
const settingsPath = path19.join(projectDir, CLAUDE_DIR, STANDARD_FILES.SETTINGS_JSON);
|
|
6540
6871
|
if (!await fileExists(settingsPath)) {
|
|
6541
6872
|
verbose(`No settings.json found at '${settingsPath}'`);
|
|
6542
6873
|
return [];
|
|
@@ -6596,6 +6927,13 @@ async function claudePluginInstall(pluginPath, scope, projectDir, options) {
|
|
|
6596
6927
|
throw new Error(`Plugin installation failed: ${whyClaudeFailed(result)}`);
|
|
6597
6928
|
}
|
|
6598
6929
|
}
|
|
6930
|
+
async function claudePluginUpdate(pluginRef, scope, projectDir, options) {
|
|
6931
|
+
validatePluginName(pluginRef);
|
|
6932
|
+
const cwd = resolvePluginCwd(scope, projectDir);
|
|
6933
|
+
const args = ["plugin", "update", pluginRef, "--scope", scope];
|
|
6934
|
+
const result = await execCommand("claude", args, { cwd, ...configDirEnv(options) });
|
|
6935
|
+
if (result.exitCode !== 0) throw new Error(whyClaudeFailed(result));
|
|
6936
|
+
}
|
|
6599
6937
|
async function isClaudeCLIAvailable(options) {
|
|
6600
6938
|
try {
|
|
6601
6939
|
const result = await execCommand("claude", ["--version"], configDirEnv(options));
|
|
@@ -6684,7 +7022,7 @@ function namesNothingThere(message) {
|
|
|
6684
7022
|
return NOT_INSTALLED_MESSAGES.some((phrase) => message.includes(phrase));
|
|
6685
7023
|
}
|
|
6686
7024
|
function claudePluginsRoot(projectDir, options) {
|
|
6687
|
-
if (options?.configDir !== void 0) return
|
|
7025
|
+
if (options?.configDir !== void 0) return path20.join(options.configDir, PLUGINS_SUBDIR);
|
|
6688
7026
|
return pluginsDir(CLAUDE, "global", projectDir);
|
|
6689
7027
|
}
|
|
6690
7028
|
async function claudeListPlugins(projectDir, options) {
|
|
@@ -6712,6 +7050,7 @@ function claudeHost() {
|
|
|
6712
7050
|
addMarketplace: claudePluginMarketplaceAdd,
|
|
6713
7051
|
refreshMarketplace: claudePluginMarketplaceUpdate,
|
|
6714
7052
|
installPlugin: (pluginRef, scope, projectDir, options) => claudePluginInstall(pluginRef, toClaudePluginScope(scope), projectDir, options),
|
|
7053
|
+
updatePlugin: (pluginRef, scope, projectDir, options) => claudePluginUpdate(pluginRef, toClaudePluginScope(scope), projectDir, options),
|
|
6715
7054
|
uninstallPlugin: (pluginRef, scope, projectDir, options) => claudePluginUninstall(pluginRef, toClaudePluginScope(scope), projectDir, options),
|
|
6716
7055
|
listPlugins: claudeListPlugins
|
|
6717
7056
|
};
|
|
@@ -6719,8 +7058,89 @@ function claudeHost() {
|
|
|
6719
7058
|
|
|
6720
7059
|
// src/cli/lib/hosts/codex-host.ts
|
|
6721
7060
|
import os13 from "os";
|
|
6722
|
-
import
|
|
7061
|
+
import path21 from "path";
|
|
6723
7062
|
import { z as z7 } from "zod";
|
|
7063
|
+
|
|
7064
|
+
// src/cli/lib/hosts/codex-plugin-switch.ts
|
|
7065
|
+
import { partition as partition5 } from "remeda";
|
|
7066
|
+
function sayWhichAreLeftOn(leftOn) {
|
|
7067
|
+
return leftOn.map(({ pluginKey, reason }) => `Codex left ${pluginKey} switched on: ${reason}`).join("\n");
|
|
7068
|
+
}
|
|
7069
|
+
var PluginsLeftSwitchedOnError = class extends Error {
|
|
7070
|
+
constructor(leftOn) {
|
|
7071
|
+
super(sayWhichAreLeftOn(leftOn));
|
|
7072
|
+
this.leftOn = leftOn;
|
|
7073
|
+
}
|
|
7074
|
+
leftOn;
|
|
7075
|
+
};
|
|
7076
|
+
var FailedLeavingPluginsSwitchedOnError = class extends Error {
|
|
7077
|
+
constructor(failure, leftOn) {
|
|
7078
|
+
super(`${getErrorMessage(failure)}
|
|
7079
|
+
${sayWhichAreLeftOn(leftOn)}`, { cause: failure });
|
|
7080
|
+
this.failure = failure;
|
|
7081
|
+
this.leftOn = leftOn;
|
|
7082
|
+
}
|
|
7083
|
+
failure;
|
|
7084
|
+
leftOn;
|
|
7085
|
+
};
|
|
7086
|
+
var SWITCH = /^(\s*enabled\s*=\s*)(true|false)(\s*)$/;
|
|
7087
|
+
var SWITCHED_OFF = "false";
|
|
7088
|
+
var NOT_FOUND = -1;
|
|
7089
|
+
async function switchCodexPluginsOff(configFile, pluginKeys) {
|
|
7090
|
+
if (pluginKeys.length === 0) return;
|
|
7091
|
+
const leftOn = await pluginsStillSwitchedOn(configFile, pluginKeys);
|
|
7092
|
+
if (leftOn.length > 0) throw new PluginsLeftSwitchedOnError(leftOn);
|
|
7093
|
+
}
|
|
7094
|
+
async function pluginsStillSwitchedOn(configFile, pluginKeys) {
|
|
7095
|
+
const leftOn = (pluginKey, reason) => ({ pluginKey, configFile, reason });
|
|
7096
|
+
try {
|
|
7097
|
+
const config = await readFile(configFile);
|
|
7098
|
+
const switched = withSwitchesOff(config, pluginKeys);
|
|
7099
|
+
if (switched.config !== config) await writeFile(configFile, switched.config);
|
|
7100
|
+
return switched.unfound.map(
|
|
7101
|
+
(pluginKey) => leftOn(pluginKey, notSpelledAsCodexWritesIt(pluginKey))
|
|
7102
|
+
);
|
|
7103
|
+
} catch (error) {
|
|
7104
|
+
return pluginKeys.map((pluginKey) => leftOn(pluginKey, getErrorMessage(error)));
|
|
7105
|
+
}
|
|
7106
|
+
}
|
|
7107
|
+
function withSwitchesOff(config, pluginKeys) {
|
|
7108
|
+
const lines = config.split("\n");
|
|
7109
|
+
const located = pluginKeys.map((pluginKey) => ({
|
|
7110
|
+
pluginKey,
|
|
7111
|
+
at: switchLineOf(lines, pluginKey)
|
|
7112
|
+
}));
|
|
7113
|
+
const [found, unfound] = partition5(located, ({ at }) => at !== NOT_FOUND);
|
|
7114
|
+
const toSwitch = new Set(found.map(({ at }) => at));
|
|
7115
|
+
return {
|
|
7116
|
+
config: lines.map((line2, at) => toSwitch.has(at) ? switchedOff(line2) : line2).join("\n"),
|
|
7117
|
+
unfound: unfound.map(({ pluginKey }) => pluginKey)
|
|
7118
|
+
};
|
|
7119
|
+
}
|
|
7120
|
+
function switchLineOf(lines, pluginKey) {
|
|
7121
|
+
const header = lines.findIndex((line2) => line2.trim() === pluginTableHeader(pluginKey));
|
|
7122
|
+
if (header === NOT_FOUND) return NOT_FOUND;
|
|
7123
|
+
const end = endOfTable(lines, header);
|
|
7124
|
+
return lines.findIndex((line2, at) => at > header && at < end && SWITCH.test(line2));
|
|
7125
|
+
}
|
|
7126
|
+
function endOfTable(lines, header) {
|
|
7127
|
+
const nextTable = lines.findIndex((line2, at) => at > header && opensATable(line2));
|
|
7128
|
+
return nextTable === NOT_FOUND ? lines.length : nextTable;
|
|
7129
|
+
}
|
|
7130
|
+
function switchedOff(line2) {
|
|
7131
|
+
return line2.replace(SWITCH, `$1${SWITCHED_OFF}$3`);
|
|
7132
|
+
}
|
|
7133
|
+
function opensATable(line2) {
|
|
7134
|
+
return line2.trim().startsWith("[");
|
|
7135
|
+
}
|
|
7136
|
+
function pluginTableHeader(pluginKey) {
|
|
7137
|
+
return `[plugins."${pluginKey}"]`;
|
|
7138
|
+
}
|
|
7139
|
+
function notSpelledAsCodexWritesIt(pluginKey) {
|
|
7140
|
+
return `it has no ${pluginTableHeader(pluginKey)} table with an 'enabled' line`;
|
|
7141
|
+
}
|
|
7142
|
+
|
|
7143
|
+
// src/cli/lib/hosts/codex-host.ts
|
|
6724
7144
|
var CODEX = "codex";
|
|
6725
7145
|
var PLUGIN_CACHE_SUBDIR = "cache";
|
|
6726
7146
|
var STANDING_WARNING = /could not create PATH aliases/;
|
|
@@ -6803,7 +7223,7 @@ var pluginListingSchema = z7.object({
|
|
|
6803
7223
|
)
|
|
6804
7224
|
});
|
|
6805
7225
|
function codexPluginsRoot(projectDir, options) {
|
|
6806
|
-
if (options?.configDir !== void 0) return
|
|
7226
|
+
if (options?.configDir !== void 0) return path21.join(options.configDir, PLUGINS_SUBDIR);
|
|
6807
7227
|
return pluginsDir(CODEX, "global", projectDir);
|
|
6808
7228
|
}
|
|
6809
7229
|
async function isCodexCLIAvailable(options) {
|
|
@@ -6877,23 +7297,56 @@ async function upgradeGitMarketplace(name, options) {
|
|
|
6877
7297
|
);
|
|
6878
7298
|
}
|
|
6879
7299
|
async function reinstallEachPluginOf(name, options) {
|
|
6880
|
-
const
|
|
6881
|
-
|
|
6882
|
-
|
|
7300
|
+
const ofTheMarketplace = (await codexPluginListing(options, codexPluginCwd())).filter(
|
|
7301
|
+
(plugin) => plugin.marketplaceName === name
|
|
7302
|
+
);
|
|
7303
|
+
for (const [at, plugin] of ofTheMarketplace.entries()) {
|
|
7304
|
+
try {
|
|
7305
|
+
await installCodexPlugin(plugin.pluginId, options);
|
|
7306
|
+
} catch (refusal2) {
|
|
7307
|
+
await switchBackOffThenRethrow(refusal2, ofTheMarketplace.slice(0, at), options);
|
|
7308
|
+
}
|
|
6883
7309
|
}
|
|
7310
|
+
await switchBackOff(ofTheMarketplace, options);
|
|
7311
|
+
}
|
|
7312
|
+
async function switchBackOffThenRethrow(refusal2, added, options) {
|
|
7313
|
+
try {
|
|
7314
|
+
await switchBackOff(added, options);
|
|
7315
|
+
} catch (error) {
|
|
7316
|
+
if (!(error instanceof PluginsLeftSwitchedOnError)) throw error;
|
|
7317
|
+
throw new FailedLeavingPluginsSwitchedOnError(refusal2, error.leftOn);
|
|
7318
|
+
}
|
|
7319
|
+
throw refusal2;
|
|
7320
|
+
}
|
|
7321
|
+
async function switchBackOff(heldBefore, options) {
|
|
7322
|
+
await switchCodexPluginsOff(
|
|
7323
|
+
codexConfigFile(options),
|
|
7324
|
+
heldBefore.filter((plugin) => !plugin.enabled).map((plugin) => plugin.pluginId)
|
|
7325
|
+
);
|
|
7326
|
+
}
|
|
7327
|
+
function codexConfigFile(options) {
|
|
7328
|
+
return path21.join(codexStateRoot(options, codexPluginCwd()), CODEX_CONFIG_FILE);
|
|
6884
7329
|
}
|
|
6885
7330
|
async function codexInstallPlugin(pluginRef, _scope, _projectDir, options) {
|
|
6886
7331
|
await installCodexPlugin(pluginRef, options);
|
|
6887
7332
|
}
|
|
6888
|
-
async function installCodexPlugin(pluginRef, options) {
|
|
7333
|
+
async function installCodexPlugin(pluginRef, options, attempt = "install") {
|
|
6889
7334
|
validatePluginPath(pluginRef);
|
|
6890
7335
|
await runCodexOrThrow(
|
|
6891
7336
|
["plugin", "add", pluginRef, "--json"],
|
|
6892
7337
|
options,
|
|
6893
|
-
|
|
7338
|
+
`${attempt} ${pluginRef}`,
|
|
6894
7339
|
codexPluginCwd()
|
|
6895
7340
|
);
|
|
6896
7341
|
}
|
|
7342
|
+
async function codexUpdatePlugin(pluginRef, _scope, _projectDir, options) {
|
|
7343
|
+
const held = await heldPlugin(pluginRef, options);
|
|
7344
|
+
if (held === void 0) {
|
|
7345
|
+
throw new Error(`Failed to update ${pluginRef}: codex has not installed it`);
|
|
7346
|
+
}
|
|
7347
|
+
await installCodexPlugin(pluginRef, options, "update");
|
|
7348
|
+
await switchBackOff([held], options);
|
|
7349
|
+
}
|
|
6897
7350
|
async function codexUninstallPlugin(pluginRef, _scope, _projectDir, options) {
|
|
6898
7351
|
validatePluginName(pluginRef);
|
|
6899
7352
|
const wasInstalled = await isListed(pluginRef, options);
|
|
@@ -6911,8 +7364,11 @@ async function codexUninstallPlugin(pluginRef, _scope, _projectDir, options) {
|
|
|
6911
7364
|
return wasInstalled ? "removed" : "absent";
|
|
6912
7365
|
}
|
|
6913
7366
|
async function isListed(pluginRef, options) {
|
|
7367
|
+
return await heldPlugin(pluginRef, options) !== void 0;
|
|
7368
|
+
}
|
|
7369
|
+
async function heldPlugin(pluginRef, options) {
|
|
6914
7370
|
const installed = await codexPluginListing(options, codexPluginCwd());
|
|
6915
|
-
return installed.
|
|
7371
|
+
return installed.find((plugin) => plugin.pluginId === pluginRef);
|
|
6916
7372
|
}
|
|
6917
7373
|
async function codexPluginListing(options, askedFrom) {
|
|
6918
7374
|
const listing = await readJsonFromCodex(
|
|
@@ -6933,7 +7389,7 @@ async function codexListPlugins(projectDir, options) {
|
|
|
6933
7389
|
async function codexPluginsOrUnreadable(projectDir, options) {
|
|
6934
7390
|
const pluginsRoot = codexPluginsRoot(projectDir, options);
|
|
6935
7391
|
if (pluginsRoot === null) return { kind: "listed", plugins: [] };
|
|
6936
|
-
const cacheRoot2 =
|
|
7392
|
+
const cacheRoot2 = path21.join(pluginsRoot, PLUGIN_CACHE_SUBDIR);
|
|
6937
7393
|
let installed;
|
|
6938
7394
|
try {
|
|
6939
7395
|
installed = await codexPluginListing(options, projectDir);
|
|
@@ -6945,7 +7401,7 @@ async function codexPluginsOrUnreadable(projectDir, options) {
|
|
|
6945
7401
|
kind: "listed",
|
|
6946
7402
|
plugins: installed.map((plugin) => ({
|
|
6947
7403
|
pluginKey: plugin.pluginId,
|
|
6948
|
-
installPath:
|
|
7404
|
+
installPath: path21.join(cacheRoot2, plugin.marketplaceName, plugin.name, plugin.version),
|
|
6949
7405
|
enabled: plugin.enabled
|
|
6950
7406
|
}))
|
|
6951
7407
|
};
|
|
@@ -6960,6 +7416,7 @@ function codexHost() {
|
|
|
6960
7416
|
addMarketplace: codexMarketplaceAdd,
|
|
6961
7417
|
refreshMarketplace: refreshCodexMarketplace,
|
|
6962
7418
|
installPlugin: codexInstallPlugin,
|
|
7419
|
+
updatePlugin: codexUpdatePlugin,
|
|
6963
7420
|
uninstallPlugin: codexUninstallPlugin,
|
|
6964
7421
|
listPlugins: codexListPlugins
|
|
6965
7422
|
};
|
|
@@ -7225,10 +7682,11 @@ function sumCounts(scopeCounts) {
|
|
|
7225
7682
|
async function countCompiledAgentsPerScope(installation, scopes) {
|
|
7226
7683
|
return Promise.all(
|
|
7227
7684
|
scopes.map(async (scope) => {
|
|
7228
|
-
const
|
|
7685
|
+
const provider = providerAtScope(installation.projectDir, scope);
|
|
7686
|
+
const dir = agentsDir(provider, scope, installation.projectDir);
|
|
7229
7687
|
return {
|
|
7230
7688
|
dir,
|
|
7231
|
-
count: await countDirEntries(dir, isCompiledAgentFileIn(agentCodec(
|
|
7689
|
+
count: await countDirEntries(dir, isCompiledAgentFileIn(agentCodec(provider).extension))
|
|
7232
7690
|
};
|
|
7233
7691
|
})
|
|
7234
7692
|
);
|
|
@@ -7257,7 +7715,7 @@ function formatInstallationDisplay(info, skillLines = []) {
|
|
|
7257
7715
|
}
|
|
7258
7716
|
|
|
7259
7717
|
// src/cli/lib/plugins/plugin-validator.ts
|
|
7260
|
-
import
|
|
7718
|
+
import path22 from "path";
|
|
7261
7719
|
|
|
7262
7720
|
// src/cli/lib/validation-result.ts
|
|
7263
7721
|
function validResult() {
|
|
@@ -7289,15 +7747,15 @@ async function validatePluginStructure(pluginPath) {
|
|
|
7289
7747
|
if (!await directoryExists2(pluginPath)) {
|
|
7290
7748
|
return invalidResult(`Plugin directory does not exist: ${pluginPath}`);
|
|
7291
7749
|
}
|
|
7292
|
-
const pluginDir =
|
|
7750
|
+
const pluginDir = path22.join(pluginPath, PLUGIN_MANIFEST_DIR);
|
|
7293
7751
|
if (!await directoryExists2(pluginDir)) {
|
|
7294
7752
|
errors.push(`Missing ${PLUGIN_MANIFEST_DIR}/ directory`);
|
|
7295
7753
|
}
|
|
7296
|
-
const manifestPath =
|
|
7754
|
+
const manifestPath = path22.join(pluginDir, STANDARD_FILES.PLUGIN_JSON);
|
|
7297
7755
|
if (!await fileExists(manifestPath)) {
|
|
7298
7756
|
errors.push(`Missing ${PLUGIN_MANIFEST_DIR}/${STANDARD_FILES.PLUGIN_JSON}`);
|
|
7299
7757
|
}
|
|
7300
|
-
const readmePath =
|
|
7758
|
+
const readmePath = path22.join(pluginPath, STANDARD_FILES.README_MD);
|
|
7301
7759
|
if (!await fileExists(readmePath)) {
|
|
7302
7760
|
warnings.push("Missing README.md (recommended for documentation)");
|
|
7303
7761
|
}
|
|
@@ -7340,15 +7798,15 @@ async function validatePluginManifest(manifestPath) {
|
|
|
7340
7798
|
if (!manifest.description) {
|
|
7341
7799
|
warnings.push("Missing description field (recommended for discoverability)");
|
|
7342
7800
|
}
|
|
7343
|
-
const pluginDir =
|
|
7801
|
+
const pluginDir = path22.dirname(path22.dirname(manifestPath));
|
|
7344
7802
|
if (isNonEmptyString(manifest.skills)) {
|
|
7345
|
-
const skillsPath =
|
|
7803
|
+
const skillsPath = path22.join(pluginDir, manifest.skills);
|
|
7346
7804
|
if (!await directoryExists2(skillsPath)) {
|
|
7347
7805
|
errors.push(`Skills path does not exist: ${manifest.skills}`);
|
|
7348
7806
|
}
|
|
7349
7807
|
}
|
|
7350
7808
|
if (isNonEmptyString(manifest.agents)) {
|
|
7351
|
-
const agentsPath =
|
|
7809
|
+
const agentsPath = path22.join(pluginDir, manifest.agents);
|
|
7352
7810
|
if (!await directoryExists2(agentsPath)) {
|
|
7353
7811
|
errors.push(`Agents path does not exist: ${manifest.agents}`);
|
|
7354
7812
|
}
|
|
@@ -7417,14 +7875,14 @@ function prefixResult(result, prefix) {
|
|
|
7417
7875
|
async function validateEachFile(pluginPath, scannedDir, files, validate) {
|
|
7418
7876
|
const results = await Promise.all(
|
|
7419
7877
|
files.map(async (file) => {
|
|
7420
|
-
const filePath =
|
|
7421
|
-
return prefixResult(await validate(filePath),
|
|
7878
|
+
const filePath = path22.join(scannedDir, file);
|
|
7879
|
+
return prefixResult(await validate(filePath), path22.relative(pluginPath, filePath));
|
|
7422
7880
|
})
|
|
7423
7881
|
);
|
|
7424
7882
|
return mergeValidationResults(results);
|
|
7425
7883
|
}
|
|
7426
7884
|
async function validatePluginSkillFiles(pluginPath, skillsRelPath) {
|
|
7427
|
-
const skillsDir2 =
|
|
7885
|
+
const skillsDir2 = path22.join(pluginPath, skillsRelPath);
|
|
7428
7886
|
if (!await directoryExists2(skillsDir2)) return validResult();
|
|
7429
7887
|
const files = await glob(`**/${STANDARD_FILES.SKILL_MD}`, skillsDir2);
|
|
7430
7888
|
if (files.length === 0) {
|
|
@@ -7437,7 +7895,7 @@ async function validatePluginSkillFiles(pluginPath, skillsRelPath) {
|
|
|
7437
7895
|
return validateEachFile(pluginPath, skillsDir2, files, validateSkillFrontmatter);
|
|
7438
7896
|
}
|
|
7439
7897
|
async function validatePluginAgentFiles(pluginPath, agentsRelPath) {
|
|
7440
|
-
const agentsDir2 =
|
|
7898
|
+
const agentsDir2 = path22.join(pluginPath, agentsRelPath);
|
|
7441
7899
|
if (!await directoryExists2(agentsDir2)) return validResult();
|
|
7442
7900
|
const files = await glob(agentCodec("claude").listGlob, agentsDir2);
|
|
7443
7901
|
if (files.length === 0) {
|
|
@@ -7508,7 +7966,7 @@ async function listPluginDirNames(pluginsDir2) {
|
|
|
7508
7966
|
const dirChecks = await Promise.all(
|
|
7509
7967
|
allDirs.map(async (dirName) => ({
|
|
7510
7968
|
dirName,
|
|
7511
|
-
isPlugin: await directoryExists2(
|
|
7969
|
+
isPlugin: await directoryExists2(path22.join(pluginsDir2, dirName, PLUGIN_MANIFEST_DIR))
|
|
7512
7970
|
}))
|
|
7513
7971
|
);
|
|
7514
7972
|
return dirChecks.filter((c) => c.isPlugin).map((c) => c.dirName);
|
|
@@ -7516,7 +7974,7 @@ async function listPluginDirNames(pluginsDir2) {
|
|
|
7516
7974
|
async function validateEachPlugin(pluginsDir2, pluginDirs) {
|
|
7517
7975
|
const results = [];
|
|
7518
7976
|
for (const pluginName of pluginDirs) {
|
|
7519
|
-
const result = await validatePlugin(
|
|
7977
|
+
const result = await validatePlugin(path22.join(pluginsDir2, pluginName));
|
|
7520
7978
|
results.push({ name: pluginName, result });
|
|
7521
7979
|
}
|
|
7522
7980
|
return results;
|
|
@@ -7537,10 +7995,10 @@ function summarizePluginValidations(results) {
|
|
|
7537
7995
|
}
|
|
7538
7996
|
|
|
7539
7997
|
// src/cli/lib/plugins/plugin-discovery.ts
|
|
7540
|
-
async function getVerifiedPluginInstallPaths(projectDir) {
|
|
7998
|
+
async function getVerifiedPluginInstallPaths(projectDir, provider = providerInUse(projectDir)) {
|
|
7541
7999
|
let enabled;
|
|
7542
8000
|
try {
|
|
7543
|
-
const installed = await
|
|
8001
|
+
const installed = await hostFor(provider).listPlugins(projectDir);
|
|
7544
8002
|
enabled = installed.filter((plugin) => plugin.enabled).map(({ pluginKey, installPath }) => ({ pluginKey, installPath }));
|
|
7545
8003
|
} catch (error) {
|
|
7546
8004
|
verbose(`Failed to list installed plugins: ${getErrorMessage(error)}`);
|
|
@@ -7559,8 +8017,8 @@ async function hasManifestOnDisk({ pluginKey, installPath }) {
|
|
|
7559
8017
|
}
|
|
7560
8018
|
return manifestExists;
|
|
7561
8019
|
}
|
|
7562
|
-
async function discoverAllPluginSkills(projectDir) {
|
|
7563
|
-
const pluginPaths = await getVerifiedPluginInstallPaths(projectDir);
|
|
8020
|
+
async function discoverAllPluginSkills(projectDir, provider = providerInUse(projectDir)) {
|
|
8021
|
+
const pluginPaths = await getVerifiedPluginInstallPaths(projectDir, provider);
|
|
7564
8022
|
if (pluginPaths.length === 0) {
|
|
7565
8023
|
verbose(`No enabled plugins found for '${projectDir}'`);
|
|
7566
8024
|
return {};
|
|
@@ -7599,14 +8057,14 @@ function sanitizeSkillName(name) {
|
|
|
7599
8057
|
return name.replace(/\+/g, "-");
|
|
7600
8058
|
}
|
|
7601
8059
|
function isSingleFolderName(name) {
|
|
7602
|
-
return name !== "" && name !== "." && name !== ".." &&
|
|
8060
|
+
return name !== "" && name !== "." && name !== ".." && path23.basename(name) === name;
|
|
7603
8061
|
}
|
|
7604
|
-
async function assertClearable(pluginDir, skillPath,
|
|
7605
|
-
const skill = `Skill '${
|
|
7606
|
-
const file = `File: ${
|
|
7607
|
-
if (!isSingleFolderName(
|
|
8062
|
+
async function assertClearable(pluginDir, skillPath, skillName2) {
|
|
8063
|
+
const skill = `Skill '${path23.basename(skillPath)}'`;
|
|
8064
|
+
const file = `File: ${path23.join(skillPath, STANDARD_FILES.SKILL_MD)}`;
|
|
8065
|
+
if (!isSingleFolderName(skillName2)) {
|
|
7608
8066
|
throw new Error(
|
|
7609
|
-
`${skill} is named '${
|
|
8067
|
+
`${skill} is named '${skillName2}', which is not a single folder name, so its plugin cannot be built inside the output directory. ${file}`
|
|
7610
8068
|
);
|
|
7611
8069
|
}
|
|
7612
8070
|
if (isPathWithin(skillPath, pluginDir)) {
|
|
@@ -7616,12 +8074,12 @@ async function assertClearable(pluginDir, skillPath, skillName) {
|
|
|
7616
8074
|
}
|
|
7617
8075
|
if (await holdsContentNoBuildMade(pluginDir)) {
|
|
7618
8076
|
throw new Error(
|
|
7619
|
-
`${skill} would be built into ${pluginDir}, which holds no ${
|
|
8077
|
+
`${skill} would be built into ${pluginDir}, which holds no ${path23.join(PLUGIN_MANIFEST_DIR, PLUGIN_MANIFEST_FILE)} and is not empty, so no earlier build made it and a rebuild would delete what it holds. Move its contents out, or build into another output directory. ${file}`
|
|
7620
8078
|
);
|
|
7621
8079
|
}
|
|
7622
8080
|
}
|
|
7623
8081
|
async function readPluginSkillMetadata(skillPath) {
|
|
7624
|
-
const metadataPath =
|
|
8082
|
+
const metadataPath = path23.join(skillPath, STANDARD_FILES.METADATA_YAML);
|
|
7625
8083
|
if (!await fileExists(metadataPath)) {
|
|
7626
8084
|
return null;
|
|
7627
8085
|
}
|
|
@@ -7639,8 +8097,8 @@ async function readPluginSkillMetadata(skillPath) {
|
|
|
7639
8097
|
return null;
|
|
7640
8098
|
}
|
|
7641
8099
|
}
|
|
7642
|
-
function generateReadme(
|
|
7643
|
-
return `# ${
|
|
8100
|
+
function generateReadme(skillName2, frontmatter) {
|
|
8101
|
+
return `# ${skillName2}
|
|
7644
8102
|
|
|
7645
8103
|
${frontmatter.description}
|
|
7646
8104
|
|
|
@@ -7650,7 +8108,7 @@ Add this plugin to your Claude Code configuration:
|
|
|
7650
8108
|
|
|
7651
8109
|
\`\`\`json
|
|
7652
8110
|
{
|
|
7653
|
-
"plugins": ["${
|
|
8111
|
+
"plugins": ["${skillName2}"]
|
|
7654
8112
|
}
|
|
7655
8113
|
\`\`\`
|
|
7656
8114
|
|
|
@@ -7665,28 +8123,28 @@ This skill is automatically available when installed.
|
|
|
7665
8123
|
}
|
|
7666
8124
|
async function writeSkillFiles(skillPath, pluginSkillDir, skillMdContent) {
|
|
7667
8125
|
await ensureDir(pluginSkillDir);
|
|
7668
|
-
await writeFile(
|
|
8126
|
+
await writeFile(path23.join(pluginSkillDir, STANDARD_FILES.SKILL_MD), skillMdContent);
|
|
7669
8127
|
verbose(` Copied ${STANDARD_FILES.SKILL_MD}`);
|
|
7670
8128
|
for (const fileName of SKILL_CONTENT_FILES) {
|
|
7671
8129
|
if (fileName === STANDARD_FILES.SKILL_MD) continue;
|
|
7672
|
-
const sourcePath =
|
|
8130
|
+
const sourcePath = path23.join(skillPath, fileName);
|
|
7673
8131
|
if (await fileExists(sourcePath)) {
|
|
7674
8132
|
const content = await readFile(sourcePath);
|
|
7675
|
-
await writeFile(
|
|
8133
|
+
await writeFile(path23.join(pluginSkillDir, fileName), content);
|
|
7676
8134
|
verbose(` Copied ${fileName}`);
|
|
7677
8135
|
}
|
|
7678
8136
|
}
|
|
7679
8137
|
for (const dirName of SKILL_CONTENT_DIRS) {
|
|
7680
|
-
const sourceDir2 =
|
|
8138
|
+
const sourceDir2 = path23.join(skillPath, dirName);
|
|
7681
8139
|
if (await fileExists(sourceDir2)) {
|
|
7682
|
-
await copy(sourceDir2,
|
|
8140
|
+
await copy(sourceDir2, path23.join(pluginSkillDir, dirName));
|
|
7683
8141
|
verbose(` Copied ${dirName}/`);
|
|
7684
8142
|
}
|
|
7685
8143
|
}
|
|
7686
8144
|
}
|
|
7687
8145
|
async function readSkillMdToBuild(skillPath) {
|
|
7688
|
-
const dirBasename =
|
|
7689
|
-
const skillMdPath =
|
|
8146
|
+
const dirBasename = path23.basename(skillPath);
|
|
8147
|
+
const skillMdPath = path23.join(skillPath, STANDARD_FILES.SKILL_MD);
|
|
7690
8148
|
if (!await fileExists(skillMdPath)) {
|
|
7691
8149
|
throw new Error(
|
|
7692
8150
|
`Skill '${dirBasename}' is missing required ${STANDARD_FILES.SKILL_MD} file. Expected at: ${skillMdPath}`
|
|
@@ -7704,13 +8162,13 @@ async function readSkillMdToBuild(skillPath) {
|
|
|
7704
8162
|
async function compileSkillPlugin(options) {
|
|
7705
8163
|
const { skillPath, outputDir, skillName: overrideName } = options;
|
|
7706
8164
|
const { skillMdContent, frontmatter } = await readSkillMdToBuild(skillPath);
|
|
7707
|
-
const
|
|
7708
|
-
const pluginDir =
|
|
7709
|
-
await assertClearable(pluginDir, skillPath,
|
|
7710
|
-
verbose(`Compiling skill plugin: ${
|
|
8165
|
+
const skillName2 = overrideName ?? sanitizeSkillName(frontmatter.name);
|
|
8166
|
+
const pluginDir = path23.join(outputDir, skillName2);
|
|
8167
|
+
await assertClearable(pluginDir, skillPath, skillName2);
|
|
8168
|
+
verbose(`Compiling skill plugin: ${skillName2} from ${skillPath}`);
|
|
7711
8169
|
const metadata = await readPluginSkillMetadata(skillPath);
|
|
7712
8170
|
const manifestAt = (version) => generateSkillPluginManifest({
|
|
7713
|
-
skillName,
|
|
8171
|
+
skillName: skillName2,
|
|
7714
8172
|
description: frontmatter.description,
|
|
7715
8173
|
...metadata?.author !== void 0 && { author: metadata.author },
|
|
7716
8174
|
...metadata?.category !== void 0 && { category: metadata.category },
|
|
@@ -7722,21 +8180,21 @@ async function compileSkillPlugin(options) {
|
|
|
7722
8180
|
await writeContentHash(pluginDir, await computeSkillFolderHash(skillPath), getPluginManifestPath);
|
|
7723
8181
|
await writeSkillFiles(
|
|
7724
8182
|
skillPath,
|
|
7725
|
-
|
|
8183
|
+
path23.join(pluginDir, STANDARD_DIRS.SKILLS, skillName2),
|
|
7726
8184
|
skillMdContent
|
|
7727
8185
|
);
|
|
7728
|
-
const readme = generateReadme(
|
|
7729
|
-
await writeFile(
|
|
8186
|
+
const readme = generateReadme(skillName2, frontmatter);
|
|
8187
|
+
await writeFile(path23.join(pluginDir, STANDARD_FILES.README_MD), readme);
|
|
7730
8188
|
verbose(" Generated README.md");
|
|
7731
8189
|
const manifest = manifestAt(
|
|
7732
8190
|
versionAfterRebuild(earlierBuild, await computeShippedHash(pluginDir))
|
|
7733
8191
|
);
|
|
7734
8192
|
await writePluginManifest(pluginDir, manifest);
|
|
7735
|
-
verbose(` Wrote plugin.json for ${
|
|
8193
|
+
verbose(` Wrote plugin.json for ${skillName2} (v${manifest.version})`);
|
|
7736
8194
|
return {
|
|
7737
8195
|
pluginPath: pluginDir,
|
|
7738
8196
|
manifest,
|
|
7739
|
-
skillName
|
|
8197
|
+
skillName: skillName2
|
|
7740
8198
|
};
|
|
7741
8199
|
}
|
|
7742
8200
|
async function compileAllSkillPlugins(skillsDir2, outputDir) {
|
|
@@ -7744,7 +8202,7 @@ async function compileAllSkillPlugins(skillsDir2, outputDir) {
|
|
|
7744
8202
|
const failed = [];
|
|
7745
8203
|
const skillMdFiles = await glob(`**/${STANDARD_FILES.SKILL_MD}`, skillsDir2);
|
|
7746
8204
|
for (const skillMdFile of skillMdFiles) {
|
|
7747
|
-
const skillPath =
|
|
8205
|
+
const skillPath = path23.join(skillsDir2, path23.dirname(skillMdFile));
|
|
7748
8206
|
try {
|
|
7749
8207
|
const result = await compileSkillPlugin({
|
|
7750
8208
|
skillPath,
|
|
@@ -7754,7 +8212,7 @@ async function compileAllSkillPlugins(skillsDir2, outputDir) {
|
|
|
7754
8212
|
log(` [OK] ${result.skillName}`);
|
|
7755
8213
|
} catch (error) {
|
|
7756
8214
|
const errorMessage = getErrorMessage(error);
|
|
7757
|
-
const dirBasename =
|
|
8215
|
+
const dirBasename = path23.basename(skillPath);
|
|
7758
8216
|
failed.push(dirBasename);
|
|
7759
8217
|
warn(`Failed to compile skill from '${dirBasename}': ${errorMessage}`);
|
|
7760
8218
|
}
|
|
@@ -7770,7 +8228,7 @@ Compiled ${plural(results.length, "skill plugin")}:`);
|
|
|
7770
8228
|
}
|
|
7771
8229
|
|
|
7772
8230
|
// src/cli/lib/skills/local-skill-loader.ts
|
|
7773
|
-
import
|
|
8231
|
+
import path24 from "path";
|
|
7774
8232
|
function copiedInSkillsAt(root, provider) {
|
|
7775
8233
|
const scope = isHomeDirectory(root) ? "global" : "project";
|
|
7776
8234
|
return {
|
|
@@ -7796,9 +8254,9 @@ async function discoverLocalSkills(root, provider = providerInUse(root)) {
|
|
|
7796
8254
|
};
|
|
7797
8255
|
}
|
|
7798
8256
|
async function extractLocalSkill(localSkillsPath, pathPrefix, skillDirName) {
|
|
7799
|
-
const skillDir =
|
|
7800
|
-
const metadataPath =
|
|
7801
|
-
const skillMdPath =
|
|
8257
|
+
const skillDir = path24.join(localSkillsPath, skillDirName);
|
|
8258
|
+
const metadataPath = path24.join(skillDir, STANDARD_FILES.METADATA_YAML);
|
|
8259
|
+
const skillMdPath = path24.join(skillDir, STANDARD_FILES.SKILL_MD);
|
|
7802
8260
|
if (!await fileExists(metadataPath)) {
|
|
7803
8261
|
verbose(`Skipping local skill '${skillDirName}': No metadata.yaml found`);
|
|
7804
8262
|
return null;
|
|
@@ -7828,7 +8286,7 @@ async function extractLocalSkill(localSkillsPath, pathPrefix, skillDirName) {
|
|
|
7828
8286
|
return null;
|
|
7829
8287
|
}
|
|
7830
8288
|
const relativePath = `${pathPrefix}/${skillDirName}/`;
|
|
7831
|
-
const absolutePath =
|
|
8289
|
+
const absolutePath = path24.join(localSkillsPath, skillDirName) + path24.sep;
|
|
7832
8290
|
const skillId = frontmatter.name;
|
|
7833
8291
|
const extracted = {
|
|
7834
8292
|
id: skillId,
|
|
@@ -7851,7 +8309,7 @@ async function extractLocalSkill(localSkillsPath, pathPrefix, skillDirName) {
|
|
|
7851
8309
|
}
|
|
7852
8310
|
|
|
7853
8311
|
// src/cli/lib/skills/unresolved-skill-entries.ts
|
|
7854
|
-
import
|
|
8312
|
+
import path25 from "path";
|
|
7855
8313
|
async function findUnusableSavedSkillMetadata(savedSkills, projectDir) {
|
|
7856
8314
|
const verdicts = await Promise.all(
|
|
7857
8315
|
savedSkills.map((saved) => unusableMetadataOf(saved, projectDir))
|
|
@@ -7870,11 +8328,11 @@ function claimsLocalCopy(saved) {
|
|
|
7870
8328
|
}
|
|
7871
8329
|
function installedMetadataPath(saved, projectDir) {
|
|
7872
8330
|
const { skillsDir: skillsDir2 } = resolveInstallPaths(projectDir, saved.scope);
|
|
7873
|
-
return
|
|
8331
|
+
return path25.join(skillsDir2, saved.id, STANDARD_FILES.METADATA_YAML);
|
|
7874
8332
|
}
|
|
7875
8333
|
|
|
7876
8334
|
// src/cli/lib/skills/local-skill-mover.ts
|
|
7877
|
-
import
|
|
8335
|
+
import path26 from "path";
|
|
7878
8336
|
var THE_DIRECTORY_ITSELF = ".";
|
|
7879
8337
|
function validateSkillId(skillId) {
|
|
7880
8338
|
return !(skillId.length === 0 || skillId === THE_DIRECTORY_ITSELF || skillId.includes("\0") || skillId.includes("..") || skillId.includes("/") || skillId.includes("\\"));
|
|
@@ -7884,8 +8342,8 @@ async function deleteLocalSkill(projectDir, skillId, scope = "project") {
|
|
|
7884
8342
|
warn(`Invalid skill ID for deletion: '${skillId}'`);
|
|
7885
8343
|
return;
|
|
7886
8344
|
}
|
|
7887
|
-
const skillsDir2 =
|
|
7888
|
-
const skillPath =
|
|
8345
|
+
const skillsDir2 = path26.resolve(resolveInstallPaths(projectDir, scope).skillsDir);
|
|
8346
|
+
const skillPath = path26.resolve(skillsDir2, skillId);
|
|
7889
8347
|
if (!isPathWithin(skillPath, skillsDir2)) {
|
|
7890
8348
|
warn(`Skill ID '${skillId}' resolves outside the skills directory.`);
|
|
7891
8349
|
return;
|
|
@@ -7903,10 +8361,10 @@ async function migrateLocalSkillScope(skillId, fromScope, projectDir) {
|
|
|
7903
8361
|
return;
|
|
7904
8362
|
}
|
|
7905
8363
|
const toScope = fromScope === "global" ? "project" : "global";
|
|
7906
|
-
const fromSkillsDir =
|
|
7907
|
-
const toSkillsDir =
|
|
7908
|
-
const fromPath =
|
|
7909
|
-
const toPath =
|
|
8364
|
+
const fromSkillsDir = path26.resolve(resolveInstallPaths(projectDir, fromScope).skillsDir);
|
|
8365
|
+
const toSkillsDir = path26.resolve(resolveInstallPaths(projectDir, toScope).skillsDir);
|
|
8366
|
+
const fromPath = path26.resolve(fromSkillsDir, skillId);
|
|
8367
|
+
const toPath = path26.resolve(toSkillsDir, skillId);
|
|
7910
8368
|
if (!isPathWithin(fromPath, fromSkillsDir)) {
|
|
7911
8369
|
warn(`Skill ID '${skillId}' resolves outside the source skills directory.`);
|
|
7912
8370
|
return;
|
|
@@ -7933,7 +8391,7 @@ async function migrateLocalSkillScope(skillId, fromScope, projectDir) {
|
|
|
7933
8391
|
|
|
7934
8392
|
// src/cli/lib/matrix/matrix-loader.ts
|
|
7935
8393
|
import { parse as parseYaml5 } from "yaml";
|
|
7936
|
-
import
|
|
8394
|
+
import path27 from "path";
|
|
7937
8395
|
async function loadSkillCategories(configPath) {
|
|
7938
8396
|
const data = await loadConfig(configPath, skillCategoriesFileSchema);
|
|
7939
8397
|
if (!data) {
|
|
@@ -7942,10 +8400,18 @@ async function loadSkillCategories(configPath) {
|
|
|
7942
8400
|
verbose(`Loaded skill categories: ${configPath}`);
|
|
7943
8401
|
return data.categories;
|
|
7944
8402
|
}
|
|
8403
|
+
var NoSkillRulesError = class extends Error {
|
|
8404
|
+
constructor(configPath) {
|
|
8405
|
+
super(`Invalid skill rules at '${configPath}': failed to load or validate`);
|
|
8406
|
+
this.configPath = configPath;
|
|
8407
|
+
this.name = "NoSkillRulesError";
|
|
8408
|
+
}
|
|
8409
|
+
configPath;
|
|
8410
|
+
};
|
|
7945
8411
|
async function loadSkillRules(configPath) {
|
|
7946
8412
|
const data = await loadConfig(configPath, skillRulesFileSchema);
|
|
7947
8413
|
if (!data) {
|
|
7948
|
-
throw new
|
|
8414
|
+
throw new NoSkillRulesError(configPath);
|
|
7949
8415
|
}
|
|
7950
8416
|
const config = {
|
|
7951
8417
|
version: data.version,
|
|
@@ -7961,29 +8427,33 @@ async function loadSkillRules(configPath) {
|
|
|
7961
8427
|
}
|
|
7962
8428
|
async function extractAllSkills(skillsDir2) {
|
|
7963
8429
|
const metadataFiles = await glob(`**/${STANDARD_FILES.METADATA_YAML}`, skillsDir2);
|
|
7964
|
-
const
|
|
8430
|
+
const folders = [];
|
|
7965
8431
|
for (const metadataFile of metadataFiles) {
|
|
7966
|
-
|
|
7967
|
-
if (skill) skills.push(skill);
|
|
8432
|
+
folders.push(await readSkillFolder(skillsDir2, metadataFile));
|
|
7968
8433
|
}
|
|
7969
|
-
return
|
|
8434
|
+
return {
|
|
8435
|
+
skills: folders.flatMap((folder) => folder.kind === "loaded" ? [folder.skill] : []),
|
|
8436
|
+
unloadedSkillIds: folders.flatMap(
|
|
8437
|
+
(folder) => folder.kind === "unloaded" ? [folder.skillId] : []
|
|
8438
|
+
)
|
|
8439
|
+
};
|
|
7970
8440
|
}
|
|
7971
|
-
async function
|
|
7972
|
-
const skillDir =
|
|
7973
|
-
const skillMdPath =
|
|
8441
|
+
async function readSkillFolder(skillsDir2, metadataFile) {
|
|
8442
|
+
const skillDir = path27.dirname(metadataFile);
|
|
8443
|
+
const skillMdPath = path27.join(skillsDir2, skillDir, STANDARD_FILES.SKILL_MD);
|
|
7974
8444
|
if (!await fileExists(skillMdPath)) {
|
|
7975
|
-
|
|
7976
|
-
return
|
|
8445
|
+
warn(`Skipping '${metadataFile}': No ${STANDARD_FILES.SKILL_MD} found`);
|
|
8446
|
+
return { kind: "unnamed" };
|
|
7977
8447
|
}
|
|
7978
|
-
const metadata = await readMatrixMetadataOrWarn(metadataFile,
|
|
7979
|
-
if (!metadata) return
|
|
8448
|
+
const metadata = await readMatrixMetadataOrWarn(metadataFile, path27.join(skillsDir2, metadataFile));
|
|
8449
|
+
if (!metadata) return unloadedSkillNamedBy(skillMdPath);
|
|
7980
8450
|
const frontmatter = parseFrontmatterOrWarn(skillDir, skillMdPath, await readFile(skillMdPath));
|
|
7981
|
-
if (!frontmatter) return
|
|
8451
|
+
if (!frontmatter) return { kind: "unnamed" };
|
|
7982
8452
|
if (!metadata.displayName) {
|
|
7983
8453
|
warn(
|
|
7984
8454
|
`Skipping '${metadataFile}': invalid ${STANDARD_FILES.METADATA_YAML} \u2014 missing required field: ${METADATA_KEYS.DISPLAY_NAME}`
|
|
7985
8455
|
);
|
|
7986
|
-
return
|
|
8456
|
+
return { kind: "unloaded", skillId: frontmatter.name };
|
|
7987
8457
|
}
|
|
7988
8458
|
const skillId = frontmatter.name;
|
|
7989
8459
|
const extracted = {
|
|
@@ -8001,7 +8471,16 @@ async function extractSkill(skillsDir2, metadataFile) {
|
|
|
8001
8471
|
...metadata.custom === true ? { custom: true } : {}
|
|
8002
8472
|
};
|
|
8003
8473
|
verbose(`Extracted skill: ${skillId}`);
|
|
8004
|
-
return extracted;
|
|
8474
|
+
return { kind: "loaded", skill: extracted };
|
|
8475
|
+
}
|
|
8476
|
+
async function unloadedSkillNamedBy(skillMdPath) {
|
|
8477
|
+
try {
|
|
8478
|
+
const frontmatter = parseFrontmatter(await readFile(skillMdPath), skillMdPath);
|
|
8479
|
+
return frontmatter ? { kind: "unloaded", skillId: frontmatter.name } : { kind: "unnamed" };
|
|
8480
|
+
} catch (error) {
|
|
8481
|
+
verbose(`No skill named by '${skillMdPath}' \u2014 ${getErrorMessage(error)}`);
|
|
8482
|
+
return { kind: "unnamed" };
|
|
8483
|
+
}
|
|
8005
8484
|
}
|
|
8006
8485
|
async function readMatrixMetadataOrWarn(metadataFile, metadataPath) {
|
|
8007
8486
|
const metadataContent = await readFile(metadataPath);
|
|
@@ -8029,7 +8508,7 @@ function parseFrontmatterOrWarn(skillDir, skillMdPath, skillMdContent) {
|
|
|
8029
8508
|
const unreadable = unreadableFrontmatter(skillMdContent);
|
|
8030
8509
|
if (unreadable !== void 0) {
|
|
8031
8510
|
warn(
|
|
8032
|
-
`Skipping '${
|
|
8511
|
+
`Skipping '${path27.join(skillDir, STANDARD_FILES.SKILL_MD)}': unparseable ${STANDARD_FILES.SKILL_MD} frontmatter at ${skillMdPath} \u2014 ${unreadable}`
|
|
8033
8512
|
);
|
|
8034
8513
|
}
|
|
8035
8514
|
return null;
|
|
@@ -9911,17 +10390,29 @@ async function loadCatalogue(options) {
|
|
|
9911
10390
|
devMode = false,
|
|
9912
10391
|
matrixOnly = false
|
|
9913
10392
|
} = options;
|
|
9914
|
-
const
|
|
10393
|
+
const resolvedProjectDir = projectDir || process.cwd();
|
|
10394
|
+
const sourceConfig = await resolveSource({
|
|
10395
|
+
caller,
|
|
10396
|
+
flag: sourceFlag,
|
|
10397
|
+
projectDir: resolvedProjectDir,
|
|
10398
|
+
provider
|
|
10399
|
+
});
|
|
9915
10400
|
const { source } = sourceConfig;
|
|
9916
10401
|
verbose(`Loading skills from source: ${source}`);
|
|
9917
10402
|
const catalogue = await resolveBaseResult(sourceConfig, { purpose, devMode, matrixOnly });
|
|
9918
|
-
const resolvedProjectDir = projectDir || process.cwd();
|
|
9919
10403
|
const result = {
|
|
9920
10404
|
...catalogue,
|
|
9921
|
-
marketplaceSkillIds: new Set(typedKeys(catalogue.matrix.skills))
|
|
10405
|
+
marketplaceSkillIds: new Set(typedKeys(catalogue.matrix.skills)),
|
|
10406
|
+
marketplaceSkills: { ...catalogue.matrix.skills }
|
|
9922
10407
|
};
|
|
9923
|
-
|
|
9924
|
-
|
|
10408
|
+
const globalProvider = provider ?? inheritedGlobalProvider(resolvedProjectDir);
|
|
10409
|
+
result.matrix = await mergeLocalSkills(
|
|
10410
|
+
result.matrix,
|
|
10411
|
+
resolvedProjectDir,
|
|
10412
|
+
provider,
|
|
10413
|
+
globalProvider
|
|
10414
|
+
);
|
|
10415
|
+
await seatGlobalInstallationSkills(result.matrix, source, purpose, globalProvider);
|
|
9925
10416
|
if (!options.skipExtraSources) {
|
|
9926
10417
|
await loadSkillsFromAllSources(
|
|
9927
10418
|
result.matrix,
|
|
@@ -9935,16 +10426,20 @@ async function loadCatalogue(options) {
|
|
|
9935
10426
|
initializeMatrix(result.matrix);
|
|
9936
10427
|
return result;
|
|
9937
10428
|
}
|
|
9938
|
-
async function seatGlobalInstallationSkills(matrix2, loadedSource, purpose) {
|
|
9939
|
-
const uncarried = await globalPluginSkillsUncarried(matrix2);
|
|
10429
|
+
async function seatGlobalInstallationSkills(matrix2, loadedSource, purpose, globalProvider) {
|
|
10430
|
+
const uncarried = await globalPluginSkillsUncarried(matrix2, globalProvider);
|
|
9940
10431
|
if (uncarried.length === 0) return;
|
|
9941
|
-
const globalCatalogue = await loadGlobalInstallationCatalogue(
|
|
10432
|
+
const globalCatalogue = await loadGlobalInstallationCatalogue(
|
|
10433
|
+
loadedSource,
|
|
10434
|
+
purpose,
|
|
10435
|
+
globalProvider
|
|
10436
|
+
);
|
|
9942
10437
|
if (globalCatalogue === null) return;
|
|
9943
10438
|
for (const id of uncarried) seatFromCatalogue(matrix2, globalCatalogue, id);
|
|
9944
10439
|
}
|
|
9945
|
-
async function globalPluginSkillsUncarried(matrix2) {
|
|
10440
|
+
async function globalPluginSkillsUncarried(matrix2, globalProvider) {
|
|
9946
10441
|
try {
|
|
9947
|
-
const globalConfig = await loadGlobalSourceConfig();
|
|
10442
|
+
const globalConfig = await loadGlobalSourceConfig(globalProvider);
|
|
9948
10443
|
return (globalConfig?.skills ?? []).filter(isActivePlugin).filter((skill) => matrix2.skills[skill.id] === void 0).map((skill) => skill.id);
|
|
9949
10444
|
} catch (error) {
|
|
9950
10445
|
verbose(`Global installation's skills not seated \u2014 ${getErrorMessage(error)}`);
|
|
@@ -9954,9 +10449,13 @@ async function globalPluginSkillsUncarried(matrix2) {
|
|
|
9954
10449
|
function isActivePlugin(skill) {
|
|
9955
10450
|
return !skill.excluded && skill.origin !== EJECT_SOURCE;
|
|
9956
10451
|
}
|
|
9957
|
-
async function loadGlobalInstallationCatalogue(loadedSource, purpose) {
|
|
10452
|
+
async function loadGlobalInstallationCatalogue(loadedSource, purpose, globalProvider) {
|
|
9958
10453
|
try {
|
|
9959
|
-
const globalSourceConfig = await resolveSource({
|
|
10454
|
+
const globalSourceConfig = await resolveSource({
|
|
10455
|
+
caller: "stored",
|
|
10456
|
+
projectDir: os14.homedir(),
|
|
10457
|
+
provider: globalProvider
|
|
10458
|
+
});
|
|
9960
10459
|
if (globalSourceConfig.source === loadedSource) return null;
|
|
9961
10460
|
return await resolveBaseResult(globalSourceConfig, {
|
|
9962
10461
|
purpose,
|
|
@@ -10012,6 +10511,8 @@ async function resolveBaseResult(sourceConfig, { purpose, devMode, matrixOnly })
|
|
|
10012
10511
|
const fetched = matrixOnly ? NOTHING_FETCHED : await fetchFromSource(source);
|
|
10013
10512
|
return {
|
|
10014
10513
|
matrix: copyOfBuiltInMatrix(),
|
|
10514
|
+
// Checked when it was generated, so nothing in it failed to load.
|
|
10515
|
+
unloadedSkillIds: /* @__PURE__ */ new Set(),
|
|
10015
10516
|
sourceConfig,
|
|
10016
10517
|
sourcePath: fetched.path,
|
|
10017
10518
|
isLocal: false,
|
|
@@ -10082,8 +10583,8 @@ function goesOnWithoutManifest(source, purpose) {
|
|
|
10082
10583
|
function configuredLabel(sourceConfig) {
|
|
10083
10584
|
return sourceConfig.marketplace === void 0 ? {} : { marketplace: sourceConfig.marketplace };
|
|
10084
10585
|
}
|
|
10085
|
-
async function mergeLocalSkills(matrix2, projectDir, provider) {
|
|
10086
|
-
const withGlobal = isHomeDirectory(projectDir) ? matrix2 : await mergeDiscoveredLocalSkills(matrix2, os14.homedir(), "global",
|
|
10586
|
+
async function mergeLocalSkills(matrix2, projectDir, provider, globalProvider) {
|
|
10587
|
+
const withGlobal = isHomeDirectory(projectDir) ? matrix2 : await mergeDiscoveredLocalSkills(matrix2, os14.homedir(), "global", globalProvider);
|
|
10087
10588
|
return mergeDiscoveredLocalSkills(withGlobal, projectDir, "project", provider);
|
|
10088
10589
|
}
|
|
10089
10590
|
async function mergeDiscoveredLocalSkills(matrix2, dir, label, provider) {
|
|
@@ -10097,10 +10598,11 @@ async function mergeDiscoveredLocalSkills(matrix2, dir, label, provider) {
|
|
|
10097
10598
|
async function loadFromLocal(source, sourceConfig, purpose) {
|
|
10098
10599
|
const skillsPath = isLocalSource(source) ? (await fetchFromSource(source)).path : PROJECT_ROOT;
|
|
10099
10600
|
verbose(`Loading skills from local path: ${skillsPath}`);
|
|
10100
|
-
const
|
|
10601
|
+
const { matrix: matrix2, unloadedSkillIds } = await loadAndMergeFromBasePath(skillsPath, source);
|
|
10101
10602
|
const labels = await resolveMarketplaceLabels(skillsPath, sourceConfig, purpose);
|
|
10102
10603
|
return {
|
|
10103
|
-
matrix:
|
|
10604
|
+
matrix: matrix2,
|
|
10605
|
+
unloadedSkillIds,
|
|
10104
10606
|
sourceConfig,
|
|
10105
10607
|
sourcePath: skillsPath,
|
|
10106
10608
|
isLocal: true,
|
|
@@ -10112,10 +10614,11 @@ async function loadFromRemote(source, sourceConfig, purpose) {
|
|
|
10112
10614
|
verbose(`Fetching skills from remote source: ${source}`);
|
|
10113
10615
|
const fetchResult = await fetchFromSource(source);
|
|
10114
10616
|
verbose(`Fetched to: ${fetchResult.path}`);
|
|
10115
|
-
const
|
|
10617
|
+
const { matrix: matrix2, unloadedSkillIds } = await loadAndMergeFromBasePath(fetchResult.path, source);
|
|
10116
10618
|
const labels = await resolveMarketplaceLabels(source, sourceConfig, purpose);
|
|
10117
10619
|
return {
|
|
10118
|
-
matrix:
|
|
10620
|
+
matrix: matrix2,
|
|
10621
|
+
unloadedSkillIds,
|
|
10119
10622
|
sourceConfig,
|
|
10120
10623
|
sourcePath: fetchResult.path,
|
|
10121
10624
|
isLocal: false,
|
|
@@ -10124,7 +10627,7 @@ async function loadFromRemote(source, sourceConfig, purpose) {
|
|
|
10124
10627
|
};
|
|
10125
10628
|
}
|
|
10126
10629
|
async function loadMarketplaceMatrix(marketplaceDir) {
|
|
10127
|
-
const matrix2 = await loadAndMergeFromBasePath(marketplaceDir, marketplaceDir);
|
|
10630
|
+
const { matrix: matrix2 } = await loadAndMergeFromBasePath(marketplaceDir, marketplaceDir);
|
|
10128
10631
|
return { ...matrix2, categories: categoriesTheseSkillsAreIn(matrix2) };
|
|
10129
10632
|
}
|
|
10130
10633
|
function categoriesTheseSkillsAreIn(matrix2) {
|
|
@@ -10140,16 +10643,17 @@ async function loadAndMergeFromBasePath(basePath, source) {
|
|
|
10140
10643
|
const skillsDirRelPath = sourceProjectConfig?.skillsDir ?? SKILLS_DIR_PATH;
|
|
10141
10644
|
const stacksRelFile = sourceProjectConfig?.stacksFile;
|
|
10142
10645
|
const { categories, sourceRules } = await loadSourceTaxonomy(basePath);
|
|
10143
|
-
const skillsDir2 =
|
|
10646
|
+
const skillsDir2 = path28.join(basePath, skillsDirRelPath);
|
|
10144
10647
|
verbose(`Skills from source: ${skillsDir2}`);
|
|
10145
|
-
const skills = await extractAllSkills(skillsDir2);
|
|
10648
|
+
const { skills, unloadedSkillIds } = await extractAllSkills(skillsDir2);
|
|
10146
10649
|
await refuseCatalogueCollisions(basePath, source, skills);
|
|
10147
10650
|
const relationships = relationshipsForSource(skills, sourceRules);
|
|
10148
10651
|
const mergedMatrix = mergeMatrixWithSkills(categories, relationships, skills);
|
|
10149
|
-
initializeMatrix(mergedMatrix);
|
|
10150
10652
|
const stacks = await resolveOfferedStacks(basePath, stacksRelFile, source);
|
|
10151
|
-
mergedMatrix.suggestedStacks = stacks.map(
|
|
10152
|
-
|
|
10653
|
+
mergedMatrix.suggestedStacks = stacks.map(
|
|
10654
|
+
(stack) => convertStackToResolvedStack(stack, mergedMatrix)
|
|
10655
|
+
);
|
|
10656
|
+
return { matrix: mergedMatrix, unloadedSkillIds: new Set(unloadedSkillIds) };
|
|
10153
10657
|
}
|
|
10154
10658
|
async function loadSourceTaxonomy(basePath) {
|
|
10155
10659
|
const sourceCategories = await loadSourceCategories(basePath);
|
|
@@ -10163,7 +10667,7 @@ async function loadSourceTaxonomy(basePath) {
|
|
|
10163
10667
|
return { categories, sourceRules };
|
|
10164
10668
|
}
|
|
10165
10669
|
async function loadSourceCategories(basePath) {
|
|
10166
|
-
const categoriesPath =
|
|
10670
|
+
const categoriesPath = path28.join(basePath, SKILL_CATEGORIES_PATH);
|
|
10167
10671
|
if (!await fileExists(categoriesPath)) return void 0;
|
|
10168
10672
|
const categories = await loadSkillCategories(categoriesPath);
|
|
10169
10673
|
verbose(
|
|
@@ -10172,7 +10676,7 @@ async function loadSourceCategories(basePath) {
|
|
|
10172
10676
|
return categories;
|
|
10173
10677
|
}
|
|
10174
10678
|
async function loadSourceRules(basePath) {
|
|
10175
|
-
const rulesPath =
|
|
10679
|
+
const rulesPath = path28.join(basePath, SKILL_RULES_PATH);
|
|
10176
10680
|
if (!await fileExists(rulesPath)) return void 0;
|
|
10177
10681
|
const rules = await loadSkillRules(rulesPath);
|
|
10178
10682
|
verbose(`Loaded source rules: ${rulesPath}`);
|
|
@@ -10187,11 +10691,11 @@ async function refuseCatalogueCollisions(basePath, source, skills) {
|
|
|
10187
10691
|
throw new Error(catalogueCollisionError(collidingIds, source));
|
|
10188
10692
|
}
|
|
10189
10693
|
function catalogueCollisionError(collidingIds, source) {
|
|
10190
|
-
const
|
|
10191
|
-
const unlisted = collidingIds.length -
|
|
10694
|
+
const listed = collidingIds.slice(0, MAX_REPORTED_COLLISIONS).map((id) => ` ${id}`);
|
|
10695
|
+
const unlisted = collidingIds.length - listed.length;
|
|
10192
10696
|
return [
|
|
10193
10697
|
`Marketplace '${source}' ships ${collidingIds.length} skill id(s) the public catalogue already owns:`,
|
|
10194
|
-
...
|
|
10698
|
+
...listed,
|
|
10195
10699
|
...unlisted > 0 ? [` ... and ${unlisted} more`] : [],
|
|
10196
10700
|
`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.`
|
|
10197
10701
|
].join("\n");
|
|
@@ -10226,11 +10730,12 @@ function withdrawnSkillsWarning(stack, withdrawn) {
|
|
|
10226
10730
|
const named = withdrawn.map((id) => `'${id}'`).join(", ");
|
|
10227
10731
|
return `Stack '${stack.id}' names ${withdrawn.length} skill(s) this marketplace's catalogue does not carry: ${named}. Left out of the stack \u2014 selecting it would install nothing.`;
|
|
10228
10732
|
}
|
|
10229
|
-
function convertStackToResolvedStack(stack) {
|
|
10733
|
+
function convertStackToResolvedStack(stack, catalogue) {
|
|
10230
10734
|
const { declared, undeclared } = declaredAgentsIn(stack);
|
|
10231
10735
|
if (undeclared.length > 0) warn(undeclaredAgentsWarning(stack, undeclared));
|
|
10736
|
+
const isCarried = carriedBy(catalogue);
|
|
10232
10737
|
const agentConfigs = agentConfigsFor(stack, declared);
|
|
10233
|
-
const [allSkillIds, withdrawn] =
|
|
10738
|
+
const [allSkillIds, withdrawn] = partition6(namedSkillIds(agentConfigs), isCarried);
|
|
10234
10739
|
if (withdrawn.length > 0) warn(withdrawnSkillsWarning(stack, withdrawn));
|
|
10235
10740
|
verbose(
|
|
10236
10741
|
`Stack '${stack.id}' has ${allSkillIds.length} skills from ${agentConfigs.length} agents`
|
|
@@ -10239,7 +10744,7 @@ function convertStackToResolvedStack(stack) {
|
|
|
10239
10744
|
id: stack.id,
|
|
10240
10745
|
name: stack.name,
|
|
10241
10746
|
description: stack.description,
|
|
10242
|
-
skills: skillsByAgent(agentConfigs),
|
|
10747
|
+
skills: skillsByAgent(agentConfigs, isCarried),
|
|
10243
10748
|
allSkillIds,
|
|
10244
10749
|
philosophy: stack.philosophy || ""
|
|
10245
10750
|
};
|
|
@@ -10257,21 +10762,21 @@ function namedSkillIds(agentConfigs) {
|
|
|
10257
10762
|
)
|
|
10258
10763
|
);
|
|
10259
10764
|
}
|
|
10260
|
-
function
|
|
10261
|
-
return id in
|
|
10765
|
+
function carriedBy(catalogue) {
|
|
10766
|
+
return (id) => id in catalogue.skills;
|
|
10262
10767
|
}
|
|
10263
|
-
function skillsByAgent(agentConfigs) {
|
|
10768
|
+
function skillsByAgent(agentConfigs, isCarried) {
|
|
10264
10769
|
return typedFromEntries(
|
|
10265
10770
|
agentConfigs.map(
|
|
10266
|
-
({ agentId, agentConfig }) => [agentId, resolveStackAgentSkills(agentConfig)]
|
|
10771
|
+
({ agentId, agentConfig }) => [agentId, resolveStackAgentSkills(agentConfig, isCarried)]
|
|
10267
10772
|
)
|
|
10268
10773
|
);
|
|
10269
10774
|
}
|
|
10270
|
-
function resolveStackAgentSkills(agentConfig) {
|
|
10775
|
+
function resolveStackAgentSkills(agentConfig, isCarried) {
|
|
10271
10776
|
const carriedByCategory = typedEntries(agentConfig).map(
|
|
10272
10777
|
([category, assignments]) => (
|
|
10273
10778
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Object.entries drops the `| undefined` a Partial<Record> admits
|
|
10274
|
-
[category, (assignments ?? []).map((a) => a.id).filter(
|
|
10779
|
+
[category, (assignments ?? []).map((a) => a.id).filter(isCarried)]
|
|
10275
10780
|
)
|
|
10276
10781
|
);
|
|
10277
10782
|
return typedFromEntries(carriedByCategory.filter(([, ids]) => ids.length > 0));
|
|
@@ -10280,7 +10785,10 @@ function declaresCategory(matrix2, category) {
|
|
|
10280
10785
|
return category !== LOCAL_PSEUDO_CATEGORY && matrix2.categories[category] !== void 0;
|
|
10281
10786
|
}
|
|
10282
10787
|
function undeclaredCategoryRefusal(metadata, category) {
|
|
10283
|
-
|
|
10788
|
+
if (metadata.localPath === void 0) {
|
|
10789
|
+
throw new Error(`Local skill '${metadata.id}' was discovered without its directory`);
|
|
10790
|
+
}
|
|
10791
|
+
const metadataPath = path28.join(metadata.localPath, STANDARD_FILES.METADATA_YAML);
|
|
10284
10792
|
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.`;
|
|
10285
10793
|
}
|
|
10286
10794
|
function mergeLocalSkillsIntoMatrix(matrix2, localResult) {
|
|
@@ -10357,7 +10865,10 @@ export {
|
|
|
10357
10865
|
rootsHoldingTheRetiredSourceFolder,
|
|
10358
10866
|
chooseProviderForThisRun,
|
|
10359
10867
|
forgetTheProviderChosenForThisRun,
|
|
10868
|
+
declaresNoContent,
|
|
10360
10869
|
providerInUse,
|
|
10870
|
+
inheritedGlobalProvider,
|
|
10871
|
+
providerAtScope,
|
|
10361
10872
|
sourceFolderInUse,
|
|
10362
10873
|
relativeConfigPath,
|
|
10363
10874
|
sourceFolderName,
|
|
@@ -10390,9 +10901,10 @@ export {
|
|
|
10390
10901
|
skillAssignedToNoAgent,
|
|
10391
10902
|
scopeBlockedStackAssignment,
|
|
10392
10903
|
globalScopedAgentsHint,
|
|
10393
|
-
marketplacesRefreshed,
|
|
10394
10904
|
marketplaceRefreshFailed,
|
|
10395
10905
|
marketplacesRefreshFailed,
|
|
10906
|
+
codexPluginLeftSwitchedOn,
|
|
10907
|
+
pluginsLeftSwitchedOn,
|
|
10396
10908
|
marketplaceOwnerHasNoName,
|
|
10397
10909
|
marketplaceHasNoVersion,
|
|
10398
10910
|
marketplaceNameNotPublishable,
|
|
@@ -10414,10 +10926,13 @@ export {
|
|
|
10414
10926
|
sharedSkillsLeftBehind,
|
|
10415
10927
|
sharedConfigExistingInstall,
|
|
10416
10928
|
sharedConfigProjectScopeAtHome,
|
|
10417
|
-
|
|
10929
|
+
skippedSkills,
|
|
10418
10930
|
skippedUnknownAgents,
|
|
10419
10931
|
carriedSkillsWritten,
|
|
10932
|
+
copiedSkillsRemovedAfterRefusedSave,
|
|
10933
|
+
carriedSkillsRemovedAfterRefusedSave,
|
|
10420
10934
|
SHARED_CONFIG_APPLY,
|
|
10935
|
+
idNamesAnotherMarketplace,
|
|
10421
10936
|
registeredProjectsRecompiled,
|
|
10422
10937
|
sharedConfigNeedsTerminal,
|
|
10423
10938
|
nothingInstalledToApplyTo,
|
|
@@ -10475,7 +10990,9 @@ export {
|
|
|
10475
10990
|
formatZodIssues,
|
|
10476
10991
|
validateSkillMetadata,
|
|
10477
10992
|
splitMetadataValidationIssues,
|
|
10993
|
+
ConfigSchemaError,
|
|
10478
10994
|
ConfigDefaultExportError,
|
|
10995
|
+
ConfigEvaluationError,
|
|
10479
10996
|
NO_VALID_DEFAULT_EXPORT,
|
|
10480
10997
|
configLoadFailure,
|
|
10481
10998
|
loadConfig,
|
|
@@ -10494,21 +11011,23 @@ export {
|
|
|
10494
11011
|
MarketplaceRefusedError,
|
|
10495
11012
|
isLocalSource,
|
|
10496
11013
|
normalizeStackRecord,
|
|
11014
|
+
StacksLoadError,
|
|
10497
11015
|
loadStackById,
|
|
10498
11016
|
defaultUsageGuidance,
|
|
10499
11017
|
resolveAgentConfigToSkills,
|
|
10500
11018
|
getStackSkillIds,
|
|
10501
11019
|
ConfigLoadError,
|
|
11020
|
+
configDeclaresNothing,
|
|
10502
11021
|
loadProjectConfigFromDir,
|
|
10503
11022
|
loadProjectConfigAt,
|
|
10504
11023
|
loadInstalledConfig,
|
|
10505
|
-
|
|
11024
|
+
configsInPlay,
|
|
10506
11025
|
findConfigLoadFailures,
|
|
10507
11026
|
loadProjectConfig,
|
|
11027
|
+
loadConfigInPlay,
|
|
10508
11028
|
validateProjectConfig,
|
|
10509
11029
|
INSTALL_MODE_LABELS,
|
|
10510
11030
|
INSTALL_MODE_DESCRIPTIONS,
|
|
10511
|
-
declaresNoContent,
|
|
10512
11031
|
deriveInstallMode,
|
|
10513
11032
|
detectProjectInstallation,
|
|
10514
11033
|
holdsItsOwnInstallation,
|
|
@@ -10550,6 +11069,7 @@ export {
|
|
|
10550
11069
|
readLocalSkillMetadata,
|
|
10551
11070
|
validateSkillPath,
|
|
10552
11071
|
copySkillsToLocalFlattened,
|
|
11072
|
+
copyMarketplaceSkillsToLocalFlattened,
|
|
10553
11073
|
getUserPluginsDir,
|
|
10554
11074
|
getProjectPluginsDir,
|
|
10555
11075
|
getPluginAgentsDir,
|
|
@@ -10576,9 +11096,12 @@ export {
|
|
|
10576
11096
|
execCommand,
|
|
10577
11097
|
getInstalledPluginsRegistryPath,
|
|
10578
11098
|
listRegisteredPluginInstalls,
|
|
11099
|
+
PluginsLeftSwitchedOnError,
|
|
11100
|
+
FailedLeavingPluginsSwitchedOnError,
|
|
10579
11101
|
refuseUnofferedPlacements,
|
|
10580
11102
|
hostFor,
|
|
10581
11103
|
hostAt,
|
|
11104
|
+
getVerifiedPluginInstallPaths,
|
|
10582
11105
|
discoverAllPluginSkills,
|
|
10583
11106
|
listPluginNames,
|
|
10584
11107
|
compileSkillPlugin,
|
|
@@ -10587,6 +11110,7 @@ export {
|
|
|
10587
11110
|
findUnusableSavedSkillMetadata,
|
|
10588
11111
|
deleteLocalSkill,
|
|
10589
11112
|
migrateLocalSkillScope,
|
|
11113
|
+
NoSkillRulesError,
|
|
10590
11114
|
claimSlug,
|
|
10591
11115
|
matrixHealthIssues,
|
|
10592
11116
|
generateMarketplace,
|
|
@@ -10613,6 +11137,8 @@ export {
|
|
|
10613
11137
|
sourceScopesInPlay,
|
|
10614
11138
|
scopeRootsInPlay,
|
|
10615
11139
|
detectInstallations,
|
|
11140
|
+
configsDeclaringNothing,
|
|
11141
|
+
holdsAConfigDeclaringNothing,
|
|
10616
11142
|
ejectCopyFailureError,
|
|
10617
11143
|
copyingBesideGlobalPlugins,
|
|
10618
11144
|
installingBesideGlobalCopies,
|
|
@@ -10620,4 +11146,4 @@ export {
|
|
|
10620
11146
|
executeMigration,
|
|
10621
11147
|
keepsGlobalInstall
|
|
10622
11148
|
};
|
|
10623
|
-
//# sourceMappingURL=chunk-
|
|
11149
|
+
//# sourceMappingURL=chunk-ENET4IF6.js.map
|