@cleocode/caamp 2026.9.19 → 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,
@@ -52,7 +59,7 @@ import {
52
59
  tokenizeCriteriaValue,
53
60
  updateInstructionsSingleOperation,
54
61
  validateSkill
55
- } from "./chunk-IBHW6GEI.js";
62
+ } from "./chunk-QC6CHW72.js";
56
63
  import {
57
64
  buildSkillsMap,
58
65
  checkAllInjections,
@@ -65,7 +72,7 @@ import {
65
72
  groupByInstructFile,
66
73
  injectAll,
67
74
  providerSupports
68
- } from "./chunk-CHPNPLOK.js";
75
+ } from "./chunk-JELNPPLN.js";
69
76
  import {
70
77
  CANONICAL_HOOK_EVENTS,
71
78
  buildHookMatrix,
@@ -76,7 +83,6 @@ import {
76
83
  translateToAll
77
84
  } from "./chunk-YCAANESR.js";
78
85
  import {
79
- buildSkillSubPathCandidates,
80
86
  resolveProviderConfigPath,
81
87
  resolveProviderSkillsDir
82
88
  } from "./chunk-HDZYZTR2.js";
@@ -153,7 +159,7 @@ function emitSuccess(operation, result, mvi = "standard") {
153
159
  };
154
160
  console.log(JSON.stringify(envelope, null, 2));
155
161
  }
