@cleocode/caamp 1.1.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -4,9 +4,11 @@ import {
4
4
  MarketplaceClient,
5
5
  RECOMMENDATION_ERROR_CODES,
6
6
  applyMcpInstallWithPolicy,
7
+ buildCleoProfile,
7
8
  buildServerConfig,
8
9
  buildSkillSubPathCandidates,
9
10
  checkAllInjections,
11
+ checkCommandReachability,
10
12
  checkSkillUpdate,
11
13
  configureProviderGlobalAndProject,
12
14
  detectAllProviders,
@@ -40,6 +42,8 @@ import {
40
42
  listMcpServers,
41
43
  listProfiles,
42
44
  listSkills,
45
+ normalizeCleoChannel,
46
+ parseEnvAssignments,
43
47
  parseSource,
44
48
  readConfig,
45
49
  readLockFile,
@@ -50,6 +54,8 @@ import {
50
54
  removeMcpServer,
51
55
  removeSkill,
52
56
  removeSkillFromLock,
57
+ resolveChannelFromServerName,
58
+ resolveCleoServerName,
53
59
  resolveConfigPath,
54
60
  resolvePreferredConfigScope,
55
61
  resolveProfile,
@@ -65,7 +71,7 @@ import {
65
71
  tokenizeCriteriaValue,
66
72
  updateInstructionsSingleOperation,
67
73
  validateSkill
68
- } from "./chunk-7YV3KXEJ.js";
74
+ } from "./chunk-O2YG5HT7.js";
69
75
 
70
76
  // src/cli.ts
71
77
  import { Command } from "commander";
@@ -1601,10 +1607,559 @@ function registerInstructionsCommands(program2) {
1601
1607
  registerInstructionsUpdate(instructions);
1602
1608
  }
1603
1609
 
1604
- // src/commands/mcp/install.ts
1610
+ // src/commands/mcp/cleo.ts
1611
+ import { createInterface } from "readline/promises";
1605
1612
  import pc6 from "picocolors";
1613
+ function collect(value, previous) {
1614
+ return [...previous, value];
1615
+ }
1616
+ function collectTargetProviders(providerIds, all) {
1617
+ if (all) {
1618
+ return getInstalledProviders();
1619
+ }
1620
+ if (providerIds.length > 0) {
1621
+ return providerIds.map((id) => getProvider(id)).filter((provider) => provider !== void 0);
1622
+ }
1623
+ return getInstalledProviders();
1624
+ }
1625
+ async function validateProfile(provider, scope, serverName) {
1626
+ const entries = await listMcpServers(provider, scope);
1627
+ const entry = entries.find((candidate) => candidate.name === serverName);
1628
+ if (!entry) {
1629
+ return { valid: false, reason: "server missing after write" };
1630
+ }
1631
+ const command = typeof entry.config.command === "string" ? entry.config.command : void 0;
1632
+ if (!command) {
1633
+ return { valid: true };
1634
+ }
1635
+ const reachability = checkCommandReachability(command);
1636
+ if (!reachability.reachable) {
1637
+ return {
1638
+ valid: false,
1639
+ reason: `command not reachable (${reachability.method}: ${reachability.detail})`
1640
+ };
1641
+ }
1642
+ return { valid: true };
1643
+ }
1644
+ async function detectServerConflicts(providers, scope, targetServerName) {
1645
+ const warnings = [];
1646
+ for (const provider of providers) {
1647
+ const entries = await listMcpServers(provider, scope);
1648
+ const existing = entries.find((entry) => entry.name === targetServerName);
1649
+ if (!existing) continue;
1650
+ const command = typeof existing.config.command === "string" ? existing.config.command : "";
1651
+ const args = Array.isArray(existing.config.args) ? existing.config.args.filter((value) => typeof value === "string") : [];
1652
+ const flat = `${command} ${args.join(" ")}`.toLowerCase();
1653
+ if (!flat.includes("cleo")) {
1654
+ warnings.push({
1655
+ providerId: provider.id,
1656
+ message: `Server name '${targetServerName}' already exists with a non-CLEO command in ${provider.id}.`
1657
+ });
1658
+ }
1659
+ }
1660
+ return warnings;
1661
+ }
1662
+ function formatInstallResultHuman(mode, channel, serverName, scope, results, validations) {
1663
+ console.log(pc6.bold(`${mode === "install" ? "Install" : "Update"} CLEO channel: ${channel}`));
1664
+ console.log(pc6.dim(`Server: ${serverName} Scope: ${scope}`));
1665
+ console.log();
1666
+ for (const result of results) {
1667
+ const validation = validations.find((entry) => entry.providerId === result.provider.id);
1668
+ if (result.success) {
1669
+ const validationLabel = validation?.valid ? pc6.green("validated") : pc6.yellow(`validation warning: ${validation?.reason ?? "unknown"}`);
1670
+ console.log(` ${pc6.green("+")} ${result.provider.toolName.padEnd(22)} ${pc6.dim(result.configPath)} ${validationLabel}`);
1671
+ } else {
1672
+ console.log(` ${pc6.red("x")} ${result.provider.toolName.padEnd(22)} ${pc6.red(result.error ?? "failed")}`);
1673
+ console.log(pc6.dim(" Recovery: verify config path permissions and retry with --dry-run."));
1674
+ }
1675
+ }
1676
+ console.log();
1677
+ }
1678
+ async function runInteractiveInstall(opts) {
1679
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
1680
+ try {
1681
+ const discovered = getInstalledProviders();
1682
+ if (discovered.length === 0) {
1683
+ throw new Error("No installed providers were detected for interactive setup.");
1684
+ }
1685
+ console.log(pc6.bold("CLEO MCP Setup"));
1686
+ console.log(pc6.dim("Step 1/6 - Select provider(s)"));
1687
+ for (const [index, provider] of discovered.entries()) {
1688
+ console.log(` ${index + 1}. ${provider.id} (${provider.toolName})`);
1689
+ }
1690
+ const providerAnswer = await rl.question(pc6.dim("Choose providers (e.g. 1,2 or all): "));
1691
+ 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));
1692
+ if (selectedProviders.length === 0) {
1693
+ throw new Error("No providers selected.");
1694
+ }
1695
+ console.log();
1696
+ console.log(pc6.dim("Step 2/6 - Select channel"));
1697
+ const channelAnswer = await rl.question(pc6.dim("Channel [stable/beta/dev] (stable): "));
1698
+ const selectedChannel = normalizeCleoChannel(channelAnswer || "stable");
1699
+ let command = opts.command;
1700
+ let args = [...opts.arg];
1701
+ let env = [...opts.env];
1702
+ let cleoDir = opts.cleoDir;
1703
+ if (selectedChannel === "dev") {
1704
+ command = await rl.question(pc6.dim("Dev command (required): "));
1705
+ const argsAnswer = await rl.question(pc6.dim("Dev args (space-separated, optional): "));
1706
+ args = argsAnswer.trim() === "" ? [] : argsAnswer.trim().split(/\s+/);
1707
+ const dirAnswer = await rl.question(pc6.dim("CLEO_DIR (~/.cleo-dev default): "));
1708
+ cleoDir = dirAnswer.trim() === "" ? "~/.cleo-dev" : dirAnswer.trim();
1709
+ if (cleoDir.trim() !== "") {
1710
+ env = [
1711
+ ...env.filter((entry) => !entry.startsWith("CLEO_DIR=")),
1712
+ `CLEO_DIR=${cleoDir}`
1713
+ ];
1714
+ }
1715
+ }
1716
+ const profile = buildCleoProfile({
1717
+ channel: selectedChannel,
1718
+ version: opts.version,
1719
+ command,
1720
+ args,
1721
+ env: parseEnvAssignments(env),
1722
+ cleoDir
1723
+ });
1724
+ console.log();
1725
+ console.log(pc6.dim("Step 3/6 - Preview profile diff"));
1726
+ console.log(` Server: ${pc6.bold(profile.serverName)}`);
1727
+ console.log(` Channel: ${selectedChannel}`);
1728
+ console.log(` Config: ${JSON.stringify(profile.config)}`);
1729
+ console.log();
1730
+ console.log(pc6.dim("Step 4/6 - Confirm apply"));
1731
+ const confirm = await rl.question(pc6.dim("Apply this configuration? [y/N] "));
1732
+ if (!["y", "yes"].includes(confirm.trim().toLowerCase())) {
1733
+ throw new Error("Cancelled by user.");
1734
+ }
1735
+ return {
1736
+ ...opts,
1737
+ provider: selectedProviders,
1738
+ channel: selectedChannel,
1739
+ command,
1740
+ arg: args,
1741
+ env,
1742
+ cleoDir,
1743
+ yes: true
1744
+ };
1745
+ } finally {
1746
+ rl.close();
1747
+ }
1748
+ }
1749
+ async function executeCleoInstall(mode, opts, operation) {
1750
+ const mvi = "standard";
1751
+ let format;
1752
+ try {
1753
+ format = resolveFormat({
1754
+ jsonFlag: opts.json ?? false,
1755
+ humanFlag: (opts.human ?? false) || isHuman(),
1756
+ projectDefault: "json"
1757
+ });
1758
+ } catch (error) {
1759
+ const message = error instanceof Error ? error.message : String(error);
1760
+ emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
1761
+ process.exit(1);
1762
+ }
1763
+ const interactive = (opts.interactive ?? false) && format === "human";
1764
+ const resolvedOpts = interactive ? await runInteractiveInstall(opts) : opts;
1765
+ const channel = normalizeCleoChannel(resolvedOpts.channel);
1766
+ const providers = collectTargetProviders(resolvedOpts.provider, resolvedOpts.all);
1767
+ if (providers.length === 0) {
1768
+ const message = "No target providers found.";
1769
+ if (format === "json") {
1770
+ emitJsonError(operation, mvi, ErrorCodes.PROVIDER_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
1771
+ } else {
1772
+ console.error(pc6.red(message));
1773
+ }
1774
+ process.exit(1);
1775
+ }
1776
+ const envMap = parseEnvAssignments(resolvedOpts.env);
1777
+ const profile = buildCleoProfile({
1778
+ channel,
1779
+ version: resolvedOpts.version,
1780
+ command: resolvedOpts.command,
1781
+ args: resolvedOpts.arg,
1782
+ env: envMap,
1783
+ cleoDir: resolvedOpts.cleoDir
1784
+ });
1785
+ const scope = resolvedOpts.global ? "global" : "project";
1786
+ if (resolvedOpts.dryRun) {
1787
+ if (format === "human") {
1788
+ console.log(pc6.bold(`Dry run: ${mode} CLEO (${channel})`));
1789
+ console.log(pc6.dim(`Server: ${profile.serverName} Scope: ${scope}`));
1790
+ console.log(pc6.dim(`Providers: ${providers.map((provider) => provider.id).join(", ")}`));
1791
+ console.log(pc6.dim(`Command: ${profile.config.command ?? "(none)"} ${(profile.config.args ?? []).join(" ")}`));
1792
+ if (profile.config.env && Object.keys(profile.config.env).length > 0) {
1793
+ console.log(pc6.dim(`Env: ${JSON.stringify(profile.config.env)}`));
1794
+ }
1795
+ } else {
1796
+ outputSuccess(operation, mvi, {
1797
+ action: mode,
1798
+ channel,
1799
+ serverName: profile.serverName,
1800
+ providers: providers.map((provider) => provider.id),
1801
+ scope,
1802
+ command: profile.config.command,
1803
+ args: profile.config.args ?? [],
1804
+ env: profile.config.env ?? {},
1805
+ packageSpec: profile.packageSpec,
1806
+ dryRun: true
1807
+ });
1808
+ }
1809
+ return;
1810
+ }
1811
+ const conflictWarnings = await detectServerConflicts(providers, scope, profile.serverName);
1812
+ if (format === "human" && conflictWarnings.length > 0) {
1813
+ console.log(pc6.yellow("Warning: potential server name conflicts detected."));
1814
+ for (const warning of conflictWarnings) {
1815
+ console.log(pc6.yellow(` - ${warning.message}`));
1816
+ }
1817
+ console.log(pc6.dim("Recovery: run with --dry-run, inspect provider config, then retry with explicit channel/profile."));
1818
+ console.log();
1819
+ }
1820
+ const results = await installMcpServerToAll(providers, profile.serverName, profile.config, scope);
1821
+ const succeeded = results.filter((result) => result.success);
1822
+ const validations = [];
1823
+ for (const result of succeeded) {
1824
+ const validation = await validateProfile(result.provider, scope, profile.serverName);
1825
+ validations.push({ providerId: result.provider.id, valid: validation.valid, reason: validation.reason });
1826
+ }
1827
+ if (succeeded.length > 0) {
1828
+ await recordMcpInstall(
1829
+ profile.serverName,
1830
+ profile.packageSpec ?? resolvedOpts.command ?? "cleo-dev",
1831
+ channel === "dev" ? "command" : "package",
1832
+ succeeded.map((result) => result.provider.id),
1833
+ resolvedOpts.global ?? false
1834
+ );
1835
+ }
1836
+ if (format === "human") {
1837
+ formatInstallResultHuman(mode, channel, profile.serverName, scope, results, validations);
1838
+ }
1839
+ const validationFailures = validations.filter((entry) => !entry.valid);
1840
+ if (interactive && validationFailures.length > 0 && format === "human") {
1841
+ console.log(pc6.dim("Step 5/6 - Validation"));
1842
+ console.log(pc6.yellow(`Validation found ${validationFailures.length} issue(s).`));
1843
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
1844
+ try {
1845
+ console.log(pc6.dim("Step 6/6 - Rollback"));
1846
+ const answer = await rl.question(pc6.dim("Rollback failed validations? [y/N] "));
1847
+ if (["y", "yes"].includes(answer.trim().toLowerCase())) {
1848
+ for (const failure of validationFailures) {
1849
+ const provider = providers.find((candidate) => candidate.id === failure.providerId);
1850
+ if (!provider) continue;
1851
+ await removeMcpServer(provider, profile.serverName, scope);
1852
+ }
1853
+ console.log(pc6.yellow("Rollback completed for failed provider validations."));
1854
+ }
1855
+ } finally {
1856
+ rl.close();
1857
+ }
1858
+ }
1859
+ if (format === "json") {
1860
+ outputSuccess(operation, mvi, {
1861
+ action: mode,
1862
+ channel,
1863
+ serverName: profile.serverName,
1864
+ scope,
1865
+ command: profile.config.command,
1866
+ args: profile.config.args ?? [],
1867
+ env: profile.config.env ?? {},
1868
+ packageSpec: profile.packageSpec,
1869
+ providers: results.map((result) => ({
1870
+ id: result.provider.id,
1871
+ success: result.success,
1872
+ configPath: result.configPath,
1873
+ error: result.error,
1874
+ validation: validations.find((entry) => entry.providerId === result.provider.id) ?? null
1875
+ })),
1876
+ conflicts: conflictWarnings,
1877
+ validationStatus: validationFailures.length === 0 ? "ok" : "warning"
1878
+ });
1879
+ }
1880
+ }
1881
+ async function executeCleoUninstall(opts, operation) {
1882
+ const mvi = "standard";
1883
+ let format;
1884
+ try {
1885
+ format = resolveFormat({
1886
+ jsonFlag: opts.json ?? false,
1887
+ humanFlag: (opts.human ?? false) || isHuman(),
1888
+ projectDefault: "json"
1889
+ });
1890
+ } catch (error) {
1891
+ const message = error instanceof Error ? error.message : String(error);
1892
+ emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
1893
+ process.exit(1);
1894
+ }
1895
+ const channel = normalizeCleoChannel(opts.channel);
1896
+ const serverName = resolveCleoServerName(channel);
1897
+ const providers = collectTargetProviders(opts.provider, opts.all);
1898
+ if (providers.length === 0) {
1899
+ const message = "No target providers found.";
1900
+ if (format === "json") {
1901
+ emitJsonError(operation, mvi, ErrorCodes.PROVIDER_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
1902
+ } else {
1903
+ console.error(pc6.red(message));
1904
+ }
1905
+ process.exit(1);
1906
+ }
1907
+ const scope = opts.global ? "global" : "project";
1908
+ if (opts.dryRun) {
1909
+ if (format === "human") {
1910
+ console.log(pc6.bold("Dry run: uninstall CLEO profile"));
1911
+ console.log(pc6.dim(`Server: ${serverName} Channel: ${channel} Scope: ${scope}`));
1912
+ console.log(pc6.dim(`Providers: ${providers.map((provider) => provider.id).join(", ")}`));
1913
+ } else {
1914
+ outputSuccess(operation, mvi, {
1915
+ action: "uninstall",
1916
+ channel,
1917
+ serverName,
1918
+ providers: providers.map((provider) => provider.id),
1919
+ scope,
1920
+ dryRun: true
1921
+ });
1922
+ }
1923
+ return;
1924
+ }
1925
+ const removed = [];
1926
+ for (const provider of providers) {
1927
+ const success = await removeMcpServer(provider, serverName, scope);
1928
+ if (success) removed.push(provider.id);
1929
+ }
1930
+ if (removed.length > 0) {
1931
+ await removeMcpFromLock(serverName);
1932
+ }
1933
+ if (format === "human") {
1934
+ const prefix = removed.length > 0 ? pc6.green("Removed") : pc6.yellow("No matching profile found for");
1935
+ console.log(`${prefix} ${pc6.bold(serverName)} (${channel}) on ${removed.length}/${providers.length} providers.`);
1936
+ }
1937
+ if (format === "json") {
1938
+ outputSuccess(operation, mvi, {
1939
+ action: "uninstall",
1940
+ channel,
1941
+ serverName,
1942
+ scope,
1943
+ removed,
1944
+ providerCount: providers.length,
1945
+ dryRun: false
1946
+ });
1947
+ }
1948
+ }
1949
+ async function executeCleoShow(opts, operation) {
1950
+ const mvi = "standard";
1951
+ let format;
1952
+ try {
1953
+ format = resolveFormat({
1954
+ jsonFlag: opts.json ?? false,
1955
+ humanFlag: (opts.human ?? false) || isHuman(),
1956
+ projectDefault: "json"
1957
+ });
1958
+ } catch (error) {
1959
+ const message = error instanceof Error ? error.message : String(error);
1960
+ emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
1961
+ process.exit(1);
1962
+ }
1963
+ const providers = collectTargetProviders(opts.provider, opts.all);
1964
+ if (providers.length === 0) {
1965
+ const message = "No target providers found.";
1966
+ if (format === "json") {
1967
+ emitJsonError(operation, mvi, ErrorCodes.PROVIDER_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
1968
+ } else {
1969
+ console.error(pc6.red(message));
1970
+ }
1971
+ process.exit(1);
1972
+ }
1973
+ const channelFilter = opts.channel ? normalizeCleoChannel(opts.channel) : null;
1974
+ const scope = opts.global ? "global" : "project";
1975
+ const entries = [];
1976
+ for (const provider of providers) {
1977
+ const providerEntries = await listMcpServers(provider, scope);
1978
+ for (const entry of providerEntries) {
1979
+ const channel = resolveChannelFromServerName(entry.name);
1980
+ if (!channel) continue;
1981
+ if (channelFilter && channel !== channelFilter) continue;
1982
+ entries.push({
1983
+ provider: provider.id,
1984
+ serverName: entry.name,
1985
+ channel,
1986
+ command: typeof entry.config.command === "string" ? entry.config.command : void 0,
1987
+ args: Array.isArray(entry.config.args) ? entry.config.args.filter((value) => typeof value === "string") : [],
1988
+ env: typeof entry.config.env === "object" && entry.config.env !== null ? entry.config.env : {}
1989
+ });
1990
+ }
1991
+ }
1992
+ if (format === "human") {
1993
+ if (entries.length === 0) {
1994
+ console.log(pc6.dim("No CLEO MCP profiles found."));
1995
+ } else {
1996
+ for (const entry of entries) {
1997
+ console.log(`${pc6.bold(entry.provider.padEnd(22))} ${entry.serverName.padEnd(10)} ${pc6.dim(entry.channel)}`);
1998
+ }
1999
+ }
2000
+ }
2001
+ if (format === "json") {
2002
+ outputSuccess(operation, mvi, {
2003
+ providers: providers.map((provider) => provider.id),
2004
+ scope,
2005
+ channel: channelFilter,
2006
+ profiles: entries,
2007
+ count: entries.length
2008
+ });
2009
+ }
2010
+ }
2011
+ function buildInstallOptions(command) {
2012
+ 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");
2013
+ }
2014
+ function registerMcpCleoCommands(parent) {
2015
+ const cleo = parent.command("cleo").description("Manage CLEO MCP channel profiles");
2016
+ buildInstallOptions(
2017
+ cleo.command("install").description("Install CLEO MCP profile by channel")
2018
+ ).action(async (opts) => {
2019
+ await executeCleoInstall("install", opts, "mcp.cleo.install");
2020
+ });
2021
+ buildInstallOptions(
2022
+ cleo.command("update").description("Update CLEO MCP profile by channel")
2023
+ ).action(async (opts) => {
2024
+ await executeCleoInstall("update", opts, "mcp.cleo.update");
2025
+ });
2026
+ 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) => {
2027
+ await executeCleoUninstall(opts, "mcp.cleo.uninstall");
2028
+ });
2029
+ 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) => {
2030
+ await executeCleoShow(opts, "mcp.cleo.show");
2031
+ });
2032
+ }
2033
+ function registerMcpCleoCompatibilityCommands(parent) {
2034
+ 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) => {
2035
+ if (name !== "cleo") {
2036
+ emitJsonError("mcp.update", "standard", ErrorCodes.INVALID_INPUT, "Only managed profile 'cleo' is supported by mcp update.", ErrorCategories.VALIDATION, { name });
2037
+ process.exit(1);
2038
+ }
2039
+ await executeCleoInstall("update", opts, "mcp.update");
2040
+ });
2041
+ 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) => {
2042
+ if (name !== "cleo") {
2043
+ emitJsonError("mcp.uninstall", "standard", ErrorCodes.INVALID_INPUT, "Only managed profile 'cleo' is supported by mcp uninstall.", ErrorCategories.VALIDATION, { name });
2044
+ process.exit(1);
2045
+ }
2046
+ await executeCleoUninstall(opts, "mcp.uninstall");
2047
+ });
2048
+ 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) => {
2049
+ if (name !== "cleo") {
2050
+ emitJsonError("mcp.show", "standard", ErrorCodes.INVALID_INPUT, "Only managed profile 'cleo' is supported by mcp show.", ErrorCategories.VALIDATION, { name });
2051
+ process.exit(1);
2052
+ }
2053
+ await executeCleoShow(opts, "mcp.show");
2054
+ });
2055
+ }
2056
+ function mapCompatibilityInstallOptions(opts) {
2057
+ return {
2058
+ channel: opts.channel,
2059
+ provider: [...opts.provider ?? [], ...opts.agent ?? []],
2060
+ all: opts.all,
2061
+ global: opts.global,
2062
+ version: opts.version,
2063
+ command: opts.command,
2064
+ arg: opts.arg ?? [],
2065
+ env: opts.env ?? [],
2066
+ cleoDir: opts.cleoDir,
2067
+ dryRun: opts.dryRun,
2068
+ yes: opts.yes,
2069
+ interactive: opts.interactive,
2070
+ json: opts.json,
2071
+ human: opts.human
2072
+ };
2073
+ }
2074
+ function shouldUseCleoCompatibilityInstall(source, channel) {
2075
+ if (source.trim().toLowerCase() !== "cleo") return false;
2076
+ return typeof channel === "string" && channel.trim() !== "";
2077
+ }
2078
+ function registerCleoCommands(program2) {
2079
+ const cleo = program2.command("cleo").description("Manage CLEO channel profiles");
2080
+ buildInstallOptions(
2081
+ cleo.command("install").description("Install CLEO profile by channel")
2082
+ ).action(async (opts) => {
2083
+ await executeCleoInstall("install", opts, "cleo.install");
2084
+ });
2085
+ buildInstallOptions(
2086
+ cleo.command("update").description("Update CLEO profile by channel")
2087
+ ).action(async (opts) => {
2088
+ await executeCleoInstall("update", opts, "cleo.update");
2089
+ });
2090
+ cleo.command("uninstall").description("Uninstall CLEO 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) => {
2091
+ await executeCleoUninstall(opts, "cleo.uninstall");
2092
+ });
2093
+ cleo.command("show").description("Show installed CLEO 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) => {
2094
+ await executeCleoShow(opts, "cleo.show");
2095
+ });
2096
+ }
2097
+
2098
+ // src/commands/mcp/detect.ts
2099
+ import { existsSync as existsSync3 } from "fs";
2100
+ import pc7 from "picocolors";
2101
+ function registerMcpDetect(parent) {
2102
+ 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) => {
2103
+ const operation = "mcp.detect";
2104
+ const mvi = "standard";
2105
+ let format;
2106
+ try {
2107
+ format = resolveFormat({
2108
+ jsonFlag: opts.json ?? false,
2109
+ humanFlag: (opts.human ?? false) || isHuman(),
2110
+ projectDefault: "json"
2111
+ });
2112
+ } catch (error) {
2113
+ const message = error instanceof Error ? error.message : String(error);
2114
+ emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
2115
+ process.exit(1);
2116
+ }
2117
+ const providers = getInstalledProviders();
2118
+ const providersResult = [];
2119
+ let totalConfigs = 0;
2120
+ for (const provider of providers) {
2121
+ const globalPath = resolveConfigPath(provider, "global");
2122
+ const projectPath = resolveConfigPath(provider, "project");
2123
+ const globalEntries = await listMcpServers(provider, "global");
2124
+ const projectEntries = await listMcpServers(provider, "project");
2125
+ const configsFound = (globalPath && existsSync3(globalPath) ? 1 : 0) + (projectPath && existsSync3(projectPath) ? 1 : 0);
2126
+ totalConfigs += configsFound;
2127
+ const allServers = [...globalEntries.map((e) => e.name), ...projectEntries.map((e) => e.name)];
2128
+ providersResult.push({
2129
+ id: provider.id,
2130
+ configsFound,
2131
+ servers: allServers
2132
+ });
2133
+ }
2134
+ if (format === "json") {
2135
+ outputSuccess(operation, mvi, {
2136
+ providers: providersResult,
2137
+ totalConfigs
2138
+ });
2139
+ return;
2140
+ }
2141
+ console.log(pc7.bold(`
2142
+ ${providers.length} provider(s) with MCP support:
2143
+ `));
2144
+ for (const provider of providersResult) {
2145
+ const globalPath = resolveConfigPath(providers.find((p) => p.id === provider.id), "global");
2146
+ const projectPath = resolveConfigPath(providers.find((p) => p.id === provider.id), "project");
2147
+ const hasGlobal = globalPath && existsSync3(globalPath);
2148
+ const hasProject = projectPath && existsSync3(projectPath);
2149
+ const globalIcon = hasGlobal ? pc7.green("G") : pc7.dim("-");
2150
+ const projectIcon = hasProject ? pc7.green("P") : pc7.dim("-");
2151
+ const serverList = provider.servers.length > 0 ? pc7.dim(provider.servers.join(", ")) : pc7.dim("no servers");
2152
+ console.log(` [${globalIcon}${projectIcon}] ${pc7.bold(provider.id.padEnd(20))} ${serverList}`);
2153
+ }
2154
+ console.log(pc7.dim("\nG = global config, P = project config"));
2155
+ console.log();
2156
+ });
2157
+ }
2158
+
2159
+ // src/commands/mcp/install.ts
2160
+ import pc8 from "picocolors";
1606
2161
  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) => {
2162
+ 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
2163
  const operation = "mcp.install";
1609
2164
  const mvi = "standard";
1610
2165
  let format;
@@ -1619,6 +2174,11 @@ function registerMcpInstall(parent) {
1619
2174
  emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
1620
2175
  process.exit(1);
1621
2176
  }
2177
+ if (shouldUseCleoCompatibilityInstall(source, opts.channel)) {
2178
+ const cleoOpts = mapCompatibilityInstallOptions(opts);
2179
+ await executeCleoInstall("install", cleoOpts, operation);
2180
+ return;
2181
+ }
1622
2182
  const parsed = parseSource(source);
1623
2183
  const serverName = opts.name ?? parsed.inferredName;
1624
2184
  const headers = {};
@@ -1634,6 +2194,8 @@ function registerMcpInstall(parent) {
1634
2194
  providers = getInstalledProviders();
1635
2195
  } else if (opts.agent.length > 0) {
1636
2196
  providers = opts.agent.map((a) => getProvider(a)).filter((p) => p !== void 0);
2197
+ } else if (opts.provider.length > 0) {
2198
+ providers = opts.provider.map((a) => getProvider(a)).filter((p) => p !== void 0);
1637
2199
  } else {
1638
2200
  providers = getInstalledProviders();
1639
2201
  }
@@ -1642,7 +2204,7 @@ function registerMcpInstall(parent) {
1642
2204
  if (format === "json") {
1643
2205
  emitJsonError(operation, mvi, ErrorCodes.PROVIDER_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
1644
2206
  } else {
1645
- console.error(pc6.red(message));
2207
+ console.error(pc8.red(message));
1646
2208
  }
1647
2209
  process.exit(1);
1648
2210
  }
@@ -1658,8 +2220,8 @@ function registerMcpInstall(parent) {
1658
2220
  dryRun: true
1659
2221
  });
1660
2222
  } else {
1661
- console.log(pc6.bold("Dry run - would install:"));
1662
- console.log(` Server: ${pc6.bold(serverName)}`);
2223
+ console.log(pc8.bold("Dry run - would install:"));
2224
+ console.log(` Server: ${pc8.bold(serverName)}`);
1663
2225
  console.log(` Config: ${JSON.stringify(config, null, 2)}`);
1664
2226
  console.log(` Scope: ${scope}`);
1665
2227
  console.log(` Providers: ${providers.map((p) => p.id).join(", ")}`);
@@ -1667,7 +2229,7 @@ function registerMcpInstall(parent) {
1667
2229
  return;
1668
2230
  }
1669
2231
  if (format === "human") {
1670
- console.log(pc6.dim(`Installing "${serverName}" to ${providers.length} provider(s)...
2232
+ console.log(pc8.dim(`Installing "${serverName}" to ${providers.length} provider(s)...
1671
2233
  `));
1672
2234
  }
1673
2235
  const results = await installMcpServerToAll(
@@ -1681,9 +2243,9 @@ function registerMcpInstall(parent) {
1681
2243
  if (format === "human") {
1682
2244
  for (const r of results) {
1683
2245
  if (r.success) {
1684
- console.log(` ${pc6.green("\u2713")} ${r.provider.toolName.padEnd(22)} ${pc6.dim(r.configPath)}`);
2246
+ console.log(` ${pc8.green("\u2713")} ${r.provider.toolName.padEnd(22)} ${pc8.dim(r.configPath)}`);
1685
2247
  } else {
1686
- console.log(` ${pc6.red("\u2717")} ${r.provider.toolName.padEnd(22)} ${pc6.red(r.error ?? "failed")}`);
2248
+ console.log(` ${pc8.red("\u2717")} ${r.provider.toolName.padEnd(22)} ${pc8.red(r.error ?? "failed")}`);
1687
2249
  }
1688
2250
  }
1689
2251
  }
@@ -1706,85 +2268,16 @@ function registerMcpInstall(parent) {
1706
2268
  dryRun: false
1707
2269
  });
1708
2270
  } else {
1709
- console.log(pc6.bold(`
2271
+ console.log(pc8.bold(`
1710
2272
  ${succeeded.length}/${results.length} providers configured.`));
1711
2273
  }
1712
2274
  });
1713
2275
  }
