agents-inc 0.156.0 → 0.157.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/dist/{chunk-PMUABBPG.js → chunk-24IGOJ7E.js} +9 -13
- package/dist/chunk-24IGOJ7E.js.map +1 -0
- package/dist/{chunk-H6UTKTLK.js → chunk-3E52LCYC.js} +98 -34
- package/dist/chunk-3E52LCYC.js.map +1 -0
- package/dist/{chunk-UMH2YZKG.js → chunk-4CIAZOTD.js} +4 -6
- package/dist/{chunk-UMH2YZKG.js.map → chunk-4CIAZOTD.js.map} +1 -1
- package/dist/{chunk-D2CAV7MG.js → chunk-4DMFK67G.js} +2 -2
- package/dist/{chunk-PNMM5HNX.js → chunk-4EPJWN7C.js} +9 -10
- package/dist/chunk-4EPJWN7C.js.map +1 -0
- package/dist/{chunk-MHRC3KRX.js → chunk-4KHUHPX4.js} +3 -3
- package/dist/chunk-4KHUHPX4.js.map +1 -0
- package/dist/{chunk-2H7UWE2G.js → chunk-CEGHC7L5.js} +13 -12
- package/dist/chunk-CEGHC7L5.js.map +1 -0
- package/dist/{chunk-MO5FGLVS.js → chunk-DCIC35R7.js} +11 -12
- package/dist/chunk-DCIC35R7.js.map +1 -0
- package/dist/{chunk-ILUVL7WM.js → chunk-DZD25NHW.js} +2 -2
- package/dist/{chunk-IO5YBKHV.js → chunk-G6G2FT2G.js} +10 -8
- package/dist/chunk-G6G2FT2G.js.map +1 -0
- package/dist/{chunk-RWOYPOMR.js → chunk-IGOFZG3S.js} +19 -5
- package/dist/chunk-IGOFZG3S.js.map +1 -0
- package/dist/{chunk-CLFHC2DK.js → chunk-K2VTGT45.js} +17 -18
- package/dist/chunk-K2VTGT45.js.map +1 -0
- package/dist/{chunk-YO2LTXB4.js → chunk-KTXVQ7OM.js} +115 -10
- package/dist/chunk-KTXVQ7OM.js.map +1 -0
- package/dist/{chunk-SY4X2LND.js → chunk-KUZBMEWR.js} +3 -3
- package/dist/{chunk-ZUAZHDKP.js → chunk-LDBVIPTP.js} +277 -40
- package/dist/chunk-LDBVIPTP.js.map +1 -0
- package/dist/{chunk-PCC4LBKB.js → chunk-MU2TQ5KK.js} +2 -2
- package/dist/{chunk-4G2YHZIM.js → chunk-O4K42CJB.js} +4 -6
- package/dist/{chunk-4G2YHZIM.js.map → chunk-O4K42CJB.js.map} +1 -1
- package/dist/{chunk-HXKOIWLU.js → chunk-P6OKOH62.js} +2 -6
- package/dist/chunk-P6OKOH62.js.map +1 -0
- package/dist/{chunk-IXRZAHZF.js → chunk-PGZSNNDA.js} +4 -4
- package/dist/{chunk-LJCNUP6M.js → chunk-PVPNVK3W.js} +2 -2
- package/dist/{chunk-4A4L3YIW.js → chunk-PXGDA2SY.js} +6 -7
- package/dist/chunk-PXGDA2SY.js.map +1 -0
- package/dist/{chunk-G47TDUK3.js → chunk-QNAMXQKF.js} +2 -2
- package/dist/{chunk-5OKLCVM4.js → chunk-QO4XD5V5.js} +3 -3
- package/dist/{chunk-5F2YR5XV.js → chunk-QXPMQBEI.js} +15 -5
- package/dist/chunk-QXPMQBEI.js.map +1 -0
- package/dist/{chunk-F5T6TV4Z.js → chunk-RLU2IXD3.js} +40 -14
- package/dist/chunk-RLU2IXD3.js.map +1 -0
- package/dist/{chunk-WCAUUBHO.js → chunk-RVXI5VQU.js} +14758 -14501
- package/dist/chunk-RVXI5VQU.js.map +1 -0
- package/dist/{chunk-WFRTHH3A.js → chunk-TZRXWFF5.js} +3 -3
- package/dist/{chunk-HG4JPXJT.js → chunk-V7PMQDDU.js} +23 -10
- package/dist/chunk-V7PMQDDU.js.map +1 -0
- package/dist/{chunk-Y5Y7XHVU.js → chunk-VI6U2PJG.js} +4 -4
- package/dist/{chunk-TBOVYLQJ.js → chunk-VTIDLRNA.js} +2 -2
- package/dist/{chunk-OKH64JZE.js → chunk-WCZNQECQ.js} +2 -2
- package/dist/{chunk-QWVXOCXO.js → chunk-WENBESZO.js} +7 -9
- package/dist/{chunk-QWVXOCXO.js.map → chunk-WENBESZO.js.map} +1 -1
- package/dist/{chunk-TNGMF4UX.js → chunk-XOZAKFTS.js} +12 -12
- package/dist/chunk-XOZAKFTS.js.map +1 -0
- package/dist/{chunk-6RLV45HN.js → chunk-YG5YPVTJ.js} +6 -6
- package/dist/chunk-YG5YPVTJ.js.map +1 -0
- package/dist/{chunk-HTYH7CKN.js → chunk-YKFCUVHG.js} +3 -3
- package/dist/{chunk-VPASBA7P.js → chunk-YXI3BTQQ.js} +2 -2
- package/dist/chunk-Z3H2K5PK.js +1352 -0
- package/dist/chunk-Z3H2K5PK.js.map +1 -0
- package/dist/{chunk-5QZAK6J7.js → chunk-ZAQ6WMJV.js} +50 -18
- package/dist/chunk-ZAQ6WMJV.js.map +1 -0
- package/dist/{chunk-GS2NZ3GD.js → chunk-ZBYVDXZG.js} +5 -5
- package/dist/chunk-ZBYVDXZG.js.map +1 -0
- package/dist/commands/build/marketplace.js +9 -12
- package/dist/commands/build/marketplace.js.map +1 -1
- package/dist/commands/build/plugins.js +9 -12
- package/dist/commands/build/plugins.js.map +1 -1
- package/dist/commands/compile.js +15 -19
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/doctor.js +75 -54
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/edit.js +154 -109
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/eject.js +68 -38
- package/dist/commands/eject.js.map +1 -1
- package/dist/commands/init.js +36 -39
- package/dist/commands/list.js +8 -10
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/new/marketplace.js +8 -11
- package/dist/commands/new/marketplace.js.map +1 -1
- package/dist/commands/search.js +10 -13
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/share.js +8 -10
- package/dist/commands/share.js.map +1 -1
- package/dist/commands/uninstall.js +39 -28
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.js +7 -10
- package/dist/commands/update.js.map +1 -1
- package/dist/components/common/removal-plan-confirm.js +2 -2
- package/dist/components/common/select-list.js +2 -2
- package/dist/components/wizard/category-grid.js +4 -5
- package/dist/components/wizard/checkbox-grid.js +4 -4
- package/dist/components/wizard/domain-selection.js +9 -11
- package/dist/components/wizard/run-wizard-session.js +26 -28
- package/dist/components/wizard/scroll-affordance.js +2 -2
- package/dist/components/wizard/skill-agent-summary.js +5 -7
- package/dist/components/wizard/source-grid.js +5 -6
- package/dist/components/wizard/stack-selection.js +7 -9
- package/dist/components/wizard/step-agents.js +8 -10
- package/dist/components/wizard/step-build.js +7 -9
- package/dist/components/wizard/step-confirm.js +9 -11
- package/dist/components/wizard/step-sources.js +8 -10
- package/dist/components/wizard/step-stack.js +8 -10
- package/dist/components/wizard/summary-panel.js +8 -10
- package/dist/components/wizard/toast.js +2 -2
- package/dist/components/wizard/wizard-layout.js +12 -14
- package/dist/components/wizard/wizard-tabs.js +2 -4
- package/dist/components/wizard/wizard.js +25 -27
- package/dist/hooks/init.js +36 -39
- package/dist/hooks/init.js.map +1 -1
- package/dist/load-agent-defs-M6LRVISN.js +13 -0
- package/dist/recompile-project-agents-G7ZM25PC.js +17 -0
- package/dist/{source-loader-3DL3NYUX.js → source-loader-BIAEDR5R.js} +4 -8
- package/dist/src/agents/_templates/agent.liquid +2 -2
- package/dist/stores/wizard-store.js +6 -6
- package/package.json +1 -1
- package/src/agents/_templates/agent.liquid +2 -2
- package/src/schemas/custom-metadata.schema.json +1 -1
- package/src/schemas/marketplace.schema.json +2 -1
- package/dist/chunk-2H7UWE2G.js.map +0 -1
- package/dist/chunk-4A4L3YIW.js.map +0 -1
- package/dist/chunk-5F2YR5XV.js.map +0 -1
- package/dist/chunk-5QZAK6J7.js.map +0 -1
- package/dist/chunk-6RLV45HN.js.map +0 -1
- package/dist/chunk-CLFHC2DK.js.map +0 -1
- package/dist/chunk-F2AMBBIA.js +0 -64
- package/dist/chunk-F2AMBBIA.js.map +0 -1
- package/dist/chunk-F5T6TV4Z.js.map +0 -1
- package/dist/chunk-GS2NZ3GD.js.map +0 -1
- package/dist/chunk-H6UTKTLK.js.map +0 -1
- package/dist/chunk-HG4JPXJT.js.map +0 -1
- package/dist/chunk-HXKOIWLU.js.map +0 -1
- package/dist/chunk-IO5YBKHV.js.map +0 -1
- package/dist/chunk-MHRC3KRX.js.map +0 -1
- package/dist/chunk-MO5FGLVS.js.map +0 -1
- package/dist/chunk-PMUABBPG.js.map +0 -1
- package/dist/chunk-PNMM5HNX.js.map +0 -1
- package/dist/chunk-RWOYPOMR.js.map +0 -1
- package/dist/chunk-TNGMF4UX.js.map +0 -1
- package/dist/chunk-VASLW636.js +0 -23
- package/dist/chunk-VASLW636.js.map +0 -1
- package/dist/chunk-WCAUUBHO.js.map +0 -1
- package/dist/chunk-WUQ3MTPF.js +0 -173
- package/dist/chunk-WUQ3MTPF.js.map +0 -1
- package/dist/chunk-XAXJWXI4.js +0 -1667
- package/dist/chunk-XAXJWXI4.js.map +0 -1
- package/dist/chunk-YO2LTXB4.js.map +0 -1
- package/dist/chunk-ZUAZHDKP.js.map +0 -1
- package/dist/copy-local-skills-NSUTBVLJ.js +0 -14
- package/dist/load-agent-defs-X4E57I2U.js +0 -15
- package/dist/loader-G6BFG2VA.js +0 -24
- package/dist/recompile-project-agents-JE4MM72I.js +0 -20
- package/dist/recompile-project-agents-JE4MM72I.js.map +0 -1
- package/dist/source-loader-3DL3NYUX.js.map +0 -1
- /package/dist/{chunk-D2CAV7MG.js.map → chunk-4DMFK67G.js.map} +0 -0
- /package/dist/{chunk-ILUVL7WM.js.map → chunk-DZD25NHW.js.map} +0 -0
- /package/dist/{chunk-SY4X2LND.js.map → chunk-KUZBMEWR.js.map} +0 -0
- /package/dist/{chunk-PCC4LBKB.js.map → chunk-MU2TQ5KK.js.map} +0 -0
- /package/dist/{chunk-IXRZAHZF.js.map → chunk-PGZSNNDA.js.map} +0 -0
- /package/dist/{chunk-LJCNUP6M.js.map → chunk-PVPNVK3W.js.map} +0 -0
- /package/dist/{chunk-G47TDUK3.js.map → chunk-QNAMXQKF.js.map} +0 -0
- /package/dist/{chunk-5OKLCVM4.js.map → chunk-QO4XD5V5.js.map} +0 -0
- /package/dist/{chunk-WFRTHH3A.js.map → chunk-TZRXWFF5.js.map} +0 -0
- /package/dist/{chunk-Y5Y7XHVU.js.map → chunk-VI6U2PJG.js.map} +0 -0
- /package/dist/{chunk-TBOVYLQJ.js.map → chunk-VTIDLRNA.js.map} +0 -0
- /package/dist/{chunk-OKH64JZE.js.map → chunk-WCZNQECQ.js.map} +0 -0
- /package/dist/{chunk-HTYH7CKN.js.map → chunk-YKFCUVHG.js.map} +0 -0
- /package/dist/{chunk-VPASBA7P.js.map → chunk-YXI3BTQQ.js.map} +0 -0
- /package/dist/{copy-local-skills-NSUTBVLJ.js.map → load-agent-defs-M6LRVISN.js.map} +0 -0
- /package/dist/{load-agent-defs-X4E57I2U.js.map → recompile-project-agents-G7ZM25PC.js.map} +0 -0
- /package/dist/{loader-G6BFG2VA.js.map → source-loader-BIAEDR5R.js.map} +0 -0
|
@@ -1,55 +1,112 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
agentFrontmatterValidationSchema,
|
|
3
4
|
buildCompileAgents,
|
|
4
5
|
computeStringHash,
|
|
5
|
-
|
|
6
|
+
copy,
|
|
6
7
|
determinePluginVersion,
|
|
8
|
+
directoryExists,
|
|
7
9
|
discoverAllPluginSkills,
|
|
8
10
|
effectivelyExcludedSkillIds,
|
|
9
|
-
fetchFromSource,
|
|
10
|
-
generateAgentPluginManifest,
|
|
11
|
-
getPluginAgentsDir,
|
|
12
|
-
getPluginManifestPath,
|
|
13
|
-
hasProvenanceMarker,
|
|
14
|
-
loadProjectConfig,
|
|
15
|
-
loadProjectSourceConfig,
|
|
16
|
-
resolveAgents,
|
|
17
|
-
writeCompiledAgentsByScope,
|
|
18
|
-
writeContentHash,
|
|
19
|
-
writePluginManifest
|
|
20
|
-
} from "./chunk-WCAUUBHO.js";
|
|
21
|
-
import {
|
|
22
|
-
agentFrontmatterValidationSchema,
|
|
23
|
-
copy,
|
|
24
|
-
directoryExists,
|
|
25
11
|
ensureDir,
|
|
26
12
|
extractFrontmatter,
|
|
13
|
+
fetchFromSource,
|
|
14
|
+
fileExists,
|
|
27
15
|
formatZodIssues,
|
|
16
|
+
generateAgentPluginManifest,
|
|
28
17
|
getErrorMessage,
|
|
18
|
+
getPluginAgentsDir,
|
|
19
|
+
getPluginManifestPath,
|
|
29
20
|
glob,
|
|
30
21
|
isAgentName,
|
|
31
22
|
loadAllAgents,
|
|
32
23
|
loadProjectAgents,
|
|
24
|
+
loadProjectConfig,
|
|
25
|
+
loadProjectSourceConfig,
|
|
33
26
|
log,
|
|
34
27
|
readFile,
|
|
35
28
|
readFileOptional,
|
|
36
29
|
remove,
|
|
30
|
+
resolveAgents,
|
|
31
|
+
resolveInstallPaths,
|
|
37
32
|
verbose,
|
|
38
|
-
warn
|
|
39
|
-
|
|
33
|
+
warn,
|
|
34
|
+
writeContentHash,
|
|
35
|
+
writeFile,
|
|
36
|
+
writePluginManifest
|
|
37
|
+
} from "./chunk-RVXI5VQU.js";
|
|
40
38
|
import {
|
|
41
39
|
typedEntries,
|
|
42
40
|
typedFromEntries,
|
|
43
41
|
typedKeys
|
|
44
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-IGOFZG3S.js";
|
|
45
43
|
import {
|
|
44
|
+
CLAUDE_DIR,
|
|
45
|
+
CLAUDE_SRC_DIR,
|
|
46
|
+
DEFAULT_PLUGIN_NAME,
|
|
46
47
|
DIRS,
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
EJECT_SOURCE,
|
|
49
|
+
PROJECT_ROOT,
|
|
50
|
+
STANDARD_DIRS,
|
|
51
|
+
STANDARD_FILES
|
|
52
|
+
} from "./chunk-G6G2FT2G.js";
|
|
49
53
|
|
|
50
54
|
// src/cli/lib/agents/list-compiled-agents.ts
|
|
51
|
-
import
|
|
55
|
+
import path2 from "path";
|
|
52
56
|
import { partition } from "remeda";
|
|
57
|
+
|
|
58
|
+
// src/cli/lib/agents/agent-provenance.ts
|
|
59
|
+
import path from "path";
|
|
60
|
+
import { z } from "zod";
|
|
61
|
+
var MARKER_OPEN = `<!-- Generated by ${DEFAULT_PLUGIN_NAME} v`;
|
|
62
|
+
var MARKER_NOTICE = " \u2014 do not edit; compile rewrites this file";
|
|
63
|
+
var MARKER_CLOSE = " -->";
|
|
64
|
+
var FRONTMATTER_FENCE = "---";
|
|
65
|
+
var PACKAGE_JSON_FILE = "package.json";
|
|
66
|
+
var ownPackageJsonSchema = z.object({ version: z.string().min(1) });
|
|
67
|
+
function provenanceMarker(version) {
|
|
68
|
+
return `${MARKER_OPEN}${version}${MARKER_NOTICE}${MARKER_CLOSE}`;
|
|
69
|
+
}
|
|
70
|
+
function isProvenanceMarker(line) {
|
|
71
|
+
return line.startsWith(MARKER_OPEN) && line.endsWith(MARKER_CLOSE);
|
|
72
|
+
}
|
|
73
|
+
function bodyStartIndex(lines) {
|
|
74
|
+
if (lines[0] !== FRONTMATTER_FENCE) return 0;
|
|
75
|
+
const closingFence = lines.indexOf(FRONTMATTER_FENCE, 1);
|
|
76
|
+
if (closingFence === -1) return 0;
|
|
77
|
+
return closingFence + 1;
|
|
78
|
+
}
|
|
79
|
+
function hasProvenanceMarker(content) {
|
|
80
|
+
const lines = content.split("\n");
|
|
81
|
+
const firstBodyLine = lines[bodyStartIndex(lines)];
|
|
82
|
+
return firstBodyLine !== void 0 && isProvenanceMarker(firstBodyLine);
|
|
83
|
+
}
|
|
84
|
+
function stampProvenanceMarker(content, version) {
|
|
85
|
+
const lines = content.split("\n");
|
|
86
|
+
const bodyStart = bodyStartIndex(lines);
|
|
87
|
+
const replacedLines = hasProvenanceMarker(content) ? 1 : 0;
|
|
88
|
+
return [
|
|
89
|
+
...lines.slice(0, bodyStart),
|
|
90
|
+
provenanceMarker(version),
|
|
91
|
+
...lines.slice(bodyStart + replacedLines)
|
|
92
|
+
].join("\n");
|
|
93
|
+
}
|
|
94
|
+
var ownVersion;
|
|
95
|
+
function cliVersion() {
|
|
96
|
+
ownVersion ??= readOwnVersion();
|
|
97
|
+
return ownVersion;
|
|
98
|
+
}
|
|
99
|
+
async function readOwnVersion() {
|
|
100
|
+
const manifestPath = path.join(PROJECT_ROOT, PACKAGE_JSON_FILE);
|
|
101
|
+
const raw = JSON.parse(await readFile(manifestPath));
|
|
102
|
+
const parsed = ownPackageJsonSchema.safeParse(raw);
|
|
103
|
+
if (!parsed.success) {
|
|
104
|
+
throw new Error(`Could not read this CLI's own version from ${manifestPath}`);
|
|
105
|
+
}
|
|
106
|
+
return parsed.data.version;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/cli/lib/agents/list-compiled-agents.ts
|
|
53
110
|
async function listAgentMdFiles(agentsDir) {
|
|
54
111
|
return glob("*.md", agentsDir);
|
|
55
112
|
}
|
|
@@ -60,29 +117,29 @@ async function splitAgentsByProvenance(agentsDir) {
|
|
|
60
117
|
return { marked: marked.map(agentName), unmarked: unmarked.map(agentName) };
|
|
61
118
|
}
|
|
62
119
|
async function readAgentProvenance(agentsDir, file) {
|
|
63
|
-
const content = await readFileOptional(
|
|
64
|
-
return { name:
|
|
120
|
+
const content = await readFileOptional(path2.join(agentsDir, file));
|
|
121
|
+
return { name: path2.basename(file, ".md"), compiledHere: hasProvenanceMarker(content) };
|
|
65
122
|
}
|
|
66
123
|
function agentName(agent) {
|
|
67
124
|
return agent.name;
|
|
68
125
|
}
|
|
69
126
|
async function listCompiledAgentNames(agentsDir) {
|
|
70
127
|
const files = await listAgentMdFiles(agentsDir);
|
|
71
|
-
return files.map((f) =>
|
|
128
|
+
return files.map((f) => path2.basename(f, ".md"));
|
|
72
129
|
}
|
|
73
130
|
async function pruneStaleCompiledAgents(agentsDir, keep) {
|
|
74
131
|
const isStaleCompiledAgent = (file) => {
|
|
75
|
-
const name =
|
|
132
|
+
const name = path2.basename(file, ".md");
|
|
76
133
|
return isAgentName(name) && !keep.has(name);
|
|
77
134
|
};
|
|
78
135
|
const files = await listAgentMdFiles(agentsDir);
|
|
79
136
|
await Promise.all(
|
|
80
|
-
files.filter(isStaleCompiledAgent).map((file) => remove(
|
|
137
|
+
files.filter(isStaleCompiledAgent).map((file) => remove(path2.join(agentsDir, file)))
|
|
81
138
|
);
|
|
82
139
|
}
|
|
83
140
|
|
|
84
141
|
// src/cli/lib/agents/agent-fetcher.ts
|
|
85
|
-
import
|
|
142
|
+
import path3 from "path";
|
|
86
143
|
async function getAgentDefinitions(remoteSource) {
|
|
87
144
|
if (remoteSource) {
|
|
88
145
|
return fetchAgentDefinitionsFromRemote(remoteSource);
|
|
@@ -90,7 +147,7 @@ async function getAgentDefinitions(remoteSource) {
|
|
|
90
147
|
return getLocalAgentDefinitions();
|
|
91
148
|
}
|
|
92
149
|
async function getLocalAgentDefinitions() {
|
|
93
|
-
const agentsDir =
|
|
150
|
+
const agentsDir = path3.join(PROJECT_ROOT, DIRS.agents);
|
|
94
151
|
if (!await directoryExists(agentsDir)) {
|
|
95
152
|
throw new Error(
|
|
96
153
|
`Agent partials not found at '${agentsDir}'. Ensure the CLI is properly installed.`
|
|
@@ -110,7 +167,7 @@ async function fetchAgentDefinitionsFromRemote(source, options = {}) {
|
|
|
110
167
|
verbose(`Using agentsDir from source config: ${sourceProjectConfig.agentsDir}`);
|
|
111
168
|
}
|
|
112
169
|
const agentsDirRelPath = options.agentsDir ?? sourceProjectConfig?.agentsDir ?? DIRS.agents;
|
|
113
|
-
const agentsDir =
|
|
170
|
+
const agentsDir = path3.join(result.path, agentsDirRelPath);
|
|
114
171
|
if (!await directoryExists(agentsDir)) {
|
|
115
172
|
throw new Error(`Agent partials not found at '${agentsDir}'`);
|
|
116
173
|
}
|
|
@@ -123,7 +180,187 @@ async function fetchAgentDefinitionsFromRemote(source, options = {}) {
|
|
|
123
180
|
|
|
124
181
|
// src/cli/lib/agents/agent-recompiler.ts
|
|
125
182
|
import { partition as partition2 } from "remeda";
|
|
126
|
-
|
|
183
|
+
|
|
184
|
+
// src/cli/lib/agents/write-compiled-agents.ts
|
|
185
|
+
import os from "os";
|
|
186
|
+
import path5 from "path";
|
|
187
|
+
|
|
188
|
+
// src/cli/lib/compiler.ts
|
|
189
|
+
import { Liquid } from "liquidjs";
|
|
190
|
+
import path4 from "path";
|
|
191
|
+
var LIQUID_SYNTAX_PATTERN = /\{\{|\}\}|\{%|%\}/g;
|
|
192
|
+
function sanitizeLiquidSyntax(value, fieldName) {
|
|
193
|
+
if (!LIQUID_SYNTAX_PATTERN.test(value)) return value;
|
|
194
|
+
LIQUID_SYNTAX_PATTERN.lastIndex = 0;
|
|
195
|
+
const sanitized = value.replace(LIQUID_SYNTAX_PATTERN, "");
|
|
196
|
+
warn(`Stripped Liquid template syntax from '${fieldName}' \u2014 possible template injection attempt`);
|
|
197
|
+
return sanitized;
|
|
198
|
+
}
|
|
199
|
+
function sanitizeStringArray(values, fieldName) {
|
|
200
|
+
return values.map((v) => sanitizeLiquidSyntax(v, fieldName));
|
|
201
|
+
}
|
|
202
|
+
function sanitizeSkills(skills) {
|
|
203
|
+
return skills.map((s) => ({
|
|
204
|
+
...s,
|
|
205
|
+
id: sanitizeLiquidSyntax(s.id, "skill.id"),
|
|
206
|
+
description: sanitizeLiquidSyntax(s.description, "skill.description"),
|
|
207
|
+
usage: sanitizeLiquidSyntax(s.usage, "skill.usage"),
|
|
208
|
+
...s.pluginRef !== void 0 && {
|
|
209
|
+
pluginRef: sanitizeLiquidSyntax(s.pluginRef, "skill.pluginRef")
|
|
210
|
+
}
|
|
211
|
+
}));
|
|
212
|
+
}
|
|
213
|
+
function sanitizeCompiledAgentData(data) {
|
|
214
|
+
const sanitizedAgent = {
|
|
215
|
+
...data.agent,
|
|
216
|
+
name: sanitizeLiquidSyntax(data.agent.name, "agent.name"),
|
|
217
|
+
title: sanitizeLiquidSyntax(data.agent.title, "agent.title"),
|
|
218
|
+
description: sanitizeLiquidSyntax(data.agent.description, "agent.description"),
|
|
219
|
+
tools: sanitizeStringArray(data.agent.tools, "agent.tools"),
|
|
220
|
+
...data.agent.disallowedTools !== void 0 && {
|
|
221
|
+
disallowedTools: sanitizeStringArray(data.agent.disallowedTools, "agent.disallowedTools")
|
|
222
|
+
},
|
|
223
|
+
...data.agent.model !== void 0 && {
|
|
224
|
+
model: sanitizeLiquidSyntax(data.agent.model, "agent.model")
|
|
225
|
+
},
|
|
226
|
+
...data.agent.effort !== void 0 && {
|
|
227
|
+
effort: sanitizeLiquidSyntax(data.agent.effort, "agent.effort")
|
|
228
|
+
},
|
|
229
|
+
...data.agent.permissionMode !== void 0 && {
|
|
230
|
+
permissionMode: sanitizeLiquidSyntax(data.agent.permissionMode, "agent.permissionMode")
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
const sanitizedSkills = sanitizeSkills(data.skills);
|
|
234
|
+
const sanitizedPreloaded = sanitizeSkills(data.preloadedSkills);
|
|
235
|
+
const sanitizedDynamic = sanitizeSkills(data.dynamicSkills);
|
|
236
|
+
const sanitizedPreloadedIds = data.preloadedSkillIds.map(
|
|
237
|
+
(id) => sanitizeLiquidSyntax(id, "preloadedSkillId")
|
|
238
|
+
);
|
|
239
|
+
return {
|
|
240
|
+
agent: sanitizedAgent,
|
|
241
|
+
identity: data.identity,
|
|
242
|
+
playbook: data.playbook,
|
|
243
|
+
output: data.output,
|
|
244
|
+
criticalRequirementsTop: data.criticalRequirementsTop,
|
|
245
|
+
criticalReminders: data.criticalReminders,
|
|
246
|
+
skills: sanitizedSkills,
|
|
247
|
+
preloadedSkills: sanitizedPreloaded,
|
|
248
|
+
dynamicSkills: sanitizedDynamic,
|
|
249
|
+
preloadedSkillIds: sanitizedPreloadedIds
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
async function readAgentFiles(name, agent, projectRoot) {
|
|
253
|
+
const agentSourceRoot = agent.sourceRoot || projectRoot;
|
|
254
|
+
const agentBaseDir = agent.agentBaseDir || DIRS.agents;
|
|
255
|
+
const agentDir = path4.join(agentSourceRoot, agentBaseDir, agent.path || name);
|
|
256
|
+
const identity = await readFile(path4.join(agentDir, STANDARD_FILES.IDENTITY_MD));
|
|
257
|
+
const playbook = await readFile(path4.join(agentDir, STANDARD_FILES.PLAYBOOK_MD));
|
|
258
|
+
const criticalRequirementsTop = await readFileOptional(
|
|
259
|
+
path4.join(agentDir, STANDARD_FILES.CRITICAL_REQUIREMENTS_MD),
|
|
260
|
+
""
|
|
261
|
+
);
|
|
262
|
+
const criticalReminders = await readFileOptional(
|
|
263
|
+
path4.join(agentDir, STANDARD_FILES.CRITICAL_REMINDERS_MD),
|
|
264
|
+
""
|
|
265
|
+
);
|
|
266
|
+
const agentPath = agent.path || name;
|
|
267
|
+
const parts = agentPath.split("/");
|
|
268
|
+
const category = parts[0] || name;
|
|
269
|
+
const categoryDir = path4.join(agentSourceRoot, agentBaseDir, category);
|
|
270
|
+
let output = await readFileOptional(path4.join(agentDir, STANDARD_FILES.OUTPUT_MD), "");
|
|
271
|
+
if (!output) {
|
|
272
|
+
output = await readFileOptional(path4.join(categoryDir, STANDARD_FILES.OUTPUT_MD), "");
|
|
273
|
+
}
|
|
274
|
+
return { identity, playbook, output, criticalRequirementsTop, criticalReminders };
|
|
275
|
+
}
|
|
276
|
+
function buildAgentTemplateContext(name, agent, files, mapSkill = (skill) => skill) {
|
|
277
|
+
const skills = agent.skills.map(mapSkill);
|
|
278
|
+
const preloadedSkills = skills.filter((s) => s.preloaded);
|
|
279
|
+
const dynamicSkills = skills.filter((s) => !s.preloaded);
|
|
280
|
+
const preloadedSkillIds = preloadedSkills.map((s) => s.pluginRef ?? s.id);
|
|
281
|
+
verbose(
|
|
282
|
+
`Skills for ${name}: ${preloadedSkills.length} preloaded, ${dynamicSkills.length} dynamic`
|
|
283
|
+
);
|
|
284
|
+
return {
|
|
285
|
+
agent,
|
|
286
|
+
...files,
|
|
287
|
+
skills,
|
|
288
|
+
preloadedSkills,
|
|
289
|
+
dynamicSkills,
|
|
290
|
+
preloadedSkillIds
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
async function renderCompiledAgent(engine, data) {
|
|
294
|
+
const rendered = await engine.renderFile("agent", sanitizeCompiledAgentData(data));
|
|
295
|
+
return stampProvenanceMarker(rendered, await cliVersion());
|
|
296
|
+
}
|
|
297
|
+
async function createLiquidEngine(projectDir) {
|
|
298
|
+
const roots = [];
|
|
299
|
+
if (projectDir) {
|
|
300
|
+
const srcTemplatesDir = path4.join(
|
|
301
|
+
projectDir,
|
|
302
|
+
CLAUDE_SRC_DIR,
|
|
303
|
+
STANDARD_DIRS.AGENTS,
|
|
304
|
+
path4.basename(DIRS.templates)
|
|
305
|
+
);
|
|
306
|
+
if (await directoryExists(srcTemplatesDir)) {
|
|
307
|
+
roots.push(srcTemplatesDir);
|
|
308
|
+
verbose(`Using local templates from: ${srcTemplatesDir}`);
|
|
309
|
+
}
|
|
310
|
+
const legacyTemplatesDir = path4.join(projectDir, CLAUDE_DIR, STANDARD_DIRS.TEMPLATES);
|
|
311
|
+
if (await directoryExists(legacyTemplatesDir)) {
|
|
312
|
+
roots.push(legacyTemplatesDir);
|
|
313
|
+
verbose(`Using legacy templates from: ${legacyTemplatesDir}`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
roots.push(path4.join(PROJECT_ROOT, DIRS.templates));
|
|
317
|
+
return new Liquid({
|
|
318
|
+
root: roots,
|
|
319
|
+
extname: ".liquid",
|
|
320
|
+
strictVariables: false,
|
|
321
|
+
strictFilters: true
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
function pluginRefFor(skill) {
|
|
325
|
+
if (skill.source === void 0 || skill.source === EJECT_SOURCE) return {};
|
|
326
|
+
return { pluginRef: `${skill.id}:${skill.id}` };
|
|
327
|
+
}
|
|
328
|
+
async function compileAgentForPlugin(name, agent, fallbackRoot, engine) {
|
|
329
|
+
verbose(`Compiling agent: ${name}`);
|
|
330
|
+
const files = await readAgentFiles(name, agent, fallbackRoot);
|
|
331
|
+
const data = buildAgentTemplateContext(name, agent, files, (skill) => ({
|
|
332
|
+
...skill,
|
|
333
|
+
...pluginRefFor(skill)
|
|
334
|
+
}));
|
|
335
|
+
return renderCompiledAgent(engine, data);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// src/cli/lib/agents/write-compiled-agents.ts
|
|
339
|
+
async function holdsExactly(filePath, content) {
|
|
340
|
+
return await fileExists(filePath) && await readFile(filePath) === content;
|
|
341
|
+
}
|
|
342
|
+
var UNROUTED_AGENT_SCOPE = "project";
|
|
343
|
+
async function writeCompiledAgentsByScope(params) {
|
|
344
|
+
const globalAgentsDir = resolveInstallPaths(os.homedir(), "global").agentsDir;
|
|
345
|
+
const outcomes = [];
|
|
346
|
+
for (const [name, agent] of typedEntries(params.resolvedAgents)) {
|
|
347
|
+
try {
|
|
348
|
+
const output = await compileAgentForPlugin(name, agent, params.sourcePath, params.engine);
|
|
349
|
+
const scope = params.agentScopeMap?.get(name) ?? UNROUTED_AGENT_SCOPE;
|
|
350
|
+
const targetDir = scope === "global" ? globalAgentsDir : params.projectAgentsDir;
|
|
351
|
+
const targetPath = path5.join(targetDir, `${name}.md`);
|
|
352
|
+
const rewritten = !await holdsExactly(targetPath, output);
|
|
353
|
+
if (rewritten) await writeFile(targetPath, output);
|
|
354
|
+
outcomes.push({ name, ok: true, scope, targetDir, rewritten });
|
|
355
|
+
} catch (error) {
|
|
356
|
+
outcomes.push({ name, ok: false, error });
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return outcomes;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// src/cli/lib/agents/agent-recompiler.ts
|
|
363
|
+
import path6 from "path";
|
|
127
364
|
async function getExistingAgentNames(pluginDir) {
|
|
128
365
|
return listCompiledAgentNames(getPluginAgentsDir(pluginDir));
|
|
129
366
|
}
|
|
@@ -221,7 +458,7 @@ async function recompileAgents(options) {
|
|
|
221
458
|
knownAgents.map((name) => [name, allConfigAgents[name] ?? {}])
|
|
222
459
|
);
|
|
223
460
|
const compileConfig = {
|
|
224
|
-
name: filteredConfig?.name ||
|
|
461
|
+
name: filteredConfig?.name || path6.basename(pluginDir),
|
|
225
462
|
description: filteredConfig?.description || "Recompiled plugin",
|
|
226
463
|
agents: configAgents
|
|
227
464
|
};
|
|
@@ -240,7 +477,7 @@ async function recompileAgents(options) {
|
|
|
240
477
|
}
|
|
241
478
|
|
|
242
479
|
// src/cli/lib/agents/agent-plugin-compiler.ts
|
|
243
|
-
import
|
|
480
|
+
import path7 from "path";
|
|
244
481
|
function parseAgentFrontmatter(content, filePath) {
|
|
245
482
|
const raw = extractFrontmatter(content);
|
|
246
483
|
if (!raw) {
|
|
@@ -255,7 +492,7 @@ function parseAgentFrontmatter(content, filePath) {
|
|
|
255
492
|
}
|
|
256
493
|
async function compileAgentPlugin(options) {
|
|
257
494
|
const { agentPath, outputDir } = options;
|
|
258
|
-
const fileName =
|
|
495
|
+
const fileName = path7.basename(agentPath);
|
|
259
496
|
const content = await readFile(agentPath);
|
|
260
497
|
const frontmatter = parseAgentFrontmatter(content, agentPath);
|
|
261
498
|
if (!frontmatter) {
|
|
@@ -265,8 +502,8 @@ async function compileAgentPlugin(options) {
|
|
|
265
502
|
}
|
|
266
503
|
const agentName2 = frontmatter.name;
|
|
267
504
|
verbose(`Compiling agent plugin: ${agentName2} from ${agentPath}`);
|
|
268
|
-
const pluginDir =
|
|
269
|
-
const agentsDir =
|
|
505
|
+
const pluginDir = path7.join(outputDir, `agent-${agentName2}`);
|
|
506
|
+
const agentsDir = path7.join(pluginDir, "agents");
|
|
270
507
|
await ensureDir(pluginDir);
|
|
271
508
|
await ensureDir(agentsDir);
|
|
272
509
|
const newHash = computeStringHash(content);
|
|
@@ -283,7 +520,7 @@ async function compileAgentPlugin(options) {
|
|
|
283
520
|
await writePluginManifest(pluginDir, manifest);
|
|
284
521
|
await writeContentHash(pluginDir, contentHash, getPluginManifestPath);
|
|
285
522
|
verbose(` Wrote plugin.json for ${agentName2} (v${version})`);
|
|
286
|
-
await copy(agentPath,
|
|
523
|
+
await copy(agentPath, path7.join(agentsDir, `${agentName2}.md`));
|
|
287
524
|
verbose(` Copied agent ${fileName} -> agents/${agentName2}.md`);
|
|
288
525
|
return {
|
|
289
526
|
pluginPath: pluginDir,
|
|
@@ -295,7 +532,7 @@ async function compileAllAgentPlugins(agentsDir, outputDir) {
|
|
|
295
532
|
const results = [];
|
|
296
533
|
const agentMdFiles = await listAgentMdFiles(agentsDir);
|
|
297
534
|
for (const agentFile of agentMdFiles) {
|
|
298
|
-
const agentPath =
|
|
535
|
+
const agentPath = path7.join(agentsDir, agentFile);
|
|
299
536
|
try {
|
|
300
537
|
const result = await compileAgentPlugin({
|
|
301
538
|
agentPath,
|
|
@@ -328,4 +565,4 @@ export {
|
|
|
328
565
|
compileAllAgentPlugins,
|
|
329
566
|
printAgentCompilationSummary
|
|
330
567
|
};
|
|
331
|
-
//# sourceMappingURL=chunk-
|
|
568
|
+
//# sourceMappingURL=chunk-LDBVIPTP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/lib/agents/list-compiled-agents.ts","../src/cli/lib/agents/agent-provenance.ts","../src/cli/lib/agents/agent-fetcher.ts","../src/cli/lib/agents/agent-recompiler.ts","../src/cli/lib/agents/write-compiled-agents.ts","../src/cli/lib/compiler.ts","../src/cli/lib/agents/agent-plugin-compiler.ts"],"sourcesContent":["import path from \"path\";\nimport { partition } from \"remeda\";\nimport { glob, readFileOptional, remove } from \"../../utils/fs\";\nimport { isAgentName } from \"../../utils/type-guards\";\nimport { hasProvenanceMarker } from \"./agent-provenance\";\nimport type { AgentName } from \"../../types\";\n\n/** Compiled-agent basenames in a directory, split by whether this CLI compiled them. */\nexport type AgentProvenanceSplit = {\n /** Files carrying the provenance marker — provably written by this CLI. */\n marked: string[];\n /** Everything else in the directory, the user's own agents among them. */\n unmarked: string[];\n};\n\n/** Lists compiled-agent markdown filenames (e.g. \"web-developer.md\") in a directory. */\nexport async function listAgentMdFiles(agentsDir: string): Promise<string[]> {\n return glob(\"*.md\", agentsDir);\n}\n\n/**\n * Splits the agent files in a directory by the provenance marker each compiled agent carries.\n *\n * This is the only thing that identifies this CLI's own output once the configuration naming\n * it is gone, so the split errs one way by construction: a file that cannot be read yields no\n * marker and lands in `unmarked`, because \"cannot prove it is ours\" and \"is not ours\" call for\n * the same answer — leave it alone.\n */\nexport async function splitAgentsByProvenance(agentsDir: string): Promise<AgentProvenanceSplit> {\n const files = await listAgentMdFiles(agentsDir);\n const classified = await Promise.all(files.map((file) => readAgentProvenance(agentsDir, file)));\n const [marked, unmarked] = partition(classified, (agent) => agent.compiledHere);\n\n return { marked: marked.map(agentName), unmarked: unmarked.map(agentName) };\n}\n\ntype ClassifiedAgent = { name: string; compiledHere: boolean };\n\nasync function readAgentProvenance(agentsDir: string, file: string): Promise<ClassifiedAgent> {\n const content = await readFileOptional(path.join(agentsDir, file));\n return { name: path.basename(file, \".md\"), compiledHere: hasProvenanceMarker(content) };\n}\n\nfunction agentName(agent: ClassifiedAgent): string {\n return agent.name;\n}\n\n/** Lists compiled-agent names (filenames without the `.md` extension) in a directory. */\nexport async function listCompiledAgentNames(agentsDir: string): Promise<AgentName[]> {\n const files = await listAgentMdFiles(agentsDir);\n // Boundary cast: compiled .md filenames are agent names by convention; custom\n // agents from marketplace stacks may not be members of the AgentName union.\n return files.map((f) => path.basename(f, \".md\") as AgentName);\n}\n\n/**\n * Removes stale CLI-compiled agent files from `agentsDir`: deletes any `.md`\n * whose basename is a built-in CLI agent name (`isAgentName`) that is NOT in\n * `keep`. The built-in-name check protects hand-authored agents (whose basename\n * is not a built-in name) — it is never the removal criterion. Only call this\n * when `keep` is the COMPLETE set of agents that should live in `agentsDir`.\n */\nexport async function pruneStaleCompiledAgents(\n agentsDir: string,\n keep: ReadonlySet<AgentName>,\n): Promise<void> {\n // A non-built-in basename is a hand-authored agent — always preserved.\n const isStaleCompiledAgent = (file: string): boolean => {\n const name = path.basename(file, \".md\");\n return isAgentName(name) && !keep.has(name);\n };\n\n const files = await listAgentMdFiles(agentsDir);\n await Promise.all(\n files.filter(isStaleCompiledAgent).map((file) => remove(path.join(agentsDir, file))),\n );\n}\n","import path from \"path\";\nimport { z } from \"zod\";\n\nimport { DEFAULT_PLUGIN_NAME, PROJECT_ROOT } from \"../../consts\";\nimport { readFile } from \"../../utils/fs\";\n\n/**\n * The provenance marker every compiled agent carries: an HTML comment on the first line\n * after the frontmatter, naming the generator, its version, and the fact that the file is\n * rewritten rather than edited.\n *\n * **A body comment, deliberately not a frontmatter field.** Claude Code documents sixteen\n * supported frontmatter keys and says nothing about how it treats an unknown one, so a\n * stricter release could reject every agent this CLI has ever written. The body is free-form\n * by contract, which makes a marker there safe, greppable, versioned, and the do-not-edit\n * notice at the same time.\n *\n * The version is informational — {@link hasProvenanceMarker} matches on the marker's SHAPE,\n * so an agent compiled by any release is recognised by any other. Matching the exact text\n * would sweep only the agents the running version happened to write.\n */\nconst MARKER_OPEN = `<!-- Generated by ${DEFAULT_PLUGIN_NAME} v`;\nconst MARKER_NOTICE = \" — do not edit; compile rewrites this file\";\nconst MARKER_CLOSE = \" -->\";\n\nconst FRONTMATTER_FENCE = \"---\";\n\nconst PACKAGE_JSON_FILE = \"package.json\";\n\n/** The one field the marker needs from this CLI's own manifest. */\nconst ownPackageJsonSchema = z.object({ version: z.string().min(1) });\n\nexport function provenanceMarker(version: string): string {\n return `${MARKER_OPEN}${version}${MARKER_NOTICE}${MARKER_CLOSE}`;\n}\n\n/** Whether a line is a marker this CLI wrote, at any version and under any wording. */\nfunction isProvenanceMarker(line: string): boolean {\n return line.startsWith(MARKER_OPEN) && line.endsWith(MARKER_CLOSE);\n}\n\n/**\n * The index of the first body line: past the closing frontmatter fence when there is one,\n * and the top of the file when there is not — a template override may render no frontmatter,\n * and the marker still needs a defined home.\n */\nfunction bodyStartIndex(lines: readonly string[]): number {\n if (lines[0] !== FRONTMATTER_FENCE) return 0;\n const closingFence = lines.indexOf(FRONTMATTER_FENCE, 1);\n if (closingFence === -1) return 0;\n return closingFence + 1;\n}\n\n/**\n * Whether this CLI compiled the agent file this content came from.\n *\n * The marker's POSITION is part of the claim: an agent that merely quotes the line further\n * down (a prompt about this very feature, say) is the user's, and a sweep reading that as\n * provenance would delete a file nothing here wrote.\n */\nexport function hasProvenanceMarker(content: string): boolean {\n const lines = content.split(\"\\n\");\n const firstBodyLine = lines[bodyStartIndex(lines)];\n return firstBodyLine !== undefined && isProvenanceMarker(firstBodyLine);\n}\n\n/**\n * The same content carrying exactly one provenance marker, on the first line after the\n * frontmatter.\n *\n * Idempotent by replacement rather than by insertion: content that already carries a marker\n * has that line rewritten, so stamping twice at one version is a fixed point and a version\n * bump moves the line instead of stacking a second one beside it.\n */\nexport function stampProvenanceMarker(content: string, version: string): string {\n const lines = content.split(\"\\n\");\n const bodyStart = bodyStartIndex(lines);\n const replacedLines = hasProvenanceMarker(content) ? 1 : 0;\n\n return [\n ...lines.slice(0, bodyStart),\n provenanceMarker(version),\n ...lines.slice(bodyStart + replacedLines),\n ].join(\"\\n\");\n}\n\nlet ownVersion: Promise<string> | undefined;\n\n/**\n * This CLI's own published version, read once per process from the package manifest beside\n * the code — the same `PROJECT_ROOT` the bundled agent partials and templates resolve from,\n * so the version in a marker is the version of the compiler that wrote it.\n */\nexport function cliVersion(): Promise<string> {\n ownVersion ??= readOwnVersion();\n return ownVersion;\n}\n\nasync function readOwnVersion(): Promise<string> {\n const manifestPath = path.join(PROJECT_ROOT, PACKAGE_JSON_FILE);\n const raw: unknown = JSON.parse(await readFile(manifestPath));\n\n const parsed = ownPackageJsonSchema.safeParse(raw);\n if (!parsed.success) {\n throw new Error(`Could not read this CLI's own version from ${manifestPath}`);\n }\n return parsed.data.version;\n}\n","import path from \"path\";\nimport { directoryExists } from \"../../utils/fs\";\nimport { verbose } from \"../../utils/logger\";\nimport { PROJECT_ROOT, DIRS } from \"../../consts\";\nimport { fetchFromSource, type FetchOptions } from \"../loading\";\nimport { loadProjectSourceConfig } from \"../configuration\";\nimport type { AgentSourcePaths } from \"../../types\";\n\nexport async function getAgentDefinitions(remoteSource?: string): Promise<AgentSourcePaths> {\n if (remoteSource) {\n return fetchAgentDefinitionsFromRemote(remoteSource);\n }\n return getLocalAgentDefinitions();\n}\n\nexport async function getLocalAgentDefinitions(): Promise<AgentSourcePaths> {\n const agentsDir = path.join(PROJECT_ROOT, DIRS.agents);\n\n if (!(await directoryExists(agentsDir))) {\n throw new Error(\n `Agent partials not found at '${agentsDir}'. Ensure the CLI is properly installed.`,\n );\n }\n\n verbose(`Agent partials loaded from CLI: ${agentsDir}`);\n\n return {\n agentsDir,\n sourcePath: PROJECT_ROOT,\n };\n}\n\nexport async function fetchAgentDefinitionsFromRemote(\n source: string,\n options: FetchOptions & { agentsDir?: string } = {},\n): Promise<AgentSourcePaths> {\n verbose(`Fetching agent partials from remote: ${source}`);\n\n const result = await fetchFromSource(source, { subdir: \"\" });\n\n // ABORT on an unreadable config. It names the directory the fetched repository keeps its agent\n // partials in; defaulting past it compiles agents from whatever happens to sit at `DIRS.agents`,\n // or reports partials missing from a repository that has them.\n const sourceProjectConfig = options.agentsDir\n ? undefined\n : await loadProjectSourceConfig(result.path);\n if (sourceProjectConfig?.agentsDir) {\n verbose(`Using agentsDir from source config: ${sourceProjectConfig.agentsDir}`);\n }\n const agentsDirRelPath = options.agentsDir ?? sourceProjectConfig?.agentsDir ?? DIRS.agents;\n\n const agentsDir = path.join(result.path, agentsDirRelPath);\n\n if (!(await directoryExists(agentsDir))) {\n throw new Error(`Agent partials not found at '${agentsDir}'`);\n }\n\n verbose(`Agent partials fetched from: ${result.path}`);\n\n return {\n agentsDir,\n sourcePath: result.path,\n };\n}\n","import { partition } from \"remeda\";\nimport { writeCompiledAgentsByScope, type AgentWriteOutcome } from \"./write-compiled-agents\";\nimport { listCompiledAgentNames } from \"./list-compiled-agents\";\nimport path from \"path\";\n\nimport { getErrorMessage } from \"../../utils/errors\";\nimport type {\n AgentDefinition,\n AgentName,\n CompileAgentConfig,\n CompileConfig,\n ProjectConfig,\n SkillDefinitionMap,\n SkillScope,\n} from \"../../types\";\n\nimport { buildCompileAgents } from \"../installation/local-installer\";\nimport { ensureDir } from \"../../utils/fs\";\nimport { verbose } from \"../../utils/logger\";\nimport { typedEntries, typedFromEntries, typedKeys } from \"../../utils/typed-object\";\nimport { createLiquidEngine } from \"../compiler\";\nimport { loadProjectConfig, effectivelyExcludedSkillIds } from \"../configuration\";\nimport { loadAllAgents, loadProjectAgents } from \"../loading\";\nimport { getPluginAgentsDir } from \"../plugins\";\nimport { discoverAllPluginSkills } from \"../plugins/plugin-discovery\";\nimport { resolveAgents } from \"../resolver\";\n\nexport type RecompileAgentsOptions = {\n pluginDir: string;\n sourcePath: string;\n agents?: AgentName[];\n skills?: SkillDefinitionMap;\n projectDir?: string;\n outputDir?: string;\n /** When provided, routes agents by scope: global agents to ~/.claude/agents/, project agents to outputDir */\n agentScopeMap?: Map<AgentName, SkillScope>;\n};\n\nexport type RecompileAgentsResult = {\n compiled: AgentName[];\n /**\n * The subset of `compiled` whose file this pass actually wrote. `compiled` is\n * every agent that came through the pass with a correct file at the end of it;\n * `rewritten` is the ones whose content moved, so `compiled minus rewritten` is\n * the count a summary reports as unchanged.\n */\n rewritten: AgentName[];\n failed: AgentName[];\n warnings: string[];\n};\n\nasync function getExistingAgentNames(pluginDir: string): Promise<AgentName[]> {\n return listCompiledAgentNames(getPluginAgentsDir(pluginDir));\n}\n\ntype ResolveAgentNamesParams = {\n specifiedAgents?: AgentName[];\n projectConfig: ProjectConfig | null;\n allAgents: Partial<Record<AgentName, AgentDefinition>>;\n outputDir?: string;\n pluginDir: string;\n};\n\nasync function resolveAgentNames(params: ResolveAgentNamesParams): Promise<AgentName[]> {\n const { specifiedAgents, projectConfig, allAgents, outputDir, pluginDir } = params;\n\n if (specifiedAgents) {\n return specifiedAgents;\n }\n\n // A present config is authoritative over its agent roster — even when empty.\n // `agents: []` means \"no agents\", so it must NOT fall through to the\n // all-agents branch below; that branch exists only for the config-LESS case.\n if (projectConfig) {\n const agentNames = projectConfig.agents.map((a) => a.name);\n verbose(`Using agents from config: ${agentNames.join(\", \")}`);\n return agentNames;\n }\n\n if (outputDir) {\n const names = typedKeys<AgentName>(allAgents);\n verbose(`Using all available agents from source: ${names.join(\", \")}`);\n return names;\n }\n\n return getExistingAgentNames(pluginDir);\n}\n\n/** Splits write outcomes into the recompile result; logs each success at verbose level. */\nfunction buildRecompileResult(\n outcomes: AgentWriteOutcome[],\n priorWarnings: string[],\n): RecompileAgentsResult {\n const [succeeded, failedOutcomes] = partition(\n outcomes,\n (outcome): outcome is Extract<AgentWriteOutcome, { ok: true }> => outcome.ok,\n );\n succeeded.forEach((outcome) =>\n verbose(\n ` ${outcome.rewritten ? \"Rewrote\" : \"Unchanged\"}: ${outcome.name} (${outcome.scope} -> ${outcome.targetDir})`,\n ),\n );\n return {\n compiled: succeeded.map((outcome) => outcome.name),\n rewritten: succeeded.filter((outcome) => outcome.rewritten).map((outcome) => outcome.name),\n failed: failedOutcomes.map((outcome) => outcome.name),\n warnings: [\n ...priorWarnings,\n ...failedOutcomes.map(\n (outcome) => `Failed to compile ${outcome.name}: ${getErrorMessage(outcome.error)}`,\n ),\n ],\n };\n}\n\nexport function filterExcludedEntries(config: ProjectConfig): ProjectConfig {\n const excludedIds = effectivelyExcludedSkillIds(config.skills);\n const activeSkills = config.skills.filter((s) => !s.excluded);\n const activeAgents = config.agents.filter((a) => !a.excluded);\n\n // Also remove excluded skill refs from stack assignments. A config with no stack keeps\n // none — the key stays absent rather than becoming an explicit `undefined`.\n const filteredStack =\n config.stack === undefined\n ? {}\n : {\n stack: Object.fromEntries(\n typedEntries(config.stack).map(([agentName, agentStack]) => [\n agentName,\n Object.fromEntries(\n typedEntries(agentStack).map(([category, assignments]) => [\n category,\n assignments.filter((a) => !excludedIds.has(a.id)),\n ]),\n ),\n ]),\n ),\n };\n\n return { ...config, skills: activeSkills, agents: activeAgents, ...filteredStack };\n}\n\nexport async function recompileAgents(\n options: RecompileAgentsOptions,\n): Promise<RecompileAgentsResult> {\n const { pluginDir, sourcePath, skills: providedSkills, projectDir, outputDir } = options;\n\n const configDir = projectDir ?? pluginDir;\n const loadedConfig = await loadProjectConfig(configDir);\n const projectConfig = loadedConfig?.config ?? null;\n\n // Filter excluded entries once at the entry point — callees receive clean data\n const filteredConfig = projectConfig ? filterExcludedEntries(projectConfig) : null;\n\n const builtinAgents = await loadAllAgents(sourcePath);\n const projectAgents = projectDir ? await loadProjectAgents(projectDir) : {};\n\n // Priority: project agents > built-in agents\n const allAgents: Partial<Record<AgentName, AgentDefinition>> = {\n ...builtinAgents,\n ...projectAgents,\n };\n\n const agentNames = await resolveAgentNames({\n ...(options.agents !== undefined && { specifiedAgents: options.agents }),\n projectConfig: filteredConfig,\n allAgents,\n ...(outputDir !== undefined && { outputDir }),\n pluginDir,\n });\n\n if (agentNames.length === 0) {\n return { compiled: [], rewritten: [], failed: [], warnings: [\"No agents found to recompile\"] };\n }\n\n verbose(`Recompiling ${agentNames.length} agents in ${outputDir ?? pluginDir}`);\n\n // When skills are not provided, discover from all plugin directories.\n const pluginSkills: SkillDefinitionMap =\n providedSkills ?? (await discoverAllPluginSkills(projectDir ?? pluginDir));\n\n const allConfigAgents = filteredConfig ? buildCompileAgents(filteredConfig, allAgents) : {};\n\n // Restrict to only the agents we're compiling (agentNames).\n // buildCompileAgents returns entries for ALL agents in the config, but when\n // scopeFilter is active, we only want the agents matching that scope.\n // Without this filter, a project pass would compile global agents without\n // their stack (since the project config omits global agent stack entries)\n // and overwrite correctly compiled global agent files.\n const [knownAgents, missingAgents] = partition(agentNames, (name) =>\n Boolean(allConfigAgents[name] || allAgents[name]),\n );\n const missingWarnings = missingAgents.map(\n (name) => `Agent \"${name}\" not found in source definitions`,\n );\n const configAgents = typedFromEntries<AgentName, CompileAgentConfig>(\n knownAgents.map((name) => [name, allConfigAgents[name] ?? {}]),\n );\n\n const compileConfig: CompileConfig = {\n name: filteredConfig?.name || path.basename(pluginDir),\n description: filteredConfig?.description || \"Recompiled plugin\",\n agents: configAgents,\n };\n\n const engine = await createLiquidEngine(projectDir);\n const resolvedAgents = await resolveAgents(allAgents, pluginSkills, compileConfig, sourcePath);\n\n const agentsDir = outputDir ?? getPluginAgentsDir(pluginDir);\n await ensureDir(agentsDir);\n\n const outcomes = await writeCompiledAgentsByScope({\n resolvedAgents,\n sourcePath,\n engine,\n projectAgentsDir: agentsDir,\n ...(options.agentScopeMap !== undefined && { agentScopeMap: options.agentScopeMap }),\n });\n return buildRecompileResult(outcomes, missingWarnings);\n}\n","import os from \"os\";\nimport path from \"path\";\nimport type { Liquid } from \"liquidjs\";\nimport { compileAgentForPlugin } from \"../compiler.js\";\nimport { resolveInstallPaths } from \"../installation/install-base-dir.js\";\nimport { writeFile, fileExists, readFile } from \"../../utils/fs.js\";\nimport { typedEntries } from \"../../utils/typed-object.js\";\nimport type { AgentConfig, AgentName, SkillScope } from \"../../types/index.js\";\n\nexport type AgentWriteOutcome =\n | {\n name: AgentName;\n ok: true;\n scope: SkillScope;\n targetDir: string;\n /**\n * Whether this pass actually wrote the file. False means the compiled output\n * matched what was already on disk, so nothing was written — which is what\n * lets a recompile summary tell a real one from a no-op instead of counting\n * the roster it walked.\n */\n rewritten: boolean;\n }\n | { name: AgentName; ok: false; error: unknown };\n\n/**\n * Whether the target already holds exactly this content, making a write a no-op.\n *\n * Skipping that write is what gives \"unchanged\" a meaning a caller can check: an\n * agent reported unchanged keeps its mtime, and an mtime is the only trace a\n * rewrite-with-identical-bytes leaves anywhere.\n */\nasync function holdsExactly(filePath: string, content: string): Promise<boolean> {\n return (await fileExists(filePath)) && (await readFile(filePath)) === content;\n}\n\n/**\n * Where an agent goes when `agentScopeMap` has nothing to say about it — either because the caller\n * passed no map at all (`agents-inc update` recompiles that way) or because this agent is not in\n * the one it passed (a hand-authored agent under `.claude/agents/` has no config row).\n *\n * This is a ROUTING answer, not a selection default: `\"project\"` here means \"the directory the\n * caller named in `projectAgentsDir`\", which for an unrouted write is the only defensible target.\n * It is deliberately NOT `DEFAULT_SELECTION_OPTIONS.scope` from `@workspace/matrix` — that constant\n * says what an untouched *pick* installs as, and adopting it here would relocate the agents of every\n * caller that never asked for global routing into the user's `~/.claude/agents`.\n */\nconst UNROUTED_AGENT_SCOPE: SkillScope = \"project\";\n\n/**\n * Compiles each resolved agent and writes it to its scope's agents directory:\n * global agents to `~/.claude/agents/`, project agents to `projectAgentsDir`.\n * Per-agent failures are collected as outcomes — callers own the policy\n * (recompile reports and continues; install hard-errors).\n *\n * Neither directory is created up front. `writeFile` makes a target's parent on\n * the way past, so a directory appears exactly when an agent routes into it —\n * which is what keeps a wholly project-scoped pass from leaving an empty\n * `~/.claude/agents/` behind in a home that has no global install.\n */\nexport async function writeCompiledAgentsByScope(params: {\n resolvedAgents: Partial<Record<AgentName, AgentConfig>>;\n sourcePath: string;\n engine: Liquid;\n projectAgentsDir: string;\n agentScopeMap?: Map<AgentName, SkillScope>;\n}): Promise<AgentWriteOutcome[]> {\n const globalAgentsDir = resolveInstallPaths(os.homedir(), \"global\").agentsDir;\n\n const outcomes: AgentWriteOutcome[] = [];\n for (const [name, agent] of typedEntries<AgentName, AgentConfig>(params.resolvedAgents)) {\n try {\n const output = await compileAgentForPlugin(name, agent, params.sourcePath, params.engine);\n const scope = params.agentScopeMap?.get(name) ?? UNROUTED_AGENT_SCOPE;\n const targetDir = scope === \"global\" ? globalAgentsDir : params.projectAgentsDir;\n const targetPath = path.join(targetDir, `${name}.md`);\n const rewritten = !(await holdsExactly(targetPath, output));\n if (rewritten) await writeFile(targetPath, output);\n outcomes.push({ name, ok: true, scope, targetDir, rewritten });\n } catch (error) {\n outcomes.push({ name, ok: false, error });\n }\n }\n return outcomes;\n}\n","import { Liquid } from \"liquidjs\";\nimport path from \"path\";\nimport { readFile, readFileOptional, directoryExists } from \"../utils/fs\";\nimport { verbose, warn } from \"../utils/logger\";\nimport {\n CLAUDE_DIR,\n CLAUDE_SRC_DIR,\n DIRS,\n EJECT_SOURCE,\n PROJECT_ROOT,\n STANDARD_FILES,\n STANDARD_DIRS,\n} from \"../consts\";\nimport { cliVersion, stampProvenanceMarker } from \"./agents/agent-provenance\";\nimport type { AgentConfig, AgentName, CompiledAgentData, PluginSkillRef, Skill } from \"../types\";\n\n/** Pattern matching Liquid template delimiters that could enable template injection */\nconst LIQUID_SYNTAX_PATTERN = /\\{\\{|\\}\\}|\\{%|%\\}/g;\n\n/**\n * Strips Liquid template syntax (`{{`, `}}`, `{%`, `%}`) from a string value.\n * Prevents template injection when user-controlled data is passed to the Liquid engine.\n *\n * @param value - Input string that may contain Liquid syntax\n * @param fieldName - Name of the field (for warning messages)\n * @returns Sanitized string with Liquid delimiters removed\n */\nexport function sanitizeLiquidSyntax<T extends string>(value: T, fieldName: string): T {\n if (!LIQUID_SYNTAX_PATTERN.test(value)) return value;\n LIQUID_SYNTAX_PATTERN.lastIndex = 0;\n const sanitized = value.replace(LIQUID_SYNTAX_PATTERN, \"\");\n warn(`Stripped Liquid template syntax from '${fieldName}' — possible template injection attempt`);\n // Boundary cast: .replace() widens the branded string T; stripping characters keeps it in T's domain\n return sanitized as T;\n}\n\nfunction sanitizeStringArray(values: string[], fieldName: string): string[] {\n return values.map((v) => sanitizeLiquidSyntax(v, fieldName));\n}\n\nfunction sanitizeSkills(skills: Skill[]): Skill[] {\n return skills.map((s) => ({\n ...s,\n id: sanitizeLiquidSyntax(s.id, \"skill.id\"),\n description: sanitizeLiquidSyntax(s.description, \"skill.description\"),\n usage: sanitizeLiquidSyntax(s.usage, \"skill.usage\"),\n ...(s.pluginRef !== undefined && {\n pluginRef: sanitizeLiquidSyntax(s.pluginRef, \"skill.pluginRef\"),\n }),\n }));\n}\n\n/**\n * Sanitizes user-controlled metadata fields in compiled agent data to prevent\n * Liquid template injection. Strips `{{`, `}}`, `{%`, `%}` from agent\n * metadata and skill metadata before template rendering.\n *\n * Content fields (identity, playbook, output, criticalRequirementsTop,\n * criticalReminders) are passed through unchanged — LiquidJS does not\n * re-evaluate template syntax inside variable values, so double-curlies\n * in content (e.g. GitHub Actions `${{ secrets.X }}`) are safe.\n */\nexport function sanitizeCompiledAgentData(data: CompiledAgentData): CompiledAgentData {\n const sanitizedAgent: AgentConfig = {\n ...data.agent,\n name: sanitizeLiquidSyntax(data.agent.name, \"agent.name\"),\n title: sanitizeLiquidSyntax(data.agent.title, \"agent.title\"),\n description: sanitizeLiquidSyntax(data.agent.description, \"agent.description\"),\n tools: sanitizeStringArray(data.agent.tools, \"agent.tools\"),\n ...(data.agent.disallowedTools !== undefined && {\n disallowedTools: sanitizeStringArray(data.agent.disallowedTools, \"agent.disallowedTools\"),\n }),\n ...(data.agent.model !== undefined && {\n model: sanitizeLiquidSyntax(data.agent.model, \"agent.model\"),\n }),\n ...(data.agent.effort !== undefined && {\n effort: sanitizeLiquidSyntax(data.agent.effort, \"agent.effort\"),\n }),\n ...(data.agent.permissionMode !== undefined && {\n permissionMode: sanitizeLiquidSyntax(data.agent.permissionMode, \"agent.permissionMode\"),\n }),\n };\n\n const sanitizedSkills = sanitizeSkills(data.skills);\n const sanitizedPreloaded = sanitizeSkills(data.preloadedSkills);\n const sanitizedDynamic = sanitizeSkills(data.dynamicSkills);\n const sanitizedPreloadedIds = data.preloadedSkillIds.map((id) =>\n sanitizeLiquidSyntax(id, \"preloadedSkillId\"),\n );\n\n return {\n agent: sanitizedAgent,\n identity: data.identity,\n playbook: data.playbook,\n output: data.output,\n criticalRequirementsTop: data.criticalRequirementsTop,\n criticalReminders: data.criticalReminders,\n skills: sanitizedSkills,\n preloadedSkills: sanitizedPreloaded,\n dynamicSkills: sanitizedDynamic,\n preloadedSkillIds: sanitizedPreloadedIds,\n };\n}\n\ntype AgentFiles = Pick<\n CompiledAgentData,\n \"identity\" | \"playbook\" | \"output\" | \"criticalRequirementsTop\" | \"criticalReminders\"\n>;\n\nasync function readAgentFiles(\n name: AgentName,\n agent: AgentConfig,\n projectRoot: string,\n): Promise<AgentFiles> {\n const agentSourceRoot = agent.sourceRoot || projectRoot;\n const agentBaseDir = agent.agentBaseDir || DIRS.agents;\n const agentDir = path.join(agentSourceRoot, agentBaseDir, agent.path || name);\n\n const identity = await readFile(path.join(agentDir, STANDARD_FILES.IDENTITY_MD));\n const playbook = await readFile(path.join(agentDir, STANDARD_FILES.PLAYBOOK_MD));\n const criticalRequirementsTop = await readFileOptional(\n path.join(agentDir, STANDARD_FILES.CRITICAL_REQUIREMENTS_MD),\n \"\",\n );\n const criticalReminders = await readFileOptional(\n path.join(agentDir, STANDARD_FILES.CRITICAL_REMINDERS_MD),\n \"\",\n );\n\n const agentPath = agent.path || name;\n const parts = agentPath.split(\"/\");\n const category = parts[0] || name;\n const categoryDir = path.join(agentSourceRoot, agentBaseDir, category);\n\n let output = await readFileOptional(path.join(agentDir, STANDARD_FILES.OUTPUT_MD), \"\");\n if (!output) {\n output = await readFileOptional(path.join(categoryDir, STANDARD_FILES.OUTPUT_MD), \"\");\n }\n\n return { identity, playbook, output, criticalRequirementsTop, criticalReminders };\n}\n\nexport function buildAgentTemplateContext(\n name: AgentName,\n agent: AgentConfig,\n files: AgentFiles,\n mapSkill: (skill: Skill) => Skill = (skill) => skill,\n): CompiledAgentData {\n const skills = agent.skills.map(mapSkill);\n const preloadedSkills = skills.filter((s) => s.preloaded);\n const dynamicSkills = skills.filter((s) => !s.preloaded);\n const preloadedSkillIds = preloadedSkills.map((s) => s.pluginRef ?? s.id);\n\n verbose(\n `Skills for ${name}: ${preloadedSkills.length} preloaded, ${dynamicSkills.length} dynamic`,\n );\n\n return {\n agent,\n ...files,\n skills,\n preloadedSkills,\n dynamicSkills,\n preloadedSkillIds,\n };\n}\n\n/**\n * Renders the agent template and stamps the result with the provenance marker.\n *\n * Both compile entry points render through here, so there is no path that writes a compiled\n * agent this CLI cannot later recognise as its own — which is what `uninstall` reads back\n * when the configuration naming the agents is gone. The stamp replaces rather than inserts,\n * so a template that emits the marker itself still produces exactly one.\n */\nasync function renderCompiledAgent(engine: Liquid, data: CompiledAgentData): Promise<string> {\n // Boundary cast: liquidjs types renderFile as `Promise<any>` because a template\n // can render to any value. The agent template renders a markdown file, and both\n // callers have declared `Promise<string>` since they were written.\n const rendered = (await engine.renderFile(\"agent\", sanitizeCompiledAgentData(data))) as string;\n return stampProvenanceMarker(rendered, await cliVersion());\n}\n\n/**\n * Creates a Liquid template engine with a layered template root hierarchy.\n *\n * Template resolution order (first match wins):\n * 1. Project-local templates: `{projectDir}/.claude-src/agents/_templates/`\n * 2. Legacy templates: `{projectDir}/.claude/templates/`\n * 3. Built-in templates: `{PROJECT_ROOT}/src/agents/_templates/`\n *\n * @param projectDir - Optional project directory for local template overrides\n * @returns Configured Liquid engine with `.liquid` extension and strict filters\n */\nexport async function createLiquidEngine(projectDir?: string): Promise<Liquid> {\n const roots: string[] = [];\n\n if (projectDir) {\n const srcTemplatesDir = path.join(\n projectDir,\n CLAUDE_SRC_DIR,\n STANDARD_DIRS.AGENTS,\n path.basename(DIRS.templates),\n );\n if (await directoryExists(srcTemplatesDir)) {\n roots.push(srcTemplatesDir);\n verbose(`Using local templates from: ${srcTemplatesDir}`);\n }\n\n const legacyTemplatesDir = path.join(projectDir, CLAUDE_DIR, STANDARD_DIRS.TEMPLATES);\n if (await directoryExists(legacyTemplatesDir)) {\n roots.push(legacyTemplatesDir);\n verbose(`Using legacy templates from: ${legacyTemplatesDir}`);\n }\n }\n\n roots.push(path.join(PROJECT_ROOT, DIRS.templates));\n\n return new Liquid({\n root: roots,\n extname: \".liquid\",\n strictVariables: false,\n strictFilters: true,\n });\n}\n\n/**\n * The per-skill pluginRef decision. A skill renders as `${id}:${id}` only\n * when it has an explicit non-eject source on its SkillReference — i.e. it was\n * installed from a marketplace. `undefined` source (user-authored local skills\n * with no SkillConfig entry) and `\"eject\"` both fall through to bare id.\n */\nfunction pluginRefFor(skill: Skill): { pluginRef?: PluginSkillRef } {\n if (skill.source === undefined || skill.source === EJECT_SOURCE) return {};\n return { pluginRef: `${skill.id}:${skill.id}` as const };\n}\n\nexport async function compileAgentForPlugin(\n name: AgentName,\n agent: AgentConfig,\n fallbackRoot: string,\n engine: Liquid,\n): Promise<string> {\n verbose(`Compiling agent: ${name}`);\n\n const files = await readAgentFiles(name, agent, fallbackRoot);\n\n // Per-skill pluginRef attachment. Each skill's own `source` decides\n // whether it renders as `${id}:${id}` (plugin-installed) or bare id (ejected).\n // This correctly handles mixed-mode agents where some skills are plugin and\n // others are ejected. Missing `source` (user-authored local skills with no\n // SkillConfig entry) falls through to bare id — the expected case, not a\n // silent fallback.\n const data = buildAgentTemplateContext(name, agent, files, (skill) => ({\n ...skill,\n ...pluginRefFor(skill),\n }));\n\n return renderCompiledAgent(engine, data);\n}\n","import path from \"path\";\nimport { getErrorMessage } from \"../../utils/errors\";\nimport { readFile, ensureDir, copy } from \"../../utils/fs\";\nimport { log, verbose, warn } from \"../../utils/logger\";\nimport {\n generateAgentPluginManifest,\n writePluginManifest,\n getPluginManifestPath,\n} from \"../plugins\";\nimport { listAgentMdFiles } from \"./list-compiled-agents\";\nimport { computeStringHash, determinePluginVersion, writeContentHash } from \"../versioning\";\nimport { extractFrontmatter } from \"../../utils/frontmatter\";\nimport type { AgentFrontmatter, PluginManifest } from \"../../types\";\nimport { agentFrontmatterValidationSchema, formatZodIssues } from \"../schemas\";\n\nexport type AgentPluginOptions = {\n agentPath: string;\n outputDir: string;\n};\n\nexport type CompiledAgentPlugin = {\n pluginPath: string;\n manifest: PluginManifest;\n agentName: string;\n};\n\nfunction parseAgentFrontmatter(\n content: string,\n filePath: string,\n): Pick<AgentFrontmatter, \"name\" | \"description\"> | null {\n const raw = extractFrontmatter(content);\n if (!raw) {\n return null;\n }\n\n const result = agentFrontmatterValidationSchema.safeParse(raw);\n if (!result.success) {\n warn(`Invalid agent frontmatter in ${filePath}: ${formatZodIssues(result.error.issues)}`);\n return null;\n }\n\n return { name: result.data.name, description: result.data.description };\n}\n\nexport async function compileAgentPlugin(\n options: AgentPluginOptions,\n): Promise<CompiledAgentPlugin> {\n const { agentPath, outputDir } = options;\n const fileName = path.basename(agentPath);\n\n const content = await readFile(agentPath);\n const frontmatter = parseAgentFrontmatter(content, agentPath);\n\n if (!frontmatter) {\n throw new Error(\n `Agent '${fileName}' has invalid or missing YAML frontmatter. ` +\n `Required fields: 'name' and 'description'. File: ${agentPath}`,\n );\n }\n\n const agentName = frontmatter.name;\n\n verbose(`Compiling agent plugin: ${agentName} from ${agentPath}`);\n\n const pluginDir = path.join(outputDir, `agent-${agentName}`);\n const agentsDir = path.join(pluginDir, \"agents\");\n\n await ensureDir(pluginDir);\n await ensureDir(agentsDir);\n\n const newHash = computeStringHash(content);\n const { version, contentHash } = await determinePluginVersion(\n newHash,\n pluginDir,\n getPluginManifestPath,\n );\n\n const manifest = generateAgentPluginManifest({\n agentName,\n description: frontmatter.description,\n version,\n });\n\n await writePluginManifest(pluginDir, manifest);\n\n await writeContentHash(pluginDir, contentHash, getPluginManifestPath);\n\n verbose(` Wrote plugin.json for ${agentName} (v${version})`);\n\n await copy(agentPath, path.join(agentsDir, `${agentName}.md`));\n verbose(` Copied agent ${fileName} -> agents/${agentName}.md`);\n\n return {\n pluginPath: pluginDir,\n manifest,\n agentName,\n };\n}\n\nexport async function compileAllAgentPlugins(\n agentsDir: string,\n outputDir: string,\n): Promise<CompiledAgentPlugin[]> {\n const results: CompiledAgentPlugin[] = [];\n\n const agentMdFiles = await listAgentMdFiles(agentsDir);\n\n for (const agentFile of agentMdFiles) {\n const agentPath = path.join(agentsDir, agentFile);\n\n try {\n const result = await compileAgentPlugin({\n agentPath,\n outputDir,\n });\n results.push(result);\n log(` [OK] agent-${result.agentName}`);\n } catch (error) {\n const errorMessage = getErrorMessage(error);\n warn(`Failed to compile agent from '${agentFile}': ${errorMessage}`);\n }\n }\n\n return results;\n}\n\nexport function printAgentCompilationSummary(results: CompiledAgentPlugin[]): void {\n log(`\\nCompiled ${results.length} agent plugins:`);\n for (const result of results) {\n log(` - agent-${result.agentName} (v${result.manifest.version})`);\n }\n}\n"],"mappings":"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","names":["path","path","path","path","partition","path","path","path","path","path","partition","agentName","path","path","path","agentName"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
StackSelection
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4EPJWN7C.js";
|
|
5
5
|
|
|
6
6
|
// src/cli/components/wizard/step-stack.tsx
|
|
7
7
|
import { Box } from "ink";
|
|
@@ -14,4 +14,4 @@ var StepStack = ({ onCancel }) => {
|
|
|
14
14
|
export {
|
|
15
15
|
StepStack
|
|
16
16
|
};
|
|
17
|
-
//# sourceMappingURL=chunk-
|
|
17
|
+
//# sourceMappingURL=chunk-MU2TQ5KK.js.map
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
readPluginManifest
|
|
4
|
-
} from "./chunk-WCAUUBHO.js";
|
|
5
2
|
import {
|
|
6
3
|
ensureDir,
|
|
7
4
|
glob,
|
|
5
|
+
readPluginManifest,
|
|
8
6
|
verbose,
|
|
9
7
|
warn,
|
|
10
8
|
writeFile
|
|
11
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-RVXI5VQU.js";
|
|
12
10
|
import {
|
|
13
11
|
DEFAULT_PUBLIC_SOURCE_NAME,
|
|
14
12
|
DEFAULT_VERSION,
|
|
15
13
|
PLUGIN_MANIFEST_DIR,
|
|
16
14
|
PLUGIN_MANIFEST_FILE,
|
|
17
15
|
PUBLIC_CATALOGUE_PACKAGE
|
|
18
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-G6G2FT2G.js";
|
|
19
17
|
|
|
20
18
|
// src/cli/lib/marketplace-generator.ts
|
|
21
19
|
import path from "path";
|
|
@@ -129,4 +127,4 @@ export {
|
|
|
129
127
|
writeMarketplace,
|
|
130
128
|
getMarketplaceStats
|
|
131
129
|
};
|
|
132
|
-
//# sourceMappingURL=chunk-
|
|
130
|
+
//# sourceMappingURL=chunk-O4K42CJB.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/lib/marketplace-generator.ts"],"sourcesContent":["import path from \"path\";\nimport { countBy, sortBy } from \"remeda\";\n\nimport {\n DEFAULT_PUBLIC_SOURCE_NAME,\n DEFAULT_VERSION,\n PLUGIN_MANIFEST_DIR,\n PLUGIN_MANIFEST_FILE,\n PUBLIC_CATALOGUE_PACKAGE,\n} from \"../consts\";\nimport { writeFile, glob, ensureDir } from \"../utils/fs\";\nimport { verbose, warn } from \"../utils/logger\";\nimport type { Marketplace, MarketplacePlugin, PluginManifest } from \"../types\";\nimport { readPluginManifest } from \"./plugins\";\n\nconst PLUGIN_MANIFEST_PATH = `${PLUGIN_MANIFEST_DIR}/${PLUGIN_MANIFEST_FILE}`;\nconst MARKETPLACE_SCHEMA_URL = \"https://anthropic.com/claude-code/marketplace.schema.json\";\n\n/** Namespace a skill with no marketplace at all is given. */\nconst EXTERNAL_SKILL_NAMESPACE = \"external\";\n\n/** Namespace a skill created in place, rather than fetched from a marketplace, is given. */\nconst LOCAL_SKILL_NAMESPACE = \"local\";\n\n/**\n * Marketplace names that are not an author's to take.\n *\n * The public catalogue's skills ship unprefixed, so every bare id in the catalogue\n * already lives in `agents-inc`; the other two hold the skills that belong to no\n * marketplace. Publishing under any of them claims ids the marketplace does not own.\n */\nconst RESERVED_MARKETPLACE_NAMES: readonly string[] = [\n DEFAULT_PUBLIC_SOURCE_NAME,\n EXTERNAL_SKILL_NAMESPACE,\n LOCAL_SKILL_NAMESPACE,\n];\n\n/** How many offending ids a namespace refusal lists before summarising the rest. */\nconst MAX_REPORTED_NAMESPACE_VIOLATIONS = 10;\n\ntype MarketplaceOptions = {\n name: string;\n version?: string;\n description?: string;\n ownerName: string;\n ownerEmail?: string;\n pluginRoot: string;\n};\n\nfunction convertManifestToMarketplacePlugin(\n manifest: PluginManifest,\n pluginRoot: string,\n pluginDirName: string,\n): MarketplacePlugin {\n return {\n name: manifest.name,\n source: `./${pluginRoot}/${pluginDirName}`,\n ...(manifest.description !== undefined && { description: manifest.description }),\n ...(manifest.version !== undefined && { version: manifest.version }),\n ...(manifest.author !== undefined && { author: manifest.author }),\n ...(manifest.category !== undefined && { category: manifest.category }),\n ...(manifest.keywords !== undefined && { keywords: manifest.keywords }),\n };\n}\n\nexport async function generateMarketplace(\n pluginsDir: string,\n options: MarketplaceOptions,\n): Promise<Marketplace> {\n verbose(`Scanning plugins directory: ${pluginsDir}`);\n\n const manifestFiles = await glob(`**/${PLUGIN_MANIFEST_PATH}`, pluginsDir);\n verbose(`Found ${manifestFiles.length} plugin manifests`);\n\n const plugins: MarketplacePlugin[] = [];\n\n for (const manifestFile of manifestFiles) {\n const separator = manifestFile.indexOf(\"/\");\n const pluginDirName = separator === -1 ? manifestFile : manifestFile.slice(0, separator);\n const pluginDir = path.join(pluginsDir, pluginDirName);\n\n const manifest = await readPluginManifest(pluginDir);\n if (!manifest) {\n warn(`Could not read plugin manifest: '${manifestFile}'`);\n continue;\n }\n\n const plugin = convertManifestToMarketplacePlugin(\n manifest,\n options.pluginRoot.replace(/^\\.\\//, \"\"),\n pluginDirName,\n );\n plugins.push(plugin);\n verbose(` [OK] ${plugin.name}`);\n }\n\n const sortedPlugins = sortBy(plugins, (p) => p.name);\n\n // Field order matters: the object is serialized verbatim into marketplace.json,\n // so conditional fields keep the same positions the previous emissions used.\n return {\n $schema: MARKETPLACE_SCHEMA_URL,\n name: options.name,\n version: options.version ?? DEFAULT_VERSION,\n owner: {\n name: options.ownerName,\n ...(options.ownerEmail ? { email: options.ownerEmail } : {}),\n },\n metadata: {\n pluginRoot: options.pluginRoot,\n },\n plugins: sortedPlugins,\n ...(options.description ? { description: options.description } : {}),\n };\n}\n\n/**\n * Refuses a marketplace name that belongs to a namespace the author does not own,\n * or returns null when the name is theirs to take.\n *\n * `packageName` is package.json's own `name`. It is read for one purpose: the public\n * catalogue publishes under a reserved name legitimately, and\n * {@link PUBLIC_CATALOGUE_PACKAGE} is what distinguishes it from a claimant.\n */\nexport function validateMarketplaceName(\n marketplaceName: string,\n packageName: string,\n): string | null {\n if (!RESERVED_MARKETPLACE_NAMES.includes(marketplaceName)) return null;\n if (isCatalogueOwnReservedName(marketplaceName, packageName)) return null;\n\n return reservedNameError(marketplaceName);\n}\n\n/**\n * Refuses a marketplace whose skill ids fall outside its own namespace, or returns\n * null when every id carries it.\n *\n * The public catalogue is exempt: its skills ship unprefixed by design and\n * `agents-inc` IS the namespace they occupy. Reading that exemption off the NAME is\n * only safe because {@link validateMarketplaceName} runs first and lets nothing but\n * the catalogue's own package hold it — the two checks are a pair, in that order.\n */\nexport function validateSkillIdNamespace(marketplace: Marketplace): string | null {\n if (marketplace.name === DEFAULT_PUBLIC_SOURCE_NAME) return null;\n\n const foreignIds = marketplace.plugins\n .filter((plugin) => !carriesNamespace(plugin.name, marketplace.name))\n .map((plugin) => plugin.name);\n\n if (foreignIds.length === 0) return null;\n\n return namespaceViolationError(foreignIds, marketplace.name);\n}\n\n/**\n * Whether the reserved NAME this build publishes under is the catalogue's own, which\n * takes both halves: the name claimed is the catalogue's, and the package claiming it\n * is the catalogue's package.\n *\n * The load side asks a different question of the same identity: `isPublicCatalogueCheckout`\n * in `configuration/config.ts` asks whether a DIRECTORY is a checkout of that repository,\n * and reads the package name off it rather than being handed one.\n */\nfunction isCatalogueOwnReservedName(marketplaceName: string, packageName: string): boolean {\n return marketplaceName === DEFAULT_PUBLIC_SOURCE_NAME && packageName === PUBLIC_CATALOGUE_PACKAGE;\n}\n\nfunction carriesNamespace(skillId: string, marketplaceName: string): boolean {\n return skillId.startsWith(`${marketplaceName}-`);\n}\n\nfunction reservedNameError(marketplaceName: string): string {\n return (\n `Marketplace name '${marketplaceName}' is reserved. ` +\n `'${DEFAULT_PUBLIC_SOURCE_NAME}' is the public catalogue's own namespace, and ` +\n `'${EXTERNAL_SKILL_NAMESPACE}' and '${LOCAL_SKILL_NAMESPACE}' hold the skills that belong to ` +\n `no marketplace — publishing under any of them claims skill ids that are not this ` +\n `marketplace's. Choose a name of your own: set package.json 'name', or pass --name.`\n );\n}\n\nfunction namespaceViolationError(foreignIds: string[], marketplaceName: string): string {\n const listed = foreignIds\n .slice(0, MAX_REPORTED_NAMESPACE_VIOLATIONS)\n .map((id) => ` ${id} -> ${marketplaceName}-${id}`);\n const unlisted = foreignIds.length - listed.length;\n\n return [\n `Marketplace '${marketplaceName}' ships ${foreignIds.length} skill id(s) outside its own ` +\n `namespace. Every skill id must begin with the marketplace's name:`,\n ...listed,\n ...(unlisted > 0 ? [` ... and ${unlisted} more`] : []),\n `Rename each skill directory and the id in its metadata, re-run 'build plugins', then ` +\n `build the marketplace again.`,\n ].join(\"\\n\");\n}\n\nexport async function writeMarketplace(\n outputPath: string,\n marketplace: Marketplace,\n): Promise<void> {\n await ensureDir(path.dirname(outputPath));\n const content = `${JSON.stringify(marketplace, null, 2)}\\n`;\n await writeFile(outputPath, content);\n}\n\nexport function getMarketplaceStats(marketplace: Marketplace): {\n total: number;\n byCategory: Record<string, number>;\n} {\n return {\n total: marketplace.plugins.length,\n byCategory: countBy(marketplace.plugins, (p) => p.category ?? \"uncategorized\"),\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/cli/lib/marketplace-generator.ts"],"sourcesContent":["import path from \"path\";\nimport { countBy, sortBy } from \"remeda\";\n\nimport {\n DEFAULT_PUBLIC_SOURCE_NAME,\n DEFAULT_VERSION,\n PLUGIN_MANIFEST_DIR,\n PLUGIN_MANIFEST_FILE,\n PUBLIC_CATALOGUE_PACKAGE,\n} from \"../consts\";\nimport { writeFile, glob, ensureDir } from \"../utils/fs\";\nimport { verbose, warn } from \"../utils/logger\";\nimport type { Marketplace, MarketplacePlugin, PluginManifest } from \"../types\";\nimport { readPluginManifest } from \"./plugins\";\n\nconst PLUGIN_MANIFEST_PATH = `${PLUGIN_MANIFEST_DIR}/${PLUGIN_MANIFEST_FILE}`;\nconst MARKETPLACE_SCHEMA_URL = \"https://anthropic.com/claude-code/marketplace.schema.json\";\n\n/** Namespace a skill with no marketplace at all is given. */\nconst EXTERNAL_SKILL_NAMESPACE = \"external\";\n\n/** Namespace a skill created in place, rather than fetched from a marketplace, is given. */\nconst LOCAL_SKILL_NAMESPACE = \"local\";\n\n/**\n * Marketplace names that are not an author's to take.\n *\n * The public catalogue's skills ship unprefixed, so every bare id in the catalogue\n * already lives in `agents-inc`; the other two hold the skills that belong to no\n * marketplace. Publishing under any of them claims ids the marketplace does not own.\n */\nconst RESERVED_MARKETPLACE_NAMES: readonly string[] = [\n DEFAULT_PUBLIC_SOURCE_NAME,\n EXTERNAL_SKILL_NAMESPACE,\n LOCAL_SKILL_NAMESPACE,\n];\n\n/** How many offending ids a namespace refusal lists before summarising the rest. */\nconst MAX_REPORTED_NAMESPACE_VIOLATIONS = 10;\n\ntype MarketplaceOptions = {\n name: string;\n version?: string;\n description?: string;\n ownerName: string;\n ownerEmail?: string;\n pluginRoot: string;\n};\n\nfunction convertManifestToMarketplacePlugin(\n manifest: PluginManifest,\n pluginRoot: string,\n pluginDirName: string,\n): MarketplacePlugin {\n return {\n name: manifest.name,\n source: `./${pluginRoot}/${pluginDirName}`,\n ...(manifest.description !== undefined && { description: manifest.description }),\n ...(manifest.version !== undefined && { version: manifest.version }),\n ...(manifest.author !== undefined && { author: manifest.author }),\n ...(manifest.category !== undefined && { category: manifest.category }),\n ...(manifest.keywords !== undefined && { keywords: manifest.keywords }),\n };\n}\n\nexport async function generateMarketplace(\n pluginsDir: string,\n options: MarketplaceOptions,\n): Promise<Marketplace> {\n verbose(`Scanning plugins directory: ${pluginsDir}`);\n\n const manifestFiles = await glob(`**/${PLUGIN_MANIFEST_PATH}`, pluginsDir);\n verbose(`Found ${manifestFiles.length} plugin manifests`);\n\n const plugins: MarketplacePlugin[] = [];\n\n for (const manifestFile of manifestFiles) {\n const separator = manifestFile.indexOf(\"/\");\n const pluginDirName = separator === -1 ? manifestFile : manifestFile.slice(0, separator);\n const pluginDir = path.join(pluginsDir, pluginDirName);\n\n const manifest = await readPluginManifest(pluginDir);\n if (!manifest) {\n warn(`Could not read plugin manifest: '${manifestFile}'`);\n continue;\n }\n\n const plugin = convertManifestToMarketplacePlugin(\n manifest,\n options.pluginRoot.replace(/^\\.\\//, \"\"),\n pluginDirName,\n );\n plugins.push(plugin);\n verbose(` [OK] ${plugin.name}`);\n }\n\n const sortedPlugins = sortBy(plugins, (p) => p.name);\n\n // Field order matters: the object is serialized verbatim into marketplace.json,\n // so conditional fields keep the same positions the previous emissions used.\n return {\n $schema: MARKETPLACE_SCHEMA_URL,\n name: options.name,\n version: options.version ?? DEFAULT_VERSION,\n owner: {\n name: options.ownerName,\n ...(options.ownerEmail ? { email: options.ownerEmail } : {}),\n },\n metadata: {\n pluginRoot: options.pluginRoot,\n },\n plugins: sortedPlugins,\n ...(options.description ? { description: options.description } : {}),\n };\n}\n\n/**\n * Refuses a marketplace name that belongs to a namespace the author does not own,\n * or returns null when the name is theirs to take.\n *\n * `packageName` is package.json's own `name`. It is read for one purpose: the public\n * catalogue publishes under a reserved name legitimately, and\n * {@link PUBLIC_CATALOGUE_PACKAGE} is what distinguishes it from a claimant.\n */\nexport function validateMarketplaceName(\n marketplaceName: string,\n packageName: string,\n): string | null {\n if (!RESERVED_MARKETPLACE_NAMES.includes(marketplaceName)) return null;\n if (isCatalogueOwnReservedName(marketplaceName, packageName)) return null;\n\n return reservedNameError(marketplaceName);\n}\n\n/**\n * Refuses a marketplace whose skill ids fall outside its own namespace, or returns\n * null when every id carries it.\n *\n * The public catalogue is exempt: its skills ship unprefixed by design and\n * `agents-inc` IS the namespace they occupy. Reading that exemption off the NAME is\n * only safe because {@link validateMarketplaceName} runs first and lets nothing but\n * the catalogue's own package hold it — the two checks are a pair, in that order.\n */\nexport function validateSkillIdNamespace(marketplace: Marketplace): string | null {\n if (marketplace.name === DEFAULT_PUBLIC_SOURCE_NAME) return null;\n\n const foreignIds = marketplace.plugins\n .filter((plugin) => !carriesNamespace(plugin.name, marketplace.name))\n .map((plugin) => plugin.name);\n\n if (foreignIds.length === 0) return null;\n\n return namespaceViolationError(foreignIds, marketplace.name);\n}\n\n/**\n * Whether the reserved NAME this build publishes under is the catalogue's own, which\n * takes both halves: the name claimed is the catalogue's, and the package claiming it\n * is the catalogue's package.\n *\n * The load side asks a different question of the same identity: `isPublicCatalogueCheckout`\n * in `configuration/config.ts` asks whether a DIRECTORY is a checkout of that repository,\n * and reads the package name off it rather than being handed one.\n */\nfunction isCatalogueOwnReservedName(marketplaceName: string, packageName: string): boolean {\n return marketplaceName === DEFAULT_PUBLIC_SOURCE_NAME && packageName === PUBLIC_CATALOGUE_PACKAGE;\n}\n\nfunction carriesNamespace(skillId: string, marketplaceName: string): boolean {\n return skillId.startsWith(`${marketplaceName}-`);\n}\n\nfunction reservedNameError(marketplaceName: string): string {\n return (\n `Marketplace name '${marketplaceName}' is reserved. ` +\n `'${DEFAULT_PUBLIC_SOURCE_NAME}' is the public catalogue's own namespace, and ` +\n `'${EXTERNAL_SKILL_NAMESPACE}' and '${LOCAL_SKILL_NAMESPACE}' hold the skills that belong to ` +\n `no marketplace — publishing under any of them claims skill ids that are not this ` +\n `marketplace's. Choose a name of your own: set package.json 'name', or pass --name.`\n );\n}\n\nfunction namespaceViolationError(foreignIds: string[], marketplaceName: string): string {\n const listed = foreignIds\n .slice(0, MAX_REPORTED_NAMESPACE_VIOLATIONS)\n .map((id) => ` ${id} -> ${marketplaceName}-${id}`);\n const unlisted = foreignIds.length - listed.length;\n\n return [\n `Marketplace '${marketplaceName}' ships ${foreignIds.length} skill id(s) outside its own ` +\n `namespace. Every skill id must begin with the marketplace's name:`,\n ...listed,\n ...(unlisted > 0 ? [` ... and ${unlisted} more`] : []),\n `Rename each skill directory and the id in its metadata, re-run 'build plugins', then ` +\n `build the marketplace again.`,\n ].join(\"\\n\");\n}\n\nexport async function writeMarketplace(\n outputPath: string,\n marketplace: Marketplace,\n): Promise<void> {\n await ensureDir(path.dirname(outputPath));\n const content = `${JSON.stringify(marketplace, null, 2)}\\n`;\n await writeFile(outputPath, content);\n}\n\nexport function getMarketplaceStats(marketplace: Marketplace): {\n total: number;\n byCategory: Record<string, number>;\n} {\n return {\n total: marketplace.plugins.length,\n byCategory: countBy(marketplace.plugins, (p) => p.category ?? \"uncategorized\"),\n };\n}\n"],"mappings":"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","names":[]}
|