156
- function emitError(operation, error, mvi = "standard") {
162
+ function emitError2(operation, error, mvi = "standard") {
157
163
  let envelope;
158
164
  if (error instanceof LAFSCommandError) {
159
165
  envelope = {
@@ -204,7 +210,7 @@ async function runLafsCommand(command, mvi, action) {
204
210
  const result = await action();
205
211
  emitSuccess(command, result, mvi);
206
212
  } catch (error) {
207
- emitError(command, error, mvi);
213
+ emitError2(command, error, mvi);
208
214
  process.exit(1);
209
215
  }
210
216
  }
@@ -499,121 +505,6 @@ function registerAdvancedCommands(program2) {
499
505
  // src/commands/config.ts
500
506
  import { existsSync } from "fs";
501
507
  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
- // Skills federation gates (T9564 — Sphere B)
602
- SKILL_TRUST_GATE_BLOCKED: "E_SKILL_TRUST_GATE_BLOCKED",
603
- FEDERATION_UNKNOWN_SOURCE_INTERACTIVE_REQUIRED: "E_FEDERATION_UNKNOWN_SOURCE_INTERACTIVE_REQUIRED",
604
- FEDERATION_CHECKSUM_MISMATCH: "E_FEDERATION_CHECKSUM_MISMATCH",
605
- REMOVE_FAILED: "E_REMOVE_FAILED",
606
- UPDATE_FAILED: "E_UPDATE_FAILED",
607
- VALIDATION_FAILED: "E_VALIDATION_FAILED",
608
- AUDIT_FAILED: "E_AUDIT_FAILED",
609
- // System errors
610
- NETWORK_ERROR: "E_NETWORK_ERROR",
611
- FILE_SYSTEM_ERROR: "E_FILE_SYSTEM_ERROR",
612
- PERMISSION_DENIED: "E_PERMISSION_DENIED",
613
- INTERNAL_ERROR: "E_INTERNAL_ERROR"
614
- };
615
-
616
- // src/commands/config.ts
617
508
  function registerConfigCommand(program2) {
618
509
  const config = program2.command("config").description("View provider configuration");
619
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(
@@ -1950,60 +1841,8 @@ function registerMcpCommands(program2) {
1950
1841
  // src/commands/pi/cant.ts
1951
1842
  import { existsSync as existsSync4 } from "fs";
1952
1843
  import { writeFile } from "fs/promises";
1953
- import { tmpdir as tmpdir3 } from "os";
1954
- import { isAbsolute, join as join5 } from "path";
1955
-
1956
- // src/core/sources/github.ts
1957
- import { mkdtemp, rm } from "fs/promises";
1958
1844
  import { tmpdir } from "os";
1959
- import { join as join3 } from "path";
1960
- import { simpleGit } from "simple-git";
1961
- async function cloneRepo(owner, repo, ref, subPath) {
1962
- const tmpDir = await mkdtemp(join3(tmpdir(), "caamp-"));
1963
- const repoUrl = `https://github.com/${owner}/${repo}.git`;
1964
- const git = simpleGit();
1965
- const cloneOptions = ["--depth", "1"];
1966
- if (ref) {
1967
- cloneOptions.push("--branch", ref);
1968
- }
1969
- await git.clone(repoUrl, tmpDir, cloneOptions);
1970
- const localPath = subPath ? join3(tmpDir, subPath) : tmpDir;
1971
- return {
1972
- localPath,
1973
- cleanup: async () => {
1974
- try {
1975
- await rm(tmpDir, { recursive: true });
1976
- } catch {
1977
- }
1978
- }
1979
- };
1980
- }
1981
-
1982
- // src/core/sources/gitlab.ts
1983
- import { mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
1984
- import { tmpdir as tmpdir2 } from "os";
1985
- import { join as join4 } from "path";
1986
- import { simpleGit as simpleGit2 } from "simple-git";
1987
- async function cloneGitLabRepo(owner, repo, ref, subPath) {
1988
- const tmpDir = await mkdtemp2(join4(tmpdir2(), "caamp-gl-"));
1989
- const repoUrl = `https://gitlab.com/${owner}/${repo}.git`;
1990
- const git = simpleGit2();
1991
- const cloneOptions = ["--depth", "1"];
1992
- if (ref) {
1993
- cloneOptions.push("--branch", ref);
1994
- }
1995
- await git.clone(repoUrl, tmpDir, cloneOptions);
1996
- const localPath = subPath ? join4(tmpDir, subPath) : tmpDir;
1997
- return {
1998
- localPath,
1999
- cleanup: async () => {
2000
- try {
2001
- await rm2(tmpDir, { recursive: true });
2002
- } catch {
2003
- }
2004
- }
2005
- };
2006
- }
1845
+ import { isAbsolute, join as join3 } from "path";
2007
1846
 
2008
1847
  // src/commands/pi/common.ts
2009
1848
  var PI_ERROR_CODES = {
@@ -2066,7 +1905,7 @@ function resolveProjectDir2(tier, explicit) {
2066
1905
  // src/commands/pi/cant.ts
2067
1906
  async function resolveCantSource(source) {
2068
1907
  if (isAbsolute(source) || source.startsWith("./") || source.startsWith("../") || source.startsWith("~")) {
2069
- 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;
2070
1909
  if (!existsSync4(expanded)) {
2071
1910
  throw new LAFSCommandError(
2072
1911
  PI_ERROR_CODES.NOT_FOUND,
@@ -2086,7 +1925,7 @@ async function resolveCantSource(source) {
2086
1925
  const parsed2 = parseSource(source);
2087
1926
  if (parsed2.type === "github" && parsed2.owner !== void 0 && parsed2.repo !== void 0) {
2088
1927
  const cloneResult = await cloneRepo(parsed2.owner, parsed2.repo, parsed2.ref);
2089
- 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;
2090
1929
  if (!existsSync4(filePath)) {
2091
1930
  await cloneResult.cleanup();
2092
1931
  throw new LAFSCommandError(
@@ -2104,7 +1943,7 @@ async function resolveCantSource(source) {
2104
1943
  }
2105
1944
  if (parsed2.type === "gitlab" && parsed2.owner !== void 0 && parsed2.repo !== void 0) {
2106
1945
  const cloneResult = await cloneGitLabRepo(parsed2.owner, parsed2.repo, parsed2.ref);
2107
- 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;
2108
1947
  if (!existsSync4(filePath)) {
2109
1948
  await cloneResult.cleanup();
2110
1949
  throw new LAFSCommandError(
@@ -2131,7 +1970,7 @@ async function resolveCantSource(source) {
2131
1970
  }
2132
1971
  const body = await resp.text();
2133
1972
  const baseName = inferNameFromUrl(source);
2134
- 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`);
2135
1974
  await writeFile(tmp, body, "utf8");
2136
1975
  return {
2137
1976
  localPath: tmp,
@@ -2147,7 +1986,7 @@ async function resolveCantSource(source) {
2147
1986
  const parsed = parseSource(source);
2148
1987
  if (parsed.type === "github" && parsed.owner !== void 0 && parsed.repo !== void 0) {
2149
1988
  const cloneResult = await cloneRepo(parsed.owner, parsed.repo, parsed.ref);
2150
- 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;
2151
1990
  if (!existsSync4(filePath)) {
2152
1991
  await cloneResult.cleanup();
2153
1992
  throw new LAFSCommandError(
@@ -2311,11 +2150,11 @@ function registerPiCantCommands(parent) {
2311
2150
  // src/commands/pi/extensions.ts
2312
2151
  import { existsSync as existsSync5 } from "fs";
2313
2152
  import { writeFile as writeFile2 } from "fs/promises";
2314
- import { tmpdir as tmpdir4 } from "os";
2315
- 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";
2316
2155
  async function resolveExtensionSource(source) {
2317
2156
  if (isAbsolute2(source) || source.startsWith("./") || source.startsWith("../") || source.startsWith("~")) {
2318
- 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;
2319
2158
  if (!existsSync5(expanded)) {
2320
2159
  throw new LAFSCommandError(
2321
2160
  PI_ERROR_CODES.NOT_FOUND,
@@ -2336,7 +2175,7 @@ async function resolveExtensionSource(source) {
2336
2175
  const parsed2 = parseSource(source);
2337
2176
  if (parsed2.type === "github" && parsed2.owner !== void 0 && parsed2.repo !== void 0) {
2338
2177
  const cloneResult = await cloneRepo(parsed2.owner, parsed2.repo, parsed2.ref);
2339
- 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;
2340
2179
  if (!existsSync5(filePath)) {
2341
2180
  await cloneResult.cleanup();
2342
2181
  throw new LAFSCommandError(
@@ -2354,7 +2193,7 @@ async function resolveExtensionSource(source) {
2354
2193
  }
2355
2194
  if (parsed2.type === "gitlab" && parsed2.owner !== void 0 && parsed2.repo !== void 0) {
2356
2195
  const cloneResult = await cloneGitLabRepo(parsed2.owner, parsed2.repo, parsed2.ref);
2357
- 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;
2358
2197
  if (!existsSync5(filePath)) {
2359
2198
  await cloneResult.cleanup();
2360
2199
  throw new LAFSCommandError(
@@ -2381,7 +2220,7 @@ async function resolveExtensionSource(source) {
2381
2220
  }
2382
2221
  const body = await resp.text();
2383
2222
  const baseName = inferNameFromUrl2(source);
2384
- 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`);
2385
2224
  await writeFile2(tmp, body, "utf8");
2386
2225
  return {
2387
2226
  localPath: tmp,
@@ -2397,7 +2236,7 @@ async function resolveExtensionSource(source) {
2397
2236
  const parsed = parseSource(source);
2398
2237
  if (parsed.type === "github" && parsed.owner !== void 0 && parsed.repo !== void 0) {
2399
2238
  const cloneResult = await cloneRepo(parsed.owner, parsed.repo, parsed.ref);
2400
- 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;
2401
2240
  if (!existsSync5(filePath)) {
2402
2241
  await cloneResult.cleanup();
2403
2242
  throw new LAFSCommandError(
@@ -2666,7 +2505,7 @@ function registerPiModelsCommands(parent) {
2666
2505
 
2667
2506
  // src/commands/pi/prompts.ts
2668
2507
  import { existsSync as existsSync6 } from "fs";
2669
- import { join as join7, resolve } from "path";
2508
+ import { join as join5, resolve } from "path";
2670
2509
  function inferPromptName(sourceDir) {
2671
2510
  const normalized = resolve(sourceDir).replace(/[\\/]+$/, "");
2672
2511
  const base = normalized.split(/[\\/]/).pop();
@@ -2696,7 +2535,7 @@ function registerPiPromptsCommands(parent) {
2696
2535
  false
2697
2536
  );
2698
2537
  }
2699
- if (!existsSync6(join7(absSource, "prompt.md"))) {
2538
+ if (!existsSync6(join5(absSource, "prompt.md"))) {
2700
2539
  throw new LAFSCommandError(
2701
2540
  PI_ERROR_CODES.VALIDATION,
2702
2541
  `Source directory is missing prompt.md: ${absSource}`,
@@ -3013,8 +2852,8 @@ function registerPiCommands(program2) {
3013
2852
  }
3014
2853
 
3015
2854
  // src/commands/providers.ts
3016
- import { randomUUID as randomUUID3 } from "crypto";
3017
- import { resolveOutputFormat as resolveOutputFormat2 } from "@cleocode/lafs";
2855
+ import { randomUUID as randomUUID2 } from "crypto";
2856
+ import { resolveOutputFormat } from "@cleocode/lafs";
3018
2857
  import pc6 from "picocolors";
3019
2858
  function registerProvidersCommand(program2) {
3020
2859
  const providers = program2.command("providers").description("Manage AI agent providers");
@@ -3023,7 +2862,7 @@ function registerProvidersCommand(program2) {
3023
2862
  const mvi = "standard";
3024
2863
  let format;
3025
2864
  try {
3026
- format = resolveOutputFormat2({
2865
+ format = resolveOutputFormat({
3027
2866
  jsonFlag: opts.json ?? false,
3028
2867
  humanFlag: (opts.human ?? false) || isHuman(),
3029
2868
  projectDefault: "json"
@@ -3073,7 +2912,7 @@ CAMP Provider Registry v${getRegistryVersion()}`));
3073
2912
  const mvi = "standard";
3074
2913
  let format;
3075
2914
  try {
3076
- format = resolveOutputFormat2({
2915
+ format = resolveOutputFormat({
3077
2916
  jsonFlag: opts.json ?? false,
3078
2917
  humanFlag: (opts.human ?? false) || isHuman(),
3079
2918
  projectDefault: "json"
@@ -3129,7 +2968,7 @@ Detected ${installed.length} installed providers:
3129
2968
  const mvi = "standard";
3130
2969
  let format;
3131
2970
  try {
3132
- format = resolveOutputFormat2({
2971
+ format = resolveOutputFormat({
3133
2972
  jsonFlag: opts.json ?? false,
3134
2973
  humanFlag: (opts.human ?? false) || isHuman(),
3135
2974
  projectDefault: "json"
@@ -3202,7 +3041,7 @@ ${provider.toolName}`));
3202
3041
  const mvi = "standard";
3203
3042
  let format;
3204
3043
  try {
3205
- format = resolveOutputFormat2({
3044
+ format = resolveOutputFormat({
3206
3045
  jsonFlag: opts.json ?? false,
3207
3046
  humanFlag: (opts.human ?? false) || isHuman(),
3208
3047
  projectDefault: "json"
@@ -3260,7 +3099,7 @@ ${provider.toolName}`));
3260
3099
  const mvi = "standard";
3261
3100
  let format;
3262
3101
  try {
3263
- format = resolveOutputFormat2({
3102
+ format = resolveOutputFormat({
3264
3103
  jsonFlag: opts.json ?? false,
3265
3104
  humanFlag: (opts.human ?? false) || isHuman(),
3266
3105
  projectDefault: "json"
@@ -3328,7 +3167,7 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
3328
3167
  const mvi = "standard";
3329
3168
  let format;
3330
3169
  try {
3331
- format = resolveOutputFormat2({
3170
+ format = resolveOutputFormat({
3332
3171
  jsonFlag: opts.json ?? false,
3333
3172
  humanFlag: (opts.human ?? false) || isHuman(),
3334
3173
  projectDefault: "json"
@@ -3375,7 +3214,7 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
3375
3214
  const mvi = "standard";
3376
3215
  let format;
3377
3216
  try {
3378
- format = resolveOutputFormat2({
3217
+ format = resolveOutputFormat({
3379
3218
  jsonFlag: opts.json ?? false,
3380
3219
  humanFlag: (opts.human ?? false) || isHuman(),
3381
3220
  projectDefault: "json"
@@ -3506,7 +3345,7 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
3506
3345
  const mvi = "standard";
3507
3346
  let format;
3508
3347
  try {
3509
- format = resolveOutputFormat2({
3348
+ format = resolveOutputFormat({
3510
3349
  jsonFlag: opts.json ?? false,
3511
3350
  humanFlag: (opts.human ?? false) || isHuman(),
3512
3351
  projectDefault: "json"
@@ -3576,7 +3415,7 @@ function buildEnvelope2(operation, mvi, result, error) {
3576
3415
  schemaVersion: "1.0.0",
3577
3416
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3578
3417
  operation,
3579
- requestId: randomUUID3(),
3418
+ requestId: randomUUID2(),
3580
3419
  transport: "cli",
3581
3420
  strict: true,
3582
3421
  mvi,
@@ -3892,8 +3731,8 @@ function registerSkillsCheck(parent) {
3892
3731
  }
3893
3732
 
3894
3733
  // src/commands/skills/find.ts
3895
- import { randomUUID as randomUUID4 } from "crypto";
3896
- import { resolveOutputFormat as resolveOutputFormat3 } from "@cleocode/lafs";
3734
+ import { randomUUID as randomUUID3 } from "crypto";
3735
+ import { resolveOutputFormat as resolveOutputFormat2 } from "@cleocode/lafs";
3897
3736
  import pc9 from "picocolors";
3898
3737
  var SkillsFindValidationError = class extends Error {
3899
3738
  code;
@@ -3925,7 +3764,7 @@ function registerSkillsFind(parent) {
3925
3764
  const mvi = details ? "full" : "standard";
3926
3765
  let format;
3927
3766
  try {
3928
- format = resolveOutputFormat3({
3767
+ format = resolveOutputFormat2({
3929
3768
  jsonFlag: opts.json ?? false,
3930
3769
  humanFlag: (opts.human ?? false) || isHuman(),
3931
3770
  projectDefault: "json"
@@ -4154,7 +3993,7 @@ function buildEnvelope3(operation, mvi, result, error) {
4154
3993
  schemaVersion: "1.0.0",
4155
3994
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4156
3995
  operation,
4157
- requestId: randomUUID4(),
3996
+ requestId: randomUUID3(),
4158
3997
  transport: "cli",
4159
3998
  strict: true,
4160
3999
  mvi,
@@ -4181,7 +4020,7 @@ function emitJsonError3(operation, mvi, code, message, category, details = {}) {
4181
4020
  // src/commands/skills/init.ts
4182
4021
  import { existsSync as existsSync10 } from "fs";
4183
4022
  import { mkdir, writeFile as writeFile3 } from "fs/promises";
4184
- import { join as join8 } from "path";
4023
+ import { join as join6 } from "path";
4185
4024
  import pc10 from "picocolors";
4186
4025
  function registerSkillsInit(parent) {
4187
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(
@@ -4207,7 +4046,7 @@ function registerSkillsInit(parent) {
4207
4046
  process.exit(1);
4208
4047
  }
4209
4048
  const skillName = name ?? "my-skill";
4210
- const skillDir = join8(opts.dir, skillName);
4049
+ const skillDir = join6(opts.dir, skillName);
4211
4050
  if (existsSync10(skillDir)) {
4212
4051
  const message = `Directory already exists: ${skillDir}`;
4213
4052
  if (format === "json") {
@@ -4250,7 +4089,7 @@ Provide detailed instructions for the AI agent here.
4250
4089
 
4251
4090
  Show example inputs and expected outputs.
4252
4091
  `;
4253
- await writeFile3(join8(skillDir, "SKILL.md"), template, "utf-8");
4092
+ await writeFile3(join6(skillDir, "SKILL.md"), template, "utf-8");
4254
4093
  const result = {
4255
4094
  name: skillName,
4256
4095
  directory: skillDir,
@@ -4264,95 +4103,88 @@ Show example inputs and expected outputs.
4264
4103
  console.log(pc10.green(`\u2713 Created skill template: ${skillDir}/SKILL.md`));
4265
4104
  console.log(pc10.dim("\nNext steps:"));
4266
4105
  console.log(pc10.dim(" 1. Edit SKILL.md with your instructions"));
4267
- 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")}`));
4268
4107
  console.log(pc10.dim(` 3. Install: caamp skills install ${skillDir}`));
4269
4108
  }
4270
4109
  );
4271
4110
  }
4272
4111
 
4273
4112
  // src/commands/skills/install.ts
4274
- import { existsSync as existsSync11 } from "fs";
4275
- import { createRequire } from "module";
4276
- import { pushWarning } from "@cleocode/lafs";
4277
4113
  import pc11 from "picocolors";
4278
- var cachedCore;
4279
- var defaultCoreResolver = async () => {
4280
- const require2 = createRequire(import.meta.url);
4281
- require2.resolve("@cleocode/core");
4282
- return await import("@cleocode/core");
4283
- };
4284
- var coreResolver = defaultCoreResolver;
4285
- async function resolveCore() {
4286
- if (cachedCore !== void 0) return cachedCore;
4287
- try {
4288
- cachedCore = await coreResolver();
4289
- } catch (err) {
4290
- cachedCore = null;
4291
- pushWarning({
4292
- code: "W_CORE_UNAVAILABLE",
4293
- severity: "warn",
4294
- message: "caamp running without @cleocode/core \u2014 trust gate skipped (skill installs not security-checked)",
4295
- context: {
4296
- error: err instanceof Error ? err.message : String(err)
4297
- }
4298
- });
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;
4299
4127
  }
4300
- return cachedCore;
4301
4128
  }
4302
- async function evaluateSkillTrustGate(localPath, source, force = false) {
4303
- const core = await resolveCore();
4304
- if (!core) {
4305
- return {
4306
- decision: "allow",
4307
- reason: "Trust gate skipped \u2014 @cleocode/core not available",
4308
- scan: {
4309
- skillName: localPath.split("/").filter(Boolean).pop() ?? localPath,
4310
- source,
4311
- trustLevel: "community",
4312
- verdict: "safe",
4313
- findings: [],
4314
- scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
4315
- summary: "core-unavailable"
4316
- }
4317
- };
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;
4318
4139
  }
4319
- const scan = core.scanSkill(localPath, source);
4320
- const gate = core.shouldAllowInstall(scan, force);
4321
- return { decision: gate.decision, reason: gate.reason, scan };
4322
4140
  }
4323
- async function evaluateFederationGate(source, opts = {}) {
4324
- const core = await resolveCore();
4325
- if (!core) {
4326
- return {
4327
- decision: "allow",
4328
- reason: "Federation gate skipped \u2014 @cleocode/core not available",
4329
- peer: null,
4330
- isFederationSource: false,
4331
- computedChecksum: null,
4332
- expectedChecksum: null
4333
- };
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
+ }
4334
4174
  }
4335
- return core.evaluateFederationInstallGate({
4336
- source,
4337
- artefactPath: opts.artefactPath,
4338
- expectedChecksum: opts.expectedChecksum,
4339
- approveNewSource: opts.approveNewSource
4340
- });
4341
- }
4342
- async function recordSkillTrustBypass(scan, reason = null) {
4343
- const core = await resolveCore();
4344
- if (!core) return;
4345
- try {
4346
- core.recordTrustBypass(scan, reason);
4347
- } catch (err) {
4348
- const msg = err instanceof Error ? err.message : String(err);
4349
- pushWarning({
4350
- code: "W_AUDIT_LOG_FAILED",
4351
- severity: "warn",
4352
- message: "trust-bypass audit record failed",
4353
- context: { error: msg }
4354
- });
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)."));
4355
4183
  }
4184
+ if (err.details.availableSkills && format === "human") {
4185
+ console.log(pc11.dim(`Available skills: ${err.details.availableSkills.join(", ")}`));
4186
+ }
4187
+ process.exit(1);
4356
4188
  }
4357
4189
  function registerSkillsInstall(parent) {
4358
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(
@@ -4402,7 +4234,7 @@ function registerSkillsInstall(parent) {
4402
4234
  if (providers.length === 0) {
4403
4235
  const message = "No target providers found. Use --agent or --all.";
4404
4236
  if (format === "json") {
4405
- emitError2(
4237
+ emitError(
4406
4238
  operation,
4407
4239
  mvi,
4408
4240
  ErrorCodes.PROVIDER_NOT_FOUND,
@@ -4427,7 +4259,7 @@ function registerSkillsInstall(parent) {
4427
4259
  if (!source) {
4428
4260
  const message = "Missing required argument: source";
4429
4261
  if (format === "json") {
4430
- emitError2(
4262
+ emitError(
4431
4263
  operation,
4432
4264
  mvi,
4433
4265
  ErrorCodes.INVALID_INPUT,
@@ -4444,335 +4276,74 @@ function registerSkillsInstall(parent) {
4444
4276
  if (format === "human") {
4445
4277
  console.log(pc11.dim(`Installing to ${providers.length} provider(s)...`));
4446
4278
  }
4447
- let localPath;
4448
- let cleanup;
4449
- let skillName;
4450
- let sourceValue;
4451
- let sourceType;
4452
- if (isMarketplaceScoped(source)) {
4453
- const sourceResult = await handleMarketplaceSource(
4454
- source,
4279
+ let result;
4280
+ try {
4281
+ result = await installSkillFromSource(source, {
4455
4282
  providers,
4456
- opts.global ?? false,
4457
- format,
4458
- operation,
4459
- mvi
4460
- );
4461
- if (sourceResult.success) {
4462
- localPath = sourceResult.localPath;
4463
- cleanup = sourceResult.cleanup;
4464
- skillName = sourceResult.skillName;
4465
- sourceValue = sourceResult.sourceValue;
4466
- sourceType = sourceResult.sourceType;
4467
- } else {
4468
- process.exit(1);
4469
- }
4470
- } else {
4471
- const parsed = parseSource(source);
4472
- skillName = parsed.inferredName;
4473
- sourceValue = parsed.value;
4474
- sourceType = parsed.type;
4475
- if (parsed.type === "github" && parsed.owner && parsed.repo) {
4476
- try {
4477
- const result = await cloneRepo(parsed.owner, parsed.repo, parsed.ref, parsed.path);
4478
- localPath = result.localPath;
4479
- cleanup = result.cleanup;
4480
- } catch (error) {
4481
- const message = `Failed to clone GitHub repository: ${formatNetworkError(error)}`;
4482
- if (format === "json") {
4483
- emitJsonError(
4484
- operation,
4485
- mvi,
4486
- ErrorCodes.NETWORK_ERROR,
4487
- message,
4488
- ErrorCategories.TRANSIENT
4489
- );
4490
- }
4491
- console.error(pc11.red(message));
4492
- process.exit(1);
4493
- }
4494
- } else if (parsed.type === "gitlab" && parsed.owner && parsed.repo) {
4495
- try {
4496
- const result = await cloneGitLabRepo(
4497
- parsed.owner,
4498
- parsed.repo,
4499
- parsed.ref,
4500
- parsed.path
4501
- );
4502
- localPath = result.localPath;
4503
- cleanup = result.cleanup;
4504
- } catch (error) {
4505
- const message = `Failed to clone GitLab repository: ${formatNetworkError(error)}`;
4506
- if (format === "json") {
4507
- emitJsonError(
4508
- operation,
4509
- mvi,
4510
- ErrorCodes.NETWORK_ERROR,
4511
- message,
4512
- ErrorCategories.TRANSIENT
4513
- );
4514
- }
4515
- console.error(pc11.red(message));
4516
- process.exit(1);
4517
- }
4518
- } else if (parsed.type === "local") {
4519
- localPath = parsed.value;
4520
- const discovered = await discoverSkill(localPath);
4521
- if (discovered) {
4522
- skillName = discovered.name;
4523
- }
4524
- } else if (parsed.type === "package") {
4525
- if (!isCatalogAvailable()) {
4526
- const message = "No skill library registered. Register one with registerSkillLibraryFromPath() or set CAAMP_SKILL_LIBRARY env var.";
4527
- if (format === "json") {
4528
- emitJsonError(
4529
- operation,
4530
- mvi,
4531
- ErrorCodes.INVALID_INPUT,
4532
- message,
4533
- ErrorCategories.VALIDATION
4534
- );
4535
- }
4536
- console.error(pc11.red(message));
4537
- process.exit(1);
4538
- }
4539
- const catalogSkill = getSkill(parsed.inferredName);
4540
- if (catalogSkill) {
4541
- localPath = getSkillDir(catalogSkill.name);
4542
- skillName = catalogSkill.name;
4543
- sourceValue = `library:${catalogSkill.name}`;
4544
- sourceType = "library";
4545
- if (format === "human") {
4546
- console.log(
4547
- ` Found in catalog: ${pc11.bold(catalogSkill.name)} v${catalogSkill.version} (${pc11.dim(catalogSkill.category)})`
4548
- );
4549
- }
4550
- } else {
4551
- const message = `Skill not found in catalog: ${parsed.inferredName}`;
4552
- if (format === "json") {
4553
- emitJsonError(
4554
- operation,
4555
- mvi,
4556
- ErrorCodes.SKILL_NOT_FOUND,
4557
- message,
4558
- ErrorCategories.NOT_FOUND,
4559
- {
4560
- availableSkills: listSkills()
4561
- }
4562
- );
4563
- }
4564
- console.error(pc11.red(message));
4565
- console.log(pc11.dim("Available skills: " + listSkills().join(", ")));
4566
- process.exit(1);
4567
- }
4568
- } else {
4569
- const message = `Unsupported source type: ${parsed.type}`;
4570
- if (format === "json") {
4571
- emitJsonError(
4572
- operation,
4573
- mvi,
4574
- ErrorCodes.INVALID_FORMAT,
4575
- message,
4576
- ErrorCategories.VALIDATION
4577
- );
4578
- }
4579
- console.error(pc11.red(message));
4580
- 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);
4581
4292
  }
4293
+ throw error;
4582
4294
  }
4583
- try {
4584
- if (!localPath) {
4585
- const message = "No local skill path resolved for installation";
4586
- if (format === "json") {
4587
- emitJsonError(
4588
- operation,
4589
- mvi,
4590
- ErrorCodes.INTERNAL_ERROR,
4591
- message,
4592
- ErrorCategories.INTERNAL
4593
- );
4594
- }
4595
- console.error(pc11.red(message));
4596
- process.exit(1);
4597
- }
4598
- if (sourceType !== "library" && sourceType !== "package") {
4599
- const fedGate = await evaluateFederationGate(sourceValue, {
4600
- artefactPath: localPath,
4601
- approveNewSource: opts.allowNewSource === true
4602
- });
4603
- if (fedGate.decision === "block-checksum") {
4604
- const message = `Federation install blocked: ${fedGate.reason}`;
4605
- if (format === "json") {
4606
- emitJsonError(
4607
- operation,
4608
- mvi,
4609
- ErrorCodes.FEDERATION_CHECKSUM_MISMATCH,
4610
- message,
4611
- ErrorCategories.VALIDATION,
4612
- {
4613
- expectedChecksum: fedGate.expectedChecksum,
4614
- computedChecksum: fedGate.computedChecksum
4615
- }
4616
- );
4617
- }
4618
- console.error(pc11.red(message));
4619
- process.exit(1);
4620
- }
4621
- if (fedGate.decision === "prompt-first-install") {
4622
- const message = `${fedGate.reason} Use --allow-new-source to approve in non-interactive contexts.`;
4623
- if (format === "json") {
4624
- emitJsonError(
4625
- operation,
4626
- mvi,
4627
- ErrorCodes.FEDERATION_UNKNOWN_SOURCE_INTERACTIVE_REQUIRED,
4628
- message,
4629
- ErrorCategories.VALIDATION,
4630
- { peer: fedGate.peer?.url ?? null }
4631
- );
4632
- }
4633
- console.error(pc11.yellow(message));
4634
- process.exit(1);
4635
- }
4636
- }
4637
- if (sourceType !== "library" && sourceType !== "package") {
4638
- const gate = await evaluateSkillTrustGate(localPath, sourceValue, opts.force ?? false);
4639
- if (gate.decision === "block") {
4640
- const message = `Trust gate blocked install: ${gate.reason}`;
4641
- if (format === "json") {
4642
- const envelope = buildEnvelope(
4643
- operation,
4644
- mvi,
4645
- { scan: gate.scan },
4646
- {
4647
- code: ErrorCodes.SKILL_TRUST_GATE_BLOCKED,
4648
- message,
4649
- category: ErrorCategories.VALIDATION,
4650
- retryable: false,
4651
- retryAfterMs: null,
4652
- details: {
4653
- verdict: gate.scan.verdict,
4654
- trustLevel: gate.scan.trustLevel,
4655
- findingsCount: gate.scan.findings.length
4656
- }
4657
- }
4658
- );
4659
- console.error(JSON.stringify(envelope, null, 2));
4660
- } else {
4661
- console.error(pc11.red(`
4662
- ${message}`));
4663
- for (const f of gate.scan.findings.slice(0, 5)) {
4664
- console.error(
4665
- pc11.dim(
4666
- ` [${f.severity}] ${f.category} ${f.file}:${f.line} \u2014 ${f.description}`
4667
- )
4668
- );
4669
- }
4670
- console.error(pc11.yellow(" Use --force to override (audited)."));
4671
- }
4672
- process.exit(1);
4673
- }
4674
- if (gate.decision === "ask") {
4675
- const message = `Trust gate requires confirmation: ${gate.reason}`;
4676
- if (format === "json") {
4677
- emitJsonError(
4678
- operation,
4679
- mvi,
4680
- ErrorCodes.SKILL_TRUST_GATE_BLOCKED,
4681
- message,
4682
- ErrorCategories.VALIDATION
4683
- );
4684
- }
4685
- console.error(pc11.red(message));
4686
- process.exit(1);
4687
- }
4688
- if (opts.force === true && gate.scan.verdict !== "safe") {
4689
- await recordSkillTrustBypass(gate.scan, "operator --force on caamp skills install");
4690
- }
4691
- }
4692
- const result = await dispatchInstallSkillAcrossProviders(
4693
- localPath,
4694
- skillName,
4695
- providers,
4696
- opts.global ?? false
4697
- );
4698
- if (result.success) {
4699
- const isGlobal = sourceType === "library" || sourceType === "package" ? true : opts.global ?? false;
4700
- await recordSkillInstall(
4701
- skillName,
4702
- sourceValue,
4703
- sourceValue,
4704
- sourceType,
4705
- result.linkedAgents,
4706
- result.canonicalPath,
4707
- isGlobal
4708
- );
4709
- const installedItem = {
4710
- name: skillName,
4711
- scopedName: sourceValue,
4712
- canonicalPath: result.canonicalPath,
4713
- providers: result.linkedAgents
4714
- };
4715
- const summary = {
4716
- installed: [installedItem],
4717
- failed: [],
4718
- count: {
4719
- installed: 1,
4720
- failed: 0,
4721
- total: 1
4722
- }
4723
- };
4724
- if (format === "json") {
4725
- outputSuccess(operation, mvi, summary);
4726
- } else {
4727
- console.log(pc11.green(`
4728
- \u2713 Installed ${pc11.bold(skillName)}`));
4729
- console.log(` Canonical: ${pc11.dim(result.canonicalPath)}`);
4730
- console.log(` Linked to: ${result.linkedAgents.join(", ")}`);
4731
- if (result.errors.length > 0) {
4732
- console.log(pc11.yellow("\nWarnings:"));
4733
- for (const err of result.errors) {
4734
- console.log(` ${pc11.yellow("!")} ${err}`);
4735
- }
4736
- }
4737
- }
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);
4738
4309
  } else {
4739
- const summary = {
4740
- installed: [],
4741
- failed: [
4742
- {
4743
- name: skillName,
4744
- error: result.errors.join(", ")
4745
- }
4746
- ],
4747
- count: {
4748
- installed: 0,
4749
- failed: 1,
4750
- total: 1
4751
- }
4752
- };
4753
- if (format === "json") {
4754
- const envelope = buildEnvelope(operation, mvi, summary, {
4755
- code: ErrorCodes.INSTALL_FAILED,
4756
- message: result.errors.join(", "),
4757
- category: ErrorCategories.INTERNAL,
4758
- retryable: false,
4759
- retryAfterMs: null,
4760
- details: { skillName, sourceValue }
4761
- });
4762
- console.error(JSON.stringify(envelope, null, 2));
4763
- } else {
4764
- console.log(pc11.yellow(`
4765
- \u2717 Failed to install ${pc11.bold(skillName)}`));
4766
- 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:"));
4767
4316
  for (const err of result.errors) {
4768
4317
  console.log(` ${pc11.yellow("!")} ${err}`);
4769
4318
  }
4770
4319
  }
4771
- process.exit(1);
4772
4320
  }
4773
- } finally {
4774
- if (cleanup) await cleanup();
4321
+ return;
4775
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);
4776
4347
  }
4777
4348
  );
4778
4349
  }
@@ -4780,7 +4351,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
4780
4351
  if (!isCatalogAvailable()) {
4781
4352
  const message = "No skill library registered. Register one with registerSkillLibraryFromPath() or set CAAMP_SKILL_LIBRARY env var.";
4782
4353
  if (format === "json") {
4783
- emitError2(operation, mvi, ErrorCodes.INVALID_INPUT, message, ErrorCategories.VALIDATION);
4354
+ emitError(operation, mvi, ErrorCodes.INVALID_INPUT, message, ErrorCategories.VALIDATION);
4784
4355
  }
4785
4356
  console.error(pc11.red(message));
4786
4357
  process.exit(1);
@@ -4814,22 +4385,20 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
4814
4385
  const installed = [];
4815
4386
  const failed = [];
4816
4387
  for (const name of profileSkills) {
4817
- const skillDir = getSkillDir(name);
4818
4388
  try {
4819
- 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
+ );
4820
4398
  if (result.success) {
4821
4399
  if (format === "human") {
4822
4400
  console.log(pc11.green(` + ${name}`));
4823
4401
  }
4824
- await recordSkillInstall(
4825
- name,
4826
- `library:${name}`,
4827
- `library:${name}`,
4828
- "library",
4829
- result.linkedAgents,
4830
- result.canonicalPath,
4831
- true
4832
- );
4833
4402
  installed.push({
4834
4403
  name,
4835
4404
  scopedName: `library:${name}`,
@@ -4890,89 +4459,10 @@ ${pc11.green(`${installed.length} installed`)}, ${failed.length > 0 ? pc11.yello
4890
4459
  }
4891
4460
  }
4892
4461
  }
4893
- async function handleMarketplaceSource(source, _providers, _isGlobal, format, operation, mvi) {
4894
- if (format === "human") {
4895
- console.log(pc11.dim(`Searching marketplace for ${source}...`));
4896
- }
4897
- const client = new MarketplaceClient();
4898
- let skill;
4899
- try {
4900
- skill = await client.getSkill(source);
4901
- } catch (error) {
4902
- const message = `Marketplace lookup failed: ${formatNetworkError(error)}`;
4903
- if (format === "json") {
4904
- emitJsonError(operation, mvi, ErrorCodes.NETWORK_ERROR, message, ErrorCategories.TRANSIENT);
4905
- }
4906
- console.error(pc11.red(message));
4907
- return { success: false };
4908
- }
4909
- if (!skill) {
4910
- const message = `Skill not found: ${source}`;
4911
- if (format === "json") {
4912
- emitJsonError(operation, mvi, ErrorCodes.SKILL_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
4913
- }
4914
- console.error(pc11.red(message));
4915
- return { success: false };
4916
- }
4917
- if (format === "human") {
4918
- console.log(
4919
- ` Found: ${pc11.bold(skill.name)} by ${skill.author} (${pc11.dim(skill.repoFullName)})`
4920
- );
4921
- }
4922
- const parsed = parseSource(skill.githubUrl);
4923
- if (parsed.type !== "github" || !parsed.owner || !parsed.repo) {
4924
- const message = "Could not resolve GitHub source";
4925
- if (format === "json") {
4926
- emitJsonError(operation, mvi, ErrorCodes.INVALID_FORMAT, message, ErrorCategories.VALIDATION);
4927
- }
4928
- console.error(pc11.red(message));
4929
- return { success: false };
4930
- }
4931
- try {
4932
- const subPathCandidates = buildSkillSubPathCandidates(skill.path, parsed.path);
4933
- let cloneError;
4934
- let cloned = false;
4935
- let localPath;
4936
- let cleanup;
4937
- for (const subPath of subPathCandidates) {
4938
- try {
4939
- const result = await cloneRepo(parsed.owner, parsed.repo, parsed.ref, subPath);
4940
- if (subPath && !existsSync11(result.localPath)) {
4941
- await result.cleanup();
4942
- continue;
4943
- }
4944
- localPath = result.localPath;
4945
- cleanup = result.cleanup;
4946
- cloned = true;
4947
- break;
4948
- } catch (error) {
4949
- cloneError = error;
4950
- }
4951
- }
4952
- if (!cloned) {
4953
- throw cloneError ?? new Error("Unable to resolve skill path from marketplace metadata");
4954
- }
4955
- return {
4956
- success: true,
4957
- localPath,
4958
- cleanup,
4959
- skillName: skill.name,
4960
- sourceValue: skill.githubUrl,
4961
- sourceType: parsed.type
4962
- };
4963
- } catch (error) {
4964
- const message = `Failed to fetch source repository: ${formatNetworkError(error)}`;
4965
- if (format === "json") {
4966
- emitJsonError(operation, mvi, ErrorCodes.NETWORK_ERROR, message, ErrorCategories.TRANSIENT);
4967
- }
4968
- console.error(pc11.red(message));
4969
- return { success: false };
4970
- }
4971
- }
4972
4462
 
4973
4463
  // src/commands/skills/list.ts
4974
- import { randomUUID as randomUUID5 } from "crypto";
4975
- import { resolveOutputFormat as resolveOutputFormat4 } from "@cleocode/lafs";
4464
+ import { randomUUID as randomUUID4 } from "crypto";
4465
+ import { resolveOutputFormat as resolveOutputFormat3 } from "@cleocode/lafs";
4976
4466
  import pc12 from "picocolors";
4977
4467
  function registerSkillsList(parent) {
4978
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) => {
@@ -4980,7 +4470,7 @@ function registerSkillsList(parent) {
4980
4470
  const mvi = "standard";
4981
4471
  let format;
4982
4472
  try {
4983
- format = resolveOutputFormat4({
4473
+ format = resolveOutputFormat3({
4984
4474
  jsonFlag: opts.json ?? false,
4985
4475
  humanFlag: (opts.human ?? false) || isHuman(),
4986
4476
  projectDefault: "json"
@@ -5053,7 +4543,7 @@ function buildEnvelope4(operation, mvi, result, error) {
5053
4543
  schemaVersion: "1.0.0",
5054
4544
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
5055
4545
  operation,
5056
- requestId: randomUUID5(),
4546
+ requestId: randomUUID4(),
5057
4547
  transport: "cli",
5058
4548
  strict: true,
5059
4549
  mvi,
@@ -5310,12 +4800,14 @@ ${outdated.length} skill(s) have updates available:
5310
4800
  skipped.push(skill.name);
5311
4801
  continue;
5312
4802
  }
5313
- const installResult = await dispatchInstallSkillAcrossProviders(
5314
- localPath,
5315
- skill.name,
5316
- providers,
5317
- entry.isGlobal,
5318
- 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 }
5319
4811
  );
5320
4812
  if (installResult.success) {
5321
4813
  await recordSkillInstall(
@@ -5379,7 +4871,7 @@ ${outdated.length} skill(s) have updates available:
5379
4871
  }
5380
4872
 
5381
4873
  // src/commands/skills/validate.ts
5382
- import { resolveOutputFormat as resolveOutputFormat5 } from "@cleocode/lafs";
4874
+ import { resolveOutputFormat as resolveOutputFormat4 } from "@cleocode/lafs";
5383
4875
  import pc15 from "picocolors";
5384
4876
  function registerSkillsValidate(parent) {
5385
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) => {
@@ -5387,7 +4879,7 @@ function registerSkillsValidate(parent) {
5387
4879
  const mvi = "standard";
5388
4880
  let format;
5389
4881
  try {
5390
- format = resolveOutputFormat5({
4882
+ format = resolveOutputFormat4({
5391
4883
  jsonFlag: opts.json ?? false,
5392
4884
  humanFlag: (opts.human ?? false) || isHuman(),
5393
4885
  projectDefault: "json"