@cleocode/caamp 0.1.0 → 0.3.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/README.md +152 -32
- package/dist/{chunk-63BH7QMR.js → chunk-PCWTRJV2.js} +201 -90
- package/dist/chunk-PCWTRJV2.js.map +1 -0
- package/dist/cli.d.ts +0 -0
- package/dist/cli.js +405 -11
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1491 -60
- package/dist/index.js +9 -1
- package/dist/index.js.map +0 -0
- package/package.json +16 -13
- package/providers/registry.json +455 -5
- package/dist/chunk-63BH7QMR.js.map +0 -1
|
@@ -140,13 +140,33 @@ function getRegistryVersion() {
|
|
|
140
140
|
return loadRegistry().version;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
// src/core/logger.ts
|
|
144
|
+
var verboseMode = false;
|
|
145
|
+
var quietMode = false;
|
|
146
|
+
function setVerbose(v) {
|
|
147
|
+
verboseMode = v;
|
|
148
|
+
}
|
|
149
|
+
function setQuiet(q) {
|
|
150
|
+
quietMode = q;
|
|
151
|
+
}
|
|
152
|
+
function debug(...args) {
|
|
153
|
+
if (verboseMode) console.error("[debug]", ...args);
|
|
154
|
+
}
|
|
155
|
+
function isVerbose() {
|
|
156
|
+
return verboseMode;
|
|
157
|
+
}
|
|
158
|
+
function isQuiet() {
|
|
159
|
+
return quietMode;
|
|
160
|
+
}
|
|
161
|
+
|
|
143
162
|
// src/core/registry/detection.ts
|
|
144
163
|
import { existsSync as existsSync2 } from "fs";
|
|
145
164
|
import { execFileSync } from "child_process";
|
|
146
165
|
import { join as join2 } from "path";
|
|
147
166
|
function checkBinary(binary) {
|
|
148
167
|
try {
|
|
149
|
-
|
|
168
|
+
const cmd = process.platform === "win32" ? "where" : "which";
|
|
169
|
+
execFileSync(cmd, [binary], { stdio: "pipe" });
|
|
150
170
|
return true;
|
|
151
171
|
} catch {
|
|
152
172
|
return false;
|
|
@@ -171,10 +191,12 @@ function checkFlatpak(flatpakId) {
|
|
|
171
191
|
function detectProvider(provider) {
|
|
172
192
|
const matchedMethods = [];
|
|
173
193
|
const detection = provider.detection;
|
|
194
|
+
debug(`detecting provider ${provider.id} via methods: ${detection.methods.join(", ")}`);
|
|
174
195
|
for (const method of detection.methods) {
|
|
175
196
|
switch (method) {
|
|
176
197
|
case "binary":
|
|
177
198
|
if (detection.binary && checkBinary(detection.binary)) {
|
|
199
|
+
debug(` ${provider.id}: binary "${detection.binary}" found`);
|
|
178
200
|
matchedMethods.push("binary");
|
|
179
201
|
}
|
|
180
202
|
break;
|
|
@@ -444,7 +466,7 @@ async function listCanonicalSkills() {
|
|
|
444
466
|
return entries.filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => e.name);
|
|
445
467
|
}
|
|
446
468
|
|
|
447
|
-
// src/core/
|
|
469
|
+
// src/core/lock-utils.ts
|
|
448
470
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
449
471
|
import { existsSync as existsSync4 } from "fs";
|
|
450
472
|
import { homedir as homedir3 } from "os";
|
|
@@ -466,54 +488,9 @@ async function writeLockFile(lock) {
|
|
|
466
488
|
await mkdir2(LOCK_DIR, { recursive: true });
|
|
467
489
|
await writeFile2(LOCK_FILE, JSON.stringify(lock, null, 2) + "\n", "utf-8");
|
|
468
490
|
}
|
|
469
|
-
async function recordMcpInstall(serverName, source, sourceType, agents, isGlobal) {
|
|
470
|
-
const lock = await readLockFile();
|
|
471
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
472
|
-
const existing = lock.mcpServers[serverName];
|
|
473
|
-
lock.mcpServers[serverName] = {
|
|
474
|
-
name: serverName,
|
|
475
|
-
scopedName: serverName,
|
|
476
|
-
source,
|
|
477
|
-
sourceType,
|
|
478
|
-
installedAt: existing?.installedAt ?? now,
|
|
479
|
-
updatedAt: now,
|
|
480
|
-
agents: [.../* @__PURE__ */ new Set([...existing?.agents ?? [], ...agents])],
|
|
481
|
-
canonicalPath: "",
|
|
482
|
-
isGlobal
|
|
483
|
-
};
|
|
484
|
-
await writeLockFile(lock);
|
|
485
|
-
}
|
|
486
|
-
async function removeMcpFromLock(serverName) {
|
|
487
|
-
const lock = await readLockFile();
|
|
488
|
-
if (!(serverName in lock.mcpServers)) return false;
|
|
489
|
-
delete lock.mcpServers[serverName];
|
|
490
|
-
await writeLockFile(lock);
|
|
491
|
-
return true;
|
|
492
|
-
}
|
|
493
|
-
async function getTrackedMcpServers() {
|
|
494
|
-
const lock = await readLockFile();
|
|
495
|
-
return lock.mcpServers;
|
|
496
|
-
}
|
|
497
|
-
async function saveLastSelectedAgents(agents) {
|
|
498
|
-
const lock = await readLockFile();
|
|
499
|
-
lock.lastSelectedAgents = agents;
|
|
500
|
-
await writeLockFile(lock);
|
|
501
|
-
}
|
|
502
|
-
async function getLastSelectedAgents() {
|
|
503
|
-
const lock = await readLockFile();
|
|
504
|
-
return lock.lastSelectedAgents;
|
|
505
|
-
}
|
|
506
491
|
|
|
507
492
|
// src/core/skills/lock.ts
|
|
508
|
-
import {
|
|
509
|
-
import { homedir as homedir4 } from "os";
|
|
510
|
-
import { join as join5 } from "path";
|
|
511
|
-
var LOCK_DIR2 = join5(homedir4(), ".agents");
|
|
512
|
-
var LOCK_FILE2 = join5(LOCK_DIR2, ".caamp-lock.json");
|
|
513
|
-
async function writeLockFile2(lock) {
|
|
514
|
-
await mkdir3(LOCK_DIR2, { recursive: true });
|
|
515
|
-
await writeFile3(LOCK_FILE2, JSON.stringify(lock, null, 2) + "\n", "utf-8");
|
|
516
|
-
}
|
|
493
|
+
import { simpleGit } from "simple-git";
|
|
517
494
|
async function recordSkillInstall(skillName, scopedName, source, sourceType, agents, canonicalPath, isGlobal, projectDir, version) {
|
|
518
495
|
const lock = await readLockFile();
|
|
519
496
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -531,28 +508,71 @@ async function recordSkillInstall(skillName, scopedName, source, sourceType, age
|
|
|
531
508
|
isGlobal,
|
|
532
509
|
projectDir
|
|
533
510
|
};
|
|
534
|
-
await
|
|
511
|
+
await writeLockFile(lock);
|
|
535
512
|
}
|
|
536
513
|
async function removeSkillFromLock(skillName) {
|
|
537
514
|
const lock = await readLockFile();
|
|
538
515
|
if (!(skillName in lock.skills)) return false;
|
|
539
516
|
delete lock.skills[skillName];
|
|
540
|
-
await
|
|
517
|
+
await writeLockFile(lock);
|
|
541
518
|
return true;
|
|
542
519
|
}
|
|
543
520
|
async function getTrackedSkills() {
|
|
544
521
|
const lock = await readLockFile();
|
|
545
522
|
return lock.skills;
|
|
546
523
|
}
|
|
524
|
+
async function fetchLatestSha(repoUrl, ref) {
|
|
525
|
+
try {
|
|
526
|
+
const git = simpleGit();
|
|
527
|
+
const target = ref ?? "HEAD";
|
|
528
|
+
const args = target === "HEAD" ? [repoUrl, "HEAD"] : ["--refs", repoUrl, target];
|
|
529
|
+
const result = await git.listRemote(args);
|
|
530
|
+
const firstLine = result.trim().split("\n")[0];
|
|
531
|
+
if (!firstLine) return null;
|
|
532
|
+
const sha = firstLine.split(" ")[0];
|
|
533
|
+
return sha ?? null;
|
|
534
|
+
} catch {
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
547
538
|
async function checkSkillUpdate(skillName) {
|
|
548
539
|
const lock = await readLockFile();
|
|
549
540
|
const entry = lock.skills[skillName];
|
|
550
541
|
if (!entry) {
|
|
551
|
-
return { hasUpdate: false };
|
|
542
|
+
return { hasUpdate: false, status: "unknown" };
|
|
543
|
+
}
|
|
544
|
+
if (entry.sourceType !== "github" && entry.sourceType !== "gitlab") {
|
|
545
|
+
return {
|
|
546
|
+
hasUpdate: false,
|
|
547
|
+
currentVersion: entry.version,
|
|
548
|
+
status: "unknown"
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
const parsed = parseSource(entry.source);
|
|
552
|
+
if (!parsed.owner || !parsed.repo) {
|
|
553
|
+
return {
|
|
554
|
+
hasUpdate: false,
|
|
555
|
+
currentVersion: entry.version,
|
|
556
|
+
status: "unknown"
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
const host = parsed.type === "gitlab" ? "gitlab.com" : "github.com";
|
|
560
|
+
const repoUrl = `https://${host}/${parsed.owner}/${parsed.repo}.git`;
|
|
561
|
+
const latestSha = await fetchLatestSha(repoUrl, parsed.ref);
|
|
562
|
+
if (!latestSha) {
|
|
563
|
+
return {
|
|
564
|
+
hasUpdate: false,
|
|
565
|
+
currentVersion: entry.version,
|
|
566
|
+
status: "unknown"
|
|
567
|
+
};
|
|
552
568
|
}
|
|
569
|
+
const currentVersion = entry.version;
|
|
570
|
+
const hasUpdate = !currentVersion || !latestSha.startsWith(currentVersion.slice(0, 7));
|
|
553
571
|
return {
|
|
554
|
-
hasUpdate
|
|
555
|
-
currentVersion:
|
|
572
|
+
hasUpdate,
|
|
573
|
+
currentVersion: currentVersion ?? "unknown",
|
|
574
|
+
latestVersion: latestSha.slice(0, 12),
|
|
575
|
+
status: hasUpdate ? "update-available" : "up-to-date"
|
|
556
576
|
};
|
|
557
577
|
}
|
|
558
578
|
|
|
@@ -647,13 +667,42 @@ var SkillsShAdapter = class {
|
|
|
647
667
|
// src/core/marketplace/client.ts
|
|
648
668
|
var MarketplaceClient = class {
|
|
649
669
|
adapters;
|
|
670
|
+
/**
|
|
671
|
+
* Create a new marketplace client.
|
|
672
|
+
*
|
|
673
|
+
* @param adapters - Custom marketplace adapters (defaults to agentskills.in and skills.sh)
|
|
674
|
+
*
|
|
675
|
+
* @example
|
|
676
|
+
* ```typescript
|
|
677
|
+
* // Use default adapters
|
|
678
|
+
* const client = new MarketplaceClient();
|
|
679
|
+
*
|
|
680
|
+
* // Use custom adapters
|
|
681
|
+
* const client = new MarketplaceClient([myAdapter]);
|
|
682
|
+
* ```
|
|
683
|
+
*/
|
|
650
684
|
constructor(adapters) {
|
|
651
685
|
this.adapters = adapters ?? [
|
|
652
686
|
new SkillsMPAdapter(),
|
|
653
687
|
new SkillsShAdapter()
|
|
654
688
|
];
|
|
655
689
|
}
|
|
656
|
-
/**
|
|
690
|
+
/**
|
|
691
|
+
* Search all marketplaces and return deduplicated, sorted results.
|
|
692
|
+
*
|
|
693
|
+
* Queries all adapters in parallel and deduplicates by `scopedName`,
|
|
694
|
+
* keeping the entry with the highest star count. Results are sorted by
|
|
695
|
+
* stars descending.
|
|
696
|
+
*
|
|
697
|
+
* @param query - Search query string
|
|
698
|
+
* @param limit - Maximum number of results to return (default: 20)
|
|
699
|
+
* @returns Deduplicated and sorted marketplace results
|
|
700
|
+
*
|
|
701
|
+
* @example
|
|
702
|
+
* ```typescript
|
|
703
|
+
* const results = await client.search("code review", 10);
|
|
704
|
+
* ```
|
|
705
|
+
*/
|
|
657
706
|
async search(query, limit = 20) {
|
|
658
707
|
const promises = this.adapters.map(
|
|
659
708
|
(adapter) => adapter.search(query, limit).catch(() => [])
|
|
@@ -671,7 +720,19 @@ var MarketplaceClient = class {
|
|
|
671
720
|
deduplicated.sort((a, b) => b.stars - a.stars);
|
|
672
721
|
return deduplicated.slice(0, limit);
|
|
673
722
|
}
|
|
674
|
-
/**
|
|
723
|
+
/**
|
|
724
|
+
* Get a specific skill by its scoped name from any marketplace.
|
|
725
|
+
*
|
|
726
|
+
* Tries each adapter in order and returns the first match.
|
|
727
|
+
*
|
|
728
|
+
* @param scopedName - Scoped skill name (e.g. `"@author/my-skill"`)
|
|
729
|
+
* @returns The marketplace result, or `null` if not found in any marketplace
|
|
730
|
+
*
|
|
731
|
+
* @example
|
|
732
|
+
* ```typescript
|
|
733
|
+
* const skill = await client.getSkill("@anthropic/memory");
|
|
734
|
+
* ```
|
|
735
|
+
*/
|
|
675
736
|
async getSkill(scopedName) {
|
|
676
737
|
for (const adapter of this.adapters) {
|
|
677
738
|
const result = await adapter.getSkill(scopedName).catch(() => null);
|
|
@@ -684,7 +745,7 @@ var MarketplaceClient = class {
|
|
|
684
745
|
// src/core/skills/discovery.ts
|
|
685
746
|
import { readFile as readFile3, readdir } from "fs/promises";
|
|
686
747
|
import { existsSync as existsSync5 } from "fs";
|
|
687
|
-
import { join as
|
|
748
|
+
import { join as join5 } from "path";
|
|
688
749
|
import matter from "gray-matter";
|
|
689
750
|
async function parseSkillFile(filePath) {
|
|
690
751
|
try {
|
|
@@ -708,7 +769,7 @@ async function parseSkillFile(filePath) {
|
|
|
708
769
|
}
|
|
709
770
|
}
|
|
710
771
|
async function discoverSkill(skillDir) {
|
|
711
|
-
const skillFile =
|
|
772
|
+
const skillFile = join5(skillDir, "SKILL.md");
|
|
712
773
|
if (!existsSync5(skillFile)) return null;
|
|
713
774
|
const metadata = await parseSkillFile(skillFile);
|
|
714
775
|
if (!metadata) return null;
|
|
@@ -725,7 +786,7 @@ async function discoverSkills(rootDir) {
|
|
|
725
786
|
const skills = [];
|
|
726
787
|
for (const entry of entries) {
|
|
727
788
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
728
|
-
const skillDir =
|
|
789
|
+
const skillDir = join5(rootDir, entry.name);
|
|
729
790
|
const skill = await discoverSkill(skillDir);
|
|
730
791
|
if (skill) {
|
|
731
792
|
skills.push(skill);
|
|
@@ -1161,13 +1222,13 @@ async function scanFile(filePath, rules) {
|
|
|
1161
1222
|
}
|
|
1162
1223
|
async function scanDirectory(dirPath) {
|
|
1163
1224
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1164
|
-
const { join:
|
|
1225
|
+
const { join: join8 } = await import("path");
|
|
1165
1226
|
if (!existsSync6(dirPath)) return [];
|
|
1166
1227
|
const entries = await readdir2(dirPath, { withFileTypes: true });
|
|
1167
1228
|
const results = [];
|
|
1168
1229
|
for (const entry of entries) {
|
|
1169
1230
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
1170
|
-
const skillFile =
|
|
1231
|
+
const skillFile = join8(dirPath, entry.name, "SKILL.md");
|
|
1171
1232
|
if (existsSync6(skillFile)) {
|
|
1172
1233
|
results.push(await scanFile(skillFile));
|
|
1173
1234
|
}
|
|
@@ -1381,13 +1442,13 @@ function getNestedValue(obj, keyPath) {
|
|
|
1381
1442
|
return current;
|
|
1382
1443
|
}
|
|
1383
1444
|
async function ensureDir(filePath) {
|
|
1384
|
-
const { mkdir:
|
|
1385
|
-
const { dirname:
|
|
1386
|
-
await
|
|
1445
|
+
const { mkdir: mkdir4 } = await import("fs/promises");
|
|
1446
|
+
const { dirname: dirname4 } = await import("path");
|
|
1447
|
+
await mkdir4(dirname4(filePath), { recursive: true });
|
|
1387
1448
|
}
|
|
1388
1449
|
|
|
1389
1450
|
// src/core/formats/json.ts
|
|
1390
|
-
import { readFile as readFile6, writeFile as
|
|
1451
|
+
import { readFile as readFile6, writeFile as writeFile3 } from "fs/promises";
|
|
1391
1452
|
import { existsSync as existsSync8 } from "fs";
|
|
1392
1453
|
import * as jsonc from "jsonc-parser";
|
|
1393
1454
|
async function readJsonConfig(filePath) {
|
|
@@ -1441,7 +1502,7 @@ async function writeJsonConfig(filePath, configKey, serverName, serverConfig) {
|
|
|
1441
1502
|
if (!content.endsWith("\n")) {
|
|
1442
1503
|
content += "\n";
|
|
1443
1504
|
}
|
|
1444
|
-
await
|
|
1505
|
+
await writeFile3(filePath, content, "utf-8");
|
|
1445
1506
|
}
|
|
1446
1507
|
async function removeJsonConfig(filePath, configKey, serverName) {
|
|
1447
1508
|
if (!existsSync8(filePath)) return false;
|
|
@@ -1461,12 +1522,12 @@ async function removeJsonConfig(filePath, configKey, serverName) {
|
|
|
1461
1522
|
if (!content.endsWith("\n")) {
|
|
1462
1523
|
content += "\n";
|
|
1463
1524
|
}
|
|
1464
|
-
await
|
|
1525
|
+
await writeFile3(filePath, content, "utf-8");
|
|
1465
1526
|
return true;
|
|
1466
1527
|
}
|
|
1467
1528
|
|
|
1468
1529
|
// src/core/formats/yaml.ts
|
|
1469
|
-
import { readFile as readFile7, writeFile as
|
|
1530
|
+
import { readFile as readFile7, writeFile as writeFile4 } from "fs/promises";
|
|
1470
1531
|
import { existsSync as existsSync9 } from "fs";
|
|
1471
1532
|
import yaml from "js-yaml";
|
|
1472
1533
|
async function readYamlConfig(filePath) {
|
|
@@ -1491,7 +1552,7 @@ async function writeYamlConfig(filePath, configKey, serverName, serverConfig) {
|
|
|
1491
1552
|
noRefs: true,
|
|
1492
1553
|
sortKeys: false
|
|
1493
1554
|
});
|
|
1494
|
-
await
|
|
1555
|
+
await writeFile4(filePath, content, "utf-8");
|
|
1495
1556
|
}
|
|
1496
1557
|
async function removeYamlConfig(filePath, configKey, serverName) {
|
|
1497
1558
|
if (!existsSync9(filePath)) return false;
|
|
@@ -1511,12 +1572,12 @@ async function removeYamlConfig(filePath, configKey, serverName) {
|
|
|
1511
1572
|
noRefs: true,
|
|
1512
1573
|
sortKeys: false
|
|
1513
1574
|
});
|
|
1514
|
-
await
|
|
1575
|
+
await writeFile4(filePath, content, "utf-8");
|
|
1515
1576
|
return true;
|
|
1516
1577
|
}
|
|
1517
1578
|
|
|
1518
1579
|
// src/core/formats/toml.ts
|
|
1519
|
-
import { readFile as readFile8, writeFile as
|
|
1580
|
+
import { readFile as readFile8, writeFile as writeFile5 } from "fs/promises";
|
|
1520
1581
|
import { existsSync as existsSync10 } from "fs";
|
|
1521
1582
|
import TOML from "@iarna/toml";
|
|
1522
1583
|
async function readTomlConfig(filePath) {
|
|
@@ -1536,7 +1597,7 @@ async function writeTomlConfig(filePath, configKey, serverName, serverConfig) {
|
|
|
1536
1597
|
}
|
|
1537
1598
|
const merged = deepMerge(existing, newEntry);
|
|
1538
1599
|
const content = TOML.stringify(merged);
|
|
1539
|
-
await
|
|
1600
|
+
await writeFile5(filePath, content, "utf-8");
|
|
1540
1601
|
}
|
|
1541
1602
|
async function removeTomlConfig(filePath, configKey, serverName) {
|
|
1542
1603
|
if (!existsSync10(filePath)) return false;
|
|
@@ -1551,12 +1612,13 @@ async function removeTomlConfig(filePath, configKey, serverName) {
|
|
|
1551
1612
|
if (!(serverName in current)) return false;
|
|
1552
1613
|
delete current[serverName];
|
|
1553
1614
|
const content = TOML.stringify(existing);
|
|
1554
|
-
await
|
|
1615
|
+
await writeFile5(filePath, content, "utf-8");
|
|
1555
1616
|
return true;
|
|
1556
1617
|
}
|
|
1557
1618
|
|
|
1558
1619
|
// src/core/formats/index.ts
|
|
1559
1620
|
async function readConfig(filePath, format) {
|
|
1621
|
+
debug(`reading config: ${filePath} (format: ${format})`);
|
|
1560
1622
|
switch (format) {
|
|
1561
1623
|
case "json":
|
|
1562
1624
|
case "jsonc":
|
|
@@ -1570,6 +1632,7 @@ async function readConfig(filePath, format) {
|
|
|
1570
1632
|
}
|
|
1571
1633
|
}
|
|
1572
1634
|
async function writeConfig(filePath, format, key, serverName, serverConfig) {
|
|
1635
|
+
debug(`writing config: ${filePath} (format: ${format}, key: ${key}, server: ${serverName})`);
|
|
1573
1636
|
switch (format) {
|
|
1574
1637
|
case "json":
|
|
1575
1638
|
case "jsonc":
|
|
@@ -1693,17 +1756,18 @@ function getTransform(providerId) {
|
|
|
1693
1756
|
}
|
|
1694
1757
|
|
|
1695
1758
|
// src/core/mcp/reader.ts
|
|
1696
|
-
import { join as
|
|
1759
|
+
import { join as join6 } from "path";
|
|
1697
1760
|
import { existsSync as existsSync11 } from "fs";
|
|
1698
1761
|
function resolveConfigPath(provider, scope, projectDir) {
|
|
1699
1762
|
if (scope === "project") {
|
|
1700
1763
|
if (!provider.configPathProject) return null;
|
|
1701
|
-
return
|
|
1764
|
+
return join6(projectDir ?? process.cwd(), provider.configPathProject);
|
|
1702
1765
|
}
|
|
1703
1766
|
return provider.configPathGlobal;
|
|
1704
1767
|
}
|
|
1705
1768
|
async function listMcpServers(provider, scope, projectDir) {
|
|
1706
1769
|
const configPath = resolveConfigPath(provider, scope, projectDir);
|
|
1770
|
+
debug(`listing MCP servers for ${provider.id} (${scope}) at ${configPath ?? "(none)"}`);
|
|
1707
1771
|
if (!configPath || !existsSync11(configPath)) return [];
|
|
1708
1772
|
try {
|
|
1709
1773
|
const config = await readConfig(configPath, provider.configFormat);
|
|
@@ -1753,6 +1817,8 @@ function buildConfig(provider, serverName, config) {
|
|
|
1753
1817
|
}
|
|
1754
1818
|
async function installMcpServer(provider, serverName, config, scope = "project", projectDir) {
|
|
1755
1819
|
const configPath = resolveConfigPath(provider, scope, projectDir);
|
|
1820
|
+
debug(`installing MCP server "${serverName}" for ${provider.id} (${scope})`);
|
|
1821
|
+
debug(` config path: ${configPath ?? "(none)"}`);
|
|
1756
1822
|
if (!configPath) {
|
|
1757
1823
|
return {
|
|
1758
1824
|
provider,
|
|
@@ -1764,6 +1830,8 @@ async function installMcpServer(provider, serverName, config, scope = "project",
|
|
|
1764
1830
|
}
|
|
1765
1831
|
try {
|
|
1766
1832
|
const transformedConfig = buildConfig(provider, serverName, config);
|
|
1833
|
+
const transform = getTransform(provider.id);
|
|
1834
|
+
debug(` transform applied: ${transform ? "yes" : "no"}`);
|
|
1767
1835
|
await writeConfig(
|
|
1768
1836
|
configPath,
|
|
1769
1837
|
provider.configFormat,
|
|
@@ -1816,11 +1884,50 @@ function buildServerConfig(source, transport, headers) {
|
|
|
1816
1884
|
};
|
|
1817
1885
|
}
|
|
1818
1886
|
|
|
1887
|
+
// src/core/mcp/lock.ts
|
|
1888
|
+
async function recordMcpInstall(serverName, source, sourceType, agents, isGlobal) {
|
|
1889
|
+
const lock = await readLockFile();
|
|
1890
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1891
|
+
const existing = lock.mcpServers[serverName];
|
|
1892
|
+
lock.mcpServers[serverName] = {
|
|
1893
|
+
name: serverName,
|
|
1894
|
+
scopedName: serverName,
|
|
1895
|
+
source,
|
|
1896
|
+
sourceType,
|
|
1897
|
+
installedAt: existing?.installedAt ?? now,
|
|
1898
|
+
updatedAt: now,
|
|
1899
|
+
agents: [.../* @__PURE__ */ new Set([...existing?.agents ?? [], ...agents])],
|
|
1900
|
+
canonicalPath: "",
|
|
1901
|
+
isGlobal
|
|
1902
|
+
};
|
|
1903
|
+
await writeLockFile(lock);
|
|
1904
|
+
}
|
|
1905
|
+
async function removeMcpFromLock(serverName) {
|
|
1906
|
+
const lock = await readLockFile();
|
|
1907
|
+
if (!(serverName in lock.mcpServers)) return false;
|
|
1908
|
+
delete lock.mcpServers[serverName];
|
|
1909
|
+
await writeLockFile(lock);
|
|
1910
|
+
return true;
|
|
1911
|
+
}
|
|
1912
|
+
async function getTrackedMcpServers() {
|
|
1913
|
+
const lock = await readLockFile();
|
|
1914
|
+
return lock.mcpServers;
|
|
1915
|
+
}
|
|
1916
|
+
async function saveLastSelectedAgents(agents) {
|
|
1917
|
+
const lock = await readLockFile();
|
|
1918
|
+
lock.lastSelectedAgents = agents;
|
|
1919
|
+
await writeLockFile(lock);
|
|
1920
|
+
}
|
|
1921
|
+
async function getLastSelectedAgents() {
|
|
1922
|
+
const lock = await readLockFile();
|
|
1923
|
+
return lock.lastSelectedAgents;
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1819
1926
|
// src/core/instructions/injector.ts
|
|
1820
|
-
import { readFile as readFile9, writeFile as
|
|
1927
|
+
import { readFile as readFile9, writeFile as writeFile6 } from "fs/promises";
|
|
1821
1928
|
import { existsSync as existsSync12 } from "fs";
|
|
1822
|
-
import { join as
|
|
1823
|
-
import { mkdir as
|
|
1929
|
+
import { join as join7, dirname as dirname3 } from "path";
|
|
1930
|
+
import { mkdir as mkdir3 } from "fs/promises";
|
|
1824
1931
|
var MARKER_START = "<!-- CAAMP:START -->";
|
|
1825
1932
|
var MARKER_END = "<!-- CAAMP:END -->";
|
|
1826
1933
|
var MARKER_PATTERN = /<!-- CAAMP:START -->[\s\S]*?<!-- CAAMP:END -->/;
|
|
@@ -1849,19 +1956,19 @@ ${MARKER_END}`;
|
|
|
1849
1956
|
}
|
|
1850
1957
|
async function inject(filePath, content) {
|
|
1851
1958
|
const block = buildBlock(content);
|
|
1852
|
-
await
|
|
1959
|
+
await mkdir3(dirname3(filePath), { recursive: true });
|
|
1853
1960
|
if (!existsSync12(filePath)) {
|
|
1854
|
-
await
|
|
1961
|
+
await writeFile6(filePath, block + "\n", "utf-8");
|
|
1855
1962
|
return "created";
|
|
1856
1963
|
}
|
|
1857
1964
|
const existing = await readFile9(filePath, "utf-8");
|
|
1858
1965
|
if (MARKER_PATTERN.test(existing)) {
|
|
1859
1966
|
const updated2 = existing.replace(MARKER_PATTERN, block);
|
|
1860
|
-
await
|
|
1967
|
+
await writeFile6(filePath, updated2, "utf-8");
|
|
1861
1968
|
return "updated";
|
|
1862
1969
|
}
|
|
1863
1970
|
const updated = block + "\n\n" + existing;
|
|
1864
|
-
await
|
|
1971
|
+
await writeFile6(filePath, updated, "utf-8");
|
|
1865
1972
|
return "added";
|
|
1866
1973
|
}
|
|
1867
1974
|
async function removeInjection(filePath) {
|
|
@@ -1873,7 +1980,7 @@ async function removeInjection(filePath) {
|
|
|
1873
1980
|
const { rm: rm2 } = await import("fs/promises");
|
|
1874
1981
|
await rm2(filePath);
|
|
1875
1982
|
} else {
|
|
1876
|
-
await
|
|
1983
|
+
await writeFile6(filePath, cleaned + "\n", "utf-8");
|
|
1877
1984
|
}
|
|
1878
1985
|
return true;
|
|
1879
1986
|
}
|
|
@@ -1881,7 +1988,7 @@ async function checkAllInjections(providers, projectDir, scope, expectedContent)
|
|
|
1881
1988
|
const results = [];
|
|
1882
1989
|
const checked = /* @__PURE__ */ new Set();
|
|
1883
1990
|
for (const provider of providers) {
|
|
1884
|
-
const filePath = scope === "global" ?
|
|
1991
|
+
const filePath = scope === "global" ? join7(provider.pathGlobal, provider.instructFile) : join7(projectDir, provider.instructFile);
|
|
1885
1992
|
if (checked.has(filePath)) continue;
|
|
1886
1993
|
checked.add(filePath);
|
|
1887
1994
|
const status = await checkInjection(filePath, expectedContent);
|
|
@@ -1898,7 +2005,7 @@ async function injectAll(providers, projectDir, scope, content) {
|
|
|
1898
2005
|
const results = /* @__PURE__ */ new Map();
|
|
1899
2006
|
const injected = /* @__PURE__ */ new Set();
|
|
1900
2007
|
for (const provider of providers) {
|
|
1901
|
-
const filePath = scope === "global" ?
|
|
2008
|
+
const filePath = scope === "global" ? join7(provider.pathGlobal, provider.instructFile) : join7(projectDir, provider.instructFile);
|
|
1902
2009
|
if (injected.has(filePath)) continue;
|
|
1903
2010
|
injected.add(filePath);
|
|
1904
2011
|
const action = await inject(filePath, content);
|
|
@@ -1945,6 +2052,10 @@ export {
|
|
|
1945
2052
|
getInstructionFiles,
|
|
1946
2053
|
getProviderCount,
|
|
1947
2054
|
getRegistryVersion,
|
|
2055
|
+
setVerbose,
|
|
2056
|
+
setQuiet,
|
|
2057
|
+
isVerbose,
|
|
2058
|
+
isQuiet,
|
|
1948
2059
|
detectProvider,
|
|
1949
2060
|
detectAllProviders,
|
|
1950
2061
|
getInstalledProviders,
|
|
@@ -1955,11 +2066,6 @@ export {
|
|
|
1955
2066
|
removeSkill,
|
|
1956
2067
|
listCanonicalSkills,
|
|
1957
2068
|
readLockFile,
|
|
1958
|
-
recordMcpInstall,
|
|
1959
|
-
removeMcpFromLock,
|
|
1960
|
-
getTrackedMcpServers,
|
|
1961
|
-
saveLastSelectedAgents,
|
|
1962
|
-
getLastSelectedAgents,
|
|
1963
2069
|
recordSkillInstall,
|
|
1964
2070
|
removeSkillFromLock,
|
|
1965
2071
|
getTrackedSkills,
|
|
@@ -1987,6 +2093,11 @@ export {
|
|
|
1987
2093
|
installMcpServer,
|
|
1988
2094
|
installMcpServerToAll,
|
|
1989
2095
|
buildServerConfig,
|
|
2096
|
+
recordMcpInstall,
|
|
2097
|
+
removeMcpFromLock,
|
|
2098
|
+
getTrackedMcpServers,
|
|
2099
|
+
saveLastSelectedAgents,
|
|
2100
|
+
getLastSelectedAgents,
|
|
1990
2101
|
checkInjection,
|
|
1991
2102
|
inject,
|
|
1992
2103
|
removeInjection,
|
|
@@ -1995,4 +2106,4 @@ export {
|
|
|
1995
2106
|
generateInjectionContent,
|
|
1996
2107
|
groupByInstructFile
|
|
1997
2108
|
};
|
|
1998
|
-
//# sourceMappingURL=chunk-
|
|
2109
|
+
//# sourceMappingURL=chunk-PCWTRJV2.js.map
|