@cleocode/caamp 1.1.3 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-7YV3KXEJ.js → chunk-GR47LTXR.js} +89 -22
- package/dist/chunk-GR47LTXR.js.map +1 -0
- package/dist/cli.js +877 -268
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +16 -6
- package/dist/index.js +3 -1
- package/package.json +1 -1
- package/dist/chunk-7YV3KXEJ.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -65,7 +65,7 @@ import {
|
|
|
65
65
|
tokenizeCriteriaValue,
|
|
66
66
|
updateInstructionsSingleOperation,
|
|
67
67
|
validateSkill
|
|
68
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-GR47LTXR.js";
|
|
69
69
|
|
|
70
70
|
// src/cli.ts
|
|
71
71
|
import { Command } from "commander";
|
|
@@ -1601,10 +1601,668 @@ function registerInstructionsCommands(program2) {
|
|
|
1601
1601
|
registerInstructionsUpdate(instructions);
|
|
1602
1602
|
}
|
|
1603
1603
|
|
|
1604
|
-
// src/commands/mcp/
|
|
1604
|
+
// src/commands/mcp/cleo.ts
|
|
1605
|
+
import { createInterface } from "readline/promises";
|
|
1605
1606
|
import pc6 from "picocolors";
|
|
1607
|
+
|
|
1608
|
+
// src/core/mcp/cleo.ts
|
|
1609
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
1610
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1611
|
+
import { homedir as homedir2 } from "os";
|
|
1612
|
+
import { isAbsolute, resolve } from "path";
|
|
1613
|
+
var CLEO_SERVER_NAMES = {
|
|
1614
|
+
stable: "cleo",
|
|
1615
|
+
beta: "cleo-beta",
|
|
1616
|
+
dev: "cleo-dev"
|
|
1617
|
+
};
|
|
1618
|
+
var CLEO_MCP_NPM_PACKAGE = "@cleocode/cleo";
|
|
1619
|
+
var CLEO_DEV_DIR_DEFAULT = "~/.cleo-dev";
|
|
1620
|
+
function normalizeCleoChannel(value) {
|
|
1621
|
+
if (!value || value.trim() === "") return "stable";
|
|
1622
|
+
const normalized = value.trim().toLowerCase();
|
|
1623
|
+
if (normalized === "stable" || normalized === "beta" || normalized === "dev") {
|
|
1624
|
+
return normalized;
|
|
1625
|
+
}
|
|
1626
|
+
throw new Error(`Invalid channel "${value}". Expected stable, beta, or dev.`);
|
|
1627
|
+
}
|
|
1628
|
+
function resolveCleoServerName(channel) {
|
|
1629
|
+
return CLEO_SERVER_NAMES[channel];
|
|
1630
|
+
}
|
|
1631
|
+
function resolveChannelFromServerName(serverName) {
|
|
1632
|
+
if (serverName === CLEO_SERVER_NAMES.stable) return "stable";
|
|
1633
|
+
if (serverName === CLEO_SERVER_NAMES.beta) return "beta";
|
|
1634
|
+
if (serverName === CLEO_SERVER_NAMES.dev) return "dev";
|
|
1635
|
+
return null;
|
|
1636
|
+
}
|
|
1637
|
+
function splitCommand(command, explicitArgs = []) {
|
|
1638
|
+
if (explicitArgs.length > 0) {
|
|
1639
|
+
return { command, args: explicitArgs };
|
|
1640
|
+
}
|
|
1641
|
+
const parts = command.trim().split(/\s+/);
|
|
1642
|
+
const binary = parts[0] ?? "";
|
|
1643
|
+
if (!binary) {
|
|
1644
|
+
throw new Error("Command is required for dev channel.");
|
|
1645
|
+
}
|
|
1646
|
+
return {
|
|
1647
|
+
command: binary,
|
|
1648
|
+
args: parts.slice(1)
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
function normalizeEnv(env, channel, cleoDir) {
|
|
1652
|
+
const result = { ...env ?? {} };
|
|
1653
|
+
if (channel === "dev" && !result.CLEO_DIR) {
|
|
1654
|
+
result.CLEO_DIR = cleoDir ?? CLEO_DEV_DIR_DEFAULT;
|
|
1655
|
+
}
|
|
1656
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
1657
|
+
}
|
|
1658
|
+
function resolvePackageSpec(channel, version) {
|
|
1659
|
+
const tag = version?.trim() || (channel === "stable" ? "latest" : "beta");
|
|
1660
|
+
return `${CLEO_MCP_NPM_PACKAGE}@${tag}`;
|
|
1661
|
+
}
|
|
1662
|
+
function buildCleoProfile(options) {
|
|
1663
|
+
const channel = options.channel;
|
|
1664
|
+
const serverName = resolveCleoServerName(channel);
|
|
1665
|
+
if (channel === "dev") {
|
|
1666
|
+
if (!options.command || options.command.trim() === "") {
|
|
1667
|
+
throw new Error("Dev channel requires --command.");
|
|
1668
|
+
}
|
|
1669
|
+
const parsed = splitCommand(options.command, options.args ?? []);
|
|
1670
|
+
const env = normalizeEnv(options.env, channel, options.cleoDir);
|
|
1671
|
+
return {
|
|
1672
|
+
channel,
|
|
1673
|
+
serverName,
|
|
1674
|
+
config: {
|
|
1675
|
+
command: parsed.command,
|
|
1676
|
+
args: parsed.args,
|
|
1677
|
+
...env ? { env } : {}
|
|
1678
|
+
}
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
const packageSpec = resolvePackageSpec(channel, options.version);
|
|
1682
|
+
return {
|
|
1683
|
+
channel,
|
|
1684
|
+
serverName,
|
|
1685
|
+
packageSpec,
|
|
1686
|
+
config: {
|
|
1687
|
+
command: "npx",
|
|
1688
|
+
args: ["-y", packageSpec, "cleo-mcp"]
|
|
1689
|
+
}
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
function expandHome(pathValue) {
|
|
1693
|
+
if (pathValue === "~") return homedir2();
|
|
1694
|
+
if (pathValue.startsWith("~/")) {
|
|
1695
|
+
return resolve(homedir2(), pathValue.slice(2));
|
|
1696
|
+
}
|
|
1697
|
+
return pathValue;
|
|
1698
|
+
}
|
|
1699
|
+
function checkCommandReachability(command) {
|
|
1700
|
+
const hasPathSeparator = command.includes("/") || command.includes("\\");
|
|
1701
|
+
if (hasPathSeparator || command.startsWith("~")) {
|
|
1702
|
+
const expanded = expandHome(command);
|
|
1703
|
+
const candidate = isAbsolute(expanded) ? expanded : resolve(process.cwd(), expanded);
|
|
1704
|
+
if (existsSync3(candidate)) {
|
|
1705
|
+
return { reachable: true, method: "path", detail: candidate };
|
|
1706
|
+
}
|
|
1707
|
+
return { reachable: false, method: "path", detail: candidate };
|
|
1708
|
+
}
|
|
1709
|
+
try {
|
|
1710
|
+
const lookup = process.platform === "win32" ? "where" : "which";
|
|
1711
|
+
execFileSync2(lookup, [command], { stdio: "pipe" });
|
|
1712
|
+
return { reachable: true, method: "lookup", detail: command };
|
|
1713
|
+
} catch {
|
|
1714
|
+
return { reachable: false, method: "lookup", detail: command };
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
function parseEnvAssignments(values) {
|
|
1718
|
+
const env = {};
|
|
1719
|
+
for (const value of values) {
|
|
1720
|
+
const idx = value.indexOf("=");
|
|
1721
|
+
if (idx <= 0) {
|
|
1722
|
+
throw new Error(`Invalid --env value "${value}". Use KEY=value.`);
|
|
1723
|
+
}
|
|
1724
|
+
const key = value.slice(0, idx).trim();
|
|
1725
|
+
const val = value.slice(idx + 1).trim();
|
|
1726
|
+
if (!key) {
|
|
1727
|
+
throw new Error(`Invalid --env value "${value}". Key cannot be empty.`);
|
|
1728
|
+
}
|
|
1729
|
+
env[key] = val;
|
|
1730
|
+
}
|
|
1731
|
+
return env;
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
// src/commands/mcp/cleo.ts
|
|
1735
|
+
function collect(value, previous) {
|
|
1736
|
+
return [...previous, value];
|
|
1737
|
+
}
|
|
1738
|
+
function collectTargetProviders(providerIds, all) {
|
|
1739
|
+
if (all) {
|
|
1740
|
+
return getInstalledProviders();
|
|
1741
|
+
}
|
|
1742
|
+
if (providerIds.length > 0) {
|
|
1743
|
+
return providerIds.map((id) => getProvider(id)).filter((provider) => provider !== void 0);
|
|
1744
|
+
}
|
|
1745
|
+
return getInstalledProviders();
|
|
1746
|
+
}
|
|
1747
|
+
async function validateProfile(provider, scope, serverName) {
|
|
1748
|
+
const entries = await listMcpServers(provider, scope);
|
|
1749
|
+
const entry = entries.find((candidate) => candidate.name === serverName);
|
|
1750
|
+
if (!entry) {
|
|
1751
|
+
return { valid: false, reason: "server missing after write" };
|
|
1752
|
+
}
|
|
1753
|
+
const command = typeof entry.config.command === "string" ? entry.config.command : void 0;
|
|
1754
|
+
if (!command) {
|
|
1755
|
+
return { valid: true };
|
|
1756
|
+
}
|
|
1757
|
+
const reachability = checkCommandReachability(command);
|
|
1758
|
+
if (!reachability.reachable) {
|
|
1759
|
+
return {
|
|
1760
|
+
valid: false,
|
|
1761
|
+
reason: `command not reachable (${reachability.method}: ${reachability.detail})`
|
|
1762
|
+
};
|
|
1763
|
+
}
|
|
1764
|
+
return { valid: true };
|
|
1765
|
+
}
|
|
1766
|
+
async function detectServerConflicts(providers, scope, targetServerName) {
|
|
1767
|
+
const warnings = [];
|
|
1768
|
+
for (const provider of providers) {
|
|
1769
|
+
const entries = await listMcpServers(provider, scope);
|
|
1770
|
+
const existing = entries.find((entry) => entry.name === targetServerName);
|
|
1771
|
+
if (!existing) continue;
|
|
1772
|
+
const command = typeof existing.config.command === "string" ? existing.config.command : "";
|
|
1773
|
+
const args = Array.isArray(existing.config.args) ? existing.config.args.filter((value) => typeof value === "string") : [];
|
|
1774
|
+
const flat = `${command} ${args.join(" ")}`.toLowerCase();
|
|
1775
|
+
if (!flat.includes("cleo")) {
|
|
1776
|
+
warnings.push({
|
|
1777
|
+
providerId: provider.id,
|
|
1778
|
+
message: `Server name '${targetServerName}' already exists with a non-CLEO command in ${provider.id}.`
|
|
1779
|
+
});
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
return warnings;
|
|
1783
|
+
}
|
|
1784
|
+
function formatInstallResultHuman(mode, channel, serverName, scope, results, validations) {
|
|
1785
|
+
console.log(pc6.bold(`${mode === "install" ? "Install" : "Update"} CLEO channel: ${channel}`));
|
|
1786
|
+
console.log(pc6.dim(`Server: ${serverName} Scope: ${scope}`));
|
|
1787
|
+
console.log();
|
|
1788
|
+
for (const result of results) {
|
|
1789
|
+
const validation = validations.find((entry) => entry.providerId === result.provider.id);
|
|
1790
|
+
if (result.success) {
|
|
1791
|
+
const validationLabel = validation?.valid ? pc6.green("validated") : pc6.yellow(`validation warning: ${validation?.reason ?? "unknown"}`);
|
|
1792
|
+
console.log(` ${pc6.green("+")} ${result.provider.toolName.padEnd(22)} ${pc6.dim(result.configPath)} ${validationLabel}`);
|
|
1793
|
+
} else {
|
|
1794
|
+
console.log(` ${pc6.red("x")} ${result.provider.toolName.padEnd(22)} ${pc6.red(result.error ?? "failed")}`);
|
|
1795
|
+
console.log(pc6.dim(" Recovery: verify config path permissions and retry with --dry-run."));
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
console.log();
|
|
1799
|
+
}
|
|
1800
|
+
async function runInteractiveInstall(opts) {
|
|
1801
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1802
|
+
try {
|
|
1803
|
+
const discovered = getInstalledProviders();
|
|
1804
|
+
if (discovered.length === 0) {
|
|
1805
|
+
throw new Error("No installed providers were detected for interactive setup.");
|
|
1806
|
+
}
|
|
1807
|
+
console.log(pc6.bold("CLEO MCP Setup"));
|
|
1808
|
+
console.log(pc6.dim("Step 1/6 - Select provider(s)"));
|
|
1809
|
+
for (const [index, provider] of discovered.entries()) {
|
|
1810
|
+
console.log(` ${index + 1}. ${provider.id} (${provider.toolName})`);
|
|
1811
|
+
}
|
|
1812
|
+
const providerAnswer = await rl.question(pc6.dim("Choose providers (e.g. 1,2 or all): "));
|
|
1813
|
+
const selectedProviders = providerAnswer.trim().toLowerCase() === "all" ? discovered.map((provider) => provider.id) : providerAnswer.split(",").map((part) => Number(part.trim())).filter((value) => Number.isFinite(value) && value > 0 && value <= discovered.length).map((index) => discovered[index - 1]?.id).filter((id) => Boolean(id));
|
|
1814
|
+
if (selectedProviders.length === 0) {
|
|
1815
|
+
throw new Error("No providers selected.");
|
|
1816
|
+
}
|
|
1817
|
+
console.log();
|
|
1818
|
+
console.log(pc6.dim("Step 2/6 - Select channel"));
|
|
1819
|
+
const channelAnswer = await rl.question(pc6.dim("Channel [stable/beta/dev] (stable): "));
|
|
1820
|
+
const selectedChannel = normalizeCleoChannel(channelAnswer || "stable");
|
|
1821
|
+
let command = opts.command;
|
|
1822
|
+
let args = [...opts.arg];
|
|
1823
|
+
let env = [...opts.env];
|
|
1824
|
+
let cleoDir = opts.cleoDir;
|
|
1825
|
+
if (selectedChannel === "dev") {
|
|
1826
|
+
command = await rl.question(pc6.dim("Dev command (required): "));
|
|
1827
|
+
const argsAnswer = await rl.question(pc6.dim("Dev args (space-separated, optional): "));
|
|
1828
|
+
args = argsAnswer.trim() === "" ? [] : argsAnswer.trim().split(/\s+/);
|
|
1829
|
+
const dirAnswer = await rl.question(pc6.dim("CLEO_DIR (~/.cleo-dev default): "));
|
|
1830
|
+
cleoDir = dirAnswer.trim() === "" ? "~/.cleo-dev" : dirAnswer.trim();
|
|
1831
|
+
if (cleoDir.trim() !== "") {
|
|
1832
|
+
env = [
|
|
1833
|
+
...env.filter((entry) => !entry.startsWith("CLEO_DIR=")),
|
|
1834
|
+
`CLEO_DIR=${cleoDir}`
|
|
1835
|
+
];
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
const profile = buildCleoProfile({
|
|
1839
|
+
channel: selectedChannel,
|
|
1840
|
+
version: opts.version,
|
|
1841
|
+
command,
|
|
1842
|
+
args,
|
|
1843
|
+
env: parseEnvAssignments(env),
|
|
1844
|
+
cleoDir
|
|
1845
|
+
});
|
|
1846
|
+
console.log();
|
|
1847
|
+
console.log(pc6.dim("Step 3/6 - Preview profile diff"));
|
|
1848
|
+
console.log(` Server: ${pc6.bold(profile.serverName)}`);
|
|
1849
|
+
console.log(` Channel: ${selectedChannel}`);
|
|
1850
|
+
console.log(` Config: ${JSON.stringify(profile.config)}`);
|
|
1851
|
+
console.log();
|
|
1852
|
+
console.log(pc6.dim("Step 4/6 - Confirm apply"));
|
|
1853
|
+
const confirm = await rl.question(pc6.dim("Apply this configuration? [y/N] "));
|
|
1854
|
+
if (!["y", "yes"].includes(confirm.trim().toLowerCase())) {
|
|
1855
|
+
throw new Error("Cancelled by user.");
|
|
1856
|
+
}
|
|
1857
|
+
return {
|
|
1858
|
+
...opts,
|
|
1859
|
+
provider: selectedProviders,
|
|
1860
|
+
channel: selectedChannel,
|
|
1861
|
+
command,
|
|
1862
|
+
arg: args,
|
|
1863
|
+
env,
|
|
1864
|
+
cleoDir,
|
|
1865
|
+
yes: true
|
|
1866
|
+
};
|
|
1867
|
+
} finally {
|
|
1868
|
+
rl.close();
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
async function executeCleoInstall(mode, opts, operation) {
|
|
1872
|
+
const mvi = "standard";
|
|
1873
|
+
let format;
|
|
1874
|
+
try {
|
|
1875
|
+
format = resolveFormat({
|
|
1876
|
+
jsonFlag: opts.json ?? false,
|
|
1877
|
+
humanFlag: (opts.human ?? false) || isHuman(),
|
|
1878
|
+
projectDefault: "json"
|
|
1879
|
+
});
|
|
1880
|
+
} catch (error) {
|
|
1881
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1882
|
+
emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
|
|
1883
|
+
process.exit(1);
|
|
1884
|
+
}
|
|
1885
|
+
const interactive = (opts.interactive ?? false) && format === "human";
|
|
1886
|
+
const resolvedOpts = interactive ? await runInteractiveInstall(opts) : opts;
|
|
1887
|
+
const channel = normalizeCleoChannel(resolvedOpts.channel);
|
|
1888
|
+
const providers = collectTargetProviders(resolvedOpts.provider, resolvedOpts.all);
|
|
1889
|
+
if (providers.length === 0) {
|
|
1890
|
+
const message = "No target providers found.";
|
|
1891
|
+
if (format === "json") {
|
|
1892
|
+
emitJsonError(operation, mvi, ErrorCodes.PROVIDER_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
|
|
1893
|
+
} else {
|
|
1894
|
+
console.error(pc6.red(message));
|
|
1895
|
+
}
|
|
1896
|
+
process.exit(1);
|
|
1897
|
+
}
|
|
1898
|
+
const envMap = parseEnvAssignments(resolvedOpts.env);
|
|
1899
|
+
const profile = buildCleoProfile({
|
|
1900
|
+
channel,
|
|
1901
|
+
version: resolvedOpts.version,
|
|
1902
|
+
command: resolvedOpts.command,
|
|
1903
|
+
args: resolvedOpts.arg,
|
|
1904
|
+
env: envMap,
|
|
1905
|
+
cleoDir: resolvedOpts.cleoDir
|
|
1906
|
+
});
|
|
1907
|
+
const scope = resolvedOpts.global ? "global" : "project";
|
|
1908
|
+
if (resolvedOpts.dryRun) {
|
|
1909
|
+
if (format === "human") {
|
|
1910
|
+
console.log(pc6.bold(`Dry run: ${mode} CLEO (${channel})`));
|
|
1911
|
+
console.log(pc6.dim(`Server: ${profile.serverName} Scope: ${scope}`));
|
|
1912
|
+
console.log(pc6.dim(`Providers: ${providers.map((provider) => provider.id).join(", ")}`));
|
|
1913
|
+
console.log(pc6.dim(`Command: ${profile.config.command ?? "(none)"} ${(profile.config.args ?? []).join(" ")}`));
|
|
1914
|
+
if (profile.config.env && Object.keys(profile.config.env).length > 0) {
|
|
1915
|
+
console.log(pc6.dim(`Env: ${JSON.stringify(profile.config.env)}`));
|
|
1916
|
+
}
|
|
1917
|
+
} else {
|
|
1918
|
+
outputSuccess(operation, mvi, {
|
|
1919
|
+
action: mode,
|
|
1920
|
+
channel,
|
|
1921
|
+
serverName: profile.serverName,
|
|
1922
|
+
providers: providers.map((provider) => provider.id),
|
|
1923
|
+
scope,
|
|
1924
|
+
command: profile.config.command,
|
|
1925
|
+
args: profile.config.args ?? [],
|
|
1926
|
+
env: profile.config.env ?? {},
|
|
1927
|
+
packageSpec: profile.packageSpec,
|
|
1928
|
+
dryRun: true
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
return;
|
|
1932
|
+
}
|
|
1933
|
+
const conflictWarnings = await detectServerConflicts(providers, scope, profile.serverName);
|
|
1934
|
+
if (format === "human" && conflictWarnings.length > 0) {
|
|
1935
|
+
console.log(pc6.yellow("Warning: potential server name conflicts detected."));
|
|
1936
|
+
for (const warning of conflictWarnings) {
|
|
1937
|
+
console.log(pc6.yellow(` - ${warning.message}`));
|
|
1938
|
+
}
|
|
1939
|
+
console.log(pc6.dim("Recovery: run with --dry-run, inspect provider config, then retry with explicit channel/profile."));
|
|
1940
|
+
console.log();
|
|
1941
|
+
}
|
|
1942
|
+
const results = await installMcpServerToAll(providers, profile.serverName, profile.config, scope);
|
|
1943
|
+
const succeeded = results.filter((result) => result.success);
|
|
1944
|
+
const validations = [];
|
|
1945
|
+
for (const result of succeeded) {
|
|
1946
|
+
const validation = await validateProfile(result.provider, scope, profile.serverName);
|
|
1947
|
+
validations.push({ providerId: result.provider.id, valid: validation.valid, reason: validation.reason });
|
|
1948
|
+
}
|
|
1949
|
+
if (succeeded.length > 0) {
|
|
1950
|
+
await recordMcpInstall(
|
|
1951
|
+
profile.serverName,
|
|
1952
|
+
profile.packageSpec ?? resolvedOpts.command ?? "cleo-dev",
|
|
1953
|
+
channel === "dev" ? "command" : "package",
|
|
1954
|
+
succeeded.map((result) => result.provider.id),
|
|
1955
|
+
resolvedOpts.global ?? false
|
|
1956
|
+
);
|
|
1957
|
+
}
|
|
1958
|
+
if (format === "human") {
|
|
1959
|
+
formatInstallResultHuman(mode, channel, profile.serverName, scope, results, validations);
|
|
1960
|
+
}
|
|
1961
|
+
const validationFailures = validations.filter((entry) => !entry.valid);
|
|
1962
|
+
if (interactive && validationFailures.length > 0 && format === "human") {
|
|
1963
|
+
console.log(pc6.dim("Step 5/6 - Validation"));
|
|
1964
|
+
console.log(pc6.yellow(`Validation found ${validationFailures.length} issue(s).`));
|
|
1965
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1966
|
+
try {
|
|
1967
|
+
console.log(pc6.dim("Step 6/6 - Rollback"));
|
|
1968
|
+
const answer = await rl.question(pc6.dim("Rollback failed validations? [y/N] "));
|
|
1969
|
+
if (["y", "yes"].includes(answer.trim().toLowerCase())) {
|
|
1970
|
+
for (const failure of validationFailures) {
|
|
1971
|
+
const provider = providers.find((candidate) => candidate.id === failure.providerId);
|
|
1972
|
+
if (!provider) continue;
|
|
1973
|
+
await removeMcpServer(provider, profile.serverName, scope);
|
|
1974
|
+
}
|
|
1975
|
+
console.log(pc6.yellow("Rollback completed for failed provider validations."));
|
|
1976
|
+
}
|
|
1977
|
+
} finally {
|
|
1978
|
+
rl.close();
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
if (format === "json") {
|
|
1982
|
+
outputSuccess(operation, mvi, {
|
|
1983
|
+
action: mode,
|
|
1984
|
+
channel,
|
|
1985
|
+
serverName: profile.serverName,
|
|
1986
|
+
scope,
|
|
1987
|
+
command: profile.config.command,
|
|
1988
|
+
args: profile.config.args ?? [],
|
|
1989
|
+
env: profile.config.env ?? {},
|
|
1990
|
+
packageSpec: profile.packageSpec,
|
|
1991
|
+
providers: results.map((result) => ({
|
|
1992
|
+
id: result.provider.id,
|
|
1993
|
+
success: result.success,
|
|
1994
|
+
configPath: result.configPath,
|
|
1995
|
+
error: result.error,
|
|
1996
|
+
validation: validations.find((entry) => entry.providerId === result.provider.id) ?? null
|
|
1997
|
+
})),
|
|
1998
|
+
conflicts: conflictWarnings,
|
|
1999
|
+
validationStatus: validationFailures.length === 0 ? "ok" : "warning"
|
|
2000
|
+
});
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
async function executeCleoUninstall(opts, operation) {
|
|
2004
|
+
const mvi = "standard";
|
|
2005
|
+
let format;
|
|
2006
|
+
try {
|
|
2007
|
+
format = resolveFormat({
|
|
2008
|
+
jsonFlag: opts.json ?? false,
|
|
2009
|
+
humanFlag: (opts.human ?? false) || isHuman(),
|
|
2010
|
+
projectDefault: "json"
|
|
2011
|
+
});
|
|
2012
|
+
} catch (error) {
|
|
2013
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2014
|
+
emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
|
|
2015
|
+
process.exit(1);
|
|
2016
|
+
}
|
|
2017
|
+
const channel = normalizeCleoChannel(opts.channel);
|
|
2018
|
+
const serverName = resolveCleoServerName(channel);
|
|
2019
|
+
const providers = collectTargetProviders(opts.provider, opts.all);
|
|
2020
|
+
if (providers.length === 0) {
|
|
2021
|
+
const message = "No target providers found.";
|
|
2022
|
+
if (format === "json") {
|
|
2023
|
+
emitJsonError(operation, mvi, ErrorCodes.PROVIDER_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
|
|
2024
|
+
} else {
|
|
2025
|
+
console.error(pc6.red(message));
|
|
2026
|
+
}
|
|
2027
|
+
process.exit(1);
|
|
2028
|
+
}
|
|
2029
|
+
const scope = opts.global ? "global" : "project";
|
|
2030
|
+
if (opts.dryRun) {
|
|
2031
|
+
if (format === "human") {
|
|
2032
|
+
console.log(pc6.bold("Dry run: uninstall CLEO profile"));
|
|
2033
|
+
console.log(pc6.dim(`Server: ${serverName} Channel: ${channel} Scope: ${scope}`));
|
|
2034
|
+
console.log(pc6.dim(`Providers: ${providers.map((provider) => provider.id).join(", ")}`));
|
|
2035
|
+
} else {
|
|
2036
|
+
outputSuccess(operation, mvi, {
|
|
2037
|
+
action: "uninstall",
|
|
2038
|
+
channel,
|
|
2039
|
+
serverName,
|
|
2040
|
+
providers: providers.map((provider) => provider.id),
|
|
2041
|
+
scope,
|
|
2042
|
+
dryRun: true
|
|
2043
|
+
});
|
|
2044
|
+
}
|
|
2045
|
+
return;
|
|
2046
|
+
}
|
|
2047
|
+
const removed = [];
|
|
2048
|
+
for (const provider of providers) {
|
|
2049
|
+
const success = await removeMcpServer(provider, serverName, scope);
|
|
2050
|
+
if (success) removed.push(provider.id);
|
|
2051
|
+
}
|
|
2052
|
+
if (removed.length > 0) {
|
|
2053
|
+
await removeMcpFromLock(serverName);
|
|
2054
|
+
}
|
|
2055
|
+
if (format === "human") {
|
|
2056
|
+
const prefix = removed.length > 0 ? pc6.green("Removed") : pc6.yellow("No matching profile found for");
|
|
2057
|
+
console.log(`${prefix} ${pc6.bold(serverName)} (${channel}) on ${removed.length}/${providers.length} providers.`);
|
|
2058
|
+
}
|
|
2059
|
+
if (format === "json") {
|
|
2060
|
+
outputSuccess(operation, mvi, {
|
|
2061
|
+
action: "uninstall",
|
|
2062
|
+
channel,
|
|
2063
|
+
serverName,
|
|
2064
|
+
scope,
|
|
2065
|
+
removed,
|
|
2066
|
+
providerCount: providers.length,
|
|
2067
|
+
dryRun: false
|
|
2068
|
+
});
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
async function executeCleoShow(opts, operation) {
|
|
2072
|
+
const mvi = "standard";
|
|
2073
|
+
let format;
|
|
2074
|
+
try {
|
|
2075
|
+
format = resolveFormat({
|
|
2076
|
+
jsonFlag: opts.json ?? false,
|
|
2077
|
+
humanFlag: (opts.human ?? false) || isHuman(),
|
|
2078
|
+
projectDefault: "json"
|
|
2079
|
+
});
|
|
2080
|
+
} catch (error) {
|
|
2081
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2082
|
+
emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
|
|
2083
|
+
process.exit(1);
|
|
2084
|
+
}
|
|
2085
|
+
const providers = collectTargetProviders(opts.provider, opts.all);
|
|
2086
|
+
if (providers.length === 0) {
|
|
2087
|
+
const message = "No target providers found.";
|
|
2088
|
+
if (format === "json") {
|
|
2089
|
+
emitJsonError(operation, mvi, ErrorCodes.PROVIDER_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
|
|
2090
|
+
} else {
|
|
2091
|
+
console.error(pc6.red(message));
|
|
2092
|
+
}
|
|
2093
|
+
process.exit(1);
|
|
2094
|
+
}
|
|
2095
|
+
const channelFilter = opts.channel ? normalizeCleoChannel(opts.channel) : null;
|
|
2096
|
+
const scope = opts.global ? "global" : "project";
|
|
2097
|
+
const entries = [];
|
|
2098
|
+
for (const provider of providers) {
|
|
2099
|
+
const providerEntries = await listMcpServers(provider, scope);
|
|
2100
|
+
for (const entry of providerEntries) {
|
|
2101
|
+
const channel = resolveChannelFromServerName(entry.name);
|
|
2102
|
+
if (!channel) continue;
|
|
2103
|
+
if (channelFilter && channel !== channelFilter) continue;
|
|
2104
|
+
entries.push({
|
|
2105
|
+
provider: provider.id,
|
|
2106
|
+
serverName: entry.name,
|
|
2107
|
+
channel,
|
|
2108
|
+
command: typeof entry.config.command === "string" ? entry.config.command : void 0,
|
|
2109
|
+
args: Array.isArray(entry.config.args) ? entry.config.args.filter((value) => typeof value === "string") : [],
|
|
2110
|
+
env: typeof entry.config.env === "object" && entry.config.env !== null ? entry.config.env : {}
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
if (format === "human") {
|
|
2115
|
+
if (entries.length === 0) {
|
|
2116
|
+
console.log(pc6.dim("No CLEO MCP profiles found."));
|
|
2117
|
+
} else {
|
|
2118
|
+
for (const entry of entries) {
|
|
2119
|
+
console.log(`${pc6.bold(entry.provider.padEnd(22))} ${entry.serverName.padEnd(10)} ${pc6.dim(entry.channel)}`);
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
if (format === "json") {
|
|
2124
|
+
outputSuccess(operation, mvi, {
|
|
2125
|
+
providers: providers.map((provider) => provider.id),
|
|
2126
|
+
scope,
|
|
2127
|
+
channel: channelFilter,
|
|
2128
|
+
profiles: entries,
|
|
2129
|
+
count: entries.length
|
|
2130
|
+
});
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
function buildInstallOptions(command) {
|
|
2134
|
+
return command.requiredOption("--channel <channel>", "CLEO channel: stable|beta|dev").option("--provider <id>", "Target provider (repeatable)", collect, []).option("--all", "Apply to all detected providers").option("-g, --global", "Use global scope").option("--version <tag>", "Tag/version for stable or beta").option("--command <command>", "Dev channel command").option("--arg <arg>", "Dev command arg (repeatable)", collect, []).option("--env <kv>", "Environment assignment KEY=value (repeatable)", collect, []).option("--cleo-dir <path>", "CLEO_DIR override for dev channel").option("--dry-run", "Preview without writing").option("-y, --yes", "Skip confirmation").option("--interactive", "Guided interactive setup").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format");
|
|
2135
|
+
}
|
|
2136
|
+
function registerMcpCleoCommands(parent) {
|
|
2137
|
+
const cleo = parent.command("cleo").description("Manage CLEO MCP channel profiles");
|
|
2138
|
+
buildInstallOptions(
|
|
2139
|
+
cleo.command("install").description("Install CLEO MCP profile by channel")
|
|
2140
|
+
).action(async (opts) => {
|
|
2141
|
+
await executeCleoInstall("install", opts, "mcp.cleo.install");
|
|
2142
|
+
});
|
|
2143
|
+
buildInstallOptions(
|
|
2144
|
+
cleo.command("update").description("Update CLEO MCP profile by channel")
|
|
2145
|
+
).action(async (opts) => {
|
|
2146
|
+
await executeCleoInstall("update", opts, "mcp.cleo.update");
|
|
2147
|
+
});
|
|
2148
|
+
cleo.command("uninstall").description("Uninstall CLEO MCP profile for a channel").requiredOption("--channel <channel>", "CLEO channel: stable|beta|dev").option("--provider <id>", "Target provider (repeatable)", collect, []).option("--all", "Apply to all detected providers").option("-g, --global", "Use global scope").option("--dry-run", "Preview without writing").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
2149
|
+
await executeCleoUninstall(opts, "mcp.cleo.uninstall");
|
|
2150
|
+
});
|
|
2151
|
+
cleo.command("show").description("Show installed CLEO MCP channel profiles").option("--provider <id>", "Target provider (repeatable)", collect, []).option("--all", "Inspect all detected providers").option("-g, --global", "Use global scope").option("--channel <channel>", "Filter channel: stable|beta|dev").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
2152
|
+
await executeCleoShow(opts, "mcp.cleo.show");
|
|
2153
|
+
});
|
|
2154
|
+
}
|
|
2155
|
+
function registerMcpCleoCompatibilityCommands(parent) {
|
|
2156
|
+
parent.command("update").description("Update channel-managed MCP profile").argument("<name>", "Managed MCP profile name (cleo)").requiredOption("--channel <channel>", "CLEO channel: stable|beta|dev").option("--provider <id>", "Target provider (repeatable)", collect, []).option("--all", "Apply to all detected providers").option("-g, --global", "Use global scope").option("--version <tag>", "Tag/version for stable or beta").option("--command <command>", "Dev channel command").option("--arg <arg>", "Dev command arg (repeatable)", collect, []).option("--env <kv>", "Environment assignment KEY=value (repeatable)", collect, []).option("--cleo-dir <path>", "CLEO_DIR override for dev channel").option("--dry-run", "Preview without writing").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (name, opts) => {
|
|
2157
|
+
if (name !== "cleo") {
|
|
2158
|
+
emitJsonError("mcp.update", "standard", ErrorCodes.INVALID_INPUT, "Only managed profile 'cleo' is supported by mcp update.", ErrorCategories.VALIDATION, { name });
|
|
2159
|
+
process.exit(1);
|
|
2160
|
+
}
|
|
2161
|
+
await executeCleoInstall("update", opts, "mcp.update");
|
|
2162
|
+
});
|
|
2163
|
+
parent.command("uninstall").description("Uninstall channel-managed MCP profile").argument("<name>", "Managed MCP profile name (cleo)").requiredOption("--channel <channel>", "CLEO channel: stable|beta|dev").option("--provider <id>", "Target provider (repeatable)", collect, []).option("--all", "Apply to all detected providers").option("-g, --global", "Use global scope").option("--dry-run", "Preview without writing").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (name, opts) => {
|
|
2164
|
+
if (name !== "cleo") {
|
|
2165
|
+
emitJsonError("mcp.uninstall", "standard", ErrorCodes.INVALID_INPUT, "Only managed profile 'cleo' is supported by mcp uninstall.", ErrorCategories.VALIDATION, { name });
|
|
2166
|
+
process.exit(1);
|
|
2167
|
+
}
|
|
2168
|
+
await executeCleoUninstall(opts, "mcp.uninstall");
|
|
2169
|
+
});
|
|
2170
|
+
parent.command("show").description("Show channel-managed MCP profile").argument("<name>", "Managed MCP profile name (cleo)").option("--provider <id>", "Target provider (repeatable)", collect, []).option("--all", "Inspect all detected providers").option("-g, --global", "Use global scope").option("--channel <channel>", "Filter channel: stable|beta|dev").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (name, opts) => {
|
|
2171
|
+
if (name !== "cleo") {
|
|
2172
|
+
emitJsonError("mcp.show", "standard", ErrorCodes.INVALID_INPUT, "Only managed profile 'cleo' is supported by mcp show.", ErrorCategories.VALIDATION, { name });
|
|
2173
|
+
process.exit(1);
|
|
2174
|
+
}
|
|
2175
|
+
await executeCleoShow(opts, "mcp.show");
|
|
2176
|
+
});
|
|
2177
|
+
}
|
|
2178
|
+
function mapCompatibilityInstallOptions(opts) {
|
|
2179
|
+
return {
|
|
2180
|
+
channel: opts.channel,
|
|
2181
|
+
provider: [...opts.provider ?? [], ...opts.agent ?? []],
|
|
2182
|
+
all: opts.all,
|
|
2183
|
+
global: opts.global,
|
|
2184
|
+
version: opts.version,
|
|
2185
|
+
command: opts.command,
|
|
2186
|
+
arg: opts.arg ?? [],
|
|
2187
|
+
env: opts.env ?? [],
|
|
2188
|
+
cleoDir: opts.cleoDir,
|
|
2189
|
+
dryRun: opts.dryRun,
|
|
2190
|
+
yes: opts.yes,
|
|
2191
|
+
interactive: opts.interactive,
|
|
2192
|
+
json: opts.json,
|
|
2193
|
+
human: opts.human
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
function shouldUseCleoCompatibilityInstall(source, channel) {
|
|
2197
|
+
if (source.trim().toLowerCase() !== "cleo") return false;
|
|
2198
|
+
return typeof channel === "string" && channel.trim() !== "";
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
// src/commands/mcp/detect.ts
|
|
2202
|
+
import { existsSync as existsSync4 } from "fs";
|
|
2203
|
+
import pc7 from "picocolors";
|
|
2204
|
+
function registerMcpDetect(parent) {
|
|
2205
|
+
parent.command("detect").description("Auto-detect installed MCP tools and their configurations").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
2206
|
+
const operation = "mcp.detect";
|
|
2207
|
+
const mvi = "standard";
|
|
2208
|
+
let format;
|
|
2209
|
+
try {
|
|
2210
|
+
format = resolveFormat({
|
|
2211
|
+
jsonFlag: opts.json ?? false,
|
|
2212
|
+
humanFlag: (opts.human ?? false) || isHuman(),
|
|
2213
|
+
projectDefault: "json"
|
|
2214
|
+
});
|
|
2215
|
+
} catch (error) {
|
|
2216
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2217
|
+
emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
|
|
2218
|
+
process.exit(1);
|
|
2219
|
+
}
|
|
2220
|
+
const providers = getInstalledProviders();
|
|
2221
|
+
const providersResult = [];
|
|
2222
|
+
let totalConfigs = 0;
|
|
2223
|
+
for (const provider of providers) {
|
|
2224
|
+
const globalPath = resolveConfigPath(provider, "global");
|
|
2225
|
+
const projectPath = resolveConfigPath(provider, "project");
|
|
2226
|
+
const globalEntries = await listMcpServers(provider, "global");
|
|
2227
|
+
const projectEntries = await listMcpServers(provider, "project");
|
|
2228
|
+
const configsFound = (globalPath && existsSync4(globalPath) ? 1 : 0) + (projectPath && existsSync4(projectPath) ? 1 : 0);
|
|
2229
|
+
totalConfigs += configsFound;
|
|
2230
|
+
const allServers = [...globalEntries.map((e) => e.name), ...projectEntries.map((e) => e.name)];
|
|
2231
|
+
providersResult.push({
|
|
2232
|
+
id: provider.id,
|
|
2233
|
+
configsFound,
|
|
2234
|
+
servers: allServers
|
|
2235
|
+
});
|
|
2236
|
+
}
|
|
2237
|
+
if (format === "json") {
|
|
2238
|
+
outputSuccess(operation, mvi, {
|
|
2239
|
+
providers: providersResult,
|
|
2240
|
+
totalConfigs
|
|
2241
|
+
});
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
console.log(pc7.bold(`
|
|
2245
|
+
${providers.length} provider(s) with MCP support:
|
|
2246
|
+
`));
|
|
2247
|
+
for (const provider of providersResult) {
|
|
2248
|
+
const globalPath = resolveConfigPath(providers.find((p) => p.id === provider.id), "global");
|
|
2249
|
+
const projectPath = resolveConfigPath(providers.find((p) => p.id === provider.id), "project");
|
|
2250
|
+
const hasGlobal = globalPath && existsSync4(globalPath);
|
|
2251
|
+
const hasProject = projectPath && existsSync4(projectPath);
|
|
2252
|
+
const globalIcon = hasGlobal ? pc7.green("G") : pc7.dim("-");
|
|
2253
|
+
const projectIcon = hasProject ? pc7.green("P") : pc7.dim("-");
|
|
2254
|
+
const serverList = provider.servers.length > 0 ? pc7.dim(provider.servers.join(", ")) : pc7.dim("no servers");
|
|
2255
|
+
console.log(` [${globalIcon}${projectIcon}] ${pc7.bold(provider.id.padEnd(20))} ${serverList}`);
|
|
2256
|
+
}
|
|
2257
|
+
console.log(pc7.dim("\nG = global config, P = project config"));
|
|
2258
|
+
console.log();
|
|
2259
|
+
});
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
// src/commands/mcp/install.ts
|
|
2263
|
+
import pc8 from "picocolors";
|
|
1606
2264
|
function registerMcpInstall(parent) {
|
|
1607
|
-
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").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (source, opts) => {
|
|
2265
|
+
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("--provider <id>", "Target provider ID (alias for --agent)", (v, prev) => [...prev, v], []).option("-g, --global", "Install to global/user config").option("-n, --name <name>", "Override inferred server name").option("--channel <channel>", "Managed channel profile (stable|beta|dev)").option("--version <tag>", "Managed profile tag/version for stable or beta").option("--command <command>", "Managed dev profile command").option("--arg <arg>", "Managed dev command arg (repeatable)", (v, prev) => [...prev, v], []).option("--env <kv>", "Managed env assignment KEY=value (repeatable)", (v, prev) => [...prev, v], []).option("--cleo-dir <path>", "Managed dev CLEO_DIR override").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("--interactive", "Guided interactive setup for managed profiles").option("--dry-run", "Preview without writing").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (source, opts) => {
|
|
1608
2266
|
const operation = "mcp.install";
|
|
1609
2267
|
const mvi = "standard";
|
|
1610
2268
|
let format;
|
|
@@ -1619,6 +2277,11 @@ function registerMcpInstall(parent) {
|
|
|
1619
2277
|
emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
|
|
1620
2278
|
process.exit(1);
|
|
1621
2279
|
}
|
|
2280
|
+
if (shouldUseCleoCompatibilityInstall(source, opts.channel)) {
|
|
2281
|
+
const cleoOpts = mapCompatibilityInstallOptions(opts);
|
|
2282
|
+
await executeCleoInstall("install", cleoOpts, operation);
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
1622
2285
|
const parsed = parseSource(source);
|
|
1623
2286
|
const serverName = opts.name ?? parsed.inferredName;
|
|
1624
2287
|
const headers = {};
|
|
@@ -1634,6 +2297,8 @@ function registerMcpInstall(parent) {
|
|
|
1634
2297
|
providers = getInstalledProviders();
|
|
1635
2298
|
} else if (opts.agent.length > 0) {
|
|
1636
2299
|
providers = opts.agent.map((a) => getProvider(a)).filter((p) => p !== void 0);
|
|
2300
|
+
} else if (opts.provider.length > 0) {
|
|
2301
|
+
providers = opts.provider.map((a) => getProvider(a)).filter((p) => p !== void 0);
|
|
1637
2302
|
} else {
|
|
1638
2303
|
providers = getInstalledProviders();
|
|
1639
2304
|
}
|
|
@@ -1642,7 +2307,7 @@ function registerMcpInstall(parent) {
|
|
|
1642
2307
|
if (format === "json") {
|
|
1643
2308
|
emitJsonError(operation, mvi, ErrorCodes.PROVIDER_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
|
|
1644
2309
|
} else {
|
|
1645
|
-
console.error(
|
|
2310
|
+
console.error(pc8.red(message));
|
|
1646
2311
|
}
|
|
1647
2312
|
process.exit(1);
|
|
1648
2313
|
}
|
|
@@ -1658,8 +2323,8 @@ function registerMcpInstall(parent) {
|
|
|
1658
2323
|
dryRun: true
|
|
1659
2324
|
});
|
|
1660
2325
|
} else {
|
|
1661
|
-
console.log(
|
|
1662
|
-
console.log(` Server: ${
|
|
2326
|
+
console.log(pc8.bold("Dry run - would install:"));
|
|
2327
|
+
console.log(` Server: ${pc8.bold(serverName)}`);
|
|
1663
2328
|
console.log(` Config: ${JSON.stringify(config, null, 2)}`);
|
|
1664
2329
|
console.log(` Scope: ${scope}`);
|
|
1665
2330
|
console.log(` Providers: ${providers.map((p) => p.id).join(", ")}`);
|
|
@@ -1667,7 +2332,7 @@ function registerMcpInstall(parent) {
|
|
|
1667
2332
|
return;
|
|
1668
2333
|
}
|
|
1669
2334
|
if (format === "human") {
|
|
1670
|
-
console.log(
|
|
2335
|
+
console.log(pc8.dim(`Installing "${serverName}" to ${providers.length} provider(s)...
|
|
1671
2336
|
`));
|
|
1672
2337
|
}
|
|
1673
2338
|
const results = await installMcpServerToAll(
|
|
@@ -1681,9 +2346,9 @@ function registerMcpInstall(parent) {
|
|
|
1681
2346
|
if (format === "human") {
|
|
1682
2347
|
for (const r of results) {
|
|
1683
2348
|
if (r.success) {
|
|
1684
|
-
console.log(` ${
|
|
2349
|
+
console.log(` ${pc8.green("\u2713")} ${r.provider.toolName.padEnd(22)} ${pc8.dim(r.configPath)}`);
|
|
1685
2350
|
} else {
|
|
1686
|
-
console.log(` ${
|
|
2351
|
+
console.log(` ${pc8.red("\u2717")} ${r.provider.toolName.padEnd(22)} ${pc8.red(r.error ?? "failed")}`);
|
|
1687
2352
|
}
|
|
1688
2353
|
}
|
|
1689
2354
|
}
|
|
@@ -1706,85 +2371,16 @@ function registerMcpInstall(parent) {
|
|
|
1706
2371
|
dryRun: false
|
|
1707
2372
|
});
|
|
1708
2373
|
} else {
|
|
1709
|
-
console.log(
|
|
2374
|
+
console.log(pc8.bold(`
|
|
1710
2375
|
${succeeded.length}/${results.length} providers configured.`));
|
|
1711
2376
|
}
|
|
1712
2377
|
});
|
|
1713
2378
|
}
|
|
1714
2379
|
|
|
1715
|
-
// src/commands/mcp/remove.ts
|
|
1716
|
-
import pc7 from "picocolors";
|
|
1717
|
-
function registerMcpRemove(parent) {
|
|
1718
|
-
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").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (name, opts) => {
|
|
1719
|
-
const operation = "mcp.remove";
|
|
1720
|
-
const mvi = "standard";
|
|
1721
|
-
let format;
|
|
1722
|
-
try {
|
|
1723
|
-
format = resolveFormat({
|
|
1724
|
-
jsonFlag: opts.json ?? false,
|
|
1725
|
-
humanFlag: (opts.human ?? false) || isHuman(),
|
|
1726
|
-
projectDefault: "json"
|
|
1727
|
-
});
|
|
1728
|
-
} catch (error) {
|
|
1729
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1730
|
-
emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
|
|
1731
|
-
process.exit(1);
|
|
1732
|
-
}
|
|
1733
|
-
let providers;
|
|
1734
|
-
if (opts.all) {
|
|
1735
|
-
providers = getInstalledProviders();
|
|
1736
|
-
} else if (opts.agent.length > 0) {
|
|
1737
|
-
providers = opts.agent.map((a) => getProvider(a)).filter((p) => p !== void 0);
|
|
1738
|
-
} else {
|
|
1739
|
-
providers = getInstalledProviders();
|
|
1740
|
-
}
|
|
1741
|
-
if (providers.length === 0) {
|
|
1742
|
-
const message = "No target providers found.";
|
|
1743
|
-
if (format === "json") {
|
|
1744
|
-
emitJsonError(operation, mvi, ErrorCodes.PROVIDER_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
|
|
1745
|
-
} else {
|
|
1746
|
-
console.error(pc7.red(message));
|
|
1747
|
-
}
|
|
1748
|
-
process.exit(1);
|
|
1749
|
-
}
|
|
1750
|
-
const scope = opts.global ? "global" : "project";
|
|
1751
|
-
const removed = [];
|
|
1752
|
-
const notFound = [];
|
|
1753
|
-
for (const provider of providers) {
|
|
1754
|
-
const success = await removeMcpServer(provider, name, scope);
|
|
1755
|
-
if (success) {
|
|
1756
|
-
removed.push(provider.id);
|
|
1757
|
-
if (format === "human") {
|
|
1758
|
-
console.log(` ${pc7.green("\u2713")} Removed from ${provider.toolName}`);
|
|
1759
|
-
}
|
|
1760
|
-
} else {
|
|
1761
|
-
notFound.push(provider.id);
|
|
1762
|
-
}
|
|
1763
|
-
}
|
|
1764
|
-
if (removed.length > 0) {
|
|
1765
|
-
await removeMcpFromLock(name);
|
|
1766
|
-
}
|
|
1767
|
-
if (format === "json") {
|
|
1768
|
-
outputSuccess(operation, mvi, {
|
|
1769
|
-
removed,
|
|
1770
|
-
providers: removed,
|
|
1771
|
-
notFound: notFound.length > 0 ? notFound : void 0
|
|
1772
|
-
});
|
|
1773
|
-
} else {
|
|
1774
|
-
if (removed.length > 0) {
|
|
1775
|
-
console.log(pc7.green(`
|
|
1776
|
-
\u2713 Removed "${name}" from ${removed.length} provider(s).`));
|
|
1777
|
-
} else {
|
|
1778
|
-
console.log(pc7.yellow(`Server "${name}" not found in any provider config.`));
|
|
1779
|
-
}
|
|
1780
|
-
}
|
|
1781
|
-
});
|
|
1782
|
-
}
|
|
1783
|
-
|
|
1784
2380
|
// src/commands/mcp/list.ts
|
|
1785
|
-
import
|
|
2381
|
+
import pc9 from "picocolors";
|
|
1786
2382
|
function registerMcpList(parent) {
|
|
1787
|
-
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 (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
2383
|
+
parent.command("list").description("List configured MCP servers").option("-a, --agent <name>", "List for specific agent").option("--provider <id>", "Provider ID alias for --agent").option("-g, --global", "List global config").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
1788
2384
|
const operation = "mcp.list";
|
|
1789
2385
|
const mvi = "standard";
|
|
1790
2386
|
let format;
|
|
@@ -1799,15 +2395,16 @@ function registerMcpList(parent) {
|
|
|
1799
2395
|
emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
|
|
1800
2396
|
process.exit(1);
|
|
1801
2397
|
}
|
|
1802
|
-
const
|
|
1803
|
-
|
|
1804
|
-
|
|
2398
|
+
const selectedProvider = opts.provider ?? opts.agent;
|
|
2399
|
+
const providers = selectedProvider ? [getProvider(selectedProvider)].filter((p) => p !== void 0) : getInstalledProviders();
|
|
2400
|
+
if (selectedProvider && providers.length === 0) {
|
|
2401
|
+
const message = `Provider not found: ${selectedProvider}`;
|
|
1805
2402
|
if (format === "json") {
|
|
1806
2403
|
emitJsonError(operation, mvi, ErrorCodes.PROVIDER_NOT_FOUND, message, ErrorCategories.NOT_FOUND, {
|
|
1807
|
-
|
|
2404
|
+
provider: selectedProvider
|
|
1808
2405
|
});
|
|
1809
2406
|
} else {
|
|
1810
|
-
console.error(
|
|
2407
|
+
console.error(pc9.red(message));
|
|
1811
2408
|
}
|
|
1812
2409
|
process.exit(1);
|
|
1813
2410
|
}
|
|
@@ -1827,33 +2424,32 @@ function registerMcpList(parent) {
|
|
|
1827
2424
|
outputSuccess(operation, mvi, {
|
|
1828
2425
|
servers: allEntries,
|
|
1829
2426
|
count: allEntries.length,
|
|
1830
|
-
scope: opts.global ? "global" :
|
|
2427
|
+
scope: opts.global ? "global" : selectedProvider ? `agent:${selectedProvider}` : "project"
|
|
1831
2428
|
});
|
|
1832
2429
|
return;
|
|
1833
2430
|
}
|
|
1834
2431
|
if (allEntries.length === 0) {
|
|
1835
|
-
console.log(
|
|
2432
|
+
console.log(pc9.dim("No MCP servers configured."));
|
|
1836
2433
|
return;
|
|
1837
2434
|
}
|
|
1838
|
-
console.log(
|
|
2435
|
+
console.log(pc9.bold(`
|
|
1839
2436
|
${allEntries.length} MCP server(s) configured:
|
|
1840
2437
|
`));
|
|
1841
2438
|
for (const entry of allEntries) {
|
|
1842
|
-
const scopeIndicator = entry.scope === "global" ?
|
|
1843
|
-
console.log(` ${scopeIndicator}${
|
|
2439
|
+
const scopeIndicator = entry.scope === "global" ? pc9.dim("[G] ") : pc9.dim("[P] ");
|
|
2440
|
+
console.log(` ${scopeIndicator}${pc9.bold(entry.name.padEnd(25))} ${entry.command ? pc9.dim(entry.command) : ""}`);
|
|
1844
2441
|
}
|
|
1845
2442
|
console.log();
|
|
1846
|
-
console.log(
|
|
2443
|
+
console.log(pc9.dim("G = global config, P = project config"));
|
|
1847
2444
|
console.log();
|
|
1848
2445
|
});
|
|
1849
2446
|
}
|
|
1850
2447
|
|
|
1851
|
-
// src/commands/mcp/
|
|
1852
|
-
import
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
const operation = "mcp.detect";
|
|
2448
|
+
// src/commands/mcp/remove.ts
|
|
2449
|
+
import pc10 from "picocolors";
|
|
2450
|
+
function registerMcpRemove(parent) {
|
|
2451
|
+
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("--provider <id>", "Target provider ID (alias for --agent)", (v, prev) => [...prev, v], []).option("-g, --global", "Remove from global config").option("--all", "Remove from all detected agents").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (name, opts) => {
|
|
2452
|
+
const operation = "mcp.remove";
|
|
1857
2453
|
const mvi = "standard";
|
|
1858
2454
|
let format;
|
|
1859
2455
|
try {
|
|
@@ -1867,45 +2463,56 @@ function registerMcpDetect(parent) {
|
|
|
1867
2463
|
emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
|
|
1868
2464
|
process.exit(1);
|
|
1869
2465
|
}
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
2466
|
+
let providers;
|
|
2467
|
+
if (opts.all) {
|
|
2468
|
+
providers = getInstalledProviders();
|
|
2469
|
+
} else if (opts.agent.length > 0) {
|
|
2470
|
+
providers = opts.agent.map((a) => getProvider(a)).filter((p) => p !== void 0);
|
|
2471
|
+
} else if (opts.provider.length > 0) {
|
|
2472
|
+
providers = opts.provider.map((a) => getProvider(a)).filter((p) => p !== void 0);
|
|
2473
|
+
} else {
|
|
2474
|
+
providers = getInstalledProviders();
|
|
2475
|
+
}
|
|
2476
|
+
if (providers.length === 0) {
|
|
2477
|
+
const message = "No target providers found.";
|
|
2478
|
+
if (format === "json") {
|
|
2479
|
+
emitJsonError(operation, mvi, ErrorCodes.PROVIDER_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
|
|
2480
|
+
} else {
|
|
2481
|
+
console.error(pc10.red(message));
|
|
2482
|
+
}
|
|
2483
|
+
process.exit(1);
|
|
2484
|
+
}
|
|
2485
|
+
const scope = opts.global ? "global" : "project";
|
|
2486
|
+
const removed = [];
|
|
2487
|
+
const notFound = [];
|
|
1873
2488
|
for (const provider of providers) {
|
|
1874
|
-
const
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
2489
|
+
const success = await removeMcpServer(provider, name, scope);
|
|
2490
|
+
if (success) {
|
|
2491
|
+
removed.push(provider.id);
|
|
2492
|
+
if (format === "human") {
|
|
2493
|
+
console.log(` ${pc10.green("\u2713")} Removed from ${provider.toolName}`);
|
|
2494
|
+
}
|
|
2495
|
+
} else {
|
|
2496
|
+
notFound.push(provider.id);
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
if (removed.length > 0) {
|
|
2500
|
+
await removeMcpFromLock(name);
|
|
1886
2501
|
}
|
|
1887
2502
|
if (format === "json") {
|
|
1888
2503
|
outputSuccess(operation, mvi, {
|
|
1889
|
-
|
|
1890
|
-
|
|
2504
|
+
removed,
|
|
2505
|
+
providers: removed,
|
|
2506
|
+
notFound: notFound.length > 0 ? notFound : void 0
|
|
1891
2507
|
});
|
|
1892
|
-
|
|
2508
|
+
} else {
|
|
2509
|
+
if (removed.length > 0) {
|
|
2510
|
+
console.log(pc10.green(`
|
|
2511
|
+
\u2713 Removed "${name}" from ${removed.length} provider(s).`));
|
|
2512
|
+
} else {
|
|
2513
|
+
console.log(pc10.yellow(`Server "${name}" not found in any provider config.`));
|
|
2514
|
+
}
|
|
1893
2515
|
}
|
|
1894
|
-
console.log(pc9.bold(`
|
|
1895
|
-
${providers.length} provider(s) with MCP support:
|
|
1896
|
-
`));
|
|
1897
|
-
for (const provider of providersResult) {
|
|
1898
|
-
const globalPath = resolveConfigPath(providers.find((p) => p.id === provider.id), "global");
|
|
1899
|
-
const projectPath = resolveConfigPath(providers.find((p) => p.id === provider.id), "project");
|
|
1900
|
-
const hasGlobal = globalPath && existsSync3(globalPath);
|
|
1901
|
-
const hasProject = projectPath && existsSync3(projectPath);
|
|
1902
|
-
const globalIcon = hasGlobal ? pc9.green("G") : pc9.dim("-");
|
|
1903
|
-
const projectIcon = hasProject ? pc9.green("P") : pc9.dim("-");
|
|
1904
|
-
const serverList = provider.servers.length > 0 ? pc9.dim(provider.servers.join(", ")) : pc9.dim("no servers");
|
|
1905
|
-
console.log(` [${globalIcon}${projectIcon}] ${pc9.bold(provider.id.padEnd(20))} ${serverList}`);
|
|
1906
|
-
}
|
|
1907
|
-
console.log(pc9.dim("\nG = global config, P = project config"));
|
|
1908
|
-
console.log();
|
|
1909
2516
|
});
|
|
1910
2517
|
}
|
|
1911
2518
|
|
|
@@ -1916,12 +2523,14 @@ function registerMcpCommands(program2) {
|
|
|
1916
2523
|
registerMcpRemove(mcp);
|
|
1917
2524
|
registerMcpList(mcp);
|
|
1918
2525
|
registerMcpDetect(mcp);
|
|
2526
|
+
registerMcpCleoCommands(mcp);
|
|
2527
|
+
registerMcpCleoCompatibilityCommands(mcp);
|
|
1919
2528
|
}
|
|
1920
2529
|
|
|
1921
2530
|
// src/commands/providers.ts
|
|
1922
2531
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
1923
2532
|
import { resolveOutputFormat as resolveOutputFormat2 } from "@cleocode/lafs-protocol";
|
|
1924
|
-
import
|
|
2533
|
+
import pc11 from "picocolors";
|
|
1925
2534
|
function registerProvidersCommand(program2) {
|
|
1926
2535
|
const providers = program2.command("providers").description("Manage AI agent providers");
|
|
1927
2536
|
providers.command("list").description("List all supported providers").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").option("--tier <tier>", "Filter by priority tier (high, medium, low)").action(async (opts) => {
|
|
@@ -1955,19 +2564,19 @@ function registerProvidersCommand(program2) {
|
|
|
1955
2564
|
console.log(JSON.stringify(envelope, null, 2));
|
|
1956
2565
|
return;
|
|
1957
2566
|
}
|
|
1958
|
-
console.log(
|
|
2567
|
+
console.log(pc11.bold(`
|
|
1959
2568
|
CAMP Provider Registry v${getRegistryVersion()}`));
|
|
1960
|
-
console.log(
|
|
2569
|
+
console.log(pc11.dim(`${getProviderCount()} providers
|
|
1961
2570
|
`));
|
|
1962
2571
|
const tiers = ["high", "medium", "low"];
|
|
1963
2572
|
for (const tier of tiers) {
|
|
1964
2573
|
const tierProviders = all.filter((p) => p.priority === tier);
|
|
1965
2574
|
if (tierProviders.length === 0) continue;
|
|
1966
|
-
const tierLabel = tier === "high" ?
|
|
2575
|
+
const tierLabel = tier === "high" ? pc11.green("HIGH") : tier === "medium" ? pc11.yellow("MEDIUM") : pc11.dim("LOW");
|
|
1967
2576
|
console.log(`${tierLabel} priority:`);
|
|
1968
2577
|
for (const p of tierProviders) {
|
|
1969
|
-
const status = p.status === "active" ?
|
|
1970
|
-
console.log(` ${
|
|
2578
|
+
const status = p.status === "active" ? pc11.green("active") : p.status === "beta" ? pc11.yellow("beta") : pc11.dim(p.status);
|
|
2579
|
+
console.log(` ${pc11.bold(p.agentFlag.padEnd(20))} ${p.toolName.padEnd(22)} ${p.vendor.padEnd(16)} [${status}]`);
|
|
1971
2580
|
}
|
|
1972
2581
|
console.log();
|
|
1973
2582
|
}
|
|
@@ -2011,17 +2620,17 @@ CAMP Provider Registry v${getRegistryVersion()}`));
|
|
|
2011
2620
|
console.log(JSON.stringify(envelope, null, 2));
|
|
2012
2621
|
return;
|
|
2013
2622
|
}
|
|
2014
|
-
console.log(
|
|
2623
|
+
console.log(pc11.bold(`
|
|
2015
2624
|
Detected ${installed.length} installed providers:
|
|
2016
2625
|
`));
|
|
2017
2626
|
for (const r of installed) {
|
|
2018
2627
|
const methods = r.methods.join(", ");
|
|
2019
|
-
const project = r.projectDetected ?
|
|
2020
|
-
console.log(` ${
|
|
2628
|
+
const project = r.projectDetected ? pc11.green(" [project]") : "";
|
|
2629
|
+
console.log(` ${pc11.green("\u2713")} ${pc11.bold(r.provider.toolName.padEnd(22))} via ${pc11.dim(methods)}${project}`);
|
|
2021
2630
|
}
|
|
2022
2631
|
const notInstalled = results.filter((r) => !r.installed);
|
|
2023
2632
|
if (notInstalled.length > 0) {
|
|
2024
|
-
console.log(
|
|
2633
|
+
console.log(pc11.dim(`
|
|
2025
2634
|
${notInstalled.length} providers not detected`));
|
|
2026
2635
|
}
|
|
2027
2636
|
console.log();
|
|
@@ -2049,7 +2658,7 @@ Detected ${installed.length} installed providers:
|
|
|
2049
2658
|
id
|
|
2050
2659
|
});
|
|
2051
2660
|
} else {
|
|
2052
|
-
console.error(
|
|
2661
|
+
console.error(pc11.red(message));
|
|
2053
2662
|
}
|
|
2054
2663
|
process.exit(1);
|
|
2055
2664
|
}
|
|
@@ -2065,9 +2674,9 @@ Detected ${installed.length} installed providers:
|
|
|
2065
2674
|
console.log(JSON.stringify(envelope, null, 2));
|
|
2066
2675
|
return;
|
|
2067
2676
|
}
|
|
2068
|
-
console.log(
|
|
2677
|
+
console.log(pc11.bold(`
|
|
2069
2678
|
${provider.toolName}`));
|
|
2070
|
-
console.log(
|
|
2679
|
+
console.log(pc11.dim(`by ${provider.vendor}
|
|
2071
2680
|
`));
|
|
2072
2681
|
console.log(` ID: ${provider.id}`);
|
|
2073
2682
|
console.log(` Flag: --agent ${provider.agentFlag}`);
|
|
@@ -2083,7 +2692,7 @@ ${provider.toolName}`));
|
|
|
2083
2692
|
console.log(` Transports: ${provider.supportedTransports.join(", ")}`);
|
|
2084
2693
|
console.log(` Headers: ${provider.supportsHeaders ? "yes" : "no"}`);
|
|
2085
2694
|
console.log();
|
|
2086
|
-
console.log(
|
|
2695
|
+
console.log(pc11.dim(" Paths:"));
|
|
2087
2696
|
console.log(` Global dir: ${provider.pathGlobal}`);
|
|
2088
2697
|
console.log(` Project dir: ${provider.pathProject || "(none)"}`);
|
|
2089
2698
|
console.log(` Global config: ${provider.configPathGlobal}`);
|
|
@@ -2126,8 +2735,8 @@ function emitJsonError2(operation, mvi, code, message, category, details = {}) {
|
|
|
2126
2735
|
}
|
|
2127
2736
|
|
|
2128
2737
|
// src/commands/skills/install.ts
|
|
2129
|
-
import { existsSync as
|
|
2130
|
-
import
|
|
2738
|
+
import { existsSync as existsSync5 } from "fs";
|
|
2739
|
+
import pc12 from "picocolors";
|
|
2131
2740
|
|
|
2132
2741
|
// src/core/sources/github.ts
|
|
2133
2742
|
import { simpleGit } from "simple-git";
|
|
@@ -2211,7 +2820,7 @@ function registerSkillsInstall(parent) {
|
|
|
2211
2820
|
if (format === "json") {
|
|
2212
2821
|
emitError2(operation, mvi, ErrorCodes.PROVIDER_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
|
|
2213
2822
|
}
|
|
2214
|
-
console.error(
|
|
2823
|
+
console.error(pc12.red(message));
|
|
2215
2824
|
process.exit(1);
|
|
2216
2825
|
}
|
|
2217
2826
|
if (opts.profile) {
|
|
@@ -2223,12 +2832,12 @@ function registerSkillsInstall(parent) {
|
|
|
2223
2832
|
if (format === "json") {
|
|
2224
2833
|
emitError2(operation, mvi, ErrorCodes.INVALID_INPUT, message, ErrorCategories.VALIDATION);
|
|
2225
2834
|
}
|
|
2226
|
-
console.error(
|
|
2227
|
-
console.log(
|
|
2835
|
+
console.error(pc12.red(message));
|
|
2836
|
+
console.log(pc12.dim("Usage: caamp skills install <source> or caamp skills install --profile <name>"));
|
|
2228
2837
|
process.exit(1);
|
|
2229
2838
|
}
|
|
2230
2839
|
if (format === "human") {
|
|
2231
|
-
console.log(
|
|
2840
|
+
console.log(pc12.dim(`Installing to ${providers.length} provider(s)...`));
|
|
2232
2841
|
}
|
|
2233
2842
|
let localPath;
|
|
2234
2843
|
let cleanup;
|
|
@@ -2268,7 +2877,7 @@ function registerSkillsInstall(parent) {
|
|
|
2268
2877
|
if (format === "json") {
|
|
2269
2878
|
emitJsonError(operation, mvi, ErrorCodes.NETWORK_ERROR, message, ErrorCategories.TRANSIENT);
|
|
2270
2879
|
}
|
|
2271
|
-
console.error(
|
|
2880
|
+
console.error(pc12.red(message));
|
|
2272
2881
|
process.exit(1);
|
|
2273
2882
|
}
|
|
2274
2883
|
} else if (parsed.type === "gitlab" && parsed.owner && parsed.repo) {
|
|
@@ -2281,7 +2890,7 @@ function registerSkillsInstall(parent) {
|
|
|
2281
2890
|
if (format === "json") {
|
|
2282
2891
|
emitJsonError(operation, mvi, ErrorCodes.NETWORK_ERROR, message, ErrorCategories.TRANSIENT);
|
|
2283
2892
|
}
|
|
2284
|
-
console.error(
|
|
2893
|
+
console.error(pc12.red(message));
|
|
2285
2894
|
process.exit(1);
|
|
2286
2895
|
}
|
|
2287
2896
|
} else if (parsed.type === "local") {
|
|
@@ -2296,7 +2905,7 @@ function registerSkillsInstall(parent) {
|
|
|
2296
2905
|
if (format === "json") {
|
|
2297
2906
|
emitJsonError(operation, mvi, ErrorCodes.INVALID_INPUT, message, ErrorCategories.VALIDATION);
|
|
2298
2907
|
}
|
|
2299
|
-
console.error(
|
|
2908
|
+
console.error(pc12.red(message));
|
|
2300
2909
|
process.exit(1);
|
|
2301
2910
|
}
|
|
2302
2911
|
const catalogSkill = getSkill(parsed.inferredName);
|
|
@@ -2306,7 +2915,7 @@ function registerSkillsInstall(parent) {
|
|
|
2306
2915
|
sourceValue = `library:${catalogSkill.name}`;
|
|
2307
2916
|
sourceType = "library";
|
|
2308
2917
|
if (format === "human") {
|
|
2309
|
-
console.log(` Found in catalog: ${
|
|
2918
|
+
console.log(` Found in catalog: ${pc12.bold(catalogSkill.name)} v${catalogSkill.version} (${pc12.dim(catalogSkill.category)})`);
|
|
2310
2919
|
}
|
|
2311
2920
|
} else {
|
|
2312
2921
|
const message = `Skill not found in catalog: ${parsed.inferredName}`;
|
|
@@ -2315,8 +2924,8 @@ function registerSkillsInstall(parent) {
|
|
|
2315
2924
|
availableSkills: listSkills()
|
|
2316
2925
|
});
|
|
2317
2926
|
}
|
|
2318
|
-
console.error(
|
|
2319
|
-
console.log(
|
|
2927
|
+
console.error(pc12.red(message));
|
|
2928
|
+
console.log(pc12.dim("Available skills: " + listSkills().join(", ")));
|
|
2320
2929
|
process.exit(1);
|
|
2321
2930
|
}
|
|
2322
2931
|
} else {
|
|
@@ -2324,7 +2933,7 @@ function registerSkillsInstall(parent) {
|
|
|
2324
2933
|
if (format === "json") {
|
|
2325
2934
|
emitJsonError(operation, mvi, ErrorCodes.INVALID_FORMAT, message, ErrorCategories.VALIDATION);
|
|
2326
2935
|
}
|
|
2327
|
-
console.error(
|
|
2936
|
+
console.error(pc12.red(message));
|
|
2328
2937
|
process.exit(1);
|
|
2329
2938
|
}
|
|
2330
2939
|
}
|
|
@@ -2334,7 +2943,7 @@ function registerSkillsInstall(parent) {
|
|
|
2334
2943
|
if (format === "json") {
|
|
2335
2944
|
emitJsonError(operation, mvi, ErrorCodes.INTERNAL_ERROR, message, ErrorCategories.INTERNAL);
|
|
2336
2945
|
}
|
|
2337
|
-
console.error(
|
|
2946
|
+
console.error(pc12.red(message));
|
|
2338
2947
|
process.exit(1);
|
|
2339
2948
|
}
|
|
2340
2949
|
const result = await installSkill(
|
|
@@ -2372,14 +2981,14 @@ function registerSkillsInstall(parent) {
|
|
|
2372
2981
|
if (format === "json") {
|
|
2373
2982
|
outputSuccess(operation, mvi, summary);
|
|
2374
2983
|
} else {
|
|
2375
|
-
console.log(
|
|
2376
|
-
\u2713 Installed ${
|
|
2377
|
-
console.log(` Canonical: ${
|
|
2984
|
+
console.log(pc12.green(`
|
|
2985
|
+
\u2713 Installed ${pc12.bold(skillName)}`));
|
|
2986
|
+
console.log(` Canonical: ${pc12.dim(result.canonicalPath)}`);
|
|
2378
2987
|
console.log(` Linked to: ${result.linkedAgents.join(", ")}`);
|
|
2379
2988
|
if (result.errors.length > 0) {
|
|
2380
|
-
console.log(
|
|
2989
|
+
console.log(pc12.yellow("\nWarnings:"));
|
|
2381
2990
|
for (const err of result.errors) {
|
|
2382
|
-
console.log(` ${
|
|
2991
|
+
console.log(` ${pc12.yellow("!")} ${err}`);
|
|
2383
2992
|
}
|
|
2384
2993
|
}
|
|
2385
2994
|
}
|
|
@@ -2407,11 +3016,11 @@ function registerSkillsInstall(parent) {
|
|
|
2407
3016
|
});
|
|
2408
3017
|
console.error(JSON.stringify(envelope, null, 2));
|
|
2409
3018
|
} else {
|
|
2410
|
-
console.log(
|
|
2411
|
-
\u2717 Failed to install ${
|
|
2412
|
-
console.log(
|
|
3019
|
+
console.log(pc12.yellow(`
|
|
3020
|
+
\u2717 Failed to install ${pc12.bold(skillName)}`));
|
|
3021
|
+
console.log(pc12.yellow("Errors:"));
|
|
2413
3022
|
for (const err of result.errors) {
|
|
2414
|
-
console.log(` ${
|
|
3023
|
+
console.log(` ${pc12.yellow("!")} ${err}`);
|
|
2415
3024
|
}
|
|
2416
3025
|
}
|
|
2417
3026
|
process.exit(1);
|
|
@@ -2427,7 +3036,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
2427
3036
|
if (format === "json") {
|
|
2428
3037
|
emitError2(operation, mvi, ErrorCodes.INVALID_INPUT, message, ErrorCategories.VALIDATION);
|
|
2429
3038
|
}
|
|
2430
|
-
console.error(
|
|
3039
|
+
console.error(pc12.red(message));
|
|
2431
3040
|
process.exit(1);
|
|
2432
3041
|
}
|
|
2433
3042
|
const profileSkills = resolveProfile(profileName);
|
|
@@ -2438,16 +3047,16 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
2438
3047
|
availableProfiles: listProfiles()
|
|
2439
3048
|
});
|
|
2440
3049
|
}
|
|
2441
|
-
console.error(
|
|
3050
|
+
console.error(pc12.red(message));
|
|
2442
3051
|
const available = listProfiles();
|
|
2443
3052
|
if (available.length > 0) {
|
|
2444
|
-
console.log(
|
|
3053
|
+
console.log(pc12.dim("Available profiles: " + available.join(", ")));
|
|
2445
3054
|
}
|
|
2446
3055
|
process.exit(1);
|
|
2447
3056
|
}
|
|
2448
3057
|
if (format === "human") {
|
|
2449
|
-
console.log(`Installing profile ${
|
|
2450
|
-
console.log(
|
|
3058
|
+
console.log(`Installing profile ${pc12.bold(profileName)} (${profileSkills.length} skill(s))...`);
|
|
3059
|
+
console.log(pc12.dim(`Target: ${providers.length} provider(s)`));
|
|
2451
3060
|
}
|
|
2452
3061
|
const installed = [];
|
|
2453
3062
|
const failed = [];
|
|
@@ -2462,7 +3071,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
2462
3071
|
);
|
|
2463
3072
|
if (result.success) {
|
|
2464
3073
|
if (format === "human") {
|
|
2465
|
-
console.log(
|
|
3074
|
+
console.log(pc12.green(` + ${name}`));
|
|
2466
3075
|
}
|
|
2467
3076
|
await recordSkillInstall(
|
|
2468
3077
|
name,
|
|
@@ -2481,7 +3090,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
2481
3090
|
});
|
|
2482
3091
|
} else {
|
|
2483
3092
|
if (format === "human") {
|
|
2484
|
-
console.log(
|
|
3093
|
+
console.log(pc12.yellow(` ! ${name}: ${result.errors.join(", ")}`));
|
|
2485
3094
|
}
|
|
2486
3095
|
failed.push({
|
|
2487
3096
|
name,
|
|
@@ -2491,7 +3100,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
2491
3100
|
} catch (err) {
|
|
2492
3101
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
2493
3102
|
if (format === "human") {
|
|
2494
|
-
console.log(
|
|
3103
|
+
console.log(pc12.red(` x ${name}: ${errorMsg}`));
|
|
2495
3104
|
}
|
|
2496
3105
|
failed.push({
|
|
2497
3106
|
name,
|
|
@@ -2525,7 +3134,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
|
|
|
2525
3134
|
}
|
|
2526
3135
|
} else {
|
|
2527
3136
|
console.log(`
|
|
2528
|
-
${
|
|
3137
|
+
${pc12.green(`${installed.length} installed`)}, ${failed.length > 0 ? pc12.yellow(`${failed.length} failed`) : "0 failed"}`);
|
|
2529
3138
|
if (failed.length > 0) {
|
|
2530
3139
|
process.exit(1);
|
|
2531
3140
|
}
|
|
@@ -2533,7 +3142,7 @@ ${pc11.green(`${installed.length} installed`)}, ${failed.length > 0 ? pc11.yello
|
|
|
2533
3142
|
}
|
|
2534
3143
|
async function handleMarketplaceSource(source, _providers, _isGlobal, format, operation, mvi) {
|
|
2535
3144
|
if (format === "human") {
|
|
2536
|
-
console.log(
|
|
3145
|
+
console.log(pc12.dim(`Searching marketplace for ${source}...`));
|
|
2537
3146
|
}
|
|
2538
3147
|
const client = new MarketplaceClient();
|
|
2539
3148
|
let skill;
|
|
@@ -2544,7 +3153,7 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
|
|
|
2544
3153
|
if (format === "json") {
|
|
2545
3154
|
emitJsonError(operation, mvi, ErrorCodes.NETWORK_ERROR, message, ErrorCategories.TRANSIENT);
|
|
2546
3155
|
}
|
|
2547
|
-
console.error(
|
|
3156
|
+
console.error(pc12.red(message));
|
|
2548
3157
|
return { success: false };
|
|
2549
3158
|
}
|
|
2550
3159
|
if (!skill) {
|
|
@@ -2552,11 +3161,11 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
|
|
|
2552
3161
|
if (format === "json") {
|
|
2553
3162
|
emitJsonError(operation, mvi, ErrorCodes.SKILL_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
|
|
2554
3163
|
}
|
|
2555
|
-
console.error(
|
|
3164
|
+
console.error(pc12.red(message));
|
|
2556
3165
|
return { success: false };
|
|
2557
3166
|
}
|
|
2558
3167
|
if (format === "human") {
|
|
2559
|
-
console.log(` Found: ${
|
|
3168
|
+
console.log(` Found: ${pc12.bold(skill.name)} by ${skill.author} (${pc12.dim(skill.repoFullName)})`);
|
|
2560
3169
|
}
|
|
2561
3170
|
const parsed = parseSource(skill.githubUrl);
|
|
2562
3171
|
if (parsed.type !== "github" || !parsed.owner || !parsed.repo) {
|
|
@@ -2564,7 +3173,7 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
|
|
|
2564
3173
|
if (format === "json") {
|
|
2565
3174
|
emitJsonError(operation, mvi, ErrorCodes.INVALID_FORMAT, message, ErrorCategories.VALIDATION);
|
|
2566
3175
|
}
|
|
2567
|
-
console.error(
|
|
3176
|
+
console.error(pc12.red(message));
|
|
2568
3177
|
return { success: false };
|
|
2569
3178
|
}
|
|
2570
3179
|
try {
|
|
@@ -2576,7 +3185,7 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
|
|
|
2576
3185
|
for (const subPath of subPathCandidates) {
|
|
2577
3186
|
try {
|
|
2578
3187
|
const result = await cloneRepo(parsed.owner, parsed.repo, parsed.ref, subPath);
|
|
2579
|
-
if (subPath && !
|
|
3188
|
+
if (subPath && !existsSync5(result.localPath)) {
|
|
2580
3189
|
await result.cleanup();
|
|
2581
3190
|
continue;
|
|
2582
3191
|
}
|
|
@@ -2604,13 +3213,13 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
|
|
|
2604
3213
|
if (format === "json") {
|
|
2605
3214
|
emitJsonError(operation, mvi, ErrorCodes.NETWORK_ERROR, message, ErrorCategories.TRANSIENT);
|
|
2606
3215
|
}
|
|
2607
|
-
console.error(
|
|
3216
|
+
console.error(pc12.red(message));
|
|
2608
3217
|
return { success: false };
|
|
2609
3218
|
}
|
|
2610
3219
|
}
|
|
2611
3220
|
|
|
2612
3221
|
// src/commands/skills/remove.ts
|
|
2613
|
-
import
|
|
3222
|
+
import pc13 from "picocolors";
|
|
2614
3223
|
function registerSkillsRemove(parent) {
|
|
2615
3224
|
parent.command("remove").description("Remove installed skill(s)").argument("[name]", "Skill name to remove").option("-g, --global", "Remove from global scope").option("-y, --yes", "Skip confirmation").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (name, opts) => {
|
|
2616
3225
|
const operation = "skills.remove";
|
|
@@ -2649,14 +3258,14 @@ function registerSkillsRemove(parent) {
|
|
|
2649
3258
|
return;
|
|
2650
3259
|
}
|
|
2651
3260
|
if (removed.length > 0) {
|
|
2652
|
-
console.log(
|
|
3261
|
+
console.log(pc13.green(`\u2713 Removed ${pc13.bold(name)} from: ${removed.join(", ")}`));
|
|
2653
3262
|
await removeSkillFromLock(name);
|
|
2654
3263
|
} else {
|
|
2655
|
-
console.log(
|
|
3264
|
+
console.log(pc13.yellow(`Skill ${name} not found in any provider.`));
|
|
2656
3265
|
}
|
|
2657
3266
|
if (result.errors.length > 0) {
|
|
2658
3267
|
for (const err of result.errors) {
|
|
2659
|
-
console.log(
|
|
3268
|
+
console.log(pc13.red(` ${err}`));
|
|
2660
3269
|
}
|
|
2661
3270
|
}
|
|
2662
3271
|
} else {
|
|
@@ -2669,7 +3278,7 @@ function registerSkillsRemove(parent) {
|
|
|
2669
3278
|
count: { removed: 0, total: 0 }
|
|
2670
3279
|
});
|
|
2671
3280
|
} else {
|
|
2672
|
-
console.log(
|
|
3281
|
+
console.log(pc13.dim("No skills installed."));
|
|
2673
3282
|
}
|
|
2674
3283
|
return;
|
|
2675
3284
|
}
|
|
@@ -2682,11 +3291,11 @@ function registerSkillsRemove(parent) {
|
|
|
2682
3291
|
});
|
|
2683
3292
|
return;
|
|
2684
3293
|
}
|
|
2685
|
-
console.log(
|
|
3294
|
+
console.log(pc13.bold("Installed skills:"));
|
|
2686
3295
|
for (const s of skills) {
|
|
2687
3296
|
console.log(` ${s}`);
|
|
2688
3297
|
}
|
|
2689
|
-
console.log(
|
|
3298
|
+
console.log(pc13.dim("\nUse: caamp skills remove <name>"));
|
|
2690
3299
|
}
|
|
2691
3300
|
});
|
|
2692
3301
|
}
|
|
@@ -2694,7 +3303,7 @@ function registerSkillsRemove(parent) {
|
|
|
2694
3303
|
// src/commands/skills/list.ts
|
|
2695
3304
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
2696
3305
|
import { resolveOutputFormat as resolveOutputFormat3 } from "@cleocode/lafs-protocol";
|
|
2697
|
-
import
|
|
3306
|
+
import pc14 from "picocolors";
|
|
2698
3307
|
function registerSkillsList(parent) {
|
|
2699
3308
|
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) => {
|
|
2700
3309
|
const operation = "skills.list";
|
|
@@ -2721,7 +3330,7 @@ function registerSkillsList(parent) {
|
|
|
2721
3330
|
agent: opts.agent
|
|
2722
3331
|
});
|
|
2723
3332
|
} else {
|
|
2724
|
-
console.error(
|
|
3333
|
+
console.error(pc14.red(message));
|
|
2725
3334
|
}
|
|
2726
3335
|
process.exit(1);
|
|
2727
3336
|
}
|
|
@@ -2749,19 +3358,19 @@ function registerSkillsList(parent) {
|
|
|
2749
3358
|
return;
|
|
2750
3359
|
}
|
|
2751
3360
|
if (skills.length === 0) {
|
|
2752
|
-
console.log(
|
|
3361
|
+
console.log(pc14.dim("No skills found."));
|
|
2753
3362
|
return;
|
|
2754
3363
|
}
|
|
2755
|
-
console.log(
|
|
3364
|
+
console.log(pc14.bold(`
|
|
2756
3365
|
${skills.length} skill(s) found:
|
|
2757
3366
|
`));
|
|
2758
3367
|
skills.forEach((skill, index) => {
|
|
2759
3368
|
const num = (index + 1).toString().padStart(2);
|
|
2760
|
-
console.log(` ${
|
|
3369
|
+
console.log(` ${pc14.cyan(num)}. ${pc14.bold(skill.name.padEnd(30))} ${pc14.dim(skill.metadata?.description ?? "")}`);
|
|
2761
3370
|
});
|
|
2762
|
-
console.log(
|
|
3371
|
+
console.log(pc14.dim(`
|
|
2763
3372
|
Install with: caamp skills install <name>`));
|
|
2764
|
-
console.log(
|
|
3373
|
+
console.log(pc14.dim(`Remove with: caamp skills remove <name>`));
|
|
2765
3374
|
});
|
|
2766
3375
|
}
|
|
2767
3376
|
function buildEnvelope5(operation, mvi, result, error) {
|
|
@@ -2801,7 +3410,7 @@ import { randomUUID as randomUUID5 } from "crypto";
|
|
|
2801
3410
|
import {
|
|
2802
3411
|
resolveOutputFormat as resolveOutputFormat4
|
|
2803
3412
|
} from "@cleocode/lafs-protocol";
|
|
2804
|
-
import
|
|
3413
|
+
import pc15 from "picocolors";
|
|
2805
3414
|
var SkillsFindValidationError = class extends Error {
|
|
2806
3415
|
code;
|
|
2807
3416
|
constructor(code, message) {
|
|
@@ -2827,7 +3436,7 @@ function registerSkillsFind(parent) {
|
|
|
2827
3436
|
if (opts.json) {
|
|
2828
3437
|
emitJsonError4(operation, mvi, "E_FORMAT_CONFLICT", message, "VALIDATION");
|
|
2829
3438
|
} else {
|
|
2830
|
-
console.error(
|
|
3439
|
+
console.error(pc15.red(message));
|
|
2831
3440
|
}
|
|
2832
3441
|
process.exit(1);
|
|
2833
3442
|
}
|
|
@@ -2888,19 +3497,19 @@ function registerSkillsFind(parent) {
|
|
|
2888
3497
|
query: query ?? null
|
|
2889
3498
|
});
|
|
2890
3499
|
} else {
|
|
2891
|
-
console.error(
|
|
3500
|
+
console.error(pc15.red(`Recommendation failed: ${message}`));
|
|
2892
3501
|
}
|
|
2893
3502
|
process.exit(1);
|
|
2894
3503
|
}
|
|
2895
3504
|
}
|
|
2896
3505
|
if (!query) {
|
|
2897
|
-
console.log(
|
|
3506
|
+
console.log(pc15.dim("Usage: caamp skills find <query>"));
|
|
2898
3507
|
return;
|
|
2899
3508
|
}
|
|
2900
3509
|
const limit = parseInt(opts.limit, 10);
|
|
2901
3510
|
const client = new MarketplaceClient();
|
|
2902
3511
|
if (format === "human") {
|
|
2903
|
-
console.log(
|
|
3512
|
+
console.log(pc15.dim(`Searching marketplaces for "${query}"...
|
|
2904
3513
|
`));
|
|
2905
3514
|
}
|
|
2906
3515
|
let results;
|
|
@@ -2914,7 +3523,7 @@ function registerSkillsFind(parent) {
|
|
|
2914
3523
|
limit
|
|
2915
3524
|
});
|
|
2916
3525
|
} else {
|
|
2917
|
-
console.error(
|
|
3526
|
+
console.error(pc15.red(`Marketplace search failed: ${message}`));
|
|
2918
3527
|
}
|
|
2919
3528
|
process.exit(1);
|
|
2920
3529
|
}
|
|
@@ -2934,21 +3543,21 @@ function registerSkillsFind(parent) {
|
|
|
2934
3543
|
return;
|
|
2935
3544
|
}
|
|
2936
3545
|
if (results.length === 0) {
|
|
2937
|
-
console.log(
|
|
3546
|
+
console.log(pc15.yellow("No results found."));
|
|
2938
3547
|
return;
|
|
2939
3548
|
}
|
|
2940
|
-
console.log(
|
|
3549
|
+
console.log(pc15.dim(`Found ${results.length} result(s) for "${query}":
|
|
2941
3550
|
`));
|
|
2942
3551
|
results.forEach((skill, index) => {
|
|
2943
3552
|
const num = (index + 1).toString().padStart(2);
|
|
2944
|
-
const stars = skill.stars > 0 ?
|
|
2945
|
-
console.log(` ${
|
|
2946
|
-
console.log(` ${
|
|
2947
|
-
console.log(` ${
|
|
3553
|
+
const stars = skill.stars > 0 ? pc15.yellow(`\u2605 ${formatStars(skill.stars)}`) : "";
|
|
3554
|
+
console.log(` ${pc15.cyan(num)}. ${pc15.bold(skill.scopedName)} ${stars}`);
|
|
3555
|
+
console.log(` ${pc15.dim(skill.description?.slice(0, 80) ?? "")}`);
|
|
3556
|
+
console.log(` ${pc15.dim(`from ${skill.source}`)}`);
|
|
2948
3557
|
console.log();
|
|
2949
3558
|
});
|
|
2950
|
-
console.log(
|
|
2951
|
-
console.log(
|
|
3559
|
+
console.log(pc15.dim("Install with: caamp skills install <name>"));
|
|
3560
|
+
console.log(pc15.dim("Or select by number: caamp skills install <n>"));
|
|
2952
3561
|
});
|
|
2953
3562
|
}
|
|
2954
3563
|
function formatStars(n) {
|
|
@@ -3062,7 +3671,7 @@ function emitJsonError4(operation, mvi, code, message, category, details = {}) {
|
|
|
3062
3671
|
}
|
|
3063
3672
|
|
|
3064
3673
|
// src/commands/skills/check.ts
|
|
3065
|
-
import
|
|
3674
|
+
import pc16 from "picocolors";
|
|
3066
3675
|
function registerSkillsCheck(parent) {
|
|
3067
3676
|
parent.command("check").description("Check for available skill updates").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
3068
3677
|
const operation = "skills.check";
|
|
@@ -3089,12 +3698,12 @@ function registerSkillsCheck(parent) {
|
|
|
3089
3698
|
total: 0
|
|
3090
3699
|
});
|
|
3091
3700
|
} else {
|
|
3092
|
-
console.log(
|
|
3701
|
+
console.log(pc16.dim("No tracked skills."));
|
|
3093
3702
|
}
|
|
3094
3703
|
return;
|
|
3095
3704
|
}
|
|
3096
3705
|
if (format === "human") {
|
|
3097
|
-
console.log(
|
|
3706
|
+
console.log(pc16.dim(`Checking ${entries.length} skill(s) for updates...
|
|
3098
3707
|
`));
|
|
3099
3708
|
}
|
|
3100
3709
|
const skillResults = [];
|
|
@@ -3130,37 +3739,37 @@ function registerSkillsCheck(parent) {
|
|
|
3130
3739
|
for (const r of skillResults) {
|
|
3131
3740
|
let statusLabel;
|
|
3132
3741
|
if (r.hasUpdate) {
|
|
3133
|
-
statusLabel =
|
|
3742
|
+
statusLabel = pc16.yellow("update available");
|
|
3134
3743
|
} else if (r.currentVersion !== "unknown") {
|
|
3135
|
-
statusLabel =
|
|
3744
|
+
statusLabel = pc16.green("up to date");
|
|
3136
3745
|
} else {
|
|
3137
|
-
statusLabel =
|
|
3746
|
+
statusLabel = pc16.dim("unknown");
|
|
3138
3747
|
}
|
|
3139
|
-
console.log(` ${
|
|
3748
|
+
console.log(` ${pc16.bold(r.name.padEnd(30))} ${statusLabel}`);
|
|
3140
3749
|
if (r.currentVersion !== "unknown" || r.latestVersion !== "unknown") {
|
|
3141
3750
|
const current = r.currentVersion !== "unknown" ? r.currentVersion.slice(0, 12) : "?";
|
|
3142
3751
|
const latest = r.latestVersion !== "unknown" ? r.latestVersion : "?";
|
|
3143
3752
|
if (r.hasUpdate) {
|
|
3144
|
-
console.log(` ${
|
|
3753
|
+
console.log(` ${pc16.dim("current:")} ${current} ${pc16.dim("->")} ${pc16.cyan(latest)}`);
|
|
3145
3754
|
} else {
|
|
3146
|
-
console.log(` ${
|
|
3755
|
+
console.log(` ${pc16.dim("version:")} ${current}`);
|
|
3147
3756
|
}
|
|
3148
3757
|
}
|
|
3149
|
-
console.log(` ${
|
|
3150
|
-
console.log(` ${
|
|
3758
|
+
console.log(` ${pc16.dim(`source: ${r.source}`)}`);
|
|
3759
|
+
console.log(` ${pc16.dim(`agents: ${r.agents.join(", ")}`)}`);
|
|
3151
3760
|
console.log();
|
|
3152
3761
|
}
|
|
3153
3762
|
if (updatesAvailable > 0) {
|
|
3154
|
-
console.log(
|
|
3155
|
-
console.log(
|
|
3763
|
+
console.log(pc16.yellow(`${updatesAvailable} update(s) available.`));
|
|
3764
|
+
console.log(pc16.dim("Run `caamp skills update` to update all."));
|
|
3156
3765
|
} else {
|
|
3157
|
-
console.log(
|
|
3766
|
+
console.log(pc16.green("All skills are up to date."));
|
|
3158
3767
|
}
|
|
3159
3768
|
});
|
|
3160
3769
|
}
|
|
3161
3770
|
|
|
3162
3771
|
// src/commands/skills/update.ts
|
|
3163
|
-
import
|
|
3772
|
+
import pc17 from "picocolors";
|
|
3164
3773
|
function registerSkillsUpdate(parent) {
|
|
3165
3774
|
parent.command("update").description("Update all outdated skills").option("-y, --yes", "Skip confirmation").option("--json", "Output as JSON (default)").option("--human", "Output in human-readable format").action(async (opts) => {
|
|
3166
3775
|
const operation = "skills.update";
|
|
@@ -3188,12 +3797,12 @@ function registerSkillsUpdate(parent) {
|
|
|
3188
3797
|
count: { updated: 0, failed: 0, skipped: 0 }
|
|
3189
3798
|
});
|
|
3190
3799
|
} else {
|
|
3191
|
-
console.log(
|
|
3800
|
+
console.log(pc17.dim("No tracked skills to update."));
|
|
3192
3801
|
}
|
|
3193
3802
|
return;
|
|
3194
3803
|
}
|
|
3195
3804
|
if (format === "human") {
|
|
3196
|
-
console.log(
|
|
3805
|
+
console.log(pc17.dim(`Checking ${entries.length} skill(s) for updates...`));
|
|
3197
3806
|
}
|
|
3198
3807
|
const outdated = [];
|
|
3199
3808
|
for (const [name] of entries) {
|
|
@@ -3215,29 +3824,29 @@ function registerSkillsUpdate(parent) {
|
|
|
3215
3824
|
count: { updated: 0, failed: 0, skipped: 0 }
|
|
3216
3825
|
});
|
|
3217
3826
|
} else {
|
|
3218
|
-
console.log(
|
|
3827
|
+
console.log(pc17.green("\nAll skills are up to date."));
|
|
3219
3828
|
}
|
|
3220
3829
|
return;
|
|
3221
3830
|
}
|
|
3222
3831
|
if (format === "human") {
|
|
3223
|
-
console.log(
|
|
3832
|
+
console.log(pc17.yellow(`
|
|
3224
3833
|
${outdated.length} skill(s) have updates available:
|
|
3225
3834
|
`));
|
|
3226
3835
|
for (const skill of outdated) {
|
|
3227
3836
|
const current = skill.currentVersion?.slice(0, 12) ?? "?";
|
|
3228
3837
|
const latest = skill.latestVersion ?? "?";
|
|
3229
|
-
console.log(` ${
|
|
3838
|
+
console.log(` ${pc17.bold(skill.name)} ${pc17.dim(current)} ${pc17.dim("->")} ${pc17.cyan(latest)}`);
|
|
3230
3839
|
}
|
|
3231
3840
|
}
|
|
3232
3841
|
if (!opts.yes && format === "human") {
|
|
3233
3842
|
const readline = await import("readline");
|
|
3234
3843
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
3235
|
-
const answer = await new Promise((
|
|
3236
|
-
rl.question(
|
|
3844
|
+
const answer = await new Promise((resolve2) => {
|
|
3845
|
+
rl.question(pc17.dim("\nProceed with update? [y/N] "), resolve2);
|
|
3237
3846
|
});
|
|
3238
3847
|
rl.close();
|
|
3239
3848
|
if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
|
|
3240
|
-
console.log(
|
|
3849
|
+
console.log(pc17.dim("Update cancelled."));
|
|
3241
3850
|
return;
|
|
3242
3851
|
}
|
|
3243
3852
|
}
|
|
@@ -3251,7 +3860,7 @@ ${outdated.length} skill(s) have updates available:
|
|
|
3251
3860
|
const entry = tracked[skill.name];
|
|
3252
3861
|
if (!entry) continue;
|
|
3253
3862
|
if (format === "human") {
|
|
3254
|
-
console.log(
|
|
3863
|
+
console.log(pc17.dim(`Updating ${pc17.bold(skill.name)}...`));
|
|
3255
3864
|
}
|
|
3256
3865
|
try {
|
|
3257
3866
|
const parsed = parseSource(entry.source);
|
|
@@ -3267,7 +3876,7 @@ ${outdated.length} skill(s) have updates available:
|
|
|
3267
3876
|
cleanup = result.cleanup;
|
|
3268
3877
|
} else {
|
|
3269
3878
|
if (format === "human") {
|
|
3270
|
-
console.log(
|
|
3879
|
+
console.log(pc17.yellow(` Skipped ${skill.name}: source type "${parsed.type}" does not support auto-update`));
|
|
3271
3880
|
}
|
|
3272
3881
|
skipped.push(skill.name);
|
|
3273
3882
|
continue;
|
|
@@ -3276,7 +3885,7 @@ ${outdated.length} skill(s) have updates available:
|
|
|
3276
3885
|
const providers = entry.agents.map((a) => getProvider(a)).filter((p) => p !== void 0);
|
|
3277
3886
|
if (providers.length === 0) {
|
|
3278
3887
|
if (format === "human") {
|
|
3279
|
-
console.log(
|
|
3888
|
+
console.log(pc17.yellow(` Skipped ${skill.name}: no valid providers found`));
|
|
3280
3889
|
}
|
|
3281
3890
|
skipped.push(skill.name);
|
|
3282
3891
|
continue;
|
|
@@ -3301,18 +3910,18 @@ ${outdated.length} skill(s) have updates available:
|
|
|
3301
3910
|
skill.latestVersion
|
|
3302
3911
|
);
|
|
3303
3912
|
if (format === "human") {
|
|
3304
|
-
console.log(
|
|
3913
|
+
console.log(pc17.green(` Updated ${pc17.bold(skill.name)}`));
|
|
3305
3914
|
}
|
|
3306
3915
|
updated.push(skill.name);
|
|
3307
3916
|
} else {
|
|
3308
3917
|
if (format === "human") {
|
|
3309
|
-
console.log(
|
|
3918
|
+
console.log(pc17.red(` Failed to update ${skill.name}: no agents linked`));
|
|
3310
3919
|
}
|
|
3311
3920
|
failed.push({ name: skill.name, error: "no agents linked" });
|
|
3312
3921
|
}
|
|
3313
3922
|
if (installResult.errors.length > 0 && format === "human") {
|
|
3314
3923
|
for (const err of installResult.errors) {
|
|
3315
|
-
console.log(
|
|
3924
|
+
console.log(pc17.yellow(` ${err}`));
|
|
3316
3925
|
}
|
|
3317
3926
|
}
|
|
3318
3927
|
} finally {
|
|
@@ -3321,7 +3930,7 @@ ${outdated.length} skill(s) have updates available:
|
|
|
3321
3930
|
} catch (err) {
|
|
3322
3931
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3323
3932
|
if (format === "human") {
|
|
3324
|
-
console.log(
|
|
3933
|
+
console.log(pc17.red(` Failed to update ${skill.name}: ${msg}`));
|
|
3325
3934
|
}
|
|
3326
3935
|
failed.push({ name: skill.name, error: msg });
|
|
3327
3936
|
}
|
|
@@ -3341,19 +3950,19 @@ ${outdated.length} skill(s) have updates available:
|
|
|
3341
3950
|
}
|
|
3342
3951
|
console.log();
|
|
3343
3952
|
if (updated.length > 0) {
|
|
3344
|
-
console.log(
|
|
3953
|
+
console.log(pc17.green(`Updated ${updated.length} skill(s).`));
|
|
3345
3954
|
}
|
|
3346
3955
|
if (failed.length > 0) {
|
|
3347
|
-
console.log(
|
|
3956
|
+
console.log(pc17.red(`Failed to update ${failed.length} skill(s).`));
|
|
3348
3957
|
}
|
|
3349
3958
|
});
|
|
3350
3959
|
}
|
|
3351
3960
|
|
|
3352
3961
|
// src/commands/skills/init.ts
|
|
3353
|
-
import { existsSync as
|
|
3962
|
+
import { existsSync as existsSync6 } from "fs";
|
|
3354
3963
|
import { mkdir, writeFile } from "fs/promises";
|
|
3355
3964
|
import { join as join5 } from "path";
|
|
3356
|
-
import
|
|
3965
|
+
import pc18 from "picocolors";
|
|
3357
3966
|
function registerSkillsInit(parent) {
|
|
3358
3967
|
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(async (name, opts) => {
|
|
3359
3968
|
const operation = "skills.init";
|
|
@@ -3372,14 +3981,14 @@ function registerSkillsInit(parent) {
|
|
|
3372
3981
|
}
|
|
3373
3982
|
const skillName = name ?? "my-skill";
|
|
3374
3983
|
const skillDir = join5(opts.dir, skillName);
|
|
3375
|
-
if (
|
|
3984
|
+
if (existsSync6(skillDir)) {
|
|
3376
3985
|
const message = `Directory already exists: ${skillDir}`;
|
|
3377
3986
|
if (format === "json") {
|
|
3378
3987
|
emitJsonError(operation, mvi, ErrorCodes.INVALID_CONSTRAINT, message, ErrorCategories.CONFLICT, {
|
|
3379
3988
|
path: skillDir
|
|
3380
3989
|
});
|
|
3381
3990
|
} else {
|
|
3382
|
-
console.error(
|
|
3991
|
+
console.error(pc18.red(message));
|
|
3383
3992
|
}
|
|
3384
3993
|
process.exit(1);
|
|
3385
3994
|
}
|
|
@@ -3418,22 +4027,22 @@ Show example inputs and expected outputs.
|
|
|
3418
4027
|
outputSuccess(operation, mvi, result);
|
|
3419
4028
|
return;
|
|
3420
4029
|
}
|
|
3421
|
-
console.log(
|
|
3422
|
-
console.log(
|
|
3423
|
-
console.log(
|
|
3424
|
-
console.log(
|
|
3425
|
-
console.log(
|
|
4030
|
+
console.log(pc18.green(`\u2713 Created skill template: ${skillDir}/SKILL.md`));
|
|
4031
|
+
console.log(pc18.dim("\nNext steps:"));
|
|
4032
|
+
console.log(pc18.dim(" 1. Edit SKILL.md with your instructions"));
|
|
4033
|
+
console.log(pc18.dim(` 2. Validate: caamp skills validate ${join5(skillDir, "SKILL.md")}`));
|
|
4034
|
+
console.log(pc18.dim(` 3. Install: caamp skills install ${skillDir}`));
|
|
3426
4035
|
});
|
|
3427
4036
|
}
|
|
3428
4037
|
|
|
3429
4038
|
// src/commands/skills/audit.ts
|
|
3430
|
-
import { existsSync as
|
|
3431
|
-
import
|
|
4039
|
+
import { existsSync as existsSync7, statSync } from "fs";
|
|
4040
|
+
import pc19 from "picocolors";
|
|
3432
4041
|
function registerSkillsAudit(parent) {
|
|
3433
4042
|
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 (raw SARIF, not LAFS envelope)").option("--json", "Output as JSON (LAFS envelope)").option("--human", "Output in human-readable format").action(async (path, opts) => {
|
|
3434
4043
|
const operation = "skills.audit";
|
|
3435
4044
|
const mvi = "standard";
|
|
3436
|
-
if (!
|
|
4045
|
+
if (!existsSync7(path)) {
|
|
3437
4046
|
const message = `Path not found: ${path}`;
|
|
3438
4047
|
if (opts.sarif) {
|
|
3439
4048
|
console.error(JSON.stringify({
|
|
@@ -3517,7 +4126,7 @@ function registerSkillsAudit(parent) {
|
|
|
3517
4126
|
outputSuccess(operation, mvi, summary2);
|
|
3518
4127
|
return;
|
|
3519
4128
|
}
|
|
3520
|
-
console.log(
|
|
4129
|
+
console.log(pc19.dim("No SKILL.md files found to scan."));
|
|
3521
4130
|
return;
|
|
3522
4131
|
}
|
|
3523
4132
|
const summary = {
|
|
@@ -3551,21 +4160,21 @@ function registerSkillsAudit(parent) {
|
|
|
3551
4160
|
}
|
|
3552
4161
|
let totalFindings = 0;
|
|
3553
4162
|
for (const result of results) {
|
|
3554
|
-
const icon = result.passed ?
|
|
4163
|
+
const icon = result.passed ? pc19.green("\u2713") : pc19.red("\u2717");
|
|
3555
4164
|
console.log(`
|
|
3556
|
-
${icon} ${
|
|
4165
|
+
${icon} ${pc19.bold(result.file)} (score: ${result.score}/100)`);
|
|
3557
4166
|
if (result.findings.length === 0) {
|
|
3558
|
-
console.log(
|
|
4167
|
+
console.log(pc19.dim(" No issues found."));
|
|
3559
4168
|
continue;
|
|
3560
4169
|
}
|
|
3561
4170
|
totalFindings += result.findings.length;
|
|
3562
4171
|
for (const f of result.findings) {
|
|
3563
|
-
const sev = f.rule.severity === "critical" ?
|
|
4172
|
+
const sev = f.rule.severity === "critical" ? pc19.red(f.rule.severity) : f.rule.severity === "high" ? pc19.red(f.rule.severity) : f.rule.severity === "medium" ? pc19.yellow(f.rule.severity) : pc19.dim(f.rule.severity);
|
|
3564
4173
|
console.log(` ${sev.padEnd(20)} ${f.rule.id} ${f.rule.name}`);
|
|
3565
|
-
console.log(` ${
|
|
4174
|
+
console.log(` ${pc19.dim(`L${f.line}: ${f.context.slice(0, 80)}`)}`);
|
|
3566
4175
|
}
|
|
3567
4176
|
}
|
|
3568
|
-
console.log(
|
|
4177
|
+
console.log(pc19.bold(`
|
|
3569
4178
|
${results.length} file(s) scanned, ${totalFindings} finding(s)`));
|
|
3570
4179
|
if (!allPassed) {
|
|
3571
4180
|
process.exit(1);
|
|
@@ -3575,7 +4184,7 @@ ${results.length} file(s) scanned, ${totalFindings} finding(s)`));
|
|
|
3575
4184
|
|
|
3576
4185
|
// src/commands/skills/validate.ts
|
|
3577
4186
|
import { resolveOutputFormat as resolveOutputFormat5 } from "@cleocode/lafs-protocol";
|
|
3578
|
-
import
|
|
4187
|
+
import pc20 from "picocolors";
|
|
3579
4188
|
function registerSkillsValidate(parent) {
|
|
3580
4189
|
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) => {
|
|
3581
4190
|
const operation = "skills.validate";
|
|
@@ -3602,7 +4211,7 @@ function registerSkillsValidate(parent) {
|
|
|
3602
4211
|
path
|
|
3603
4212
|
});
|
|
3604
4213
|
} else {
|
|
3605
|
-
console.error(
|
|
4214
|
+
console.error(pc20.red(message));
|
|
3606
4215
|
}
|
|
3607
4216
|
process.exit(1);
|
|
3608
4217
|
}
|
|
@@ -3624,12 +4233,12 @@ function registerSkillsValidate(parent) {
|
|
|
3624
4233
|
console.log(JSON.stringify(envelope, null, 2));
|
|
3625
4234
|
} else {
|
|
3626
4235
|
if (result.valid) {
|
|
3627
|
-
console.log(
|
|
4236
|
+
console.log(pc20.green(`\u2713 ${path} is valid`));
|
|
3628
4237
|
} else {
|
|
3629
|
-
console.log(
|
|
4238
|
+
console.log(pc20.red(`\u2717 ${path} has validation errors`));
|
|
3630
4239
|
}
|
|
3631
4240
|
for (const issue of result.issues) {
|
|
3632
|
-
const icon = issue.level === "error" ?
|
|
4241
|
+
const icon = issue.level === "error" ? pc20.red("\u2717") : pc20.yellow("!");
|
|
3633
4242
|
console.log(` ${icon} [${issue.field}] ${issue.message}`);
|
|
3634
4243
|
}
|
|
3635
4244
|
}
|