@cleocode/caamp 2026.5.83 → 2026.5.86
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-OCI4RYI5.js → chunk-H5YXR2DC.js} +31 -2
- package/dist/chunk-H5YXR2DC.js.map +1 -0
- package/dist/{chunk-OPUPNLUJ.js → chunk-PGETUTYZ.js} +2 -2
- package/dist/{chunk-SV33CE5X.js → chunk-QKC2JRSG.js} +2 -2
- package/dist/{chunk-4W35AEYA.js → chunk-WXL7RPS4.js} +865 -75
- package/dist/chunk-WXL7RPS4.js.map +1 -0
- package/dist/cli.js +614 -245
- package/dist/cli.js.map +1 -1
- package/dist/{hooks-5EXBKPII.js → hooks-ZN3C4WCK.js} +3 -3
- package/dist/index.d.ts +731 -10
- package/dist/index.js +29 -5
- package/dist/index.js.map +1 -1
- package/dist/{injector-F4EHB3CZ.js → injector-QPOIJVOR.js} +3 -3
- package/package.json +5 -5
- package/dist/chunk-4W35AEYA.js.map +0 -1
- package/dist/chunk-OCI4RYI5.js.map +0 -1
- /package/dist/{chunk-OPUPNLUJ.js.map → chunk-PGETUTYZ.js.map} +0 -0
- /package/dist/{chunk-SV33CE5X.js.map → chunk-QKC2JRSG.js.map} +0 -0
- /package/dist/{hooks-5EXBKPII.js.map → hooks-ZN3C4WCK.js.map} +0 -0
- /package/dist/{injector-F4EHB3CZ.js.map → injector-QPOIJVOR.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
CANONICAL_SKILLS_DIR,
|
|
4
|
+
ErrorCategories,
|
|
5
|
+
ErrorCodes,
|
|
4
6
|
MarketplaceClient,
|
|
5
7
|
PiHarness,
|
|
6
8
|
RECOMMENDATION_ERROR_CODES,
|
|
9
|
+
buildEnvelope,
|
|
7
10
|
checkSkillUpdate,
|
|
8
11
|
detectAllProviders,
|
|
9
12
|
detectMcpInstallations,
|
|
@@ -12,6 +15,8 @@ import {
|
|
|
12
15
|
discoverSkillsMulti,
|
|
13
16
|
dispatchInstallSkillAcrossProviders,
|
|
14
17
|
dispatchRemoveSkillAcrossProviders,
|
|
18
|
+
emitError,
|
|
19
|
+
emitJsonError,
|
|
15
20
|
fetchWithTimeout,
|
|
16
21
|
formatNetworkError,
|
|
17
22
|
formatSkillRecommendations,
|
|
@@ -20,27 +25,31 @@ import {
|
|
|
20
25
|
getSkill,
|
|
21
26
|
getSkillDir,
|
|
22
27
|
getTrackedSkills,
|
|
28
|
+
handleFormatError,
|
|
23
29
|
installBatchWithRollback,
|
|
24
30
|
installMcpServer,
|
|
25
31
|
isCatalogAvailable,
|
|
26
32
|
isHuman,
|
|
27
33
|
isMarketplaceScoped,
|
|
28
|
-
isQuiet,
|
|
29
34
|
isVerbose,
|
|
30
35
|
listAllMcpServers,
|
|
31
36
|
listCanonicalSkills,
|
|
32
37
|
listMcpServers,
|
|
33
38
|
listProfiles,
|
|
34
39
|
listSkills,
|
|
40
|
+
outputSuccess,
|
|
35
41
|
parseSource,
|
|
36
42
|
readConfig,
|
|
37
43
|
readLockFile,
|
|
38
44
|
recommendSkills,
|
|
39
45
|
recordSkillInstall,
|
|
46
|
+
registerDoctorBridge,
|
|
47
|
+
registerSkillsDoctorAdopt,
|
|
40
48
|
removeMcpServer,
|
|
41
49
|
removeMcpServerFromAll,
|
|
42
50
|
removeSkillFromLock,
|
|
43
51
|
resolveDefaultTargetProviders,
|
|
52
|
+
resolveFormat,
|
|
44
53
|
resolveProfile,
|
|
45
54
|
scanDirectory,
|
|
46
55
|
scanFile,
|
|
@@ -52,7 +61,7 @@ import {
|
|
|
52
61
|
tokenizeCriteriaValue,
|
|
53
62
|
updateInstructionsSingleOperation,
|
|
54
63
|
validateSkill
|
|
55
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-WXL7RPS4.js";
|
|
56
65
|
import {
|
|
57
66
|
buildSkillsMap,
|
|
58
67
|
checkAllInjections,
|
|
@@ -65,7 +74,7 @@ import {
|
|
|
65
74
|
groupByInstructFile,
|
|
66
75
|
injectAll,
|
|
67
76
|
providerSupports
|
|
68
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-PGETUTYZ.js";
|
|
69
78
|
import {
|
|
70
79
|
CANONICAL_HOOK_EVENTS,
|
|
71
80
|
buildHookMatrix,
|
|
@@ -74,12 +83,12 @@ import {
|
|
|
74
83
|
getHookSupport,
|
|
75
84
|
getProviderSummary,
|
|
76
85
|
translateToAll
|
|
77
|
-
} from "./chunk-
|
|
86
|
+
} from "./chunk-QKC2JRSG.js";
|
|
78
87
|
import {
|
|
79
88
|
buildSkillSubPathCandidates,
|
|
80
89
|
resolveProviderConfigPath,
|
|
81
90
|
resolveProviderSkillsDir
|
|
82
|
-
} from "./chunk-
|
|
91
|
+
} from "./chunk-H5YXR2DC.js";
|
|
83
92
|
|
|
84
93
|
// src/cli.ts
|
|
85
94
|
import { Command } from "commander";
|
|
@@ -153,7 +162,7 @@ function emitSuccess(operation, result, mvi = "standard") {
|
|
|
153
162
|
};
|
|
154
163
|
console.log(JSON.stringify(envelope, null, 2));
|
|
155
164
|
}
|
|
156
|
-
function
|
|
165
|
+
function emitError2(operation, error, mvi = "standard") {
|
|
157
166
|
let envelope;
|
|
158
167
|
if (error instanceof LAFSCommandError) {
|
|
159
168
|
envelope = {
|
|
@@ -204,7 +213,7 @@ async function runLafsCommand(command, mvi, action) {
|
|
|
204
213
|
const result = await action();
|
|
205
214
|
emitSuccess(command, result, mvi);
|
|
206
215
|
} catch (error) {
|
|
207
|
-
|
|
216
|
+
emitError2(command, error, mvi);
|
|
208
217
|
process.exit(1);
|
|
209
218
|
}
|
|
210
219
|
}
|
|
@@ -499,117 +508,6 @@ function registerAdvancedCommands(program2) {
|
|
|
499
508
|
// src/commands/config.ts
|
|
500
509
|
import { existsSync } from "fs";
|
|
501
510
|
import pc from "picocolors";
|
|
502
|
-
|
|
503
|
-
// src/core/lafs.ts
|
|
504
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
505
|
-
import { resolveOutputFormat } from "@cleocode/lafs";
|
|
506
|
-
function resolveFormat(options) {
|
|
507
|
-
return resolveOutputFormat({
|
|
508
|
-
jsonFlag: options.jsonFlag ?? false,
|
|
509
|
-
humanFlag: (options.humanFlag ?? false) || isHuman(),
|
|
510
|
-
projectDefault: options.projectDefault ?? "json"
|
|
511
|
-
}).format;
|
|
512
|
-
}
|
|
513
|
-
function buildEnvelope(operation, mvi, result, error, page = null, sessionId, warnings) {
|
|
514
|
-
return {
|
|
515
|
-
$schema: "https://lafs.dev/schemas/v1/envelope.schema.json",
|
|
516
|
-
_meta: {
|
|
517
|
-
specVersion: "1.0.0",
|
|
518
|
-
schemaVersion: "1.0.0",
|
|
519
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
520
|
-
operation,
|
|
521
|
-
requestId: randomUUID2(),
|
|
522
|
-
transport: "cli",
|
|
523
|
-
strict: true,
|
|
524
|
-
mvi,
|
|
525
|
-
contextVersion: 0,
|
|
526
|
-
...sessionId && { sessionId },
|
|
527
|
-
...warnings && warnings.length > 0 && { warnings }
|
|
528
|
-
},
|
|
529
|
-
success: error === null,
|
|
530
|
-
result,
|
|
531
|
-
error,
|
|
532
|
-
page
|
|
533
|
-
};
|
|
534
|
-
}
|
|
535
|
-
function emitError2(operation, mvi, code, message, category, details = {}, exitCode = 1) {
|
|
536
|
-
const envelope = buildEnvelope(operation, mvi, null, {
|
|
537
|
-
code,
|
|
538
|
-
message,
|
|
539
|
-
category,
|
|
540
|
-
retryable: category === "TRANSIENT" || category === "RATE_LIMIT",
|
|
541
|
-
retryAfterMs: null,
|
|
542
|
-
details
|
|
543
|
-
});
|
|
544
|
-
console.error(JSON.stringify(envelope, null, 2));
|
|
545
|
-
process.exit(exitCode);
|
|
546
|
-
}
|
|
547
|
-
function emitJsonError(operation, mvi, code, message, category, details = {}) {
|
|
548
|
-
const envelope = buildEnvelope(operation, mvi, null, {
|
|
549
|
-
code,
|
|
550
|
-
message,
|
|
551
|
-
category,
|
|
552
|
-
retryable: category === "TRANSIENT" || category === "RATE_LIMIT",
|
|
553
|
-
retryAfterMs: null,
|
|
554
|
-
details
|
|
555
|
-
});
|
|
556
|
-
console.error(JSON.stringify(envelope, null, 2));
|
|
557
|
-
}
|
|
558
|
-
function outputSuccess(operation, mvi, result, page, sessionId, warnings) {
|
|
559
|
-
const envelope = buildEnvelope(operation, mvi, result, null, page ?? null, sessionId, warnings);
|
|
560
|
-
if (isQuiet() && !envelope.error) {
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
console.log(JSON.stringify(envelope, null, 2));
|
|
564
|
-
}
|
|
565
|
-
function handleFormatError(error, operation, mvi, jsonFlag) {
|
|
566
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
567
|
-
if (jsonFlag) {
|
|
568
|
-
emitJsonError(operation, mvi, "E_FORMAT_CONFLICT", message, "VALIDATION");
|
|
569
|
-
} else {
|
|
570
|
-
console.error(message);
|
|
571
|
-
}
|
|
572
|
-
process.exit(1);
|
|
573
|
-
}
|
|
574
|
-
var ErrorCategories = {
|
|
575
|
-
VALIDATION: "VALIDATION",
|
|
576
|
-
AUTH: "AUTH",
|
|
577
|
-
PERMISSION: "PERMISSION",
|
|
578
|
-
NOT_FOUND: "NOT_FOUND",
|
|
579
|
-
CONFLICT: "CONFLICT",
|
|
580
|
-
RATE_LIMIT: "RATE_LIMIT",
|
|
581
|
-
TRANSIENT: "TRANSIENT",
|
|
582
|
-
INTERNAL: "INTERNAL",
|
|
583
|
-
CONTRACT: "CONTRACT",
|
|
584
|
-
MIGRATION: "MIGRATION"
|
|
585
|
-
};
|
|
586
|
-
var ErrorCodes = {
|
|
587
|
-
// Format errors
|
|
588
|
-
FORMAT_CONFLICT: "E_FORMAT_CONFLICT",
|
|
589
|
-
INVALID_JSON: "E_INVALID_JSON",
|
|
590
|
-
// Not found errors
|
|
591
|
-
SKILL_NOT_FOUND: "E_SKILL_NOT_FOUND",
|
|
592
|
-
PROVIDER_NOT_FOUND: "E_PROVIDER_NOT_FOUND",
|
|
593
|
-
MCP_SERVER_NOT_FOUND: "E_MCP_SERVER_NOT_FOUND",
|
|
594
|
-
FILE_NOT_FOUND: "E_FILE_NOT_FOUND",
|
|
595
|
-
// Validation errors
|
|
596
|
-
INVALID_INPUT: "E_INVALID_INPUT",
|
|
597
|
-
INVALID_CONSTRAINT: "E_INVALID_CONSTRAINT",
|
|
598
|
-
INVALID_FORMAT: "E_INVALID_FORMAT",
|
|
599
|
-
// Operation errors
|
|
600
|
-
INSTALL_FAILED: "E_INSTALL_FAILED",
|
|
601
|
-
REMOVE_FAILED: "E_REMOVE_FAILED",
|
|
602
|
-
UPDATE_FAILED: "E_UPDATE_FAILED",
|
|
603
|
-
VALIDATION_FAILED: "E_VALIDATION_FAILED",
|
|
604
|
-
AUDIT_FAILED: "E_AUDIT_FAILED",
|
|
605
|
-
// System errors
|
|
606
|
-
NETWORK_ERROR: "E_NETWORK_ERROR",
|
|
607
|
-
FILE_SYSTEM_ERROR: "E_FILE_SYSTEM_ERROR",
|
|
608
|
-
PERMISSION_DENIED: "E_PERMISSION_DENIED",
|
|
609
|
-
INTERNAL_ERROR: "E_INTERNAL_ERROR"
|
|
610
|
-
};
|
|
611
|
-
|
|
612
|
-
// src/commands/config.ts
|
|
613
511
|
function registerConfigCommand(program2) {
|
|
614
512
|
const config = program2.command("config").description("View provider configuration");
|
|
615
513
|
config.command("show").description("Show provider configuration").argument("<provider>", "Provider ID or alias").option("-g, --global", "Show global config").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(
|
|
@@ -2939,8 +2837,8 @@ function registerPiCommands(program2) {
|
|
|
2939
2837
|
}
|
|
2940
2838
|
|
|
2941
2839
|
// src/commands/providers.ts
|
|
2942
|
-
import { randomUUID as
|
|
2943
|
-
import { resolveOutputFormat
|
|
2840
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2841
|
+
import { resolveOutputFormat } from "@cleocode/lafs";
|
|
2944
2842
|
import pc6 from "picocolors";
|
|
2945
2843
|
function registerProvidersCommand(program2) {
|
|
2946
2844
|
const providers = program2.command("providers").description("Manage AI agent providers");
|
|
@@ -2949,7 +2847,7 @@ function registerProvidersCommand(program2) {
|
|
|
2949
2847
|
const mvi = "standard";
|
|
2950
2848
|
let format;
|
|
2951
2849
|
try {
|
|
2952
|
-
format =
|
|
2850
|
+
format = resolveOutputFormat({
|
|
2953
2851
|
jsonFlag: opts.json ?? false,
|
|
2954
2852
|
humanFlag: (opts.human ?? false) || isHuman(),
|
|
2955
2853
|
projectDefault: "json"
|
|
@@ -2999,7 +2897,7 @@ CAMP Provider Registry v${getRegistryVersion()}`));
|
|
|
2999
2897
|
const mvi = "standard";
|
|
3000
2898
|
let format;
|
|
3001
2899
|
try {
|
|
3002
|
-
format =
|
|
2900
|
+
format = resolveOutputFormat({
|
|
3003
2901
|
jsonFlag: opts.json ?? false,
|
|
3004
2902
|
humanFlag: (opts.human ?? false) || isHuman(),
|
|
3005
2903
|
projectDefault: "json"
|
|
@@ -3055,7 +2953,7 @@ Detected ${installed.length} installed providers:
|
|
|
3055
2953
|
const mvi = "standard";
|
|
3056
2954
|
let format;
|
|
3057
2955
|
try {
|
|
3058
|
-
format =
|
|
2956
|
+
format = resolveOutputFormat({
|
|
3059
2957
|
jsonFlag: opts.json ?? false,
|
|
3060
2958
|
humanFlag: (opts.human ?? false) || isHuman(),
|
|
3061
2959
|
projectDefault: "json"
|
|
@@ -3128,7 +3026,7 @@ ${provider.toolName}`));
|
|
|
3128
3026
|
const mvi = "standard";
|
|
3129
3027
|
let format;
|
|
3130
3028
|
try {
|
|
3131
|
-
format =
|
|
3029
|
+
format = resolveOutputFormat({
|
|
3132
3030
|
jsonFlag: opts.json ?? false,
|
|
3133
3031
|
humanFlag: (opts.human ?? false) || isHuman(),
|
|
3134
3032
|
projectDefault: "json"
|
|
@@ -3186,7 +3084,7 @@ ${provider.toolName}`));
|
|
|
3186
3084
|
const mvi = "standard";
|
|
3187
3085
|
let format;
|
|
3188
3086
|
try {
|
|
3189
|
-
format =
|
|
3087
|
+
format = resolveOutputFormat({
|
|
3190
3088
|
jsonFlag: opts.json ?? false,
|
|
3191
3089
|
humanFlag: (opts.human ?? false) || isHuman(),
|
|
3192
3090
|
projectDefault: "json"
|
|
@@ -3254,7 +3152,7 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
|
|
|
3254
3152
|
const mvi = "standard";
|
|
3255
3153
|
let format;
|
|
3256
3154
|
try {
|
|
3257
|
-
format =
|
|
3155
|
+
format = resolveOutputFormat({
|
|
3258
3156
|
jsonFlag: opts.json ?? false,
|
|
3259
3157
|
humanFlag: (opts.human ?? false) || isHuman(),
|
|
3260
3158
|
projectDefault: "json"
|
|
@@ -3301,7 +3199,7 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
|
|
|
3301
3199
|
const mvi = "standard";
|
|
3302
3200
|
let format;
|
|
3303
3201
|
try {
|
|
3304
|
-
format =
|
|
3202
|
+
format = resolveOutputFormat({
|
|
3305
3203
|
jsonFlag: opts.json ?? false,
|
|
3306
3204
|
humanFlag: (opts.human ?? false) || isHuman(),
|
|
3307
3205
|
projectDefault: "json"
|
|
@@ -3349,7 +3247,7 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
|
|
|
3349
3247
|
return;
|
|
3350
3248
|
}
|
|
3351
3249
|
if (opts.from) {
|
|
3352
|
-
const { toCanonical } = await import("./hooks-
|
|
3250
|
+
const { toCanonical } = await import("./hooks-ZN3C4WCK.js");
|
|
3353
3251
|
const canonical2 = toCanonical(event, opts.from);
|
|
3354
3252
|
if (format === "json") {
|
|
3355
3253
|
const envelope = buildEnvelope2(
|
|
@@ -3389,7 +3287,7 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
|
|
|
3389
3287
|
}
|
|
3390
3288
|
process.exit(1);
|
|
3391
3289
|
}
|
|
3392
|
-
const { getMappedProviderIds } = await import("./hooks-
|
|
3290
|
+
const { getMappedProviderIds } = await import("./hooks-ZN3C4WCK.js");
|
|
3393
3291
|
const allIds = getMappedProviderIds();
|
|
3394
3292
|
const translations = translateToAll(canonical, allIds);
|
|
3395
3293
|
if (format === "json") {
|
|
@@ -3432,7 +3330,7 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
|
|
|
3432
3330
|
const mvi = "standard";
|
|
3433
3331
|
let format;
|
|
3434
3332
|
try {
|
|
3435
|
-
format =
|
|
3333
|
+
format = resolveOutputFormat({
|
|
3436
3334
|
jsonFlag: opts.json ?? false,
|
|
3437
3335
|
humanFlag: (opts.human ?? false) || isHuman(),
|
|
3438
3336
|
projectDefault: "json"
|
|
@@ -3502,7 +3400,7 @@ function buildEnvelope2(operation, mvi, result, error) {
|
|
|
3502
3400
|
schemaVersion: "1.0.0",
|
|
3503
3401
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3504
3402
|
operation,
|
|
3505
|
-
requestId:
|
|
3403
|
+
requestId: randomUUID2(),
|
|
3506
3404
|
transport: "cli",
|
|
3507
3405
|
strict: true,
|
|
3508
3406
|
mvi,
|
|
@@ -3817,10 +3715,308 @@ function registerSkillsCheck(parent) {
|
|
|
3817
3715
|
});
|
|
3818
3716
|
}
|
|
3819
3717
|
|
|
3820
|
-
// src/commands/skills/
|
|
3821
|
-
|
|
3822
|
-
|
|
3718
|
+
// src/commands/skills/doctor.ts
|
|
3719
|
+
function registerSkillsDoctor(parent) {
|
|
3720
|
+
const doctor = parent.command("doctor").description("Diagnose and repair the skill storage topology");
|
|
3721
|
+
registerDoctorBridge(doctor);
|
|
3722
|
+
}
|
|
3723
|
+
|
|
3724
|
+
// src/commands/skills/doctor-migrate.ts
|
|
3823
3725
|
import pc9 from "picocolors";
|
|
3726
|
+
|
|
3727
|
+
// src/core/skills/migration.ts
|
|
3728
|
+
import { execFile } from "child_process";
|
|
3729
|
+
import {
|
|
3730
|
+
cpSync,
|
|
3731
|
+
existsSync as existsSync10,
|
|
3732
|
+
mkdirSync,
|
|
3733
|
+
readdirSync as readdirSync2,
|
|
3734
|
+
rmSync,
|
|
3735
|
+
statSync as statSync3,
|
|
3736
|
+
writeFileSync
|
|
3737
|
+
} from "fs";
|
|
3738
|
+
import { homedir as homedir2 } from "os";
|
|
3739
|
+
import { join as join8 } from "path";
|
|
3740
|
+
import { promisify } from "util";
|
|
3741
|
+
var execFileAsync = promisify(execFile);
|
|
3742
|
+
var LEGACY_MIGRATED_MARKER = ".MIGRATED-TO-CLEO";
|
|
3743
|
+
function defaultMigrationOptions(manifestNames) {
|
|
3744
|
+
const home = homedir2();
|
|
3745
|
+
return {
|
|
3746
|
+
legacyRoot: join8(home, ".local", "share", "agents", "skills"),
|
|
3747
|
+
canonicalRoot: join8(home, ".cleo", "skills"),
|
|
3748
|
+
backupDir: join8(home, ".cleo", "backups", "skills"),
|
|
3749
|
+
manifestNames,
|
|
3750
|
+
now: () => /* @__PURE__ */ new Date(),
|
|
3751
|
+
tarExec: systemTarExec,
|
|
3752
|
+
recordRow: () => void 0
|
|
3753
|
+
};
|
|
3754
|
+
}
|
|
3755
|
+
var systemTarExec = {
|
|
3756
|
+
async create({ archivePath, sourceRoot }) {
|
|
3757
|
+
const parent = join8(sourceRoot, "..");
|
|
3758
|
+
const base = sourceRoot.slice(parent.length + 1);
|
|
3759
|
+
await execFileAsync("tar", ["-czf", archivePath, "-C", parent, base]);
|
|
3760
|
+
},
|
|
3761
|
+
async extract({ archivePath, destinationRoot }) {
|
|
3762
|
+
mkdirSync(destinationRoot, { recursive: true });
|
|
3763
|
+
const parent = join8(destinationRoot, "..");
|
|
3764
|
+
await execFileAsync("tar", ["-xzf", archivePath, "-C", parent]);
|
|
3765
|
+
}
|
|
3766
|
+
};
|
|
3767
|
+
function isAlreadyMigrated(legacyRoot) {
|
|
3768
|
+
if (!existsSync10(legacyRoot)) return true;
|
|
3769
|
+
return existsSync10(join8(legacyRoot, LEGACY_MIGRATED_MARKER));
|
|
3770
|
+
}
|
|
3771
|
+
function listSkillDirs(root) {
|
|
3772
|
+
if (!existsSync10(root)) return [];
|
|
3773
|
+
const entries = readdirSync2(root, { withFileTypes: true });
|
|
3774
|
+
return entries.filter((d) => d.isDirectory() && d.name !== LEGACY_MIGRATED_MARKER).map((d) => d.name).sort();
|
|
3775
|
+
}
|
|
3776
|
+
function planMigration(options) {
|
|
3777
|
+
const start = Date.now();
|
|
3778
|
+
const migrated = [];
|
|
3779
|
+
const skipped = [];
|
|
3780
|
+
if (isAlreadyMigrated(options.legacyRoot)) {
|
|
3781
|
+
return {
|
|
3782
|
+
action: "no-op",
|
|
3783
|
+
migrated,
|
|
3784
|
+
skipped,
|
|
3785
|
+
backupPath: null,
|
|
3786
|
+
legacyRoot: options.legacyRoot,
|
|
3787
|
+
canonicalRoot: options.canonicalRoot,
|
|
3788
|
+
durationMs: Date.now() - start
|
|
3789
|
+
};
|
|
3790
|
+
}
|
|
3791
|
+
const names = listSkillDirs(options.legacyRoot);
|
|
3792
|
+
for (const name of names) {
|
|
3793
|
+
const legacyPath = join8(options.legacyRoot, name);
|
|
3794
|
+
if (!statSync3(legacyPath).isDirectory()) {
|
|
3795
|
+
skipped.push({ name, reason: "not-a-directory", legacyPath });
|
|
3796
|
+
continue;
|
|
3797
|
+
}
|
|
3798
|
+
const destPath = join8(options.canonicalRoot, name);
|
|
3799
|
+
if (existsSync10(destPath)) {
|
|
3800
|
+
skipped.push({ name, reason: "already-present", legacyPath });
|
|
3801
|
+
continue;
|
|
3802
|
+
}
|
|
3803
|
+
migrated.push({
|
|
3804
|
+
name,
|
|
3805
|
+
installPath: destPath,
|
|
3806
|
+
legacyPath,
|
|
3807
|
+
sourceType: options.manifestNames.includes(name) ? "canonical" : "user"
|
|
3808
|
+
});
|
|
3809
|
+
}
|
|
3810
|
+
return {
|
|
3811
|
+
action: "dry-run",
|
|
3812
|
+
migrated,
|
|
3813
|
+
skipped,
|
|
3814
|
+
backupPath: null,
|
|
3815
|
+
legacyRoot: options.legacyRoot,
|
|
3816
|
+
canonicalRoot: options.canonicalRoot,
|
|
3817
|
+
durationMs: Date.now() - start
|
|
3818
|
+
};
|
|
3819
|
+
}
|
|
3820
|
+
function formatBackupTimestamp(now) {
|
|
3821
|
+
const pad = (n) => n.toString().padStart(2, "0");
|
|
3822
|
+
return `${now.getUTCFullYear()}${pad(now.getUTCMonth() + 1)}${pad(now.getUTCDate())}-${pad(now.getUTCHours())}${pad(now.getUTCMinutes())}${pad(now.getUTCSeconds())}`;
|
|
3823
|
+
}
|
|
3824
|
+
function listBackups(backupDir) {
|
|
3825
|
+
if (!existsSync10(backupDir)) return [];
|
|
3826
|
+
const files = readdirSync2(backupDir).filter(
|
|
3827
|
+
(f) => f.startsWith("skills-pre-migrate-") && f.endsWith(".tgz")
|
|
3828
|
+
);
|
|
3829
|
+
files.sort().reverse();
|
|
3830
|
+
return files.map((f) => join8(backupDir, f));
|
|
3831
|
+
}
|
|
3832
|
+
async function runMigration(options) {
|
|
3833
|
+
const start = Date.now();
|
|
3834
|
+
const now = (options.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
3835
|
+
const tar = options.tarExec ?? systemTarExec;
|
|
3836
|
+
const sink = options.recordRow ?? (() => void 0);
|
|
3837
|
+
if (isAlreadyMigrated(options.legacyRoot)) {
|
|
3838
|
+
return {
|
|
3839
|
+
action: "no-op",
|
|
3840
|
+
migrated: [],
|
|
3841
|
+
skipped: [],
|
|
3842
|
+
backupPath: null,
|
|
3843
|
+
legacyRoot: options.legacyRoot,
|
|
3844
|
+
canonicalRoot: options.canonicalRoot,
|
|
3845
|
+
durationMs: Date.now() - start
|
|
3846
|
+
};
|
|
3847
|
+
}
|
|
3848
|
+
const plan = planMigration(options);
|
|
3849
|
+
mkdirSync(options.canonicalRoot, { recursive: true });
|
|
3850
|
+
mkdirSync(options.backupDir, { recursive: true });
|
|
3851
|
+
const backupName = `skills-pre-migrate-${formatBackupTimestamp(now)}.tgz`;
|
|
3852
|
+
const backupPath = join8(options.backupDir, backupName);
|
|
3853
|
+
await tar.create({ archivePath: backupPath, sourceRoot: options.legacyRoot });
|
|
3854
|
+
for (const entry of plan.migrated) {
|
|
3855
|
+
cpSync(entry.legacyPath, entry.installPath, { recursive: true, dereference: false });
|
|
3856
|
+
await sink(entry);
|
|
3857
|
+
}
|
|
3858
|
+
writeFileSync(
|
|
3859
|
+
join8(options.legacyRoot, LEGACY_MIGRATED_MARKER),
|
|
3860
|
+
JSON.stringify(
|
|
3861
|
+
{
|
|
3862
|
+
migratedAt: now.toISOString(),
|
|
3863
|
+
backupPath,
|
|
3864
|
+
canonicalRoot: options.canonicalRoot,
|
|
3865
|
+
entries: plan.migrated.length
|
|
3866
|
+
},
|
|
3867
|
+
null,
|
|
3868
|
+
2
|
|
3869
|
+
),
|
|
3870
|
+
"utf-8"
|
|
3871
|
+
);
|
|
3872
|
+
return {
|
|
3873
|
+
action: "migrate",
|
|
3874
|
+
migrated: plan.migrated,
|
|
3875
|
+
skipped: plan.skipped,
|
|
3876
|
+
backupPath,
|
|
3877
|
+
legacyRoot: options.legacyRoot,
|
|
3878
|
+
canonicalRoot: options.canonicalRoot,
|
|
3879
|
+
durationMs: Date.now() - start
|
|
3880
|
+
};
|
|
3881
|
+
}
|
|
3882
|
+
async function runRollback(options) {
|
|
3883
|
+
const start = Date.now();
|
|
3884
|
+
const tar = options.tarExec ?? systemTarExec;
|
|
3885
|
+
const backups = listBackups(options.backupDir);
|
|
3886
|
+
const newest = backups[0];
|
|
3887
|
+
if (!newest) {
|
|
3888
|
+
throw new Error(
|
|
3889
|
+
`No backup tarballs found under ${options.backupDir}. Run "cleo skills migrate" (without --rollback) first.`
|
|
3890
|
+
);
|
|
3891
|
+
}
|
|
3892
|
+
if (existsSync10(options.legacyRoot)) {
|
|
3893
|
+
rmSync(options.legacyRoot, { recursive: true, force: true });
|
|
3894
|
+
}
|
|
3895
|
+
await tar.extract({ archivePath: newest, destinationRoot: options.legacyRoot });
|
|
3896
|
+
return {
|
|
3897
|
+
action: "rollback",
|
|
3898
|
+
migrated: [],
|
|
3899
|
+
skipped: [],
|
|
3900
|
+
backupPath: newest,
|
|
3901
|
+
legacyRoot: options.legacyRoot,
|
|
3902
|
+
canonicalRoot: options.canonicalRoot,
|
|
3903
|
+
durationMs: Date.now() - start
|
|
3904
|
+
};
|
|
3905
|
+
}
|
|
3906
|
+
|
|
3907
|
+
// src/commands/skills/doctor-migrate.ts
|
|
3908
|
+
function buildOptions(opts) {
|
|
3909
|
+
if (opts.__optionsOverride) return opts.__optionsOverride;
|
|
3910
|
+
const manifestNames = opts.__manifestNamesOverride ?? [];
|
|
3911
|
+
return defaultMigrationOptions(manifestNames);
|
|
3912
|
+
}
|
|
3913
|
+
function renderHuman(outcome) {
|
|
3914
|
+
const lines = [];
|
|
3915
|
+
lines.push(pc9.bold(`
|
|
3916
|
+
skills ${outcome.action}`));
|
|
3917
|
+
lines.push(pc9.dim(` legacy: ${outcome.legacyRoot}`));
|
|
3918
|
+
lines.push(pc9.dim(` canonical: ${outcome.canonicalRoot}`));
|
|
3919
|
+
if (outcome.backupPath) {
|
|
3920
|
+
lines.push(pc9.dim(` backup: ${outcome.backupPath}`));
|
|
3921
|
+
}
|
|
3922
|
+
lines.push(pc9.dim(` duration: ${outcome.durationMs}ms`));
|
|
3923
|
+
if (outcome.migrated.length > 0) {
|
|
3924
|
+
lines.push(pc9.green(`
|
|
3925
|
+
Migrated (${outcome.migrated.length}):`));
|
|
3926
|
+
for (const m of outcome.migrated) {
|
|
3927
|
+
lines.push(` + ${pc9.bold(m.name)} ${pc9.dim(`[${m.sourceType}]`)}`);
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3930
|
+
if (outcome.skipped.length > 0) {
|
|
3931
|
+
lines.push(pc9.yellow(`
|
|
3932
|
+
Skipped (${outcome.skipped.length}):`));
|
|
3933
|
+
for (const s of outcome.skipped) {
|
|
3934
|
+
lines.push(` ! ${pc9.bold(s.name)} ${pc9.dim(`(${s.reason})`)}`);
|
|
3935
|
+
}
|
|
3936
|
+
}
|
|
3937
|
+
if (outcome.action === "no-op") {
|
|
3938
|
+
lines.push(pc9.dim("\nLegacy store is already migrated (sentinel present)."));
|
|
3939
|
+
}
|
|
3940
|
+
if (outcome.action === "dry-run") {
|
|
3941
|
+
lines.push(pc9.dim("\n(dry run \u2014 no filesystem changes were made)"));
|
|
3942
|
+
}
|
|
3943
|
+
return lines.join("\n");
|
|
3944
|
+
}
|
|
3945
|
+
function registerSkillsDoctorMigrate(parent) {
|
|
3946
|
+
parent.command("migrate").description("Migrate legacy skills (~/.local/share/agents/skills) into ~/.cleo/skills").option("--dry-run", "Preview the plan without writing anything", false).option("--rollback", "Restore from the most recent backup tarball", false).option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
3947
|
+
const operation = "skills.migrate";
|
|
3948
|
+
const mvi = "standard";
|
|
3949
|
+
let format;
|
|
3950
|
+
try {
|
|
3951
|
+
format = resolveFormat({
|
|
3952
|
+
jsonFlag: opts.json ?? false,
|
|
3953
|
+
humanFlag: (opts.human ?? false) || isHuman(),
|
|
3954
|
+
projectDefault: "json"
|
|
3955
|
+
});
|
|
3956
|
+
} catch (error) {
|
|
3957
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3958
|
+
emitJsonError(
|
|
3959
|
+
operation,
|
|
3960
|
+
mvi,
|
|
3961
|
+
ErrorCodes.FORMAT_CONFLICT,
|
|
3962
|
+
message,
|
|
3963
|
+
ErrorCategories.VALIDATION
|
|
3964
|
+
);
|
|
3965
|
+
process.exit(1);
|
|
3966
|
+
}
|
|
3967
|
+
if (opts.dryRun && opts.rollback) {
|
|
3968
|
+
const message = "Cannot combine --dry-run with --rollback";
|
|
3969
|
+
if (format === "json") {
|
|
3970
|
+
emitJsonError(
|
|
3971
|
+
operation,
|
|
3972
|
+
mvi,
|
|
3973
|
+
ErrorCodes.INVALID_INPUT,
|
|
3974
|
+
message,
|
|
3975
|
+
ErrorCategories.VALIDATION
|
|
3976
|
+
);
|
|
3977
|
+
} else {
|
|
3978
|
+
console.error(pc9.red(message));
|
|
3979
|
+
}
|
|
3980
|
+
process.exit(1);
|
|
3981
|
+
}
|
|
3982
|
+
const options = buildOptions(opts);
|
|
3983
|
+
try {
|
|
3984
|
+
let outcome;
|
|
3985
|
+
if (opts.rollback) {
|
|
3986
|
+
outcome = await runRollback(options);
|
|
3987
|
+
} else if (opts.dryRun) {
|
|
3988
|
+
outcome = planMigration(options);
|
|
3989
|
+
} else {
|
|
3990
|
+
outcome = await runMigration(options);
|
|
3991
|
+
}
|
|
3992
|
+
if (format === "json") {
|
|
3993
|
+
outputSuccess(operation, mvi, outcome);
|
|
3994
|
+
} else {
|
|
3995
|
+
console.log(renderHuman(outcome));
|
|
3996
|
+
}
|
|
3997
|
+
} catch (error) {
|
|
3998
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3999
|
+
if (format === "json") {
|
|
4000
|
+
emitJsonError(
|
|
4001
|
+
operation,
|
|
4002
|
+
mvi,
|
|
4003
|
+
ErrorCodes.INTERNAL_ERROR,
|
|
4004
|
+
message,
|
|
4005
|
+
ErrorCategories.MIGRATION,
|
|
4006
|
+
{ legacyRoot: options.legacyRoot, canonicalRoot: options.canonicalRoot }
|
|
4007
|
+
);
|
|
4008
|
+
} else {
|
|
4009
|
+
console.error(pc9.red(`migration failed: ${message}`));
|
|
4010
|
+
}
|
|
4011
|
+
process.exit(1);
|
|
4012
|
+
}
|
|
4013
|
+
});
|
|
4014
|
+
}
|
|
4015
|
+
|
|
4016
|
+
// src/commands/skills/find.ts
|
|
4017
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
4018
|
+
import { resolveOutputFormat as resolveOutputFormat2 } from "@cleocode/lafs";
|
|
4019
|
+
import pc10 from "picocolors";
|
|
3824
4020
|
var SkillsFindValidationError = class extends Error {
|
|
3825
4021
|
code;
|
|
3826
4022
|
constructor(code, message) {
|
|
@@ -3851,7 +4047,7 @@ function registerSkillsFind(parent) {
|
|
|
3851
4047
|
const mvi = details ? "full" : "standard";
|
|
3852
4048
|
let format;
|
|
3853
4049
|
try {
|
|
3854
|
-
format =
|
|
4050
|
+
format = resolveOutputFormat2({
|
|
3855
4051
|
jsonFlag: opts.json ?? false,
|
|
3856
4052
|
humanFlag: (opts.human ?? false) || isHuman(),
|
|
3857
4053
|
projectDefault: "json"
|
|
@@ -3861,7 +4057,7 @@ function registerSkillsFind(parent) {
|
|
|
3861
4057
|
if (opts.json) {
|
|
3862
4058
|
emitJsonError3(operation, mvi, "E_FORMAT_CONFLICT", message, "VALIDATION");
|
|
3863
4059
|
} else {
|
|
3864
|
-
console.error(
|
|
4060
|
+
console.error(pc10.red(message));
|
|
3865
4061
|
}
|
|
3866
4062
|
process.exit(1);
|
|
3867
4063
|
}
|
|
@@ -3928,19 +4124,19 @@ function registerSkillsFind(parent) {
|
|
|
3928
4124
|
query: query ?? null
|
|
3929
4125
|
});
|
|
3930
4126
|
} else {
|
|
3931
|
-
console.error(
|
|
4127
|
+
console.error(pc10.red(`Recommendation failed: ${message}`));
|
|
3932
4128
|
}
|
|
3933
4129
|
process.exit(1);
|
|
3934
4130
|
}
|
|
3935
4131
|
}
|
|
3936
4132
|
if (!query) {
|
|
3937
|
-
console.log(
|
|
4133
|
+
console.log(pc10.dim("Usage: caamp skills find <query>"));
|
|
3938
4134
|
return;
|
|
3939
4135
|
}
|
|
3940
4136
|
const limit = parseInt(opts.limit, 10);
|
|
3941
4137
|
const client = new MarketplaceClient();
|
|
3942
4138
|
if (format === "human") {
|
|
3943
|
-
console.log(
|
|
4139
|
+
console.log(pc10.dim(`Searching marketplaces for "${query}"...
|
|
3944
4140
|
`));
|
|
3945
4141
|
}
|
|
3946
4142
|
let results;
|
|
@@ -3954,7 +4150,7 @@ function registerSkillsFind(parent) {
|
|
|
3954
4150
|
limit
|
|
3955
4151
|
});
|
|
3956
4152
|
} else {
|
|
3957
|
-
console.error(
|
|
4153
|
+
console.error(pc10.red(`Marketplace search failed: ${message}`));
|
|
3958
4154
|
}
|
|
3959
4155
|
process.exit(1);
|
|
3960
4156
|
}
|
|
@@ -3974,21 +4170,21 @@ function registerSkillsFind(parent) {
|
|
|
3974
4170
|
return;
|
|
3975
4171
|
}
|
|
3976
4172
|
if (results.length === 0) {
|
|
3977
|
-
console.log(
|
|
4173
|
+
console.log(pc10.yellow("No results found."));
|
|
3978
4174
|
return;
|
|
3979
4175
|
}
|
|
3980
|
-
console.log(
|
|
4176
|
+
console.log(pc10.dim(`Found ${results.length} result(s) for "${query}":
|
|
3981
4177
|
`));
|
|
3982
4178
|
results.forEach((skill, index) => {
|
|
3983
4179
|
const num = (index + 1).toString().padStart(2);
|
|
3984
|
-
const stars = skill.stars > 0 ?
|
|
3985
|
-
console.log(` ${
|
|
3986
|
-
console.log(` ${
|
|
3987
|
-
console.log(` ${
|
|
4180
|
+
const stars = skill.stars > 0 ? pc10.yellow(`\u2605 ${formatStars(skill.stars)}`) : "";
|
|
4181
|
+
console.log(` ${pc10.cyan(num)}. ${pc10.bold(skill.scopedName)} ${stars}`);
|
|
4182
|
+
console.log(` ${pc10.dim(skill.description?.slice(0, 80) ?? "")}`);
|
|
4183
|
+
console.log(` ${pc10.dim(`from ${skill.source}`)}`);
|
|
3988
4184
|
console.log();
|
|
3989
4185
|
});
|
|
3990
|
-
console.log(
|
|
3991
|
-
console.log(
|
|
4186
|
+
console.log(pc10.dim("Install with: caamp skills install <name>"));
|
|
4187
|
+
console.log(pc10.dim("Or select by number: caamp skills install <n>"));
|
|
3992
4188
|
});
|
|
3993
4189
|
}
|
|
3994
4190
|
function formatStars(n) {
|
|
@@ -4080,7 +4276,7 @@ function buildEnvelope3(operation, mvi, result, error) {
|
|
|
4080
4276
|
schemaVersion: "1.0.0",
|
|
4081
4277
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4082
4278
|
operation,
|
|
4083
|
-
requestId:
|
|
4279
|
+
requestId: randomUUID3(),
|
|
4084
4280
|
transport: "cli",
|
|
4085
4281
|
strict: true,
|
|
4086
4282
|
mvi,
|
|
@@ -4105,10 +4301,10 @@ function emitJsonError3(operation, mvi, code, message, category, details = {}) {
|
|
|
4105
4301
|
}
|
|
4106
4302
|
|
|
4107
4303
|
// src/commands/skills/init.ts
|
|
4108
|
-
import { existsSync as
|
|
4304
|
+
import { existsSync as existsSync11 } from "fs";
|
|
4109
4305
|
import { mkdir, writeFile as writeFile3 } from "fs/promises";
|
|
4110
|
-
import { join as
|
|
4111
|
-
import
|
|
4306
|
+
import { join as join9 } from "path";
|
|
4307
|
+
import pc11 from "picocolors";
|
|
4112
4308
|
function registerSkillsInit(parent) {
|
|
4113
4309
|
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(
|
|
4114
4310
|
async (name, opts) => {
|
|
@@ -4133,8 +4329,8 @@ function registerSkillsInit(parent) {
|
|
|
4133
4329
|
process.exit(1);
|
|
4134
4330
|
}
|
|
4135
4331
|
const skillName = name ?? "my-skill";
|
|
4136
|
-
const skillDir =
|
|
4137
|
-
if (
|
|
4332
|
+
const skillDir = join9(opts.dir, skillName);
|
|
4333
|
+
if (existsSync11(skillDir)) {
|
|
4138
4334
|
const message = `Directory already exists: ${skillDir}`;
|
|
4139
4335
|
if (format === "json") {
|
|
4140
4336
|
emitJsonError(
|
|
@@ -4148,7 +4344,7 @@ function registerSkillsInit(parent) {
|
|
|
4148
4344
|
}
|
|
4149
4345
|
);
|
|
4150
4346
|
} else {
|
|
4151
|
-
console.error(
|
|
4347
|
+
console.error(pc11.red(message));
|
|
4152
4348
|
}
|
|
4153
4349
|
process.exit(1);
|
|
4154
4350
|
}
|
|
@@ -4176,7 +4372,7 @@ Provide detailed instructions for the AI agent here.
|
|
|
4176
4372
|
|
|
4177
4373
|
Show example inputs and expected outputs.
|
|
4178
4374
|
`;
|
|
4179
|
-
await writeFile3(
|
|
4375
|
+
await writeFile3(join9(skillDir, "SKILL.md"), template, "utf-8");
|
|
4180
4376
|
const result = {
|
|
4181
4377
|
name: skillName,
|
|
4182
4378
|
directory: skillDir,
|
|
@@ -4187,18 +4383,88 @@ Show example inputs and expected outputs.
|
|
|
4187
4383
|
outputSuccess(operation, mvi, result);
|
|
4188
4384
|
return;
|
|
4189
4385
|
}
|
|
4190
|
-
console.log(
|
|
4191
|
-
console.log(
|
|
4192
|
-
console.log(
|
|
4193
|
-
console.log(
|
|
4194
|
-
console.log(
|
|
4386
|
+
console.log(pc11.green(`\u2713 Created skill template: ${skillDir}/SKILL.md`));
|
|
4387
|
+
console.log(pc11.dim("\nNext steps:"));
|
|
4388
|
+
console.log(pc11.dim(" 1. Edit SKILL.md with your instructions"));
|
|
4389
|
+
console.log(pc11.dim(` 2. Validate: caamp skills validate ${join9(skillDir, "SKILL.md")}`));
|
|
4390
|
+
console.log(pc11.dim(` 3. Install: caamp skills install ${skillDir}`));
|
|
4195
4391
|
}
|
|
4196
4392
|
);
|
|
4197
4393
|
}
|
|
4198
4394
|
|
|
4199
4395
|
// src/commands/skills/install.ts
|
|
4200
|
-
import { existsSync as
|
|
4201
|
-
import
|
|
4396
|
+
import { existsSync as existsSync12 } from "fs";
|
|
4397
|
+
import pc12 from "picocolors";
|
|
4398
|
+
|
|
4399
|
+
// src/core/skills/trust-gate-adapter.ts
|
|
4400
|
+
var cachedCore;
|
|
4401
|
+
async function resolveCore() {
|
|
4402
|
+
if (cachedCore !== void 0) return cachedCore;
|
|
4403
|
+
try {
|
|
4404
|
+
const mod = await import("@cleocode/core");
|
|
4405
|
+
cachedCore = mod;
|
|
4406
|
+
} catch {
|
|
4407
|
+
cachedCore = null;
|
|
4408
|
+
process.stderr.write(
|
|
4409
|
+
"[caamp] WARNING: @cleocode/core unavailable \u2014 skipping trust gate (skill installs not security-checked)\n"
|
|
4410
|
+
);
|
|
4411
|
+
}
|
|
4412
|
+
return cachedCore;
|
|
4413
|
+
}
|
|
4414
|
+
async function evaluateSkillTrustGate(localPath, source, force = false) {
|
|
4415
|
+
const core = await resolveCore();
|
|
4416
|
+
if (!core) {
|
|
4417
|
+
return {
|
|
4418
|
+
decision: "allow",
|
|
4419
|
+
reason: "Trust gate skipped \u2014 @cleocode/core not available",
|
|
4420
|
+
scan: {
|
|
4421
|
+
skillName: localPath.split("/").filter(Boolean).pop() ?? localPath,
|
|
4422
|
+
source,
|
|
4423
|
+
trustLevel: "community",
|
|
4424
|
+
verdict: "safe",
|
|
4425
|
+
findings: [],
|
|
4426
|
+
scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4427
|
+
summary: "core-unavailable"
|
|
4428
|
+
}
|
|
4429
|
+
};
|
|
4430
|
+
}
|
|
4431
|
+
const scan = core.scanSkill(localPath, source);
|
|
4432
|
+
const gate = core.shouldAllowInstall(scan, force);
|
|
4433
|
+
return { decision: gate.decision, reason: gate.reason, scan };
|
|
4434
|
+
}
|
|
4435
|
+
async function evaluateFederationGate(source, opts = {}) {
|
|
4436
|
+
const core = await resolveCore();
|
|
4437
|
+
if (!core) {
|
|
4438
|
+
return {
|
|
4439
|
+
decision: "allow",
|
|
4440
|
+
reason: "Federation gate skipped \u2014 @cleocode/core not available",
|
|
4441
|
+
peer: null,
|
|
4442
|
+
isFederationSource: false,
|
|
4443
|
+
computedChecksum: null,
|
|
4444
|
+
expectedChecksum: null
|
|
4445
|
+
};
|
|
4446
|
+
}
|
|
4447
|
+
return core.evaluateFederationInstallGate({
|
|
4448
|
+
source,
|
|
4449
|
+
artefactPath: opts.artefactPath,
|
|
4450
|
+
expectedChecksum: opts.expectedChecksum,
|
|
4451
|
+
approveNewSource: opts.approveNewSource
|
|
4452
|
+
});
|
|
4453
|
+
}
|
|
4454
|
+
async function recordSkillTrustBypass(scan, reason = null) {
|
|
4455
|
+
const core = await resolveCore();
|
|
4456
|
+
if (!core) return;
|
|
4457
|
+
try {
|
|
4458
|
+
core.recordTrustBypass(scan, reason);
|
|
4459
|
+
} catch (err) {
|
|
4460
|
+
process.stderr.write(
|
|
4461
|
+
`[caamp] WARNING: failed to record trust-bypass audit row: ${err instanceof Error ? err.message : String(err)}
|
|
4462
|
+
`
|
|
4463
|
+
);
|
|
4464
|
+
}
|
|
4465
|
+
}
|
|
4466
|
+
|
|
4467
|
+
// src/commands/skills/install.ts
|
|
4202
4468
|
function registerSkillsInstall(parent) {
|
|
4203
4469
|
parent.command("install").description("Install a skill from GitHub, URL, marketplace, or registered skill library").argument("[source]", "Skill source (GitHub URL, owner/repo, @author/name, skill-name)").option(
|
|
4204
4470
|
"-a, --agent <name>",
|
|
@@ -4208,6 +4474,12 @@ function registerSkillsInstall(parent) {
|
|
|
4208
4474
|
).option("-g, --global", "Install globally").option("-y, --yes", "Skip confirmation").option("--all", "Install to all detected agents").option(
|
|
4209
4475
|
"--profile <name>",
|
|
4210
4476
|
"Install a skill library profile (minimal, core, recommended, full)"
|
|
4477
|
+
).option(
|
|
4478
|
+
"--force",
|
|
4479
|
+
"Override trust-gate block decisions (audited to .cleo/audit/skill-trust-bypass.jsonl)"
|
|
4480
|
+
).option(
|
|
4481
|
+
"--allow-new-source",
|
|
4482
|
+
"Bypass first-install confirmation prompt for unknown federation sources (non-TTY safe)"
|
|
4211
4483
|
).option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(
|
|
4212
4484
|
async (source, opts) => {
|
|
4213
4485
|
const operation = "skills.install";
|
|
@@ -4241,7 +4513,7 @@ function registerSkillsInstall(parent) {
|
|
|
4241
4513
|
if (providers.length === 0) {
|
|
4242
4514
|
const message = "No target providers found. Use --agent or --all.";
|
|
4243
4515
|
if (format === "json") {
|
|
4244
|
-
|
|
4516
|
+
emitError(
|
|
4245
4517
|
operation,
|
|
4246
4518
|
mvi,
|
|
4247
4519
|
ErrorCodes.PROVIDER_NOT_FOUND,
|
|
@@ -4249,7 +4521,7 @@ function registerSkillsInstall(parent) {
|
|
|
4249
4521
|
ErrorCategories.NOT_FOUND
|
|
4250
4522
|
);
|
|
4251
4523
|
}
|
|
4252
|
-
console.error(
|
|
4524
|
+
console.error(pc12.red(message));
|
|
4253
4525
|
process.exit(1);
|
|
4254
4526
|
}
|
|
4255
4527
|
if (opts.profile) {
|
|
@@ -4266,7 +4538,7 @@ function registerSkillsInstall(parent) {
|
|
|
4266
4538
|
if (!source) {
|
|
4267
4539
|
const message = "Missing required argument: source";
|
|
4268
4540
|
if (format === "json") {
|
|
4269
|
-
|
|
4541
|
+
emitError(
|
|
4270
4542
|
operation,
|
|
4271
4543
|
mvi,
|
|
4272
4544
|
ErrorCodes.INVALID_INPUT,
|
|
@@ -4274,14 +4546,14 @@ function registerSkillsInstall(parent) {
|
|
|
4274
4546
|
ErrorCategories.VALIDATION
|
|
4275
4547
|
);
|
|
4276
4548
|
}
|
|
4277
|
-
console.error(
|
|
4549
|
+
console.error(pc12.red(message));
|
|
4278
4550
|
console.log(
|
|
4279
|
-
|
|
4551
|
+
pc12.dim("Usage: caamp skills install <source> or caamp skills install --profile <name>")
|
|
4280
4552
|
);
|
|
4281
4553
|
process.exit(1);
|
|
4282
4554
|
}
|
|
4283
4555
|
if (format === "human") {
|
|
4284
|
-
console.log(
|
|
4556
|
+
console.log(pc12.dim(`Installing to ${providers.length} provider(s)...`));
|
|
4285
4557
|
}
|
|
4286
4558
|
let localPath;
|
|
4287
4559
|
let cleanup;
|
|
@@ -4327,7 +4599,7 @@ function registerSkillsInstall(parent) {
|
|
|
4327
4599
|
ErrorCategories.TRANSIENT
|
|
4328
4600
|
);
|
|
4329
4601
|
}
|
|
4330
|
-
console.error(
|
|
4602
|
+
console.error(pc12.red(message));
|
|
4331
4603
|
process.exit(1);
|
|
4332
4604
|
}
|
|
4333
4605
|
} else if (parsed.type === "gitlab" && parsed.owner && parsed.repo) {
|
|
@@ -4351,7 +4623,7 @@ function registerSkillsInstall(parent) {
|
|
|
4351
4623
|
ErrorCategories.TRANSIENT
|
|
4352
4624
|
);
|
|
4353
4625
|
}
|
|
4354
|
-
console.error(
|
|
4626
|
+
console.error(pc12.red(message));
|
|
4355
4627
|
process.exit(1);
|
|
4356
4628
|
}
|
|
4357
4629
|
} else if (parsed.type === "local") {
|
|
@@ -4372,7 +4644,7 @@ function registerSkillsInstall(parent) {
|
|
|
4372
4644
|
ErrorCategories.VALIDATION
|
|
4373
4645
|
);
|
|
4374
4646
|
}
|
|
4375
|
-
console.error(
|
|
4647
|
+
console.error(pc12.red(message));
|
|
4376
4648
|
process.exit(1);
|
|
4377
4649
|
}
|
|
4378
4650
|
const catalogSkill = getSkill(parsed.inferredName);
|
|
@@ -4383,7 +4655,7 @@ function registerSkillsInstall(parent) {
|
|
|
4383
4655
|
sourceType = "library";
|
|
4384
4656
|
if (format === "human") {
|
|
4385
4657
|
console.log(
|
|
4386
|
-
` Found in catalog: ${
|
|
4658
|
+
` Found in catalog: ${pc12.bold(catalogSkill.name)} v${catalogSkill.version} (${pc12.dim(catalogSkill.category)})`
|
|
4387
4659
|
);
|
|
4388
4660
|
}
|
|
4389
4661
|
} else {
|
|
@@ -4400,8 +4672,8 @@ function registerSkillsInstall(parent) {
|
|
|
4400
4672
|
}
|
|
4401
4673
|
);
|
|
4402
4674
|
}
|
|
4403
|
-
console.error(
|
|
4404
|
-
console.log(
|
|
4675
|
+
console.error(pc12.red(message));
|
|
4676
|
+
console.log(pc12.dim("Available skills: " + listSkills().join(", ")));
|
|
4405
4677
|
process.exit(1);
|
|
4406
4678
|
}
|
|
4407
4679
|
} else {
|
|
@@ -4415,7 +4687,7 @@ function registerSkillsInstall(parent) {
|
|
|
4415
4687
|
ErrorCategories.VALIDATION
|
|
4416
4688
|
);
|
|
4417
4689
|
}
|
|
4418
|
-
console.error(
|
|
4690
|
+
console.error(pc12.red(message));
|
|
4419
4691
|
process.exit(1);
|
|
4420
4692
|
}
|
|
4421
4693
|
}
|
|
@@ -4431,9 +4703,103 @@ function registerSkillsInstall(parent) {
|
|
|
4431
4703
|
ErrorCategories.INTERNAL
|
|
4432
4704
|
);
|
|
4433
4705
|
}
|
|
4434
|
-
console.error(
|
|
4706
|
+
console.error(pc12.red(message));
|
|
4435
4707
|
process.exit(1);
|
|
4436
4708
|
}
|
|
4709
|
+
if (sourceType !== "library" && sourceType !== "package") {
|
|
4710
|
+
const fedGate = await evaluateFederationGate(sourceValue, {
|
|
4711
|
+
artefactPath: localPath,
|
|
4712
|
+
approveNewSource: opts.allowNewSource === true
|
|
4713
|
+
});
|
|
4714
|
+
if (fedGate.decision === "block-checksum") {
|
|
4715
|
+
const message = `Federation install blocked: ${fedGate.reason}`;
|
|
4716
|
+
if (format === "json") {
|
|
4717
|
+
emitJsonError(
|
|
4718
|
+
operation,
|
|
4719
|
+
mvi,
|
|
4720
|
+
ErrorCodes.FEDERATION_CHECKSUM_MISMATCH,
|
|
4721
|
+
message,
|
|
4722
|
+
ErrorCategories.VALIDATION,
|
|
4723
|
+
{
|
|
4724
|
+
expectedChecksum: fedGate.expectedChecksum,
|
|
4725
|
+
computedChecksum: fedGate.computedChecksum
|
|
4726
|
+
}
|
|
4727
|
+
);
|
|
4728
|
+
}
|
|
4729
|
+
console.error(pc12.red(message));
|
|
4730
|
+
process.exit(1);
|
|
4731
|
+
}
|
|
4732
|
+
if (fedGate.decision === "prompt-first-install") {
|
|
4733
|
+
const message = `${fedGate.reason} Use --allow-new-source to approve in non-interactive contexts.`;
|
|
4734
|
+
if (format === "json") {
|
|
4735
|
+
emitJsonError(
|
|
4736
|
+
operation,
|
|
4737
|
+
mvi,
|
|
4738
|
+
ErrorCodes.FEDERATION_UNKNOWN_SOURCE_INTERACTIVE_REQUIRED,
|
|
4739
|
+
message,
|
|
4740
|
+
ErrorCategories.VALIDATION,
|
|
4741
|
+
{ peer: fedGate.peer?.url ?? null }
|
|
4742
|
+
);
|
|
4743
|
+
}
|
|
4744
|
+
console.error(pc12.yellow(message));
|
|
4745
|
+
process.exit(1);
|
|
4746
|
+
}
|
|
4747
|
+
}
|
|
4748
|
+
if (sourceType !== "library" && sourceType !== "package") {
|
|
4749
|
+
const gate = await evaluateSkillTrustGate(localPath, sourceValue, opts.force ?? false);
|
|
4750
|
+
if (gate.decision === "block") {
|
|
4751
|
+
const message = `Trust gate blocked install: ${gate.reason}`;
|
|
4752
|
+
if (format === "json") {
|
|
4753
|
+
const envelope = buildEnvelope(
|
|
4754
|
+
operation,
|
|
4755
|
+
mvi,
|
|
4756
|
+
{ scan: gate.scan },
|
|
4757
|
+
{
|
|
4758
|
+
code: ErrorCodes.SKILL_TRUST_GATE_BLOCKED,
|
|
4759
|
+
message,
|
|
4760
|
+
category: ErrorCategories.VALIDATION,
|
|
4761
|
+
retryable: false,
|
|
4762
|
+
retryAfterMs: null,
|
|
4763
|
+
details: {
|
|
4764
|
+
verdict: gate.scan.verdict,
|
|
4765
|
+
trustLevel: gate.scan.trustLevel,
|
|
4766
|
+
findingsCount: gate.scan.findings.length
|
|
4767
|
+
}
|
|
4768
|
+
}
|
|
4769
|
+
);
|
|
4770
|
+
console.error(JSON.stringify(envelope, null, 2));
|
|
4771
|
+
} else {
|
|
4772
|
+
console.error(pc12.red(`
|
|
4773
|
+
${message}`));
|
|
4774
|
+
for (const f of gate.scan.findings.slice(0, 5)) {
|
|
4775
|
+
console.error(
|
|
4776
|
+
pc12.dim(
|
|
4777
|
+
` [${f.severity}] ${f.category} ${f.file}:${f.line} \u2014 ${f.description}`
|
|
4778
|
+
)
|
|
4779
|
+
);
|
|
4780
|
+
}
|
|
4781
|
+
console.error(pc12.yellow(" Use --force to override (audited)."));
|
|
4782
|
+
}
|
|
4783
|
+
process.exit(1);
|
|
4784
|
+
}
|
|
4785
|
+
if (gate.decision === "ask") {
|
|
4786
|
+
const message = `Trust gate requires confirmation: ${gate.reason}`;
|
|
4787
|
+
if (format === "json") {
|
|
4788
|
+
emitJsonError(
|
|
4789
|
+
operation,
|
|
4790
|
+
mvi,
|
|
4791
|
+
ErrorCodes.SKILL_TRUST_GATE_BLOCKED,
|
|
4792
|
+
message,
|
|
4793
|
+
ErrorCategories.VALIDATION
|
|
4794
|
+
);
|
|
4795
|
+
}
|
|
4796
|
+
console.error(pc12.red(message));
|
|
4797
|
+
process.exit(1);
|
|
4798
|
+
}
|
|
4799
|
+
if (opts.force === true && gate.scan.verdict !== "safe") {
|
|
4800
|
+
await recordSkillTrustBypass(gate.scan, "operator --force on caamp skills install");
|
|
4801
|
+
}
|
|
4802
|
+
}
|
|
4437
4803
|
const result = await dispatchInstallSkillAcrossProviders(
|
|
4438
4804
|
localPath,
|
|
4439
4805
|
skillName,
|
|
@@ -4469,14 +4835,14 @@ function registerSkillsInstall(parent) {
|
|
|
4469
4835
|
if (format === "json") {
|
|
4470
4836
|
outputSuccess(operation, mvi, summary);
|
|
4471
4837
|
} else {
|
|
4472
|
-
console.log(
|
|
4473
|
-
\u2713 Installed ${
|
|
4474
|
-
console.log(` Canonical: ${
|
|
4838
|
+
console.log(pc12.green(`
|
|
4839
|
+
\u2713 Installed ${pc12.bold(skillName)}`));
|
|
4840
|
+
console.log(` Canonical: ${pc12.dim(result.canonicalPath)}`);
|
|
4475
4841
|
console.log(` Linked to: ${result.linkedAgents.join(", ")}`);
|
|
4476
4842
|
if (result.errors.length > 0) {
|
|
4477
|
-
console.log(
|
|
4843
|
+
console.log(pc12.yellow("\nWarnings:"));
|
|
4478
4844
|
for (const err of result.errors) {
|
|
4479
|
-
console.log(` ${
|
|
4845
|
+
console.log(` ${pc12.yellow("!")} ${err}`);
|
|
4480
4846
|
}
|
|
4481
4847
|
}
|
|
4482
4848
|
}
|
|
@@ -4506,11 +4872,11 @@ function registerSkillsInstall(parent) {
|
|
|
4506
4872
|
});
|
|
4507
4873
|
console.error(JSON.stringify(envelope, null, 2));
|
|
4508
4874
|
} else {
|
|
4509
|
-
console.log(
|
|
4510
|
-
\u2717 Failed to install ${
|
|
4511
|
-
console.log(
|
|
4875
|
+
console.log(pc12.yellow(`
|
|
4876
|
+
\u2717 Failed to install ${pc12.bold(skillName)}`));
|
|
4877
|
+
console.log(pc12.yellow("Errors:"));
|
|
4512
4878
|
for (const err of result.errors) {
|
|
4513
|
-
console.log(` ${
|
|
4879
|
+
console.log(` ${pc12.yellow("!")} ${err}`);
|
|
4514
4880
|
}
|
|
4515
4881
|
}
|
|
4516
4882
|
process.exit(1);
|
|
@@ -4525,9 +4891,9 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
4525
4891
|
if (!isCatalogAvailable()) {
|
|
4526
4892
|
const message = "No skill library registered. Register one with registerSkillLibraryFromPath() or set CAAMP_SKILL_LIBRARY env var.";
|
|
4527
4893
|
if (format === "json") {
|
|
4528
|
-
|
|
4894
|
+
emitError(operation, mvi, ErrorCodes.INVALID_INPUT, message, ErrorCategories.VALIDATION);
|
|
4529
4895
|
}
|
|
4530
|
-
console.error(
|
|
4896
|
+
console.error(pc12.red(message));
|
|
4531
4897
|
process.exit(1);
|
|
4532
4898
|
}
|
|
4533
4899
|
const profileSkills = resolveProfile(profileName);
|
|
@@ -4545,16 +4911,16 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
4545
4911
|
}
|
|
4546
4912
|
);
|
|
4547
4913
|
}
|
|
4548
|
-
console.error(
|
|
4914
|
+
console.error(pc12.red(message));
|
|
4549
4915
|
const available = listProfiles();
|
|
4550
4916
|
if (available.length > 0) {
|
|
4551
|
-
console.log(
|
|
4917
|
+
console.log(pc12.dim("Available profiles: " + available.join(", ")));
|
|
4552
4918
|
}
|
|
4553
4919
|
process.exit(1);
|
|
4554
4920
|
}
|
|
4555
4921
|
if (format === "human") {
|
|
4556
|
-
console.log(`Installing profile ${
|
|
4557
|
-
console.log(
|
|
4922
|
+
console.log(`Installing profile ${pc12.bold(profileName)} (${profileSkills.length} skill(s))...`);
|
|
4923
|
+
console.log(pc12.dim(`Target: ${providers.length} provider(s)`));
|
|
4558
4924
|
}
|
|
4559
4925
|
const installed = [];
|
|
4560
4926
|
const failed = [];
|
|
@@ -4564,7 +4930,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
4564
4930
|
const result = await dispatchInstallSkillAcrossProviders(skillDir, name, providers, isGlobal);
|
|
4565
4931
|
if (result.success) {
|
|
4566
4932
|
if (format === "human") {
|
|
4567
|
-
console.log(
|
|
4933
|
+
console.log(pc12.green(` + ${name}`));
|
|
4568
4934
|
}
|
|
4569
4935
|
await recordSkillInstall(
|
|
4570
4936
|
name,
|
|
@@ -4583,7 +4949,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
4583
4949
|
});
|
|
4584
4950
|
} else {
|
|
4585
4951
|
if (format === "human") {
|
|
4586
|
-
console.log(
|
|
4952
|
+
console.log(pc12.yellow(` ! ${name}: ${result.errors.join(", ")}`));
|
|
4587
4953
|
}
|
|
4588
4954
|
failed.push({
|
|
4589
4955
|
name,
|
|
@@ -4593,7 +4959,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
4593
4959
|
} catch (err) {
|
|
4594
4960
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
4595
4961
|
if (format === "human") {
|
|
4596
|
-
console.log(
|
|
4962
|
+
console.log(pc12.red(` x ${name}: ${errorMsg}`));
|
|
4597
4963
|
}
|
|
4598
4964
|
failed.push({
|
|
4599
4965
|
name,
|
|
@@ -4628,7 +4994,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
4628
4994
|
} else {
|
|
4629
4995
|
console.log(
|
|
4630
4996
|
`
|
|
4631
|
-
${
|
|
4997
|
+
${pc12.green(`${installed.length} installed`)}, ${failed.length > 0 ? pc12.yellow(`${failed.length} failed`) : "0 failed"}`
|
|
4632
4998
|
);
|
|
4633
4999
|
if (failed.length > 0) {
|
|
4634
5000
|
process.exit(1);
|
|
@@ -4637,7 +5003,7 @@ ${pc11.green(`${installed.length} installed`)}, ${failed.length > 0 ? pc11.yello
|
|
|
4637
5003
|
}
|
|
4638
5004
|
async function handleMarketplaceSource(source, _providers, _isGlobal, format, operation, mvi) {
|
|
4639
5005
|
if (format === "human") {
|
|
4640
|
-
console.log(
|
|
5006
|
+
console.log(pc12.dim(`Searching marketplace for ${source}...`));
|
|
4641
5007
|
}
|
|
4642
5008
|
const client = new MarketplaceClient();
|
|
4643
5009
|
let skill;
|
|
@@ -4648,7 +5014,7 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
|
|
|
4648
5014
|
if (format === "json") {
|
|
4649
5015
|
emitJsonError(operation, mvi, ErrorCodes.NETWORK_ERROR, message, ErrorCategories.TRANSIENT);
|
|
4650
5016
|
}
|
|
4651
|
-
console.error(
|
|
5017
|
+
console.error(pc12.red(message));
|
|
4652
5018
|
return { success: false };
|
|
4653
5019
|
}
|
|
4654
5020
|
if (!skill) {
|
|
@@ -4656,12 +5022,12 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
|
|
|
4656
5022
|
if (format === "json") {
|
|
4657
5023
|
emitJsonError(operation, mvi, ErrorCodes.SKILL_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
|
|
4658
5024
|
}
|
|
4659
|
-
console.error(
|
|
5025
|
+
console.error(pc12.red(message));
|
|
4660
5026
|
return { success: false };
|
|
4661
5027
|
}
|
|
4662
5028
|
if (format === "human") {
|
|
4663
5029
|
console.log(
|
|
4664
|
-
` Found: ${
|
|
5030
|
+
` Found: ${pc12.bold(skill.name)} by ${skill.author} (${pc12.dim(skill.repoFullName)})`
|
|
4665
5031
|
);
|
|
4666
5032
|
}
|
|
4667
5033
|
const parsed = parseSource(skill.githubUrl);
|
|
@@ -4670,7 +5036,7 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
|
|
|
4670
5036
|
if (format === "json") {
|
|
4671
5037
|
emitJsonError(operation, mvi, ErrorCodes.INVALID_FORMAT, message, ErrorCategories.VALIDATION);
|
|
4672
5038
|
}
|
|
4673
|
-
console.error(
|
|
5039
|
+
console.error(pc12.red(message));
|
|
4674
5040
|
return { success: false };
|
|
4675
5041
|
}
|
|
4676
5042
|
try {
|
|
@@ -4682,7 +5048,7 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
|
|
|
4682
5048
|
for (const subPath of subPathCandidates) {
|
|
4683
5049
|
try {
|
|
4684
5050
|
const result = await cloneRepo(parsed.owner, parsed.repo, parsed.ref, subPath);
|
|
4685
|
-
if (subPath && !
|
|
5051
|
+
if (subPath && !existsSync12(result.localPath)) {
|
|
4686
5052
|
await result.cleanup();
|
|
4687
5053
|
continue;
|
|
4688
5054
|
}
|
|
@@ -4710,22 +5076,22 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
|
|
|
4710
5076
|
if (format === "json") {
|
|
4711
5077
|
emitJsonError(operation, mvi, ErrorCodes.NETWORK_ERROR, message, ErrorCategories.TRANSIENT);
|
|
4712
5078
|
}
|
|
4713
|
-
console.error(
|
|
5079
|
+
console.error(pc12.red(message));
|
|
4714
5080
|
return { success: false };
|
|
4715
5081
|
}
|
|
4716
5082
|
}
|
|
4717
5083
|
|
|
4718
5084
|
// src/commands/skills/list.ts
|
|
4719
|
-
import { randomUUID as
|
|
4720
|
-
import { resolveOutputFormat as
|
|
4721
|
-
import
|
|
5085
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
5086
|
+
import { resolveOutputFormat as resolveOutputFormat3 } from "@cleocode/lafs";
|
|
5087
|
+
import pc13 from "picocolors";
|
|
4722
5088
|
function registerSkillsList(parent) {
|
|
4723
5089
|
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) => {
|
|
4724
5090
|
const operation = "skills.list";
|
|
4725
5091
|
const mvi = "standard";
|
|
4726
5092
|
let format;
|
|
4727
5093
|
try {
|
|
4728
|
-
format =
|
|
5094
|
+
format = resolveOutputFormat3({
|
|
4729
5095
|
jsonFlag: opts.json ?? false,
|
|
4730
5096
|
humanFlag: (opts.human ?? false) || isHuman(),
|
|
4731
5097
|
projectDefault: "json"
|
|
@@ -4745,7 +5111,7 @@ function registerSkillsList(parent) {
|
|
|
4745
5111
|
agent: opts.agent
|
|
4746
5112
|
});
|
|
4747
5113
|
} else {
|
|
4748
|
-
console.error(
|
|
5114
|
+
console.error(pc13.red(message));
|
|
4749
5115
|
}
|
|
4750
5116
|
process.exit(1);
|
|
4751
5117
|
}
|
|
@@ -4773,21 +5139,21 @@ function registerSkillsList(parent) {
|
|
|
4773
5139
|
return;
|
|
4774
5140
|
}
|
|
4775
5141
|
if (skills.length === 0) {
|
|
4776
|
-
console.log(
|
|
5142
|
+
console.log(pc13.dim("No skills found."));
|
|
4777
5143
|
return;
|
|
4778
5144
|
}
|
|
4779
|
-
console.log(
|
|
5145
|
+
console.log(pc13.bold(`
|
|
4780
5146
|
${skills.length} skill(s) found:
|
|
4781
5147
|
`));
|
|
4782
5148
|
skills.forEach((skill, index) => {
|
|
4783
5149
|
const num = (index + 1).toString().padStart(2);
|
|
4784
5150
|
console.log(
|
|
4785
|
-
` ${
|
|
5151
|
+
` ${pc13.cyan(num)}. ${pc13.bold(skill.name.padEnd(30))} ${pc13.dim(skill.metadata?.description ?? "")}`
|
|
4786
5152
|
);
|
|
4787
5153
|
});
|
|
4788
|
-
console.log(
|
|
5154
|
+
console.log(pc13.dim(`
|
|
4789
5155
|
Install with: caamp skills install <name>`));
|
|
4790
|
-
console.log(
|
|
5156
|
+
console.log(pc13.dim(`Remove with: caamp skills remove <name>`));
|
|
4791
5157
|
});
|
|
4792
5158
|
}
|
|
4793
5159
|
function buildEnvelope4(operation, mvi, result, error) {
|
|
@@ -4798,7 +5164,7 @@ function buildEnvelope4(operation, mvi, result, error) {
|
|
|
4798
5164
|
schemaVersion: "1.0.0",
|
|
4799
5165
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4800
5166
|
operation,
|
|
4801
|
-
requestId:
|
|
5167
|
+
requestId: randomUUID4(),
|
|
4802
5168
|
transport: "cli",
|
|
4803
5169
|
strict: true,
|
|
4804
5170
|
mvi,
|
|
@@ -4823,7 +5189,7 @@ function emitJsonError4(operation, mvi, code, message, category, details = {}) {
|
|
|
4823
5189
|
}
|
|
4824
5190
|
|
|
4825
5191
|
// src/commands/skills/remove.ts
|
|
4826
|
-
import
|
|
5192
|
+
import pc14 from "picocolors";
|
|
4827
5193
|
function registerSkillsRemove(parent) {
|
|
4828
5194
|
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(
|
|
4829
5195
|
async (name, opts) => {
|
|
@@ -4873,14 +5239,14 @@ function registerSkillsRemove(parent) {
|
|
|
4873
5239
|
return;
|
|
4874
5240
|
}
|
|
4875
5241
|
if (removed.length > 0) {
|
|
4876
|
-
console.log(
|
|
5242
|
+
console.log(pc14.green(`\u2713 Removed ${pc14.bold(name)} from: ${removed.join(", ")}`));
|
|
4877
5243
|
await removeSkillFromLock(name);
|
|
4878
5244
|
} else {
|
|
4879
|
-
console.log(
|
|
5245
|
+
console.log(pc14.yellow(`Skill ${name} not found in any provider.`));
|
|
4880
5246
|
}
|
|
4881
5247
|
if (result.errors.length > 0) {
|
|
4882
5248
|
for (const err of result.errors) {
|
|
4883
|
-
console.log(
|
|
5249
|
+
console.log(pc14.red(` ${err}`));
|
|
4884
5250
|
}
|
|
4885
5251
|
}
|
|
4886
5252
|
} else {
|
|
@@ -4893,7 +5259,7 @@ function registerSkillsRemove(parent) {
|
|
|
4893
5259
|
count: { removed: 0, total: 0 }
|
|
4894
5260
|
});
|
|
4895
5261
|
} else {
|
|
4896
|
-
console.log(
|
|
5262
|
+
console.log(pc14.dim("No skills installed."));
|
|
4897
5263
|
}
|
|
4898
5264
|
return;
|
|
4899
5265
|
}
|
|
@@ -4906,18 +5272,18 @@ function registerSkillsRemove(parent) {
|
|
|
4906
5272
|
});
|
|
4907
5273
|
return;
|
|
4908
5274
|
}
|
|
4909
|
-
console.log(
|
|
5275
|
+
console.log(pc14.bold("Installed skills:"));
|
|
4910
5276
|
for (const s of skills) {
|
|
4911
5277
|
console.log(` ${s}`);
|
|
4912
5278
|
}
|
|
4913
|
-
console.log(
|
|
5279
|
+
console.log(pc14.dim("\nUse: caamp skills remove <name>"));
|
|
4914
5280
|
}
|
|
4915
5281
|
}
|
|
4916
5282
|
);
|
|
4917
5283
|
}
|
|
4918
5284
|
|
|
4919
5285
|
// src/commands/skills/update.ts
|
|
4920
|
-
import
|
|
5286
|
+
import pc15 from "picocolors";
|
|
4921
5287
|
function registerSkillsUpdate(parent) {
|
|
4922
5288
|
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) => {
|
|
4923
5289
|
const operation = "skills.update";
|
|
@@ -4951,12 +5317,12 @@ function registerSkillsUpdate(parent) {
|
|
|
4951
5317
|
count: { updated: 0, failed: 0, skipped: 0 }
|
|
4952
5318
|
});
|
|
4953
5319
|
} else {
|
|
4954
|
-
console.log(
|
|
5320
|
+
console.log(pc15.dim("No tracked skills to update."));
|
|
4955
5321
|
}
|
|
4956
5322
|
return;
|
|
4957
5323
|
}
|
|
4958
5324
|
if (format === "human") {
|
|
4959
|
-
console.log(
|
|
5325
|
+
console.log(pc15.dim(`Checking ${entries.length} skill(s) for updates...`));
|
|
4960
5326
|
}
|
|
4961
5327
|
const outdated = [];
|
|
4962
5328
|
for (const [name] of entries) {
|
|
@@ -4978,19 +5344,19 @@ function registerSkillsUpdate(parent) {
|
|
|
4978
5344
|
count: { updated: 0, failed: 0, skipped: 0 }
|
|
4979
5345
|
});
|
|
4980
5346
|
} else {
|
|
4981
|
-
console.log(
|
|
5347
|
+
console.log(pc15.green("\nAll skills are up to date."));
|
|
4982
5348
|
}
|
|
4983
5349
|
return;
|
|
4984
5350
|
}
|
|
4985
5351
|
if (format === "human") {
|
|
4986
|
-
console.log(
|
|
5352
|
+
console.log(pc15.yellow(`
|
|
4987
5353
|
${outdated.length} skill(s) have updates available:
|
|
4988
5354
|
`));
|
|
4989
5355
|
for (const skill of outdated) {
|
|
4990
5356
|
const current = skill.currentVersion?.slice(0, 12) ?? "?";
|
|
4991
5357
|
const latest = skill.latestVersion ?? "?";
|
|
4992
5358
|
console.log(
|
|
4993
|
-
` ${
|
|
5359
|
+
` ${pc15.bold(skill.name)} ${pc15.dim(current)} ${pc15.dim("->")} ${pc15.cyan(latest)}`
|
|
4994
5360
|
);
|
|
4995
5361
|
}
|
|
4996
5362
|
}
|
|
@@ -4998,11 +5364,11 @@ ${outdated.length} skill(s) have updates available:
|
|
|
4998
5364
|
const readline = await import("readline");
|
|
4999
5365
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
5000
5366
|
const answer = await new Promise((resolve3) => {
|
|
5001
|
-
rl.question(
|
|
5367
|
+
rl.question(pc15.dim("\nProceed with update? [y/N] "), resolve3);
|
|
5002
5368
|
});
|
|
5003
5369
|
rl.close();
|
|
5004
5370
|
if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
|
|
5005
|
-
console.log(
|
|
5371
|
+
console.log(pc15.dim("Update cancelled."));
|
|
5006
5372
|
return;
|
|
5007
5373
|
}
|
|
5008
5374
|
}
|
|
@@ -5016,7 +5382,7 @@ ${outdated.length} skill(s) have updates available:
|
|
|
5016
5382
|
const entry = tracked[skill.name];
|
|
5017
5383
|
if (!entry) continue;
|
|
5018
5384
|
if (format === "human") {
|
|
5019
|
-
console.log(
|
|
5385
|
+
console.log(pc15.dim(`Updating ${pc15.bold(skill.name)}...`));
|
|
5020
5386
|
}
|
|
5021
5387
|
try {
|
|
5022
5388
|
const parsed = parseSource(entry.source);
|
|
@@ -5038,7 +5404,7 @@ ${outdated.length} skill(s) have updates available:
|
|
|
5038
5404
|
} else {
|
|
5039
5405
|
if (format === "human") {
|
|
5040
5406
|
console.log(
|
|
5041
|
-
|
|
5407
|
+
pc15.yellow(
|
|
5042
5408
|
` Skipped ${skill.name}: source type "${parsed.type}" does not support auto-update`
|
|
5043
5409
|
)
|
|
5044
5410
|
);
|
|
@@ -5050,7 +5416,7 @@ ${outdated.length} skill(s) have updates available:
|
|
|
5050
5416
|
const providers = entry.agents.map((a) => getProvider(a)).filter((p) => p !== void 0);
|
|
5051
5417
|
if (providers.length === 0) {
|
|
5052
5418
|
if (format === "human") {
|
|
5053
|
-
console.log(
|
|
5419
|
+
console.log(pc15.yellow(` Skipped ${skill.name}: no valid providers found`));
|
|
5054
5420
|
}
|
|
5055
5421
|
skipped.push(skill.name);
|
|
5056
5422
|
continue;
|
|
@@ -5075,18 +5441,18 @@ ${outdated.length} skill(s) have updates available:
|
|
|
5075
5441
|
skill.latestVersion
|
|
5076
5442
|
);
|
|
5077
5443
|
if (format === "human") {
|
|
5078
|
-
console.log(
|
|
5444
|
+
console.log(pc15.green(` Updated ${pc15.bold(skill.name)}`));
|
|
5079
5445
|
}
|
|
5080
5446
|
updated.push(skill.name);
|
|
5081
5447
|
} else {
|
|
5082
5448
|
if (format === "human") {
|
|
5083
|
-
console.log(
|
|
5449
|
+
console.log(pc15.red(` Failed to update ${skill.name}: no agents linked`));
|
|
5084
5450
|
}
|
|
5085
5451
|
failed.push({ name: skill.name, error: "no agents linked" });
|
|
5086
5452
|
}
|
|
5087
5453
|
if (installResult.errors.length > 0 && format === "human") {
|
|
5088
5454
|
for (const err of installResult.errors) {
|
|
5089
|
-
console.log(
|
|
5455
|
+
console.log(pc15.yellow(` ${err}`));
|
|
5090
5456
|
}
|
|
5091
5457
|
}
|
|
5092
5458
|
} finally {
|
|
@@ -5095,7 +5461,7 @@ ${outdated.length} skill(s) have updates available:
|
|
|
5095
5461
|
} catch (err) {
|
|
5096
5462
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5097
5463
|
if (format === "human") {
|
|
5098
|
-
console.log(
|
|
5464
|
+
console.log(pc15.red(` Failed to update ${skill.name}: ${msg}`));
|
|
5099
5465
|
}
|
|
5100
5466
|
failed.push({ name: skill.name, error: msg });
|
|
5101
5467
|
}
|
|
@@ -5115,24 +5481,24 @@ ${outdated.length} skill(s) have updates available:
|
|
|
5115
5481
|
}
|
|
5116
5482
|
console.log();
|
|
5117
5483
|
if (updated.length > 0) {
|
|
5118
|
-
console.log(
|
|
5484
|
+
console.log(pc15.green(`Updated ${updated.length} skill(s).`));
|
|
5119
5485
|
}
|
|
5120
5486
|
if (failed.length > 0) {
|
|
5121
|
-
console.log(
|
|
5487
|
+
console.log(pc15.red(`Failed to update ${failed.length} skill(s).`));
|
|
5122
5488
|
}
|
|
5123
5489
|
});
|
|
5124
5490
|
}
|
|
5125
5491
|
|
|
5126
5492
|
// src/commands/skills/validate.ts
|
|
5127
|
-
import { resolveOutputFormat as
|
|
5128
|
-
import
|
|
5493
|
+
import { resolveOutputFormat as resolveOutputFormat4 } from "@cleocode/lafs";
|
|
5494
|
+
import pc16 from "picocolors";
|
|
5129
5495
|
function registerSkillsValidate(parent) {
|
|
5130
5496
|
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) => {
|
|
5131
5497
|
const operation = "skills.validate";
|
|
5132
5498
|
const mvi = "standard";
|
|
5133
5499
|
let format;
|
|
5134
5500
|
try {
|
|
5135
|
-
format =
|
|
5501
|
+
format = resolveOutputFormat4({
|
|
5136
5502
|
jsonFlag: opts.json ?? false,
|
|
5137
5503
|
humanFlag: (opts.human ?? false) || isHuman(),
|
|
5138
5504
|
projectDefault: "json"
|
|
@@ -5165,7 +5531,7 @@ function registerSkillsValidate(parent) {
|
|
|
5165
5531
|
}
|
|
5166
5532
|
);
|
|
5167
5533
|
} else {
|
|
5168
|
-
console.error(
|
|
5534
|
+
console.error(pc16.red(message));
|
|
5169
5535
|
}
|
|
5170
5536
|
process.exit(1);
|
|
5171
5537
|
}
|
|
@@ -5187,12 +5553,12 @@ function registerSkillsValidate(parent) {
|
|
|
5187
5553
|
console.log(JSON.stringify(envelope, null, 2));
|
|
5188
5554
|
} else {
|
|
5189
5555
|
if (result.valid) {
|
|
5190
|
-
console.log(
|
|
5556
|
+
console.log(pc16.green(`\u2713 ${path} is valid`));
|
|
5191
5557
|
} else {
|
|
5192
|
-
console.log(
|
|
5558
|
+
console.log(pc16.red(`\u2717 ${path} has validation errors`));
|
|
5193
5559
|
}
|
|
5194
5560
|
for (const issue of result.issues) {
|
|
5195
|
-
const icon = issue.level === "error" ?
|
|
5561
|
+
const icon = issue.level === "error" ? pc16.red("\u2717") : pc16.yellow("!");
|
|
5196
5562
|
console.log(` ${icon} [${issue.field}] ${issue.message}`);
|
|
5197
5563
|
}
|
|
5198
5564
|
}
|
|
@@ -5214,6 +5580,9 @@ function registerSkillsCommands(program2) {
|
|
|
5214
5580
|
registerSkillsInit(skills);
|
|
5215
5581
|
registerSkillsAudit(skills);
|
|
5216
5582
|
registerSkillsValidate(skills);
|
|
5583
|
+
registerSkillsDoctorMigrate(skills);
|
|
5584
|
+
registerSkillsDoctor(skills);
|
|
5585
|
+
registerSkillsDoctorAdopt(skills);
|
|
5217
5586
|
}
|
|
5218
5587
|
|
|
5219
5588
|
// src/cli.ts
|