1714
2276
 
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
2277
  // src/commands/mcp/list.ts
1785
- import pc8 from "picocolors";
2278
+ import pc9 from "picocolors";
1786
2279
  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) => {
2280
+ 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
2281
  const operation = "mcp.list";
1789
2282
  const mvi = "standard";
1790
2283
  let format;
@@ -1799,15 +2292,16 @@ function registerMcpList(parent) {
1799
2292
  emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
1800
2293
  process.exit(1);
1801
2294
  }
1802
- const providers = opts.agent ? [getProvider(opts.agent)].filter((p) => p !== void 0) : getInstalledProviders();
1803
- if (opts.agent && providers.length === 0) {
1804
- const message = `Provider not found: ${opts.agent}`;
2295
+ const selectedProvider = opts.provider ?? opts.agent;
2296
+ const providers = selectedProvider ? [getProvider(selectedProvider)].filter((p) => p !== void 0) : getInstalledProviders();
2297
+ if (selectedProvider && providers.length === 0) {
2298
+ const message = `Provider not found: ${selectedProvider}`;
1805
2299
  if (format === "json") {
1806
2300
  emitJsonError(operation, mvi, ErrorCodes.PROVIDER_NOT_FOUND, message, ErrorCategories.NOT_FOUND, {
1807
- agent: opts.agent
2301
+ provider: selectedProvider
1808
2302
  });
1809
2303
  } else {
1810
- console.error(pc8.red(message));
2304
+ console.error(pc9.red(message));
1811
2305
  }
1812
2306
  process.exit(1);
1813
2307
  }
@@ -1827,33 +2321,32 @@ function registerMcpList(parent) {
1827
2321
  outputSuccess(operation, mvi, {
1828
2322
  servers: allEntries,
1829
2323
  count: allEntries.length,
1830
- scope: opts.global ? "global" : opts.agent ? `agent:${opts.agent}` : "project"
2324
+ scope: opts.global ? "global" : selectedProvider ? `agent:${selectedProvider}` : "project"
1831
2325
  });
1832
2326
  return;
1833
2327
  }
1834
2328
  if (allEntries.length === 0) {
1835
- console.log(pc8.dim("No MCP servers configured."));
2329
+ console.log(pc9.dim("No MCP servers configured."));
1836
2330
  return;
1837
2331
  }
1838
- console.log(pc8.bold(`
2332
+ console.log(pc9.bold(`
1839
2333
  ${allEntries.length} MCP server(s) configured:
1840
2334
  `));
1841
2335
  for (const entry of allEntries) {
1842
- const scopeIndicator = entry.scope === "global" ? pc8.dim("[G] ") : pc8.dim("[P] ");
1843
- console.log(` ${scopeIndicator}${pc8.bold(entry.name.padEnd(25))} ${entry.command ? pc8.dim(entry.command) : ""}`);
2336
+ const scopeIndicator = entry.scope === "global" ? pc9.dim("[G] ") : pc9.dim("[P] ");
2337
+ console.log(` ${scopeIndicator}${pc9.bold(entry.name.padEnd(25))} ${entry.command ? pc9.dim(entry.command) : ""}`);
1844
2338
  }
1845
2339
  console.log();
1846
- console.log(pc8.dim("G = global config, P = project config"));
2340
+ console.log(pc9.dim("G = global config, P = project config"));
1847
2341
  console.log();
1848
2342
  });
1849
2343
  }
1850
2344
 
1851
- // src/commands/mcp/detect.ts
1852
- import { existsSync as existsSync3 } from "fs";
1853
- import pc9 from "picocolors";
1854
- function registerMcpDetect(parent) {
1855
- 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) => {
1856
- const operation = "mcp.detect";
2345
+ // src/commands/mcp/remove.ts
2346
+ import pc10 from "picocolors";
2347
+ function registerMcpRemove(parent) {
2348
+ 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) => {
2349
+ const operation = "mcp.remove";
1857
2350
  const mvi = "standard";
1858
2351
  let format;
1859
2352
  try {
@@ -1867,45 +2360,56 @@ function registerMcpDetect(parent) {
1867
2360
  emitJsonError(operation, mvi, ErrorCodes.FORMAT_CONFLICT, message, ErrorCategories.VALIDATION);
1868
2361
  process.exit(1);
1869
2362
  }
1870
- const providers = getInstalledProviders();
1871
- const providersResult = [];
1872
- let totalConfigs = 0;
2363
+ let providers;
2364
+ if (opts.all) {
2365
+ providers = getInstalledProviders();
2366
+ } else if (opts.agent.length > 0) {
2367
+ providers = opts.agent.map((a) => getProvider(a)).filter((p) => p !== void 0);
2368
+ } else if (opts.provider.length > 0) {
2369
+ providers = opts.provider.map((a) => getProvider(a)).filter((p) => p !== void 0);
2370
+ } else {
2371
+ providers = getInstalledProviders();
2372
+ }
2373
+ if (providers.length === 0) {
2374
+ const message = "No target providers found.";
2375
+ if (format === "json") {
2376
+ emitJsonError(operation, mvi, ErrorCodes.PROVIDER_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
2377
+ } else {
2378
+ console.error(pc10.red(message));
2379
+ }
2380
+ process.exit(1);
2381
+ }
2382
+ const scope = opts.global ? "global" : "project";
2383
+ const removed = [];
2384
+ const notFound = [];
1873
2385
  for (const provider of providers) {
1874
- const globalPath = resolveConfigPath(provider, "global");
1875
- const projectPath = resolveConfigPath(provider, "project");
1876
- const globalEntries = await listMcpServers(provider, "global");
1877
- const projectEntries = await listMcpServers(provider, "project");
1878
- const configsFound = (globalPath && existsSync3(globalPath) ? 1 : 0) + (projectPath && existsSync3(projectPath) ? 1 : 0);
1879
- totalConfigs += configsFound;
1880
- const allServers = [...globalEntries.map((e) => e.name), ...projectEntries.map((e) => e.name)];
1881
- providersResult.push({
1882
- id: provider.id,
1883
- configsFound,
1884
- servers: allServers
1885
- });
2386
+ const success = await removeMcpServer(provider, name, scope);
2387
+ if (success) {
2388
+ removed.push(provider.id);
2389
+ if (format === "human") {
2390
+ console.log(` ${pc10.green("\u2713")} Removed from ${provider.toolName}`);
2391
+ }
2392
+ } else {
2393
+ notFound.push(provider.id);
2394
+ }
2395
+ }
2396
+ if (removed.length > 0) {
2397
+ await removeMcpFromLock(name);
1886
2398
  }
1887
2399
  if (format === "json") {
1888
2400
  outputSuccess(operation, mvi, {
1889
- providers: providersResult,
1890
- totalConfigs
2401
+ removed,
2402
+ providers: removed,
2403
+ notFound: notFound.length > 0 ? notFound : void 0
1891
2404
  });
1892
- return;
1893
- }
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}`);
2405
+ } else {
2406
+ if (removed.length > 0) {
2407
+ console.log(pc10.green(`
2408
+ \u2713 Removed "${name}" from ${removed.length} provider(s).`));
2409
+ } else {
2410
+ console.log(pc10.yellow(`Server "${name}" not found in any provider config.`));
2411
+ }
1906
2412
  }
1907
- console.log(pc9.dim("\nG = global config, P = project config"));
1908
- console.log();
1909
2413
  });
