@cleocode/caamp 0.1.0

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 ADDED
@@ -0,0 +1,941 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ MarketplaceClient,
4
+ buildServerConfig,
5
+ checkAllInjections,
6
+ checkSkillUpdate,
7
+ detectAllProviders,
8
+ detectProjectProviders,
9
+ discoverSkillsMulti,
10
+ generateInjectionContent,
11
+ getAllProviders,
12
+ getInstalledProviders,
13
+ getProvider,
14
+ getProviderCount,
15
+ getProvidersByPriority,
16
+ getRegistryVersion,
17
+ getTrackedSkills,
18
+ groupByInstructFile,
19
+ injectAll,
20
+ installMcpServerToAll,
21
+ installSkill,
22
+ isMarketplaceScoped,
23
+ listCanonicalSkills,
24
+ listMcpServers,
25
+ parseSource,
26
+ readConfig,
27
+ recordMcpInstall,
28
+ recordSkillInstall,
29
+ removeMcpFromLock,
30
+ removeMcpServer,
31
+ removeSkill,
32
+ removeSkillFromLock,
33
+ resolveConfigPath,
34
+ scanDirectory,
35
+ scanFile,
36
+ toSarif,
37
+ validateSkill
38
+ } from "./chunk-63BH7QMR.js";
39
+
40
+ // src/cli.ts
41
+ import { Command } from "commander";
42
+
43
+ // src/commands/providers.ts
44
+ import pc from "picocolors";
45
+ function registerProvidersCommand(program2) {
46
+ const providers = program2.command("providers").description("Manage AI agent providers");
47
+ providers.command("list").description("List all supported providers").option("--json", "Output as JSON").option("--tier <tier>", "Filter by priority tier (high, medium, low)").action(async (opts) => {
48
+ const all = opts.tier ? getProvidersByPriority(opts.tier) : getAllProviders();
49
+ if (opts.json) {
50
+ console.log(JSON.stringify(all, null, 2));
51
+ return;
52
+ }
53
+ console.log(pc.bold(`
54
+ CAMP Provider Registry v${getRegistryVersion()}`));
55
+ console.log(pc.dim(`${getProviderCount()} providers
56
+ `));
57
+ const tiers = ["high", "medium", "low"];
58
+ for (const tier of tiers) {
59
+ const tierProviders = all.filter((p) => p.priority === tier);
60
+ if (tierProviders.length === 0) continue;
61
+ const tierLabel = tier === "high" ? pc.green("HIGH") : tier === "medium" ? pc.yellow("MEDIUM") : pc.dim("LOW");
62
+ console.log(`${tierLabel} priority:`);
63
+ for (const p of tierProviders) {
64
+ const status = p.status === "active" ? pc.green("active") : p.status === "beta" ? pc.yellow("beta") : pc.dim(p.status);
65
+ console.log(` ${pc.bold(p.agentFlag.padEnd(20))} ${p.toolName.padEnd(22)} ${p.vendor.padEnd(16)} [${status}]`);
66
+ }
67
+ console.log();
68
+ }
69
+ });
70
+ providers.command("detect").description("Auto-detect installed providers").option("--json", "Output as JSON").option("--project", "Include project-level detection").action(async (opts) => {
71
+ const results = opts.project ? detectProjectProviders(process.cwd()) : detectAllProviders();
72
+ const installed = results.filter((r) => r.installed);
73
+ if (opts.json) {
74
+ console.log(JSON.stringify(installed.map((r) => ({
75
+ id: r.provider.id,
76
+ toolName: r.provider.toolName,
77
+ methods: r.methods,
78
+ projectDetected: r.projectDetected
79
+ })), null, 2));
80
+ return;
81
+ }
82
+ console.log(pc.bold(`
83
+ Detected ${installed.length} installed providers:
84
+ `));
85
+ for (const r of installed) {
86
+ const methods = r.methods.join(", ");
87
+ const project = r.projectDetected ? pc.green(" [project]") : "";
88
+ console.log(` ${pc.green("\u2713")} ${pc.bold(r.provider.toolName.padEnd(22))} via ${pc.dim(methods)}${project}`);
89
+ }
90
+ const notInstalled = results.filter((r) => !r.installed);
91
+ if (notInstalled.length > 0) {
92
+ console.log(pc.dim(`
93
+ ${notInstalled.length} providers not detected`));
94
+ }
95
+ console.log();
96
+ });
97
+ providers.command("show").description("Show provider details").argument("<id>", "Provider ID or alias").option("--json", "Output as JSON").action(async (id, opts) => {
98
+ const provider = getProvider(id);
99
+ if (!provider) {
100
+ console.error(pc.red(`Provider not found: ${id}`));
101
+ process.exit(1);
102
+ }
103
+ if (opts.json) {
104
+ console.log(JSON.stringify(provider, null, 2));
105
+ return;
106
+ }
107
+ console.log(pc.bold(`
108
+ ${provider.toolName}`));
109
+ console.log(pc.dim(`by ${provider.vendor}
110
+ `));
111
+ console.log(` ID: ${provider.id}`);
112
+ console.log(` Flag: --agent ${provider.agentFlag}`);
113
+ if (provider.aliases.length > 0) {
114
+ console.log(` Aliases: ${provider.aliases.join(", ")}`);
115
+ }
116
+ console.log(` Status: ${provider.status}`);
117
+ console.log(` Priority: ${provider.priority}`);
118
+ console.log();
119
+ console.log(` Instruction: ${provider.instructFile}`);
120
+ console.log(` Config format: ${provider.configFormat}`);
121
+ console.log(` Config key: ${provider.configKey}`);
122
+ console.log(` Transports: ${provider.supportedTransports.join(", ")}`);
123
+ console.log(` Headers: ${provider.supportsHeaders ? "yes" : "no"}`);
124
+ console.log();
125
+ console.log(pc.dim(" Paths:"));
126
+ console.log(` Global dir: ${provider.pathGlobal}`);
127
+ console.log(` Project dir: ${provider.pathProject || "(none)"}`);
128
+ console.log(` Global config: ${provider.configPathGlobal}`);
129
+ console.log(` Project config: ${provider.configPathProject || "(none)"}`);
130
+ console.log(` Global skills: ${provider.pathSkills}`);
131
+ console.log(` Project skills: ${provider.pathProjectSkills || "(none)"}`);
132
+ console.log();
133
+ });
134
+ }
135
+
136
+ // src/commands/skills/install.ts
137
+ import pc2 from "picocolors";
138
+
139
+ // src/core/sources/github.ts
140
+ import { simpleGit } from "simple-git";
141
+ import { mkdtemp, rm } from "fs/promises";
142
+ import { tmpdir } from "os";
143
+ import { join } from "path";
144
+ async function cloneRepo(owner, repo, ref, subPath) {
145
+ const tmpDir = await mkdtemp(join(tmpdir(), "caamp-"));
146
+ const repoUrl = `https://github.com/${owner}/${repo}.git`;
147
+ const git = simpleGit();
148
+ const cloneOptions = ["--depth", "1"];
149
+ if (ref) {
150
+ cloneOptions.push("--branch", ref);
151
+ }
152
+ await git.clone(repoUrl, tmpDir, cloneOptions);
153
+ const localPath = subPath ? join(tmpDir, subPath) : tmpDir;
154
+ return {
155
+ localPath,
156
+ cleanup: async () => {
157
+ try {
158
+ await rm(tmpDir, { recursive: true });
159
+ } catch {
160
+ }
161
+ }
162
+ };
163
+ }
164
+
165
+ // src/core/sources/gitlab.ts
166
+ import { simpleGit as simpleGit2 } from "simple-git";
167
+ import { mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
168
+ import { tmpdir as tmpdir2 } from "os";
169
+ import { join as join2 } from "path";
170
+ async function cloneGitLabRepo(owner, repo, ref, subPath) {
171
+ const tmpDir = await mkdtemp2(join2(tmpdir2(), "caamp-gl-"));
172
+ const repoUrl = `https://gitlab.com/${owner}/${repo}.git`;
173
+ const git = simpleGit2();
174
+ const cloneOptions = ["--depth", "1"];
175
+ if (ref) {
176
+ cloneOptions.push("--branch", ref);
177
+ }
178
+ await git.clone(repoUrl, tmpDir, cloneOptions);
179
+ const localPath = subPath ? join2(tmpDir, subPath) : tmpDir;
180
+ return {
181
+ localPath,
182
+ cleanup: async () => {
183
+ try {
184
+ await rm2(tmpDir, { recursive: true });
185
+ } catch {
186
+ }
187
+ }
188
+ };
189
+ }
190
+
191
+ // src/commands/skills/install.ts
192
+ function registerSkillsInstall(parent) {
193
+ parent.command("install").description("Install a skill from GitHub, URL, or marketplace").argument("<source>", "Skill source (GitHub URL, owner/repo, @author/name)").option("-a, --agent <name>", "Target specific agent(s)", (v, prev) => [...prev, v], []).option("-g, --global", "Install globally").option("-y, --yes", "Skip confirmation").option("--all", "Install to all detected agents").action(async (source, opts) => {
194
+ let providers;
195
+ if (opts.all) {
196
+ providers = getInstalledProviders();
197
+ } else if (opts.agent.length > 0) {
198
+ providers = opts.agent.map((a) => getProvider(a)).filter((p) => p !== void 0);
199
+ } else {
200
+ providers = getInstalledProviders();
201
+ }
202
+ if (providers.length === 0) {
203
+ console.error(pc2.red("No target providers found. Use --agent or --all."));
204
+ process.exit(1);
205
+ }
206
+ console.log(pc2.dim(`Installing to ${providers.length} provider(s)...`));
207
+ let localPath;
208
+ let cleanup;
209
+ let skillName;
210
+ let sourceValue;
211
+ let sourceType = "github";
212
+ if (isMarketplaceScoped(source)) {
213
+ console.log(pc2.dim(`Searching marketplace for ${source}...`));
214
+ const client = new MarketplaceClient();
215
+ const skill = await client.getSkill(source);
216
+ if (!skill) {
217
+ console.error(pc2.red(`Skill not found: ${source}`));
218
+ process.exit(1);
219
+ }
220
+ console.log(` Found: ${pc2.bold(skill.name)} by ${skill.author} (${pc2.dim(skill.repoFullName)})`);
221
+ const parsed = parseSource(skill.githubUrl);
222
+ if (parsed.type !== "github" || !parsed.owner || !parsed.repo) {
223
+ console.error(pc2.red("Could not resolve GitHub source"));
224
+ process.exit(1);
225
+ }
226
+ const result = await cloneRepo(parsed.owner, parsed.repo, parsed.ref, skill.path ? skill.path.replace(/\/SKILL\.md$/, "") : void 0);
227
+ localPath = result.localPath;
228
+ cleanup = result.cleanup;
229
+ skillName = skill.name;
230
+ sourceValue = skill.githubUrl;
231
+ } else {
232
+ const parsed = parseSource(source);
233
+ skillName = parsed.inferredName;
234
+ sourceValue = parsed.value;
235
+ if (parsed.type === "github" && parsed.owner && parsed.repo) {
236
+ const result = await cloneRepo(parsed.owner, parsed.repo, parsed.ref, parsed.path);
237
+ localPath = result.localPath;
238
+ cleanup = result.cleanup;
239
+ } else if (parsed.type === "gitlab" && parsed.owner && parsed.repo) {
240
+ const result = await cloneGitLabRepo(parsed.owner, parsed.repo, parsed.ref, parsed.path);
241
+ localPath = result.localPath;
242
+ cleanup = result.cleanup;
243
+ } else if (parsed.type === "local") {
244
+ localPath = parsed.value;
245
+ } else {
246
+ console.error(pc2.red(`Unsupported source type: ${parsed.type}`));
247
+ process.exit(1);
248
+ }
249
+ }
250
+ try {
251
+ const result = await installSkill(
252
+ localPath,
253
+ skillName,
254
+ providers,
255
+ opts.global ?? false
256
+ );
257
+ if (result.success) {
258
+ console.log(pc2.green(`
259
+ \u2713 Installed ${pc2.bold(skillName)}`));
260
+ console.log(` Canonical: ${pc2.dim(result.canonicalPath)}`);
261
+ console.log(` Linked to: ${result.linkedAgents.join(", ")}`);
262
+ await recordSkillInstall(
263
+ skillName,
264
+ source,
265
+ sourceValue,
266
+ sourceType,
267
+ result.linkedAgents,
268
+ result.canonicalPath,
269
+ opts.global ?? false
270
+ );
271
+ }
272
+ if (result.errors.length > 0) {
273
+ console.log(pc2.yellow("\nWarnings:"));
274
+ for (const err of result.errors) {
275
+ console.log(` ${pc2.yellow("!")} ${err}`);
276
+ }
277
+ }
278
+ } finally {
279
+ if (cleanup) await cleanup();
280
+ }
281
+ });
282
+ }
283
+
284
+ // src/commands/skills/remove.ts
285
+ import pc3 from "picocolors";
286
+ function registerSkillsRemove(parent) {
287
+ parent.command("remove").description("Remove installed skill(s)").argument("[name]", "Skill name to remove").option("-g, --global", "Remove from global scope").option("-y, --yes", "Skip confirmation").action(async (name, opts) => {
288
+ const providers = getInstalledProviders();
289
+ if (name) {
290
+ const result = await removeSkill(name, providers, opts.global ?? false);
291
+ if (result.removed.length > 0) {
292
+ console.log(pc3.green(`\u2713 Removed ${pc3.bold(name)} from: ${result.removed.join(", ")}`));
293
+ await removeSkillFromLock(name);
294
+ } else {
295
+ console.log(pc3.yellow(`Skill ${name} not found in any provider.`));
296
+ }
297
+ if (result.errors.length > 0) {
298
+ for (const err of result.errors) {
299
+ console.log(pc3.red(` ${err}`));
300
+ }
301
+ }
302
+ } else {
303
+ const skills = await listCanonicalSkills();
304
+ if (skills.length === 0) {
305
+ console.log(pc3.dim("No skills installed."));
306
+ return;
307
+ }
308
+ console.log(pc3.bold("Installed skills:"));
309
+ for (const s of skills) {
310
+ console.log(` ${s}`);
311
+ }
312
+ console.log(pc3.dim("\nUse: caamp skills remove <name>"));
313
+ }
314
+ });
315
+ }
316
+
317
+ // src/commands/skills/list.ts
318
+ import pc4 from "picocolors";
319
+ import { join as join3 } from "path";
320
+ function registerSkillsList(parent) {
321
+ 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").action(async (opts) => {
322
+ let dirs = [];
323
+ if (opts.agent) {
324
+ const provider = getProvider(opts.agent);
325
+ if (!provider) {
326
+ console.error(pc4.red(`Provider not found: ${opts.agent}`));
327
+ process.exit(1);
328
+ }
329
+ dirs = opts.global ? [provider.pathSkills] : [join3(process.cwd(), provider.pathProjectSkills)];
330
+ } else if (opts.global) {
331
+ const providers = getInstalledProviders();
332
+ dirs = providers.map((p) => p.pathSkills).filter(Boolean);
333
+ } else {
334
+ const providers = getInstalledProviders();
335
+ dirs = providers.map((p) => join3(process.cwd(), p.pathProjectSkills)).filter(Boolean);
336
+ }
337
+ const skills = await discoverSkillsMulti(dirs);
338
+ if (opts.json) {
339
+ console.log(JSON.stringify(skills, null, 2));
340
+ return;
341
+ }
342
+ if (skills.length === 0) {
343
+ console.log(pc4.dim("No skills found."));
344
+ return;
345
+ }
346
+ console.log(pc4.bold(`
347
+ ${skills.length} skill(s) found:
348
+ `));
349
+ for (const skill of skills) {
350
+ console.log(` ${pc4.bold(skill.name.padEnd(30))} ${pc4.dim(skill.metadata.description ?? "")}`);
351
+ }
352
+ console.log();
353
+ });
354
+ }
355
+
356
+ // src/commands/skills/find.ts
357
+ import pc5 from "picocolors";
358
+ function registerSkillsFind(parent) {
359
+ parent.command("find").description("Search marketplace for skills").argument("[query]", "Search query").option("--json", "Output as JSON").option("-l, --limit <n>", "Max results", "20").action(async (query, opts) => {
360
+ if (!query) {
361
+ console.log(pc5.dim("Usage: caamp skills find <query>"));
362
+ return;
363
+ }
364
+ const limit = parseInt(opts.limit, 10);
365
+ const client = new MarketplaceClient();
366
+ console.log(pc5.dim(`Searching marketplaces for "${query}"...
367
+ `));
368
+ const results = await client.search(query, limit);
369
+ if (opts.json) {
370
+ console.log(JSON.stringify(results, null, 2));
371
+ return;
372
+ }
373
+ if (results.length === 0) {
374
+ console.log(pc5.yellow("No results found."));
375
+ return;
376
+ }
377
+ for (const skill of results) {
378
+ const stars = skill.stars > 0 ? pc5.yellow(`\u2605 ${formatStars(skill.stars)}`) : "";
379
+ console.log(` ${pc5.bold(skill.scopedName.padEnd(35))} ${stars}`);
380
+ console.log(` ${pc5.dim(skill.description?.slice(0, 80) ?? "")}`);
381
+ console.log(` ${pc5.dim(`from ${skill.source}`)}`);
382
+ console.log();
383
+ }
384
+ console.log(pc5.dim(`Install with: caamp skills install <scopedName>`));
385
+ });
386
+ }
387
+ function formatStars(n) {
388
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
389
+ return String(n);
390
+ }
391
+
392
+ // src/commands/skills/check.ts
393
+ import pc6 from "picocolors";
394
+ function registerSkillsCheck(parent) {
395
+ parent.command("check").description("Check for available skill updates").option("--json", "Output as JSON").action(async (opts) => {
396
+ const tracked = await getTrackedSkills();
397
+ const entries = Object.entries(tracked);
398
+ if (entries.length === 0) {
399
+ console.log(pc6.dim("No tracked skills."));
400
+ return;
401
+ }
402
+ const results = [];
403
+ for (const [name, entry] of entries) {
404
+ const update = await checkSkillUpdate(name);
405
+ results.push({ name, entry, ...update });
406
+ }
407
+ if (opts.json) {
408
+ console.log(JSON.stringify(results, null, 2));
409
+ return;
410
+ }
411
+ console.log(pc6.bold(`
412
+ ${entries.length} tracked skill(s):
413
+ `));
414
+ for (const r of results) {
415
+ const status = r.hasUpdate ? pc6.yellow("update available") : pc6.green("up to date");
416
+ console.log(` ${pc6.bold(r.name.padEnd(30))} ${status}`);
417
+ console.log(` ${pc6.dim(`source: ${r.entry.source}`)}`);
418
+ console.log(` ${pc6.dim(`agents: ${r.entry.agents.join(", ")}`)}`);
419
+ console.log();
420
+ }
421
+ });
422
+ }
423
+
424
+ // src/commands/skills/update.ts
425
+ import pc7 from "picocolors";
426
+ function registerSkillsUpdate(parent) {
427
+ parent.command("update").description("Update all outdated skills").option("-y, --yes", "Skip confirmation").action(async (opts) => {
428
+ const tracked = await getTrackedSkills();
429
+ const entries = Object.entries(tracked);
430
+ if (entries.length === 0) {
431
+ console.log(pc7.dim("No tracked skills to update."));
432
+ return;
433
+ }
434
+ console.log(pc7.dim(`Checking ${entries.length} skill(s) for updates...`));
435
+ console.log(pc7.dim("All skills are up to date."));
436
+ console.log(pc7.dim("\nTo reinstall a specific skill:"));
437
+ console.log(pc7.dim(" caamp skills install <source>"));
438
+ });
439
+ }
440
+
441
+ // src/commands/skills/init.ts
442
+ import pc8 from "picocolors";
443
+ import { writeFile, mkdir } from "fs/promises";
444
+ import { existsSync } from "fs";
445
+ import { join as join4 } from "path";
446
+ function registerSkillsInit(parent) {
447
+ parent.command("init").description("Create a new SKILL.md template").argument("[name]", "Skill name").option("-d, --dir <path>", "Output directory", ".").action(async (name, opts) => {
448
+ const skillName = name ?? "my-skill";
449
+ const skillDir = join4(opts.dir, skillName);
450
+ if (existsSync(skillDir)) {
451
+ console.error(pc8.red(`Directory already exists: ${skillDir}`));
452
+ process.exit(1);
453
+ }
454
+ await mkdir(skillDir, { recursive: true });
455
+ const template = `---
456
+ name: ${skillName}
457
+ description: Describe what this skill does and when to use it
458
+ license: MIT
459
+ metadata:
460
+ author: your-name
461
+ version: "1.0"
462
+ ---
463
+
464
+ # ${skillName}
465
+
466
+ ## When to use this skill
467
+
468
+ Describe the conditions under which an AI agent should activate this skill.
469
+
470
+ ## Instructions
471
+
472
+ Provide detailed instructions for the AI agent here.
473
+
474
+ ## Examples
475
+
476
+ Show example inputs and expected outputs.
477
+ `;
478
+ await writeFile(join4(skillDir, "SKILL.md"), template, "utf-8");
479
+ console.log(pc8.green(`\u2713 Created skill template: ${skillDir}/SKILL.md`));
480
+ console.log(pc8.dim("\nNext steps:"));
481
+ console.log(pc8.dim(" 1. Edit SKILL.md with your instructions"));
482
+ console.log(pc8.dim(" 2. Validate: caamp skills validate " + join4(skillDir, "SKILL.md")));
483
+ console.log(pc8.dim(" 3. Install: caamp skills install " + skillDir));
484
+ });
485
+ }
486
+
487
+ // src/commands/skills/audit.ts
488
+ import pc9 from "picocolors";
489
+ import { existsSync as existsSync2, statSync } from "fs";
490
+ function registerSkillsAudit(parent) {
491
+ parent.command("audit").description("Security scan skill files (46+ rules, SARIF output)").argument("[path]", "Path to SKILL.md or directory", ".").option("--sarif", "Output in SARIF format").option("--json", "Output as JSON").action(async (path, opts) => {
492
+ if (!existsSync2(path)) {
493
+ console.error(pc9.red(`Path not found: ${path}`));
494
+ process.exit(1);
495
+ }
496
+ const stat = statSync(path);
497
+ let results;
498
+ if (stat.isFile()) {
499
+ results = [await scanFile(path)];
500
+ } else {
501
+ results = await scanDirectory(path);
502
+ }
503
+ if (results.length === 0) {
504
+ console.log(pc9.dim("No SKILL.md files found to scan."));
505
+ return;
506
+ }
507
+ if (opts.sarif) {
508
+ console.log(JSON.stringify(toSarif(results), null, 2));
509
+ return;
510
+ }
511
+ if (opts.json) {
512
+ console.log(JSON.stringify(results, null, 2));
513
+ return;
514
+ }
515
+ let totalFindings = 0;
516
+ let allPassed = true;
517
+ for (const result of results) {
518
+ const icon = result.passed ? pc9.green("\u2713") : pc9.red("\u2717");
519
+ console.log(`
520
+ ${icon} ${pc9.bold(result.file)} (score: ${result.score}/100)`);
521
+ if (result.findings.length === 0) {
522
+ console.log(pc9.dim(" No issues found."));
523
+ continue;
524
+ }
525
+ totalFindings += result.findings.length;
526
+ if (!result.passed) allPassed = false;
527
+ for (const f of result.findings) {
528
+ const sev = f.rule.severity === "critical" ? pc9.red(f.rule.severity) : f.rule.severity === "high" ? pc9.red(f.rule.severity) : f.rule.severity === "medium" ? pc9.yellow(f.rule.severity) : pc9.dim(f.rule.severity);
529
+ console.log(` ${sev.padEnd(20)} ${f.rule.id} ${f.rule.name}`);
530
+ console.log(` ${pc9.dim(`L${f.line}: ${f.context.slice(0, 80)}`)}`);
531
+ }
532
+ }
533
+ console.log(pc9.bold(`
534
+ ${results.length} file(s) scanned, ${totalFindings} finding(s)`));
535
+ if (!allPassed) {
536
+ process.exit(1);
537
+ }
538
+ });
539
+ }
540
+
541
+ // src/commands/skills/validate.ts
542
+ import pc10 from "picocolors";
543
+ function registerSkillsValidate(parent) {
544
+ parent.command("validate").description("Validate SKILL.md format").argument("[path]", "Path to SKILL.md", "SKILL.md").option("--json", "Output as JSON").action(async (path, opts) => {
545
+ const result = await validateSkill(path);
546
+ if (opts.json) {
547
+ console.log(JSON.stringify(result, null, 2));
548
+ return;
549
+ }
550
+ if (result.valid) {
551
+ console.log(pc10.green(`\u2713 ${path} is valid`));
552
+ } else {
553
+ console.log(pc10.red(`\u2717 ${path} has validation errors`));
554
+ }
555
+ for (const issue of result.issues) {
556
+ const icon = issue.level === "error" ? pc10.red("\u2717") : pc10.yellow("!");
557
+ console.log(` ${icon} [${issue.field}] ${issue.message}`);
558
+ }
559
+ if (!result.valid) {
560
+ process.exit(1);
561
+ }
562
+ });
563
+ }
564
+
565
+ // src/commands/skills/index.ts
566
+ function registerSkillsCommands(program2) {
567
+ const skills = program2.command("skills").description("Manage AI agent skills");
568
+ registerSkillsInstall(skills);
569
+ registerSkillsRemove(skills);
570
+ registerSkillsList(skills);
571
+ registerSkillsFind(skills);
572
+ registerSkillsCheck(skills);
573
+ registerSkillsUpdate(skills);
574
+ registerSkillsInit(skills);
575
+ registerSkillsAudit(skills);
576
+ registerSkillsValidate(skills);
577
+ }
578
+
579
+ // src/commands/mcp/install.ts
580
+ import pc11 from "picocolors";
581
+ function registerMcpInstall(parent) {
582
+ parent.command("install").description("Install MCP server to agent configs").argument("<source>", "MCP server source (URL, npm package, or command)").option("-a, --agent <name>", "Target specific agent(s)", (v, prev) => [...prev, v], []).option("-g, --global", "Install to global/user config").option("-n, --name <name>", "Override inferred server name").option("-t, --transport <type>", "Transport type: http (default) or sse", "http").option("--header <header>", "HTTP header (Key: Value)", (v, prev) => [...prev, v], []).option("-y, --yes", "Skip confirmation").option("--all", "Install to all detected agents").option("--dry-run", "Preview without writing").action(async (source, opts) => {
583
+ const parsed = parseSource(source);
584
+ const serverName = opts.name ?? parsed.inferredName;
585
+ const headers = {};
586
+ for (const h of opts.header) {
587
+ const idx = h.indexOf(":");
588
+ if (idx > 0) {
589
+ headers[h.slice(0, idx).trim()] = h.slice(idx + 1).trim();
590
+ }
591
+ }
592
+ const config = buildServerConfig(parsed, opts.transport, headers);
593
+ let providers;
594
+ if (opts.all) {
595
+ providers = getInstalledProviders();
596
+ } else if (opts.agent.length > 0) {
597
+ providers = opts.agent.map((a) => getProvider(a)).filter((p) => p !== void 0);
598
+ } else {
599
+ providers = getInstalledProviders();
600
+ }
601
+ if (providers.length === 0) {
602
+ console.error(pc11.red("No target providers found."));
603
+ process.exit(1);
604
+ }
605
+ const scope = opts.global ? "global" : "project";
606
+ if (opts.dryRun) {
607
+ console.log(pc11.bold("Dry run - would install:"));
608
+ console.log(` Server: ${pc11.bold(serverName)}`);
609
+ console.log(` Config: ${JSON.stringify(config, null, 2)}`);
610
+ console.log(` Scope: ${scope}`);
611
+ console.log(` Providers: ${providers.map((p) => p.id).join(", ")}`);
612
+ return;
613
+ }
614
+ console.log(pc11.dim(`Installing "${serverName}" to ${providers.length} provider(s)...
615
+ `));
616
+ const results = await installMcpServerToAll(
617
+ providers,
618
+ serverName,
619
+ config,
620
+ scope
621
+ );
622
+ for (const r of results) {
623
+ if (r.success) {
624
+ console.log(` ${pc11.green("\u2713")} ${r.provider.toolName.padEnd(22)} ${pc11.dim(r.configPath)}`);
625
+ } else {
626
+ console.log(` ${pc11.red("\u2717")} ${r.provider.toolName.padEnd(22)} ${pc11.red(r.error ?? "failed")}`);
627
+ }
628
+ }
629
+ const succeeded = results.filter((r) => r.success);
630
+ if (succeeded.length > 0) {
631
+ await recordMcpInstall(
632
+ serverName,
633
+ source,
634
+ parsed.type,
635
+ succeeded.map((r) => r.provider.id),
636
+ opts.global ?? false
637
+ );
638
+ }
639
+ console.log(pc11.bold(`
640
+ ${succeeded.length}/${results.length} providers configured.`));
641
+ });
642
+ }
643
+
644
+ // src/commands/mcp/remove.ts
645
+ import pc12 from "picocolors";
646
+ function registerMcpRemove(parent) {
647
+ parent.command("remove").description("Remove MCP server from agent configs").argument("<name>", "MCP server name to remove").option("-a, --agent <name>", "Target specific agent(s)", (v, prev) => [...prev, v], []).option("-g, --global", "Remove from global config").option("--all", "Remove from all detected agents").action(async (name, opts) => {
648
+ let providers;
649
+ if (opts.all) {
650
+ providers = getInstalledProviders();
651
+ } else if (opts.agent.length > 0) {
652
+ providers = opts.agent.map((a) => getProvider(a)).filter((p) => p !== void 0);
653
+ } else {
654
+ providers = getInstalledProviders();
655
+ }
656
+ const scope = opts.global ? "global" : "project";
657
+ let removed = 0;
658
+ for (const provider of providers) {
659
+ const success = await removeMcpServer(provider, name, scope);
660
+ if (success) {
661
+ console.log(` ${pc12.green("\u2713")} Removed from ${provider.toolName}`);
662
+ removed++;
663
+ }
664
+ }
665
+ if (removed > 0) {
666
+ await removeMcpFromLock(name);
667
+ console.log(pc12.green(`
668
+ \u2713 Removed "${name}" from ${removed} provider(s).`));
669
+ } else {
670
+ console.log(pc12.yellow(`Server "${name}" not found in any provider config.`));
671
+ }
672
+ });
673
+ }
674
+
675
+ // src/commands/mcp/list.ts
676
+ import pc13 from "picocolors";
677
+ function registerMcpList(parent) {
678
+ parent.command("list").description("List configured MCP servers").option("-a, --agent <name>", "List for specific agent").option("-g, --global", "List global config").option("--json", "Output as JSON").action(async (opts) => {
679
+ const providers = opts.agent ? [getProvider(opts.agent)].filter((p) => p !== void 0) : getInstalledProviders();
680
+ const allEntries = [];
681
+ for (const provider of providers) {
682
+ const scope = opts.global ? "global" : provider.configPathProject ? "project" : "global";
683
+ const entries = await listMcpServers(provider, scope);
684
+ allEntries.push(...entries);
685
+ }
686
+ if (opts.json) {
687
+ console.log(JSON.stringify(allEntries.map((e) => ({
688
+ provider: e.providerId,
689
+ name: e.name,
690
+ config: e.config
691
+ })), null, 2));
692
+ return;
693
+ }
694
+ if (allEntries.length === 0) {
695
+ console.log(pc13.dim("No MCP servers configured."));
696
+ return;
697
+ }
698
+ console.log(pc13.bold(`
699
+ ${allEntries.length} MCP server(s) configured:
700
+ `));
701
+ for (const entry of allEntries) {
702
+ console.log(` ${pc13.bold(entry.name.padEnd(25))} ${pc13.dim(entry.providerId)}`);
703
+ }
704
+ console.log();
705
+ });
706
+ }
707
+
708
+ // src/commands/mcp/detect.ts
709
+ import pc14 from "picocolors";
710
+ import { existsSync as existsSync3 } from "fs";
711
+ function registerMcpDetect(parent) {
712
+ parent.command("detect").description("Auto-detect installed MCP tools and their configurations").option("--json", "Output as JSON").action(async (opts) => {
713
+ const providers = getInstalledProviders();
714
+ const detected = [];
715
+ for (const provider of providers) {
716
+ const globalPath = resolveConfigPath(provider, "global");
717
+ const projectPath = resolveConfigPath(provider, "project");
718
+ const globalEntries = await listMcpServers(provider, "global");
719
+ const projectEntries = await listMcpServers(provider, "project");
720
+ detected.push({
721
+ provider: provider.id,
722
+ hasGlobalConfig: globalPath !== null && existsSync3(globalPath),
723
+ hasProjectConfig: projectPath !== null && existsSync3(projectPath),
724
+ globalServers: globalEntries.map((e) => e.name),
725
+ projectServers: projectEntries.map((e) => e.name)
726
+ });
727
+ }
728
+ if (opts.json) {
729
+ console.log(JSON.stringify(detected, null, 2));
730
+ return;
731
+ }
732
+ console.log(pc14.bold(`
733
+ ${detected.length} provider(s) with MCP support:
734
+ `));
735
+ for (const d of detected) {
736
+ const globalIcon = d.hasGlobalConfig ? pc14.green("G") : pc14.dim("-");
737
+ const projectIcon = d.hasProjectConfig ? pc14.green("P") : pc14.dim("-");
738
+ const servers = [...d.globalServers, ...d.projectServers];
739
+ const serverList = servers.length > 0 ? pc14.dim(servers.join(", ")) : pc14.dim("no servers");
740
+ console.log(` [${globalIcon}${projectIcon}] ${pc14.bold(d.provider.padEnd(20))} ${serverList}`);
741
+ }
742
+ console.log(pc14.dim("\nG = global config, P = project config"));
743
+ console.log();
744
+ });
745
+ }
746
+
747
+ // src/commands/mcp/index.ts
748
+ function registerMcpCommands(program2) {
749
+ const mcp = program2.command("mcp").description("Manage MCP server configurations");
750
+ registerMcpInstall(mcp);
751
+ registerMcpRemove(mcp);
752
+ registerMcpList(mcp);
753
+ registerMcpDetect(mcp);
754
+ }
755
+
756
+ // src/commands/instructions/inject.ts
757
+ import pc15 from "picocolors";
758
+ function registerInstructionsInject(parent) {
759
+ parent.command("inject").description("Inject instruction blocks into all provider files").option("-a, --agent <name>", "Target specific agent(s)", (v, prev) => [...prev, v], []).option("-g, --global", "Inject into global instruction files").option("--content <text>", "Custom content to inject").option("--dry-run", "Preview without writing").option("--all", "Target all known providers").action(async (opts) => {
760
+ let providers;
761
+ if (opts.all) {
762
+ providers = getAllProviders();
763
+ } else if (opts.agent.length > 0) {
764
+ providers = opts.agent.map((a) => getProvider(a)).filter((p) => p !== void 0);
765
+ } else {
766
+ providers = getInstalledProviders();
767
+ }
768
+ if (providers.length === 0) {
769
+ console.error(pc15.red("No providers found."));
770
+ process.exit(1);
771
+ }
772
+ const content = opts.content ?? generateInjectionContent();
773
+ const scope = opts.global ? "global" : "project";
774
+ const groups = groupByInstructFile(providers);
775
+ if (opts.dryRun) {
776
+ console.log(pc15.bold("Dry run - would inject into:\n"));
777
+ for (const [file, group] of groups) {
778
+ console.log(` ${pc15.bold(file)}: ${group.map((p) => p.id).join(", ")}`);
779
+ }
780
+ console.log(pc15.dim(`
781
+ Scope: ${scope}`));
782
+ console.log(pc15.dim(` Content length: ${content.length} chars`));
783
+ return;
784
+ }
785
+ const results = await injectAll(providers, process.cwd(), scope, content);
786
+ for (const [file, action] of results) {
787
+ const icon = action === "created" ? pc15.green("+") : action === "updated" ? pc15.yellow("~") : pc15.blue("^");
788
+ console.log(` ${icon} ${file} (${action})`);
789
+ }
790
+ console.log(pc15.bold(`
791
+ ${results.size} file(s) processed.`));
792
+ });
793
+ }
794
+
795
+ // src/commands/instructions/check.ts
796
+ import pc16 from "picocolors";
797
+ function registerInstructionsCheck(parent) {
798
+ parent.command("check").description("Check injection status across providers").option("-a, --agent <name>", "Check specific agent(s)", (v, prev) => [...prev, v], []).option("-g, --global", "Check global instruction files").option("--json", "Output as JSON").option("--all", "Check all known providers").action(async (opts) => {
799
+ let providers;
800
+ if (opts.all) {
801
+ providers = getAllProviders();
802
+ } else if (opts.agent.length > 0) {
803
+ providers = opts.agent.map((a) => getProvider(a)).filter((p) => p !== void 0);
804
+ } else {
805
+ providers = getInstalledProviders();
806
+ }
807
+ const scope = opts.global ? "global" : "project";
808
+ const results = await checkAllInjections(providers, process.cwd(), scope);
809
+ if (opts.json) {
810
+ console.log(JSON.stringify(results, null, 2));
811
+ return;
812
+ }
813
+ console.log(pc16.bold(`
814
+ Instruction file status (${scope}):
815
+ `));
816
+ for (const r of results) {
817
+ let icon;
818
+ let label;
819
+ switch (r.status) {
820
+ case "current":
821
+ icon = pc16.green("\u2713");
822
+ label = "current";
823
+ break;
824
+ case "outdated":
825
+ icon = pc16.yellow("~");
826
+ label = "outdated";
827
+ break;
828
+ case "missing":
829
+ icon = pc16.red("\u2717");
830
+ label = "missing";
831
+ break;
832
+ case "none":
833
+ icon = pc16.dim("-");
834
+ label = "no injection";
835
+ break;
836
+ }
837
+ console.log(` ${icon} ${r.file.padEnd(40)} ${label}`);
838
+ }
839
+ console.log();
840
+ });
841
+ }
842
+
843
+ // src/commands/instructions/update.ts
844
+ import pc17 from "picocolors";
845
+ function registerInstructionsUpdate(parent) {
846
+ parent.command("update").description("Update all instruction file injections").option("-g, --global", "Update global instruction files").option("-y, --yes", "Skip confirmation").action(async (opts) => {
847
+ const providers = getInstalledProviders();
848
+ const scope = opts.global ? "global" : "project";
849
+ const content = generateInjectionContent();
850
+ const checks = await checkAllInjections(providers, process.cwd(), scope, content);
851
+ const needsUpdate = checks.filter((c) => c.status !== "current");
852
+ if (needsUpdate.length === 0) {
853
+ console.log(pc17.green("All instruction files are up to date."));
854
+ return;
855
+ }
856
+ console.log(pc17.bold(`${needsUpdate.length} file(s) need updating:
857
+ `));
858
+ for (const c of needsUpdate) {
859
+ console.log(` ${c.file} (${c.status})`);
860
+ }
861
+ const providerIds = new Set(needsUpdate.map((c) => c.provider));
862
+ const toUpdate = providers.filter((p) => providerIds.has(p.id));
863
+ const results = await injectAll(toUpdate, process.cwd(), scope, content);
864
+ console.log();
865
+ for (const [file, action] of results) {
866
+ console.log(` ${pc17.green("\u2713")} ${file} (${action})`);
867
+ }
868
+ console.log(pc17.bold(`
869
+ ${results.size} file(s) updated.`));
870
+ });
871
+ }
872
+
873
+ // src/commands/instructions/index.ts
874
+ function registerInstructionsCommands(program2) {
875
+ const instructions = program2.command("instructions").description("Manage instruction file injections");
876
+ registerInstructionsInject(instructions);
877
+ registerInstructionsCheck(instructions);
878
+ registerInstructionsUpdate(instructions);
879
+ }
880
+
881
+ // src/commands/config.ts
882
+ import pc18 from "picocolors";
883
+ import { join as join5 } from "path";
884
+ import { existsSync as existsSync4 } from "fs";
885
+ function registerConfigCommand(program2) {
886
+ const config = program2.command("config").description("View provider configuration");
887
+ config.command("show").description("Show provider configuration").argument("<provider>", "Provider ID or alias").option("-g, --global", "Show global config").option("--json", "Output as JSON").action(async (providerId, opts) => {
888
+ const provider = getProvider(providerId);
889
+ if (!provider) {
890
+ console.error(pc18.red(`Provider not found: ${providerId}`));
891
+ process.exit(1);
892
+ }
893
+ const configPath = opts.global ? provider.configPathGlobal : provider.configPathProject ? join5(process.cwd(), provider.configPathProject) : provider.configPathGlobal;
894
+ if (!existsSync4(configPath)) {
895
+ console.log(pc18.dim(`No config file at: ${configPath}`));
896
+ return;
897
+ }
898
+ try {
899
+ const data = await readConfig(configPath, provider.configFormat);
900
+ if (opts.json) {
901
+ console.log(JSON.stringify(data, null, 2));
902
+ } else {
903
+ console.log(pc18.bold(`
904
+ ${provider.toolName} config (${configPath}):
905
+ `));
906
+ console.log(JSON.stringify(data, null, 2));
907
+ }
908
+ } catch (err) {
909
+ console.error(pc18.red(`Error reading config: ${err instanceof Error ? err.message : String(err)}`));
910
+ process.exit(1);
911
+ }
912
+ });
913
+ config.command("path").description("Show config file path").argument("<provider>", "Provider ID or alias").argument("[scope]", "Scope: project (default) or global", "project").action((providerId, scope) => {
914
+ const provider = getProvider(providerId);
915
+ if (!provider) {
916
+ console.error(pc18.red(`Provider not found: ${providerId}`));
917
+ process.exit(1);
918
+ }
919
+ if (scope === "global") {
920
+ console.log(provider.configPathGlobal);
921
+ } else {
922
+ if (provider.configPathProject) {
923
+ console.log(join5(process.cwd(), provider.configPathProject));
924
+ } else {
925
+ console.log(pc18.dim(`${provider.toolName} has no project-level config`));
926
+ console.log(provider.configPathGlobal);
927
+ }
928
+ }
929
+ });
930
+ }
931
+
932
+ // src/cli.ts
933
+ var program = new Command();
934
+ program.name("caamp").description("Central AI Agent Managed Packages - unified provider registry and package manager").version("0.1.0");
935
+ registerProvidersCommand(program);
936
+ registerSkillsCommands(program);
937
+ registerMcpCommands(program);
938
+ registerInstructionsCommands(program);
939
+ registerConfigCommand(program);
940
+ program.parse();
941
+ //# sourceMappingURL=cli.js.map