@cleocode/caamp 2026.4.0 → 2026.4.3
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/dist/{chunk-3WKBFXLE.js → chunk-6NBM4CAF.js} +796 -1611
- package/dist/chunk-6NBM4CAF.js.map +1 -0
- package/dist/{chunk-5UUABVWS.js → chunk-CRU25LRL.js} +2 -2
- package/dist/{chunk-OH6Z2N3G.js → chunk-FLBRAXDW.js} +2 -2
- package/dist/{chunk-3IEKCREL.js → chunk-XWQ5WPHC.js} +1 -8
- package/dist/{chunk-3IEKCREL.js.map → chunk-XWQ5WPHC.js.map} +1 -1
- package/dist/cli.js +364 -1906
- package/dist/cli.js.map +1 -1
- package/dist/{hooks-Q7KO2SGK.js → hooks-VLIP52LY.js} +3 -3
- package/dist/index.d.ts +6 -1043
- package/dist/index.js +5 -60
- package/dist/index.js.map +1 -1
- package/dist/{injector-XCWEBXWK.js → injector-ALLOKC54.js} +3 -3
- package/package.json +2 -2
- package/dist/chunk-3WKBFXLE.js.map +0 -1
- /package/dist/{chunk-5UUABVWS.js.map → chunk-CRU25LRL.js.map} +0 -0
- /package/dist/{chunk-OH6Z2N3G.js.map → chunk-FLBRAXDW.js.map} +0 -0
- /package/dist/{hooks-Q7KO2SGK.js.map → hooks-VLIP52LY.js.map} +0 -0
- /package/dist/{injector-XCWEBXWK.js.map → injector-ALLOKC54.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -3,27 +3,18 @@ import {
|
|
|
3
3
|
CANONICAL_SKILLS_DIR,
|
|
4
4
|
MarketplaceClient,
|
|
5
5
|
RECOMMENDATION_ERROR_CODES,
|
|
6
|
-
applyMcpInstallWithPolicy,
|
|
7
|
-
buildCleoProfile,
|
|
8
|
-
buildServerConfig,
|
|
9
|
-
checkCommandReachability,
|
|
10
6
|
checkSkillUpdate,
|
|
11
|
-
configureProviderGlobalAndProject,
|
|
12
7
|
detectAllProviders,
|
|
13
|
-
detectMcpConfigConflicts,
|
|
14
8
|
detectProjectProviders,
|
|
15
9
|
discoverSkill,
|
|
16
10
|
discoverSkillsMulti,
|
|
17
|
-
extractVersionTag,
|
|
18
11
|
formatNetworkError,
|
|
19
12
|
formatSkillRecommendations,
|
|
20
13
|
getInstalledProviders,
|
|
21
14
|
getSkill,
|
|
22
15
|
getSkillDir,
|
|
23
|
-
getTrackedMcpServers,
|
|
24
16
|
getTrackedSkills,
|
|
25
17
|
installBatchWithRollback,
|
|
26
|
-
installMcpServerToAll,
|
|
27
18
|
installSkill,
|
|
28
19
|
isCatalogAvailable,
|
|
29
20
|
isHuman,
|
|
@@ -31,25 +22,15 @@ import {
|
|
|
31
22
|
isQuiet,
|
|
32
23
|
isVerbose,
|
|
33
24
|
listCanonicalSkills,
|
|
34
|
-
listMcpServers,
|
|
35
25
|
listProfiles,
|
|
36
26
|
listSkills,
|
|
37
|
-
normalizeCleoChannel,
|
|
38
|
-
parseEnvAssignments,
|
|
39
27
|
parseSource,
|
|
40
28
|
readConfig,
|
|
41
29
|
readLockFile,
|
|
42
30
|
recommendSkills,
|
|
43
|
-
reconcileCleoLock,
|
|
44
|
-
recordMcpInstall,
|
|
45
31
|
recordSkillInstall,
|
|
46
|
-
removeMcpFromLock,
|
|
47
|
-
removeMcpServer,
|
|
48
32
|
removeSkill,
|
|
49
33
|
removeSkillFromLock,
|
|
50
|
-
resolveChannelFromServerName,
|
|
51
|
-
resolveCleoServerName,
|
|
52
|
-
resolveConfigPath,
|
|
53
34
|
resolveProfile,
|
|
54
35
|
scanDirectory,
|
|
55
36
|
scanFile,
|
|
@@ -61,7 +42,7 @@ import {
|
|
|
61
42
|
tokenizeCriteriaValue,
|
|
62
43
|
updateInstructionsSingleOperation,
|
|
63
44
|
validateSkill
|
|
64
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-6NBM4CAF.js";
|
|
65
46
|
import {
|
|
66
47
|
buildSkillsMap,
|
|
67
48
|
checkAllInjections,
|
|
@@ -74,7 +55,7 @@ import {
|
|
|
74
55
|
groupByInstructFile,
|
|
75
56
|
injectAll,
|
|
76
57
|
providerSupports
|
|
77
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-CRU25LRL.js";
|
|
78
59
|
import {
|
|
79
60
|
CANONICAL_HOOK_EVENTS,
|
|
80
61
|
buildHookMatrix,
|
|
@@ -83,13 +64,12 @@ import {
|
|
|
83
64
|
getHookSupport,
|
|
84
65
|
getProviderSummary,
|
|
85
66
|
translateToAll
|
|
86
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-FLBRAXDW.js";
|
|
87
68
|
import {
|
|
88
69
|
buildSkillSubPathCandidates,
|
|
89
|
-
resolvePreferredConfigScope,
|
|
90
70
|
resolveProviderConfigPath,
|
|
91
71
|
resolveProviderSkillsDir
|
|
92
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-XWQ5WPHC.js";
|
|
93
73
|
|
|
94
74
|
// src/cli.ts
|
|
95
75
|
import { Command } from "commander";
|
|
@@ -265,57 +245,6 @@ async function readJsonFile(path) {
|
|
|
265
245
|
);
|
|
266
246
|
}
|
|
267
247
|
}
|
|
268
|
-
async function readMcpOperations(path) {
|
|
269
|
-
const value = await readJsonFile(path);
|
|
270
|
-
if (!Array.isArray(value)) {
|
|
271
|
-
throw new LAFSCommandError(
|
|
272
|
-
"E_ADVANCED_VALIDATION_MCP_ARRAY",
|
|
273
|
-
`MCP operations file must be a JSON array: ${path}`,
|
|
274
|
-
"Provide an array of objects with serverName and config fields."
|
|
275
|
-
);
|
|
276
|
-
}
|
|
277
|
-
const operations = [];
|
|
278
|
-
for (const [index, item] of value.entries()) {
|
|
279
|
-
if (!item || typeof item !== "object") {
|
|
280
|
-
throw new LAFSCommandError(
|
|
281
|
-
"E_ADVANCED_VALIDATION_MCP_ITEM",
|
|
282
|
-
`Invalid MCP operation at index ${index}`,
|
|
283
|
-
"Each operation must be an object with serverName and config."
|
|
284
|
-
);
|
|
285
|
-
}
|
|
286
|
-
const obj = item;
|
|
287
|
-
const serverName = obj.serverName;
|
|
288
|
-
const config = obj.config;
|
|
289
|
-
const scope = obj.scope;
|
|
290
|
-
if (typeof serverName !== "string" || serverName.length === 0) {
|
|
291
|
-
throw new LAFSCommandError(
|
|
292
|
-
"E_ADVANCED_VALIDATION_MCP_NAME",
|
|
293
|
-
`Invalid serverName at index ${index}`,
|
|
294
|
-
"Set serverName to a non-empty string."
|
|
295
|
-
);
|
|
296
|
-
}
|
|
297
|
-
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
298
|
-
throw new LAFSCommandError(
|
|
299
|
-
"E_ADVANCED_VALIDATION_MCP_CONFIG",
|
|
300
|
-
`Invalid config at index ${index}`,
|
|
301
|
-
"Set config to an object matching McpServerConfig."
|
|
302
|
-
);
|
|
303
|
-
}
|
|
304
|
-
if (scope !== void 0 && scope !== "project" && scope !== "global") {
|
|
305
|
-
throw new LAFSCommandError(
|
|
306
|
-
"E_ADVANCED_VALIDATION_SCOPE",
|
|
307
|
-
`Invalid scope at index ${index}: ${String(scope)}`,
|
|
308
|
-
"Use scope value 'project' or 'global'."
|
|
309
|
-
);
|
|
310
|
-
}
|
|
311
|
-
operations.push({
|
|
312
|
-
serverName,
|
|
313
|
-
config,
|
|
314
|
-
...scope ? { scope } : {}
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
return operations;
|
|
318
|
-
}
|
|
319
248
|
async function readSkillOperations(path) {
|
|
320
249
|
const value = await readJsonFile(path);
|
|
321
250
|
if (!Array.isArray(value)) {
|
|
@@ -390,104 +319,24 @@ async function readTextInput(inlineContent, filePath) {
|
|
|
390
319
|
}
|
|
391
320
|
}
|
|
392
321
|
|
|
393
|
-
// src/commands/advanced/apply.ts
|
|
394
|
-
var VALID_POLICIES = /* @__PURE__ */ new Set(["fail", "skip", "overwrite"]);
|
|
395
|
-
function parsePolicy(value) {
|
|
396
|
-
if (!VALID_POLICIES.has(value)) {
|
|
397
|
-
throw new LAFSCommandError(
|
|
398
|
-
"E_ADVANCED_VALIDATION_POLICY",
|
|
399
|
-
`Invalid policy: ${value}`,
|
|
400
|
-
"Use one of: fail, skip, overwrite."
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
return value;
|
|
404
|
-
}
|
|
405
|
-
function registerAdvancedApply(parent) {
|
|
406
|
-
parent.command("apply").description("Apply MCP operations with configurable conflict policy").requiredOption("--mcp-file <path>", "JSON file containing McpBatchOperation[]").option("--policy <policy>", "Conflict policy: fail|skip|overwrite", "fail").option(
|
|
407
|
-
"-a, --agent <name>",
|
|
408
|
-
"Target specific provider(s)",
|
|
409
|
-
(v, prev) => [...prev, v],
|
|
410
|
-
[]
|
|
411
|
-
).option("--all", "Use all registry providers (not only detected)").option("--min-tier <tier>", "Minimum priority tier: high|medium|low", "low").option("--project-dir <path>", "Project directory to resolve project-scope paths").option("--details", "Include detailed apply result").action(
|
|
412
|
-
async (opts) => runLafsCommand("advanced.apply", opts.details ? "full" : "standard", async () => {
|
|
413
|
-
const baseProviders = resolveProviders({ all: opts.all, agent: opts.agent });
|
|
414
|
-
const minimumPriority = parsePriority(opts.minTier);
|
|
415
|
-
const providers = selectProvidersByMinimumPriority(baseProviders, minimumPriority);
|
|
416
|
-
const operations = await readMcpOperations(opts.mcpFile);
|
|
417
|
-
const policy = parsePolicy(opts.policy);
|
|
418
|
-
if (providers.length === 0) {
|
|
419
|
-
throw new LAFSCommandError(
|
|
420
|
-
"E_ADVANCED_NO_TARGET_PROVIDERS",
|
|
421
|
-
"No target providers resolved for apply operation.",
|
|
422
|
-
"Use --all or pass provider IDs with --agent."
|
|
423
|
-
);
|
|
424
|
-
}
|
|
425
|
-
const result = await applyMcpInstallWithPolicy(
|
|
426
|
-
providers,
|
|
427
|
-
operations,
|
|
428
|
-
policy,
|
|
429
|
-
opts.projectDir
|
|
430
|
-
);
|
|
431
|
-
if (policy === "fail" && result.conflicts.length > 0) {
|
|
432
|
-
throw new LAFSCommandError(
|
|
433
|
-
"E_ADVANCED_CONFLICTS_BLOCKING",
|
|
434
|
-
"Conflicts detected and policy is set to fail.",
|
|
435
|
-
"Run `caamp advanced conflicts` to inspect, or rerun with --policy skip/overwrite.",
|
|
436
|
-
true,
|
|
437
|
-
result
|
|
438
|
-
);
|
|
439
|
-
}
|
|
440
|
-
const failedWrites = result.applied.filter((entry) => !entry.success);
|
|
441
|
-
if (failedWrites.length > 0) {
|
|
442
|
-
throw new LAFSCommandError(
|
|
443
|
-
"E_ADVANCED_APPLY_WRITE_FAILED",
|
|
444
|
-
"One or more MCP writes failed.",
|
|
445
|
-
"Check result details, fix provider config issues, and retry.",
|
|
446
|
-
true,
|
|
447
|
-
result
|
|
448
|
-
);
|
|
449
|
-
}
|
|
450
|
-
return {
|
|
451
|
-
objective: "Apply MCP operations with policy-driven conflict handling",
|
|
452
|
-
constraints: {
|
|
453
|
-
policy,
|
|
454
|
-
minimumPriority,
|
|
455
|
-
providerCount: providers.length,
|
|
456
|
-
operationCount: operations.length
|
|
457
|
-
},
|
|
458
|
-
acceptanceCriteria: {
|
|
459
|
-
conflicts: result.conflicts.length,
|
|
460
|
-
writesSucceeded: result.applied.length
|
|
461
|
-
},
|
|
462
|
-
data: opts.details ? result : {
|
|
463
|
-
conflicts: result.conflicts.length,
|
|
464
|
-
applied: result.applied.length,
|
|
465
|
-
skipped: result.skipped.length
|
|
466
|
-
}
|
|
467
|
-
};
|
|
468
|
-
})
|
|
469
|
-
);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
322
|
// src/commands/advanced/batch.ts
|
|
473
323
|
function registerAdvancedBatch(parent) {
|
|
474
|
-
parent.command("batch").description("Run rollback-capable batch install for
|
|
324
|
+
parent.command("batch").description("Run rollback-capable batch install for skills").option(
|
|
475
325
|
"-a, --agent <name>",
|
|
476
326
|
"Target specific provider(s)",
|
|
477
327
|
(v, prev) => [...prev, v],
|
|
478
328
|
[]
|
|
479
|
-
).option("--all", "Use all registry providers (not only detected)").option("--min-tier <tier>", "Minimum priority tier: high|medium|low", "low").
|
|
329
|
+
).option("--all", "Use all registry providers (not only detected)").option("--min-tier <tier>", "Minimum priority tier: high|medium|low", "low").requiredOption("--skills-file <path>", "JSON file containing SkillBatchOperation[]").option("--project-dir <path>", "Project directory to resolve project-scope paths").option("--details", "Include detailed operation result").action(
|
|
480
330
|
async (opts) => runLafsCommand("advanced.batch", opts.details ? "full" : "standard", async () => {
|
|
481
331
|
const baseProviders = resolveProviders({ all: opts.all, agent: opts.agent });
|
|
482
332
|
const minimumPriority = parsePriority(opts.minTier);
|
|
483
333
|
const providers = selectProvidersByMinimumPriority(baseProviders, minimumPriority);
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
if (mcp.length === 0 && skills.length === 0) {
|
|
334
|
+
const skills = await readSkillOperations(opts.skillsFile);
|
|
335
|
+
if (skills.length === 0) {
|
|
487
336
|
throw new LAFSCommandError(
|
|
488
337
|
"E_ADVANCED_VALIDATION_NO_OPS",
|
|
489
338
|
"No operations provided.",
|
|
490
|
-
"Provide --
|
|
339
|
+
"Provide a --skills-file with at least one operation."
|
|
491
340
|
);
|
|
492
341
|
}
|
|
493
342
|
if (providers.length === 0) {
|
|
@@ -500,7 +349,6 @@ function registerAdvancedBatch(parent) {
|
|
|
500
349
|
const result = await installBatchWithRollback({
|
|
501
350
|
providers,
|
|
502
351
|
minimumPriority,
|
|
503
|
-
mcp,
|
|
504
352
|
skills,
|
|
505
353
|
projectDir: opts.projectDir
|
|
506
354
|
});
|
|
@@ -514,11 +362,10 @@ function registerAdvancedBatch(parent) {
|
|
|
514
362
|
);
|
|
515
363
|
}
|
|
516
364
|
return {
|
|
517
|
-
objective: "Install
|
|
365
|
+
objective: "Install skills with rollback safety",
|
|
518
366
|
constraints: {
|
|
519
367
|
minimumPriority,
|
|
520
368
|
providerCount: providers.length,
|
|
521
|
-
mcpOps: mcp.length,
|
|
522
369
|
skillOps: skills.length
|
|
523
370
|
},
|
|
524
371
|
acceptanceCriteria: {
|
|
@@ -527,7 +374,6 @@ function registerAdvancedBatch(parent) {
|
|
|
527
374
|
},
|
|
528
375
|
data: opts.details ? result : {
|
|
529
376
|
providerCount: result.providerIds.length,
|
|
530
|
-
mcpApplied: result.mcpApplied,
|
|
531
377
|
skillsApplied: result.skillsApplied,
|
|
532
378
|
rollbackPerformed: result.rollbackPerformed
|
|
533
379
|
}
|
|
@@ -536,140 +382,6 @@ function registerAdvancedBatch(parent) {
|
|
|
536
382
|
);
|
|
537
383
|
}
|
|
538
384
|
|
|
539
|
-
// src/commands/advanced/configure.ts
|
|
540
|
-
function registerAdvancedConfigure(parent) {
|
|
541
|
-
parent.command("configure").description("Configure global + project scope for one provider in one operation").requiredOption("-a, --agent <name>", "Target provider ID or alias").option("--global-mcp-file <path>", "JSON file for global MCP operations").option("--project-mcp-file <path>", "JSON file for project MCP operations").option("--instruction <text>", "Instruction content for both scopes").option("--instruction-file <path>", "Instruction content file for both scopes").option("--instruction-global <text>", "Instruction content for global scope").option("--instruction-global-file <path>", "Instruction content file for global scope").option("--instruction-project <text>", "Instruction content for project scope").option("--instruction-project-file <path>", "Instruction content file for project scope").option("--project-dir <path>", "Project directory to resolve project-scope paths").option("--details", "Include detailed write results").action(
|
|
542
|
-
async (opts) => runLafsCommand("advanced.configure", opts.details ? "full" : "standard", async () => {
|
|
543
|
-
const provider = getProvider(opts.agent);
|
|
544
|
-
if (!provider) {
|
|
545
|
-
throw new LAFSCommandError(
|
|
546
|
-
"E_ADVANCED_PROVIDER_NOT_FOUND",
|
|
547
|
-
`Unknown provider: ${opts.agent}`,
|
|
548
|
-
"Check `caamp providers list` for valid provider IDs/aliases."
|
|
549
|
-
);
|
|
550
|
-
}
|
|
551
|
-
const globalMcp = opts.globalMcpFile ? await readMcpOperations(opts.globalMcpFile) : [];
|
|
552
|
-
const projectMcp = opts.projectMcpFile ? await readMcpOperations(opts.projectMcpFile) : [];
|
|
553
|
-
const sharedInstruction = await readTextInput(opts.instruction, opts.instructionFile);
|
|
554
|
-
const globalInstruction = await readTextInput(
|
|
555
|
-
opts.instructionGlobal,
|
|
556
|
-
opts.instructionGlobalFile
|
|
557
|
-
);
|
|
558
|
-
const projectInstruction = await readTextInput(
|
|
559
|
-
opts.instructionProject,
|
|
560
|
-
opts.instructionProjectFile
|
|
561
|
-
);
|
|
562
|
-
let instructionContent;
|
|
563
|
-
if (globalInstruction || projectInstruction) {
|
|
564
|
-
instructionContent = {
|
|
565
|
-
...globalInstruction ? { global: globalInstruction } : {},
|
|
566
|
-
...projectInstruction ? { project: projectInstruction } : {}
|
|
567
|
-
};
|
|
568
|
-
} else if (sharedInstruction) {
|
|
569
|
-
instructionContent = sharedInstruction;
|
|
570
|
-
}
|
|
571
|
-
if (globalMcp.length === 0 && projectMcp.length === 0 && !instructionContent) {
|
|
572
|
-
throw new LAFSCommandError(
|
|
573
|
-
"E_ADVANCED_VALIDATION_NO_OPS",
|
|
574
|
-
"No configuration operations were provided.",
|
|
575
|
-
"Provide MCP files and/or instruction content."
|
|
576
|
-
);
|
|
577
|
-
}
|
|
578
|
-
const result = await configureProviderGlobalAndProject(provider, {
|
|
579
|
-
globalMcp: globalMcp.map((entry) => ({
|
|
580
|
-
serverName: entry.serverName,
|
|
581
|
-
config: entry.config
|
|
582
|
-
})),
|
|
583
|
-
projectMcp: projectMcp.map((entry) => ({
|
|
584
|
-
serverName: entry.serverName,
|
|
585
|
-
config: entry.config
|
|
586
|
-
})),
|
|
587
|
-
instructionContent,
|
|
588
|
-
projectDir: opts.projectDir
|
|
589
|
-
});
|
|
590
|
-
const globalFailures = result.mcp.global.filter((entry) => !entry.success);
|
|
591
|
-
const projectFailures = result.mcp.project.filter((entry) => !entry.success);
|
|
592
|
-
if (globalFailures.length > 0 || projectFailures.length > 0) {
|
|
593
|
-
throw new LAFSCommandError(
|
|
594
|
-
"E_ADVANCED_CONFIGURE_FAILED",
|
|
595
|
-
"One or more MCP writes failed during configure operation.",
|
|
596
|
-
"Inspect the failed write entries and provider config paths, then retry.",
|
|
597
|
-
true,
|
|
598
|
-
result
|
|
599
|
-
);
|
|
600
|
-
}
|
|
601
|
-
return {
|
|
602
|
-
objective: "Configure global and project settings in one operation",
|
|
603
|
-
constraints: {
|
|
604
|
-
provider: provider.id,
|
|
605
|
-
globalMcpOps: globalMcp.length,
|
|
606
|
-
projectMcpOps: projectMcp.length,
|
|
607
|
-
instructionMode: instructionContent ? typeof instructionContent === "string" ? "shared" : "scoped" : "none"
|
|
608
|
-
},
|
|
609
|
-
acceptanceCriteria: {
|
|
610
|
-
globalWrites: result.mcp.global.length,
|
|
611
|
-
projectWrites: result.mcp.project.length
|
|
612
|
-
},
|
|
613
|
-
data: opts.details ? result : {
|
|
614
|
-
providerId: result.providerId,
|
|
615
|
-
configPaths: result.configPaths,
|
|
616
|
-
globalWrites: result.mcp.global.length,
|
|
617
|
-
projectWrites: result.mcp.project.length,
|
|
618
|
-
instructionUpdates: {
|
|
619
|
-
global: result.instructions.global?.size ?? 0,
|
|
620
|
-
project: result.instructions.project?.size ?? 0
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
};
|
|
624
|
-
})
|
|
625
|
-
);
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
// src/commands/advanced/conflicts.ts
|
|
629
|
-
function registerAdvancedConflicts(parent) {
|
|
630
|
-
parent.command("conflicts").description("Preflight MCP conflict detection across providers").requiredOption("--mcp-file <path>", "JSON file containing McpBatchOperation[]").option(
|
|
631
|
-
"-a, --agent <name>",
|
|
632
|
-
"Target specific provider(s)",
|
|
633
|
-
(v, prev) => [...prev, v],
|
|
634
|
-
[]
|
|
635
|
-
).option("--all", "Use all registry providers (not only detected)").option("--min-tier <tier>", "Minimum priority tier: high|medium|low", "low").option("--project-dir <path>", "Project directory to resolve project-scope paths").option("--details", "Include full conflict list").action(
|
|
636
|
-
async (opts) => runLafsCommand("advanced.conflicts", opts.details ? "full" : "standard", async () => {
|
|
637
|
-
const baseProviders = resolveProviders({ all: opts.all, agent: opts.agent });
|
|
638
|
-
const minimumPriority = parsePriority(opts.minTier);
|
|
639
|
-
const providers = selectProvidersByMinimumPriority(baseProviders, minimumPriority);
|
|
640
|
-
const operations = await readMcpOperations(opts.mcpFile);
|
|
641
|
-
if (providers.length === 0) {
|
|
642
|
-
throw new LAFSCommandError(
|
|
643
|
-
"E_ADVANCED_NO_TARGET_PROVIDERS",
|
|
644
|
-
"No target providers resolved for conflict detection.",
|
|
645
|
-
"Use --all or pass provider IDs with --agent."
|
|
646
|
-
);
|
|
647
|
-
}
|
|
648
|
-
const conflicts = await detectMcpConfigConflicts(providers, operations, opts.projectDir);
|
|
649
|
-
const countByCode = conflicts.reduce((acc, conflict) => {
|
|
650
|
-
acc[conflict.code] = (acc[conflict.code] ?? 0) + 1;
|
|
651
|
-
return acc;
|
|
652
|
-
}, {});
|
|
653
|
-
return {
|
|
654
|
-
objective: "Detect MCP configuration conflicts before mutation",
|
|
655
|
-
constraints: {
|
|
656
|
-
minimumPriority,
|
|
657
|
-
providerCount: providers.length,
|
|
658
|
-
operationCount: operations.length
|
|
659
|
-
},
|
|
660
|
-
acceptanceCriteria: {
|
|
661
|
-
conflictCount: conflicts.length
|
|
662
|
-
},
|
|
663
|
-
data: opts.details ? conflicts : {
|
|
664
|
-
conflictCount: conflicts.length,
|
|
665
|
-
countByCode,
|
|
666
|
-
sample: conflicts.slice(0, 5)
|
|
667
|
-
}
|
|
668
|
-
};
|
|
669
|
-
})
|
|
670
|
-
);
|
|
671
|
-
}
|
|
672
|
-
|
|
673
385
|
// src/commands/advanced/instructions.ts
|
|
674
386
|
function registerAdvancedInstructions(parent) {
|
|
675
387
|
parent.command("instructions").description("Single-operation instruction update across providers").option(
|
|
@@ -771,10 +483,7 @@ function registerAdvancedCommands(program2) {
|
|
|
771
483
|
const advanced = program2.command("advanced").description("LAFS-compliant wrappers for advanced orchestration APIs");
|
|
772
484
|
registerAdvancedProviders(advanced);
|
|
773
485
|
registerAdvancedBatch(advanced);
|
|
774
|
-
registerAdvancedConflicts(advanced);
|
|
775
|
-
registerAdvancedApply(advanced);
|
|
776
486
|
registerAdvancedInstructions(advanced);
|
|
777
|
-
registerAdvancedConfigure(advanced);
|
|
778
487
|
}
|
|
779
488
|
|
|
780
489
|
// src/commands/config.ts
|
|
@@ -1256,69 +965,6 @@ async function checkLockFile() {
|
|
|
1256
965
|
}
|
|
1257
966
|
return { name: "Lock File", checks };
|
|
1258
967
|
}
|
|
1259
|
-
async function checkMcpLockEntries() {
|
|
1260
|
-
const checks = [];
|
|
1261
|
-
try {
|
|
1262
|
-
const lock = await readLockFile();
|
|
1263
|
-
const lockNames = Object.keys(lock.mcpServers);
|
|
1264
|
-
checks.push({ label: `${lockNames.length} MCP server entries in lock`, status: "pass" });
|
|
1265
|
-
const results = detectAllProviders();
|
|
1266
|
-
const installed = results.filter((r) => r.installed);
|
|
1267
|
-
const liveCleoNames = /* @__PURE__ */ new Set();
|
|
1268
|
-
let untrackedCount = 0;
|
|
1269
|
-
for (const scope of ["project", "global"]) {
|
|
1270
|
-
for (const r of installed) {
|
|
1271
|
-
try {
|
|
1272
|
-
const entries = await listMcpServers(r.provider, scope);
|
|
1273
|
-
for (const entry of entries) {
|
|
1274
|
-
const channel = resolveChannelFromServerName(entry.name);
|
|
1275
|
-
if (!channel) continue;
|
|
1276
|
-
liveCleoNames.add(entry.name);
|
|
1277
|
-
if (!lock.mcpServers[entry.name]) {
|
|
1278
|
-
untrackedCount++;
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
} catch {
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
if (untrackedCount === 0) {
|
|
1286
|
-
checks.push({ label: "All CLEO servers tracked in lock", status: "pass" });
|
|
1287
|
-
} else {
|
|
1288
|
-
checks.push({
|
|
1289
|
-
label: `${untrackedCount} untracked CLEO server${untrackedCount !== 1 ? "s" : ""} (in config, not in lock)`,
|
|
1290
|
-
status: "warn",
|
|
1291
|
-
detail: "Run `caamp cleo repair` to backfill lock entries"
|
|
1292
|
-
});
|
|
1293
|
-
}
|
|
1294
|
-
let orphanedCount = 0;
|
|
1295
|
-
const orphanedNames = [];
|
|
1296
|
-
for (const serverName of lockNames) {
|
|
1297
|
-
const channel = resolveChannelFromServerName(serverName);
|
|
1298
|
-
if (!channel) continue;
|
|
1299
|
-
if (!liveCleoNames.has(serverName)) {
|
|
1300
|
-
orphanedCount++;
|
|
1301
|
-
orphanedNames.push(serverName);
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
|
-
if (orphanedCount === 0) {
|
|
1305
|
-
checks.push({ label: "No orphaned CLEO lock entries", status: "pass" });
|
|
1306
|
-
} else {
|
|
1307
|
-
checks.push({
|
|
1308
|
-
label: `${orphanedCount} orphaned CLEO lock entr${orphanedCount !== 1 ? "ies" : "y"} (in lock, not in any config)`,
|
|
1309
|
-
status: "warn",
|
|
1310
|
-
detail: orphanedNames.join(", ") + " \u2014 Run `caamp cleo repair --prune` to clean up"
|
|
1311
|
-
});
|
|
1312
|
-
}
|
|
1313
|
-
} catch (err) {
|
|
1314
|
-
checks.push({
|
|
1315
|
-
label: "Failed to check MCP lock entries",
|
|
1316
|
-
status: "fail",
|
|
1317
|
-
detail: err instanceof Error ? err.message : String(err)
|
|
1318
|
-
});
|
|
1319
|
-
}
|
|
1320
|
-
return { name: "MCP Lock", checks };
|
|
1321
|
-
}
|
|
1322
968
|
async function checkConfigFiles() {
|
|
1323
969
|
const checks = [];
|
|
1324
970
|
const results = detectAllProviders();
|
|
@@ -1387,7 +1033,6 @@ function registerDoctorCommand(program2) {
|
|
|
1387
1033
|
sections.push(checkInstalledProviders());
|
|
1388
1034
|
sections.push(checkSkillSymlinks());
|
|
1389
1035
|
sections.push(await checkLockFile());
|
|
1390
|
-
sections.push(await checkMcpLockEntries());
|
|
1391
1036
|
sections.push(await checkConfigFiles());
|
|
1392
1037
|
let passed = 0;
|
|
1393
1038
|
let warnings = 0;
|
|
@@ -1407,11 +1052,6 @@ function registerDoctorCommand(program2) {
|
|
|
1407
1052
|
const detectionResults = detectAllProviders();
|
|
1408
1053
|
const installedProviders = detectionResults.filter((r) => r.installed);
|
|
1409
1054
|
const { canonicalCount, brokenCount, staleCount } = countSkillIssues();
|
|
1410
|
-
const {
|
|
1411
|
-
tracked: mcpTracked,
|
|
1412
|
-
untracked: mcpUntracked,
|
|
1413
|
-
orphaned: mcpOrphaned
|
|
1414
|
-
} = countMcpLockIssues(sections);
|
|
1415
1055
|
const result = {
|
|
1416
1056
|
environment: {
|
|
1417
1057
|
node: getNodeVersion(),
|
|
@@ -1433,11 +1073,6 @@ function registerDoctorCommand(program2) {
|
|
|
1433
1073
|
brokenLinks: brokenCount,
|
|
1434
1074
|
staleLinks: staleCount
|
|
1435
1075
|
},
|
|
1436
|
-
mcpServers: {
|
|
1437
|
-
tracked: mcpTracked,
|
|
1438
|
-
untracked: mcpUntracked,
|
|
1439
|
-
orphaned: mcpOrphaned
|
|
1440
|
-
},
|
|
1441
1076
|
checks: sections.flatMap(
|
|
1442
1077
|
(s) => s.checks.map((c) => ({
|
|
1443
1078
|
label: `${s.name}: ${c.label}`,
|
|
@@ -1526,1278 +1161,32 @@ function countSkillIssues() {
|
|
|
1526
1161
|
staleCount++;
|
|
1527
1162
|
}
|
|
1528
1163
|
}
|
|
1529
|
-
} catch {
|
|
1530
|
-
}
|
|
1531
|
-
}
|
|
1532
|
-
} catch {
|
|
1533
|
-
}
|
|
1534
|
-
}
|
|
1535
|
-
return { canonicalCount, brokenCount, staleCount };
|
|
1536
|
-
}
|
|
1537
|
-
function countMcpLockIssues(sections) {
|
|
1538
|
-
const mcpSection = sections.find((s) => s.name === "MCP Lock");
|
|
1539
|
-
if (!mcpSection) return { tracked: 0, untracked: 0, orphaned: 0 };
|
|
1540
|
-
let tracked = 0;
|
|
1541
|
-
let untracked = 0;
|
|
1542
|
-
let orphaned = 0;
|
|
1543
|
-
for (const check of mcpSection.checks) {
|
|
1544
|
-
const countMatch = check.label.match(/^(\d+)/);
|
|
1545
|
-
if (!countMatch?.[1]) continue;
|
|
1546
|
-
const count = Number.parseInt(countMatch[1], 10);
|
|
1547
|
-
if (check.label.includes("MCP server entries in lock")) {
|
|
1548
|
-
tracked = count;
|
|
1549
|
-
} else if (check.label.includes("untracked")) {
|
|
1550
|
-
untracked = count;
|
|
1551
|
-
} else if (check.label.includes("orphaned")) {
|
|
1552
|
-
orphaned = count;
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1555
|
-
return { tracked, untracked, orphaned };
|
|
1556
|
-
}
|
|
1557
|
-
|
|
1558
|
-
// src/commands/instructions/check.ts
|
|
1559
|
-
import pc3 from "picocolors";
|
|
1560
|
-
function registerInstructionsCheck(parent) {
|
|
1561
|
-
parent.command("check").description("Check injection status across providers").option(
|
|
1562
|
-
"-a, --agent <name>",
|
|
1563
|
-
"Check specific agent(s)",
|
|
1564
|
-
(v, prev) => [...prev, v],
|
|
1565
|
-
[]
|
|
1566
|
-
).option("-g, --global", "Check global instruction files").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").option("--all", "Check all known providers").action(
|
|
1567
|
-
async (opts) => {
|
|
1568
|
-
const operation = "instructions.check";
|
|
1569
|
-
const mvi = "standard";
|
|
1570
|
-
let format;
|
|
1571
|
-
try {
|
|
1572
|
-
format = resolveFormat({
|
|
1573
|
-
jsonFlag: opts.json ?? false,
|
|
1574
|
-
humanFlag: opts.human ?? false,
|
|
1575
|
-
projectDefault: "json"
|
|
1576
|
-
});
|
|
1577
|
-
} catch (error) {
|
|
1578
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1579
|
-
emitJsonError(
|
|
1580
|
-
operation,
|
|
1581
|
-
mvi,
|
|
1582
|
-
ErrorCodes.FORMAT_CONFLICT,
|
|
1583
|
-
message,
|
|
1584
|
-
ErrorCategories.VALIDATION
|
|
1585
|
-
);
|
|
1586
|
-
process.exit(1);
|
|
1587
|
-
}
|
|
1588
|
-
let providers;
|
|
1589
|
-
if (opts.all) {
|
|
1590
|
-
providers = getAllProviders();
|
|
1591
|
-
} else if (opts.agent.length > 0) {
|
|
1592
|
-
providers = opts.agent.map((a) => getProvider(a)).filter((p) => p !== void 0);
|
|
1593
|
-
} else {
|
|
1594
|
-
providers = getInstalledProviders();
|
|
1595
|
-
}
|
|
1596
|
-
const scope = opts.global ? "global" : "project";
|
|
1597
|
-
const results = await checkAllInjections(providers, process.cwd(), scope);
|
|
1598
|
-
const providerStatus = results.map((r) => ({
|
|
1599
|
-
id: r.provider,
|
|
1600
|
-
present: r.status === "current" || r.status === "outdated",
|
|
1601
|
-
path: r.file
|
|
1602
|
-
}));
|
|
1603
|
-
const present = providerStatus.filter((p) => p.present).length;
|
|
1604
|
-
const missing = providerStatus.filter((p) => !p.present).length;
|
|
1605
|
-
if (format === "json") {
|
|
1606
|
-
outputSuccess(operation, mvi, {
|
|
1607
|
-
providers: providerStatus,
|
|
1608
|
-
present,
|
|
1609
|
-
missing
|
|
1610
|
-
});
|
|
1611
|
-
return;
|
|
1612
|
-
}
|
|
1613
|
-
console.log(pc3.bold(`
|
|
1614
|
-
Instruction file status (${scope}):
|
|
1615
|
-
`));
|
|
1616
|
-
for (const r of results) {
|
|
1617
|
-
let icon;
|
|
1618
|
-
let label;
|
|
1619
|
-
switch (r.status) {
|
|
1620
|
-
case "current":
|
|
1621
|
-
icon = pc3.green("\u2713");
|
|
1622
|
-
label = "current";
|
|
1623
|
-
break;
|
|
1624
|
-
case "outdated":
|
|
1625
|
-
icon = pc3.yellow("~");
|
|
1626
|
-
label = "outdated";
|
|
1627
|
-
break;
|
|
1628
|
-
case "missing":
|
|
1629
|
-
icon = pc3.red("\u2717");
|
|
1630
|
-
label = "missing";
|
|
1631
|
-
break;
|
|
1632
|
-
case "none":
|
|
1633
|
-
icon = pc3.dim("-");
|
|
1634
|
-
label = "no injection";
|
|
1635
|
-
break;
|
|
1636
|
-
}
|
|
1637
|
-
console.log(` ${icon} ${r.file.padEnd(40)} ${label}`);
|
|
1638
|
-
}
|
|
1639
|
-
console.log();
|
|
1640
|
-
}
|
|
1641
|
-
);
|
|
1642
|
-
}
|
|
1643
|
-
|
|
1644
|
-
// src/commands/instructions/inject.ts
|
|
1645
|
-
import pc4 from "picocolors";
|
|
1646
|
-
function registerInstructionsInject(parent) {
|
|
1647
|
-
parent.command("inject").description("Inject instruction blocks into all provider files").option(
|
|
1648
|
-
"-a, --agent <name>",
|
|
1649
|
-
"Target specific agent(s)",
|
|
1650
|
-
(v, prev) => [...prev, v],
|
|
1651
|
-
[]
|
|
1652
|
-
).option("-g, --global", "Inject into global instruction files").option("--content <text>", "Custom content to inject").option("--dry-run", "Preview without writing").option("--all", "Target all known providers").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(
|
|
1653
|
-
async (opts) => {
|
|
1654
|
-
const operation = "instructions.inject";
|
|
1655
|
-
const mvi = "standard";
|
|
1656
|
-
let format;
|
|
1657
|
-
try {
|
|
1658
|
-
format = resolveFormat({
|
|
1659
|
-
jsonFlag: opts.json ?? false,
|
|
1660
|
-
humanFlag: opts.human ?? false,
|
|
1661
|
-
projectDefault: "json"
|
|
1662
|
-
});
|
|
1663
|
-
} catch (error) {
|
|
1664
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1665
|
-
emitJsonError(
|
|
1666
|
-
operation,
|
|
1667
|
-
mvi,
|
|
1668
|
-
ErrorCodes.FORMAT_CONFLICT,
|
|
1669
|
-
message,
|
|
1670
|
-
ErrorCategories.VALIDATION
|
|
1671
|
-
);
|
|
1672
|
-
process.exit(1);
|
|
1673
|
-
}
|
|
1674
|
-
let providers;
|
|
1675
|
-
if (opts.all) {
|
|
1676
|
-
providers = getAllProviders();
|
|
1677
|
-
} else if (opts.agent.length > 0) {
|
|
1678
|
-
providers = opts.agent.map((a) => getProvider(a)).filter((p) => p !== void 0);
|
|
1679
|
-
} else {
|
|
1680
|
-
providers = getInstalledProviders();
|
|
1681
|
-
}
|
|
1682
|
-
if (providers.length === 0) {
|
|
1683
|
-
const message = "No providers found.";
|
|
1684
|
-
if (format === "json") {
|
|
1685
|
-
emitJsonError(
|
|
1686
|
-
operation,
|
|
1687
|
-
mvi,
|
|
1688
|
-
ErrorCodes.PROVIDER_NOT_FOUND,
|
|
1689
|
-
message,
|
|
1690
|
-
ErrorCategories.NOT_FOUND
|
|
1691
|
-
);
|
|
1692
|
-
} else {
|
|
1693
|
-
console.error(pc4.red(message));
|
|
1694
|
-
}
|
|
1695
|
-
process.exit(1);
|
|
1696
|
-
}
|
|
1697
|
-
const content = opts.content ?? generateInjectionContent();
|
|
1698
|
-
const scope = opts.global ? "global" : "project";
|
|
1699
|
-
const groups = groupByInstructFile(providers);
|
|
1700
|
-
if (opts.dryRun) {
|
|
1701
|
-
if (format === "json") {
|
|
1702
|
-
outputSuccess(operation, mvi, {
|
|
1703
|
-
injected: [],
|
|
1704
|
-
providers: providers.map((p) => p.id),
|
|
1705
|
-
count: 0,
|
|
1706
|
-
dryRun: true,
|
|
1707
|
-
wouldInject: Array.from(groups.entries()).map(([file, group]) => ({
|
|
1708
|
-
file,
|
|
1709
|
-
providers: group.map((p) => p.id)
|
|
1710
|
-
}))
|
|
1711
|
-
});
|
|
1712
|
-
} else {
|
|
1713
|
-
console.log(pc4.bold("Dry run - would inject into:\n"));
|
|
1714
|
-
for (const [file, group] of groups) {
|
|
1715
|
-
console.log(` ${pc4.bold(file)}: ${group.map((p) => p.id).join(", ")}`);
|
|
1716
|
-
}
|
|
1717
|
-
console.log(pc4.dim(`
|
|
1718
|
-
Scope: ${scope}`));
|
|
1719
|
-
console.log(pc4.dim(` Content length: ${content.length} chars`));
|
|
1720
|
-
}
|
|
1721
|
-
return;
|
|
1722
|
-
}
|
|
1723
|
-
const results = await injectAll(providers, process.cwd(), scope, content);
|
|
1724
|
-
const injected = [];
|
|
1725
|
-
for (const [file] of results) {
|
|
1726
|
-
injected.push(file);
|
|
1727
|
-
}
|
|
1728
|
-
if (format === "json") {
|
|
1729
|
-
outputSuccess(operation, mvi, {
|
|
1730
|
-
injected,
|
|
1731
|
-
providers: providers.map((p) => p.id),
|
|
1732
|
-
count: results.size
|
|
1733
|
-
});
|
|
1734
|
-
} else {
|
|
1735
|
-
for (const [file, action] of results) {
|
|
1736
|
-
const icon = action === "created" ? pc4.green("+") : action === "updated" ? pc4.yellow("~") : pc4.blue("^");
|
|
1737
|
-
console.log(` ${icon} ${file} (${action})`);
|
|
1738
|
-
}
|
|
1739
|
-
console.log(pc4.bold(`
|
|
1740
|
-
${results.size} file(s) processed.`));
|
|
1741
|
-
}
|
|
1742
|
-
}
|
|
1743
|
-
);
|
|
1744
|
-
}
|
|
1745
|
-
|
|
1746
|
-
// src/commands/instructions/update.ts
|
|
1747
|
-
import pc5 from "picocolors";
|
|
1748
|
-
function registerInstructionsUpdate(parent) {
|
|
1749
|
-
parent.command("update").description("Update all instruction file injections").option("-g, --global", "Update global instruction files").option("-y, --yes", "Skip confirmation").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
1750
|
-
const operation = "instructions.update";
|
|
1751
|
-
const mvi = "standard";
|
|
1752
|
-
let format;
|
|
1753
|
-
try {
|
|
1754
|
-
format = resolveFormat({
|
|
1755
|
-
jsonFlag: opts.json ?? false,
|
|
1756
|
-
humanFlag: opts.human ?? false,
|
|
1757
|
-
projectDefault: "json"
|
|
1758
|
-
});
|
|
1759
|
-
} catch (error) {
|
|
1760
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1761
|
-
emitJsonError(
|
|
1762
|
-
operation,
|
|
1763
|
-
mvi,
|
|
1764
|
-
ErrorCodes.FORMAT_CONFLICT,
|
|
1765
|
-
message,
|
|
1766
|
-
ErrorCategories.VALIDATION
|
|
1767
|
-
);
|
|
1768
|
-
process.exit(1);
|
|
1769
|
-
}
|
|
1770
|
-
const providers = getInstalledProviders();
|
|
1771
|
-
const scope = opts.global ? "global" : "project";
|
|
1772
|
-
const content = generateInjectionContent();
|
|
1773
|
-
const checks = await checkAllInjections(providers, process.cwd(), scope, content);
|
|
1774
|
-
const needsUpdate = checks.filter((c) => c.status !== "current");
|
|
1775
|
-
if (needsUpdate.length === 0) {
|
|
1776
|
-
if (format === "json") {
|
|
1777
|
-
outputSuccess(operation, mvi, {
|
|
1778
|
-
updated: [],
|
|
1779
|
-
failed: [],
|
|
1780
|
-
count: { updated: 0, failed: 0 }
|
|
1781
|
-
});
|
|
1782
|
-
} else {
|
|
1783
|
-
console.log(pc5.green("All instruction files are up to date."));
|
|
1784
|
-
}
|
|
1785
|
-
return;
|
|
1786
|
-
}
|
|
1787
|
-
if (format === "human") {
|
|
1788
|
-
console.log(pc5.bold(`${needsUpdate.length} file(s) need updating:
|
|
1789
|
-
`));
|
|
1790
|
-
for (const c of needsUpdate) {
|
|
1791
|
-
console.log(` ${c.file} (${c.status})`);
|
|
1792
|
-
}
|
|
1793
|
-
}
|
|
1794
|
-
const providerIds = new Set(needsUpdate.map((c) => c.provider));
|
|
1795
|
-
const toUpdate = providers.filter((p) => providerIds.has(p.id));
|
|
1796
|
-
const results = await injectAll(toUpdate, process.cwd(), scope, content);
|
|
1797
|
-
const updated = [];
|
|
1798
|
-
for (const [file] of results) {
|
|
1799
|
-
updated.push(file);
|
|
1800
|
-
}
|
|
1801
|
-
if (format === "human") {
|
|
1802
|
-
console.log();
|
|
1803
|
-
for (const [file, action] of results) {
|
|
1804
|
-
console.log(` ${pc5.green("\u2713")} ${file} (${action})`);
|
|
1805
|
-
}
|
|
1806
|
-
console.log(pc5.bold(`
|
|
1807
|
-
${results.size} file(s) updated.`));
|
|
1808
|
-
}
|
|
1809
|
-
if (format === "json") {
|
|
1810
|
-
outputSuccess(operation, mvi, {
|
|
1811
|
-
updated,
|
|
1812
|
-
failed: [],
|
|
1813
|
-
count: { updated: updated.length, failed: 0 }
|
|
1814
|
-
});
|
|
1815
|
-
}
|
|
1816
|
-
});
|
|
1817
|
-
}
|
|
1818
|
-
|
|
1819
|
-
// src/commands/instructions/index.ts
|
|
1820
|
-
function registerInstructionsCommands(program2) {
|
|
1821
|
-
const instructions = program2.command("instructions").description("Manage instruction file injections");
|
|
1822
|
-
registerInstructionsInject(instructions);
|
|
1823
|
-
registerInstructionsCheck(instructions);
|
|
1824
|
-
registerInstructionsUpdate(instructions);
|
|
1825
|
-
}
|
|
1826
|
-
|
|
1827
|
-
// src/commands/mcp/cleo.ts
|
|
1828
|
-
import { createInterface } from "readline/promises";
|
|
1829
|
-
import pc6 from "picocolors";
|
|
1830
|
-
function collect(value, previous) {
|
|
1831
|
-
return [...previous, value];
|
|
1832
|
-
}
|
|
1833
|
-
function collectTargetProviders(providerIds, all) {
|
|
1834
|
-
if (all) {
|
|
1835
|
-
return getInstalledProviders();
|
|
1836
|
-
}
|
|
1837
|
-
if (providerIds.length > 0) {
|
|
1838
|
-
return providerIds.map((id) => getProvider(id)).filter((provider) => provider !== void 0);
|
|
1839
|
-
}
|
|
1840
|
-
return getInstalledProviders();
|
|
1841
|
-
}
|
|
1842
|
-
async function validateProfile(provider, scope, serverName) {
|
|
1843
|
-
const entries = await listMcpServers(provider, scope);
|
|
1844
|
-
const entry = entries.find((candidate) => candidate.name === serverName);
|
|
1845
|
-
if (!entry) {
|
|
1846
|
-
return { valid: false, reason: "server missing after write" };
|
|
1847
|
-
}
|
|
1848
|
-
const command = typeof entry.config.command === "string" ? entry.config.command : void 0;
|
|
1849
|
-
if (!command) {
|
|
1850
|
-
return { valid: true };
|
|
1851
|
-
}
|
|
1852
|
-
const reachability = checkCommandReachability(command);
|
|
1853
|
-
if (!reachability.reachable) {
|
|
1854
|
-
return {
|
|
1855
|
-
valid: false,
|
|
1856
|
-
reason: `command not reachable (${reachability.method}: ${reachability.detail})`
|
|
1857
|
-
};
|
|
1858
|
-
}
|
|
1859
|
-
return { valid: true };
|
|
1860
|
-
}
|
|
1861
|
-
async function detectServerConflicts(providers, scope, targetServerName) {
|
|
1862
|
-
const warnings = [];
|
|
1863
|
-
for (const provider of providers) {
|
|
1864
|
-
const entries = await listMcpServers(provider, scope);
|
|
1865
|
-
const existing = entries.find((entry) => entry.name === targetServerName);
|
|
1866
|
-
if (!existing) continue;
|
|
1867
|
-
const command = typeof existing.config.command === "string" ? existing.config.command : "";
|
|
1868
|
-
const args = Array.isArray(existing.config.args) ? existing.config.args.filter((value) => typeof value === "string") : [];
|
|
1869
|
-
const flat = `${command} ${args.join(" ")}`.toLowerCase();
|
|
1870
|
-
if (!flat.includes("cleo")) {
|
|
1871
|
-
warnings.push({
|
|
1872
|
-
providerId: provider.id,
|
|
1873
|
-
message: `Server name '${targetServerName}' already exists with a non-CLEO command in ${provider.id}.`
|
|
1874
|
-
});
|
|
1875
|
-
}
|
|
1876
|
-
}
|
|
1877
|
-
return warnings;
|
|
1878
|
-
}
|
|
1879
|
-
function formatInstallResultHuman(mode, channel, serverName, scope, results, validations) {
|
|
1880
|
-
console.log(pc6.bold(`${mode === "install" ? "Install" : "Update"} CLEO channel: ${channel}`));
|
|
1881
|
-
console.log(pc6.dim(`Server: ${serverName} Scope: ${scope}`));
|
|
1882
|
-
console.log();
|
|
1883
|
-
for (const result of results) {
|
|
1884
|
-
const validation = validations.find((entry) => entry.providerId === result.provider.id);
|
|
1885
|
-
if (result.success) {
|
|
1886
|
-
const validationLabel = validation?.valid ? pc6.green("validated") : pc6.yellow(`validation warning: ${validation?.reason ?? "unknown"}`);
|
|
1887
|
-
console.log(
|
|
1888
|
-
` ${pc6.green("+")} ${result.provider.toolName.padEnd(22)} ${pc6.dim(result.configPath)} ${validationLabel}`
|
|
1889
|
-
);
|
|
1890
|
-
} else {
|
|
1891
|
-
console.log(
|
|
1892
|
-
` ${pc6.red("x")} ${result.provider.toolName.padEnd(22)} ${pc6.red(result.error ?? "failed")}`
|
|
1893
|
-
);
|
|
1894
|
-
console.log(pc6.dim(" Recovery: verify config path permissions and retry with --dry-run."));
|
|
1895
|
-
}
|
|
1896
|
-
}
|
|
1897
|
-
console.log();
|
|
1898
|
-
}
|
|
1899
|
-
async function runInteractiveInstall(opts) {
|
|
1900
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1901
|
-
try {
|
|
1902
|
-
const discovered = getInstalledProviders();
|
|
1903
|
-
if (discovered.length === 0) {
|
|
1904
|
-
throw new Error("No installed providers were detected for interactive setup.");
|
|
1905
|
-
}
|
|
1906
|
-
console.log(pc6.bold("CLEO MCP Setup"));
|
|
1907
|
-
console.log(pc6.dim("Step 1/6 - Select provider(s)"));
|
|
1908
|
-
for (const [index, provider] of discovered.entries()) {
|
|
1909
|
-
console.log(` ${index + 1}. ${provider.id} (${provider.toolName})`);
|
|
1910
|
-
}
|
|
1911
|
-
const providerAnswer = await rl.question(pc6.dim("Choose providers (e.g. 1,2 or all): "));
|
|
1912
|
-
const selectedProviders = providerAnswer.trim().toLowerCase() === "all" ? discovered.map((provider) => provider.id) : providerAnswer.split(",").map((part) => Number(part.trim())).filter((value) => Number.isFinite(value) && value > 0 && value <= discovered.length).map((index) => discovered[index - 1]?.id).filter((id) => Boolean(id));
|
|
1913
|
-
if (selectedProviders.length === 0) {
|
|
1914
|
-
throw new Error("No providers selected.");
|
|
1915
|
-
}
|
|
1916
|
-
console.log();
|
|
1917
|
-
console.log(pc6.dim("Step 2/6 - Select channel"));
|
|
1918
|
-
const channelAnswer = await rl.question(pc6.dim("Channel [stable/beta/dev] (stable): "));
|
|
1919
|
-
const selectedChannel = normalizeCleoChannel(channelAnswer || "stable");
|
|
1920
|
-
let command = opts.command;
|
|
1921
|
-
let args = [...opts.arg];
|
|
1922
|
-
let env = [...opts.env];
|
|
1923
|
-
let cleoDir = opts.cleoDir;
|
|
1924
|
-
if (selectedChannel === "dev") {
|
|
1925
|
-
command = await rl.question(pc6.dim("Dev command (required): "));
|
|
1926
|
-
const argsAnswer = await rl.question(pc6.dim("Dev args (space-separated, optional): "));
|
|
1927
|
-
args = argsAnswer.trim() === "" ? [] : argsAnswer.trim().split(/\s+/);
|
|
1928
|
-
const dirAnswer = await rl.question(pc6.dim("CLEO_DIR (~/.cleo-dev default): "));
|
|
1929
|
-
cleoDir = dirAnswer.trim() === "" ? "~/.cleo-dev" : dirAnswer.trim();
|
|
1930
|
-
if (cleoDir.trim() !== "") {
|
|
1931
|
-
env = [...env.filter((entry) => !entry.startsWith("CLEO_DIR=")), `CLEO_DIR=${cleoDir}`];
|
|
1932
|
-
}
|
|
1933
|
-
}
|
|
1934
|
-
const profile = buildCleoProfile({
|
|
1935
|
-
channel: selectedChannel,
|
|
1936
|
-
version: opts.version,
|
|
1937
|
-
command,
|
|
1938
|
-
args,
|
|
1939
|
-
env: parseEnvAssignments(env),
|
|
1940
|
-
cleoDir
|
|
1941
|
-
});
|
|
1942
|
-
console.log();
|
|
1943
|
-
console.log(pc6.dim("Step 3/6 - Preview profile diff"));
|
|
1944
|
-
console.log(` Server: ${pc6.bold(profile.serverName)}`);
|
|
1945
|
-
console.log(` Channel: ${selectedChannel}`);
|
|
1946
|
-
console.log(` Config: ${JSON.stringify(profile.config)}`);
|
|
1947
|
-
console.log();
|
|
1948
|
-
console.log(pc6.dim("Step 4/6 - Confirm apply"));
|
|
1949
|
-
const confirm = await rl.question(pc6.dim("Apply this configuration? [y/N] "));
|
|
1950
|
-
if (!["y", "yes"].includes(confirm.trim().toLowerCase())) {
|
|
1951
|
-
throw new Error("Cancelled by user.");
|
|
1952
|
-
}
|
|
1953
|
-
return {
|
|
1954
|
-
...opts,
|
|
1955
|
-
provider: selectedProviders,
|
|
1956
|
-
channel: selectedChannel,
|
|
1957
|
-
command,
|
|
1958
|
-
arg: args,
|
|
1959
|
-
env,
|
|
1960
|
-
cleoDir,
|
|
1961
|
-
yes: true
|
|
1962
|
-
};
|
|
1963
|
-
} finally {
|
|
1964
|
-
rl.close();
|
|
1965
|
-
}
|
|
1966
|
-
}
|
|
1967
|
-
async function executeCleoInstall(mode, opts, operation) {
|
|
1968
|
-
const mvi = "standard";
|
|
1969
|
-
let format;
|
|
1970
|
-
try {
|
|
1971
|
-
format = resolveFormat({
|
|
1972
|
-
jsonFlag: opts.json ?? false,
|
|
1973
|
-
humanFlag: (opts.human ?? false) || isHuman(),
|
|
1974
|
-
projectDefault: "json"
|
|
1975
|
-
});
|
|
1976
|
-
} catch (error) {
|
|
1977
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1978
|
-
emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
|
|
1979
|
-
process.exit(1);
|
|
1980
|
-
}
|
|
1981
|
-
const interactive = (opts.interactive ?? false) && format === "human";
|
|
1982
|
-
const resolvedOpts = interactive ? await runInteractiveInstall(opts) : opts;
|
|
1983
|
-
const channel = normalizeCleoChannel(resolvedOpts.channel);
|
|
1984
|
-
const providers = collectTargetProviders(resolvedOpts.provider, resolvedOpts.all);
|
|
1985
|
-
if (providers.length === 0) {
|
|
1986
|
-
const message = "No target providers found.";
|
|
1987
|
-
if (format === "json") {
|
|
1988
|
-
emitJsonError(
|
|
1989
|
-
operation,
|
|
1990
|
-
mvi,
|
|
1991
|
-
ErrorCodes.PROVIDER_NOT_FOUND,
|
|
1992
|
-
message,
|
|
1993
|
-
ErrorCategories.NOT_FOUND
|
|
1994
|
-
);
|
|
1995
|
-
} else {
|
|
1996
|
-
console.error(pc6.red(message));
|
|
1997
|
-
}
|
|
1998
|
-
process.exit(1);
|
|
1999
|
-
}
|
|
2000
|
-
const envMap = parseEnvAssignments(resolvedOpts.env);
|
|
2001
|
-
const profile = buildCleoProfile({
|
|
2002
|
-
channel,
|
|
2003
|
-
version: resolvedOpts.version,
|
|
2004
|
-
command: resolvedOpts.command,
|
|
2005
|
-
args: resolvedOpts.arg,
|
|
2006
|
-
env: envMap,
|
|
2007
|
-
cleoDir: resolvedOpts.cleoDir
|
|
2008
|
-
});
|
|
2009
|
-
const scope = resolvedOpts.global ? "global" : "project";
|
|
2010
|
-
if (resolvedOpts.dryRun) {
|
|
2011
|
-
if (format === "human") {
|
|
2012
|
-
console.log(pc6.bold(`Dry run: ${mode} CLEO (${channel})`));
|
|
2013
|
-
console.log(pc6.dim(`Server: ${profile.serverName} Scope: ${scope}`));
|
|
2014
|
-
console.log(pc6.dim(`Providers: ${providers.map((provider) => provider.id).join(", ")}`));
|
|
2015
|
-
console.log(
|
|
2016
|
-
pc6.dim(
|
|
2017
|
-
`Command: ${profile.config.command ?? "(none)"} ${(profile.config.args ?? []).join(" ")}`
|
|
2018
|
-
)
|
|
2019
|
-
);
|
|
2020
|
-
if (profile.config.env && Object.keys(profile.config.env).length > 0) {
|
|
2021
|
-
console.log(pc6.dim(`Env: ${JSON.stringify(profile.config.env)}`));
|
|
2022
|
-
}
|
|
2023
|
-
} else {
|
|
2024
|
-
outputSuccess(operation, mvi, {
|
|
2025
|
-
action: mode,
|
|
2026
|
-
channel,
|
|
2027
|
-
serverName: profile.serverName,
|
|
2028
|
-
providers: providers.map((provider) => provider.id),
|
|
2029
|
-
scope,
|
|
2030
|
-
command: profile.config.command,
|
|
2031
|
-
args: profile.config.args ?? [],
|
|
2032
|
-
env: profile.config.env ?? {},
|
|
2033
|
-
packageSpec: profile.packageSpec,
|
|
2034
|
-
dryRun: true
|
|
2035
|
-
});
|
|
2036
|
-
}
|
|
2037
|
-
return;
|
|
2038
|
-
}
|
|
2039
|
-
const conflictWarnings = await detectServerConflicts(providers, scope, profile.serverName);
|
|
2040
|
-
if (format === "human" && conflictWarnings.length > 0) {
|
|
2041
|
-
console.log(pc6.yellow("Warning: potential server name conflicts detected."));
|
|
2042
|
-
for (const warning of conflictWarnings) {
|
|
2043
|
-
console.log(pc6.yellow(` - ${warning.message}`));
|
|
2044
|
-
}
|
|
2045
|
-
console.log(
|
|
2046
|
-
pc6.dim(
|
|
2047
|
-
"Recovery: run with --dry-run, inspect provider config, then retry with explicit channel/profile."
|
|
2048
|
-
)
|
|
2049
|
-
);
|
|
2050
|
-
console.log();
|
|
2051
|
-
}
|
|
2052
|
-
const results = await installMcpServerToAll(providers, profile.serverName, profile.config, scope);
|
|
2053
|
-
const succeeded = results.filter((result) => result.success);
|
|
2054
|
-
const validations = [];
|
|
2055
|
-
for (const result of succeeded) {
|
|
2056
|
-
const validation = await validateProfile(result.provider, scope, profile.serverName);
|
|
2057
|
-
validations.push({
|
|
2058
|
-
providerId: result.provider.id,
|
|
2059
|
-
valid: validation.valid,
|
|
2060
|
-
reason: validation.reason
|
|
2061
|
-
});
|
|
2062
|
-
}
|
|
2063
|
-
if (succeeded.length > 0) {
|
|
2064
|
-
await recordMcpInstall(
|
|
2065
|
-
profile.serverName,
|
|
2066
|
-
profile.packageSpec ?? resolvedOpts.command ?? "cleo-dev",
|
|
2067
|
-
channel === "dev" ? "command" : "package",
|
|
2068
|
-
succeeded.map((result) => result.provider.id),
|
|
2069
|
-
resolvedOpts.global ?? false,
|
|
2070
|
-
resolvedOpts.version ?? extractVersionTag(profile.packageSpec)
|
|
2071
|
-
);
|
|
2072
|
-
}
|
|
2073
|
-
if (format === "human") {
|
|
2074
|
-
formatInstallResultHuman(mode, channel, profile.serverName, scope, results, validations);
|
|
2075
|
-
}
|
|
2076
|
-
const validationFailures = validations.filter((entry) => !entry.valid);
|
|
2077
|
-
if (interactive && validationFailures.length > 0 && format === "human") {
|
|
2078
|
-
console.log(pc6.dim("Step 5/6 - Validation"));
|
|
2079
|
-
console.log(pc6.yellow(`Validation found ${validationFailures.length} issue(s).`));
|
|
2080
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
2081
|
-
try {
|
|
2082
|
-
console.log(pc6.dim("Step 6/6 - Rollback"));
|
|
2083
|
-
const answer = await rl.question(pc6.dim("Rollback failed validations? [y/N] "));
|
|
2084
|
-
if (["y", "yes"].includes(answer.trim().toLowerCase())) {
|
|
2085
|
-
for (const failure of validationFailures) {
|
|
2086
|
-
const provider = providers.find((candidate) => candidate.id === failure.providerId);
|
|
2087
|
-
if (!provider) continue;
|
|
2088
|
-
await removeMcpServer(provider, profile.serverName, scope);
|
|
2089
|
-
}
|
|
2090
|
-
console.log(pc6.yellow("Rollback completed for failed provider validations."));
|
|
2091
|
-
}
|
|
2092
|
-
} finally {
|
|
2093
|
-
rl.close();
|
|
2094
|
-
}
|
|
2095
|
-
}
|
|
2096
|
-
if (format === "json") {
|
|
2097
|
-
outputSuccess(operation, mvi, {
|
|
2098
|
-
action: mode,
|
|
2099
|
-
channel,
|
|
2100
|
-
serverName: profile.serverName,
|
|
2101
|
-
scope,
|
|
2102
|
-
command: profile.config.command,
|
|
2103
|
-
args: profile.config.args ?? [],
|
|
2104
|
-
env: profile.config.env ?? {},
|
|
2105
|
-
packageSpec: profile.packageSpec,
|
|
2106
|
-
providers: results.map((result) => ({
|
|
2107
|
-
id: result.provider.id,
|
|
2108
|
-
success: result.success,
|
|
2109
|
-
configPath: result.configPath,
|
|
2110
|
-
error: result.error,
|
|
2111
|
-
validation: validations.find((entry) => entry.providerId === result.provider.id) ?? null
|
|
2112
|
-
})),
|
|
2113
|
-
conflicts: conflictWarnings,
|
|
2114
|
-
validationStatus: validationFailures.length === 0 ? "ok" : "warning"
|
|
2115
|
-
});
|
|
2116
|
-
}
|
|
2117
|
-
}
|
|
2118
|
-
async function executeCleoUninstall(opts, operation) {
|
|
2119
|
-
const mvi = "standard";
|
|
2120
|
-
let format;
|
|
2121
|
-
try {
|
|
2122
|
-
format = resolveFormat({
|
|
2123
|
-
jsonFlag: opts.json ?? false,
|
|
2124
|
-
humanFlag: (opts.human ?? false) || isHuman(),
|
|
2125
|
-
projectDefault: "json"
|
|
2126
|
-
});
|
|
2127
|
-
} catch (error) {
|
|
2128
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2129
|
-
emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
|
|
2130
|
-
process.exit(1);
|
|
2131
|
-
}
|
|
2132
|
-
const channel = normalizeCleoChannel(opts.channel);
|
|
2133
|
-
const serverName = resolveCleoServerName(channel);
|
|
2134
|
-
const providers = collectTargetProviders(opts.provider, opts.all);
|
|
2135
|
-
if (providers.length === 0) {
|
|
2136
|
-
const message = "No target providers found.";
|
|
2137
|
-
if (format === "json") {
|
|
2138
|
-
emitJsonError(
|
|
2139
|
-
operation,
|
|
2140
|
-
mvi,
|
|
2141
|
-
ErrorCodes.PROVIDER_NOT_FOUND,
|
|
2142
|
-
message,
|
|
2143
|
-
ErrorCategories.NOT_FOUND
|
|
2144
|
-
);
|
|
2145
|
-
} else {
|
|
2146
|
-
console.error(pc6.red(message));
|
|
2147
|
-
}
|
|
2148
|
-
process.exit(1);
|
|
2149
|
-
}
|
|
2150
|
-
const scope = opts.global ? "global" : "project";
|
|
2151
|
-
if (opts.dryRun) {
|
|
2152
|
-
if (format === "human") {
|
|
2153
|
-
console.log(pc6.bold("Dry run: uninstall CLEO profile"));
|
|
2154
|
-
console.log(pc6.dim(`Server: ${serverName} Channel: ${channel} Scope: ${scope}`));
|
|
2155
|
-
console.log(pc6.dim(`Providers: ${providers.map((provider) => provider.id).join(", ")}`));
|
|
2156
|
-
} else {
|
|
2157
|
-
outputSuccess(operation, mvi, {
|
|
2158
|
-
action: "uninstall",
|
|
2159
|
-
channel,
|
|
2160
|
-
serverName,
|
|
2161
|
-
providers: providers.map((provider) => provider.id),
|
|
2162
|
-
scope,
|
|
2163
|
-
dryRun: true
|
|
2164
|
-
});
|
|
2165
|
-
}
|
|
2166
|
-
return;
|
|
2167
|
-
}
|
|
2168
|
-
const removed = [];
|
|
2169
|
-
for (const provider of providers) {
|
|
2170
|
-
const success = await removeMcpServer(provider, serverName, scope);
|
|
2171
|
-
if (success) removed.push(provider.id);
|
|
2172
|
-
}
|
|
2173
|
-
if (removed.length > 0) {
|
|
2174
|
-
await removeMcpFromLock(serverName);
|
|
2175
|
-
}
|
|
2176
|
-
if (format === "human") {
|
|
2177
|
-
const prefix = removed.length > 0 ? pc6.green("Removed") : pc6.yellow("No matching profile found for");
|
|
2178
|
-
console.log(
|
|
2179
|
-
`${prefix} ${pc6.bold(serverName)} (${channel}) on ${removed.length}/${providers.length} providers.`
|
|
2180
|
-
);
|
|
2181
|
-
}
|
|
2182
|
-
if (format === "json") {
|
|
2183
|
-
outputSuccess(operation, mvi, {
|
|
2184
|
-
action: "uninstall",
|
|
2185
|
-
channel,
|
|
2186
|
-
serverName,
|
|
2187
|
-
scope,
|
|
2188
|
-
removed,
|
|
2189
|
-
providerCount: providers.length,
|
|
2190
|
-
dryRun: false
|
|
2191
|
-
});
|
|
2192
|
-
}
|
|
2193
|
-
}
|
|
2194
|
-
function checkCleoEntryHealth(command, lockTracked) {
|
|
2195
|
-
if (!command) {
|
|
2196
|
-
return {
|
|
2197
|
-
commandReachable: true,
|
|
2198
|
-
commandDetail: "(no command)",
|
|
2199
|
-
configPresent: true,
|
|
2200
|
-
lockTracked,
|
|
2201
|
-
status: lockTracked ? "healthy" : "degraded"
|
|
2202
|
-
};
|
|
2203
|
-
}
|
|
2204
|
-
const reachability = checkCommandReachability(command);
|
|
2205
|
-
if (!reachability.reachable) {
|
|
2206
|
-
return {
|
|
2207
|
-
commandReachable: false,
|
|
2208
|
-
commandDetail: reachability.detail,
|
|
2209
|
-
configPresent: true,
|
|
2210
|
-
lockTracked,
|
|
2211
|
-
status: "broken"
|
|
2212
|
-
};
|
|
2213
|
-
}
|
|
2214
|
-
return {
|
|
2215
|
-
commandReachable: true,
|
|
2216
|
-
commandDetail: reachability.detail,
|
|
2217
|
-
configPresent: true,
|
|
2218
|
-
lockTracked,
|
|
2219
|
-
status: lockTracked ? "healthy" : "degraded"
|
|
2220
|
-
};
|
|
2221
|
-
}
|
|
2222
|
-
async function executeCleoShow(opts, operation) {
|
|
2223
|
-
const mvi = "standard";
|
|
2224
|
-
let format;
|
|
2225
|
-
try {
|
|
2226
|
-
format = resolveFormat({
|
|
2227
|
-
jsonFlag: opts.json ?? false,
|
|
2228
|
-
humanFlag: (opts.human ?? false) || isHuman(),
|
|
2229
|
-
projectDefault: "json"
|
|
2230
|
-
});
|
|
2231
|
-
} catch (error) {
|
|
2232
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2233
|
-
emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
|
|
2234
|
-
process.exit(1);
|
|
2235
|
-
}
|
|
2236
|
-
const providers = collectTargetProviders(opts.provider, opts.all);
|
|
2237
|
-
if (providers.length === 0) {
|
|
2238
|
-
const message = "No target providers found.";
|
|
2239
|
-
if (format === "json") {
|
|
2240
|
-
emitJsonError(
|
|
2241
|
-
operation,
|
|
2242
|
-
mvi,
|
|
2243
|
-
ErrorCodes.PROVIDER_NOT_FOUND,
|
|
2244
|
-
message,
|
|
2245
|
-
ErrorCategories.NOT_FOUND
|
|
2246
|
-
);
|
|
2247
|
-
} else {
|
|
2248
|
-
console.error(pc6.red(message));
|
|
2249
|
-
}
|
|
2250
|
-
process.exit(1);
|
|
2251
|
-
}
|
|
2252
|
-
const channelFilter = opts.channel ? normalizeCleoChannel(opts.channel) : null;
|
|
2253
|
-
const scopes = [];
|
|
2254
|
-
if (opts.global && !opts.project) {
|
|
2255
|
-
scopes.push("global");
|
|
2256
|
-
} else if (opts.project && !opts.global) {
|
|
2257
|
-
scopes.push("project");
|
|
2258
|
-
} else {
|
|
2259
|
-
scopes.push("project", "global");
|
|
2260
|
-
}
|
|
2261
|
-
const lockEntries = await getTrackedMcpServers();
|
|
2262
|
-
const entries = [];
|
|
2263
|
-
const warnings = [];
|
|
2264
|
-
for (const scope of scopes) {
|
|
2265
|
-
for (const provider of providers) {
|
|
2266
|
-
const providerEntries = await listMcpServers(provider, scope);
|
|
2267
|
-
for (const entry of providerEntries) {
|
|
2268
|
-
const channel = resolveChannelFromServerName(entry.name);
|
|
2269
|
-
if (!channel) continue;
|
|
2270
|
-
if (channelFilter && channel !== channelFilter) continue;
|
|
2271
|
-
const command = typeof entry.config.command === "string" ? entry.config.command : void 0;
|
|
2272
|
-
const args = Array.isArray(entry.config.args) ? entry.config.args.filter((value) => typeof value === "string") : [];
|
|
2273
|
-
const env = typeof entry.config.env === "object" && entry.config.env !== null ? entry.config.env : {};
|
|
2274
|
-
const lockEntry = lockEntries[entry.name];
|
|
2275
|
-
const lockTracked = lockEntry !== void 0;
|
|
2276
|
-
const health = checkCleoEntryHealth(command, lockTracked);
|
|
2277
|
-
entries.push({
|
|
2278
|
-
provider: provider.id,
|
|
2279
|
-
providerName: provider.toolName,
|
|
2280
|
-
serverName: entry.name,
|
|
2281
|
-
channel,
|
|
2282
|
-
scope,
|
|
2283
|
-
command,
|
|
2284
|
-
args,
|
|
2285
|
-
env,
|
|
2286
|
-
version: lockEntry?.version ?? null,
|
|
2287
|
-
source: lockEntry?.source ?? null,
|
|
2288
|
-
sourceType: lockEntry?.sourceType ?? null,
|
|
2289
|
-
installedAt: lockEntry?.installedAt ?? null,
|
|
2290
|
-
updatedAt: lockEntry?.updatedAt ?? null,
|
|
2291
|
-
health
|
|
2292
|
-
});
|
|
2293
|
-
if (health.status === "broken") {
|
|
2294
|
-
warnings.push({
|
|
2295
|
-
code: "W_COMMAND_UNREACHABLE",
|
|
2296
|
-
message: `${entry.name} command not reachable on ${provider.toolName} (${health.commandDetail})`
|
|
2297
|
-
});
|
|
2298
|
-
} else if (health.status === "degraded") {
|
|
2299
|
-
warnings.push({
|
|
2300
|
-
code: "W_NOT_TRACKED",
|
|
2301
|
-
message: `${entry.name} on ${provider.toolName} is not tracked in lock file`
|
|
2302
|
-
});
|
|
2303
|
-
}
|
|
2304
|
-
}
|
|
2305
|
-
}
|
|
2306
|
-
}
|
|
2307
|
-
const issueCount = entries.filter((e) => e.health.status !== "healthy").length;
|
|
2308
|
-
if (format === "human") {
|
|
2309
|
-
if (entries.length === 0) {
|
|
2310
|
-
console.log(pc6.dim("No CLEO channel profiles found."));
|
|
2311
|
-
} else {
|
|
2312
|
-
console.log(pc6.bold("CLEO Channel Profiles"));
|
|
2313
|
-
console.log();
|
|
2314
|
-
const header = [
|
|
2315
|
-
"Channel".padEnd(10),
|
|
2316
|
-
"Version".padEnd(10),
|
|
2317
|
-
"Provider".padEnd(22),
|
|
2318
|
-
"Scope".padEnd(9),
|
|
2319
|
-
"Command".padEnd(33),
|
|
2320
|
-
"Status".padEnd(10),
|
|
2321
|
-
"Installed".padEnd(12)
|
|
2322
|
-
].join("");
|
|
2323
|
-
console.log(` ${pc6.dim(header)}`);
|
|
2324
|
-
console.log(` ${pc6.dim("-".repeat(106))}`);
|
|
2325
|
-
for (const entry of entries) {
|
|
2326
|
-
const commandStr = entry.command ? `${entry.command} ${entry.args.join(" ")}`.slice(0, 31).padEnd(33) : pc6.dim("-").padEnd(33);
|
|
2327
|
-
const versionStr = (entry.version ?? "-").padEnd(10);
|
|
2328
|
-
const installedStr = entry.installedAt ? entry.installedAt.slice(0, 10).padEnd(12) : "-".padEnd(12);
|
|
2329
|
-
let statusStr;
|
|
2330
|
-
if (entry.health.status === "healthy") {
|
|
2331
|
-
statusStr = pc6.green("healthy".padEnd(10));
|
|
2332
|
-
} else if (entry.health.status === "degraded") {
|
|
2333
|
-
statusStr = pc6.yellow("degraded".padEnd(10));
|
|
2334
|
-
} else {
|
|
2335
|
-
statusStr = pc6.red("broken".padEnd(10));
|
|
2336
|
-
}
|
|
2337
|
-
console.log(
|
|
2338
|
-
` ${entry.channel.padEnd(10)}${versionStr}${entry.providerName.padEnd(22)}${entry.scope.padEnd(9)}${commandStr}${statusStr}${installedStr}`
|
|
2339
|
-
);
|
|
2340
|
-
}
|
|
2341
|
-
console.log();
|
|
2342
|
-
const summary = ` ${entries.length} profile${entries.length !== 1 ? "s" : ""}`;
|
|
2343
|
-
if (issueCount > 0) {
|
|
2344
|
-
console.log(
|
|
2345
|
-
`${summary} | ${pc6.yellow(`${issueCount} issue${issueCount !== 1 ? "s" : ""}`)}`
|
|
2346
|
-
);
|
|
2347
|
-
console.log();
|
|
2348
|
-
console.log(" Issues:");
|
|
2349
|
-
for (const w of warnings) {
|
|
2350
|
-
console.log(` ${pc6.yellow("!")} ${w.message}`);
|
|
2351
|
-
}
|
|
2352
|
-
} else {
|
|
2353
|
-
console.log(summary);
|
|
2354
|
-
}
|
|
2355
|
-
}
|
|
2356
|
-
}
|
|
2357
|
-
if (format === "json") {
|
|
2358
|
-
outputSuccess(
|
|
2359
|
-
operation,
|
|
2360
|
-
mvi,
|
|
2361
|
-
{
|
|
2362
|
-
providers: providers.map((provider) => provider.id),
|
|
2363
|
-
scopes,
|
|
2364
|
-
channel: channelFilter,
|
|
2365
|
-
profiles: entries,
|
|
2366
|
-
count: entries.length
|
|
2367
|
-
},
|
|
2368
|
-
void 0,
|
|
2369
|
-
void 0,
|
|
2370
|
-
warnings.length > 0 ? warnings : void 0
|
|
2371
|
-
);
|
|
2372
|
-
}
|
|
2373
|
-
}
|
|
2374
|
-
async function executeCleoRepair(opts, operation) {
|
|
2375
|
-
const mvi = "standard";
|
|
2376
|
-
let format;
|
|
2377
|
-
try {
|
|
2378
|
-
format = resolveFormat({
|
|
2379
|
-
jsonFlag: opts.json ?? false,
|
|
2380
|
-
humanFlag: (opts.human ?? false) || isHuman(),
|
|
2381
|
-
projectDefault: "json"
|
|
2382
|
-
});
|
|
2383
|
-
} catch (error) {
|
|
2384
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2385
|
-
emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
|
|
2386
|
-
process.exit(1);
|
|
2387
|
-
}
|
|
2388
|
-
const providerIds = opts.provider.length > 0 ? opts.provider : void 0;
|
|
2389
|
-
const result = await reconcileCleoLock({
|
|
2390
|
-
providerIds,
|
|
2391
|
-
all: opts.all,
|
|
2392
|
-
global: opts.global,
|
|
2393
|
-
project: opts.project,
|
|
2394
|
-
prune: opts.prune,
|
|
2395
|
-
dryRun: opts.dryRun
|
|
2396
|
-
});
|
|
2397
|
-
if (format === "human") {
|
|
2398
|
-
const prefix = opts.dryRun ? "CLEO Lock Repair (dry run)" : "CLEO Lock Repair";
|
|
2399
|
-
console.log(pc6.bold(prefix));
|
|
2400
|
-
console.log();
|
|
2401
|
-
if (result.backfilled.length > 0) {
|
|
2402
|
-
for (const entry of result.backfilled) {
|
|
2403
|
-
const agents = entry.agents.join(", ");
|
|
2404
|
-
const versionStr = entry.version ? `(${entry.version})` : "";
|
|
2405
|
-
console.log(
|
|
2406
|
-
` ${pc6.green("+")} ${entry.serverName.padEnd(12)}${entry.channel.padEnd(10)}${agents.padEnd(22)}${entry.scope.padEnd(10)}${entry.source} ${pc6.dim(versionStr)}`
|
|
2407
|
-
);
|
|
2408
|
-
}
|
|
2409
|
-
}
|
|
2410
|
-
if (result.pruned.length > 0) {
|
|
2411
|
-
for (const name of result.pruned) {
|
|
2412
|
-
console.log(` ${pc6.red("-")} ${name} (removed from lock)`);
|
|
2413
|
-
}
|
|
2414
|
-
}
|
|
2415
|
-
if (result.backfilled.length === 0 && result.pruned.length === 0) {
|
|
2416
|
-
console.log(pc6.dim(" No changes needed. All CLEO entries are tracked."));
|
|
2417
|
-
}
|
|
2418
|
-
console.log();
|
|
2419
|
-
console.log(
|
|
2420
|
-
` ${result.backfilled.length} backfilled | ${result.pruned.length} pruned | ${result.alreadyTracked} already tracked`
|
|
2421
|
-
);
|
|
2422
|
-
if (result.errors.length > 0) {
|
|
2423
|
-
console.log();
|
|
2424
|
-
for (const err of result.errors) {
|
|
2425
|
-
console.log(` ${pc6.red("!")} ${err.message}`);
|
|
2426
|
-
}
|
|
2427
|
-
}
|
|
2428
|
-
}
|
|
2429
|
-
if (format === "json") {
|
|
2430
|
-
outputSuccess(operation, mvi, {
|
|
2431
|
-
backfilled: result.backfilled,
|
|
2432
|
-
pruned: result.pruned,
|
|
2433
|
-
alreadyTracked: result.alreadyTracked,
|
|
2434
|
-
dryRun: opts.dryRun ?? false,
|
|
2435
|
-
errors: result.errors
|
|
2436
|
-
});
|
|
2437
|
-
}
|
|
2438
|
-
}
|
|
2439
|
-
function buildInstallOptions(command) {
|
|
2440
|
-
return command.requiredOption("--channel <channel>", "CLEO channel: stable|beta|dev").option("--provider <id>", "Target provider (repeatable)", collect, []).option("--all", "Apply to all detected providers").option("-g, --global", "Use global scope").option("--version <tag>", "Tag/version for stable or beta").option("--command <command>", "Dev channel command").option("--arg <arg>", "Dev command arg (repeatable)", collect, []).option("--env <kv>", "Environment assignment KEY=value (repeatable)", collect, []).option("--cleo-dir <path>", "CLEO_DIR override for dev channel").option("--dry-run", "Preview without writing").option("-y, --yes", "Skip confirmation").option("--interactive", "Guided interactive setup").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format");
|
|
2441
|
-
}
|
|
2442
|
-
function registerMcpCleoCommands(parent) {
|
|
2443
|
-
const cleo = parent.command("cleo").description("Manage CLEO MCP channel profiles");
|
|
2444
|
-
buildInstallOptions(
|
|
2445
|
-
cleo.command("install").description("Install CLEO MCP profile by channel")
|
|
2446
|
-
).action(async (opts) => {
|
|
2447
|
-
await executeCleoInstall("install", opts, "mcp.cleo.install");
|
|
2448
|
-
});
|
|
2449
|
-
buildInstallOptions(
|
|
2450
|
-
cleo.command("update").description("Update CLEO MCP profile by channel")
|
|
2451
|
-
).action(async (opts) => {
|
|
2452
|
-
await executeCleoInstall("update", opts, "mcp.cleo.update");
|
|
2453
|
-
});
|
|
2454
|
-
cleo.command("uninstall").description("Uninstall CLEO MCP profile for a channel").requiredOption("--channel <channel>", "CLEO channel: stable|beta|dev").option("--provider <id>", "Target provider (repeatable)", collect, []).option("--all", "Apply to all detected providers").option("-g, --global", "Use global scope").option("--dry-run", "Preview without writing").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
2455
|
-
await executeCleoUninstall(opts, "mcp.cleo.uninstall");
|
|
2456
|
-
});
|
|
2457
|
-
cleo.command("show").description("Show installed CLEO MCP channel profiles").option("--provider <id>", "Target provider (repeatable)", collect, []).option("--all", "Inspect all detected providers").option("-g, --global", "Global scope only").option("-p, --project", "Project scope only").option("--channel <channel>", "Filter channel: stable|beta|dev").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
2458
|
-
await executeCleoShow(opts, "mcp.cleo.show");
|
|
2459
|
-
});
|
|
2460
|
-
cleo.command("repair").description("Repair lock file by backfilling untracked CLEO entries").option("--provider <id>", "Target provider (repeatable)", collect, []).option("--all", "Scan all detected providers").option("-g, --global", "Global scope only").option("-p, --project", "Project scope only").option("--prune", "Remove orphaned lock entries not in any config").option("--dry-run", "Preview without writing").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
2461
|
-
await executeCleoRepair(opts, "mcp.cleo.repair");
|
|
2462
|
-
});
|
|
2463
|
-
}
|
|
2464
|
-
function registerMcpCleoCompatibilityCommands(parent) {
|
|
2465
|
-
parent.command("update").description("Update channel-managed MCP profile").argument("<name>", "Managed MCP profile name (cleo)").requiredOption("--channel <channel>", "CLEO channel: stable|beta|dev").option("--provider <id>", "Target provider (repeatable)", collect, []).option("--all", "Apply to all detected providers").option("-g, --global", "Use global scope").option("--version <tag>", "Tag/version for stable or beta").option("--command <command>", "Dev channel command").option("--arg <arg>", "Dev command arg (repeatable)", collect, []).option("--env <kv>", "Environment assignment KEY=value (repeatable)", collect, []).option("--cleo-dir <path>", "CLEO_DIR override for dev channel").option("--dry-run", "Preview without writing").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (name, opts) => {
|
|
2466
|
-
if (name !== "cleo") {
|
|
2467
|
-
emitJsonError(
|
|
2468
|
-
"mcp.update",
|
|
2469
|
-
"standard",
|
|
2470
|
-
ErrorCodes.INVALID_INPUT,
|
|
2471
|
-
"Only managed profile 'cleo' is supported by mcp update.",
|
|
2472
|
-
ErrorCategories.VALIDATION,
|
|
2473
|
-
{ name }
|
|
2474
|
-
);
|
|
2475
|
-
process.exit(1);
|
|
2476
|
-
}
|
|
2477
|
-
await executeCleoInstall("update", opts, "mcp.update");
|
|
2478
|
-
});
|
|
2479
|
-
parent.command("uninstall").description("Uninstall channel-managed MCP profile").argument("<name>", "Managed MCP profile name (cleo)").requiredOption("--channel <channel>", "CLEO channel: stable|beta|dev").option("--provider <id>", "Target provider (repeatable)", collect, []).option("--all", "Apply to all detected providers").option("-g, --global", "Use global scope").option("--dry-run", "Preview without writing").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (name, opts) => {
|
|
2480
|
-
if (name !== "cleo") {
|
|
2481
|
-
emitJsonError(
|
|
2482
|
-
"mcp.uninstall",
|
|
2483
|
-
"standard",
|
|
2484
|
-
ErrorCodes.INVALID_INPUT,
|
|
2485
|
-
"Only managed profile 'cleo' is supported by mcp uninstall.",
|
|
2486
|
-
ErrorCategories.VALIDATION,
|
|
2487
|
-
{ name }
|
|
2488
|
-
);
|
|
2489
|
-
process.exit(1);
|
|
2490
|
-
}
|
|
2491
|
-
await executeCleoUninstall(opts, "mcp.uninstall");
|
|
2492
|
-
});
|
|
2493
|
-
parent.command("show").description("Show channel-managed MCP profile").argument("<name>", "Managed MCP profile name (cleo)").option("--provider <id>", "Target provider (repeatable)", collect, []).option("--all", "Inspect all detected providers").option("-g, --global", "Global scope only").option("-p, --project", "Project scope only").option("--channel <channel>", "Filter channel: stable|beta|dev").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (name, opts) => {
|
|
2494
|
-
if (name !== "cleo") {
|
|
2495
|
-
emitJsonError(
|
|
2496
|
-
"mcp.show",
|
|
2497
|
-
"standard",
|
|
2498
|
-
ErrorCodes.INVALID_INPUT,
|
|
2499
|
-
"Only managed profile 'cleo' is supported by mcp show.",
|
|
2500
|
-
ErrorCategories.VALIDATION,
|
|
2501
|
-
{ name }
|
|
2502
|
-
);
|
|
2503
|
-
process.exit(1);
|
|
2504
|
-
}
|
|
2505
|
-
await executeCleoShow(opts, "mcp.show");
|
|
2506
|
-
});
|
|
2507
|
-
}
|
|
2508
|
-
function mapCompatibilityInstallOptions(opts) {
|
|
2509
|
-
return {
|
|
2510
|
-
channel: opts.channel,
|
|
2511
|
-
provider: [...opts.provider ?? [], ...opts.agent ?? []],
|
|
2512
|
-
all: opts.all,
|
|
2513
|
-
global: opts.global,
|
|
2514
|
-
version: opts.version,
|
|
2515
|
-
command: opts.command,
|
|
2516
|
-
arg: opts.arg ?? [],
|
|
2517
|
-
env: opts.env ?? [],
|
|
2518
|
-
cleoDir: opts.cleoDir,
|
|
2519
|
-
dryRun: opts.dryRun,
|
|
2520
|
-
yes: opts.yes,
|
|
2521
|
-
interactive: opts.interactive,
|
|
2522
|
-
json: opts.json,
|
|
2523
|
-
human: opts.human
|
|
2524
|
-
};
|
|
2525
|
-
}
|
|
2526
|
-
function shouldUseCleoCompatibilityInstall(source, channel) {
|
|
2527
|
-
if (source.trim().toLowerCase() !== "cleo") return false;
|
|
2528
|
-
return typeof channel === "string" && channel.trim() !== "";
|
|
2529
|
-
}
|
|
2530
|
-
function registerCleoCommands(program2) {
|
|
2531
|
-
const cleo = program2.command("cleo").description("Manage CLEO channel profiles");
|
|
2532
|
-
buildInstallOptions(
|
|
2533
|
-
cleo.command("install").description("Install CLEO profile by channel")
|
|
2534
|
-
).action(async (opts) => {
|
|
2535
|
-
await executeCleoInstall("install", opts, "cleo.install");
|
|
2536
|
-
});
|
|
2537
|
-
buildInstallOptions(cleo.command("update").description("Update CLEO profile by channel")).action(
|
|
2538
|
-
async (opts) => {
|
|
2539
|
-
await executeCleoInstall("update", opts, "cleo.update");
|
|
2540
|
-
}
|
|
2541
|
-
);
|
|
2542
|
-
cleo.command("uninstall").description("Uninstall CLEO profile for a channel").requiredOption("--channel <channel>", "CLEO channel: stable|beta|dev").option("--provider <id>", "Target provider (repeatable)", collect, []).option("--all", "Apply to all detected providers").option("-g, --global", "Use global scope").option("--dry-run", "Preview without writing").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
2543
|
-
await executeCleoUninstall(opts, "cleo.uninstall");
|
|
2544
|
-
});
|
|
2545
|
-
cleo.command("show").description("Show installed CLEO channel profiles").option("--provider <id>", "Target provider (repeatable)", collect, []).option("--all", "Inspect all detected providers").option("-g, --global", "Global scope only").option("-p, --project", "Project scope only").option("--channel <channel>", "Filter channel: stable|beta|dev").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
2546
|
-
await executeCleoShow(opts, "cleo.show");
|
|
2547
|
-
});
|
|
2548
|
-
cleo.command("repair").description("Repair lock file by backfilling untracked CLEO entries").option("--provider <id>", "Target provider (repeatable)", collect, []).option("--all", "Scan all detected providers").option("-g, --global", "Global scope only").option("-p, --project", "Project scope only").option("--prune", "Remove orphaned lock entries not in any config").option("--dry-run", "Preview without writing").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
2549
|
-
await executeCleoRepair(opts, "cleo.repair");
|
|
2550
|
-
});
|
|
2551
|
-
}
|
|
2552
|
-
|
|
2553
|
-
// src/commands/mcp/detect.ts
|
|
2554
|
-
import { existsSync as existsSync3 } from "fs";
|
|
2555
|
-
import pc7 from "picocolors";
|
|
2556
|
-
function registerMcpDetect(parent) {
|
|
2557
|
-
parent.command("detect").description("Auto-detect installed MCP tools and their configurations").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
2558
|
-
const operation = "mcp.detect";
|
|
2559
|
-
const mvi = "standard";
|
|
2560
|
-
let format;
|
|
2561
|
-
try {
|
|
2562
|
-
format = resolveFormat({
|
|
2563
|
-
jsonFlag: opts.json ?? false,
|
|
2564
|
-
humanFlag: (opts.human ?? false) || isHuman(),
|
|
2565
|
-
projectDefault: "json"
|
|
2566
|
-
});
|
|
2567
|
-
} catch (error) {
|
|
2568
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2569
|
-
emitJsonError(
|
|
2570
|
-
operation,
|
|
2571
|
-
mvi,
|
|
2572
|
-
ErrorCodes.FORMAT_CONFLICT,
|
|
2573
|
-
message,
|
|
2574
|
-
ErrorCategories.VALIDATION
|
|
2575
|
-
);
|
|
2576
|
-
process.exit(1);
|
|
2577
|
-
}
|
|
2578
|
-
const providers = getInstalledProviders();
|
|
2579
|
-
const providersResult = [];
|
|
2580
|
-
let totalConfigs = 0;
|
|
2581
|
-
for (const provider of providers) {
|
|
2582
|
-
const globalPath = resolveConfigPath(provider, "global");
|
|
2583
|
-
const projectPath = resolveConfigPath(provider, "project");
|
|
2584
|
-
const globalEntries = await listMcpServers(provider, "global");
|
|
2585
|
-
const projectEntries = await listMcpServers(provider, "project");
|
|
2586
|
-
const configsFound = (globalPath && existsSync3(globalPath) ? 1 : 0) + (projectPath && existsSync3(projectPath) ? 1 : 0);
|
|
2587
|
-
totalConfigs += configsFound;
|
|
2588
|
-
const allServers = [
|
|
2589
|
-
...globalEntries.map((e) => e.name),
|
|
2590
|
-
...projectEntries.map((e) => e.name)
|
|
2591
|
-
];
|
|
2592
|
-
providersResult.push({
|
|
2593
|
-
id: provider.id,
|
|
2594
|
-
configsFound,
|
|
2595
|
-
servers: allServers
|
|
2596
|
-
});
|
|
2597
|
-
}
|
|
2598
|
-
if (format === "json") {
|
|
2599
|
-
outputSuccess(operation, mvi, {
|
|
2600
|
-
providers: providersResult,
|
|
2601
|
-
totalConfigs
|
|
2602
|
-
});
|
|
2603
|
-
return;
|
|
2604
|
-
}
|
|
2605
|
-
console.log(pc7.bold(`
|
|
2606
|
-
${providers.length} provider(s) with MCP support:
|
|
2607
|
-
`));
|
|
2608
|
-
for (const provider of providersResult) {
|
|
2609
|
-
const globalPath = resolveConfigPath(
|
|
2610
|
-
providers.find((p) => p.id === provider.id),
|
|
2611
|
-
"global"
|
|
2612
|
-
);
|
|
2613
|
-
const projectPath = resolveConfigPath(
|
|
2614
|
-
providers.find((p) => p.id === provider.id),
|
|
2615
|
-
"project"
|
|
2616
|
-
);
|
|
2617
|
-
const hasGlobal = globalPath && existsSync3(globalPath);
|
|
2618
|
-
const hasProject = projectPath && existsSync3(projectPath);
|
|
2619
|
-
const globalIcon = hasGlobal ? pc7.green("G") : pc7.dim("-");
|
|
2620
|
-
const projectIcon = hasProject ? pc7.green("P") : pc7.dim("-");
|
|
2621
|
-
const serverList = provider.servers.length > 0 ? pc7.dim(provider.servers.join(", ")) : pc7.dim("no servers");
|
|
2622
|
-
console.log(
|
|
2623
|
-
` [${globalIcon}${projectIcon}] ${pc7.bold(provider.id.padEnd(20))} ${serverList}`
|
|
2624
|
-
);
|
|
2625
|
-
}
|
|
2626
|
-
console.log(pc7.dim("\nG = global config, P = project config"));
|
|
2627
|
-
console.log();
|
|
2628
|
-
});
|
|
2629
|
-
}
|
|
2630
|
-
|
|
2631
|
-
// src/commands/mcp/install.ts
|
|
2632
|
-
import pc8 from "picocolors";
|
|
2633
|
-
function registerMcpInstall(parent) {
|
|
2634
|
-
parent.command("install").description("Install MCP server to agent configs").argument("<source>", "MCP server source (URL, npm package, or command)").option(
|
|
2635
|
-
"-a, --agent <name>",
|
|
2636
|
-
"Target specific agent(s)",
|
|
2637
|
-
(v, prev) => [...prev, v],
|
|
2638
|
-
[]
|
|
2639
|
-
).option(
|
|
2640
|
-
"--provider <id>",
|
|
2641
|
-
"Target provider ID (alias for --agent)",
|
|
2642
|
-
(v, prev) => [...prev, v],
|
|
2643
|
-
[]
|
|
2644
|
-
).option("-g, --global", "Install to global/user config").option("-n, --name <name>", "Override inferred server name").option("--channel <channel>", "Managed channel profile (stable|beta|dev)").option("--version <tag>", "Managed profile tag/version for stable or beta").option("--command <command>", "Managed dev profile command").option(
|
|
2645
|
-
"--arg <arg>",
|
|
2646
|
-
"Managed dev command arg (repeatable)",
|
|
2647
|
-
(v, prev) => [...prev, v],
|
|
2648
|
-
[]
|
|
2649
|
-
).option(
|
|
2650
|
-
"--env <kv>",
|
|
2651
|
-
"Managed env assignment KEY=value (repeatable)",
|
|
2652
|
-
(v, prev) => [...prev, v],
|
|
2653
|
-
[]
|
|
2654
|
-
).option("--cleo-dir <path>", "Managed dev CLEO_DIR override").option("-t, --transport <type>", "Transport type: http (default) or sse", "http").option(
|
|
2655
|
-
"--header <header>",
|
|
2656
|
-
"HTTP header (Key: Value)",
|
|
2657
|
-
(v, prev) => [...prev, v],
|
|
2658
|
-
[]
|
|
2659
|
-
).option("-y, --yes", "Skip confirmation").option("--all", "Install to all detected agents").option("--interactive", "Guided interactive setup for managed profiles").option("--dry-run", "Preview without writing").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(
|
|
2660
|
-
async (source, opts) => {
|
|
2661
|
-
const operation = "mcp.install";
|
|
2662
|
-
const mvi = "standard";
|
|
2663
|
-
let format;
|
|
2664
|
-
try {
|
|
2665
|
-
format = resolveFormat({
|
|
2666
|
-
jsonFlag: opts.json ?? false,
|
|
2667
|
-
humanFlag: (opts.human ?? false) || isHuman(),
|
|
2668
|
-
projectDefault: "json"
|
|
2669
|
-
});
|
|
2670
|
-
} catch (error) {
|
|
2671
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2672
|
-
emitJsonError(
|
|
2673
|
-
operation,
|
|
2674
|
-
mvi,
|
|
2675
|
-
ErrorCodes.FORMAT_CONFLICT,
|
|
2676
|
-
message,
|
|
2677
|
-
ErrorCategories.VALIDATION
|
|
2678
|
-
);
|
|
2679
|
-
process.exit(1);
|
|
2680
|
-
}
|
|
2681
|
-
if (shouldUseCleoCompatibilityInstall(source, opts.channel)) {
|
|
2682
|
-
const cleoOpts = mapCompatibilityInstallOptions(opts);
|
|
2683
|
-
await executeCleoInstall("install", cleoOpts, operation);
|
|
2684
|
-
return;
|
|
2685
|
-
}
|
|
2686
|
-
const parsed = parseSource(source);
|
|
2687
|
-
const serverName = opts.name ?? parsed.inferredName;
|
|
2688
|
-
const headers = {};
|
|
2689
|
-
for (const h of opts.header) {
|
|
2690
|
-
const idx = h.indexOf(":");
|
|
2691
|
-
if (idx > 0) {
|
|
2692
|
-
headers[h.slice(0, idx).trim()] = h.slice(idx + 1).trim();
|
|
2693
|
-
}
|
|
2694
|
-
}
|
|
2695
|
-
const config = buildServerConfig(parsed, opts.transport, headers);
|
|
2696
|
-
let providers;
|
|
2697
|
-
if (opts.all) {
|
|
2698
|
-
providers = getInstalledProviders();
|
|
2699
|
-
} else if (opts.agent.length > 0) {
|
|
2700
|
-
providers = opts.agent.map((a) => getProvider(a)).filter((p) => p !== void 0);
|
|
2701
|
-
} else if (opts.provider.length > 0) {
|
|
2702
|
-
providers = opts.provider.map((a) => getProvider(a)).filter((p) => p !== void 0);
|
|
2703
|
-
} else {
|
|
2704
|
-
providers = getInstalledProviders();
|
|
2705
|
-
}
|
|
2706
|
-
if (providers.length === 0) {
|
|
2707
|
-
const message = "No target providers found.";
|
|
2708
|
-
if (format === "json") {
|
|
2709
|
-
emitJsonError(
|
|
2710
|
-
operation,
|
|
2711
|
-
mvi,
|
|
2712
|
-
ErrorCodes.PROVIDER_NOT_FOUND,
|
|
2713
|
-
message,
|
|
2714
|
-
ErrorCategories.NOT_FOUND
|
|
2715
|
-
);
|
|
2716
|
-
} else {
|
|
2717
|
-
console.error(pc8.red(message));
|
|
2718
|
-
}
|
|
2719
|
-
process.exit(1);
|
|
2720
|
-
}
|
|
2721
|
-
const scope = opts.global ? "global" : "project";
|
|
2722
|
-
if (opts.dryRun) {
|
|
2723
|
-
if (format === "json") {
|
|
2724
|
-
outputSuccess(operation, mvi, {
|
|
2725
|
-
installed: [
|
|
2726
|
-
{
|
|
2727
|
-
name: serverName,
|
|
2728
|
-
providers: providers.map((p) => p.id),
|
|
2729
|
-
config
|
|
2730
|
-
}
|
|
2731
|
-
],
|
|
2732
|
-
dryRun: true
|
|
2733
|
-
});
|
|
2734
|
-
} else {
|
|
2735
|
-
console.log(pc8.bold("Dry run - would install:"));
|
|
2736
|
-
console.log(` Server: ${pc8.bold(serverName)}`);
|
|
2737
|
-
console.log(` Config: ${JSON.stringify(config, null, 2)}`);
|
|
2738
|
-
console.log(` Scope: ${scope}`);
|
|
2739
|
-
console.log(` Providers: ${providers.map((p) => p.id).join(", ")}`);
|
|
2740
|
-
}
|
|
2741
|
-
return;
|
|
2742
|
-
}
|
|
2743
|
-
if (format === "human") {
|
|
2744
|
-
console.log(pc8.dim(`Installing "${serverName}" to ${providers.length} provider(s)...
|
|
2745
|
-
`));
|
|
2746
|
-
}
|
|
2747
|
-
const results = await installMcpServerToAll(providers, serverName, config, scope);
|
|
2748
|
-
const succeeded = results.filter((r) => r.success);
|
|
2749
|
-
const _failed = results.filter((r) => !r.success);
|
|
2750
|
-
if (format === "human") {
|
|
2751
|
-
for (const r of results) {
|
|
2752
|
-
if (r.success) {
|
|
2753
|
-
console.log(
|
|
2754
|
-
` ${pc8.green("\u2713")} ${r.provider.toolName.padEnd(22)} ${pc8.dim(r.configPath)}`
|
|
2755
|
-
);
|
|
2756
|
-
} else {
|
|
2757
|
-
console.log(
|
|
2758
|
-
` ${pc8.red("\u2717")} ${r.provider.toolName.padEnd(22)} ${pc8.red(r.error ?? "failed")}`
|
|
2759
|
-
);
|
|
2760
|
-
}
|
|
1164
|
+
} catch {
|
|
2761
1165
|
}
|
|
2762
1166
|
}
|
|
2763
|
-
|
|
2764
|
-
await recordMcpInstall(
|
|
2765
|
-
serverName,
|
|
2766
|
-
source,
|
|
2767
|
-
parsed.type,
|
|
2768
|
-
succeeded.map((r) => r.provider.id),
|
|
2769
|
-
opts.global ?? false
|
|
2770
|
-
);
|
|
2771
|
-
}
|
|
2772
|
-
if (format === "json") {
|
|
2773
|
-
outputSuccess(operation, mvi, {
|
|
2774
|
-
installed: succeeded.map((r) => ({
|
|
2775
|
-
name: serverName,
|
|
2776
|
-
providers: [r.provider.id],
|
|
2777
|
-
config
|
|
2778
|
-
})),
|
|
2779
|
-
dryRun: false
|
|
2780
|
-
});
|
|
2781
|
-
} else {
|
|
2782
|
-
console.log(pc8.bold(`
|
|
2783
|
-
${succeeded.length}/${results.length} providers configured.`));
|
|
2784
|
-
}
|
|
1167
|
+
} catch {
|
|
2785
1168
|
}
|
|
2786
|
-
|
|
1169
|
+
}
|
|
1170
|
+
return { canonicalCount, brokenCount, staleCount };
|
|
2787
1171
|
}
|
|
2788
1172
|
|
|
2789
|
-
// src/commands/
|
|
2790
|
-
import
|
|
2791
|
-
function
|
|
2792
|
-
parent.command("
|
|
1173
|
+
// src/commands/instructions/check.ts
|
|
1174
|
+
import pc3 from "picocolors";
|
|
1175
|
+
function registerInstructionsCheck(parent) {
|
|
1176
|
+
parent.command("check").description("Check injection status across providers").option(
|
|
1177
|
+
"-a, --agent <name>",
|
|
1178
|
+
"Check specific agent(s)",
|
|
1179
|
+
(v, prev) => [...prev, v],
|
|
1180
|
+
[]
|
|
1181
|
+
).option("-g, --global", "Check global instruction files").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").option("--all", "Check all known providers").action(
|
|
2793
1182
|
async (opts) => {
|
|
2794
|
-
const operation = "
|
|
1183
|
+
const operation = "instructions.check";
|
|
2795
1184
|
const mvi = "standard";
|
|
2796
1185
|
let format;
|
|
2797
1186
|
try {
|
|
2798
1187
|
format = resolveFormat({
|
|
2799
1188
|
jsonFlag: opts.json ?? false,
|
|
2800
|
-
humanFlag:
|
|
1189
|
+
humanFlag: opts.human ?? false,
|
|
2801
1190
|
projectDefault: "json"
|
|
2802
1191
|
});
|
|
2803
1192
|
} catch (error) {
|
|
@@ -2811,90 +1200,79 @@ function registerMcpList(parent) {
|
|
|
2811
1200
|
);
|
|
2812
1201
|
process.exit(1);
|
|
2813
1202
|
}
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
(
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
emitJsonError(
|
|
2822
|
-
operation,
|
|
2823
|
-
mvi,
|
|
2824
|
-
ErrorCodes.PROVIDER_NOT_FOUND,
|
|
2825
|
-
message,
|
|
2826
|
-
ErrorCategories.NOT_FOUND,
|
|
2827
|
-
{
|
|
2828
|
-
provider: selectedProvider
|
|
2829
|
-
}
|
|
2830
|
-
);
|
|
2831
|
-
} else {
|
|
2832
|
-
console.error(pc9.red(message));
|
|
2833
|
-
}
|
|
2834
|
-
process.exit(1);
|
|
2835
|
-
}
|
|
2836
|
-
const allEntries = [];
|
|
2837
|
-
for (const provider of providers) {
|
|
2838
|
-
const scope = resolvePreferredConfigScope(provider, opts.global);
|
|
2839
|
-
const entries = await listMcpServers(provider, scope);
|
|
2840
|
-
for (const entry of entries) {
|
|
2841
|
-
allEntries.push({
|
|
2842
|
-
name: entry.name,
|
|
2843
|
-
command: typeof entry.config.command === "string" ? entry.config.command : void 0,
|
|
2844
|
-
scope
|
|
2845
|
-
});
|
|
2846
|
-
}
|
|
1203
|
+
let providers;
|
|
1204
|
+
if (opts.all) {
|
|
1205
|
+
providers = getAllProviders();
|
|
1206
|
+
} else if (opts.agent.length > 0) {
|
|
1207
|
+
providers = opts.agent.map((a) => getProvider(a)).filter((p) => p !== void 0);
|
|
1208
|
+
} else {
|
|
1209
|
+
providers = getInstalledProviders();
|
|
2847
1210
|
}
|
|
1211
|
+
const scope = opts.global ? "global" : "project";
|
|
1212
|
+
const results = await checkAllInjections(providers, process.cwd(), scope);
|
|
1213
|
+
const providerStatus = results.map((r) => ({
|
|
1214
|
+
id: r.provider,
|
|
1215
|
+
present: r.status === "current" || r.status === "outdated",
|
|
1216
|
+
path: r.file
|
|
1217
|
+
}));
|
|
1218
|
+
const present = providerStatus.filter((p) => p.present).length;
|
|
1219
|
+
const missing = providerStatus.filter((p) => !p.present).length;
|
|
2848
1220
|
if (format === "json") {
|
|
2849
1221
|
outputSuccess(operation, mvi, {
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
1222
|
+
providers: providerStatus,
|
|
1223
|
+
present,
|
|
1224
|
+
missing
|
|
2853
1225
|
});
|
|
2854
1226
|
return;
|
|
2855
1227
|
}
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
return;
|
|
2859
|
-
}
|
|
2860
|
-
console.log(pc9.bold(`
|
|
2861
|
-
${allEntries.length} MCP server(s) configured:
|
|
1228
|
+
console.log(pc3.bold(`
|
|
1229
|
+
Instruction file status (${scope}):
|
|
2862
1230
|
`));
|
|
2863
|
-
for (const
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
1231
|
+
for (const r of results) {
|
|
1232
|
+
let icon;
|
|
1233
|
+
let label;
|
|
1234
|
+
switch (r.status) {
|
|
1235
|
+
case "current":
|
|
1236
|
+
icon = pc3.green("\u2713");
|
|
1237
|
+
label = "current";
|
|
1238
|
+
break;
|
|
1239
|
+
case "outdated":
|
|
1240
|
+
icon = pc3.yellow("~");
|
|
1241
|
+
label = "outdated";
|
|
1242
|
+
break;
|
|
1243
|
+
case "missing":
|
|
1244
|
+
icon = pc3.red("\u2717");
|
|
1245
|
+
label = "missing";
|
|
1246
|
+
break;
|
|
1247
|
+
case "none":
|
|
1248
|
+
icon = pc3.dim("-");
|
|
1249
|
+
label = "no injection";
|
|
1250
|
+
break;
|
|
1251
|
+
}
|
|
1252
|
+
console.log(` ${icon} ${r.file.padEnd(40)} ${label}`);
|
|
2868
1253
|
}
|
|
2869
1254
|
console.log();
|
|
2870
|
-
console.log(pc9.dim("G = global config, P = project config"));
|
|
2871
|
-
console.log();
|
|
2872
1255
|
}
|
|
2873
1256
|
);
|
|
2874
1257
|
}
|
|
2875
1258
|
|
|
2876
|
-
// src/commands/
|
|
2877
|
-
import
|
|
2878
|
-
function
|
|
2879
|
-
parent.command("
|
|
1259
|
+
// src/commands/instructions/inject.ts
|
|
1260
|
+
import pc4 from "picocolors";
|
|
1261
|
+
function registerInstructionsInject(parent) {
|
|
1262
|
+
parent.command("inject").description("Inject instruction blocks into all provider files").option(
|
|
2880
1263
|
"-a, --agent <name>",
|
|
2881
1264
|
"Target specific agent(s)",
|
|
2882
1265
|
(v, prev) => [...prev, v],
|
|
2883
1266
|
[]
|
|
2884
|
-
).option(
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
(v, prev) => [...prev, v],
|
|
2888
|
-
[]
|
|
2889
|
-
).option("-g, --global", "Remove from global config").option("--all", "Remove from all detected agents").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(
|
|
2890
|
-
async (name, opts) => {
|
|
2891
|
-
const operation = "mcp.remove";
|
|
1267
|
+
).option("-g, --global", "Inject into global instruction files").option("--content <text>", "Custom content to inject").option("--dry-run", "Preview without writing").option("--all", "Target all known providers").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(
|
|
1268
|
+
async (opts) => {
|
|
1269
|
+
const operation = "instructions.inject";
|
|
2892
1270
|
const mvi = "standard";
|
|
2893
1271
|
let format;
|
|
2894
1272
|
try {
|
|
2895
1273
|
format = resolveFormat({
|
|
2896
1274
|
jsonFlag: opts.json ?? false,
|
|
2897
|
-
humanFlag:
|
|
1275
|
+
humanFlag: opts.human ?? false,
|
|
2898
1276
|
projectDefault: "json"
|
|
2899
1277
|
});
|
|
2900
1278
|
} catch (error) {
|
|
@@ -2910,16 +1288,14 @@ function registerMcpRemove(parent) {
|
|
|
2910
1288
|
}
|
|
2911
1289
|
let providers;
|
|
2912
1290
|
if (opts.all) {
|
|
2913
|
-
providers =
|
|
1291
|
+
providers = getAllProviders();
|
|
2914
1292
|
} else if (opts.agent.length > 0) {
|
|
2915
1293
|
providers = opts.agent.map((a) => getProvider(a)).filter((p) => p !== void 0);
|
|
2916
|
-
} else if (opts.provider.length > 0) {
|
|
2917
|
-
providers = opts.provider.map((a) => getProvider(a)).filter((p) => p !== void 0);
|
|
2918
1294
|
} else {
|
|
2919
1295
|
providers = getInstalledProviders();
|
|
2920
1296
|
}
|
|
2921
1297
|
if (providers.length === 0) {
|
|
2922
|
-
const message = "No
|
|
1298
|
+
const message = "No providers found.";
|
|
2923
1299
|
if (format === "json") {
|
|
2924
1300
|
emitJsonError(
|
|
2925
1301
|
operation,
|
|
@@ -2929,60 +1305,144 @@ function registerMcpRemove(parent) {
|
|
|
2929
1305
|
ErrorCategories.NOT_FOUND
|
|
2930
1306
|
);
|
|
2931
1307
|
} else {
|
|
2932
|
-
console.error(
|
|
1308
|
+
console.error(pc4.red(message));
|
|
2933
1309
|
}
|
|
2934
1310
|
process.exit(1);
|
|
2935
1311
|
}
|
|
1312
|
+
const content = opts.content ?? generateInjectionContent();
|
|
2936
1313
|
const scope = opts.global ? "global" : "project";
|
|
2937
|
-
const
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
1314
|
+
const groups = groupByInstructFile(providers);
|
|
1315
|
+
if (opts.dryRun) {
|
|
1316
|
+
if (format === "json") {
|
|
1317
|
+
outputSuccess(operation, mvi, {
|
|
1318
|
+
injected: [],
|
|
1319
|
+
providers: providers.map((p) => p.id),
|
|
1320
|
+
count: 0,
|
|
1321
|
+
dryRun: true,
|
|
1322
|
+
wouldInject: Array.from(groups.entries()).map(([file, group]) => ({
|
|
1323
|
+
file,
|
|
1324
|
+
providers: group.map((p) => p.id)
|
|
1325
|
+
}))
|
|
1326
|
+
});
|
|
2946
1327
|
} else {
|
|
2947
|
-
|
|
1328
|
+
console.log(pc4.bold("Dry run - would inject into:\n"));
|
|
1329
|
+
for (const [file, group] of groups) {
|
|
1330
|
+
console.log(` ${pc4.bold(file)}: ${group.map((p) => p.id).join(", ")}`);
|
|
1331
|
+
}
|
|
1332
|
+
console.log(pc4.dim(`
|
|
1333
|
+
Scope: ${scope}`));
|
|
1334
|
+
console.log(pc4.dim(` Content length: ${content.length} chars`));
|
|
2948
1335
|
}
|
|
1336
|
+
return;
|
|
2949
1337
|
}
|
|
2950
|
-
|
|
2951
|
-
|
|
1338
|
+
const results = await injectAll(providers, process.cwd(), scope, content);
|
|
1339
|
+
const injected = [];
|
|
1340
|
+
for (const [file] of results) {
|
|
1341
|
+
injected.push(file);
|
|
2952
1342
|
}
|
|
2953
1343
|
if (format === "json") {
|
|
2954
1344
|
outputSuccess(operation, mvi, {
|
|
2955
|
-
|
|
2956
|
-
providers:
|
|
2957
|
-
|
|
1345
|
+
injected,
|
|
1346
|
+
providers: providers.map((p) => p.id),
|
|
1347
|
+
count: results.size
|
|
2958
1348
|
});
|
|
2959
1349
|
} else {
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
} else {
|
|
2964
|
-
console.log(pc10.yellow(`Server "${name}" not found in any provider config.`));
|
|
1350
|
+
for (const [file, action] of results) {
|
|
1351
|
+
const icon = action === "created" ? pc4.green("+") : action === "updated" ? pc4.yellow("~") : pc4.blue("^");
|
|
1352
|
+
console.log(` ${icon} ${file} (${action})`);
|
|
2965
1353
|
}
|
|
1354
|
+
console.log(pc4.bold(`
|
|
1355
|
+
${results.size} file(s) processed.`));
|
|
2966
1356
|
}
|
|
2967
1357
|
}
|
|
2968
1358
|
);
|
|
2969
1359
|
}
|
|
2970
1360
|
|
|
2971
|
-
// src/commands/
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
1361
|
+
// src/commands/instructions/update.ts
|
|
1362
|
+
import pc5 from "picocolors";
|
|
1363
|
+
function registerInstructionsUpdate(parent) {
|
|
1364
|
+
parent.command("update").description("Update all instruction file injections").option("-g, --global", "Update global instruction files").option("-y, --yes", "Skip confirmation").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
1365
|
+
const operation = "instructions.update";
|
|
1366
|
+
const mvi = "standard";
|
|
1367
|
+
let format;
|
|
1368
|
+
try {
|
|
1369
|
+
format = resolveFormat({
|
|
1370
|
+
jsonFlag: opts.json ?? false,
|
|
1371
|
+
humanFlag: opts.human ?? false,
|
|
1372
|
+
projectDefault: "json"
|
|
1373
|
+
});
|
|
1374
|
+
} catch (error) {
|
|
1375
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1376
|
+
emitJsonError(
|
|
1377
|
+
operation,
|
|
1378
|
+
mvi,
|
|
1379
|
+
ErrorCodes.FORMAT_CONFLICT,
|
|
1380
|
+
message,
|
|
1381
|
+
ErrorCategories.VALIDATION
|
|
1382
|
+
);
|
|
1383
|
+
process.exit(1);
|
|
1384
|
+
}
|
|
1385
|
+
const providers = getInstalledProviders();
|
|
1386
|
+
const scope = opts.global ? "global" : "project";
|
|
1387
|
+
const content = generateInjectionContent();
|
|
1388
|
+
const checks = await checkAllInjections(providers, process.cwd(), scope, content);
|
|
1389
|
+
const needsUpdate = checks.filter((c) => c.status !== "current");
|
|
1390
|
+
if (needsUpdate.length === 0) {
|
|
1391
|
+
if (format === "json") {
|
|
1392
|
+
outputSuccess(operation, mvi, {
|
|
1393
|
+
updated: [],
|
|
1394
|
+
failed: [],
|
|
1395
|
+
count: { updated: 0, failed: 0 }
|
|
1396
|
+
});
|
|
1397
|
+
} else {
|
|
1398
|
+
console.log(pc5.green("All instruction files are up to date."));
|
|
1399
|
+
}
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
if (format === "human") {
|
|
1403
|
+
console.log(pc5.bold(`${needsUpdate.length} file(s) need updating:
|
|
1404
|
+
`));
|
|
1405
|
+
for (const c of needsUpdate) {
|
|
1406
|
+
console.log(` ${c.file} (${c.status})`);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
const providerIds = new Set(needsUpdate.map((c) => c.provider));
|
|
1410
|
+
const toUpdate = providers.filter((p) => providerIds.has(p.id));
|
|
1411
|
+
const results = await injectAll(toUpdate, process.cwd(), scope, content);
|
|
1412
|
+
const updated = [];
|
|
1413
|
+
for (const [file] of results) {
|
|
1414
|
+
updated.push(file);
|
|
1415
|
+
}
|
|
1416
|
+
if (format === "human") {
|
|
1417
|
+
console.log();
|
|
1418
|
+
for (const [file, action] of results) {
|
|
1419
|
+
console.log(` ${pc5.green("\u2713")} ${file} (${action})`);
|
|
1420
|
+
}
|
|
1421
|
+
console.log(pc5.bold(`
|
|
1422
|
+
${results.size} file(s) updated.`));
|
|
1423
|
+
}
|
|
1424
|
+
if (format === "json") {
|
|
1425
|
+
outputSuccess(operation, mvi, {
|
|
1426
|
+
updated,
|
|
1427
|
+
failed: [],
|
|
1428
|
+
count: { updated: updated.length, failed: 0 }
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
// src/commands/instructions/index.ts
|
|
1435
|
+
function registerInstructionsCommands(program2) {
|
|
1436
|
+
const instructions = program2.command("instructions").description("Manage instruction file injections");
|
|
1437
|
+
registerInstructionsInject(instructions);
|
|
1438
|
+
registerInstructionsCheck(instructions);
|
|
1439
|
+
registerInstructionsUpdate(instructions);
|
|
2980
1440
|
}
|
|
2981
1441
|
|
|
2982
1442
|
// src/commands/providers.ts
|
|
2983
1443
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2984
1444
|
import { resolveOutputFormat as resolveOutputFormat2 } from "@cleocode/lafs";
|
|
2985
|
-
import
|
|
1445
|
+
import pc6 from "picocolors";
|
|
2986
1446
|
function registerProvidersCommand(program2) {
|
|
2987
1447
|
const providers = program2.command("providers").description("Manage AI agent providers");
|
|
2988
1448
|
providers.command("list").description("List all supported providers").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").option("--tier <tier>", "Filter by priority tier (high, medium, low)").action(async (opts) => {
|
|
@@ -3016,20 +1476,20 @@ function registerProvidersCommand(program2) {
|
|
|
3016
1476
|
console.log(JSON.stringify(envelope, null, 2));
|
|
3017
1477
|
return;
|
|
3018
1478
|
}
|
|
3019
|
-
console.log(
|
|
1479
|
+
console.log(pc6.bold(`
|
|
3020
1480
|
CAMP Provider Registry v${getRegistryVersion()}`));
|
|
3021
|
-
console.log(
|
|
1481
|
+
console.log(pc6.dim(`${getProviderCount()} providers
|
|
3022
1482
|
`));
|
|
3023
1483
|
const tiers = ["high", "medium", "low"];
|
|
3024
1484
|
for (const tier of tiers) {
|
|
3025
1485
|
const tierProviders = all.filter((p) => p.priority === tier);
|
|
3026
1486
|
if (tierProviders.length === 0) continue;
|
|
3027
|
-
const tierLabel = tier === "high" ?
|
|
1487
|
+
const tierLabel = tier === "high" ? pc6.green("HIGH") : tier === "medium" ? pc6.yellow("MEDIUM") : pc6.dim("LOW");
|
|
3028
1488
|
console.log(`${tierLabel} priority:`);
|
|
3029
1489
|
for (const p of tierProviders) {
|
|
3030
|
-
const status = p.status === "active" ?
|
|
1490
|
+
const status = p.status === "active" ? pc6.green("active") : p.status === "beta" ? pc6.yellow("beta") : pc6.dim(p.status);
|
|
3031
1491
|
console.log(
|
|
3032
|
-
` ${
|
|
1492
|
+
` ${pc6.bold(p.agentFlag.padEnd(20))} ${p.toolName.padEnd(22)} ${p.vendor.padEnd(16)} [${status}]`
|
|
3033
1493
|
);
|
|
3034
1494
|
}
|
|
3035
1495
|
console.log();
|
|
@@ -3074,19 +1534,19 @@ CAMP Provider Registry v${getRegistryVersion()}`));
|
|
|
3074
1534
|
console.log(JSON.stringify(envelope, null, 2));
|
|
3075
1535
|
return;
|
|
3076
1536
|
}
|
|
3077
|
-
console.log(
|
|
1537
|
+
console.log(pc6.bold(`
|
|
3078
1538
|
Detected ${installed.length} installed providers:
|
|
3079
1539
|
`));
|
|
3080
1540
|
for (const r of installed) {
|
|
3081
1541
|
const methods = r.methods.join(", ");
|
|
3082
|
-
const project = r.projectDetected ?
|
|
1542
|
+
const project = r.projectDetected ? pc6.green(" [project]") : "";
|
|
3083
1543
|
console.log(
|
|
3084
|
-
` ${
|
|
1544
|
+
` ${pc6.green("\u2713")} ${pc6.bold(r.provider.toolName.padEnd(22))} via ${pc6.dim(methods)}${project}`
|
|
3085
1545
|
);
|
|
3086
1546
|
}
|
|
3087
1547
|
const notInstalled = results.filter((r) => !r.installed);
|
|
3088
1548
|
if (notInstalled.length > 0) {
|
|
3089
|
-
console.log(
|
|
1549
|
+
console.log(pc6.dim(`
|
|
3090
1550
|
${notInstalled.length} providers not detected`));
|
|
3091
1551
|
}
|
|
3092
1552
|
console.log();
|
|
@@ -3114,7 +1574,7 @@ Detected ${installed.length} installed providers:
|
|
|
3114
1574
|
id
|
|
3115
1575
|
});
|
|
3116
1576
|
} else {
|
|
3117
|
-
console.error(
|
|
1577
|
+
console.error(pc6.red(message));
|
|
3118
1578
|
}
|
|
3119
1579
|
process.exit(1);
|
|
3120
1580
|
}
|
|
@@ -3130,9 +1590,9 @@ Detected ${installed.length} installed providers:
|
|
|
3130
1590
|
console.log(JSON.stringify(envelope, null, 2));
|
|
3131
1591
|
return;
|
|
3132
1592
|
}
|
|
3133
|
-
console.log(
|
|
1593
|
+
console.log(pc6.bold(`
|
|
3134
1594
|
${provider.toolName}`));
|
|
3135
|
-
console.log(
|
|
1595
|
+
console.log(pc6.dim(`by ${provider.vendor}
|
|
3136
1596
|
`));
|
|
3137
1597
|
console.log(` ID: ${provider.id}`);
|
|
3138
1598
|
console.log(` Flag: --agent ${provider.agentFlag}`);
|
|
@@ -3148,7 +1608,7 @@ ${provider.toolName}`));
|
|
|
3148
1608
|
console.log(` Transports: ${provider.supportedTransports.join(", ")}`);
|
|
3149
1609
|
console.log(` Headers: ${provider.supportsHeaders ? "yes" : "no"}`);
|
|
3150
1610
|
console.log();
|
|
3151
|
-
console.log(
|
|
1611
|
+
console.log(pc6.dim(" Paths:"));
|
|
3152
1612
|
console.log(` Global dir: ${provider.pathGlobal}`);
|
|
3153
1613
|
console.log(` Project dir: ${provider.pathProject || "(none)"}`);
|
|
3154
1614
|
console.log(` Global config: ${provider.configPathGlobal}`);
|
|
@@ -3182,7 +1642,7 @@ ${provider.toolName}`));
|
|
|
3182
1642
|
id: opts.provider
|
|
3183
1643
|
});
|
|
3184
1644
|
} else {
|
|
3185
|
-
console.error(
|
|
1645
|
+
console.error(pc6.red(message));
|
|
3186
1646
|
}
|
|
3187
1647
|
process.exit(1);
|
|
3188
1648
|
}
|
|
@@ -3200,9 +1660,9 @@ ${provider.toolName}`));
|
|
|
3200
1660
|
console.log(JSON.stringify(envelope, null, 2));
|
|
3201
1661
|
return;
|
|
3202
1662
|
}
|
|
3203
|
-
console.log(
|
|
1663
|
+
console.log(pc6.bold("\nProvider Skills Map\n"));
|
|
3204
1664
|
console.log(
|
|
3205
|
-
` ${
|
|
1665
|
+
` ${pc6.bold("Provider".padEnd(22))} ${pc6.bold("Precedence".padEnd(30))} ${pc6.bold("Global Path".padEnd(40))} ${pc6.bold("Project Path")}`
|
|
3206
1666
|
);
|
|
3207
1667
|
console.log(` ${"\u2500".repeat(22)} ${"\u2500".repeat(30)} ${"\u2500".repeat(40)} ${"\u2500".repeat(30)}`);
|
|
3208
1668
|
for (const entry of map) {
|
|
@@ -3210,7 +1670,7 @@ ${provider.toolName}`));
|
|
|
3210
1670
|
` ${entry.toolName.padEnd(22)} ${entry.precedence.padEnd(30)} ${(entry.paths.global ?? "-").padEnd(40)} ${entry.paths.project ?? "-"}`
|
|
3211
1671
|
);
|
|
3212
1672
|
}
|
|
3213
|
-
console.log(
|
|
1673
|
+
console.log(pc6.dim(`
|
|
3214
1674
|
${map.length} providers shown`));
|
|
3215
1675
|
console.log();
|
|
3216
1676
|
});
|
|
@@ -3246,25 +1706,25 @@ ${provider.toolName}`));
|
|
|
3246
1706
|
console.log(JSON.stringify(envelope, null, 2));
|
|
3247
1707
|
return;
|
|
3248
1708
|
}
|
|
3249
|
-
console.log(
|
|
1709
|
+
console.log(pc6.bold(`
|
|
3250
1710
|
CAMP Hook Support (mappings v${getHookMappingsVersion()})
|
|
3251
1711
|
`));
|
|
3252
|
-
console.log(
|
|
1712
|
+
console.log(pc6.dim(` ${CANONICAL_HOOK_EVENTS.length} canonical events defined
|
|
3253
1713
|
`));
|
|
3254
1714
|
console.log(
|
|
3255
|
-
` ${
|
|
1715
|
+
` ${pc6.bold("Provider".padEnd(22))} ${pc6.bold("System".padEnd(10))} ${pc6.bold("Coverage".padEnd(12))} ${pc6.bold("Supported".padEnd(12))} ${pc6.bold("Provider-Only")}`
|
|
3256
1716
|
);
|
|
3257
1717
|
console.log(
|
|
3258
1718
|
` ${"\u2500".repeat(22)} ${"\u2500".repeat(10)} ${"\u2500".repeat(12)} ${"\u2500".repeat(12)} ${"\u2500".repeat(20)}`
|
|
3259
1719
|
);
|
|
3260
1720
|
for (const s of summaries) {
|
|
3261
1721
|
if (!s) continue;
|
|
3262
|
-
const system = s.hookSystem === "none" ?
|
|
3263
|
-
const coverage = s.coverage > 0 ? (s.coverage >= 75 ?
|
|
1722
|
+
const system = s.hookSystem === "none" ? pc6.dim("none") : s.experimental ? pc6.yellow(s.hookSystem + "*") : pc6.green(s.hookSystem);
|
|
1723
|
+
const coverage = s.coverage > 0 ? (s.coverage >= 75 ? pc6.green : s.coverage >= 40 ? pc6.yellow : pc6.dim)(
|
|
3264
1724
|
`${s.coverage}%`
|
|
3265
|
-
) :
|
|
3266
|
-
const supported = s.supportedCount > 0 ? `${s.supportedCount}/${s.totalCanonical}` :
|
|
3267
|
-
const provOnly = s.providerOnly.length > 0 ? String(s.providerOnly.length) :
|
|
1725
|
+
) : pc6.dim("0%");
|
|
1726
|
+
const supported = s.supportedCount > 0 ? `${s.supportedCount}/${s.totalCanonical}` : pc6.dim("0");
|
|
1727
|
+
const provOnly = s.providerOnly.length > 0 ? String(s.providerOnly.length) : pc6.dim("-");
|
|
3268
1728
|
const provider = getProvider(s.providerId);
|
|
3269
1729
|
const name = provider?.toolName ?? s.providerId;
|
|
3270
1730
|
console.log(
|
|
@@ -3273,13 +1733,13 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
|
|
|
3273
1733
|
}
|
|
3274
1734
|
const withHooks = summaries.filter((s) => s && s.supportedCount > 0);
|
|
3275
1735
|
console.log(
|
|
3276
|
-
|
|
1736
|
+
pc6.dim(
|
|
3277
1737
|
`
|
|
3278
1738
|
${withHooks.length} providers with hook support, ${summaries.length - withHooks.length} without`
|
|
3279
1739
|
)
|
|
3280
1740
|
);
|
|
3281
1741
|
if (summaries.some((s) => s?.experimental)) {
|
|
3282
|
-
console.log(
|
|
1742
|
+
console.log(pc6.dim(" * = experimental hook system"));
|
|
3283
1743
|
}
|
|
3284
1744
|
console.log();
|
|
3285
1745
|
});
|
|
@@ -3309,22 +1769,22 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
|
|
|
3309
1769
|
const p = getProvider(id);
|
|
3310
1770
|
return (p?.toolName ?? id).slice(0, 14);
|
|
3311
1771
|
});
|
|
3312
|
-
console.log(
|
|
1772
|
+
console.log(pc6.bold("\nHook Support Matrix\n"));
|
|
3313
1773
|
const eventCol = "CAAMP Event".padEnd(22);
|
|
3314
|
-
const provCols = providerNames.map((n) =>
|
|
3315
|
-
console.log(` ${
|
|
1774
|
+
const provCols = providerNames.map((n) => pc6.bold(n.padEnd(16))).join("");
|
|
1775
|
+
console.log(` ${pc6.bold(eventCol)} ${provCols}`);
|
|
3316
1776
|
console.log(` ${"\u2500".repeat(22)} ${providerNames.map(() => "\u2500".repeat(16)).join("")}`);
|
|
3317
1777
|
for (const event of matrix.events) {
|
|
3318
1778
|
const cells = matrix.providers.map((id) => {
|
|
3319
1779
|
const m = matrix.matrix[event][id];
|
|
3320
|
-
if (!m?.supported) return
|
|
3321
|
-
return
|
|
1780
|
+
if (!m?.supported) return pc6.dim("\xB7".padEnd(16));
|
|
1781
|
+
return pc6.green((m.nativeName ?? "?").slice(0, 14).padEnd(16));
|
|
3322
1782
|
}).join("");
|
|
3323
1783
|
console.log(` ${event.padEnd(22)} ${cells}`);
|
|
3324
1784
|
}
|
|
3325
1785
|
const commonEvents = getCommonEvents(matrix.providers);
|
|
3326
1786
|
console.log(
|
|
3327
|
-
|
|
1787
|
+
pc6.dim(`
|
|
3328
1788
|
Common events: ${commonEvents.length > 0 ? commonEvents.join(", ") : "none"}`)
|
|
3329
1789
|
);
|
|
3330
1790
|
console.log();
|
|
@@ -3352,7 +1812,7 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
|
|
|
3352
1812
|
if (format === "json") {
|
|
3353
1813
|
emitJsonError2(operation, mvi, "E_UNKNOWN_EVENT", msg, "VALIDATION");
|
|
3354
1814
|
} else {
|
|
3355
|
-
console.error(
|
|
1815
|
+
console.error(pc6.red(msg));
|
|
3356
1816
|
}
|
|
3357
1817
|
process.exit(1);
|
|
3358
1818
|
}
|
|
@@ -3372,18 +1832,18 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
|
|
|
3372
1832
|
} else {
|
|
3373
1833
|
if (result.supported) {
|
|
3374
1834
|
console.log(`
|
|
3375
|
-
${
|
|
3376
|
-
if (result.notes) console.log(
|
|
1835
|
+
${pc6.green(event)} \u2192 ${pc6.bold(result.native)} (${opts.to})`);
|
|
1836
|
+
if (result.notes) console.log(pc6.dim(` Note: ${result.notes}`));
|
|
3377
1837
|
} else {
|
|
3378
1838
|
console.log(`
|
|
3379
|
-
${
|
|
1839
|
+
${pc6.red(event)} \u2192 ${pc6.dim("not supported")} (${opts.to})`);
|
|
3380
1840
|
}
|
|
3381
1841
|
console.log();
|
|
3382
1842
|
}
|
|
3383
1843
|
return;
|
|
3384
1844
|
}
|
|
3385
1845
|
if (opts.from) {
|
|
3386
|
-
const { toCanonical } = await import("./hooks-
|
|
1846
|
+
const { toCanonical } = await import("./hooks-VLIP52LY.js");
|
|
3387
1847
|
const canonical2 = toCanonical(event, opts.from);
|
|
3388
1848
|
if (format === "json") {
|
|
3389
1849
|
const envelope = buildEnvelope2(
|
|
@@ -3402,11 +1862,11 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
|
|
|
3402
1862
|
} else {
|
|
3403
1863
|
if (canonical2) {
|
|
3404
1864
|
console.log(`
|
|
3405
|
-
${
|
|
1865
|
+
${pc6.bold(event)} (${opts.from}) \u2192 ${pc6.green(canonical2)}`);
|
|
3406
1866
|
} else {
|
|
3407
1867
|
console.log(
|
|
3408
1868
|
`
|
|
3409
|
-
${
|
|
1869
|
+
${pc6.bold(event)} (${opts.from}) \u2192 ${pc6.dim("no canonical mapping (provider-only event)")}`
|
|
3410
1870
|
);
|
|
3411
1871
|
}
|
|
3412
1872
|
console.log();
|
|
@@ -3419,11 +1879,11 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
|
|
|
3419
1879
|
if (format === "json") {
|
|
3420
1880
|
emitJsonError2(operation, mvi, "E_UNKNOWN_EVENT", msg, "VALIDATION");
|
|
3421
1881
|
} else {
|
|
3422
|
-
console.error(
|
|
1882
|
+
console.error(pc6.red(msg));
|
|
3423
1883
|
}
|
|
3424
1884
|
process.exit(1);
|
|
3425
1885
|
}
|
|
3426
|
-
const { getMappedProviderIds } = await import("./hooks-
|
|
1886
|
+
const { getMappedProviderIds } = await import("./hooks-VLIP52LY.js");
|
|
3427
1887
|
const allIds = getMappedProviderIds();
|
|
3428
1888
|
const translations = translateToAll(canonical, allIds);
|
|
3429
1889
|
if (format === "json") {
|
|
@@ -3441,7 +1901,7 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
|
|
|
3441
1901
|
);
|
|
3442
1902
|
console.log(JSON.stringify(envelope, null, 2));
|
|
3443
1903
|
} else {
|
|
3444
|
-
console.log(
|
|
1904
|
+
console.log(pc6.bold(`
|
|
3445
1905
|
${event} across providers:
|
|
3446
1906
|
`));
|
|
3447
1907
|
for (const id of allIds) {
|
|
@@ -3449,9 +1909,9 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
|
|
|
3449
1909
|
const provider = getProvider(id);
|
|
3450
1910
|
const name = (provider?.toolName ?? id).padEnd(22);
|
|
3451
1911
|
if (native) {
|
|
3452
|
-
console.log(` ${
|
|
1912
|
+
console.log(` ${pc6.green("\u2713")} ${name} ${pc6.bold(native)}`);
|
|
3453
1913
|
} else {
|
|
3454
|
-
console.log(` ${
|
|
1914
|
+
console.log(` ${pc6.dim("\xB7")} ${name} ${pc6.dim("not supported")}`);
|
|
3455
1915
|
}
|
|
3456
1916
|
}
|
|
3457
1917
|
console.log();
|
|
@@ -3507,13 +1967,13 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
|
|
|
3507
1967
|
console.log(JSON.stringify(envelope, null, 2));
|
|
3508
1968
|
return;
|
|
3509
1969
|
}
|
|
3510
|
-
console.log(
|
|
1970
|
+
console.log(pc6.bold("\nProvider Capability Matrix\n"));
|
|
3511
1971
|
if (opts.filter) {
|
|
3512
|
-
console.log(
|
|
1972
|
+
console.log(pc6.dim(` Filter: ${opts.filter}
|
|
3513
1973
|
`));
|
|
3514
1974
|
}
|
|
3515
1975
|
console.log(
|
|
3516
|
-
` ${
|
|
1976
|
+
` ${pc6.bold("Provider".padEnd(22))} ${pc6.bold("Skills Precedence".padEnd(20))} ${pc6.bold("Hooks".padEnd(8))} ${pc6.bold("Spawn")}`
|
|
3517
1977
|
);
|
|
3518
1978
|
console.log(` ${"\u2500".repeat(22)} ${"\u2500".repeat(20)} ${"\u2500".repeat(8)} ${"\u2500".repeat(20)}`);
|
|
3519
1979
|
for (const row of matrix) {
|
|
@@ -3523,7 +1983,7 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
|
|
|
3523
1983
|
` ${row.toolName.padEnd(22)} ${row.skillsPrecedence.padEnd(20)} ${hooks2.padEnd(8)} ${spawn}`
|
|
3524
1984
|
);
|
|
3525
1985
|
}
|
|
3526
|
-
console.log(
|
|
1986
|
+
console.log(pc6.dim(`
|
|
3527
1987
|
${matrix.length} providers shown`));
|
|
3528
1988
|
console.log();
|
|
3529
1989
|
});
|
|
@@ -3561,13 +2021,13 @@ function emitJsonError2(operation, mvi, code, message, category, details = {}) {
|
|
|
3561
2021
|
}
|
|
3562
2022
|
|
|
3563
2023
|
// src/commands/skills/audit.ts
|
|
3564
|
-
import { existsSync as
|
|
3565
|
-
import
|
|
2024
|
+
import { existsSync as existsSync3, statSync } from "fs";
|
|
2025
|
+
import pc7 from "picocolors";
|
|
3566
2026
|
function registerSkillsAudit(parent) {
|
|
3567
2027
|
parent.command("audit").description("Security scan skill files (46+ rules, SARIF output)").argument("[path]", "Path to SKILL.md or directory", ".").option("--sarif", "Output in SARIF format (raw SARIF, not LAFS envelope)").option("--json", "Output as JSON (LAFS envelope)").option("--human", "Output in human-readable format").action(async (path, opts) => {
|
|
3568
2028
|
const operation = "skills.audit";
|
|
3569
2029
|
const mvi = "standard";
|
|
3570
|
-
if (!
|
|
2030
|
+
if (!existsSync3(path)) {
|
|
3571
2031
|
const message = `Path not found: ${path}`;
|
|
3572
2032
|
if (opts.sarif) {
|
|
3573
2033
|
console.error(
|
|
@@ -3691,7 +2151,7 @@ function registerSkillsAudit(parent) {
|
|
|
3691
2151
|
outputSuccess(operation, mvi, summary2);
|
|
3692
2152
|
return;
|
|
3693
2153
|
}
|
|
3694
|
-
console.log(
|
|
2154
|
+
console.log(pc7.dim("No SKILL.md files found to scan."));
|
|
3695
2155
|
return;
|
|
3696
2156
|
}
|
|
3697
2157
|
const summary = {
|
|
@@ -3725,21 +2185,21 @@ function registerSkillsAudit(parent) {
|
|
|
3725
2185
|
}
|
|
3726
2186
|
let totalFindings = 0;
|
|
3727
2187
|
for (const result of results) {
|
|
3728
|
-
const icon = result.passed ?
|
|
2188
|
+
const icon = result.passed ? pc7.green("\u2713") : pc7.red("\u2717");
|
|
3729
2189
|
console.log(`
|
|
3730
|
-
${icon} ${
|
|
2190
|
+
${icon} ${pc7.bold(result.file)} (score: ${result.score}/100)`);
|
|
3731
2191
|
if (result.findings.length === 0) {
|
|
3732
|
-
console.log(
|
|
2192
|
+
console.log(pc7.dim(" No issues found."));
|
|
3733
2193
|
continue;
|
|
3734
2194
|
}
|
|
3735
2195
|
totalFindings += result.findings.length;
|
|
3736
2196
|
for (const f of result.findings) {
|
|
3737
|
-
const sev = f.rule.severity === "critical" ?
|
|
2197
|
+
const sev = f.rule.severity === "critical" ? pc7.red(f.rule.severity) : f.rule.severity === "high" ? pc7.red(f.rule.severity) : f.rule.severity === "medium" ? pc7.yellow(f.rule.severity) : pc7.dim(f.rule.severity);
|
|
3738
2198
|
console.log(` ${sev.padEnd(20)} ${f.rule.id} ${f.rule.name}`);
|
|
3739
|
-
console.log(` ${
|
|
2199
|
+
console.log(` ${pc7.dim(`L${f.line}: ${f.context.slice(0, 80)}`)}`);
|
|
3740
2200
|
}
|
|
3741
2201
|
}
|
|
3742
|
-
console.log(
|
|
2202
|
+
console.log(pc7.bold(`
|
|
3743
2203
|
${results.length} file(s) scanned, ${totalFindings} finding(s)`));
|
|
3744
2204
|
if (!allPassed) {
|
|
3745
2205
|
process.exit(1);
|
|
@@ -3748,7 +2208,7 @@ ${results.length} file(s) scanned, ${totalFindings} finding(s)`));
|
|
|
3748
2208
|
}
|
|
3749
2209
|
|
|
3750
2210
|
// src/commands/skills/check.ts
|
|
3751
|
-
import
|
|
2211
|
+
import pc8 from "picocolors";
|
|
3752
2212
|
function registerSkillsCheck(parent) {
|
|
3753
2213
|
parent.command("check").description("Check for available skill updates").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
3754
2214
|
const operation = "skills.check";
|
|
@@ -3781,12 +2241,12 @@ function registerSkillsCheck(parent) {
|
|
|
3781
2241
|
total: 0
|
|
3782
2242
|
});
|
|
3783
2243
|
} else {
|
|
3784
|
-
console.log(
|
|
2244
|
+
console.log(pc8.dim("No tracked skills."));
|
|
3785
2245
|
}
|
|
3786
2246
|
return;
|
|
3787
2247
|
}
|
|
3788
2248
|
if (format === "human") {
|
|
3789
|
-
console.log(
|
|
2249
|
+
console.log(pc8.dim(`Checking ${entries.length} skill(s) for updates...
|
|
3790
2250
|
`));
|
|
3791
2251
|
}
|
|
3792
2252
|
const skillResults = [];
|
|
@@ -3822,31 +2282,31 @@ function registerSkillsCheck(parent) {
|
|
|
3822
2282
|
for (const r of skillResults) {
|
|
3823
2283
|
let statusLabel;
|
|
3824
2284
|
if (r.hasUpdate) {
|
|
3825
|
-
statusLabel =
|
|
2285
|
+
statusLabel = pc8.yellow("update available");
|
|
3826
2286
|
} else if (r.currentVersion !== "unknown") {
|
|
3827
|
-
statusLabel =
|
|
2287
|
+
statusLabel = pc8.green("up to date");
|
|
3828
2288
|
} else {
|
|
3829
|
-
statusLabel =
|
|
2289
|
+
statusLabel = pc8.dim("unknown");
|
|
3830
2290
|
}
|
|
3831
|
-
console.log(` ${
|
|
2291
|
+
console.log(` ${pc8.bold(r.name.padEnd(30))} ${statusLabel}`);
|
|
3832
2292
|
if (r.currentVersion !== "unknown" || r.latestVersion !== "unknown") {
|
|
3833
2293
|
const current = r.currentVersion !== "unknown" ? r.currentVersion.slice(0, 12) : "?";
|
|
3834
2294
|
const latest = r.latestVersion !== "unknown" ? r.latestVersion : "?";
|
|
3835
2295
|
if (r.hasUpdate) {
|
|
3836
|
-
console.log(` ${
|
|
2296
|
+
console.log(` ${pc8.dim("current:")} ${current} ${pc8.dim("->")} ${pc8.cyan(latest)}`);
|
|
3837
2297
|
} else {
|
|
3838
|
-
console.log(` ${
|
|
2298
|
+
console.log(` ${pc8.dim("version:")} ${current}`);
|
|
3839
2299
|
}
|
|
3840
2300
|
}
|
|
3841
|
-
console.log(` ${
|
|
3842
|
-
console.log(` ${
|
|
2301
|
+
console.log(` ${pc8.dim(`source: ${r.source}`)}`);
|
|
2302
|
+
console.log(` ${pc8.dim(`agents: ${r.agents.join(", ")}`)}`);
|
|
3843
2303
|
console.log();
|
|
3844
2304
|
}
|
|
3845
2305
|
if (updatesAvailable > 0) {
|
|
3846
|
-
console.log(
|
|
3847
|
-
console.log(
|
|
2306
|
+
console.log(pc8.yellow(`${updatesAvailable} update(s) available.`));
|
|
2307
|
+
console.log(pc8.dim("Run `caamp skills update` to update all."));
|
|
3848
2308
|
} else {
|
|
3849
|
-
console.log(
|
|
2309
|
+
console.log(pc8.green("All skills are up to date."));
|
|
3850
2310
|
}
|
|
3851
2311
|
});
|
|
3852
2312
|
}
|
|
@@ -3854,7 +2314,7 @@ function registerSkillsCheck(parent) {
|
|
|
3854
2314
|
// src/commands/skills/find.ts
|
|
3855
2315
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
3856
2316
|
import { resolveOutputFormat as resolveOutputFormat3 } from "@cleocode/lafs";
|
|
3857
|
-
import
|
|
2317
|
+
import pc9 from "picocolors";
|
|
3858
2318
|
var SkillsFindValidationError = class extends Error {
|
|
3859
2319
|
code;
|
|
3860
2320
|
constructor(code, message) {
|
|
@@ -3895,7 +2355,7 @@ function registerSkillsFind(parent) {
|
|
|
3895
2355
|
if (opts.json) {
|
|
3896
2356
|
emitJsonError3(operation, mvi, "E_FORMAT_CONFLICT", message, "VALIDATION");
|
|
3897
2357
|
} else {
|
|
3898
|
-
console.error(
|
|
2358
|
+
console.error(pc9.red(message));
|
|
3899
2359
|
}
|
|
3900
2360
|
process.exit(1);
|
|
3901
2361
|
}
|
|
@@ -3962,19 +2422,19 @@ function registerSkillsFind(parent) {
|
|
|
3962
2422
|
query: query ?? null
|
|
3963
2423
|
});
|
|
3964
2424
|
} else {
|
|
3965
|
-
console.error(
|
|
2425
|
+
console.error(pc9.red(`Recommendation failed: ${message}`));
|
|
3966
2426
|
}
|
|
3967
2427
|
process.exit(1);
|
|
3968
2428
|
}
|
|
3969
2429
|
}
|
|
3970
2430
|
if (!query) {
|
|
3971
|
-
console.log(
|
|
2431
|
+
console.log(pc9.dim("Usage: caamp skills find <query>"));
|
|
3972
2432
|
return;
|
|
3973
2433
|
}
|
|
3974
2434
|
const limit = parseInt(opts.limit, 10);
|
|
3975
2435
|
const client = new MarketplaceClient();
|
|
3976
2436
|
if (format === "human") {
|
|
3977
|
-
console.log(
|
|
2437
|
+
console.log(pc9.dim(`Searching marketplaces for "${query}"...
|
|
3978
2438
|
`));
|
|
3979
2439
|
}
|
|
3980
2440
|
let results;
|
|
@@ -3988,7 +2448,7 @@ function registerSkillsFind(parent) {
|
|
|
3988
2448
|
limit
|
|
3989
2449
|
});
|
|
3990
2450
|
} else {
|
|
3991
|
-
console.error(
|
|
2451
|
+
console.error(pc9.red(`Marketplace search failed: ${message}`));
|
|
3992
2452
|
}
|
|
3993
2453
|
process.exit(1);
|
|
3994
2454
|
}
|
|
@@ -4008,21 +2468,21 @@ function registerSkillsFind(parent) {
|
|
|
4008
2468
|
return;
|
|
4009
2469
|
}
|
|
4010
2470
|
if (results.length === 0) {
|
|
4011
|
-
console.log(
|
|
2471
|
+
console.log(pc9.yellow("No results found."));
|
|
4012
2472
|
return;
|
|
4013
2473
|
}
|
|
4014
|
-
console.log(
|
|
2474
|
+
console.log(pc9.dim(`Found ${results.length} result(s) for "${query}":
|
|
4015
2475
|
`));
|
|
4016
2476
|
results.forEach((skill, index) => {
|
|
4017
2477
|
const num = (index + 1).toString().padStart(2);
|
|
4018
|
-
const stars = skill.stars > 0 ?
|
|
4019
|
-
console.log(` ${
|
|
4020
|
-
console.log(` ${
|
|
4021
|
-
console.log(` ${
|
|
2478
|
+
const stars = skill.stars > 0 ? pc9.yellow(`\u2605 ${formatStars(skill.stars)}`) : "";
|
|
2479
|
+
console.log(` ${pc9.cyan(num)}. ${pc9.bold(skill.scopedName)} ${stars}`);
|
|
2480
|
+
console.log(` ${pc9.dim(skill.description?.slice(0, 80) ?? "")}`);
|
|
2481
|
+
console.log(` ${pc9.dim(`from ${skill.source}`)}`);
|
|
4022
2482
|
console.log();
|
|
4023
2483
|
});
|
|
4024
|
-
console.log(
|
|
4025
|
-
console.log(
|
|
2484
|
+
console.log(pc9.dim("Install with: caamp skills install <name>"));
|
|
2485
|
+
console.log(pc9.dim("Or select by number: caamp skills install <n>"));
|
|
4026
2486
|
});
|
|
4027
2487
|
}
|
|
4028
2488
|
function formatStars(n) {
|
|
@@ -4139,10 +2599,10 @@ function emitJsonError3(operation, mvi, code, message, category, details = {}) {
|
|
|
4139
2599
|
}
|
|
4140
2600
|
|
|
4141
2601
|
// src/commands/skills/init.ts
|
|
4142
|
-
import { existsSync as
|
|
2602
|
+
import { existsSync as existsSync4 } from "fs";
|
|
4143
2603
|
import { mkdir, writeFile } from "fs/promises";
|
|
4144
2604
|
import { join as join3 } from "path";
|
|
4145
|
-
import
|
|
2605
|
+
import pc10 from "picocolors";
|
|
4146
2606
|
function registerSkillsInit(parent) {
|
|
4147
2607
|
parent.command("init").description("Create a new SKILL.md template").argument("[name]", "Skill name").option("-d, --dir <path>", "Output directory", ".").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(
|
|
4148
2608
|
async (name, opts) => {
|
|
@@ -4168,7 +2628,7 @@ function registerSkillsInit(parent) {
|
|
|
4168
2628
|
}
|
|
4169
2629
|
const skillName = name ?? "my-skill";
|
|
4170
2630
|
const skillDir = join3(opts.dir, skillName);
|
|
4171
|
-
if (
|
|
2631
|
+
if (existsSync4(skillDir)) {
|
|
4172
2632
|
const message = `Directory already exists: ${skillDir}`;
|
|
4173
2633
|
if (format === "json") {
|
|
4174
2634
|
emitJsonError(
|
|
@@ -4182,7 +2642,7 @@ function registerSkillsInit(parent) {
|
|
|
4182
2642
|
}
|
|
4183
2643
|
);
|
|
4184
2644
|
} else {
|
|
4185
|
-
console.error(
|
|
2645
|
+
console.error(pc10.red(message));
|
|
4186
2646
|
}
|
|
4187
2647
|
process.exit(1);
|
|
4188
2648
|
}
|
|
@@ -4221,18 +2681,18 @@ Show example inputs and expected outputs.
|
|
|
4221
2681
|
outputSuccess(operation, mvi, result);
|
|
4222
2682
|
return;
|
|
4223
2683
|
}
|
|
4224
|
-
console.log(
|
|
4225
|
-
console.log(
|
|
4226
|
-
console.log(
|
|
4227
|
-
console.log(
|
|
4228
|
-
console.log(
|
|
2684
|
+
console.log(pc10.green(`\u2713 Created skill template: ${skillDir}/SKILL.md`));
|
|
2685
|
+
console.log(pc10.dim("\nNext steps:"));
|
|
2686
|
+
console.log(pc10.dim(" 1. Edit SKILL.md with your instructions"));
|
|
2687
|
+
console.log(pc10.dim(` 2. Validate: caamp skills validate ${join3(skillDir, "SKILL.md")}`));
|
|
2688
|
+
console.log(pc10.dim(` 3. Install: caamp skills install ${skillDir}`));
|
|
4229
2689
|
}
|
|
4230
2690
|
);
|
|
4231
2691
|
}
|
|
4232
2692
|
|
|
4233
2693
|
// src/commands/skills/install.ts
|
|
4234
|
-
import { existsSync as
|
|
4235
|
-
import
|
|
2694
|
+
import { existsSync as existsSync5 } from "fs";
|
|
2695
|
+
import pc11 from "picocolors";
|
|
4236
2696
|
|
|
4237
2697
|
// src/core/sources/github.ts
|
|
4238
2698
|
import { mkdtemp, rm } from "fs/promises";
|
|
@@ -4337,7 +2797,7 @@ function registerSkillsInstall(parent) {
|
|
|
4337
2797
|
ErrorCategories.NOT_FOUND
|
|
4338
2798
|
);
|
|
4339
2799
|
}
|
|
4340
|
-
console.error(
|
|
2800
|
+
console.error(pc11.red(message));
|
|
4341
2801
|
process.exit(1);
|
|
4342
2802
|
}
|
|
4343
2803
|
if (opts.profile) {
|
|
@@ -4362,14 +2822,14 @@ function registerSkillsInstall(parent) {
|
|
|
4362
2822
|
ErrorCategories.VALIDATION
|
|
4363
2823
|
);
|
|
4364
2824
|
}
|
|
4365
|
-
console.error(
|
|
2825
|
+
console.error(pc11.red(message));
|
|
4366
2826
|
console.log(
|
|
4367
|
-
|
|
2827
|
+
pc11.dim("Usage: caamp skills install <source> or caamp skills install --profile <name>")
|
|
4368
2828
|
);
|
|
4369
2829
|
process.exit(1);
|
|
4370
2830
|
}
|
|
4371
2831
|
if (format === "human") {
|
|
4372
|
-
console.log(
|
|
2832
|
+
console.log(pc11.dim(`Installing to ${providers.length} provider(s)...`));
|
|
4373
2833
|
}
|
|
4374
2834
|
let localPath;
|
|
4375
2835
|
let cleanup;
|
|
@@ -4415,7 +2875,7 @@ function registerSkillsInstall(parent) {
|
|
|
4415
2875
|
ErrorCategories.TRANSIENT
|
|
4416
2876
|
);
|
|
4417
2877
|
}
|
|
4418
|
-
console.error(
|
|
2878
|
+
console.error(pc11.red(message));
|
|
4419
2879
|
process.exit(1);
|
|
4420
2880
|
}
|
|
4421
2881
|
} else if (parsed.type === "gitlab" && parsed.owner && parsed.repo) {
|
|
@@ -4439,7 +2899,7 @@ function registerSkillsInstall(parent) {
|
|
|
4439
2899
|
ErrorCategories.TRANSIENT
|
|
4440
2900
|
);
|
|
4441
2901
|
}
|
|
4442
|
-
console.error(
|
|
2902
|
+
console.error(pc11.red(message));
|
|
4443
2903
|
process.exit(1);
|
|
4444
2904
|
}
|
|
4445
2905
|
} else if (parsed.type === "local") {
|
|
@@ -4460,7 +2920,7 @@ function registerSkillsInstall(parent) {
|
|
|
4460
2920
|
ErrorCategories.VALIDATION
|
|
4461
2921
|
);
|
|
4462
2922
|
}
|
|
4463
|
-
console.error(
|
|
2923
|
+
console.error(pc11.red(message));
|
|
4464
2924
|
process.exit(1);
|
|
4465
2925
|
}
|
|
4466
2926
|
const catalogSkill = getSkill(parsed.inferredName);
|
|
@@ -4471,7 +2931,7 @@ function registerSkillsInstall(parent) {
|
|
|
4471
2931
|
sourceType = "library";
|
|
4472
2932
|
if (format === "human") {
|
|
4473
2933
|
console.log(
|
|
4474
|
-
` Found in catalog: ${
|
|
2934
|
+
` Found in catalog: ${pc11.bold(catalogSkill.name)} v${catalogSkill.version} (${pc11.dim(catalogSkill.category)})`
|
|
4475
2935
|
);
|
|
4476
2936
|
}
|
|
4477
2937
|
} else {
|
|
@@ -4488,8 +2948,8 @@ function registerSkillsInstall(parent) {
|
|
|
4488
2948
|
}
|
|
4489
2949
|
);
|
|
4490
2950
|
}
|
|
4491
|
-
console.error(
|
|
4492
|
-
console.log(
|
|
2951
|
+
console.error(pc11.red(message));
|
|
2952
|
+
console.log(pc11.dim("Available skills: " + listSkills().join(", ")));
|
|
4493
2953
|
process.exit(1);
|
|
4494
2954
|
}
|
|
4495
2955
|
} else {
|
|
@@ -4503,7 +2963,7 @@ function registerSkillsInstall(parent) {
|
|
|
4503
2963
|
ErrorCategories.VALIDATION
|
|
4504
2964
|
);
|
|
4505
2965
|
}
|
|
4506
|
-
console.error(
|
|
2966
|
+
console.error(pc11.red(message));
|
|
4507
2967
|
process.exit(1);
|
|
4508
2968
|
}
|
|
4509
2969
|
}
|
|
@@ -4519,7 +2979,7 @@ function registerSkillsInstall(parent) {
|
|
|
4519
2979
|
ErrorCategories.INTERNAL
|
|
4520
2980
|
);
|
|
4521
2981
|
}
|
|
4522
|
-
console.error(
|
|
2982
|
+
console.error(pc11.red(message));
|
|
4523
2983
|
process.exit(1);
|
|
4524
2984
|
}
|
|
4525
2985
|
const result = await installSkill(localPath, skillName, providers, opts.global ?? false);
|
|
@@ -4552,14 +3012,14 @@ function registerSkillsInstall(parent) {
|
|
|
4552
3012
|
if (format === "json") {
|
|
4553
3013
|
outputSuccess(operation, mvi, summary);
|
|
4554
3014
|
} else {
|
|
4555
|
-
console.log(
|
|
4556
|
-
\u2713 Installed ${
|
|
4557
|
-
console.log(` Canonical: ${
|
|
3015
|
+
console.log(pc11.green(`
|
|
3016
|
+
\u2713 Installed ${pc11.bold(skillName)}`));
|
|
3017
|
+
console.log(` Canonical: ${pc11.dim(result.canonicalPath)}`);
|
|
4558
3018
|
console.log(` Linked to: ${result.linkedAgents.join(", ")}`);
|
|
4559
3019
|
if (result.errors.length > 0) {
|
|
4560
|
-
console.log(
|
|
3020
|
+
console.log(pc11.yellow("\nWarnings:"));
|
|
4561
3021
|
for (const err of result.errors) {
|
|
4562
|
-
console.log(` ${
|
|
3022
|
+
console.log(` ${pc11.yellow("!")} ${err}`);
|
|
4563
3023
|
}
|
|
4564
3024
|
}
|
|
4565
3025
|
}
|
|
@@ -4589,11 +3049,11 @@ function registerSkillsInstall(parent) {
|
|
|
4589
3049
|
});
|
|
4590
3050
|
console.error(JSON.stringify(envelope, null, 2));
|
|
4591
3051
|
} else {
|
|
4592
|
-
console.log(
|
|
4593
|
-
\u2717 Failed to install ${
|
|
4594
|
-
console.log(
|
|
3052
|
+
console.log(pc11.yellow(`
|
|
3053
|
+
\u2717 Failed to install ${pc11.bold(skillName)}`));
|
|
3054
|
+
console.log(pc11.yellow("Errors:"));
|
|
4595
3055
|
for (const err of result.errors) {
|
|
4596
|
-
console.log(` ${
|
|
3056
|
+
console.log(` ${pc11.yellow("!")} ${err}`);
|
|
4597
3057
|
}
|
|
4598
3058
|
}
|
|
4599
3059
|
process.exit(1);
|
|
@@ -4610,7 +3070,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
4610
3070
|
if (format === "json") {
|
|
4611
3071
|
emitError2(operation, mvi, ErrorCodes.INVALID_INPUT, message, ErrorCategories.VALIDATION);
|
|
4612
3072
|
}
|
|
4613
|
-
console.error(
|
|
3073
|
+
console.error(pc11.red(message));
|
|
4614
3074
|
process.exit(1);
|
|
4615
3075
|
}
|
|
4616
3076
|
const profileSkills = resolveProfile(profileName);
|
|
@@ -4628,16 +3088,16 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
4628
3088
|
}
|
|
4629
3089
|
);
|
|
4630
3090
|
}
|
|
4631
|
-
console.error(
|
|
3091
|
+
console.error(pc11.red(message));
|
|
4632
3092
|
const available = listProfiles();
|
|
4633
3093
|
if (available.length > 0) {
|
|
4634
|
-
console.log(
|
|
3094
|
+
console.log(pc11.dim("Available profiles: " + available.join(", ")));
|
|
4635
3095
|
}
|
|
4636
3096
|
process.exit(1);
|
|
4637
3097
|
}
|
|
4638
3098
|
if (format === "human") {
|
|
4639
|
-
console.log(`Installing profile ${
|
|
4640
|
-
console.log(
|
|
3099
|
+
console.log(`Installing profile ${pc11.bold(profileName)} (${profileSkills.length} skill(s))...`);
|
|
3100
|
+
console.log(pc11.dim(`Target: ${providers.length} provider(s)`));
|
|
4641
3101
|
}
|
|
4642
3102
|
const installed = [];
|
|
4643
3103
|
const failed = [];
|
|
@@ -4647,7 +3107,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
4647
3107
|
const result = await installSkill(skillDir, name, providers, isGlobal);
|
|
4648
3108
|
if (result.success) {
|
|
4649
3109
|
if (format === "human") {
|
|
4650
|
-
console.log(
|
|
3110
|
+
console.log(pc11.green(` + ${name}`));
|
|
4651
3111
|
}
|
|
4652
3112
|
await recordSkillInstall(
|
|
4653
3113
|
name,
|
|
@@ -4666,7 +3126,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
4666
3126
|
});
|
|
4667
3127
|
} else {
|
|
4668
3128
|
if (format === "human") {
|
|
4669
|
-
console.log(
|
|
3129
|
+
console.log(pc11.yellow(` ! ${name}: ${result.errors.join(", ")}`));
|
|
4670
3130
|
}
|
|
4671
3131
|
failed.push({
|
|
4672
3132
|
name,
|
|
@@ -4676,7 +3136,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
4676
3136
|
} catch (err) {
|
|
4677
3137
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
4678
3138
|
if (format === "human") {
|
|
4679
|
-
console.log(
|
|
3139
|
+
console.log(pc11.red(` x ${name}: ${errorMsg}`));
|
|
4680
3140
|
}
|
|
4681
3141
|
failed.push({
|
|
4682
3142
|
name,
|
|
@@ -4711,7 +3171,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
4711
3171
|
} else {
|
|
4712
3172
|
console.log(
|
|
4713
3173
|
`
|
|
4714
|
-
${
|
|
3174
|
+
${pc11.green(`${installed.length} installed`)}, ${failed.length > 0 ? pc11.yellow(`${failed.length} failed`) : "0 failed"}`
|
|
4715
3175
|
);
|
|
4716
3176
|
if (failed.length > 0) {
|
|
4717
3177
|
process.exit(1);
|
|
@@ -4720,7 +3180,7 @@ ${pc16.green(`${installed.length} installed`)}, ${failed.length > 0 ? pc16.yello
|
|
|
4720
3180
|
}
|
|
4721
3181
|
async function handleMarketplaceSource(source, _providers, _isGlobal, format, operation, mvi) {
|
|
4722
3182
|
if (format === "human") {
|
|
4723
|
-
console.log(
|
|
3183
|
+
console.log(pc11.dim(`Searching marketplace for ${source}...`));
|
|
4724
3184
|
}
|
|
4725
3185
|
const client = new MarketplaceClient();
|
|
4726
3186
|
let skill;
|
|
@@ -4731,7 +3191,7 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
|
|
|
4731
3191
|
if (format === "json") {
|
|
4732
3192
|
emitJsonError(operation, mvi, ErrorCodes.NETWORK_ERROR, message, ErrorCategories.TRANSIENT);
|
|
4733
3193
|
}
|
|
4734
|
-
console.error(
|
|
3194
|
+
console.error(pc11.red(message));
|
|
4735
3195
|
return { success: false };
|
|
4736
3196
|
}
|
|
4737
3197
|
if (!skill) {
|
|
@@ -4739,12 +3199,12 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
|
|
|
4739
3199
|
if (format === "json") {
|
|
4740
3200
|
emitJsonError(operation, mvi, ErrorCodes.SKILL_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
|
|
4741
3201
|
}
|
|
4742
|
-
console.error(
|
|
3202
|
+
console.error(pc11.red(message));
|
|
4743
3203
|
return { success: false };
|
|
4744
3204
|
}
|
|
4745
3205
|
if (format === "human") {
|
|
4746
3206
|
console.log(
|
|
4747
|
-
` Found: ${
|
|
3207
|
+
` Found: ${pc11.bold(skill.name)} by ${skill.author} (${pc11.dim(skill.repoFullName)})`
|
|
4748
3208
|
);
|
|
4749
3209
|
}
|
|
4750
3210
|
const parsed = parseSource(skill.githubUrl);
|
|
@@ -4753,7 +3213,7 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
|
|
|
4753
3213
|
if (format === "json") {
|
|
4754
3214
|
emitJsonError(operation, mvi, ErrorCodes.INVALID_FORMAT, message, ErrorCategories.VALIDATION);
|
|
4755
3215
|
}
|
|
4756
|
-
console.error(
|
|
3216
|
+
console.error(pc11.red(message));
|
|
4757
3217
|
return { success: false };
|
|
4758
3218
|
}
|
|
4759
3219
|
try {
|
|
@@ -4765,7 +3225,7 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
|
|
|
4765
3225
|
for (const subPath of subPathCandidates) {
|
|
4766
3226
|
try {
|
|
4767
3227
|
const result = await cloneRepo(parsed.owner, parsed.repo, parsed.ref, subPath);
|
|
4768
|
-
if (subPath && !
|
|
3228
|
+
if (subPath && !existsSync5(result.localPath)) {
|
|
4769
3229
|
await result.cleanup();
|
|
4770
3230
|
continue;
|
|
4771
3231
|
}
|
|
@@ -4793,7 +3253,7 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
|
|
|
4793
3253
|
if (format === "json") {
|
|
4794
3254
|
emitJsonError(operation, mvi, ErrorCodes.NETWORK_ERROR, message, ErrorCategories.TRANSIENT);
|
|
4795
3255
|
}
|
|
4796
|
-
console.error(
|
|
3256
|
+
console.error(pc11.red(message));
|
|
4797
3257
|
return { success: false };
|
|
4798
3258
|
}
|
|
4799
3259
|
}
|
|
@@ -4801,7 +3261,7 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
|
|
|
4801
3261
|
// src/commands/skills/list.ts
|
|
4802
3262
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
4803
3263
|
import { resolveOutputFormat as resolveOutputFormat4 } from "@cleocode/lafs";
|
|
4804
|
-
import
|
|
3264
|
+
import pc12 from "picocolors";
|
|
4805
3265
|
function registerSkillsList(parent) {
|
|
4806
3266
|
parent.command("list").description("List installed skills").option("-g, --global", "List global skills").option("-a, --agent <name>", "List skills for specific agent").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
4807
3267
|
const operation = "skills.list";
|
|
@@ -4828,7 +3288,7 @@ function registerSkillsList(parent) {
|
|
|
4828
3288
|
agent: opts.agent
|
|
4829
3289
|
});
|
|
4830
3290
|
} else {
|
|
4831
|
-
console.error(
|
|
3291
|
+
console.error(pc12.red(message));
|
|
4832
3292
|
}
|
|
4833
3293
|
process.exit(1);
|
|
4834
3294
|
}
|
|
@@ -4856,21 +3316,21 @@ function registerSkillsList(parent) {
|
|
|
4856
3316
|
return;
|
|
4857
3317
|
}
|
|
4858
3318
|
if (skills.length === 0) {
|
|
4859
|
-
console.log(
|
|
3319
|
+
console.log(pc12.dim("No skills found."));
|
|
4860
3320
|
return;
|
|
4861
3321
|
}
|
|
4862
|
-
console.log(
|
|
3322
|
+
console.log(pc12.bold(`
|
|
4863
3323
|
${skills.length} skill(s) found:
|
|
4864
3324
|
`));
|
|
4865
3325
|
skills.forEach((skill, index) => {
|
|
4866
3326
|
const num = (index + 1).toString().padStart(2);
|
|
4867
3327
|
console.log(
|
|
4868
|
-
` ${
|
|
3328
|
+
` ${pc12.cyan(num)}. ${pc12.bold(skill.name.padEnd(30))} ${pc12.dim(skill.metadata?.description ?? "")}`
|
|
4869
3329
|
);
|
|
4870
3330
|
});
|
|
4871
|
-
console.log(
|
|
3331
|
+
console.log(pc12.dim(`
|
|
4872
3332
|
Install with: caamp skills install <name>`));
|
|
4873
|
-
console.log(
|
|
3333
|
+
console.log(pc12.dim(`Remove with: caamp skills remove <name>`));
|
|
4874
3334
|
});
|
|
4875
3335
|
}
|
|
4876
3336
|
function buildEnvelope4(operation, mvi, result, error) {
|
|
@@ -4906,7 +3366,7 @@ function emitJsonError4(operation, mvi, code, message, category, details = {}) {
|
|
|
4906
3366
|
}
|
|
4907
3367
|
|
|
4908
3368
|
// src/commands/skills/remove.ts
|
|
4909
|
-
import
|
|
3369
|
+
import pc13 from "picocolors";
|
|
4910
3370
|
function registerSkillsRemove(parent) {
|
|
4911
3371
|
parent.command("remove").description("Remove installed skill(s)").argument("[name]", "Skill name to remove").option("-g, --global", "Remove from global scope").option("-y, --yes", "Skip confirmation").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(
|
|
4912
3372
|
async (name, opts) => {
|
|
@@ -4952,14 +3412,14 @@ function registerSkillsRemove(parent) {
|
|
|
4952
3412
|
return;
|
|
4953
3413
|
}
|
|
4954
3414
|
if (removed.length > 0) {
|
|
4955
|
-
console.log(
|
|
3415
|
+
console.log(pc13.green(`\u2713 Removed ${pc13.bold(name)} from: ${removed.join(", ")}`));
|
|
4956
3416
|
await removeSkillFromLock(name);
|
|
4957
3417
|
} else {
|
|
4958
|
-
console.log(
|
|
3418
|
+
console.log(pc13.yellow(`Skill ${name} not found in any provider.`));
|
|
4959
3419
|
}
|
|
4960
3420
|
if (result.errors.length > 0) {
|
|
4961
3421
|
for (const err of result.errors) {
|
|
4962
|
-
console.log(
|
|
3422
|
+
console.log(pc13.red(` ${err}`));
|
|
4963
3423
|
}
|
|
4964
3424
|
}
|
|
4965
3425
|
} else {
|
|
@@ -4972,7 +3432,7 @@ function registerSkillsRemove(parent) {
|
|
|
4972
3432
|
count: { removed: 0, total: 0 }
|
|
4973
3433
|
});
|
|
4974
3434
|
} else {
|
|
4975
|
-
console.log(
|
|
3435
|
+
console.log(pc13.dim("No skills installed."));
|
|
4976
3436
|
}
|
|
4977
3437
|
return;
|
|
4978
3438
|
}
|
|
@@ -4985,18 +3445,18 @@ function registerSkillsRemove(parent) {
|
|
|
4985
3445
|
});
|
|
4986
3446
|
return;
|
|
4987
3447
|
}
|
|
4988
|
-
console.log(
|
|
3448
|
+
console.log(pc13.bold("Installed skills:"));
|
|
4989
3449
|
for (const s of skills) {
|
|
4990
3450
|
console.log(` ${s}`);
|
|
4991
3451
|
}
|
|
4992
|
-
console.log(
|
|
3452
|
+
console.log(pc13.dim("\nUse: caamp skills remove <name>"));
|
|
4993
3453
|
}
|
|
4994
3454
|
}
|
|
4995
3455
|
);
|
|
4996
3456
|
}
|
|
4997
3457
|
|
|
4998
3458
|
// src/commands/skills/update.ts
|
|
4999
|
-
import
|
|
3459
|
+
import pc14 from "picocolors";
|
|
5000
3460
|
function registerSkillsUpdate(parent) {
|
|
5001
3461
|
parent.command("update").description("Update all outdated skills").option("-y, --yes", "Skip confirmation").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
5002
3462
|
const operation = "skills.update";
|
|
@@ -5030,12 +3490,12 @@ function registerSkillsUpdate(parent) {
|
|
|
5030
3490
|
count: { updated: 0, failed: 0, skipped: 0 }
|
|
5031
3491
|
});
|
|
5032
3492
|
} else {
|
|
5033
|
-
console.log(
|
|
3493
|
+
console.log(pc14.dim("No tracked skills to update."));
|
|
5034
3494
|
}
|
|
5035
3495
|
return;
|
|
5036
3496
|
}
|
|
5037
3497
|
if (format === "human") {
|
|
5038
|
-
console.log(
|
|
3498
|
+
console.log(pc14.dim(`Checking ${entries.length} skill(s) for updates...`));
|
|
5039
3499
|
}
|
|
5040
3500
|
const outdated = [];
|
|
5041
3501
|
for (const [name] of entries) {
|
|
@@ -5057,19 +3517,19 @@ function registerSkillsUpdate(parent) {
|
|
|
5057
3517
|
count: { updated: 0, failed: 0, skipped: 0 }
|
|
5058
3518
|
});
|
|
5059
3519
|
} else {
|
|
5060
|
-
console.log(
|
|
3520
|
+
console.log(pc14.green("\nAll skills are up to date."));
|
|
5061
3521
|
}
|
|
5062
3522
|
return;
|
|
5063
3523
|
}
|
|
5064
3524
|
if (format === "human") {
|
|
5065
|
-
console.log(
|
|
3525
|
+
console.log(pc14.yellow(`
|
|
5066
3526
|
${outdated.length} skill(s) have updates available:
|
|
5067
3527
|
`));
|
|
5068
3528
|
for (const skill of outdated) {
|
|
5069
3529
|
const current = skill.currentVersion?.slice(0, 12) ?? "?";
|
|
5070
3530
|
const latest = skill.latestVersion ?? "?";
|
|
5071
3531
|
console.log(
|
|
5072
|
-
` ${
|
|
3532
|
+
` ${pc14.bold(skill.name)} ${pc14.dim(current)} ${pc14.dim("->")} ${pc14.cyan(latest)}`
|
|
5073
3533
|
);
|
|
5074
3534
|
}
|
|
5075
3535
|
}
|
|
@@ -5077,11 +3537,11 @@ ${outdated.length} skill(s) have updates available:
|
|
|
5077
3537
|
const readline = await import("readline");
|
|
5078
3538
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
5079
3539
|
const answer = await new Promise((resolve) => {
|
|
5080
|
-
rl.question(
|
|
3540
|
+
rl.question(pc14.dim("\nProceed with update? [y/N] "), resolve);
|
|
5081
3541
|
});
|
|
5082
3542
|
rl.close();
|
|
5083
3543
|
if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
|
|
5084
|
-
console.log(
|
|
3544
|
+
console.log(pc14.dim("Update cancelled."));
|
|
5085
3545
|
return;
|
|
5086
3546
|
}
|
|
5087
3547
|
}
|
|
@@ -5095,7 +3555,7 @@ ${outdated.length} skill(s) have updates available:
|
|
|
5095
3555
|
const entry = tracked[skill.name];
|
|
5096
3556
|
if (!entry) continue;
|
|
5097
3557
|
if (format === "human") {
|
|
5098
|
-
console.log(
|
|
3558
|
+
console.log(pc14.dim(`Updating ${pc14.bold(skill.name)}...`));
|
|
5099
3559
|
}
|
|
5100
3560
|
try {
|
|
5101
3561
|
const parsed = parseSource(entry.source);
|
|
@@ -5117,7 +3577,7 @@ ${outdated.length} skill(s) have updates available:
|
|
|
5117
3577
|
} else {
|
|
5118
3578
|
if (format === "human") {
|
|
5119
3579
|
console.log(
|
|
5120
|
-
|
|
3580
|
+
pc14.yellow(
|
|
5121
3581
|
` Skipped ${skill.name}: source type "${parsed.type}" does not support auto-update`
|
|
5122
3582
|
)
|
|
5123
3583
|
);
|
|
@@ -5129,7 +3589,7 @@ ${outdated.length} skill(s) have updates available:
|
|
|
5129
3589
|
const providers = entry.agents.map((a) => getProvider(a)).filter((p) => p !== void 0);
|
|
5130
3590
|
if (providers.length === 0) {
|
|
5131
3591
|
if (format === "human") {
|
|
5132
|
-
console.log(
|
|
3592
|
+
console.log(pc14.yellow(` Skipped ${skill.name}: no valid providers found`));
|
|
5133
3593
|
}
|
|
5134
3594
|
skipped.push(skill.name);
|
|
5135
3595
|
continue;
|
|
@@ -5154,18 +3614,18 @@ ${outdated.length} skill(s) have updates available:
|
|
|
5154
3614
|
skill.latestVersion
|
|
5155
3615
|
);
|
|
5156
3616
|
if (format === "human") {
|
|
5157
|
-
console.log(
|
|
3617
|
+
console.log(pc14.green(` Updated ${pc14.bold(skill.name)}`));
|
|
5158
3618
|
}
|
|
5159
3619
|
updated.push(skill.name);
|
|
5160
3620
|
} else {
|
|
5161
3621
|
if (format === "human") {
|
|
5162
|
-
console.log(
|
|
3622
|
+
console.log(pc14.red(` Failed to update ${skill.name}: no agents linked`));
|
|
5163
3623
|
}
|
|
5164
3624
|
failed.push({ name: skill.name, error: "no agents linked" });
|
|
5165
3625
|
}
|
|
5166
3626
|
if (installResult.errors.length > 0 && format === "human") {
|
|
5167
3627
|
for (const err of installResult.errors) {
|
|
5168
|
-
console.log(
|
|
3628
|
+
console.log(pc14.yellow(` ${err}`));
|
|
5169
3629
|
}
|
|
5170
3630
|
}
|
|
5171
3631
|
} finally {
|
|
@@ -5174,7 +3634,7 @@ ${outdated.length} skill(s) have updates available:
|
|
|
5174
3634
|
} catch (err) {
|
|
5175
3635
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5176
3636
|
if (format === "human") {
|
|
5177
|
-
console.log(
|
|
3637
|
+
console.log(pc14.red(` Failed to update ${skill.name}: ${msg}`));
|
|
5178
3638
|
}
|
|
5179
3639
|
failed.push({ name: skill.name, error: msg });
|
|
5180
3640
|
}
|
|
@@ -5194,17 +3654,17 @@ ${outdated.length} skill(s) have updates available:
|
|
|
5194
3654
|
}
|
|
5195
3655
|
console.log();
|
|
5196
3656
|
if (updated.length > 0) {
|
|
5197
|
-
console.log(
|
|
3657
|
+
console.log(pc14.green(`Updated ${updated.length} skill(s).`));
|
|
5198
3658
|
}
|
|
5199
3659
|
if (failed.length > 0) {
|
|
5200
|
-
console.log(
|
|
3660
|
+
console.log(pc14.red(`Failed to update ${failed.length} skill(s).`));
|
|
5201
3661
|
}
|
|
5202
3662
|
});
|
|
5203
3663
|
}
|
|
5204
3664
|
|
|
5205
3665
|
// src/commands/skills/validate.ts
|
|
5206
3666
|
import { resolveOutputFormat as resolveOutputFormat5 } from "@cleocode/lafs";
|
|
5207
|
-
import
|
|
3667
|
+
import pc15 from "picocolors";
|
|
5208
3668
|
function registerSkillsValidate(parent) {
|
|
5209
3669
|
parent.command("validate").description("Validate SKILL.md format").argument("[path]", "Path to SKILL.md", "SKILL.md").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (path, opts) => {
|
|
5210
3670
|
const operation = "skills.validate";
|
|
@@ -5244,7 +3704,7 @@ function registerSkillsValidate(parent) {
|
|
|
5244
3704
|
}
|
|
5245
3705
|
);
|
|
5246
3706
|
} else {
|
|
5247
|
-
console.error(
|
|
3707
|
+
console.error(pc15.red(message));
|
|
5248
3708
|
}
|
|
5249
3709
|
process.exit(1);
|
|
5250
3710
|
}
|
|
@@ -5266,12 +3726,12 @@ function registerSkillsValidate(parent) {
|
|
|
5266
3726
|
console.log(JSON.stringify(envelope, null, 2));
|
|
5267
3727
|
} else {
|
|
5268
3728
|
if (result.valid) {
|
|
5269
|
-
console.log(
|
|
3729
|
+
console.log(pc15.green(`\u2713 ${path} is valid`));
|
|
5270
3730
|
} else {
|
|
5271
|
-
console.log(
|
|
3731
|
+
console.log(pc15.red(`\u2717 ${path} has validation errors`));
|
|
5272
3732
|
}
|
|
5273
3733
|
for (const issue of result.issues) {
|
|
5274
|
-
const icon = issue.level === "error" ?
|
|
3734
|
+
const icon = issue.level === "error" ? pc15.red("\u2717") : pc15.yellow("!");
|
|
5275
3735
|
console.log(` ${icon} [${issue.field}] ${issue.message}`);
|
|
5276
3736
|
}
|
|
5277
3737
|
}
|
|
@@ -5306,8 +3766,6 @@ program.hook("preAction", (thisCommand) => {
|
|
|
5306
3766
|
});
|
|
5307
3767
|
registerProvidersCommand(program);
|
|
5308
3768
|
registerSkillsCommands(program);
|
|
5309
|
-
registerMcpCommands(program);
|
|
5310
|
-
registerCleoCommands(program);
|
|
5311
3769
|
registerInstructionsCommands(program);
|
|
5312
3770
|
registerConfigCommand(program);
|
|
5313
3771
|
registerDoctorCommand(program);
|