1910
2414
  }
1911
2415
 
@@ -1916,12 +2420,14 @@ function registerMcpCommands(program2) {
1916
2420
  registerMcpRemove(mcp);
1917
2421
  registerMcpList(mcp);
1918
2422
  registerMcpDetect(mcp);
2423
+ registerMcpCleoCommands(mcp);
2424
+ registerMcpCleoCompatibilityCommands(mcp);
1919
2425
  }
1920
2426
 
1921
2427
  // src/commands/providers.ts
1922
2428
  import { randomUUID as randomUUID3 } from "crypto";
1923
2429
  import { resolveOutputFormat as resolveOutputFormat2 } from "@cleocode/lafs-protocol";
1924
- import pc10 from "picocolors";
2430
+ import pc11 from "picocolors";
1925
2431
  function registerProvidersCommand(program2) {
1926
2432
  const providers = program2.command("providers").description("Manage AI agent providers");
1927
2433
  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 +2461,19 @@ function registerProvidersCommand(program2) {
1955
2461
  console.log(JSON.stringify(envelope, null, 2));
1956
2462
  return;
1957
2463
  }
1958
- console.log(pc10.bold(`
2464
+ console.log(pc11.bold(`
1959
2465
  CAMP Provider Registry v${getRegistryVersion()}`));
1960
- console.log(pc10.dim(`${getProviderCount()} providers
2466
+ console.log(pc11.dim(`${getProviderCount()} providers
1961
2467
  `));
1962
2468
  const tiers = ["high", "medium", "low"];
1963
2469
  for (const tier of tiers) {
1964
2470
  const tierProviders = all.filter((p) => p.priority === tier);
1965
2471
  if (tierProviders.length === 0) continue;
1966
- const tierLabel = tier === "high" ? pc10.green("HIGH") : tier === "medium" ? pc10.yellow("MEDIUM") : pc10.dim("LOW");
2472
+ const tierLabel = tier === "high" ? pc11.green("HIGH") : tier === "medium" ? pc11.yellow("MEDIUM") : pc11.dim("LOW");
1967
2473
  console.log(`${tierLabel} priority:`);
1968
2474
  for (const p of tierProviders) {
1969
- const status = p.status === "active" ? pc10.green("active") : p.status === "beta" ? pc10.yellow("beta") : pc10.dim(p.status);
1970
- console.log(` ${pc10.bold(p.agentFlag.padEnd(20))} ${p.toolName.padEnd(22)} ${p.vendor.padEnd(16)} [${status}]`);
2475
+ const status = p.status === "active" ? pc11.green("active") : p.status === "beta" ? pc11.yellow("beta") : pc11.dim(p.status);
2476
+ console.log(` ${pc11.bold(p.agentFlag.padEnd(20))} ${p.toolName.padEnd(22)} ${p.vendor.padEnd(16)} [${status}]`);
1971
2477
  }
1972
2478
  console.log();
1973
2479
  }
@@ -2011,17 +2517,17 @@ CAMP Provider Registry v${getRegistryVersion()}`));
2011
2517
  console.log(JSON.stringify(envelope, null, 2));
2012
2518
  return;
2013
2519
  }
2014
- console.log(pc10.bold(`
2520
+ console.log(pc11.bold(`
2015
2521
  Detected ${installed.length} installed providers:
2016
2522
  `));
2017
2523
  for (const r of installed) {
2018
2524
  const methods = r.methods.join(", ");
2019
- const project = r.projectDetected ? pc10.green(" [project]") : "";
2020
- console.log(` ${pc10.green("\u2713")} ${pc10.bold(r.provider.toolName.padEnd(22))} via ${pc10.dim(methods)}${project}`);
2525
+ const project = r.projectDetected ? pc11.green(" [project]") : "";
2526
+ console.log(` ${pc11.green("\u2713")} ${pc11.bold(r.provider.toolName.padEnd(22))} via ${pc11.dim(methods)}${project}`);
2021
2527
  }
2022
2528
  const notInstalled = results.filter((r) => !r.installed);
2023
2529
  if (notInstalled.length > 0) {
2024
- console.log(pc10.dim(`
2530
+ console.log(pc11.dim(`
2025
2531
  ${notInstalled.length} providers not detected`));
2026
2532
  }
2027
2533
  console.log();
@@ -2049,7 +2555,7 @@ Detected ${installed.length} installed providers:
2049
2555
  id
2050
2556
  });
2051
2557
  } else {
2052
- console.error(pc10.red(message));
2558
+ console.error(pc11.red(message));
2053
2559
  }
2054
2560
  process.exit(1);
2055
2561
  }
@@ -2065,9 +2571,9 @@ Detected ${installed.length} installed providers:
2065
2571
  console.log(JSON.stringify(envelope, null, 2));
2066
2572
  return;
2067
2573
  }
2068
- console.log(pc10.bold(`
2574
+ console.log(pc11.bold(`
2069
2575
  ${provider.toolName}`));
2070
- console.log(pc10.dim(`by ${provider.vendor}
2576
+ console.log(pc11.dim(`by ${provider.vendor}
2071
2577
  `));
2072
2578
  console.log(` ID: ${provider.id}`);
2073
2579
  console.log(` Flag: --agent ${provider.agentFlag}`);
@@ -2083,7 +2589,7 @@ ${provider.toolName}`));
2083
2589
  console.log(` Transports: ${provider.supportedTransports.join(", ")}`);
2084
2590
  console.log(` Headers: ${provider.supportsHeaders ? "yes" : "no"}`);
2085
2591
  console.log();
2086
- console.log(pc10.dim(" Paths:"));
2592
+ console.log(pc11.dim(" Paths:"));
2087
2593
  console.log(` Global dir: ${provider.pathGlobal}`);
2088
2594
  console.log(` Project dir: ${provider.pathProject || "(none)"}`);
2089
2595
  console.log(` Global config: ${provider.configPathGlobal}`);
@@ -2127,7 +2633,7 @@ function emitJsonError2(operation, mvi, code, message, category, details = {}) {
2127
2633
 
2128
2634
  // src/commands/skills/install.ts
2129
2635
  import { existsSync as existsSync4 } from "fs";
2130
- import pc11 from "picocolors";
2636
+ import pc12 from "picocolors";
2131
2637
 
2132
2638
  // src/core/sources/github.ts
2133
2639
  import { simpleGit } from "simple-git";
@@ -2211,7 +2717,7 @@ function registerSkillsInstall(parent) {
2211
2717
  if (format === "json") {
2212
2718
  emitError2(operation, mvi, ErrorCodes.PROVIDER_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
2213
2719
  }
2214
- console.error(pc11.red(message));
2720
+ console.error(pc12.red(message));
2215
2721
  process.exit(1);
2216
2722
  }
2217
2723
  if (opts.profile) {
@@ -2223,12 +2729,12 @@ function registerSkillsInstall(parent) {
2223
2729
  if (format === "json") {
2224
2730
  emitError2(operation, mvi, ErrorCodes.INVALID_INPUT, message, ErrorCategories.VALIDATION);
2225
2731
  }
2226
- console.error(pc11.red(message));
2227
- console.log(pc11.dim("Usage: caamp skills install <source> or caamp skills install --profile <name>"));
2732
+ console.error(pc12.red(message));
2733
+ console.log(pc12.dim("Usage: caamp skills install <source> or caamp skills install --profile <name>"));
2228
2734
  process.exit(1);
2229
2735
  }
2230
2736
  if (format === "human") {
2231
- console.log(pc11.dim(`Installing to ${providers.length} provider(s)...`));
2737
+ console.log(pc12.dim(`Installing to ${providers.length} provider(s)...`));
2232
2738
  }
2233
2739
  let localPath;
2234
2740
  let cleanup;
@@ -2268,7 +2774,7 @@ function registerSkillsInstall(parent) {
2268
2774
  if (format === "json") {
2269
2775
  emitJsonError(operation, mvi, ErrorCodes.NETWORK_ERROR, message, ErrorCategories.TRANSIENT);
2270
2776
  }
2271
- console.error(pc11.red(message));
2777
+ console.error(pc12.red(message));
2272
2778
  process.exit(1);
2273
2779
  }
2274
2780
  } else if (parsed.type === "gitlab" && parsed.owner && parsed.repo) {
@@ -2281,7 +2787,7 @@ function registerSkillsInstall(parent) {
2281
2787
  if (format === "json") {
2282
2788
  emitJsonError(operation, mvi, ErrorCodes.NETWORK_ERROR, message, ErrorCategories.TRANSIENT);
2283
2789
  }
2284
- console.error(pc11.red(message));
2790
+ console.error(pc12.red(message));
2285
2791
  process.exit(1);
2286
2792
  }
2287
2793
  } else if (parsed.type === "local") {
@@ -2296,7 +2802,7 @@ function registerSkillsInstall(parent) {
2296
2802
  if (format === "json") {
2297
2803
  emitJsonError(operation, mvi, ErrorCodes.INVALID_INPUT, message, ErrorCategories.VALIDATION);
2298
2804
  }
2299
- console.error(pc11.red(message));
2805
+ console.error(pc12.red(message));
2300
2806
  process.exit(1);
2301
2807
  }
2302
2808
  const catalogSkill = getSkill(parsed.inferredName);
@@ -2306,7 +2812,7 @@ function registerSkillsInstall(parent) {
2306
2812
  sourceValue = `library:${catalogSkill.name}`;
2307
2813
  sourceType = "library";
2308
2814
  if (format === "human") {
2309
- console.log(` Found in catalog: ${pc11.bold(catalogSkill.name)} v${catalogSkill.version} (${pc11.dim(catalogSkill.category)})`);
2815
+ console.log(` Found in catalog: ${pc12.bold(catalogSkill.name)} v${catalogSkill.version} (${pc12.dim(catalogSkill.category)})`);
2310
2816
  }
2311
2817
  } else {
2312
2818
  const message = `Skill not found in catalog: ${parsed.inferredName}`;
@@ -2315,8 +2821,8 @@ function registerSkillsInstall(parent) {
2315
2821
  availableSkills: listSkills()
2316
2822
  });
2317
2823
  }
2318
- console.error(pc11.red(message));
2319
- console.log(pc11.dim("Available skills: " + listSkills().join(", ")));
2824
+ console.error(pc12.red(message));
2825
+ console.log(pc12.dim("Available skills: " + listSkills().join(", ")));
2320
2826
  process.exit(1);
2321
2827
  }
2322
2828
  } else {
@@ -2324,7 +2830,7 @@ function registerSkillsInstall(parent) {
2324
2830
  if (format === "json") {
2325
2831
  emitJsonError(operation, mvi, ErrorCodes.INVALID_FORMAT, message, ErrorCategories.VALIDATION);
2326
2832
  }
2327
- console.error(pc11.red(message));
2833
+ console.error(pc12.red(message));
2328
2834
  process.exit(1);
2329
2835
  }
2330
2836
  }
@@ -2334,7 +2840,7 @@ function registerSkillsInstall(parent) {
2334
2840
  if (format === "json") {
2335
2841
  emitJsonError(operation, mvi, ErrorCodes.INTERNAL_ERROR, message, ErrorCategories.INTERNAL);
2336
2842
  }
2337
- console.error(pc11.red(message));
2843
+ console.error(pc12.red(message));
2338
2844
  process.exit(1);
2339
2845
  }
2340
2846
  const result = await installSkill(
@@ -2372,14 +2878,14 @@ function registerSkillsInstall(parent) {
2372
2878
  if (format === "json") {
2373
2879
  outputSuccess(operation, mvi, summary);
2374
2880
  } else {
2375
- console.log(pc11.green(`
2376
- \u2713 Installed ${pc11.bold(skillName)}`));
2377
- console.log(` Canonical: ${pc11.dim(result.canonicalPath)}`);
2881
+ console.log(pc12.green(`
2882
+ \u2713 Installed ${pc12.bold(skillName)}`));
2883
+ console.log(` Canonical: ${pc12.dim(result.canonicalPath)}`);
2378
2884
  console.log(` Linked to: ${result.linkedAgents.join(", ")}`);
2379
2885
  if (result.errors.length > 0) {
2380
- console.log(pc11.yellow("\nWarnings:"));
2886
+ console.log(pc12.yellow("\nWarnings:"));
2381
2887
  for (const err of result.errors) {
2382
- console.log(` ${pc11.yellow("!")} ${err}`);
2888
+ console.log(` ${pc12.yellow("!")} ${err}`);
2383
2889
  }
2384
2890
  }
2385
2891
  }
@@ -2407,11 +2913,11 @@ function registerSkillsInstall(parent) {
2407
2913
  });
2408
2914
  console.error(JSON.stringify(envelope, null, 2));
2409
2915
  } else {
2410
- console.log(pc11.yellow(`
2411
- \u2717 Failed to install ${pc11.bold(skillName)}`));
2412
- console.log(pc11.yellow("Errors:"));
2916
+ console.log(pc12.yellow(`
2917
+ \u2717 Failed to install ${pc12.bold(skillName)}`));
2918
+ console.log(pc12.yellow("Errors:"));
2413
2919
  for (const err of result.errors) {
2414
- console.log(` ${pc11.yellow("!")} ${err}`);
2920
+ console.log(` ${pc12.yellow("!")} ${err}`);
2415
2921
  }
2416
2922
  }
2417
2923
  process.exit(1);
@@ -2427,7 +2933,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
2427
2933
  if (format === "json") {
2428
2934
  emitError2(operation, mvi, ErrorCodes.INVALID_INPUT, message, ErrorCategories.VALIDATION);
2429
2935
  }
2430
- console.error(pc11.red(message));
2936
+ console.error(pc12.red(message));
2431
2937
  process.exit(1);
2432
2938
  }
2433
2939
  const profileSkills = resolveProfile(profileName);
@@ -2438,16 +2944,16 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
2438
2944
  availableProfiles: listProfiles()
2439
2945
  });
2440
2946
  }
2441
- console.error(pc11.red(message));
2947
+ console.error(pc12.red(message));
2442
2948
  const available = listProfiles();
2443
2949
  if (available.length > 0) {
2444
- console.log(pc11.dim("Available profiles: " + available.join(", ")));
2950
+ console.log(pc12.dim("Available profiles: " + available.join(", ")));
2445
2951
  }
2446
2952
  process.exit(1);
2447
2953
  }
2448
2954
  if (format === "human") {
2449
- console.log(`Installing profile ${pc11.bold(profileName)} (${profileSkills.length} skill(s))...`);
2450
- console.log(pc11.dim(`Target: ${providers.length} provider(s)`));
2955
+ console.log(`Installing profile ${pc12.bold(profileName)} (${profileSkills.length} skill(s))...`);
2956
+ console.log(pc12.dim(`Target: ${providers.length} provider(s)`));
2451
2957
  }
2452
2958
  const installed = [];
2453
2959
  const failed = [];
@@ -2462,7 +2968,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
2462
2968
  );
