@cleocode/caamp 2026.9.18 → 2026.9.20

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/cli.js CHANGED
@@ -1,36 +1,42 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ ErrorCategories,
4
+ ErrorCodes,
3
5
  MarketplaceClient,
4
6
  PiHarness,
5
7
  RECOMMENDATION_ERROR_CODES,
8
+ SkillInstallError,
9
+ buildEnvelope,
6
10
  checkSkillUpdate,
11
+ cloneGitLabRepo,
12
+ cloneRepo,
7
13
  detectAllProviders,
8
14
  detectMcpInstallations,
9
15
  detectProjectProviders,
10
- discoverSkill,
11
16
  discoverSkillsMulti,
12
- dispatchInstallSkillAcrossProviders,
13
17
  dispatchRemoveSkillAcrossProviders,
18
+ emitError,
19
+ emitJsonError,
14
20
  fetchWithTimeout,
15
21
  formatNetworkError,
16
22
  formatSkillRecommendations,
17
23
  getHarnessFor,
18
24
  getInstalledProviders,
19
- getSkill,
20
25
  getSkillDir,
21
26
  getTrackedSkills,
27
+ handleFormatError,
22
28
  installBatchWithRollback,
23
29
  installMcpServer,
30
+ installResolvedSkill,
31
+ installSkillFromSource,
24
32
  isCatalogAvailable,
25
33
  isHuman,
26
- isMarketplaceScoped,
27
- isQuiet,
28
34
  isVerbose,
29
35
  listAllMcpServers,
30
36
  listCanonicalSkills,
31
37
  listMcpServers,
32
38
  listProfiles,
33
- listSkills,
39
+ outputSuccess,
34
40
  parseSource,
35
41
  readConfig,
36
42
  readLockFile,
@@ -40,6 +46,7 @@ import {
40
46
  removeMcpServerFromAll,
41
47
  removeSkillFromLock,
42
48
  resolveDefaultTargetProviders,
49
+ resolveFormat,
43
50
  resolveProfile,
44
51
  scanDirectory,
45
52
  scanFile,
@@ -47,11 +54,12 @@ import {
47
54
  setHuman,
48
55
  setQuiet,
49
56
  setVerbose,
57
+ syncGlobalInstructions,
50
58
  toSarif,
51
59
  tokenizeCriteriaValue,
52
60
  updateInstructionsSingleOperation,
53
61
  validateSkill
54
- } from "./chunk-52ZWZXEN.js";
62
+ } from "./chunk-QC6CHW72.js";
55
63
  import {
56
64
  buildSkillsMap,
57
65
  checkAllInjections,
@@ -64,7 +72,7 @@ import {
64
72
  groupByInstructFile,
65
73
  injectAll,
66
74
  providerSupports
67
- } from "./chunk-G3CEXEW2.js";
75
+ } from "./chunk-JELNPPLN.js";
68
76
  import {
69
77
  CANONICAL_HOOK_EVENTS,
70
78
  buildHookMatrix,
@@ -75,7 +83,6 @@ import {
75
83
  translateToAll
76
84
  } from "./chunk-YCAANESR.js";
77
85
  import {
78
- buildSkillSubPathCandidates,
79
86
  resolveProviderConfigPath,
80
87
  resolveProviderSkillsDir
81
88
  } from "./chunk-HDZYZTR2.js";
@@ -152,7 +159,7 @@ function emitSuccess(operation, result, mvi = "standard") {
152
159
  };
153
160
  console.log(JSON.stringify(envelope, null, 2));
154
161
  }
