@cleocode/caamp 0.4.0 → 0.5.1
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/README.md +32 -10
- package/dist/{chunk-ZYINKJDE.js → chunk-YCSZGZ5W.js} +712 -270
- package/dist/chunk-YCSZGZ5W.js.map +1 -0
- package/dist/cli.d.ts +0 -0
- package/dist/cli.js +259 -71
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +362 -22
- package/dist/index.js +33 -1
- package/dist/index.js.map +0 -0
- package/package.json +7 -3
- package/providers/registry.json +0 -49
- package/dist/chunk-ZYINKJDE.js.map +0 -1
|
@@ -1,58 +1,191 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/core/paths/standard.ts
|
|
8
|
+
import { existsSync } from "fs";
|
|
3
9
|
import { homedir } from "os";
|
|
4
|
-
import { join,
|
|
5
|
-
|
|
6
|
-
function findRegistryPath() {
|
|
7
|
-
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
const devPath = join(thisDir, "..", "..", "..", "providers", "registry.json");
|
|
9
|
-
if (existsSync(devPath)) return devPath;
|
|
10
|
-
const distPath = join(thisDir, "..", "providers", "registry.json");
|
|
11
|
-
if (existsSync(distPath)) return distPath;
|
|
12
|
-
let dir = thisDir;
|
|
13
|
-
for (let i = 0; i < 5; i++) {
|
|
14
|
-
const candidate = join(dir, "providers", "registry.json");
|
|
15
|
-
if (existsSync(candidate)) return candidate;
|
|
16
|
-
dir = dirname(dir);
|
|
17
|
-
}
|
|
18
|
-
throw new Error(`Cannot find providers/registry.json (searched from ${thisDir})`);
|
|
19
|
-
}
|
|
20
|
-
var _registry = null;
|
|
21
|
-
var _providers = null;
|
|
22
|
-
var _aliasMap = null;
|
|
23
|
-
function getPlatformPaths() {
|
|
10
|
+
import { dirname, isAbsolute, join, resolve } from "path";
|
|
11
|
+
function getPlatformLocations() {
|
|
24
12
|
const home = homedir();
|
|
25
13
|
const platform = process.platform;
|
|
26
14
|
if (platform === "win32") {
|
|
27
15
|
const appData = process.env["APPDATA"] ?? join(home, "AppData", "Roaming");
|
|
28
16
|
return {
|
|
17
|
+
home,
|
|
29
18
|
config: appData,
|
|
30
19
|
vscodeConfig: join(appData, "Code", "User"),
|
|
31
20
|
zedConfig: join(appData, "Zed"),
|
|
32
|
-
claudeDesktopConfig: join(appData, "Claude")
|
|
21
|
+
claudeDesktopConfig: join(appData, "Claude"),
|
|
22
|
+
applications: []
|
|
33
23
|
};
|
|
34
|
-
}
|
|
24
|
+
}
|
|
25
|
+
if (platform === "darwin") {
|
|
26
|
+
const config2 = process.env["XDG_CONFIG_HOME"] ?? join(home, ".config");
|
|
35
27
|
return {
|
|
36
|
-
|
|
28
|
+
home,
|
|
29
|
+
config: config2,
|
|
37
30
|
vscodeConfig: join(home, "Library", "Application Support", "Code", "User"),
|
|
38
31
|
zedConfig: join(home, "Library", "Application Support", "Zed"),
|
|
39
|
-
claudeDesktopConfig: join(home, "Library", "Application Support", "Claude")
|
|
40
|
-
|
|
41
|
-
} else {
|
|
42
|
-
const config = process.env["XDG_CONFIG_HOME"] ?? join(home, ".config");
|
|
43
|
-
return {
|
|
44
|
-
config,
|
|
45
|
-
vscodeConfig: join(config, "Code", "User"),
|
|
46
|
-
zedConfig: join(config, "zed"),
|
|
47
|
-
claudeDesktopConfig: join(config, "Claude")
|
|
32
|
+
claudeDesktopConfig: join(home, "Library", "Application Support", "Claude"),
|
|
33
|
+
applications: ["/Applications", join(home, "Applications")]
|
|
48
34
|
};
|
|
49
35
|
}
|
|
36
|
+
const config = process.env["XDG_CONFIG_HOME"] ?? join(home, ".config");
|
|
37
|
+
return {
|
|
38
|
+
home,
|
|
39
|
+
config,
|
|
40
|
+
vscodeConfig: join(config, "Code", "User"),
|
|
41
|
+
zedConfig: join(config, "zed"),
|
|
42
|
+
claudeDesktopConfig: join(config, "Claude"),
|
|
43
|
+
applications: []
|
|
44
|
+
};
|
|
50
45
|
}
|
|
51
|
-
function
|
|
46
|
+
function normalizeHomeOverride(value) {
|
|
52
47
|
const home = homedir();
|
|
53
|
-
const
|
|
54
|
-
|
|
48
|
+
const trimmed = value.trim();
|
|
49
|
+
if (trimmed.startsWith("~/")) {
|
|
50
|
+
return join(home, trimmed.slice(2));
|
|
51
|
+
}
|
|
52
|
+
if (trimmed === "~") {
|
|
53
|
+
return home;
|
|
54
|
+
}
|
|
55
|
+
if (isAbsolute(trimmed)) {
|
|
56
|
+
return resolve(trimmed);
|
|
57
|
+
}
|
|
58
|
+
return resolve(home, trimmed);
|
|
55
59
|
}
|
|
60
|
+
function getAgentsHome() {
|
|
61
|
+
const override = process.env["AGENTS_HOME"];
|
|
62
|
+
if (override && override.trim().length > 0) {
|
|
63
|
+
return normalizeHomeOverride(override);
|
|
64
|
+
}
|
|
65
|
+
return join(homedir(), ".agents");
|
|
66
|
+
}
|
|
67
|
+
function getProjectAgentsDir(projectRoot = process.cwd()) {
|
|
68
|
+
return join(projectRoot, ".agents");
|
|
69
|
+
}
|
|
70
|
+
function resolveProjectPath(relativePath, projectDir = process.cwd()) {
|
|
71
|
+
return join(projectDir, relativePath);
|
|
72
|
+
}
|
|
73
|
+
function getCanonicalSkillsDir() {
|
|
74
|
+
return join(getAgentsHome(), "skills");
|
|
75
|
+
}
|
|
76
|
+
function getLockFilePath() {
|
|
77
|
+
return join(getAgentsHome(), ".caamp-lock.json");
|
|
78
|
+
}
|
|
79
|
+
function getAgentsMcpDir(scope = "global", projectDir) {
|
|
80
|
+
if (scope === "global") return join(getAgentsHome(), "mcp");
|
|
81
|
+
return join(projectDir ?? process.cwd(), ".agents", "mcp");
|
|
82
|
+
}
|
|
83
|
+
function getAgentsMcpServersPath(scope = "global", projectDir) {
|
|
84
|
+
return join(getAgentsMcpDir(scope, projectDir), "servers.json");
|
|
85
|
+
}
|
|
86
|
+
function getAgentsInstructFile(scope = "global", projectDir) {
|
|
87
|
+
if (scope === "global") return join(getAgentsHome(), "AGENTS.md");
|
|
88
|
+
return join(projectDir ?? process.cwd(), ".agents", "AGENTS.md");
|
|
89
|
+
}
|
|
90
|
+
function getAgentsConfigPath(scope = "global", projectDir) {
|
|
91
|
+
if (scope === "global") return join(getAgentsHome(), "config.toml");
|
|
92
|
+
return join(projectDir ?? process.cwd(), ".agents", "config.toml");
|
|
93
|
+
}
|
|
94
|
+
function getAgentsWikiDir(scope = "global", projectDir) {
|
|
95
|
+
if (scope === "global") return join(getAgentsHome(), "wiki");
|
|
96
|
+
return join(projectDir ?? process.cwd(), ".agents", "wiki");
|
|
97
|
+
}
|
|
98
|
+
function getAgentsSpecDir(scope = "global", projectDir) {
|
|
99
|
+
if (scope === "global") return join(getAgentsHome(), "spec");
|
|
100
|
+
return join(projectDir ?? process.cwd(), ".agents", "spec");
|
|
101
|
+
}
|
|
102
|
+
function getAgentsLinksDir(scope = "global", projectDir) {
|
|
103
|
+
if (scope === "global") return join(getAgentsHome(), "links");
|
|
104
|
+
return join(projectDir ?? process.cwd(), ".agents", "links");
|
|
105
|
+
}
|
|
106
|
+
function resolveRegistryTemplatePath(template) {
|
|
107
|
+
const locations = getPlatformLocations();
|
|
108
|
+
return template.replace(/\$HOME/g, locations.home).replace(/\$CONFIG/g, locations.config).replace(/\$VSCODE_CONFIG/g, locations.vscodeConfig).replace(/\$ZED_CONFIG/g, locations.zedConfig).replace(/\$CLAUDE_DESKTOP_CONFIG/g, locations.claudeDesktopConfig).replace(/\$AGENTS_HOME/g, getAgentsHome());
|
|
109
|
+
}
|
|
110
|
+
function resolveProviderConfigPath(provider, scope, projectDir = process.cwd()) {
|
|
111
|
+
if (scope === "global") {
|
|
112
|
+
return provider.configPathGlobal;
|
|
113
|
+
}
|
|
114
|
+
if (!provider.configPathProject) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
return resolveProjectPath(provider.configPathProject, projectDir);
|
|
118
|
+
}
|
|
119
|
+
function resolvePreferredConfigScope(provider, useGlobalFlag) {
|
|
120
|
+
if (useGlobalFlag) {
|
|
121
|
+
return "global";
|
|
122
|
+
}
|
|
123
|
+
return provider.configPathProject ? "project" : "global";
|
|
124
|
+
}
|
|
125
|
+
function resolveProviderSkillsDir(provider, scope, projectDir = process.cwd()) {
|
|
126
|
+
if (scope === "global") {
|
|
127
|
+
return provider.pathSkills;
|
|
128
|
+
}
|
|
129
|
+
return resolveProjectPath(provider.pathProjectSkills, projectDir);
|
|
130
|
+
}
|
|
131
|
+
function resolveProviderProjectPath(provider, projectDir = process.cwd()) {
|
|
132
|
+
return resolveProjectPath(provider.pathProject, projectDir);
|
|
133
|
+
}
|
|
134
|
+
function resolveProvidersRegistryPath(startDir) {
|
|
135
|
+
const candidates = [
|
|
136
|
+
join(startDir, "..", "..", "..", "providers", "registry.json"),
|
|
137
|
+
join(startDir, "..", "providers", "registry.json")
|
|
138
|
+
];
|
|
139
|
+
for (const candidate of candidates) {
|
|
140
|
+
if (existsSync(candidate)) {
|
|
141
|
+
return candidate;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
let current = startDir;
|
|
145
|
+
for (let i = 0; i < 8; i += 1) {
|
|
146
|
+
const candidate = join(current, "providers", "registry.json");
|
|
147
|
+
if (existsSync(candidate)) {
|
|
148
|
+
return candidate;
|
|
149
|
+
}
|
|
150
|
+
current = dirname(current);
|
|
151
|
+
}
|
|
152
|
+
throw new Error(`Cannot find providers/registry.json (searched from ${startDir})`);
|
|
153
|
+
}
|
|
154
|
+
function normalizeSkillSubPath(path) {
|
|
155
|
+
if (!path) return void 0;
|
|
156
|
+
const normalized = path.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\/SKILL\.md$/i, "").trim();
|
|
157
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
158
|
+
}
|
|
159
|
+
function buildSkillSubPathCandidates(marketplacePath, parsedPath) {
|
|
160
|
+
const candidates = [];
|
|
161
|
+
const base = normalizeSkillSubPath(marketplacePath);
|
|
162
|
+
const parsed = normalizeSkillSubPath(parsedPath);
|
|
163
|
+
if (base) candidates.push(base);
|
|
164
|
+
if (parsed) candidates.push(parsed);
|
|
165
|
+
const knownPrefixes = [".agents", ".claude"];
|
|
166
|
+
for (const value of [base, parsed]) {
|
|
167
|
+
if (!value || !value.startsWith("skills/")) continue;
|
|
168
|
+
for (const prefix of knownPrefixes) {
|
|
169
|
+
candidates.push(`${prefix}/${value}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (candidates.length === 0) {
|
|
173
|
+
candidates.push(void 0);
|
|
174
|
+
}
|
|
175
|
+
return Array.from(new Set(candidates));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/core/registry/providers.ts
|
|
179
|
+
import { readFileSync } from "fs";
|
|
180
|
+
import { dirname as dirname2 } from "path";
|
|
181
|
+
import { fileURLToPath } from "url";
|
|
182
|
+
function findRegistryPath() {
|
|
183
|
+
const thisDir = dirname2(fileURLToPath(import.meta.url));
|
|
184
|
+
return resolveProvidersRegistryPath(thisDir);
|
|
185
|
+
}
|
|
186
|
+
var _registry = null;
|
|
187
|
+
var _providers = null;
|
|
188
|
+
var _aliasMap = null;
|
|
56
189
|
function resolveProvider(raw) {
|
|
57
190
|
return {
|
|
58
191
|
id: raw.id,
|
|
@@ -60,19 +193,19 @@ function resolveProvider(raw) {
|
|
|
60
193
|
vendor: raw.vendor,
|
|
61
194
|
agentFlag: raw.agentFlag,
|
|
62
195
|
aliases: raw.aliases,
|
|
63
|
-
pathGlobal:
|
|
196
|
+
pathGlobal: resolveRegistryTemplatePath(raw.pathGlobal),
|
|
64
197
|
pathProject: raw.pathProject,
|
|
65
198
|
instructFile: raw.instructFile,
|
|
66
199
|
configKey: raw.configKey,
|
|
67
200
|
configFormat: raw.configFormat,
|
|
68
|
-
configPathGlobal:
|
|
201
|
+
configPathGlobal: resolveRegistryTemplatePath(raw.configPathGlobal),
|
|
69
202
|
configPathProject: raw.configPathProject,
|
|
70
|
-
pathSkills:
|
|
203
|
+
pathSkills: resolveRegistryTemplatePath(raw.pathSkills),
|
|
71
204
|
pathProjectSkills: raw.pathProjectSkills,
|
|
72
205
|
detection: {
|
|
73
206
|
methods: raw.detection.methods,
|
|
74
207
|
binary: raw.detection.binary,
|
|
75
|
-
directories: raw.detection.directories?.map(
|
|
208
|
+
directories: raw.detection.directories?.map(resolveRegistryTemplatePath),
|
|
76
209
|
appBundle: raw.detection.appBundle,
|
|
77
210
|
flatpakId: raw.detection.flatpakId
|
|
78
211
|
},
|
|
@@ -105,16 +238,17 @@ function ensureProviders() {
|
|
|
105
238
|
}
|
|
106
239
|
function getAllProviders() {
|
|
107
240
|
ensureProviders();
|
|
241
|
+
if (!_providers) return [];
|
|
108
242
|
return Array.from(_providers.values());
|
|
109
243
|
}
|
|
110
244
|
function getProvider(idOrAlias) {
|
|
111
245
|
ensureProviders();
|
|
112
|
-
const resolved = _aliasMap
|
|
113
|
-
return _providers
|
|
246
|
+
const resolved = _aliasMap?.get(idOrAlias) ?? idOrAlias;
|
|
247
|
+
return _providers?.get(resolved);
|
|
114
248
|
}
|
|
115
249
|
function resolveAlias(idOrAlias) {
|
|
116
250
|
ensureProviders();
|
|
117
|
-
return _aliasMap
|
|
251
|
+
return _aliasMap?.get(idOrAlias) ?? idOrAlias;
|
|
118
252
|
}
|
|
119
253
|
function getProvidersByPriority(priority) {
|
|
120
254
|
return getAllProviders().filter((p) => p.priority === priority);
|
|
@@ -134,7 +268,7 @@ function getInstructionFiles() {
|
|
|
134
268
|
}
|
|
135
269
|
function getProviderCount() {
|
|
136
270
|
ensureProviders();
|
|
137
|
-
return _providers
|
|
271
|
+
return _providers?.size ?? 0;
|
|
138
272
|
}
|
|
139
273
|
function getRegistryVersion() {
|
|
140
274
|
return loadRegistry().version;
|
|
@@ -163,6 +297,8 @@ function isQuiet() {
|
|
|
163
297
|
import { existsSync as existsSync2 } from "fs";
|
|
164
298
|
import { execFileSync } from "child_process";
|
|
165
299
|
import { join as join2 } from "path";
|
|
300
|
+
var DEFAULT_DETECTION_CACHE_TTL_MS = 3e4;
|
|
301
|
+
var detectionCache = null;
|
|
166
302
|
function checkBinary(binary) {
|
|
167
303
|
try {
|
|
168
304
|
const cmd = process.platform === "win32" ? "where" : "which";
|
|
@@ -177,7 +313,8 @@ function checkDirectory(dir) {
|
|
|
177
313
|
}
|
|
178
314
|
function checkAppBundle(appName) {
|
|
179
315
|
if (process.platform !== "darwin") return false;
|
|
180
|
-
|
|
316
|
+
const applications = getPlatformLocations().applications;
|
|
317
|
+
return applications.some((base) => existsSync2(join2(base, appName)));
|
|
181
318
|
}
|
|
182
319
|
function checkFlatpak(flatpakId) {
|
|
183
320
|
if (process.platform !== "linux") return false;
|
|
@@ -229,24 +366,71 @@ function detectProvider(provider) {
|
|
|
229
366
|
projectDetected: false
|
|
230
367
|
};
|
|
231
368
|
}
|
|
369
|
+
function providerSignature(provider) {
|
|
370
|
+
return JSON.stringify({
|
|
371
|
+
id: provider.id,
|
|
372
|
+
methods: provider.detection.methods,
|
|
373
|
+
binary: provider.detection.binary,
|
|
374
|
+
directories: provider.detection.directories,
|
|
375
|
+
appBundle: provider.detection.appBundle,
|
|
376
|
+
flatpakId: provider.detection.flatpakId
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
function buildProvidersSignature(providers) {
|
|
380
|
+
return providers.map(providerSignature).join("|");
|
|
381
|
+
}
|
|
382
|
+
function cloneDetectionResults(results) {
|
|
383
|
+
return results.map((result) => ({
|
|
384
|
+
provider: result.provider,
|
|
385
|
+
installed: result.installed,
|
|
386
|
+
methods: [...result.methods],
|
|
387
|
+
projectDetected: result.projectDetected
|
|
388
|
+
}));
|
|
389
|
+
}
|
|
390
|
+
function getCachedResults(signature, options) {
|
|
391
|
+
if (!detectionCache || options.forceRefresh) return null;
|
|
392
|
+
if (detectionCache.signature !== signature) return null;
|
|
393
|
+
const ttlMs = options.ttlMs ?? DEFAULT_DETECTION_CACHE_TTL_MS;
|
|
394
|
+
if (ttlMs <= 0) return null;
|
|
395
|
+
if (Date.now() - detectionCache.createdAt > ttlMs) return null;
|
|
396
|
+
return cloneDetectionResults(detectionCache.results);
|
|
397
|
+
}
|
|
398
|
+
function setCachedResults(signature, results) {
|
|
399
|
+
detectionCache = {
|
|
400
|
+
createdAt: Date.now(),
|
|
401
|
+
signature,
|
|
402
|
+
results: cloneDetectionResults(results)
|
|
403
|
+
};
|
|
404
|
+
}
|
|
232
405
|
function detectProjectProvider(provider, projectDir) {
|
|
233
406
|
if (!provider.pathProject) return false;
|
|
234
|
-
return existsSync2(
|
|
407
|
+
return existsSync2(resolveProviderProjectPath(provider, projectDir));
|
|
235
408
|
}
|
|
236
|
-
function detectAllProviders() {
|
|
409
|
+
function detectAllProviders(options = {}) {
|
|
237
410
|
const providers = getAllProviders();
|
|
238
|
-
|
|
411
|
+
const signature = buildProvidersSignature(providers);
|
|
412
|
+
const cached = getCachedResults(signature, options);
|
|
413
|
+
if (cached) {
|
|
414
|
+
debug(`detection cache hit for ${providers.length} providers`);
|
|
415
|
+
return cached;
|
|
416
|
+
}
|
|
417
|
+
const results = providers.map(detectProvider);
|
|
418
|
+
setCachedResults(signature, results);
|
|
419
|
+
return cloneDetectionResults(results);
|
|
239
420
|
}
|
|
240
|
-
function getInstalledProviders() {
|
|
241
|
-
return detectAllProviders().filter((r) => r.installed).map((r) => r.provider);
|
|
421
|
+
function getInstalledProviders(options = {}) {
|
|
422
|
+
return detectAllProviders(options).filter((r) => r.installed).map((r) => r.provider);
|
|
242
423
|
}
|
|
243
|
-
function detectProjectProviders(projectDir) {
|
|
244
|
-
const results = detectAllProviders();
|
|
424
|
+
function detectProjectProviders(projectDir, options = {}) {
|
|
425
|
+
const results = detectAllProviders(options);
|
|
245
426
|
return results.map((r) => ({
|
|
246
427
|
...r,
|
|
247
428
|
projectDetected: detectProjectProvider(r.provider, projectDir)
|
|
248
429
|
}));
|
|
249
430
|
}
|
|
431
|
+
function resetDetectionCache() {
|
|
432
|
+
detectionCache = null;
|
|
433
|
+
}
|
|
250
434
|
|
|
251
435
|
// src/core/sources/parser.ts
|
|
252
436
|
var GITHUB_SHORTHAND = /^([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)(?:\/(.+))?$/;
|
|
@@ -261,13 +445,15 @@ function inferName(source, type) {
|
|
|
261
445
|
const url = new URL(source);
|
|
262
446
|
const parts = url.hostname.split(".");
|
|
263
447
|
if (parts.length >= 2) {
|
|
264
|
-
const
|
|
448
|
+
const fallback = parts[0] ?? source;
|
|
449
|
+
const secondLevel = parts[parts.length - 2] ?? fallback;
|
|
450
|
+
const brand = parts.length === 3 ? secondLevel : fallback;
|
|
265
451
|
if (brand !== "www" && brand !== "api" && brand !== "mcp") {
|
|
266
452
|
return brand;
|
|
267
453
|
}
|
|
268
|
-
return
|
|
454
|
+
return secondLevel;
|
|
269
455
|
}
|
|
270
|
-
return parts[0];
|
|
456
|
+
return parts[0] ?? source;
|
|
271
457
|
} catch {
|
|
272
458
|
return source;
|
|
273
459
|
}
|
|
@@ -284,6 +470,11 @@ function inferName(source, type) {
|
|
|
284
470
|
const match = source.match(/\/([^/]+?)(?:\.git)?$/);
|
|
285
471
|
return match?.[1] ?? source;
|
|
286
472
|
}
|
|
473
|
+
if (type === "local") {
|
|
474
|
+
const normalized = source.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
475
|
+
const lastSegment = normalized.split("/").pop();
|
|
476
|
+
return lastSegment ?? source;
|
|
477
|
+
}
|
|
287
478
|
if (type === "command") {
|
|
288
479
|
const parts = source.split(/\s+/);
|
|
289
480
|
const command = parts.find((p) => !p.startsWith("-") && p !== "npx" && p !== "node" && p !== "python" && p !== "python3");
|
|
@@ -294,24 +485,34 @@ function inferName(source, type) {
|
|
|
294
485
|
function parseSource(input) {
|
|
295
486
|
const ghUrlMatch = input.match(GITHUB_URL);
|
|
296
487
|
if (ghUrlMatch) {
|
|
488
|
+
const owner = ghUrlMatch[1];
|
|
489
|
+
const repo = ghUrlMatch[2];
|
|
490
|
+
if (!owner || !repo) {
|
|
491
|
+
return { type: "command", value: input, inferredName: inferName(input, "command") };
|
|
492
|
+
}
|
|
297
493
|
return {
|
|
298
494
|
type: "github",
|
|
299
495
|
value: input,
|
|
300
|
-
inferredName:
|
|
301
|
-
owner
|
|
302
|
-
repo
|
|
496
|
+
inferredName: repo,
|
|
497
|
+
owner,
|
|
498
|
+
repo,
|
|
303
499
|
ref: ghUrlMatch[3],
|
|
304
500
|
path: ghUrlMatch[4]
|
|
305
501
|
};
|
|
306
502
|
}
|
|
307
503
|
const glUrlMatch = input.match(GITLAB_URL);
|
|
308
504
|
if (glUrlMatch) {
|
|
505
|
+
const owner = glUrlMatch[1];
|
|
506
|
+
const repo = glUrlMatch[2];
|
|
507
|
+
if (!owner || !repo) {
|
|
508
|
+
return { type: "command", value: input, inferredName: inferName(input, "command") };
|
|
509
|
+
}
|
|
309
510
|
return {
|
|
310
511
|
type: "gitlab",
|
|
311
512
|
value: input,
|
|
312
|
-
inferredName:
|
|
313
|
-
owner
|
|
314
|
-
repo
|
|
513
|
+
inferredName: repo,
|
|
514
|
+
owner,
|
|
515
|
+
repo,
|
|
315
516
|
ref: glUrlMatch[3],
|
|
316
517
|
path: glUrlMatch[4]
|
|
317
518
|
};
|
|
@@ -332,12 +533,17 @@ function parseSource(input) {
|
|
|
332
533
|
}
|
|
333
534
|
const ghShorthand = input.match(GITHUB_SHORTHAND);
|
|
334
535
|
if (ghShorthand && !NPM_SCOPED.test(input)) {
|
|
536
|
+
const owner = ghShorthand[1];
|
|
537
|
+
const repo = ghShorthand[2];
|
|
538
|
+
if (!owner || !repo) {
|
|
539
|
+
return { type: "command", value: input, inferredName: inferName(input, "command") };
|
|
540
|
+
}
|
|
335
541
|
return {
|
|
336
542
|
type: "github",
|
|
337
|
-
value: `https://github.com/${
|
|
338
|
-
inferredName:
|
|
339
|
-
owner
|
|
340
|
-
repo
|
|
543
|
+
value: `https://github.com/${owner}/${repo}`,
|
|
544
|
+
inferredName: repo,
|
|
545
|
+
owner,
|
|
546
|
+
repo,
|
|
341
547
|
path: ghShorthand[3]
|
|
342
548
|
};
|
|
343
549
|
}
|
|
@@ -368,15 +574,23 @@ function isMarketplaceScoped(input) {
|
|
|
368
574
|
// src/core/skills/installer.ts
|
|
369
575
|
import { mkdir, symlink, rm, cp } from "fs/promises";
|
|
370
576
|
import { existsSync as existsSync3, lstatSync } from "fs";
|
|
371
|
-
import { homedir as homedir2 } from "os";
|
|
372
577
|
import { join as join3 } from "path";
|
|
373
|
-
|
|
578
|
+
|
|
579
|
+
// src/core/paths/agents.ts
|
|
580
|
+
var AGENTS_HOME = getAgentsHome();
|
|
581
|
+
var LOCK_FILE_PATH = getLockFilePath();
|
|
582
|
+
var CANONICAL_SKILLS_DIR = getCanonicalSkillsDir();
|
|
583
|
+
var AGENTS_MCP_DIR = getAgentsMcpDir();
|
|
584
|
+
var AGENTS_MCP_SERVERS_PATH = getAgentsMcpServersPath();
|
|
585
|
+
var AGENTS_CONFIG_PATH = getAgentsConfigPath();
|
|
586
|
+
|
|
587
|
+
// src/core/skills/installer.ts
|
|
374
588
|
async function ensureCanonicalDir() {
|
|
375
|
-
await mkdir(
|
|
589
|
+
await mkdir(CANONICAL_SKILLS_DIR, { recursive: true });
|
|
376
590
|
}
|
|
377
591
|
async function installToCanonical(sourcePath, skillName) {
|
|
378
592
|
await ensureCanonicalDir();
|
|
379
|
-
const targetDir = join3(
|
|
593
|
+
const targetDir = join3(CANONICAL_SKILLS_DIR, skillName);
|
|
380
594
|
if (existsSync3(targetDir)) {
|
|
381
595
|
await rm(targetDir, { recursive: true });
|
|
382
596
|
}
|
|
@@ -384,7 +598,11 @@ async function installToCanonical(sourcePath, skillName) {
|
|
|
384
598
|
return targetDir;
|
|
385
599
|
}
|
|
386
600
|
async function linkToAgent(canonicalPath, provider, skillName, isGlobal, projectDir) {
|
|
387
|
-
const targetSkillsDir =
|
|
601
|
+
const targetSkillsDir = resolveProviderSkillsDir(
|
|
602
|
+
provider,
|
|
603
|
+
isGlobal ? "global" : "project",
|
|
604
|
+
projectDir
|
|
605
|
+
);
|
|
388
606
|
if (!targetSkillsDir) {
|
|
389
607
|
return { success: false, error: `Provider ${provider.id} has no skills directory` };
|
|
390
608
|
}
|
|
@@ -437,7 +655,11 @@ async function removeSkill(skillName, providers, isGlobal, projectDir) {
|
|
|
437
655
|
const removed = [];
|
|
438
656
|
const errors = [];
|
|
439
657
|
for (const provider of providers) {
|
|
440
|
-
const skillsDir =
|
|
658
|
+
const skillsDir = resolveProviderSkillsDir(
|
|
659
|
+
provider,
|
|
660
|
+
isGlobal ? "global" : "project",
|
|
661
|
+
projectDir
|
|
662
|
+
);
|
|
441
663
|
if (!skillsDir) continue;
|
|
442
664
|
const linkPath = join3(skillsDir, skillName);
|
|
443
665
|
if (existsSync3(linkPath)) {
|
|
@@ -449,7 +671,7 @@ async function removeSkill(skillName, providers, isGlobal, projectDir) {
|
|
|
449
671
|
}
|
|
450
672
|
}
|
|
451
673
|
}
|
|
452
|
-
const canonicalPath = join3(
|
|
674
|
+
const canonicalPath = join3(CANONICAL_SKILLS_DIR, skillName);
|
|
453
675
|
if (existsSync3(canonicalPath)) {
|
|
454
676
|
try {
|
|
455
677
|
await rm(canonicalPath, { recursive: true });
|
|
@@ -460,62 +682,95 @@ async function removeSkill(skillName, providers, isGlobal, projectDir) {
|
|
|
460
682
|
return { removed, errors };
|
|
461
683
|
}
|
|
462
684
|
async function listCanonicalSkills() {
|
|
463
|
-
if (!existsSync3(
|
|
685
|
+
if (!existsSync3(CANONICAL_SKILLS_DIR)) return [];
|
|
464
686
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
465
|
-
const entries = await readdir2(
|
|
687
|
+
const entries = await readdir2(CANONICAL_SKILLS_DIR, { withFileTypes: true });
|
|
466
688
|
return entries.filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => e.name);
|
|
467
689
|
}
|
|
468
690
|
|
|
469
691
|
// src/core/lock-utils.ts
|
|
470
|
-
import { readFile
|
|
692
|
+
import { open, readFile, writeFile, mkdir as mkdir2, rm as rm2, rename } from "fs/promises";
|
|
471
693
|
import { existsSync as existsSync4 } from "fs";
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
694
|
+
var LOCK_GUARD_PATH = `${LOCK_FILE_PATH}.lock`;
|
|
695
|
+
function sleep(ms) {
|
|
696
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
697
|
+
}
|
|
698
|
+
async function acquireLockGuard(retries = 40, delayMs = 25) {
|
|
699
|
+
await mkdir2(AGENTS_HOME, { recursive: true });
|
|
700
|
+
for (let attempt = 0; attempt < retries; attempt += 1) {
|
|
701
|
+
try {
|
|
702
|
+
const handle = await open(LOCK_GUARD_PATH, "wx");
|
|
703
|
+
await handle.close();
|
|
704
|
+
return;
|
|
705
|
+
} catch (error) {
|
|
706
|
+
if (!(error instanceof Error) || !("code" in error) || error.code !== "EEXIST") {
|
|
707
|
+
throw error;
|
|
708
|
+
}
|
|
709
|
+
await sleep(delayMs);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
throw new Error("Timed out waiting for lock file guard");
|
|
713
|
+
}
|
|
714
|
+
async function releaseLockGuard() {
|
|
715
|
+
await rm2(LOCK_GUARD_PATH, { force: true });
|
|
716
|
+
}
|
|
717
|
+
async function writeLockFileUnsafe(lock) {
|
|
718
|
+
const tmpPath = `${LOCK_FILE_PATH}.tmp-${process.pid}-${Date.now()}`;
|
|
719
|
+
await writeFile(tmpPath, JSON.stringify(lock, null, 2) + "\n", "utf-8");
|
|
720
|
+
await rename(tmpPath, LOCK_FILE_PATH);
|
|
721
|
+
}
|
|
476
722
|
async function readLockFile() {
|
|
477
723
|
try {
|
|
478
|
-
if (!existsSync4(
|
|
724
|
+
if (!existsSync4(LOCK_FILE_PATH)) {
|
|
479
725
|
return { version: 1, skills: {}, mcpServers: {} };
|
|
480
726
|
}
|
|
481
|
-
const content = await
|
|
727
|
+
const content = await readFile(LOCK_FILE_PATH, "utf-8");
|
|
482
728
|
return JSON.parse(content);
|
|
483
729
|
} catch {
|
|
484
730
|
return { version: 1, skills: {}, mcpServers: {} };
|
|
485
731
|
}
|
|
486
732
|
}
|
|
487
|
-
async function
|
|
488
|
-
await
|
|
489
|
-
|
|
733
|
+
async function updateLockFile(updater) {
|
|
734
|
+
await acquireLockGuard();
|
|
735
|
+
try {
|
|
736
|
+
const lock = await readLockFile();
|
|
737
|
+
await updater(lock);
|
|
738
|
+
await writeLockFileUnsafe(lock);
|
|
739
|
+
return lock;
|
|
740
|
+
} finally {
|
|
741
|
+
await releaseLockGuard();
|
|
742
|
+
}
|
|
490
743
|
}
|
|
491
744
|
|
|
492
745
|
// src/core/skills/lock.ts
|
|
493
746
|
import { simpleGit } from "simple-git";
|
|
494
747
|
async function recordSkillInstall(skillName, scopedName, source, sourceType, agents, canonicalPath, isGlobal, projectDir, version) {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
748
|
+
await updateLockFile((lock) => {
|
|
749
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
750
|
+
const existing = lock.skills[skillName];
|
|
751
|
+
lock.skills[skillName] = {
|
|
752
|
+
name: skillName,
|
|
753
|
+
scopedName: existing?.scopedName ?? scopedName,
|
|
754
|
+
source: existing?.source ?? source,
|
|
755
|
+
sourceType: existing?.sourceType ?? sourceType,
|
|
756
|
+
version: version ?? existing?.version,
|
|
757
|
+
installedAt: existing?.installedAt ?? now,
|
|
758
|
+
updatedAt: now,
|
|
759
|
+
agents: [.../* @__PURE__ */ new Set([...existing?.agents ?? [], ...agents])],
|
|
760
|
+
canonicalPath,
|
|
761
|
+
isGlobal: existing?.isGlobal ?? isGlobal,
|
|
762
|
+
projectDir: existing?.projectDir ?? projectDir
|
|
763
|
+
};
|
|
764
|
+
});
|
|
512
765
|
}
|
|
513
766
|
async function removeSkillFromLock(skillName) {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
767
|
+
let removed = false;
|
|
768
|
+
await updateLockFile((lock) => {
|
|
769
|
+
if (!(skillName in lock.skills)) return;
|
|
770
|
+
delete lock.skills[skillName];
|
|
771
|
+
removed = true;
|
|
772
|
+
});
|
|
773
|
+
return removed;
|
|
519
774
|
}
|
|
520
775
|
async function getTrackedSkills() {
|
|
521
776
|
const lock = await readLockFile();
|
|
@@ -827,33 +1082,33 @@ var MarketplaceClient = class {
|
|
|
827
1082
|
};
|
|
828
1083
|
|
|
829
1084
|
// src/core/skills/discovery.ts
|
|
830
|
-
import { readFile as
|
|
1085
|
+
import { readFile as readFile2, readdir } from "fs/promises";
|
|
831
1086
|
import { existsSync as existsSync5 } from "fs";
|
|
832
|
-
import { join as
|
|
1087
|
+
import { join as join4 } from "path";
|
|
833
1088
|
import matter from "gray-matter";
|
|
834
1089
|
async function parseSkillFile(filePath) {
|
|
835
1090
|
try {
|
|
836
|
-
const content = await
|
|
1091
|
+
const content = await readFile2(filePath, "utf-8");
|
|
837
1092
|
const { data } = matter(content);
|
|
838
|
-
if (!data
|
|
1093
|
+
if (!data.name || !data.description) {
|
|
839
1094
|
return null;
|
|
840
1095
|
}
|
|
841
|
-
const allowedTools = data["allowed-tools"] ?? data
|
|
1096
|
+
const allowedTools = data["allowed-tools"] ?? data.allowedTools;
|
|
842
1097
|
return {
|
|
843
|
-
name: String(data
|
|
844
|
-
description: String(data
|
|
845
|
-
license: data
|
|
846
|
-
compatibility: data
|
|
847
|
-
metadata: data
|
|
1098
|
+
name: String(data.name),
|
|
1099
|
+
description: String(data.description),
|
|
1100
|
+
license: data.license ? String(data.license) : void 0,
|
|
1101
|
+
compatibility: data.compatibility ? String(data.compatibility) : void 0,
|
|
1102
|
+
metadata: data.metadata,
|
|
848
1103
|
allowedTools: typeof allowedTools === "string" ? allowedTools.split(/\s+/) : Array.isArray(allowedTools) ? allowedTools.map(String) : void 0,
|
|
849
|
-
version: data
|
|
1104
|
+
version: data.version ? String(data.version) : void 0
|
|
850
1105
|
};
|
|
851
1106
|
} catch {
|
|
852
1107
|
return null;
|
|
853
1108
|
}
|
|
854
1109
|
}
|
|
855
1110
|
async function discoverSkill(skillDir) {
|
|
856
|
-
const skillFile =
|
|
1111
|
+
const skillFile = join4(skillDir, "SKILL.md");
|
|
857
1112
|
if (!existsSync5(skillFile)) return null;
|
|
858
1113
|
const metadata = await parseSkillFile(skillFile);
|
|
859
1114
|
if (!metadata) return null;
|
|
@@ -870,7 +1125,7 @@ async function discoverSkills(rootDir) {
|
|
|
870
1125
|
const skills = [];
|
|
871
1126
|
for (const entry of entries) {
|
|
872
1127
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
873
|
-
const skillDir =
|
|
1128
|
+
const skillDir = join4(rootDir, entry.name);
|
|
874
1129
|
const skill = await discoverSkill(skillDir);
|
|
875
1130
|
if (skill) {
|
|
876
1131
|
skills.push(skill);
|
|
@@ -893,6 +1148,135 @@ async function discoverSkillsMulti(dirs) {
|
|
|
893
1148
|
return all;
|
|
894
1149
|
}
|
|
895
1150
|
|
|
1151
|
+
// src/core/skills/catalog.ts
|
|
1152
|
+
var catalog_exports = {};
|
|
1153
|
+
__export(catalog_exports, {
|
|
1154
|
+
getCoreSkills: () => getCoreSkills,
|
|
1155
|
+
getDispatchMatrix: () => getDispatchMatrix,
|
|
1156
|
+
getLibraryRoot: () => getLibraryRoot,
|
|
1157
|
+
getManifest: () => getManifest,
|
|
1158
|
+
getProfile: () => getProfile,
|
|
1159
|
+
getProtocolPath: () => getProtocolPath,
|
|
1160
|
+
getSharedResourcePath: () => getSharedResourcePath,
|
|
1161
|
+
getSkill: () => getSkill,
|
|
1162
|
+
getSkillDependencies: () => getSkillDependencies,
|
|
1163
|
+
getSkillDir: () => getSkillDir,
|
|
1164
|
+
getSkillPath: () => getSkillPath,
|
|
1165
|
+
getSkills: () => getSkills,
|
|
1166
|
+
getSkillsByCategory: () => getSkillsByCategory,
|
|
1167
|
+
getVersion: () => getVersion,
|
|
1168
|
+
isCatalogAvailable: () => isCatalogAvailable,
|
|
1169
|
+
listProfiles: () => listProfiles,
|
|
1170
|
+
listProtocols: () => listProtocols,
|
|
1171
|
+
listSharedResources: () => listSharedResources,
|
|
1172
|
+
listSkills: () => listSkills,
|
|
1173
|
+
readProtocol: () => readProtocol,
|
|
1174
|
+
readSharedResource: () => readSharedResource,
|
|
1175
|
+
readSkillContent: () => readSkillContent,
|
|
1176
|
+
resolveDependencyTree: () => resolveDependencyTree,
|
|
1177
|
+
resolveProfile: () => resolveProfile,
|
|
1178
|
+
validateAll: () => validateAll,
|
|
1179
|
+
validateSkillFrontmatter: () => validateSkillFrontmatter
|
|
1180
|
+
});
|
|
1181
|
+
import { createRequire } from "module";
|
|
1182
|
+
var require2 = createRequire(import.meta.url);
|
|
1183
|
+
var _ctSkills;
|
|
1184
|
+
function getCtSkills() {
|
|
1185
|
+
if (!_ctSkills) {
|
|
1186
|
+
try {
|
|
1187
|
+
_ctSkills = require2("@cleocode/ct-skills");
|
|
1188
|
+
} catch {
|
|
1189
|
+
throw new Error(
|
|
1190
|
+
"@cleocode/ct-skills is not installed. Run: npm install @cleocode/ct-skills"
|
|
1191
|
+
);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
return _ctSkills;
|
|
1195
|
+
}
|
|
1196
|
+
function getSkills() {
|
|
1197
|
+
return getCtSkills().skills;
|
|
1198
|
+
}
|
|
1199
|
+
function getManifest() {
|
|
1200
|
+
return getCtSkills().manifest;
|
|
1201
|
+
}
|
|
1202
|
+
function listSkills() {
|
|
1203
|
+
return getCtSkills().listSkills();
|
|
1204
|
+
}
|
|
1205
|
+
function getSkill(name) {
|
|
1206
|
+
return getCtSkills().getSkill(name);
|
|
1207
|
+
}
|
|
1208
|
+
function getSkillPath(name) {
|
|
1209
|
+
return getCtSkills().getSkillPath(name);
|
|
1210
|
+
}
|
|
1211
|
+
function getSkillDir(name) {
|
|
1212
|
+
return getCtSkills().getSkillDir(name);
|
|
1213
|
+
}
|
|
1214
|
+
function readSkillContent(name) {
|
|
1215
|
+
return getCtSkills().readSkillContent(name);
|
|
1216
|
+
}
|
|
1217
|
+
function getCoreSkills() {
|
|
1218
|
+
return getCtSkills().getCoreSkills();
|
|
1219
|
+
}
|
|
1220
|
+
function getSkillsByCategory(category) {
|
|
1221
|
+
return getCtSkills().getSkillsByCategory(category);
|
|
1222
|
+
}
|
|
1223
|
+
function getSkillDependencies(name) {
|
|
1224
|
+
return getCtSkills().getSkillDependencies(name);
|
|
1225
|
+
}
|
|
1226
|
+
function resolveDependencyTree(names) {
|
|
1227
|
+
return getCtSkills().resolveDependencyTree(names);
|
|
1228
|
+
}
|
|
1229
|
+
function listProfiles() {
|
|
1230
|
+
return getCtSkills().listProfiles();
|
|
1231
|
+
}
|
|
1232
|
+
function getProfile(name) {
|
|
1233
|
+
return getCtSkills().getProfile(name);
|
|
1234
|
+
}
|
|
1235
|
+
function resolveProfile(name) {
|
|
1236
|
+
return getCtSkills().resolveProfile(name);
|
|
1237
|
+
}
|
|
1238
|
+
function listSharedResources() {
|
|
1239
|
+
return getCtSkills().listSharedResources();
|
|
1240
|
+
}
|
|
1241
|
+
function getSharedResourcePath(name) {
|
|
1242
|
+
return getCtSkills().getSharedResourcePath(name);
|
|
1243
|
+
}
|
|
1244
|
+
function readSharedResource(name) {
|
|
1245
|
+
return getCtSkills().readSharedResource(name);
|
|
1246
|
+
}
|
|
1247
|
+
function listProtocols() {
|
|
1248
|
+
return getCtSkills().listProtocols();
|
|
1249
|
+
}
|
|
1250
|
+
function getProtocolPath(name) {
|
|
1251
|
+
return getCtSkills().getProtocolPath(name);
|
|
1252
|
+
}
|
|
1253
|
+
function readProtocol(name) {
|
|
1254
|
+
return getCtSkills().readProtocol(name);
|
|
1255
|
+
}
|
|
1256
|
+
function validateSkillFrontmatter(name) {
|
|
1257
|
+
return getCtSkills().validateSkillFrontmatter(name);
|
|
1258
|
+
}
|
|
1259
|
+
function validateAll() {
|
|
1260
|
+
return getCtSkills().validateAll();
|
|
1261
|
+
}
|
|
1262
|
+
function getDispatchMatrix() {
|
|
1263
|
+
return getCtSkills().getDispatchMatrix();
|
|
1264
|
+
}
|
|
1265
|
+
function getVersion() {
|
|
1266
|
+
return getCtSkills().version;
|
|
1267
|
+
}
|
|
1268
|
+
function getLibraryRoot() {
|
|
1269
|
+
return getCtSkills().libraryRoot;
|
|
1270
|
+
}
|
|
1271
|
+
function isCatalogAvailable() {
|
|
1272
|
+
try {
|
|
1273
|
+
getCtSkills();
|
|
1274
|
+
return true;
|
|
1275
|
+
} catch {
|
|
1276
|
+
return false;
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
|
|
896
1280
|
// src/core/skills/recommendation.ts
|
|
897
1281
|
var RECOMMENDATION_ERROR_CODES = {
|
|
898
1282
|
QUERY_INVALID: "E_SKILLS_QUERY_INVALID",
|
|
@@ -1161,8 +1545,8 @@ async function recommendSkills2(query, criteria, options = {}) {
|
|
|
1161
1545
|
}
|
|
1162
1546
|
|
|
1163
1547
|
// src/core/skills/audit/scanner.ts
|
|
1164
|
-
import { readFile as readFile4 } from "fs/promises";
|
|
1165
1548
|
import { existsSync as existsSync6 } from "fs";
|
|
1549
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1166
1550
|
|
|
1167
1551
|
// src/core/skills/audit/rules.ts
|
|
1168
1552
|
function rule(id, name, description, severity, category, pattern) {
|
|
@@ -1232,7 +1616,7 @@ var AUDIT_RULES = [
|
|
|
1232
1616
|
"Invisible characters or zero-width spaces",
|
|
1233
1617
|
"medium",
|
|
1234
1618
|
"prompt-injection",
|
|
1235
|
-
/
|
|
1619
|
+
/(?:\u200B|\u200C|\u200D|\u2060|\uFEFF)/
|
|
1236
1620
|
),
|
|
1237
1621
|
// ── Command Injection ───────────────────────────────────────
|
|
1238
1622
|
rule(
|
|
@@ -1544,13 +1928,13 @@ async function scanFile(filePath, rules) {
|
|
|
1544
1928
|
if (!existsSync6(filePath)) {
|
|
1545
1929
|
return { file: filePath, findings: [], score: 100, passed: true };
|
|
1546
1930
|
}
|
|
1547
|
-
const content = await
|
|
1931
|
+
const content = await readFile3(filePath, "utf-8");
|
|
1548
1932
|
const lines = content.split("\n");
|
|
1549
1933
|
const activeRules = rules ?? AUDIT_RULES;
|
|
1550
1934
|
const findings = [];
|
|
1551
1935
|
for (const rule2 of activeRules) {
|
|
1552
1936
|
for (let i = 0; i < lines.length; i++) {
|
|
1553
|
-
const line = lines[i];
|
|
1937
|
+
const line = lines[i] ?? "";
|
|
1554
1938
|
const match = line.match(rule2.pattern);
|
|
1555
1939
|
if (match) {
|
|
1556
1940
|
findings.push({
|
|
@@ -1573,13 +1957,13 @@ async function scanFile(filePath, rules) {
|
|
|
1573
1957
|
}
|
|
1574
1958
|
async function scanDirectory(dirPath) {
|
|
1575
1959
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1576
|
-
const { join:
|
|
1960
|
+
const { join: join7 } = await import("path");
|
|
1577
1961
|
if (!existsSync6(dirPath)) return [];
|
|
1578
1962
|
const entries = await readdir2(dirPath, { withFileTypes: true });
|
|
1579
1963
|
const results = [];
|
|
1580
1964
|
for (const entry of entries) {
|
|
1581
1965
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
1582
|
-
const skillFile =
|
|
1966
|
+
const skillFile = join7(dirPath, entry.name, "SKILL.md");
|
|
1583
1967
|
if (existsSync6(skillFile)) {
|
|
1584
1968
|
results.push(await scanFile(skillFile));
|
|
1585
1969
|
}
|
|
@@ -1632,7 +2016,7 @@ function toSarif(results) {
|
|
|
1632
2016
|
}
|
|
1633
2017
|
|
|
1634
2018
|
// src/core/skills/validator.ts
|
|
1635
|
-
import { readFile as
|
|
2019
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1636
2020
|
import { existsSync as existsSync7 } from "fs";
|
|
1637
2021
|
import matter2 from "gray-matter";
|
|
1638
2022
|
var RESERVED_NAMES = [
|
|
@@ -1661,7 +2045,7 @@ async function validateSkill(filePath) {
|
|
|
1661
2045
|
metadata: null
|
|
1662
2046
|
};
|
|
1663
2047
|
}
|
|
1664
|
-
const content = await
|
|
2048
|
+
const content = await readFile4(filePath, "utf-8");
|
|
1665
2049
|
if (!content.startsWith("---")) {
|
|
1666
2050
|
issues.push({
|
|
1667
2051
|
level: "error",
|
|
@@ -1684,10 +2068,10 @@ async function validateSkill(filePath) {
|
|
|
1684
2068
|
});
|
|
1685
2069
|
return { valid: false, issues, metadata: null };
|
|
1686
2070
|
}
|
|
1687
|
-
if (!data
|
|
2071
|
+
if (!data.name) {
|
|
1688
2072
|
issues.push({ level: "error", field: "name", message: "Missing required field: name" });
|
|
1689
2073
|
} else {
|
|
1690
|
-
const name = String(data
|
|
2074
|
+
const name = String(data.name);
|
|
1691
2075
|
if (name.length > MAX_NAME_LENGTH) {
|
|
1692
2076
|
issues.push({
|
|
1693
2077
|
level: "error",
|
|
@@ -1717,10 +2101,10 @@ async function validateSkill(filePath) {
|
|
|
1717
2101
|
});
|
|
1718
2102
|
}
|
|
1719
2103
|
}
|
|
1720
|
-
if (!data
|
|
2104
|
+
if (!data.description) {
|
|
1721
2105
|
issues.push({ level: "error", field: "description", message: "Missing required field: description" });
|
|
1722
2106
|
} else {
|
|
1723
|
-
const desc = String(data
|
|
2107
|
+
const desc = String(data.description);
|
|
1724
2108
|
if (desc.length > MAX_DESCRIPTION_LENGTH) {
|
|
1725
2109
|
issues.push({
|
|
1726
2110
|
level: "error",
|
|
@@ -1799,12 +2183,12 @@ async function ensureDir(filePath) {
|
|
|
1799
2183
|
}
|
|
1800
2184
|
|
|
1801
2185
|
// src/core/formats/json.ts
|
|
1802
|
-
import { readFile as
|
|
2186
|
+
import { readFile as readFile5, writeFile as writeFile2 } from "fs/promises";
|
|
1803
2187
|
import { existsSync as existsSync8 } from "fs";
|
|
1804
2188
|
import * as jsonc from "jsonc-parser";
|
|
1805
2189
|
async function readJsonConfig(filePath) {
|
|
1806
2190
|
if (!existsSync8(filePath)) return {};
|
|
1807
|
-
const content = await
|
|
2191
|
+
const content = await readFile5(filePath, "utf-8");
|
|
1808
2192
|
if (!content.trim()) return {};
|
|
1809
2193
|
const errors = [];
|
|
1810
2194
|
const result = jsonc.parse(content, errors);
|
|
@@ -1831,7 +2215,7 @@ async function writeJsonConfig(filePath, configKey, serverName, serverConfig) {
|
|
|
1831
2215
|
await ensureDir(filePath);
|
|
1832
2216
|
let content;
|
|
1833
2217
|
if (existsSync8(filePath)) {
|
|
1834
|
-
content = await
|
|
2218
|
+
content = await readFile5(filePath, "utf-8");
|
|
1835
2219
|
if (!content.trim()) {
|
|
1836
2220
|
content = "{}";
|
|
1837
2221
|
}
|
|
@@ -1853,11 +2237,11 @@ async function writeJsonConfig(filePath, configKey, serverName, serverConfig) {
|
|
|
1853
2237
|
if (!content.endsWith("\n")) {
|
|
1854
2238
|
content += "\n";
|
|
1855
2239
|
}
|
|
1856
|
-
await
|
|
2240
|
+
await writeFile2(filePath, content, "utf-8");
|
|
1857
2241
|
}
|
|
1858
2242
|
async function removeJsonConfig(filePath, configKey, serverName) {
|
|
1859
2243
|
if (!existsSync8(filePath)) return false;
|
|
1860
|
-
let content = await
|
|
2244
|
+
let content = await readFile5(filePath, "utf-8");
|
|
1861
2245
|
if (!content.trim()) return false;
|
|
1862
2246
|
const { tabSize, insertSpaces } = detectIndent(content);
|
|
1863
2247
|
const formatOptions = {
|
|
@@ -1873,17 +2257,17 @@ async function removeJsonConfig(filePath, configKey, serverName) {
|
|
|
1873
2257
|
if (!content.endsWith("\n")) {
|
|
1874
2258
|
content += "\n";
|
|
1875
2259
|
}
|
|
1876
|
-
await
|
|
2260
|
+
await writeFile2(filePath, content, "utf-8");
|
|
1877
2261
|
return true;
|
|
1878
2262
|
}
|
|
1879
2263
|
|
|
1880
2264
|
// src/core/formats/yaml.ts
|
|
1881
|
-
import { readFile as readFile7, writeFile as writeFile4 } from "fs/promises";
|
|
1882
2265
|
import { existsSync as existsSync9 } from "fs";
|
|
2266
|
+
import { readFile as readFile6, writeFile as writeFile3 } from "fs/promises";
|
|
1883
2267
|
import yaml from "js-yaml";
|
|
1884
2268
|
async function readYamlConfig(filePath) {
|
|
1885
2269
|
if (!existsSync9(filePath)) return {};
|
|
1886
|
-
const content = await
|
|
2270
|
+
const content = await readFile6(filePath, "utf-8");
|
|
1887
2271
|
if (!content.trim()) return {};
|
|
1888
2272
|
const result = yaml.load(content);
|
|
1889
2273
|
return result ?? {};
|
|
@@ -1893,8 +2277,8 @@ async function writeYamlConfig(filePath, configKey, serverName, serverConfig) {
|
|
|
1893
2277
|
const existing = await readYamlConfig(filePath);
|
|
1894
2278
|
const keyParts = configKey.split(".");
|
|
1895
2279
|
let newEntry = { [serverName]: serverConfig };
|
|
1896
|
-
for (
|
|
1897
|
-
newEntry = { [
|
|
2280
|
+
for (const part of [...keyParts].reverse()) {
|
|
2281
|
+
newEntry = { [part]: newEntry };
|
|
1898
2282
|
}
|
|
1899
2283
|
const merged = deepMerge(existing, newEntry);
|
|
1900
2284
|
const content = yaml.dump(merged, {
|
|
@@ -1903,7 +2287,7 @@ async function writeYamlConfig(filePath, configKey, serverName, serverConfig) {
|
|
|
1903
2287
|
noRefs: true,
|
|
1904
2288
|
sortKeys: false
|
|
1905
2289
|
});
|
|
1906
|
-
await
|
|
2290
|
+
await writeFile3(filePath, content, "utf-8");
|
|
1907
2291
|
}
|
|
1908
2292
|
async function removeYamlConfig(filePath, configKey, serverName) {
|
|
1909
2293
|
if (!existsSync9(filePath)) return false;
|
|
@@ -1923,17 +2307,17 @@ async function removeYamlConfig(filePath, configKey, serverName) {
|
|
|
1923
2307
|
noRefs: true,
|
|
1924
2308
|
sortKeys: false
|
|
1925
2309
|
});
|
|
1926
|
-
await
|
|
2310
|
+
await writeFile3(filePath, content, "utf-8");
|
|
1927
2311
|
return true;
|
|
1928
2312
|
}
|
|
1929
2313
|
|
|
1930
2314
|
// src/core/formats/toml.ts
|
|
1931
|
-
import { readFile as readFile8, writeFile as writeFile5 } from "fs/promises";
|
|
1932
2315
|
import { existsSync as existsSync10 } from "fs";
|
|
2316
|
+
import { readFile as readFile7, writeFile as writeFile4 } from "fs/promises";
|
|
1933
2317
|
import TOML from "@iarna/toml";
|
|
1934
2318
|
async function readTomlConfig(filePath) {
|
|
1935
2319
|
if (!existsSync10(filePath)) return {};
|
|
1936
|
-
const content = await
|
|
2320
|
+
const content = await readFile7(filePath, "utf-8");
|
|
1937
2321
|
if (!content.trim()) return {};
|
|
1938
2322
|
const result = TOML.parse(content);
|
|
1939
2323
|
return result;
|
|
@@ -1943,12 +2327,12 @@ async function writeTomlConfig(filePath, configKey, serverName, serverConfig) {
|
|
|
1943
2327
|
const existing = await readTomlConfig(filePath);
|
|
1944
2328
|
const keyParts = configKey.split(".");
|
|
1945
2329
|
let newEntry = { [serverName]: serverConfig };
|
|
1946
|
-
for (
|
|
1947
|
-
newEntry = { [
|
|
2330
|
+
for (const part of [...keyParts].reverse()) {
|
|
2331
|
+
newEntry = { [part]: newEntry };
|
|
1948
2332
|
}
|
|
1949
2333
|
const merged = deepMerge(existing, newEntry);
|
|
1950
2334
|
const content = TOML.stringify(merged);
|
|
1951
|
-
await
|
|
2335
|
+
await writeFile4(filePath, content, "utf-8");
|
|
1952
2336
|
}
|
|
1953
2337
|
async function removeTomlConfig(filePath, configKey, serverName) {
|
|
1954
2338
|
if (!existsSync10(filePath)) return false;
|
|
@@ -1963,7 +2347,7 @@ async function removeTomlConfig(filePath, configKey, serverName) {
|
|
|
1963
2347
|
if (!(serverName in current)) return false;
|
|
1964
2348
|
delete current[serverName];
|
|
1965
2349
|
const content = TOML.stringify(existing);
|
|
1966
|
-
await
|
|
2350
|
+
await writeFile4(filePath, content, "utf-8");
|
|
1967
2351
|
return true;
|
|
1968
2352
|
}
|
|
1969
2353
|
|
|
@@ -2010,6 +2394,83 @@ async function removeConfig(filePath, format, key, serverName) {
|
|
|
2010
2394
|
}
|
|
2011
2395
|
}
|
|
2012
2396
|
|
|
2397
|
+
// src/core/mcp/reader.ts
|
|
2398
|
+
import { existsSync as existsSync11 } from "fs";
|
|
2399
|
+
function resolveConfigPath(provider, scope, projectDir) {
|
|
2400
|
+
return resolveProviderConfigPath(provider, scope, projectDir ?? process.cwd());
|
|
2401
|
+
}
|
|
2402
|
+
async function listMcpServers(provider, scope, projectDir) {
|
|
2403
|
+
const configPath = resolveConfigPath(provider, scope, projectDir);
|
|
2404
|
+
debug(`listing MCP servers for ${provider.id} (${scope}) at ${configPath ?? "(none)"}`);
|
|
2405
|
+
if (!configPath || !existsSync11(configPath)) return [];
|
|
2406
|
+
try {
|
|
2407
|
+
const config = await readConfig(configPath, provider.configFormat);
|
|
2408
|
+
const servers = getNestedValue(config, provider.configKey);
|
|
2409
|
+
if (!servers || typeof servers !== "object") return [];
|
|
2410
|
+
const entries = [];
|
|
2411
|
+
for (const [name, cfg] of Object.entries(servers)) {
|
|
2412
|
+
entries.push({
|
|
2413
|
+
name,
|
|
2414
|
+
providerId: provider.id,
|
|
2415
|
+
providerName: provider.toolName,
|
|
2416
|
+
scope,
|
|
2417
|
+
configPath,
|
|
2418
|
+
config: cfg ?? {}
|
|
2419
|
+
});
|
|
2420
|
+
}
|
|
2421
|
+
return entries;
|
|
2422
|
+
} catch {
|
|
2423
|
+
return [];
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
async function listAgentsMcpServers(scope, projectDir) {
|
|
2427
|
+
const serversPath = getAgentsMcpServersPath(scope, projectDir);
|
|
2428
|
+
debug(`listing .agents/ MCP servers (${scope}) at ${serversPath}`);
|
|
2429
|
+
if (!existsSync11(serversPath)) return [];
|
|
2430
|
+
try {
|
|
2431
|
+
const config = await readConfig(serversPath, "json");
|
|
2432
|
+
const servers = config["servers"];
|
|
2433
|
+
if (!servers || typeof servers !== "object") return [];
|
|
2434
|
+
const entries = [];
|
|
2435
|
+
for (const [name, cfg] of Object.entries(servers)) {
|
|
2436
|
+
entries.push({
|
|
2437
|
+
name,
|
|
2438
|
+
providerId: ".agents",
|
|
2439
|
+
providerName: ".agents/ standard",
|
|
2440
|
+
scope,
|
|
2441
|
+
configPath: serversPath,
|
|
2442
|
+
config: cfg ?? {}
|
|
2443
|
+
});
|
|
2444
|
+
}
|
|
2445
|
+
return entries;
|
|
2446
|
+
} catch {
|
|
2447
|
+
return [];
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
async function listAllMcpServers(providers, scope, projectDir) {
|
|
2451
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2452
|
+
const allEntries = [];
|
|
2453
|
+
const agentsServersPath = getAgentsMcpServersPath(scope, projectDir);
|
|
2454
|
+
const agentsEntries = await listAgentsMcpServers(scope, projectDir);
|
|
2455
|
+
if (agentsEntries.length > 0) {
|
|
2456
|
+
allEntries.push(...agentsEntries);
|
|
2457
|
+
seen.add(agentsServersPath);
|
|
2458
|
+
}
|
|
2459
|
+
for (const provider of providers) {
|
|
2460
|
+
const configPath = resolveConfigPath(provider, scope, projectDir);
|
|
2461
|
+
if (!configPath || seen.has(configPath)) continue;
|
|
2462
|
+
seen.add(configPath);
|
|
2463
|
+
const entries = await listMcpServers(provider, scope, projectDir);
|
|
2464
|
+
allEntries.push(...entries);
|
|
2465
|
+
}
|
|
2466
|
+
return allEntries;
|
|
2467
|
+
}
|
|
2468
|
+
async function removeMcpServer(provider, serverName, scope, projectDir) {
|
|
2469
|
+
const configPath = resolveConfigPath(provider, scope, projectDir);
|
|
2470
|
+
if (!configPath) return false;
|
|
2471
|
+
return removeConfig(configPath, provider.configFormat, provider.configKey, serverName);
|
|
2472
|
+
}
|
|
2473
|
+
|
|
2013
2474
|
// src/core/mcp/transforms.ts
|
|
2014
2475
|
function transformGoose(serverName, config) {
|
|
2015
2476
|
if (config.url) {
|
|
@@ -2033,7 +2494,7 @@ function transformGoose(serverName, config) {
|
|
|
2033
2494
|
timeout: 300
|
|
2034
2495
|
};
|
|
2035
2496
|
}
|
|
2036
|
-
function transformZed(
|
|
2497
|
+
function transformZed(_serverName, config) {
|
|
2037
2498
|
if (config.url) {
|
|
2038
2499
|
return {
|
|
2039
2500
|
source: "custom",
|
|
@@ -2049,7 +2510,7 @@ function transformZed(serverName, config) {
|
|
|
2049
2510
|
...config.env ? { env: config.env } : {}
|
|
2050
2511
|
};
|
|
2051
2512
|
}
|
|
2052
|
-
function transformOpenCode(
|
|
2513
|
+
function transformOpenCode(_serverName, config) {
|
|
2053
2514
|
if (config.url) {
|
|
2054
2515
|
return {
|
|
2055
2516
|
type: "remote",
|
|
@@ -2066,7 +2527,7 @@ function transformOpenCode(serverName, config) {
|
|
|
2066
2527
|
...config.env ? { environment: config.env } : {}
|
|
2067
2528
|
};
|
|
2068
2529
|
}
|
|
2069
|
-
function transformCodex(
|
|
2530
|
+
function transformCodex(_serverName, config) {
|
|
2070
2531
|
if (config.url) {
|
|
2071
2532
|
return {
|
|
2072
2533
|
type: config.type ?? "http",
|
|
@@ -2080,7 +2541,7 @@ function transformCodex(serverName, config) {
|
|
|
2080
2541
|
...config.env ? { env: config.env } : {}
|
|
2081
2542
|
};
|
|
2082
2543
|
}
|
|
2083
|
-
function transformCursor(
|
|
2544
|
+
function transformCursor(_serverName, config) {
|
|
2084
2545
|
if (config.url) {
|
|
2085
2546
|
return {
|
|
2086
2547
|
url: config.url,
|
|
@@ -2106,58 +2567,6 @@ function getTransform(providerId) {
|
|
|
2106
2567
|
}
|
|
2107
2568
|
}
|
|
2108
2569
|
|
|
2109
|
-
// src/core/mcp/reader.ts
|
|
2110
|
-
import { join as join6 } from "path";
|
|
2111
|
-
import { existsSync as existsSync11 } from "fs";
|
|
2112
|
-
function resolveConfigPath(provider, scope, projectDir) {
|
|
2113
|
-
if (scope === "project") {
|
|
2114
|
-
if (!provider.configPathProject) return null;
|
|
2115
|
-
return join6(projectDir ?? process.cwd(), provider.configPathProject);
|
|
2116
|
-
}
|
|
2117
|
-
return provider.configPathGlobal;
|
|
2118
|
-
}
|
|
2119
|
-
async function listMcpServers(provider, scope, projectDir) {
|
|
2120
|
-
const configPath = resolveConfigPath(provider, scope, projectDir);
|
|
2121
|
-
debug(`listing MCP servers for ${provider.id} (${scope}) at ${configPath ?? "(none)"}`);
|
|
2122
|
-
if (!configPath || !existsSync11(configPath)) return [];
|
|
2123
|
-
try {
|
|
2124
|
-
const config = await readConfig(configPath, provider.configFormat);
|
|
2125
|
-
const servers = getNestedValue(config, provider.configKey);
|
|
2126
|
-
if (!servers || typeof servers !== "object") return [];
|
|
2127
|
-
const entries = [];
|
|
2128
|
-
for (const [name, cfg] of Object.entries(servers)) {
|
|
2129
|
-
entries.push({
|
|
2130
|
-
name,
|
|
2131
|
-
providerId: provider.id,
|
|
2132
|
-
providerName: provider.toolName,
|
|
2133
|
-
scope,
|
|
2134
|
-
configPath,
|
|
2135
|
-
config: cfg ?? {}
|
|
2136
|
-
});
|
|
2137
|
-
}
|
|
2138
|
-
return entries;
|
|
2139
|
-
} catch {
|
|
2140
|
-
return [];
|
|
2141
|
-
}
|
|
2142
|
-
}
|
|
2143
|
-
async function listAllMcpServers(providers, scope, projectDir) {
|
|
2144
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2145
|
-
const allEntries = [];
|
|
2146
|
-
for (const provider of providers) {
|
|
2147
|
-
const configPath = resolveConfigPath(provider, scope, projectDir);
|
|
2148
|
-
if (!configPath || seen.has(configPath)) continue;
|
|
2149
|
-
seen.add(configPath);
|
|
2150
|
-
const entries = await listMcpServers(provider, scope, projectDir);
|
|
2151
|
-
allEntries.push(...entries);
|
|
2152
|
-
}
|
|
2153
|
-
return allEntries;
|
|
2154
|
-
}
|
|
2155
|
-
async function removeMcpServer(provider, serverName, scope, projectDir) {
|
|
2156
|
-
const configPath = resolveConfigPath(provider, scope, projectDir);
|
|
2157
|
-
if (!configPath) return false;
|
|
2158
|
-
return removeConfig(configPath, provider.configFormat, provider.configKey, serverName);
|
|
2159
|
-
}
|
|
2160
|
-
|
|
2161
2570
|
// src/core/mcp/installer.ts
|
|
2162
2571
|
function buildConfig(provider, serverName, config) {
|
|
2163
2572
|
const transform = getTransform(provider.id);
|
|
@@ -2228,46 +2637,49 @@ function buildServerConfig(source, transport, headers) {
|
|
|
2228
2637
|
args: ["-y", source.value]
|
|
2229
2638
|
};
|
|
2230
2639
|
}
|
|
2231
|
-
const parts = source.value.split(/\s+/);
|
|
2640
|
+
const parts = source.value.trim().split(/\s+/);
|
|
2641
|
+
const command = parts[0] ?? source.value;
|
|
2232
2642
|
return {
|
|
2233
|
-
command
|
|
2643
|
+
command,
|
|
2234
2644
|
args: parts.slice(1)
|
|
2235
2645
|
};
|
|
2236
2646
|
}
|
|
2237
2647
|
|
|
2238
2648
|
// src/core/mcp/lock.ts
|
|
2239
2649
|
async function recordMcpInstall(serverName, source, sourceType, agents, isGlobal) {
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2650
|
+
await updateLockFile((lock) => {
|
|
2651
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2652
|
+
const existing = lock.mcpServers[serverName];
|
|
2653
|
+
lock.mcpServers[serverName] = {
|
|
2654
|
+
name: serverName,
|
|
2655
|
+
scopedName: serverName,
|
|
2656
|
+
source,
|
|
2657
|
+
sourceType,
|
|
2658
|
+
installedAt: existing?.installedAt ?? now,
|
|
2659
|
+
updatedAt: now,
|
|
2660
|
+
agents: [.../* @__PURE__ */ new Set([...existing?.agents ?? [], ...agents])],
|
|
2661
|
+
canonicalPath: "",
|
|
2662
|
+
isGlobal
|
|
2663
|
+
};
|
|
2664
|
+
});
|
|
2255
2665
|
}
|
|
2256
2666
|
async function removeMcpFromLock(serverName) {
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2667
|
+
let removed = false;
|
|
2668
|
+
await updateLockFile((lock) => {
|
|
2669
|
+
if (!(serverName in lock.mcpServers)) return;
|
|
2670
|
+
delete lock.mcpServers[serverName];
|
|
2671
|
+
removed = true;
|
|
2672
|
+
});
|
|
2673
|
+
return removed;
|
|
2262
2674
|
}
|
|
2263
2675
|
async function getTrackedMcpServers() {
|
|
2264
2676
|
const lock = await readLockFile();
|
|
2265
2677
|
return lock.mcpServers;
|
|
2266
2678
|
}
|
|
2267
2679
|
async function saveLastSelectedAgents(agents) {
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2680
|
+
await updateLockFile((lock) => {
|
|
2681
|
+
lock.lastSelectedAgents = agents;
|
|
2682
|
+
});
|
|
2271
2683
|
}
|
|
2272
2684
|
async function getLastSelectedAgents() {
|
|
2273
2685
|
const lock = await readLockFile();
|
|
@@ -2275,16 +2687,16 @@ async function getLastSelectedAgents() {
|
|
|
2275
2687
|
}
|
|
2276
2688
|
|
|
2277
2689
|
// src/core/instructions/injector.ts
|
|
2278
|
-
import { readFile as
|
|
2690
|
+
import { readFile as readFile8, writeFile as writeFile5 } from "fs/promises";
|
|
2279
2691
|
import { existsSync as existsSync12 } from "fs";
|
|
2280
|
-
import { join as
|
|
2692
|
+
import { join as join5, dirname as dirname3 } from "path";
|
|
2281
2693
|
import { mkdir as mkdir3 } from "fs/promises";
|
|
2282
2694
|
var MARKER_START = "<!-- CAAMP:START -->";
|
|
2283
2695
|
var MARKER_END = "<!-- CAAMP:END -->";
|
|
2284
2696
|
var MARKER_PATTERN = /<!-- CAAMP:START -->[\s\S]*?<!-- CAAMP:END -->/;
|
|
2285
2697
|
async function checkInjection(filePath, expectedContent) {
|
|
2286
2698
|
if (!existsSync12(filePath)) return "missing";
|
|
2287
|
-
const content = await
|
|
2699
|
+
const content = await readFile8(filePath, "utf-8");
|
|
2288
2700
|
if (!MARKER_PATTERN.test(content)) return "none";
|
|
2289
2701
|
if (expectedContent) {
|
|
2290
2702
|
const blockContent = extractBlock(content);
|
|
@@ -2309,29 +2721,33 @@ async function inject(filePath, content) {
|
|
|
2309
2721
|
const block = buildBlock(content);
|
|
2310
2722
|
await mkdir3(dirname3(filePath), { recursive: true });
|
|
2311
2723
|
if (!existsSync12(filePath)) {
|
|
2312
|
-
await
|
|
2724
|
+
await writeFile5(filePath, `${block}
|
|
2725
|
+
`, "utf-8");
|
|
2313
2726
|
return "created";
|
|
2314
2727
|
}
|
|
2315
|
-
const existing = await
|
|
2728
|
+
const existing = await readFile8(filePath, "utf-8");
|
|
2316
2729
|
if (MARKER_PATTERN.test(existing)) {
|
|
2317
2730
|
const updated2 = existing.replace(MARKER_PATTERN, block);
|
|
2318
|
-
await
|
|
2731
|
+
await writeFile5(filePath, updated2, "utf-8");
|
|
2319
2732
|
return "updated";
|
|
2320
2733
|
}
|
|
2321
|
-
const updated = block
|
|
2322
|
-
|
|
2734
|
+
const updated = `${block}
|
|
2735
|
+
|
|
2736
|
+
${existing}`;
|
|
2737
|
+
await writeFile5(filePath, updated, "utf-8");
|
|
2323
2738
|
return "added";
|
|
2324
2739
|
}
|
|
2325
2740
|
async function removeInjection(filePath) {
|
|
2326
2741
|
if (!existsSync12(filePath)) return false;
|
|
2327
|
-
const content = await
|
|
2742
|
+
const content = await readFile8(filePath, "utf-8");
|
|
2328
2743
|
if (!MARKER_PATTERN.test(content)) return false;
|
|
2329
2744
|
const cleaned = content.replace(MARKER_PATTERN, "").replace(/^\n{2,}/, "\n").trim();
|
|
2330
2745
|
if (!cleaned) {
|
|
2331
|
-
const { rm:
|
|
2332
|
-
await
|
|
2746
|
+
const { rm: rm4 } = await import("fs/promises");
|
|
2747
|
+
await rm4(filePath);
|
|
2333
2748
|
} else {
|
|
2334
|
-
await
|
|
2749
|
+
await writeFile5(filePath, `${cleaned}
|
|
2750
|
+
`, "utf-8");
|
|
2335
2751
|
}
|
|
2336
2752
|
return true;
|
|
2337
2753
|
}
|
|
@@ -2339,7 +2755,7 @@ async function checkAllInjections(providers, projectDir, scope, expectedContent)
|
|
|
2339
2755
|
const results = [];
|
|
2340
2756
|
const checked = /* @__PURE__ */ new Set();
|
|
2341
2757
|
for (const provider of providers) {
|
|
2342
|
-
const filePath = scope === "global" ?
|
|
2758
|
+
const filePath = scope === "global" ? join5(provider.pathGlobal, provider.instructFile) : join5(projectDir, provider.instructFile);
|
|
2343
2759
|
if (checked.has(filePath)) continue;
|
|
2344
2760
|
checked.add(filePath);
|
|
2345
2761
|
const status = await checkInjection(filePath, expectedContent);
|
|
@@ -2356,7 +2772,7 @@ async function injectAll(providers, projectDir, scope, content) {
|
|
|
2356
2772
|
const results = /* @__PURE__ */ new Map();
|
|
2357
2773
|
const injected = /* @__PURE__ */ new Set();
|
|
2358
2774
|
for (const provider of providers) {
|
|
2359
|
-
const filePath = scope === "global" ?
|
|
2775
|
+
const filePath = scope === "global" ? join5(provider.pathGlobal, provider.instructFile) : join5(projectDir, provider.instructFile);
|
|
2360
2776
|
if (injected.has(filePath)) continue;
|
|
2361
2777
|
injected.add(filePath);
|
|
2362
2778
|
const action = await inject(filePath, content);
|
|
@@ -2398,27 +2814,26 @@ import { existsSync as existsSync13, lstatSync as lstatSync2 } from "fs";
|
|
|
2398
2814
|
import {
|
|
2399
2815
|
cp as cp2,
|
|
2400
2816
|
mkdir as mkdir4,
|
|
2401
|
-
readFile as
|
|
2402
|
-
readlink
|
|
2403
|
-
rm as
|
|
2817
|
+
readFile as readFile9,
|
|
2818
|
+
readlink,
|
|
2819
|
+
rm as rm3,
|
|
2404
2820
|
symlink as symlink2,
|
|
2405
|
-
writeFile as
|
|
2821
|
+
writeFile as writeFile6
|
|
2406
2822
|
} from "fs/promises";
|
|
2407
|
-
import {
|
|
2408
|
-
import { basename
|
|
2823
|
+
import { tmpdir } from "os";
|
|
2824
|
+
import { basename, dirname as dirname4, join as join6 } from "path";
|
|
2409
2825
|
var PRIORITY_ORDER = {
|
|
2410
2826
|
high: 0,
|
|
2411
2827
|
medium: 1,
|
|
2412
2828
|
low: 2
|
|
2413
2829
|
};
|
|
2414
|
-
var CANONICAL_SKILLS_DIR = join8(homedir4(), ".agents", "skills");
|
|
2415
2830
|
function selectProvidersByMinimumPriority(providers, minimumPriority = "low") {
|
|
2416
2831
|
const maxRank = PRIORITY_ORDER[minimumPriority];
|
|
2417
2832
|
return [...providers].filter((provider) => PRIORITY_ORDER[provider.priority] <= maxRank).sort((a, b) => PRIORITY_ORDER[a.priority] - PRIORITY_ORDER[b.priority]);
|
|
2418
2833
|
}
|
|
2419
2834
|
function resolveSkillLinkPath(provider, skillName, isGlobal, projectDir) {
|
|
2420
|
-
const skillDir = isGlobal ? provider.pathSkills :
|
|
2421
|
-
return
|
|
2835
|
+
const skillDir = isGlobal ? provider.pathSkills : join6(projectDir, provider.pathProjectSkills);
|
|
2836
|
+
return join6(skillDir, skillName);
|
|
2422
2837
|
}
|
|
2423
2838
|
async function snapshotConfigs(paths) {
|
|
2424
2839
|
const snapshots = /* @__PURE__ */ new Map();
|
|
@@ -2428,26 +2843,26 @@ async function snapshotConfigs(paths) {
|
|
|
2428
2843
|
snapshots.set(path, null);
|
|
2429
2844
|
continue;
|
|
2430
2845
|
}
|
|
2431
|
-
snapshots.set(path, await
|
|
2846
|
+
snapshots.set(path, await readFile9(path, "utf-8"));
|
|
2432
2847
|
}
|
|
2433
2848
|
return snapshots;
|
|
2434
2849
|
}
|
|
2435
2850
|
async function restoreConfigSnapshots(snapshots) {
|
|
2436
2851
|
for (const [path, content] of snapshots) {
|
|
2437
2852
|
if (content === null) {
|
|
2438
|
-
await
|
|
2853
|
+
await rm3(path, { force: true });
|
|
2439
2854
|
continue;
|
|
2440
2855
|
}
|
|
2441
2856
|
await mkdir4(dirname4(path), { recursive: true });
|
|
2442
|
-
await
|
|
2857
|
+
await writeFile6(path, content, "utf-8");
|
|
2443
2858
|
}
|
|
2444
2859
|
}
|
|
2445
2860
|
async function snapshotSkillState(providerTargets, operation, projectDir, backupRoot) {
|
|
2446
2861
|
const skillName = operation.skillName;
|
|
2447
2862
|
const isGlobal = operation.isGlobal ?? true;
|
|
2448
|
-
const canonicalPath =
|
|
2863
|
+
const canonicalPath = join6(CANONICAL_SKILLS_DIR, skillName);
|
|
2449
2864
|
const canonicalExisted = existsSync13(canonicalPath);
|
|
2450
|
-
const canonicalBackupPath =
|
|
2865
|
+
const canonicalBackupPath = join6(backupRoot, "canonical", skillName);
|
|
2451
2866
|
if (canonicalExisted) {
|
|
2452
2867
|
await mkdir4(dirname4(canonicalBackupPath), { recursive: true });
|
|
2453
2868
|
await cp2(canonicalPath, canonicalBackupPath, { recursive: true });
|
|
@@ -2464,11 +2879,11 @@ async function snapshotSkillState(providerTargets, operation, projectDir, backup
|
|
|
2464
2879
|
pathSnapshots.push({
|
|
2465
2880
|
linkPath,
|
|
2466
2881
|
state: "symlink",
|
|
2467
|
-
symlinkTarget: await
|
|
2882
|
+
symlinkTarget: await readlink(linkPath)
|
|
2468
2883
|
});
|
|
2469
2884
|
continue;
|
|
2470
2885
|
}
|
|
2471
|
-
const backupPath =
|
|
2886
|
+
const backupPath = join6(backupRoot, "links", provider.id, `${skillName}-${basename(linkPath)}`);
|
|
2472
2887
|
await mkdir4(dirname4(backupPath), { recursive: true });
|
|
2473
2888
|
if (stat.isDirectory()) {
|
|
2474
2889
|
await cp2(linkPath, backupPath, { recursive: true });
|
|
@@ -2489,14 +2904,14 @@ async function snapshotSkillState(providerTargets, operation, projectDir, backup
|
|
|
2489
2904
|
}
|
|
2490
2905
|
async function restoreSkillSnapshot(snapshot) {
|
|
2491
2906
|
if (existsSync13(snapshot.canonicalPath)) {
|
|
2492
|
-
await
|
|
2907
|
+
await rm3(snapshot.canonicalPath, { recursive: true, force: true });
|
|
2493
2908
|
}
|
|
2494
2909
|
if (snapshot.canonicalExisted && snapshot.canonicalBackupPath && existsSync13(snapshot.canonicalBackupPath)) {
|
|
2495
2910
|
await mkdir4(dirname4(snapshot.canonicalPath), { recursive: true });
|
|
2496
2911
|
await cp2(snapshot.canonicalBackupPath, snapshot.canonicalPath, { recursive: true });
|
|
2497
2912
|
}
|
|
2498
2913
|
for (const pathSnapshot of snapshot.pathSnapshots) {
|
|
2499
|
-
await
|
|
2914
|
+
await rm3(pathSnapshot.linkPath, { recursive: true, force: true });
|
|
2500
2915
|
if (pathSnapshot.state === "missing") continue;
|
|
2501
2916
|
await mkdir4(dirname4(pathSnapshot.linkPath), { recursive: true });
|
|
2502
2917
|
if (pathSnapshot.state === "symlink" && pathSnapshot.symlinkTarget) {
|
|
@@ -2529,7 +2944,7 @@ async function installBatchWithRollback(options) {
|
|
|
2529
2944
|
return paths;
|
|
2530
2945
|
});
|
|
2531
2946
|
const configSnapshots = await snapshotConfigs(configPaths);
|
|
2532
|
-
const backupRoot =
|
|
2947
|
+
const backupRoot = join6(
|
|
2533
2948
|
tmpdir(),
|
|
2534
2949
|
`caamp-skill-backup-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
2535
2950
|
);
|
|
@@ -2578,7 +2993,7 @@ async function installBatchWithRollback(options) {
|
|
|
2578
2993
|
}
|
|
2579
2994
|
skillsApplied += 1;
|
|
2580
2995
|
}
|
|
2581
|
-
await
|
|
2996
|
+
await rm3(backupRoot, { recursive: true, force: true });
|
|
2582
2997
|
return {
|
|
2583
2998
|
success: true,
|
|
2584
2999
|
providerIds: providers.map((provider) => provider.id),
|
|
@@ -2608,7 +3023,7 @@ async function installBatchWithRollback(options) {
|
|
|
2608
3023
|
rollbackErrors.push(err instanceof Error ? err.message : String(err));
|
|
2609
3024
|
}
|
|
2610
3025
|
}
|
|
2611
|
-
await
|
|
3026
|
+
await rm3(backupRoot, { recursive: true, force: true });
|
|
2612
3027
|
return {
|
|
2613
3028
|
success: false,
|
|
2614
3029
|
providerIds: providers.map((provider) => provider.id),
|
|
@@ -2720,10 +3135,10 @@ async function updateInstructionsSingleOperation(providers, content, scope = "pr
|
|
|
2720
3135
|
};
|
|
2721
3136
|
for (const [filePath, action] of actions.entries()) {
|
|
2722
3137
|
const providersForFile = providers.filter((provider) => {
|
|
2723
|
-
const expectedPath = scope === "global" ?
|
|
3138
|
+
const expectedPath = scope === "global" ? join6(provider.pathGlobal, provider.instructFile) : join6(projectDir, provider.instructFile);
|
|
2724
3139
|
return expectedPath === filePath;
|
|
2725
3140
|
});
|
|
2726
|
-
const fallback = groupedByFile.get(
|
|
3141
|
+
const fallback = groupedByFile.get(basename(filePath)) ?? [];
|
|
2727
3142
|
const selected = providersForFile.length > 0 ? providersForFile : fallback;
|
|
2728
3143
|
summary.actions.push({
|
|
2729
3144
|
file: filePath,
|
|
@@ -2786,6 +3201,23 @@ async function configureProviderGlobalAndProject(provider, options) {
|
|
|
2786
3201
|
}
|
|
2787
3202
|
|
|
2788
3203
|
export {
|
|
3204
|
+
getPlatformLocations,
|
|
3205
|
+
getAgentsHome,
|
|
3206
|
+
getProjectAgentsDir,
|
|
3207
|
+
getCanonicalSkillsDir,
|
|
3208
|
+
getLockFilePath,
|
|
3209
|
+
getAgentsMcpDir,
|
|
3210
|
+
getAgentsMcpServersPath,
|
|
3211
|
+
getAgentsInstructFile,
|
|
3212
|
+
getAgentsConfigPath,
|
|
3213
|
+
getAgentsWikiDir,
|
|
3214
|
+
getAgentsSpecDir,
|
|
3215
|
+
getAgentsLinksDir,
|
|
3216
|
+
resolveRegistryTemplatePath,
|
|
3217
|
+
resolveProviderConfigPath,
|
|
3218
|
+
resolvePreferredConfigScope,
|
|
3219
|
+
resolveProviderSkillsDir,
|
|
3220
|
+
buildSkillSubPathCandidates,
|
|
2789
3221
|
getAllProviders,
|
|
2790
3222
|
getProvider,
|
|
2791
3223
|
resolveAlias,
|
|
@@ -2803,8 +3235,10 @@ export {
|
|
|
2803
3235
|
detectAllProviders,
|
|
2804
3236
|
getInstalledProviders,
|
|
2805
3237
|
detectProjectProviders,
|
|
3238
|
+
resetDetectionCache,
|
|
2806
3239
|
parseSource,
|
|
2807
3240
|
isMarketplaceScoped,
|
|
3241
|
+
CANONICAL_SKILLS_DIR,
|
|
2808
3242
|
installSkill,
|
|
2809
3243
|
removeSkill,
|
|
2810
3244
|
listCanonicalSkills,
|
|
@@ -2819,6 +3253,13 @@ export {
|
|
|
2819
3253
|
discoverSkill,
|
|
2820
3254
|
discoverSkills,
|
|
2821
3255
|
discoverSkillsMulti,
|
|
3256
|
+
listSkills,
|
|
3257
|
+
getSkill,
|
|
3258
|
+
getSkillDir,
|
|
3259
|
+
listProfiles,
|
|
3260
|
+
resolveProfile,
|
|
3261
|
+
isCatalogAvailable,
|
|
3262
|
+
catalog_exports,
|
|
2822
3263
|
RECOMMENDATION_ERROR_CODES,
|
|
2823
3264
|
tokenizeCriteriaValue,
|
|
2824
3265
|
validateRecommendationCriteria,
|
|
@@ -2838,11 +3279,12 @@ export {
|
|
|
2838
3279
|
readConfig,
|
|
2839
3280
|
writeConfig,
|
|
2840
3281
|
removeConfig,
|
|
2841
|
-
getTransform,
|
|
2842
3282
|
resolveConfigPath,
|
|
2843
3283
|
listMcpServers,
|
|
3284
|
+
listAgentsMcpServers,
|
|
2844
3285
|
listAllMcpServers,
|
|
2845
3286
|
removeMcpServer,
|
|
3287
|
+
getTransform,
|
|
2846
3288
|
installMcpServer,
|
|
2847
3289
|
installMcpServerToAll,
|
|
2848
3290
|
buildServerConfig,
|
|
@@ -2865,4 +3307,4 @@ export {
|
|
|
2865
3307
|
updateInstructionsSingleOperation,
|
|
2866
3308
|
configureProviderGlobalAndProject
|
|
2867
3309
|
};
|
|
2868
|
-
//# sourceMappingURL=chunk-
|
|
3310
|
+
//# sourceMappingURL=chunk-YCSZGZ5W.js.map
|