2463
2969
  if (result.success) {
2464
2970
  if (format === "human") {
2465
- console.log(pc11.green(` + ${name}`));
2971
+ console.log(pc12.green(` + ${name}`));
2466
2972
  }
2467
2973
  await recordSkillInstall(
2468
2974
  name,
@@ -2481,7 +2987,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
2481
2987
  });
2482
2988
  } else {
2483
2989
  if (format === "human") {
2484
- console.log(pc11.yellow(` ! ${name}: ${result.errors.join(", ")}`));
2990
+ console.log(pc12.yellow(` ! ${name}: ${result.errors.join(", ")}`));
2485
2991
  }
2486
2992
  failed.push({
2487
2993
  name,
@@ -2491,7 +2997,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
2491
2997
  } catch (err) {
2492
2998
  const errorMsg = err instanceof Error ? err.message : String(err);
2493
2999
  if (format === "human") {
2494
- console.log(pc11.red(` x ${name}: ${errorMsg}`));
3000
+ console.log(pc12.red(` x ${name}: ${errorMsg}`));
2495
3001
  }
2496
3002
  failed.push({
2497
3003
  name,
@@ -2525,7 +3031,7 @@ async function handleProfileInstall(profileName, providers, isGlobal, format, op
2525
3031
  }
2526
3032
  } else {
2527
3033
  console.log(`
2528
- ${pc11.green(`${installed.length} installed`)}, ${failed.length > 0 ? pc11.yellow(`${failed.length} failed`) : "0 failed"}`);
3034
+ ${pc12.green(`${installed.length} installed`)}, ${failed.length > 0 ? pc12.yellow(`${failed.length} failed`) : "0 failed"}`);
2529
3035
  if (failed.length > 0) {
2530
3036
  process.exit(1);
2531
3037
  }
@@ -2533,7 +3039,7 @@ ${pc11.green(`${installed.length} installed`)}, ${failed.length > 0 ? pc11.yello
2533
3039
  }
2534
3040
  async function handleMarketplaceSource(source, _providers, _isGlobal, format, operation, mvi) {
2535
3041
  if (format === "human") {
2536
- console.log(pc11.dim(`Searching marketplace for ${source}...`));
3042
+ console.log(pc12.dim(`Searching marketplace for ${source}...`));
2537
3043
  }
2538
3044
  const client = new MarketplaceClient();
2539
3045
  let skill;
@@ -2544,7 +3050,7 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
2544
3050
  if (format === "json") {
2545
3051
  emitJsonError(operation, mvi, ErrorCodes.NETWORK_ERROR, message, ErrorCategories.TRANSIENT);
2546
3052
  }
2547
- console.error(pc11.red(message));
3053
+ console.error(pc12.red(message));
2548
3054
  return { success: false };
2549
3055
  }
2550
3056
  if (!skill) {
@@ -2552,11 +3058,11 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
2552
3058
  if (format === "json") {
2553
3059
  emitJsonError(operation, mvi, ErrorCodes.SKILL_NOT_FOUND, message, ErrorCategories.NOT_FOUND);
2554
3060
  }
2555
- console.error(pc11.red(message));
3061
+ console.error(pc12.red(message));
2556
3062
  return { success: false };
2557
3063
  }
2558
3064
  if (format === "human") {
2559
- console.log(` Found: ${pc11.bold(skill.name)} by ${skill.author} (${pc11.dim(skill.repoFullName)})`);
3065
+ console.log(` Found: ${pc12.bold(skill.name)} by ${skill.author} (${pc12.dim(skill.repoFullName)})`);
2560
3066
  }
2561
3067
  const parsed = parseSource(skill.githubUrl);
2562
3068
  if (parsed.type !== "github" || !parsed.owner || !parsed.repo) {
@@ -2564,7 +3070,7 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
2564
3070
  if (format === "json") {
2565
3071
  emitJsonError(operation, mvi, ErrorCodes.INVALID_FORMAT, message, ErrorCategories.VALIDATION);
2566
3072
  }
2567
- console.error(pc11.red(message));
3073
+ console.error(pc12.red(message));
2568
3074
  return { success: false };
2569
3075
  }
2570
3076
  try {
@@ -2604,13 +3110,13 @@ async function handleMarketplaceSource(source, _providers, _isGlobal, format, op
2604
3110
  if (format === "json") {
2605
3111
  emitJsonError(operation, mvi, ErrorCodes.NETWORK_ERROR, message, ErrorCategories.TRANSIENT);
2606
3112
  }
2607
- console.error(pc11.red(message));
3113
+ console.error(pc12.red(message));
2608
3114
  return { success: false };
2609
3115
  }
2610
3116
  }
2611
3117
 
2612
3118
  // src/commands/skills/remove.ts
2613
- import pc12 from "picocolors";
3119
+ import pc13 from "picocolors";
2614
3120
  function registerSkillsRemove(parent) {
2615
3121
  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
3122
  const operation = "skills.remove";
@@ -2649,14 +3155,14 @@ function registerSkillsRemove(parent) {
2649
3155
  return;
2650
3156
  }
2651
3157
  if (removed.length > 0) {
2652
- console.log(pc12.green(`\u2713 Removed ${pc12.bold(name)} from: ${removed.join(", ")}`));
3158
+ console.log(pc13.green(`\u2713 Removed ${pc13.bold(name)} from: ${removed.join(", ")}`));
2653
3159
  await removeSkillFromLock(name);
2654
3160
  } else {
2655
- console.log(pc12.yellow(`Skill ${name} not found in any provider.`));
3161
+ console.log(pc13.yellow(`Skill ${name} not found in any provider.`));
2656
3162
  }
2657
3163
  if (result.errors.length > 0) {
2658
3164
  for (const err of result.errors) {
2659
- console.log(pc12.red(` ${err}`));
3165
+ console.log(pc13.red(` ${err}`));
2660
3166
  }
2661
3167
  }
2662
3168
  } else {
@@ -2669,7 +3175,7 @@ function registerSkillsRemove(parent) {
2669
3175
  count: { removed: 0, total: 0 }
2670
3176
  });
2671
3177
  } else {
2672
- console.log(pc12.dim("No skills installed."));
3178
+ console.log(pc13.dim("No skills installed."));
2673
3179
  }
2674
3180
  return;
2675
3181
  }
@@ -2682,11 +3188,11 @@ function registerSkillsRemove(parent) {
2682
3188
  });
2683
3189
  return;
2684
3190
  }
2685
- console.log(pc12.bold("Installed skills:"));
3191
+ console.log(pc13.bold("Installed skills:"));
2686
3192
  for (const s of skills) {
2687
3193
  console.log(` ${s}`);
2688
3194
  }
2689
- console.log(pc12.dim("\nUse: caamp skills remove <name>"));
3195
+ console.log(pc13.dim("\nUse: caamp skills remove <name>"));
2690
3196
  }
2691
3197
  });
2692
3198
  }
@@ -2694,7 +3200,7 @@ function registerSkillsRemove(parent) {
2694
3200
  // src/commands/skills/list.ts
2695
3201
  import { randomUUID as randomUUID4 } from "crypto";
2696
3202
  import { resolveOutputFormat as resolveOutputFormat3 } from "@cleocode/lafs-protocol";
2697
- import pc13 from "picocolors";
3203
+ import pc14 from "picocolors";
2698
3204
  function registerSkillsList(parent) {
2699
3205
  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
3206
  const operation = "skills.list";
@@ -2721,7 +3227,7 @@ function registerSkillsList(parent) {
2721
3227
  agent: opts.agent
2722
3228
  });
2723
3229
  } else {
2724
- console.error(pc13.red(message));
3230
+ console.error(pc14.red(message));
2725
3231
  }
2726
3232
  process.exit(1);
2727
3233
  }
@@ -2749,19 +3255,19 @@ function registerSkillsList(parent) {
2749
3255
  return;
2750
3256
  }
2751
3257
  if (skills.length === 0) {
2752
- console.log(pc13.dim("No skills found."));
3258
+ console.log(pc14.dim("No skills found."));
2753
3259
  return;
2754
3260
  }
2755
- console.log(pc13.bold(`
3261
+ console.log(pc14.bold(`
2756
3262
  ${skills.length} skill(s) found:
2757
3263
  `));
2758
3264
  skills.forEach((skill, index) => {
2759
3265
  const num = (index + 1).toString().padStart(2);
2760
- console.log(` ${pc13.cyan(num)}. ${pc13.bold(skill.name.padEnd(30))} ${pc13.dim(skill.metadata?.description ?? "")}`);
3266
+ console.log(` ${pc14.cyan(num)}. ${pc14.bold(skill.name.padEnd(30))} ${pc14.dim(skill.metadata?.description ?? "")}`);
2761
3267
  });
2762
- console.log(pc13.dim(`
3268
+ console.log(pc14.dim(`
2763
3269
  Install with: caamp skills install <name>`));
2764
- console.log(pc13.dim(`Remove with: caamp skills remove <name>`));
3270
+ console.log(pc14.dim(`Remove with: caamp skills remove <name>`));
2765
3271
  });
2766
3272
  }
2767
3273
  function buildEnvelope5(operation, mvi, result, error) {
@@ -2801,7 +3307,7 @@ import { randomUUID as randomUUID5 } from "crypto";
2801
3307
  import {
2802
3308
  resolveOutputFormat as resolveOutputFormat4
2803
3309
  } from "@cleocode/lafs-protocol";
2804
- import pc14 from "picocolors";
3310
+ import pc15 from "picocolors";
2805
3311
  var SkillsFindValidationError = class extends Error {
2806
3312
  code;
2807
3313
  constructor(code, message) {
@@ -2827,7 +3333,7 @@ function registerSkillsFind(parent) {
2827
3333
  if (opts.json) {
2828
3334
  emitJsonError4(operation, mvi, "E_FORMAT_CONFLICT", message, "VALIDATION");
2829
3335
  } else {
2830
- console.error(pc14.red(message));
3336
+ console.error(pc15.red(message));
2831
3337
  }
2832
3338
  process.exit(1);
2833
3339
  }
@@ -2888,19 +3394,19 @@ function registerSkillsFind(parent) {
2888
3394
  query: query ?? null
2889
3395
  });
2890
3396
  } else {
2891
- console.error(pc14.red(`Recommendation failed: ${message}`));
3397
+ console.error(pc15.red(`Recommendation failed: ${message}`));
2892
3398
  }
2893
3399
  process.exit(1);
2894
3400
  }
2895
3401
  }
2896
3402
  if (!query) {
2897
- console.log(pc14.dim("Usage: caamp skills find <query>"));
3403
+ console.log(pc15.dim("Usage: caamp skills find <query>"));
2898
3404
  return;
2899
3405
  }
2900
3406
  const limit = parseInt(opts.limit, 10);
2901
3407
  const client = new MarketplaceClient();
2902
3408
  if (format === "human") {
2903
- console.log(pc14.dim(`Searching marketplaces for "${query}"...
3409
+ console.log(pc15.dim(`Searching marketplaces for "${query}"...
2904
3410
  `));
2905
3411
  }
2906
3412
  let results;
@@ -2914,7 +3420,7 @@ function registerSkillsFind(parent) {
2914
3420
  limit
2915
3421
  });
2916
3422
  } else {
2917
- console.error(pc14.red(`Marketplace search failed: ${message}`));
3423
+ console.error(pc15.red(`Marketplace search failed: ${message}`));
2918
3424
  }
2919
3425
  process.exit(1);
2920
3426
  }
@@ -2934,21 +3440,21 @@ function registerSkillsFind(parent) {
2934
3440
  return;
2935
3441
  }
2936
3442
  if (results.length === 0) {
2937
- console.log(pc14.yellow("No results found."));
3443
+ console.log(pc15.yellow("No results found."));
2938
3444
  return;
2939
3445
  }
2940
- console.log(pc14.dim(`Found ${results.length} result(s) for "${query}":
3446
+ console.log(pc15.dim(`Found ${results.length} result(s) for "${query}":
2941
3447
  `));
2942
3448
  results.forEach((skill, index) => {
2943
3449
  const num = (index + 1).toString().padStart(2);
2944
- const stars = skill.stars > 0 ? pc14.yellow(`\u2605 ${formatStars(skill.stars)}`) : "";
2945
- console.log(` ${pc14.cyan(num)}. ${pc14.bold(skill.scopedName)} ${stars}`);
2946
- console.log(` ${pc14.dim(skill.description?.slice(0, 80) ?? "")}`);
2947
- console.log(` ${pc14.dim(`from ${skill.source}`)}`);
3450
+ const stars = skill.stars > 0 ? pc15.yellow(`\u2605 ${formatStars(skill.stars)}`) : "";
3451
+ console.log(` ${pc15.cyan(num)}. ${pc15.bold(skill.scopedName)} ${stars}`);
3452
+ console.log(` ${pc15.dim(skill.description?.slice(0, 80) ?? "")}`);
3453
+ console.log(` ${pc15.dim(`from ${skill.source}`)}`);
2948
3454
  console.log();
2949
3455
  });
2950
- console.log(pc14.dim("Install with: caamp skills install <name>"));
2951
- console.log(pc14.dim("Or select by number: caamp skills install <n>"));
3456
+ console.log(pc15.dim("Install with: caamp skills install <name>"));
3457
+ console.log(pc15.dim("Or select by number: caamp skills install <n>"));
2952
3458
  });
2953
3459
  }
2954
3460
  function formatStars(n) {
@@ -3062,7 +3568,7 @@ function emitJsonError4(operation, mvi, code, message, category, details = {}) {
3062
3568
  }
3063
3569
 
3064
3570
  // src/commands/skills/check.ts
3065
- import pc15 from "picocolors";
3571
+ import pc16 from "picocolors";
3066
3572
  function registerSkillsCheck(parent) {
3067
3573
  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
3574
  const operation = "skills.check";
@@ -3089,12 +3595,12 @@ function registerSkillsCheck(parent) {
3089
3595
  total: 0
3090
3596
  });
3091
3597
  } else {
3092
- console.log(pc15.dim("No tracked skills."));
3598
+ console.log(pc16.dim("No tracked skills."));
3093
3599
  }
3094
3600
  return;
3095
3601
  }
3096
3602
  if (format === "human") {
3097
- console.log(pc15.dim(`Checking ${entries.length} skill(s) for updates...
3603
+ console.log(pc16.dim(`Checking ${entries.length} skill(s) for updates...
3098
3604
  `));
3099
3605
  }
3100
3606
  const skillResults = [];
@@ -3130,37 +3636,37 @@ function registerSkillsCheck(parent) {
3130
3636
  for (const r of skillResults) {
3131
3637
  let statusLabel;
3132
3638
  if (r.hasUpdate) {
3133
- statusLabel = pc15.yellow("update available");
3639
+ statusLabel = pc16.yellow("update available");
3134
3640
  } else if (r.currentVersion !== "unknown") {
3135
- statusLabel = pc15.green("up to date");
3641
+ statusLabel = pc16.green("up to date");
3136
3642
  } else {
3137
- statusLabel = pc15.dim("unknown");
3643
+ statusLabel = pc16.dim("unknown");
3138
3644
  }
3139
- console.log(` ${pc15.bold(r.name.padEnd(30))} ${statusLabel}`);
3645
+ console.log(` ${pc16.bold(r.name.padEnd(30))} ${statusLabel}`);
3140
3646
  if (r.currentVersion !== "unknown" || r.latestVersion !== "unknown") {
3141
3647
  const current = r.currentVersion !== "unknown" ? r.currentVersion.slice(0, 12) : "?";
3142
3648
  const latest = r.latestVersion !== "unknown" ? r.latestVersion : "?";
3143
3649
  if (r.hasUpdate) {
3144
- console.log(` ${pc15.dim("current:")} ${current} ${pc15.dim("->")} ${pc15.cyan(latest)}`);
3650
+ console.log(` ${pc16.dim("current:")} ${current} ${pc16.dim("->")} ${pc16.cyan(latest)}`);
3145
3651
  } else {
3146
- console.log(` ${pc15.dim("version:")} ${current}`);
3652
+ console.log(` ${pc16.dim("version:")} ${current}`);
3147
3653
  }
3148
3654
  }
3149
- console.log(` ${pc15.dim(`source: ${r.source}`)}`);
3150
- console.log(` ${pc15.dim(`agents: ${r.agents.join(", ")}`)}`);
3655
+ console.log(` ${pc16.dim(`source: ${r.source}`)}`);
3656
+ console.log(` ${pc16.dim(`agents: ${r.agents.join(", ")}`)}`);
3151
3657
  console.log();
3152
3658
  }
3153
3659
  if (updatesAvailable > 0) {
3154
- console.log(pc15.yellow(`${updatesAvailable} update(s) available.`));
3155
- console.log(pc15.dim("Run `caamp skills update` to update all."));
3660
+ console.log(pc16.yellow(`${updatesAvailable} update(s) available.`));
3661
+ console.log(pc16.dim("Run `caamp skills update` to update all."));
3156
3662
  } else {
3157
- console.log(pc15.green("All skills are up to date."));
3663
+ console.log(pc16.green("All skills are up to date."));
3158
3664
  }
3159
3665
  });
3160
3666
  }
3161
3667
 
3162
3668
  // src/commands/skills/update.ts
3163
- import pc16 from "picocolors";
3669
+ import pc17 from "picocolors";
3164
3670
  function registerSkillsUpdate(parent) {
3165
3671
  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
3672
  const operation = "skills.update";
@@ -3188,12 +3694,12 @@ function registerSkillsUpdate(parent) {
3188
3694
  count: { updated: 0, failed: 0, skipped: 0 }
3189
3695
  });
3190
3696
  } else {
3191
- console.log(pc16.dim("No tracked skills to update."));
3697
+ console.log(pc17.dim("No tracked skills to update."));
3192
3698
  }
3193
3699
  return;
3194
3700
  }