155
- function emitError(operation, error, mvi = "standard") {
162
+ function emitError2(operation, error, mvi = "standard") {
156
163
  let envelope;
157
164
  if (error instanceof LAFSCommandError) {
158
165
  envelope = {
@@ -203,7 +210,7 @@ async function runLafsCommand(command, mvi, action) {
203
210
  const result = await action();
204
211
  emitSuccess(command, result, mvi);
205
212
  } catch (error) {
206
- emitError(command, error, mvi);
213
+ emitError2(command, error, mvi);
207
214
  process.exit(1);
208
215
  }
209
216
  }
@@ -498,121 +505,6 @@ function registerAdvancedCommands(program2) {
498
505
  // src/commands/config.ts
499
506
  import { existsSync } from "fs";
500
507
  import pc from "picocolors";
501
-
502
- // src/core/lafs.ts
503
- import { randomUUID as randomUUID2 } from "crypto";
504
- import { resolveOutputFormat } from "@cleocode/lafs";
505
- function resolveFormat(options) {
506
- return resolveOutputFormat({
507
- jsonFlag: options.jsonFlag ?? false,
508
- humanFlag: (options.humanFlag ?? false) || isHuman(),
509
- projectDefault: options.projectDefault ?? "json"
510
- }).format;
511
- }
512
- function buildEnvelope(operation, mvi, result, error, page = null, sessionId, warnings) {
513
- return {
514
- $schema: "https://lafs.dev/schemas/v1/envelope.schema.json",
515
- _meta: {
516
- specVersion: "1.0.0",
517
- schemaVersion: "1.0.0",
518
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
519
- operation,
520
- requestId: randomUUID2(),
521
- transport: "cli",
522
- strict: true,
523
- mvi,
524
- contextVersion: 0,
525
- ...sessionId && { sessionId },
526
- ...warnings && warnings.length > 0 && { warnings }
527
- },
528
- success: error === null,
529
- result,
530
- error,
531
- page
532
- };
533
- }
534
- function emitError2(operation, mvi, code, message, category, details = {}, exitCode = 1) {
535
- const envelope = buildEnvelope(operation, mvi, null, {
536
- code,
537
- message,
538
- category,
539
- retryable: category === "TRANSIENT" || category === "RATE_LIMIT",
540
- retryAfterMs: null,
541
- details
542
- });
543
- console.error(JSON.stringify(envelope, null, 2));
544
- process.exit(exitCode);
545
- }
546
- function emitJsonError(operation, mvi, code, message, category, details = {}) {
547
- const envelope = buildEnvelope(operation, mvi, null, {
548
- code,
549
- message,
550
- category,
551
- retryable: category === "TRANSIENT" || category === "RATE_LIMIT",
552
- retryAfterMs: null,
553
- details
554
- });
555
- console.error(JSON.stringify(envelope, null, 2));
556
- }
557
- function outputSuccess(operation, mvi, result, page, sessionId, warnings) {
558
- const envelope = buildEnvelope(operation, mvi, result, null, page ?? null, sessionId, warnings);
559
- if (isQuiet() && !envelope.error) {
560
- return;
561
- }
562
- console.log(JSON.stringify(envelope, null, 2));
563
- }
564
- function handleFormatError(error, operation, mvi, jsonFlag) {
565
- const message = error instanceof Error ? error.message : String(error);
566
- if (jsonFlag) {
567
- emitJsonError(operation, mvi, "E_FORMAT_CONFLICT", message, "VALIDATION");
568
- } else {
569
- console.error(message);
570
- }
571
- process.exit(1);
572
- }
573
- var ErrorCategories = {
574
- VALIDATION: "VALIDATION",
575
- AUTH: "AUTH",
576
- PERMISSION: "PERMISSION",
577
- NOT_FOUND: "NOT_FOUND",
578
- CONFLICT: "CONFLICT",
579
- RATE_LIMIT: "RATE_LIMIT",
580
- TRANSIENT: "TRANSIENT",
581
- INTERNAL: "INTERNAL",
582
- CONTRACT: "CONTRACT",
583
- MIGRATION: "MIGRATION"
584
- };
585
- var ErrorCodes = {
586
- // Format errors
587
- FORMAT_CONFLICT: "E_FORMAT_CONFLICT",
588
- INVALID_JSON: "E_INVALID_JSON",
589
- // Not found errors
590
- SKILL_NOT_FOUND: "E_SKILL_NOT_FOUND",
591
- PROVIDER_NOT_FOUND: "E_PROVIDER_NOT_FOUND",
592
- MCP_SERVER_NOT_FOUND: "E_MCP_SERVER_NOT_FOUND",
593
- FILE_NOT_FOUND: "E_FILE_NOT_FOUND",
594
- // Validation errors
595
- INVALID_INPUT: "E_INVALID_INPUT",
596
- INVALID_CONSTRAINT: "E_INVALID_CONSTRAINT",
597
- INVALID_FORMAT: "E_INVALID_FORMAT",
598
- // Operation errors
599
- INSTALL_FAILED: "E_INSTALL_FAILED",
600
- // Skills federation gates (T9564 — Sphere B)
601
- SKILL_TRUST_GATE_BLOCKED: "E_SKILL_TRUST_GATE_BLOCKED",
602
- FEDERATION_UNKNOWN_SOURCE_INTERACTIVE_REQUIRED: "E_FEDERATION_UNKNOWN_SOURCE_INTERACTIVE_REQUIRED",
603
- FEDERATION_CHECKSUM_MISMATCH: "E_FEDERATION_CHECKSUM_MISMATCH",
604
- REMOVE_FAILED: "E_REMOVE_FAILED",
605
- UPDATE_FAILED: "E_UPDATE_FAILED",
606
- VALIDATION_FAILED: "E_VALIDATION_FAILED",
607
- AUDIT_FAILED: "E_AUDIT_FAILED",
608
- // System errors
609
- NETWORK_ERROR: "E_NETWORK_ERROR",
610
- FILE_SYSTEM_ERROR: "E_FILE_SYSTEM_ERROR",
611
- PERMISSION_DENIED: "E_PERMISSION_DENIED",
612
- INTERNAL_ERROR: "E_INTERNAL_ERROR"
613
- };
614
-
615
- // src/commands/config.ts
616
508
  function registerConfigCommand(program2) {
617
509
  const config = program2.command("config").description("View provider configuration");
618
510
  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(
@@ -1258,6 +1150,8 @@ function registerInstructionsCheck(parent) {
1258
1150
  providers = getAllProviders();
1259
1151
  } else if (opts.agent.length > 0) {
1260
1152
  providers = opts.agent.map((a) => getProvider(a)).filter((p) => p !== void 0);
1153
+ } else if (opts.global) {
1154
+ providers = getInstalledProviders();
1261
1155
  } else {
1262
1156
  providers = resolveDefaultTargetProviders();
1263
1157
  }
@@ -1416,12 +1310,24 @@ function registerInstructionsInject(parent) {
1416
1310
  }
1417
1311
  }
1418
1312
  }
1419
- if (genericProviders.length > 0) {
1420
- const genericResults = await injectAll(genericProviders, process.cwd(), scope, content);
1421
- for (const [file, action] of genericResults) {
1422
- results.set(file, action);
1313
+ const failed = [];
1314
+ for (const provider of genericProviders) {
1315
+ try {
1316
+ for (const [file, action] of await injectAll(
1317
+ [provider],
1318
+ process.cwd(),
1319
+ scope,
1320
+ content
1321
+ )) {
1322
+ results.set(file, action);
1323
+ }
1324
+ } catch (err) {
1325
+ const error = err instanceof Error ? err.message : String(err);
1326
+ failed.push({ provider: provider.id, error });
1327
+ if (format === "human") console.log(pc4.red(` x ${provider.id}: ${error}`));
1423
1328
  }
1424
1329
  }
1330
+ if (failed.length > 0) process.exitCode = 1;
1425
1331
  const injected = [];
1426
1332
  for (const [file] of results) {
1427
1333
  injected.push(file);
@@ -1430,7 +1336,8 @@ function registerInstructionsInject(parent) {
1430
1336
  outputSuccess(operation, mvi, {
1431
1337
  injected,
1432
1338
  providers: providers.map((p) => p.id),
1433
- count: results.size
1339
+ count: results.size,
1340
+ ...failed.length > 0 ? { failed } : {}
1434
1341
  });
1435
1342
  } else {
1436
1343
  for (const [file, action] of results) {
@@ -1446,6 +1353,44 @@ ${results.size} file(s) processed.`));
1446
1353
 
1447
1354
  // src/commands/instructions/update.ts
1448
1355
  import pc5 from "picocolors";
1356
+ async function updateGlobal(format) {
1357
+ const operation = "instructions.update";
1358
+ const mvi = "standard";
1359
+ const result = await syncGlobalInstructions();
1360
+ const updated = result.files.filter((file) => file.action !== "intact" && file.action !== "failed").map((file) => file.path);
1361
+ const failed = [
1362
+ ...result.files.filter((file) => file.action === "failed").map((file) => ({ provider: file.providers.join(","), error: file.error ?? "unknown" })),
1363
+ ...result.findings.map((finding) => ({
1364
+ provider: "hub",
1365
+ error: `${finding.kind}: ${finding.path} (${finding.reason})`
1366
+ }))
1367
+ ];
1368
+ if (failed.length > 0) process.exitCode = 1;
1369
+ if (format === "json") {
1370
+ outputSuccess(operation, mvi, {
1371
+ status: result.status,
1372
+ updated,
1373
+ failed,
1374
+ files: result.files,
1375
+ skippedProviders: result.skippedProviders,
1376
+ count: { updated: updated.length, failed: failed.length }
1377
+ });
1378
+ return;
1379
+ }
1380
+ if (result.status === "no-providers") {
1381
+ console.log(pc5.yellow("No provider installations detected."));
1382
+ return;
1383
+ }
1384
+ for (const file of result.files) {
1385
+ const icon = file.action === "failed" ? pc5.red("x") : pc5.green("\u2713");
1386
+ console.log(` ${icon} ${file.path} (${file.action}${file.error ? `: ${file.error}` : ""})`);
1387
+ }
1388
+ for (const finding of result.findings) {
1389
+ console.log(` ${pc5.red("x")} ${finding.kind}: ${finding.path} \u2014 ${finding.reason}`);
1390
+ }
1391
+ console.log(pc5.bold(`
1392
+ ${updated.length} file(s) updated.`));
1393
+ }
1449
1394
  function registerInstructionsUpdate(parent) {
1450
1395
  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) => {
1451
1396
  const operation = "instructions.update";
@@ -1468,8 +1413,12 @@ function registerInstructionsUpdate(parent) {
1468
1413
  );
1469
1414
  process.exit(1);
1470
1415
  }
1416
+ if (opts.global) {
1417
+ await updateGlobal(format);
1418
+ return;
1419
+ }
1471
1420
  const providers = resolveDefaultTargetProviders();
1472
- const scope = opts.global ? "global" : "project";
1421
+ const scope = "project";
1473
1422
  const content = generateInjectionContent();
1474
1423
  const harnessProviders = [];
1475
1424
  const genericProviders = [];
@@ -1503,9 +1452,21 @@ function registerInstructionsUpdate(parent) {
1503
1452
  }
1504
1453
  const providerIds = new Set(needsUpdate.map((c) => c.provider));
1505
1454
  const toUpdate = genericProviders.filter((p) => providerIds.has(p.id));
1506
- const results = await injectAll(toUpdate, process.cwd(), scope, content);
1507
- const harnessScope = scope === "global" ? { kind: "global" } : { kind: "project", projectDir: process.cwd() };
1508
- const harnessFailures = [];
1455
+ const failures = [];
1456
+ const results = /* @__PURE__ */ new Map();
1457
+ for (const provider of toUpdate) {
1458
+ try {
1459
+ for (const [file, action] of await injectAll([provider], process.cwd(), scope, content)) {
1460
+ results.set(file, action);
1461
+ }
1462
+ } catch (err) {
1463
+ failures.push({
1464
+ provider: provider.id,
1465
+ error: err instanceof Error ? err.message : String(err)
1466
+ });
1467
+ }
1468
+ }
1469
+ const harnessScope = { kind: "project", projectDir: process.cwd() };
1509
1470
  for (const provider of harnessProviders) {
1510
1471
  const harness = getHarnessFor(provider);
1511
1472
  if (harness === null) continue;
@@ -1513,22 +1474,19 @@ function registerInstructionsUpdate(parent) {
1513
1474
  await harness.injectInstructions(content, harnessScope);
1514
1475
  results.set(`${provider.id}:AGENTS.md`, "updated");
1515
1476
  } catch (err) {
1516
- harnessFailures.push({
1477
+ failures.push({
1517
1478
  provider: provider.id,
1518
1479
  error: err instanceof Error ? err.message : String(err)
1519
1480
  });
1520
1481
  }
1521
1482
  }
1522
- const updated = [];
1523
- for (const [file] of results) {
1524
- updated.push(file);
1525
- }
1483
+ const updated = [...results.keys()];
1526
1484
  if (format === "human") {
1527
1485
  console.log();
1528
1486
  for (const [file, action] of results) {
1529
1487
  console.log(` ${pc5.green("\u2713")} ${file} (${action})`);
1530
1488
  }
1531
- for (const failure of harnessFailures) {
1489
+ for (const failure of failures) {
1532
1490
  console.log(` ${pc5.red("x")} ${failure.provider}: ${failure.error}`);
1533
1491
  }
1534
1492
  console.log(pc5.bold(`
@@ -1537,8 +1495,8 @@ ${results.size} file(s) updated.`));
1537
1495
  if (format === "json") {
1538
1496
  outputSuccess(operation, mvi, {
1539
1497
  updated,
1540
- failed: harnessFailures,
1541
- count: { updated: updated.length, failed: harnessFailures.length }
1498
+ failed: failures,
1499
+ count: { updated: updated.length, failed: failures.length }
1542
1500
  });
1543
1501
  }
1544
1502
  });
@@ -1883,60 +1841,8 @@ function registerMcpCommands(program2) {
1883
1841
  // src/commands/pi/cant.ts
1884
1842
  import { existsSync as existsSync4 } from "fs";
1885
1843
  import { writeFile } from "fs/promises";
1886
- import { tmpdir as tmpdir3 } from "os";
1887
- import { isAbsolute, join as join5 } from "path";
1888
-
1889
- // src/core/sources/github.ts
1890
- import { mkdtemp, rm } from "fs/promises";
1891
1844
  import { tmpdir } from "os";
1892
- import { join as join3 } from "path";
1893
- import { simpleGit } from "simple-git";
1894
- async function cloneRepo(owner, repo, ref, subPath) {
1895
- const tmpDir = await mkdtemp(join3(tmpdir(), "caamp-"));
1896
- const repoUrl = `https://github.com/${owner}/${repo}.git`;
1897
- const git = simpleGit();
1898
- const cloneOptions = ["--depth", "1"];
1899
- if (ref) {
1900
- cloneOptions.push("--branch", ref);
1901
- }
1902
- await git.clone(repoUrl, tmpDir, cloneOptions);
1903
- const localPath = subPath ? join3(tmpDir, subPath) : tmpDir;
1904
- return {
1905
- localPath,
1906
- cleanup: async () => {
1907
- try {
1908
- await rm(tmpDir, { recursive: true });
1909
- } catch {
1910
- }
1911
- }
1912
- };
1913
- }
1914
-
1915
- // src/core/sources/gitlab.ts
1916
- import { mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
1917
- import { tmpdir as tmpdir2 } from "os";
1918
- import { join as join4 } from "path";
1919
- import { simpleGit as simpleGit2 } from "simple-git";
1920
- async function cloneGitLabRepo(owner, repo, ref, subPath) {
1921
- const tmpDir = await mkdtemp2(join4(tmpdir2(), "caamp-gl-"));
1922
- const repoUrl = `https://gitlab.com/${owner}/${repo}.git`;
1923
- const git = simpleGit2();
1924
- const cloneOptions = ["--depth", "1"];
1925
- if (ref) {
1926
- cloneOptions.push("--branch", ref);
1927
- }
1928
- await git.clone(repoUrl, tmpDir, cloneOptions);
1929
- const localPath = subPath ? join4(tmpDir, subPath) : tmpDir;
1930
- return {
1931
- localPath,
1932
- cleanup: async () => {
1933
- try {
1934
- await rm2(tmpDir, { recursive: true });
1935
- } catch {
1936
- }
1937
- }
1938
- };
1939
- }
1845
+ import { isAbsolute, join as join3 } from "path";
1940
1846
 
1941
1847
  // src/commands/pi/common.ts
1942
1848
  var PI_ERROR_CODES = {
@@ -1999,7 +1905,7 @@ function resolveProjectDir2(tier, explicit) {
1999
1905
  // src/commands/pi/cant.ts
2000
1906
  async function resolveCantSource(source) {
2001
1907
  if (isAbsolute(source) || source.startsWith("./") || source.startsWith("../") || source.startsWith("~")) {
2002
- const expanded = source.startsWith("~/") ? join5(process.env["HOME"] ?? "", source.slice(2)) : source;
1908
+ const expanded = source.startsWith("~/") ? join3(process.env["HOME"] ?? "", source.slice(2)) : source;
2003
1909
  if (!existsSync4(expanded)) {
2004
1910
  throw new LAFSCommandError(
2005
1911
  PI_ERROR_CODES.NOT_FOUND,
@@ -2019,7 +1925,7 @@ async function resolveCantSource(source) {
2019
1925
  const parsed2 = parseSource(source);
2020
1926
  if (parsed2.type === "github" && parsed2.owner !== void 0 && parsed2.repo !== void 0) {
2021
1927
  const cloneResult = await cloneRepo(parsed2.owner, parsed2.repo, parsed2.ref);
2022
- const filePath = parsed2.path !== void 0 ? join5(cloneResult.localPath, parsed2.path) : cloneResult.localPath;
1928
+ const filePath = parsed2.path !== void 0 ? join3(cloneResult.localPath, parsed2.path) : cloneResult.localPath;
2023
1929
  if (!existsSync4(filePath)) {
2024
1930
  await cloneResult.cleanup();
2025
1931
  throw new LAFSCommandError(
@@ -2037,7 +1943,7 @@ async function resolveCantSource(source) {
2037
1943
  }
2038
1944
  if (parsed2.type === "gitlab" && parsed2.owner !== void 0 && parsed2.repo !== void 0) {
2039
1945
  const cloneResult = await cloneGitLabRepo(parsed2.owner, parsed2.repo, parsed2.ref);
2040
- const filePath = parsed2.path !== void 0 ? join5(cloneResult.localPath, parsed2.path) : cloneResult.localPath;
1946
+ const filePath = parsed2.path !== void 0 ? join3(cloneResult.localPath, parsed2.path) : cloneResult.localPath;
2041
1947
  if (!existsSync4(filePath)) {
2042
1948
  await cloneResult.cleanup();
2043
1949
  throw new LAFSCommandError(
@@ -2064,7 +1970,7 @@ async function resolveCantSource(source) {
2064
1970
  }
2065
1971
  const body = await resp.text();
2066
1972
  const baseName = inferNameFromUrl(source);
2067
- const tmp = join5(tmpdir3(), `caamp-pi-cant-${process.pid}-${Date.now()}-${baseName}.cant`);
1973
+ const tmp = join3(tmpdir(), `caamp-pi-cant-${process.pid}-${Date.now()}-${baseName}.cant`);
2068
1974
  await writeFile(tmp, body, "utf8");
2069
1975
  return {
2070
1976
  localPath: tmp,
@@ -2080,7 +1986,7 @@ async function resolveCantSource(source) {
2080
1986
  const parsed = parseSource(source);
2081
1987
  if (parsed.type === "github" && parsed.owner !== void 0 && parsed.repo !== void 0) {
2082
1988
  const cloneResult = await cloneRepo(parsed.owner, parsed.repo, parsed.ref);
2083
- const filePath = parsed.path !== void 0 ? join5(cloneResult.localPath, parsed.path) : cloneResult.localPath;
1989
+ const filePath = parsed.path !== void 0 ? join3(cloneResult.localPath, parsed.path) : cloneResult.localPath;
2084
1990
  if (!existsSync4(filePath)) {
2085
1991
  await cloneResult.cleanup();
2086
1992
  throw new LAFSCommandError(
@@ -2244,11 +2150,11 @@ function registerPiCantCommands(parent) {
2244
2150
  // src/commands/pi/extensions.ts
2245
2151
  import { existsSync as existsSync5 } from "fs";
2246
2152
  import { writeFile as writeFile2 } from "fs/promises";
2247
- import { tmpdir as tmpdir4 } from "os";
2248
- import { isAbsolute as isAbsolute2, join as join6 } from "path";
2153
+ import { tmpdir as tmpdir2 } from "os";
2154
+ import { isAbsolute as isAbsolute2, join as join4 } from "path";
2249
2155
  async function resolveExtensionSource(source) {
2250
2156
  if (isAbsolute2(source) || source.startsWith("./") || source.startsWith("../") || source.startsWith("~")) {
2251
- const expanded = source.startsWith("~/") ? join6(process.env["HOME"] ?? "", source.slice(2)) : source;
2157
+ const expanded = source.startsWith("~/") ? join4(process.env["HOME"] ?? "", source.slice(2)) : source;
2252
2158
  if (!existsSync5(expanded)) {
2253
2159
  throw new LAFSCommandError(
2254
2160
  PI_ERROR_CODES.NOT_FOUND,
@@ -2269,7 +2175,7 @@ async function resolveExtensionSource(source) {
2269
2175
  const parsed2 = parseSource(source);
2270
2176
  if (parsed2.type === "github" && parsed2.owner !== void 0 && parsed2.repo !== void 0) {
2271
2177
  const cloneResult = await cloneRepo(parsed2.owner, parsed2.repo, parsed2.ref);
2272
- const filePath = parsed2.path !== void 0 ? join6(cloneResult.localPath, parsed2.path) : cloneResult.localPath;
2178
+ const filePath = parsed2.path !== void 0 ? join4(cloneResult.localPath, parsed2.path) : cloneResult.localPath;
2273
2179
  if (!existsSync5(filePath)) {
2274
2180
  await cloneResult.cleanup();
2275
2181
  throw new LAFSCommandError(
@@ -2287,7 +2193,7 @@ async function resolveExtensionSource(source) {
2287
2193
  }
2288
2194
  if (parsed2.type === "gitlab" && parsed2.owner !== void 0 && parsed2.repo !== void 0) {
2289
2195
  const cloneResult = await cloneGitLabRepo(parsed2.owner, parsed2.repo, parsed2.ref);
2290
- const filePath = parsed2.path !== void 0 ? join6(cloneResult.localPath, parsed2.path) : cloneResult.localPath;
2196
+ const filePath = parsed2.path !== void 0 ? join4(cloneResult.localPath, parsed2.path) : cloneResult.localPath;
2291
2197
  if (!existsSync5(filePath)) {
2292
2198
  await cloneResult.cleanup();
2293
2199
  throw new LAFSCommandError(
@@ -2314,7 +2220,7 @@ async function resolveExtensionSource(source) {
2314
2220
  }
2315
2221
  const body = await resp.text();
2316
2222
  const baseName = inferNameFromUrl2(source);
2317
- const tmp = join6(tmpdir4(), `caamp-pi-ext-${process.pid}-${Date.now()}-${baseName}.ts`);
2223
+ const tmp = join4(tmpdir2(), `caamp-pi-ext-${process.pid}-${Date.now()}-${baseName}.ts`);
2318
2224
  await writeFile2(tmp, body, "utf8");
2319
2225
  return {
2320
2226
  localPath: tmp,
@@ -2330,7 +2236,7 @@ async function resolveExtensionSource(source) {
2330
2236
  const parsed = parseSource(source);
2331
2237
  if (parsed.type === "github" && parsed.owner !== void 0 && parsed.repo !== void 0) {
2332
2238
  const cloneResult = await cloneRepo(parsed.owner, parsed.repo, parsed.ref);
2333
- const filePath = parsed.path !== void 0 ? join6(cloneResult.localPath, parsed.path) : cloneResult.localPath;
2239
+ const filePath = parsed.path !== void 0 ? join4(cloneResult.localPath, parsed.path) : cloneResult.localPath;
2334
2240
  if (!existsSync5(filePath)) {
2335
2241
  await cloneResult.cleanup();
2336
2242
  throw new LAFSCommandError(
@@ -2599,7 +2505,7 @@ function registerPiModelsCommands(parent) {
2599
2505
 
2600
2506
  // src/commands/pi/prompts.ts
2601
2507
  import { existsSync as existsSync6 } from "fs";
2602
- import { join as join7, resolve } from "path";
2508
+ import { join as join5, resolve } from "path";
2603
2509
  function inferPromptName(sourceDir) {
2604
2510
  const normalized = resolve(sourceDir).replace(/[\\/]+$/, "");
2605
2511
  const base = normalized.split(/[\\/]/).pop();
@@ -2629,7 +2535,7 @@ function registerPiPromptsCommands(parent) {
2629
2535
  false
2630
2536
  );
2631
2537
  }
2632
- if (!existsSync6(join7(absSource, "prompt.md"))) {
2538
+ if (!existsSync6(join5(absSource, "prompt.md"))) {
2633
2539
  throw new LAFSCommandError(
2634
2540
  PI_ERROR_CODES.VALIDATION,
2635
2541
  `Source directory is missing prompt.md: ${absSource}`,
@@ -2946,8 +2852,8 @@ function registerPiCommands(program2) {
2946
2852
  }
2947
2853
 
2948
2854
  // src/commands/providers.ts
2949
- import { randomUUID as randomUUID3 } from "crypto";
2950
- import { resolveOutputFormat as resolveOutputFormat2 } from "@cleocode/lafs";
2855
+ import { randomUUID as randomUUID2 } from "crypto";
2856
+ import { resolveOutputFormat } from "@cleocode/lafs";
2951
2857
  import pc6 from "picocolors";
2952
2858
  function registerProvidersCommand(program2) {
2953
2859
  const providers = program2.command("providers").description("Manage AI agent providers");
@@ -2956,7 +2862,7 @@ function registerProvidersCommand(program2) {
2956
2862
  const mvi = "standard";
2957
2863
  let format;
2958
2864
  try {
2959
- format = resolveOutputFormat2({
2865
+ format = resolveOutputFormat({
2960
2866
  jsonFlag: opts.json ?? false,
2961
2867
  humanFlag: (opts.human ?? false) || isHuman(),
2962
2868
  projectDefault: "json"
@@ -3006,7 +2912,7 @@ CAMP Provider Registry v${getRegistryVersion()}`));
3006
2912
  const mvi = "standard";
3007
2913
  let format;
3008
2914
  try {
3009
- format = resolveOutputFormat2({
2915
+ format = resolveOutputFormat({
3010
2916
  jsonFlag: opts.json ?? false,
3011
2917
  humanFlag: (opts.human ?? false) || isHuman(),
3012
2918
  projectDefault: "json"
@@ -3062,7 +2968,7 @@ Detected ${installed.length} installed providers:
3062
2968
  const mvi = "standard";
3063
2969
  let format;
3064
2970
  try {
3065
- format = resolveOutputFormat2({
2971
+ format = resolveOutputFormat({
3066
2972
  jsonFlag: opts.json ?? false,
3067
2973
  humanFlag: (opts.human ?? false) || isHuman(),
3068
2974
  projectDefault: "json"
@@ -3135,7 +3041,7 @@ ${provider.toolName}`));
3135
3041
  const mvi = "standard";
3136
3042
  let format;
3137
3043
  try {
3138
- format = resolveOutputFormat2({
3044
+ format = resolveOutputFormat({
3139
3045
  jsonFlag: opts.json ?? false,
3140
3046
  humanFlag: (opts.human ?? false) || isHuman(),
3141
3047
  projectDefault: "json"
@@ -3193,7 +3099,7 @@ ${provider.toolName}`));
3193
3099
  const mvi = "standard";
3194
3100
  let format;
3195
3101
  try {
3196
- format = resolveOutputFormat2({
3102
+ format = resolveOutputFormat({
3197
3103
  jsonFlag: opts.json ?? false,
3198
3104
  humanFlag: (opts.human ?? false) || isHuman(),
3199
3105
  projectDefault: "json"
@@ -3261,7 +3167,7 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
3261
3167
  const mvi = "standard";
3262
3168
  let format;
3263
3169
  try {
3264
- format = resolveOutputFormat2({
3170
+ format = resolveOutputFormat({
3265
3171
  jsonFlag: opts.json ?? false,
3266
3172
  humanFlag: (opts.human ?? false) || isHuman(),
3267
3173
  projectDefault: "json"
@@ -3308,7 +3214,7 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
3308
3214
  const mvi = "standard";
3309
3215
  let format;
3310
3216
  try {
3311
- format = resolveOutputFormat2({
3217
+ format = resolveOutputFormat({
3312
3218
  jsonFlag: opts.json ?? false,
3313
3219
  humanFlag: (opts.human ?? false) || isHuman(),
3314
3220
  projectDefault: "json"
@@ -3439,7 +3345,7 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
3439
3345
  const mvi = "standard";
3440
3346
  let format;
3441
3347
  try {
3442
- format = resolveOutputFormat2({
3348
+ format = resolveOutputFormat({
3443
3349
  jsonFlag: opts.json ?? false,
3444
3350
  humanFlag: (opts.human ?? false) || isHuman(),
3445
3351
  projectDefault: "json"
@@ -3509,7 +3415,7 @@ function buildEnvelope2(operation, mvi, result, error) {
3509
3415
  schemaVersion: "1.0.0",
3510
3416
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3511
3417
  operation,
3512
- requestId: randomUUID3(),
3418
+ requestId: randomUUID2(),
3513
3419
  transport: "cli",
3514
3420
  strict: true,
3515
3421
  mvi,
@@ -3825,8 +3731,8 @@ function registerSkillsCheck(parent) {
3825
3731
  }
3826
3732
 
3827
3733
  // src/commands/skills/find.ts
3828
- import { randomUUID as randomUUID4 } from "crypto";
3829
- import { resolveOutputFormat as resolveOutputFormat3 } from "@cleocode/lafs";
3734
+ import { randomUUID as randomUUID3 } from "crypto";
3735
+ import { resolveOutputFormat as resolveOutputFormat2 } from "@cleocode/lafs";
3830
3736
  import pc9 from "picocolors";
3831
3737
  var SkillsFindValidationError = class extends Error {
3832
3738
  code;
@@ -3858,7 +3764,7 @@ function registerSkillsFind(parent) {
3858
3764
  const mvi = details ? "full" : "standard";
3859
3765
  let format;
3860
3766
  try {
3861
- format = resolveOutputFormat3({
3767
+ format = resolveOutputFormat2({
3862
3768
  jsonFlag: opts.json ?? false,
3863
3769
  humanFlag: (opts.human ?? false) || isHuman(),
3864
3770
  projectDefault: "json"
@@ -4087,7 +3993,7 @@ function buildEnvelope3(operation, mvi, result, error) {
4087
3993
  schemaVersion: "1.0.0",
4088
3994
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4089
3995
  operation,
4090
- requestId: randomUUID4(),
3996
+ requestId: randomUUID3(),
4091
3997
  transport: "cli",
4092
3998
  strict: true,
4093
3999
  mvi,
@@ -4114,7 +4020,7 @@ function emitJsonError3(operation, mvi, code, message, category, details = {}) {
4114
4020
  // src/commands/skills/init.ts
4115
4021
  import { existsSync as existsSync10 } from "fs";
4116
4022
  import { mkdir, writeFile as writeFile3 } from "fs/promises";
4117
- import { join as join8 } from "path";
4023
+ import { join as join6 } from "path";
4118
4024
  import pc10 from "picocolors";
4119
4025
  function registerSkillsInit(parent) {
4120
4026
  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(
@@ -4140,7 +4046,7 @@ function registerSkillsInit(parent) {
4140
4046
  process.exit(1);
4141
4047
  }
4142
4048
  const skillName = name ?? "my-skill";
4143
- const skillDir = join8(opts.dir, skillName);
4049
+ const skillDir = join6(opts.dir, skillName);
4144
4050
  if (existsSync10(skillDir)) {
4145
4051
  const message = `Directory already exists: ${skillDir}`;
4146
4052
  if (format === "json") {
@@ -4183,7 +4089,7 @@ Provide detailed instructions for the AI agent here.
4183
4089
 
4184
4090
  Show example inputs and expected outputs.
4185
4091
  `;
4186
- await writeFile3(join8(skillDir, "SKILL.md"), template, "utf-8");
4092
+ await writeFile3(join6(skillDir, "SKILL.md"), template, "utf-8");
4187
4093
  const result = {
4188
4094
  name: skillName,
4189
4095
  directory: skillDir,
@@ -4197,95 +4103,88 @@ Show example inputs and expected outputs.
4197
4103
  console.log(pc10.green(`\u2713 Created skill template: ${skillDir}/SKILL.md`));
4198
4104
  console.log(pc10.dim("\nNext steps:"));
4199
4105
  console.log(pc10.dim(" 1. Edit SKILL.md with your instructions"));
4200
- console.log(pc10.dim(` 2. Validate: caamp skills validate ${join8(skillDir, "SKILL.md")}`));
4106
+ console.log(pc10.dim(` 2. Validate: caamp skills validate ${join6(skillDir, "SKILL.md")}`));
4201
4107
  console.log(pc10.dim(` 3. Install: caamp skills install ${skillDir}`));
4202
4108
  }
4203
4109
  );
4204
4110
  }
4205
4111
 
4206
4112
  // src/commands/skills/install.ts
4207
- import { existsSync as existsSync11 } from "fs";
4208
- import { createRequire } from "module";
4209
- import { pushWarning } from "@cleocode/lafs";
4210
4113
  import pc11 from "picocolors";
4211
- var cachedCore;
4212
- var defaultCoreResolver = async () => {
4213
- const require2 = createRequire(import.meta.url);
4214
- require2.resolve("@cleocode/core");
4215
- return await import("@cleocode/core");
4216
- };
4217
- var coreResolver = defaultCoreResolver;
4218
- async function resolveCore() {
4219
- if (cachedCore !== void 0) return cachedCore;
4220
- try {
4221
- cachedCore = await coreResolver();
4222
- } catch (err) {
4223
- cachedCore = null;
4224
- pushWarning({
4225
- code: "W_CORE_UNAVAILABLE",
4226
- severity: "warn",
4227
- message: "caamp running without @cleocode/core \u2014 trust gate skipped (skill installs not security-checked)",
4228
- context: {
4229
- error: err instanceof Error ? err.message : String(err)
4230
- }
4231
- });
4114
+ function renderProgress(event) {
4115
+ switch (event.kind) {
4116
+ case "marketplace-search":
4117
+ console.log(pc11.dim(`Searching marketplace for ${event.source}...`));
4118
+ return;
4119
+ case "marketplace-found":
4120
+ console.log(` Found: ${pc11.bold(event.name)} by ${event.author} (${pc11.dim(event.repo)})`);
4121
+ return;
4122
+ case "catalog-found":
4123
+ console.log(
4124
+ ` Found in catalog: ${pc11.bold(event.name)} v${event.version} (${pc11.dim(event.category)})`
4125
+ );
4126
+ return;
4232
4127
  }
4233
- return cachedCore;
4234
4128
  }
4235
- async function evaluateSkillTrustGate(localPath, source, force = false) {
4236
- const core = await resolveCore();
4237
- if (!core) {
4238
- return {
4239
- decision: "allow",
4240
- reason: "Trust gate skipped \u2014 @cleocode/core not available",
4241
- scan: {
4242
- skillName: localPath.split("/").filter(Boolean).pop() ?? localPath,
4243
- source,
4244
- trustLevel: "community",
4245
- verdict: "safe",
4246
- findings: [],
4247
- scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
4248
- summary: "core-unavailable"
4249
- }
4250
- };
4129
+ function categoryFor(err) {
4130
+ switch (err.code) {
4131
+ case ErrorCodes.NETWORK_ERROR:
4132
+ return ErrorCategories.TRANSIENT;
4133
+ case ErrorCodes.SKILL_NOT_FOUND:
4134
+ return ErrorCategories.NOT_FOUND;
4135
+ case ErrorCodes.SKILL_GATE_UNAVAILABLE:
4136
+ return ErrorCategories.INTERNAL;
4137
+ default:
4138
+ return ErrorCategories.VALIDATION;
4251
4139
  }
4252
- const scan = core.scanSkill(localPath, source);
4253
- const gate = core.shouldAllowInstall(scan, force);
4254
- return { decision: gate.decision, reason: gate.reason, scan };
4255
4140
  }
4256
- async function evaluateFederationGate(source, opts = {}) {
4257
- const core = await resolveCore();
4258
- if (!core) {
4259
- return {
4260
- decision: "allow",
4261
- reason: "Federation gate skipped \u2014 @cleocode/core not available",
4262
- peer: null,
4263
- isFederationSource: false,
4264
- computedChecksum: null,
4265
- expectedChecksum: null
4266
- };
4141
+ function reportRefusal(err, format, operation, mvi) {
4142
+ const scan = err.details.scan;
4143
+ if (format === "json") {
4144
+ if (scan && err.code === ErrorCodes.SKILL_TRUST_GATE_BLOCKED) {
4145
+ const envelope = buildEnvelope(
4146
+ operation,
4147
+ mvi,
4148
+ { scan },
4149
+ {
4150
+ code: err.code,
4151
+ message: err.message,
4152
+ category: categoryFor(err),
4153
+ retryable: false,
4154
+ retryAfterMs: null,
4155
+ details: {
4156
+ verdict: scan.verdict,
4157
+ trustLevel: scan.trustLevel,
4158
+ findingsCount: scan.findings.length
4159
+ }
4160
+ }
4161
+ );
4162
+ console.error(JSON.stringify(envelope, null, 2));
4163
+ } else {
4164
+ const details = {};
4165
+ if (err.details.availableSkills) details.availableSkills = err.details.availableSkills;
4166
+ if (err.details.federation) {
4167
+ details.expectedChecksum = err.details.federation.expectedChecksum;
4168
+ details.computedChecksum = err.details.federation.computedChecksum;
4169
+ details.peer = err.details.federation.peer?.url ?? null;
4170
+ }
4171
+ if (err.details.cause) details.cause = err.details.cause;
4172
+ emitJsonError(operation, mvi, err.code, err.message, categoryFor(err), details);
4173
+ }
4267
4174
  }
4268
- return core.evaluateFederationInstallGate({
4269
- source,
4270
- artefactPath: opts.artefactPath,
4271
- expectedChecksum: opts.expectedChecksum,
4272
- approveNewSource: opts.approveNewSource
4273
- });
4274
- }
4275
- async function recordSkillTrustBypass(scan, reason = null) {
4276
- const core = await resolveCore();
4277
- if (!core) return;
4278
- try {
4279
- core.recordTrustBypass(scan, reason);
4280
- } catch (err) {
4281
- const msg = err instanceof Error ? err.message : String(err);
4282
- pushWarning({
4283
- code: "W_AUDIT_LOG_FAILED",
4284
- severity: "warn",
4285
- message: "trust-bypass audit record failed",
4286
- context: { error: msg }
4287
- });
4175
+ console.error(pc11.red(err.message));
4176
+ if (scan && err.code === ErrorCodes.SKILL_TRUST_GATE_BLOCKED && format === "human") {
4177
+ for (const f of scan.findings.slice(0, 5)) {
4178
+ console.error(
4179
+ pc11.dim(` [${f.severity}] ${f.category} ${f.file}:${f.line} \u2014 ${f.description}`)
4180
+ );
4181
+ }
4182
+ console.error(pc11.yellow(" Use --force to override (audited)."));
4288
4183
  }
4184
+ if (err.details.availableSkills && format === "human") {
4185
+ console.log(pc11.dim(`Available skills: ${err.details.availableSkills.join(", ")}`));
4186
+ }
4187
+ process.exit(1);
4289
4188
  }
4290
4189
  function registerSkillsInstall(parent) {
4291
4190
  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(
@@ -4335,7 +4234,7 @@ function registerSkillsInstall(parent) {
4335
4234
  if (providers.length === 0) {
4336
4235
  const message = "No target providers found. Use --agent or --all.";
4337
4236
  if (format === "json") {
4338
- emitError2(
4237
+ emitError(
4339
4238
  operation,
4340
4239
  mvi,
4341
4240
  ErrorCodes.PROVIDER_NOT_FOUND,
@@ -4360,7 +4259,7 @@ function registerSkillsInstall(parent) {
4360
4259
  if (!source) {
4361
4260
  const message = "Missing required argument: source";
4362
4261
  if (format === "json") {
4363
- emitError2(
4262
+ emitError(
4364
4263
  operation,
4365
4264
  mvi,
4366
4265
  ErrorCodes.INVALID_INPUT,
@@ -4377,335 +4276,74 @@ function registerSkillsInstall(parent) {
4377
4276
  if (format === "human") {
4378
4277
  console.log(pc11.dim(`Installing to ${providers.length} provider(s)...`));
4379
4278
  }
4380
- let localPath;
4381
- let cleanup;
4382
- let skillName;
4383
- let sourceValue;
4384
- let sourceType;
4385
- if (isMarketplaceScoped(source)) {
4386
- const sourceResult = await handleMarketplaceSource(
4387
- source,
4279
+ let result;
4280
+ try {
4281
+ result = await installSkillFromSource(source, {
4388
4282
  providers,
4389
- opts.global ?? false,
4390
- format,
4391
- operation,
4392
- mvi
4393
- );
4394
- if (sourceResult.success) {
4395
- localPath = sourceResult.localPath;
4396
- cleanup = sourceResult.cleanup;
4397
- skillName = sourceResult.skillName;
4398
- sourceValue = sourceResult.sourceValue;
4399
- sourceType = sourceResult.sourceType;
4400
- } else {
4401
- process.exit(1);
4402
- }
4403
- } else {
4404
- const parsed = parseSource(source);
4405
- skillName = parsed.inferredName;
4406
- sourceValue = parsed.value;
4407
- sourceType = parsed.type;
4408
- if (parsed.type === "github" && parsed.owner && parsed.repo) {
4409
- try {
4410
- const result = await cloneRepo(parsed.owner, parsed.repo, parsed.ref, parsed.path);
4411
- localPath = result.localPath;
4412
- cleanup = result.cleanup;
4413
- } catch (error) {
4414
- const message = `Failed to clone GitHub repository: ${formatNetworkError(error)}`;
4415
- if (format === "json") {
4416
- emitJsonError(
4417
- operation,
4418
- mvi,
4419
- ErrorCodes.NETWORK_ERROR,
4420
- message,
4421
- ErrorCategories.TRANSIENT
4422
- );
4423
- }
4424
- console.error(pc11.red(message));
4425
- process.exit(1);
4426
- }
4427
- } else if (parsed.type === "gitlab" && parsed.owner && parsed.repo) {
4428
- try {
4429
- const result = await cloneGitLabRepo(
4430
- parsed.owner,
4431
- parsed.repo,
4432
- parsed.ref,
4433
- parsed.path
4434
- );
4435
- localPath = result.localPath;
4436
- cleanup = result.cleanup;
4437
- } catch (error) {
4438
- const message = `Failed to clone GitLab repository: ${formatNetworkError(error)}`;
4439
- if (format === "json") {
4440
- emitJsonError(
4441
- operation,
4442
- mvi,
4443
- ErrorCodes.NETWORK_ERROR,
4444
- message,
4445
- ErrorCategories.TRANSIENT
4446
- );
4447
- }
4448
- console.error(pc11.red(message));
4449
- process.exit(1);
4450
- }
4451
- } else if (parsed.type === "local") {
4452
- localPath = parsed.value;
4453
- const discovered = await discoverSkill(localPath);
4454
- if (discovered) {
4455
- skillName = discovered.name;
4456
- }
4457
- } else if (parsed.type === "package") {
4458
- if (!isCatalogAvailable()) {
4459
- const message = "No skill library registered. Register one with registerSkillLibraryFromPath() or set CAAMP_SKILL_LIBRARY env var.";
4460
- if (format === "json") {
4461
- emitJsonError(
4462
- operation,
4463
- mvi,
4464
- ErrorCodes.INVALID_INPUT,
4465
- message,
4466
- ErrorCategories.VALIDATION
4467
- );
4468
- }
4469
- console.error(pc11.red(message));
4470
- process.exit(1);
4471
- }
4472
- const catalogSkill = getSkill(parsed.inferredName);
4473
- if (catalogSkill) {
4474
- localPath = getSkillDir(catalogSkill.name);
4475
- skillName = catalogSkill.name;
4476
- sourceValue = `library:${catalogSkill.name}`;
4477
- sourceType = "library";
4478
- if (format === "human") {
4479
- console.log(
4480
- ` Found in catalog: ${pc11.bold(catalogSkill.name)} v${catalogSkill.version} (${pc11.dim(catalogSkill.category)})`
4481
- );
4482
- }
4483
- } else {
4484
- const message = `Skill not found in catalog: ${parsed.inferredName}`;
4485
- if (format === "json") {
4486
- emitJsonError(
4487
- operation,
4488
- mvi,
4489
- ErrorCodes.SKILL_NOT_FOUND,
4490
- message,
4491
- ErrorCategories.NOT_FOUND,
4492
- {
4493
- availableSkills: listSkills()
4494
- }
4495
- );
4496
- }
4497
- console.error(pc11.red(message));
4498
- console.log(pc11.dim("Available skills: " + listSkills().join(", ")));
4499
- process.exit(1);
4500
- }
4501
- } else {
4502
- const message = `Unsupported source type: ${parsed.type}`;
4503
- if (format === "json") {
4504
- emitJsonError(
4505
- operation,
4506
- mvi,
4507
- ErrorCodes.INVALID_FORMAT,
4508
- message,
4509
- ErrorCategories.VALIDATION
4510
- );
4511
- }
4512
- console.error(pc11.red(message));
4513
- process.exit(1);
4283
+ isGlobal: opts.global ?? false,
4284
+ force: opts.force ?? false,
4285
+ allowNewSource: opts.allowNewSource === true,
4286
+ recordLock: true,
4287
+ onProgress: format === "human" ? renderProgress : void 0
4288
+ });
4289
+ } catch (error) {
4290
+ if (error instanceof SkillInstallError) {
4291
+ reportRefusal(error, format, operation, mvi);
4514
4292
  }
4293
+ throw error;
4515
4294
  }
4516
- try {
4517
- if (!localPath) {
4518
- const message = "No local skill path resolved for installation";
4519
- if (format === "json") {
4520
- emitJsonError(
4521
- operation,
4522
- mvi,
4523
- ErrorCodes.INTERNAL_ERROR,
4524
- message,
4525
- ErrorCategories.INTERNAL
4526
- );
4527
- }
4528
- console.error(pc11.red(message));
4529
- process.exit(1);
4530
- }
4531
- if (sourceType !== "library" && sourceType !== "package") {
4532
- const fedGate = await evaluateFederationGate(sourceValue, {
4533
- artefactPath: localPath,
4534
- approveNewSource: opts.allowNewSource === true
4535
- });
4536
- if (fedGate.decision === "block-checksum") {
4537
- const message = `Federation install blocked: ${fedGate.reason}`;
4538
- if (format === "json") {
4539
- emitJsonError(
4540
- operation,
4541
- mvi,
4542
- ErrorCodes.FEDERATION_CHECKSUM_MISMATCH,
4543
- message,
4544
- ErrorCategories.VALIDATION,
4545
- {
4546
- expectedChecksum: fedGate.expectedChecksum,
4547
- computedChecksum: fedGate.computedChecksum
4548
- }
4549
- );
4550
- }
4551
- console.error(pc11.red(message));
4552
- process.exit(1);
4553
- }
4554
- if (fedGate.decision === "prompt-first-install") {
4555
- const message = `${fedGate.reason} Use --allow-new-source to approve in non-interactive contexts.`;
4556
- if (format === "json") {
4557
- emitJsonError(
4558
- operation,
4559
- mvi,
4560
- ErrorCodes.FEDERATION_UNKNOWN_SOURCE_INTERACTIVE_REQUIRED,
4561
- message,
4562
- ErrorCategories.VALIDATION,
4563
- { peer: fedGate.peer?.url ?? null }
4564
- );
4565
- }
4566
- console.error(pc11.yellow(message));
4567
- process.exit(1);
4568
- }
4569
- }
4570
- if (sourceType !== "library" && sourceType !== "package") {
4571
- const gate = await evaluateSkillTrustGate(localPath, sourceValue, opts.force ?? false);
4572
- if (gate.decision === "block") {
4573
- const message = `Trust gate blocked install: ${gate.reason}`;
4574
- if (format === "json") {
4575
- const envelope = buildEnvelope(
4576
- operation,
4577
- mvi,
4578
- { scan: gate.scan },
4579
- {
4580
- code: ErrorCodes.SKILL_TRUST_GATE_BLOCKED,
4581
- message,
4582
- category: ErrorCategories.VALIDATION,
4583
- retryable: false,
4584
- retryAfterMs: null,
4585
- details: {
4586
- verdict: gate.scan.verdict,
4587
- trustLevel: gate.scan.trustLevel,
4588
- findingsCount: gate.scan.findings.length
4589
- }
4590
- }
4591
- );
4592
- console.error(JSON.stringify(envelope, null, 2));
4593
- } else {
4594
- console.error(pc11.red(`
4595
- ${message}`));
4596
- for (const f of gate.scan.findings.slice(0, 5)) {
4597
- console.error(
4598
- pc11.dim(
4599
- ` [${f.severity}] ${f.category} ${f.file}:${f.line} \u2014 ${f.description}`
4600
- )
4601
- );
4602
- }
4603
- console.error(pc11.yellow(" Use --force to override (audited)."));
4604
- }
4605
- process.exit(1);
4606
- }
4607
- if (gate.decision === "ask") {
4608
- const message = `Trust gate requires confirmation: ${gate.reason}`;
4609
- if (format === "json") {
4610
- emitJsonError(
4611
- operation,
4612
- mvi,
4613
- ErrorCodes.SKILL_TRUST_GATE_BLOCKED,
4614
- message,
4615
- ErrorCategories.VALIDATION
4616
- );
4617
- }
4618
- console.error(pc11.red(message));
4619
- process.exit(1);
4620
- }
4621
- if (opts.force === true && gate.scan.verdict !== "safe") {
4622
- await recordSkillTrustBypass(gate.scan, "operator --force on caamp skills install");
4623
- }
4624
- }
4625
- const result = await dispatchInstallSkillAcrossProviders(
4626
- localPath,
4627
- skillName,
4628
- providers,
4629
- opts.global ?? false
4630
- );
4631
- if (result.success) {
4632
- const isGlobal = sourceType === "library" || sourceType === "package" ? true : opts.global ?? false;
4633
- await recordSkillInstall(
4634
- skillName,
4635
- sourceValue,
4636
- sourceValue,
4637
- sourceType,
4638
- result.linkedAgents,
4639
- result.canonicalPath,
4640
- isGlobal
4641
- );
4642
- const installedItem = {
4643
- name: skillName,
4644
- scopedName: sourceValue,
4645
- canonicalPath: result.canonicalPath,
4646
- providers: result.linkedAgents
4647
- };
4648
- const summary = {
4649
- installed: [installedItem],
4650
- failed: [],
4651
- count: {
4652
- installed: 1,
4653
- failed: 0,
4654
- total: 1
4655
- }
4656
- };
4657
- if (format === "json") {
4658
- outputSuccess(operation, mvi, summary);
4659
- } else {
4660
- console.log(pc11.green(`
4661
- \u2713 Installed ${pc11.bold(skillName)}`));
4662
- console.log(` Canonical: ${pc11.dim(result.canonicalPath)}`);
4663
- console.log(` Linked to: ${result.linkedAgents.join(", ")}`);
4664
- if (result.errors.length > 0) {
4665
- console.log(pc11.yellow("\nWarnings:"));
4666
- for (const err of result.errors) {
4667
- console.log(` ${pc11.yellow("!")} ${err}`);
4668
- }
4669
- }
4670
- }
4295
+ if (result.success) {
4296
+ const installedItem = {
4297
+ name: result.name,
4298
+ scopedName: result.sourceValue,
4299
+ canonicalPath: result.canonicalPath,
4300
+ providers: result.linkedAgents
4301
+ };
4302
+ const summary2 = {
4303
+ installed: [installedItem],
4304
+ failed: [],
4305
+ count: { installed: 1, failed: 0, total: 1 }
4306
+ };
4307
+ if (format === "json") {
4308
+ outputSuccess(operation, mvi, summary2);
4671
4309
  } else {
4672
- const summary = {
4673
- installed: [],
4674
- failed: [
4675
- {
4676
- name: skillName,
4677
- error: result.errors.join(", ")
4678
- }
4679
- ],
4680
- count: {
4681
- installed: 0,
4682
- failed: 1,
4683
- total: 1
4684
- }
4685
- };
4686
- if (format === "json") {
4687
- const envelope = buildEnvelope(operation, mvi, summary, {
4688
- code: ErrorCodes.INSTALL_FAILED,
4689
- message: result.errors.join(", "),
4690
- category: ErrorCategories.INTERNAL,
4691
- retryable: false,
4692
- retryAfterMs: null,
4693
- details: { skillName, sourceValue }
4694
- });
4695
- console.error(JSON.stringify(envelope, null, 2));
4696
- } else {
4697
- console.log(pc11.yellow(`
4698
- \u2717 Failed to install ${pc11.bold(skillName)}`));
4699
- console.log(pc11.yellow("Errors:"));
4310
+ console.log(pc11.green(`
4311
+ \u2713 Installed ${pc11.bold(result.name)}`));
4312
+ console.log(` Canonical: ${pc11.dim(result.canonicalPath)}`);
4313
+ console.log(` Linked to: ${result.linkedAgents.join(", ")}`);
4314
+ if (result.errors.length > 0) {
4315
+ console.log(pc11.yellow("\nWarnings:"));
4700
4316
  for (const err of result.errors) {
4701
4317
  console.log(` ${pc11.yellow("!")} ${err}`);
4702
4318
  }
4703
4319
  }
4704
- process.exit(1);
4705
4320
  }
4706
- } finally {
4707
- if (cleanup) await cleanup();
4321
+ return;
4708
4322
  }
4323
+ const summary = {
4324
+ installed: [],
4325
+ failed: [{ name: result.name, error: result.errors.join(", ") }],
4326
+ count: { installed: 0, failed: 1, total: 1 }
4327
+ };
4328
+ if (format === "json") {
4329
+ const envelope = buildEnvelope(operation, mvi, summary, {
4330
+ code: ErrorCodes.INSTALL_FAILED,
4331
+ message: result.errors.join(", "),
4332
+ category: ErrorCategories.INTERNAL,
4333
+ retryable: false,
4334
+ retryAfterMs: null,
4335
+ details: { skillName: result.name, sourceValue: result.sourceValue }
4336
+ });
4337
+ console.error(JSON.stringify(envelope, null, 2));
4338
+ } else {
4339
+ console.log(pc11.yellow(`
4340
+ \u2717 Failed to install ${pc11.bold(result.name)}`));
4341
+ console.log(pc11.yellow("Errors:"));
4342
+ for (const err of result.errors) {
4343
+ console.log(` ${pc11.yellow("!")} ${err}`);
4344
+ }
4345
+ }
4346
+ process.exit(1);
4709
4347
  }
4710
4348
  );
4711
4349
  }
@@ -4713,7 +4351,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
4713
4351
  if (!isCatalogAvailable()) {
4714
4352
  const message = "No skill library registered. Register one with registerSkillLibraryFromPath() or set CAAMP_SKILL_LIBRARY env var.";
4715
4353
  if (format === "json") {
4716
- emitError2(operation, mvi, ErrorCodes.INVALID_INPUT, message, ErrorCategories.VALIDATION);
4354
+ emitError(operation, mvi, ErrorCodes.INVALID_INPUT, message, ErrorCategories.VALIDATION);
4717
4355
  }
4718
4356
  console.error(pc11.red(message));
4719
4357
  process.exit(1);
@@ -4747,22 +4385,20 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
4747
4385
  const installed = [];
4748
4386
  const failed = [];
4749
4387
  for (const name of profileSkills) {
4750
- const skillDir = getSkillDir(name);
4751
4388
  try {
4752
- const result = await dispatchInstallSkillAcrossProviders(skillDir, name, providers, isGlobal);
4389
+ const result = await installResolvedSkill(
4390
+ {
4391
+ localPath: getSkillDir(name),
4392
+ skillName: name,
4393
+ sourceValue: `library:${name}`,
4394
+ sourceType: "library"
4395
+ },
4396
+ { providers, isGlobal, recordLock: true }
4397
+ );
4753
4398
  if (result.success) {
4754
4399
  if (format === "human") {
4755
4400
  console.log(pc11.green(` + ${name}`));
4756
4401
  }
4757
- await recordSkillInstall(
4758
- name,
4759
- `library:${name}`,
4760
- `library:${name}`,
4761
- "library",
4762
- result.linkedAgents,
4763
- result.canonicalPath,
4764
- true
4765
- );
4766
4402
  installed.push({
4767
4403
  name,
4768
4404
  scopedName: `library:${name}`,
@@ -4823,89 +4459,10 @@ ${pc11.green(`${installed.length} installed`)}, ${failed.length > 0 ? pc11.yello
4823
4459
  }
4824
4460
  }
4825
4461
  }
4826
- async function handleMarketplaceSource(source, _providers, _isGlobal, format, operation, mvi) {
4827
- if (format === "human") {
4828
- console.log(pc11.dim(`Searching marketplace for ${source}...`));
4829
- }
4830
- const client = new MarketplaceClient();
4831
- let skill;
4832
- try {
4833
- skill = await client.getSkill(source);
4834
- } catch (error) {
4835
- const message = `Marketplace lookup failed: ${formatNetworkError(error)}`;
4836
- if (format === "json") {
4837
- emitJsonError(operation, mvi, ErrorCodes.NETWORK_ERROR, message, ErrorCategories.TRANSIENT);
4838
- }
4839
- console.error(pc11.red(message));
4840
- return { success: false };
4841
- }
4842
- if (!skill) {
4843
- const message = `Skill not found: ${source}`;
4844
- if (format === "json") {
4845
- emitJsonError(operation, mvi, ErrorCodes.SKILL_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
4846
- }
4847
- console.error(pc11.red(message));
4848
- return { success: false };
4849
- }
4850
- if (format === "human") {
4851
- console.log(
4852
- ` Found: ${pc11.bold(skill.name)} by ${skill.author} (${pc11.dim(skill.repoFullName)})`
4853
- );
4854
- }
4855
- const parsed = parseSource(skill.githubUrl);
4856
- if (parsed.type !== "github" || !parsed.owner || !parsed.repo) {
4857
- const message = "Could not resolve GitHub source";
4858
- if (format === "json") {
4859
- emitJsonError(operation, mvi, ErrorCodes.INVALID_FORMAT, message, ErrorCategories.VALIDATION);
4860
- }
4861
- console.error(pc11.red(message));
4862
- return { success: false };
4863
- }
4864
- try {
4865
- const subPathCandidates = buildSkillSubPathCandidates(skill.path, parsed.path);
4866
- let cloneError;
4867
- let cloned = false;
4868
- let localPath;
4869
- let cleanup;
4870
- for (const subPath of subPathCandidates) {
4871
- try {
4872
- const result = await cloneRepo(parsed.owner, parsed.repo, parsed.ref, subPath);
4873
- if (subPath && !existsSync11(result.localPath)) {
4874
- await result.cleanup();
4875
- continue;
4876
- }
4877
- localPath = result.localPath;
4878
- cleanup = result.cleanup;
4879
- cloned = true;
4880
- break;
4881
- } catch (error) {
4882
- cloneError = error;
4883
- }
4884
- }
4885
- if (!cloned) {
4886
- throw cloneError ?? new Error("Unable to resolve skill path from marketplace metadata");
4887
- }
4888
- return {
4889
- success: true,
4890
- localPath,
4891
- cleanup,
4892
- skillName: skill.name,
4893
- sourceValue: skill.githubUrl,
4894
- sourceType: parsed.type
4895
- };
4896
- } catch (error) {
4897
- const message = `Failed to fetch source repository: ${formatNetworkError(error)}`;
4898
- if (format === "json") {
4899
- emitJsonError(operation, mvi, ErrorCodes.NETWORK_ERROR, message, ErrorCategories.TRANSIENT);
4900
- }
4901
- console.error(pc11.red(message));
4902
- return { success: false };
4903
- }
4904
- }
4905
4462
 
4906
4463
  // src/commands/skills/list.ts
4907
- import { randomUUID as randomUUID5 } from "crypto";
4908
- import { resolveOutputFormat as resolveOutputFormat4 } from "@cleocode/lafs";
4464
+ import { randomUUID as randomUUID4 } from "crypto";
4465
+ import { resolveOutputFormat as resolveOutputFormat3 } from "@cleocode/lafs";
4909
4466
  import pc12 from "picocolors";
4910
4467
  function registerSkillsList(parent) {
4911
4468
  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) => {
@@ -4913,7 +4470,7 @@ function registerSkillsList(parent) {
4913
4470
  const mvi = "standard";
4914
4471
  let format;
4915
4472
  try {
4916
- format = resolveOutputFormat4({
4473
+ format = resolveOutputFormat3({
4917
4474
  jsonFlag: opts.json ?? false,
4918
4475
  humanFlag: (opts.human ?? false) || isHuman(),
4919
4476
  projectDefault: "json"
@@ -4986,7 +4543,7 @@ function buildEnvelope4(operation, mvi, result, error) {
4986
4543
  schemaVersion: "1.0.0",
4987
4544
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4988
4545
  operation,
4989
- requestId: randomUUID5(),
4546
+ requestId: randomUUID4(),
4990
4547
  transport: "cli",
4991
4548
  strict: true,
4992
4549
  mvi,
@@ -5243,12 +4800,14 @@ ${outdated.length} skill(s) have updates available:
5243
4800
  skipped.push(skill.name);
5244
4801
  continue;
5245
4802
  }
5246
- const installResult = await dispatchInstallSkillAcrossProviders(
5247
- localPath,
5248
- skill.name,
5249
- providers,
5250
- entry.isGlobal,
5251
- entry.projectDir
4803
+ const installResult = await installResolvedSkill(
4804
+ {
4805
+ localPath,
4806
+ skillName: skill.name,
4807
+ sourceValue: entry.source,
4808
+ sourceType: parsed.type
4809
+ },
4810
+ { providers, isGlobal: entry.isGlobal, projectDir: entry.projectDir }
5252
4811
  );
5253
4812
  if (installResult.success) {
5254
4813
  await recordSkillInstall(
@@ -5312,7 +4871,7 @@ ${outdated.length} skill(s) have updates available:
5312
4871
  }
5313
4872
 
5314
4873
  // src/commands/skills/validate.ts
5315
- import { resolveOutputFormat as resolveOutputFormat5 } from "@cleocode/lafs";
4874
+ import { resolveOutputFormat as resolveOutputFormat4 } from "@cleocode/lafs";
5316
4875
  import pc15 from "picocolors";
5317
4876
  function registerSkillsValidate(parent) {
5318
4877
  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) => {
@@ -5320,7 +4879,7 @@ function registerSkillsValidate(parent) {
5320
4879
  const mvi = "standard";
5321
4880
  let format;
5322
4881
  try {
5323
- format = resolveOutputFormat5({
4882
+ format = resolveOutputFormat4({
5324
4883
  jsonFlag: opts.json ?? false,
5325
4884
  humanFlag: (opts.human ?? false) || isHuman(),
5326
4885
  projectDefault: "json"