3195
3701
  if (format === "human") {
3196
- console.log(pc16.dim(`Checking ${entries.length} skill(s) for updates...`));
3702
+ console.log(pc17.dim(`Checking ${entries.length} skill(s) for updates...`));
3197
3703
  }
3198
3704
  const outdated = [];
3199
3705
  for (const [name] of entries) {
@@ -3215,29 +3721,29 @@ function registerSkillsUpdate(parent) {
3215
3721
  count: { updated: 0, failed: 0, skipped: 0 }
3216
3722
  });
3217
3723
  } else {
3218
- console.log(pc16.green("\nAll skills are up to date."));
3724
+ console.log(pc17.green("\nAll skills are up to date."));
3219
3725
  }
3220
3726
  return;
3221
3727
  }
3222
3728
  if (format === "human") {
3223
- console.log(pc16.yellow(`
3729
+ console.log(pc17.yellow(`
3224
3730
  ${outdated.length} skill(s) have updates available:
3225
3731
  `));
3226
3732
  for (const skill of outdated) {
3227
3733
  const current = skill.currentVersion?.slice(0, 12) ?? "?";
3228
3734
  const latest = skill.latestVersion ?? "?";
3229
- console.log(` ${pc16.bold(skill.name)} ${pc16.dim(current)} ${pc16.dim("->")} ${pc16.cyan(latest)}`);
3735
+ console.log(` ${pc17.bold(skill.name)} ${pc17.dim(current)} ${pc17.dim("->")} ${pc17.cyan(latest)}`);
3230
3736
  }
3231
3737
  }
3232
3738
  if (!opts.yes && format === "human") {
3233
3739
  const readline = await import("readline");
3234
3740
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
3235
3741
  const answer = await new Promise((resolve) => {
3236
- rl.question(pc16.dim("\nProceed with update? [y/N] "), resolve);
3742
+ rl.question(pc17.dim("\nProceed with update? [y/N] "), resolve);
3237
3743
  });
3238
3744
  rl.close();
3239
3745
  if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
3240
- console.log(pc16.dim("Update cancelled."));
3746
+ console.log(pc17.dim("Update cancelled."));
3241
3747
  return;
3242
3748
  }
3243
3749
  }
@@ -3251,7 +3757,7 @@ ${outdated.length} skill(s) have updates available:
3251
3757
  const entry = tracked[skill.name];
3252
3758
  if (!entry) continue;
3253
3759
  if (format === "human") {
3254
- console.log(pc16.dim(`Updating ${pc16.bold(skill.name)}...`));
3760
+ console.log(pc17.dim(`Updating ${pc17.bold(skill.name)}...`));
3255
3761
  }
3256
3762
  try {
3257
3763
  const parsed = parseSource(entry.source);
@@ -3267,7 +3773,7 @@ ${outdated.length} skill(s) have updates available:
3267
3773
  cleanup = result.cleanup;
3268
3774
  } else {
3269
3775
  if (format === "human") {
3270
- console.log(pc16.yellow(` Skipped ${skill.name}: source type "${parsed.type}" does not support auto-update`));
3776
+ console.log(pc17.yellow(` Skipped ${skill.name}: source type "${parsed.type}" does not support auto-update`));
3271
3777
  }
3272
3778
  skipped.push(skill.name);
3273
3779
  continue;
@@ -3276,7 +3782,7 @@ ${outdated.length} skill(s) have updates available:
3276
3782
  const providers = entry.agents.map((a) => getProvider(a)).filter((p) => p !== void 0);
3277
3783
  if (providers.length === 0) {
3278
3784
  if (format === "human") {
3279
- console.log(pc16.yellow(` Skipped ${skill.name}: no valid providers found`));
3785
+ console.log(pc17.yellow(` Skipped ${skill.name}: no valid providers found`));
3280
3786
  }
3281
3787
  skipped.push(skill.name);
3282
3788
  continue;
@@ -3301,18 +3807,18 @@ ${outdated.length} skill(s) have updates available:
3301
3807
  skill.latestVersion
3302
3808
  );
3303
3809
  if (format === "human") {
3304
- console.log(pc16.green(` Updated ${pc16.bold(skill.name)}`));
3810
+ console.log(pc17.green(` Updated ${pc17.bold(skill.name)}`));
3305
3811
  }
3306
3812
  updated.push(skill.name);
3307
3813
  } else {
3308
3814
  if (format === "human") {
3309
- console.log(pc16.red(` Failed to update ${skill.name}: no agents linked`));
3815
+ console.log(pc17.red(` Failed to update ${skill.name}: no agents linked`));
3310
3816
  }
3311
3817
  failed.push({ name: skill.name, error: "no agents linked" });
3312
3818
  }
3313
3819
  if (installResult.errors.length > 0 && format === "human") {
3314
3820
  for (const err of installResult.errors) {
3315
- console.log(pc16.yellow(` ${err}`));
3821
+ console.log(pc17.yellow(` ${err}`));
3316
3822
  }
3317
3823
  }
3318
3824
  } finally {
@@ -3321,7 +3827,7 @@ ${outdated.length} skill(s) have updates available:
3321
3827
  } catch (err) {
3322
3828
  const msg = err instanceof Error ? err.message : String(err);
3323
3829
  if (format === "human") {
3324
- console.log(pc16.red(` Failed to update ${skill.name}: ${msg}`));
3830
+ console.log(pc17.red(` Failed to update ${skill.name}: ${msg}`));
3325
3831
  }
3326
3832
  failed.push({ name: skill.name, error: msg });
3327
3833
  }
@@ -3341,10 +3847,10 @@ ${outdated.length} skill(s) have updates available:
3341
3847
  }
3342
3848
  console.log();
3343
3849
  if (updated.length > 0) {
3344
- console.log(pc16.green(`Updated ${updated.length} skill(s).`));
3850
+ console.log(pc17.green(`Updated ${updated.length} skill(s).`));
3345
3851
  }
3346
3852
  if (failed.length > 0) {
3347
- console.log(pc16.red(`Failed to update ${failed.length} skill(s).`));
3853
+ console.log(pc17.red(`Failed to update ${failed.length} skill(s).`));
3348
3854
  }
3349
3855
  });
3350
3856
  }
@@ -3353,7 +3859,7 @@ ${outdated.length} skill(s) have updates available:
3353
3859
  import { existsSync as existsSync5 } from "fs";
3354
3860
  import { mkdir, writeFile } from "fs/promises";
3355
3861
  import { join as join5 } from "path";
3356
- import pc17 from "picocolors";
3862
+ import pc18 from "picocolors";
3357
3863
  function registerSkillsInit(parent) {
3358
3864
  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
3865
  const operation = "skills.init";
@@ -3379,7 +3885,7 @@ function registerSkillsInit(parent) {
3379
3885
  path: skillDir
3380
3886
  });
3381
3887
  } else {
3382
- console.error(pc17.red(message));
3888
+ console.error(pc18.red(message));
3383
3889
  }
3384
3890
  process.exit(1);
3385
3891
  }
@@ -3418,17 +3924,17 @@ Show example inputs and expected outputs.
3418
3924
  outputSuccess(operation, mvi, result);
3419
3925
  return;
3420
3926
  }
3421
- console.log(pc17.green(`\u2713 Created skill template: ${skillDir}/SKILL.md`));
3422
- console.log(pc17.dim("\nNext steps:"));
3423
- console.log(pc17.dim(" 1. Edit SKILL.md with your instructions"));
3424
- console.log(pc17.dim(` 2. Validate: caamp skills validate ${join5(skillDir, "SKILL.md")}`));
3425
- console.log(pc17.dim(` 3. Install: caamp skills install ${skillDir}`));
3927
+ console.log(pc18.green(`\u2713 Created skill template: ${skillDir}/SKILL.md`));
3928
+ console.log(pc18.dim("\nNext steps:"));
3929
+ console.log(pc18.dim(" 1. Edit SKILL.md with your instructions"));
3930
+ console.log(pc18.dim(` 2. Validate: caamp skills validate ${join5(skillDir, "SKILL.md")}`));
3931
+ console.log(pc18.dim(` 3. Install: caamp skills install ${skillDir}`));
3426
3932
  });
3427
3933
  }
3428
3934
 
3429
3935
  // src/commands/skills/audit.ts
3430
3936
  import { existsSync as existsSync6, statSync } from "fs";
3431
- import pc18 from "picocolors";
3937
+ import pc19 from "picocolors";
3432
3938
  function registerSkillsAudit(parent) {
3433
3939
  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
3940
  const operation = "skills.audit";
@@ -3517,7 +4023,7 @@ function registerSkillsAudit(parent) {
3517
4023
  outputSuccess(operation, mvi, summary2);
3518
4024
  return;
3519
4025
  }
3520
- console.log(pc18.dim("No SKILL.md files found to scan."));
4026
+ console.log(pc19.dim("No SKILL.md files found to scan."));
3521
4027
  return;
3522
4028
  }
3523
4029
  const summary = {
@@ -3551,21 +4057,21 @@ function registerSkillsAudit(parent) {
3551
4057
  }
3552
4058
  let totalFindings = 0;
3553
4059
  for (const result of results) {
3554
- const icon = result.passed ? pc18.green("\u2713") : pc18.red("\u2717");
4060
+ const icon = result.passed ? pc19.green("\u2713") : pc19.red("\u2717");
3555
4061
  console.log(`
3556
- ${icon} ${pc18.bold(result.file)} (score: ${result.score}/100)`);
4062
+ ${icon} ${pc19.bold(result.file)} (score: ${result.score}/100)`);
3557
4063
  if (result.findings.length === 0) {
3558
- console.log(pc18.dim(" No issues found."));
4064
+ console.log(pc19.dim(" No issues found."));
3559
4065
  continue;
3560
4066
  }
3561
4067
  totalFindings += result.findings.length;
3562
4068
  for (const f of result.findings) {
3563
- const sev = f.rule.severity === "critical" ? pc18.red(f.rule.severity) : f.rule.severity === "high" ? pc18.red(f.rule.severity) : f.rule.severity === "medium" ? pc18.yellow(f.rule.severity) : pc18.dim(f.rule.severity);
4069
+ 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
4070
  console.log(` ${sev.padEnd(20)} ${f.rule.id} ${f.rule.name}`);
3565
- console.log(` ${pc18.dim(`L${f.line}: ${f.context.slice(0, 80)}`)}`);
4071
+ console.log(` ${pc19.dim(`L${f.line}: ${f.context.slice(0, 80)}`)}`);
3566
4072
  }
3567
4073
  }
3568
- console.log(pc18.bold(`
4074
+ console.log(pc19.bold(`
3569
4075
  ${results.length} file(s) scanned, ${totalFindings} finding(s)`));
3570
4076
  if (!allPassed) {
3571
4077
  process.exit(1);
@@ -3575,7 +4081,7 @@ ${results.length} file(s) scanned, ${totalFindings} finding(s)`));
3575
4081
 
3576
4082
  // src/commands/skills/validate.ts
3577
4083
  import { resolveOutputFormat as resolveOutputFormat5 } from "@cleocode/lafs-protocol";
3578
- import pc19 from "picocolors";
4084
+ import pc20 from "picocolors";
3579
4085
  function registerSkillsValidate(parent) {
3580
4086
  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
4087
  const operation = "skills.validate";
@@ -3602,7 +4108,7 @@ function registerSkillsValidate(parent) {
3602
4108
  path
3603
4109
  });
3604
4110
  } else {
3605
- console.error(pc19.red(message));
4111
+ console.error(pc20.red(message));
3606
4112
  }
3607
4113
  process.exit(1);
3608
4114
  }
@@ -3624,12 +4130,12 @@ function registerSkillsValidate(parent) {
3624
4130
  console.log(JSON.stringify(envelope, null, 2));
3625
4131
  } else {
3626
4132
  if (result.valid) {
3627
- console.log(pc19.green(`\u2713 ${path} is valid`));
4133
+ console.log(pc20.green(`\u2713 ${path} is valid`));
3628
4134
  } else {
3629
- console.log(pc19.red(`\u2717 ${path} has validation errors`));
4135
+ console.log(pc20.red(`\u2717 ${path} has validation errors`));
3630
4136
  }
3631
4137
  for (const issue of result.issues) {
3632
- const icon = issue.level === "error" ? pc19.red("\u2717") : pc19.yellow("!");
4138
+ const icon = issue.level === "error" ? pc20.red("\u2717") : pc20.yellow("!");
3633
4139
  console.log(` ${icon} [${issue.field}] ${issue.message}`);
3634
4140
  }
3635
4141
  }
@@ -3665,6 +4171,7 @@ program.hook("preAction", (thisCommand) => {
3665
4171
  registerProvidersCommand(program);
3666
4172
  registerSkillsCommands(program);
3667
4173
  registerMcpCommands(program);
4174
+ registerCleoCommands(program);
3668
4175
  registerInstructionsCommands(program);
3669
4176
  registerConfigCommand(program);
3670
4177
  registerDoctorCommand(program);