mcpman 0.3.0 → 0.4.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/index.cjs CHANGED
@@ -159,9 +159,9 @@ async function atomicWrite(filePath, content) {
159
159
  throw err;
160
160
  }
161
161
  }
162
- async function pathExists(p11) {
162
+ async function pathExists(p12) {
163
163
  try {
164
- await import_node_fs3.default.promises.access(p11);
164
+ await import_node_fs3.default.promises.access(p12);
165
165
  return true;
166
166
  } catch {
167
167
  return false;
@@ -237,6 +237,12 @@ var init_base_client_handler = __esm({
237
237
  function getHomedir() {
238
238
  return import_node_os3.default.homedir();
239
239
  }
240
+ function getMcpmanDir() {
241
+ return import_node_path4.default.join(import_node_os3.default.homedir(), ".mcpman");
242
+ }
243
+ function getConfigPath() {
244
+ return import_node_path4.default.join(getMcpmanDir(), "config.json");
245
+ }
240
246
  function getAppDataDir() {
241
247
  const home = getHomedir();
242
248
  if (process.platform === "darwin") {
@@ -432,12 +438,12 @@ __export(vault_service_exports, {
432
438
  writeVault: () => writeVault
433
439
  });
434
440
  function getVaultPath() {
435
- return import_node_path6.default.join(import_node_os4.default.homedir(), ".mcpman", "vault.enc");
441
+ return import_node_path7.default.join(import_node_os4.default.homedir(), ".mcpman", "vault.enc");
436
442
  }
437
443
  function readVault(vaultPath = getVaultPath()) {
438
444
  const empty = { version: 1, servers: {} };
439
445
  try {
440
- const raw = import_node_fs4.default.readFileSync(vaultPath, "utf-8");
446
+ const raw = import_node_fs5.default.readFileSync(vaultPath, "utf-8");
441
447
  const parsed = JSON.parse(raw);
442
448
  if (parsed.version !== 1 || typeof parsed.servers !== "object") return empty;
443
449
  return parsed;
@@ -446,16 +452,16 @@ function readVault(vaultPath = getVaultPath()) {
446
452
  }
447
453
  }
448
454
  function writeVault(data, vaultPath = getVaultPath()) {
449
- const dir = import_node_path6.default.dirname(vaultPath);
450
- import_node_fs4.default.mkdirSync(dir, { recursive: true });
455
+ const dir = import_node_path7.default.dirname(vaultPath);
456
+ import_node_fs5.default.mkdirSync(dir, { recursive: true });
451
457
  const tmp = `${vaultPath}.tmp`;
452
- import_node_fs4.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
458
+ import_node_fs5.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
453
459
  if (process.platform !== "win32") {
454
- import_node_fs4.default.chmodSync(tmp, 384);
460
+ import_node_fs5.default.chmodSync(tmp, 384);
455
461
  }
456
- import_node_fs4.default.renameSync(tmp, vaultPath);
462
+ import_node_fs5.default.renameSync(tmp, vaultPath);
457
463
  if (process.platform !== "win32") {
458
- import_node_fs4.default.chmodSync(vaultPath, 384);
464
+ import_node_fs5.default.chmodSync(vaultPath, 384);
459
465
  }
460
466
  }
461
467
  function encrypt(value, password2) {
@@ -482,20 +488,20 @@ function decrypt(entry, password2) {
482
488
  ]);
483
489
  return decrypted.toString("utf-8");
484
490
  }
485
- async function getMasterPassword(confirm8 = false) {
491
+ async function getMasterPassword(confirm9 = false) {
486
492
  if (_cachedPassword) return _cachedPassword;
487
- const password2 = await p3.password({
493
+ const password2 = await p4.password({
488
494
  message: "Enter vault master password:",
489
495
  validate: (v) => v.length < 8 ? "Password must be at least 8 characters" : void 0
490
496
  });
491
- if (p3.isCancel(password2)) {
492
- p3.cancel("Vault access cancelled.");
497
+ if (p4.isCancel(password2)) {
498
+ p4.cancel("Vault access cancelled.");
493
499
  process.exit(0);
494
500
  }
495
- if (confirm8) {
496
- const confirm22 = await p3.password({ message: "Confirm master password:" });
497
- if (p3.isCancel(confirm22) || confirm22 !== password2) {
498
- p3.cancel("Passwords do not match.");
501
+ if (confirm9) {
502
+ const confirm22 = await p4.password({ message: "Confirm master password:" });
503
+ if (p4.isCancel(confirm22) || confirm22 !== password2) {
504
+ p4.cancel("Passwords do not match.");
499
505
  process.exit(1);
500
506
  }
501
507
  }
@@ -545,16 +551,16 @@ function listSecrets(server, vaultPath = getVaultPath()) {
545
551
  keys: Object.keys(keys)
546
552
  }));
547
553
  }
548
- var import_node_crypto2, import_node_fs4, import_node_os4, import_node_path6, p3, _cachedPassword;
554
+ var import_node_crypto2, import_node_fs5, import_node_os4, import_node_path7, p4, _cachedPassword;
549
555
  var init_vault_service = __esm({
550
556
  "src/core/vault-service.ts"() {
551
557
  "use strict";
552
558
  init_cjs_shims();
553
559
  import_node_crypto2 = __toESM(require("crypto"), 1);
554
- import_node_fs4 = __toESM(require("fs"), 1);
560
+ import_node_fs5 = __toESM(require("fs"), 1);
555
561
  import_node_os4 = __toESM(require("os"), 1);
556
- import_node_path6 = __toESM(require("path"), 1);
557
- p3 = __toESM(require("@clack/prompts"), 1);
562
+ import_node_path7 = __toESM(require("path"), 1);
563
+ p4 = __toESM(require("@clack/prompts"), 1);
558
564
  _cachedPassword = null;
559
565
  process.on("exit", () => {
560
566
  _cachedPassword = null;
@@ -564,7 +570,7 @@ var init_vault_service = __esm({
564
570
 
565
571
  // src/index.ts
566
572
  init_cjs_shims();
567
- var import_citty10 = require("citty");
573
+ var import_citty14 = require("citty");
568
574
 
569
575
  // src/commands/audit.ts
570
576
  init_cjs_shims();
@@ -767,11 +773,11 @@ async function scanAllServers(servers, concurrency = 3) {
767
773
  const results = [];
768
774
  const executing = /* @__PURE__ */ new Set();
769
775
  for (const [name, entry] of entries) {
770
- const p11 = scanServer(name, entry).then((r) => {
776
+ const p12 = scanServer(name, entry).then((r) => {
771
777
  results.push(r);
772
- executing.delete(p11);
778
+ executing.delete(p12);
773
779
  });
774
- executing.add(p11);
780
+ executing.add(p12);
775
781
  if (executing.size >= concurrency) await Promise.race(executing);
776
782
  }
777
783
  await Promise.all(executing);
@@ -886,11 +892,11 @@ async function checkAllVersions(lockfile) {
886
892
  const results = [];
887
893
  const executing = /* @__PURE__ */ new Set();
888
894
  for (const [name, entry] of entries) {
889
- const p11 = checkVersion(name, entry).then((r) => {
895
+ const p12 = checkVersion(name, entry).then((r) => {
890
896
  results.push(r);
891
- executing.delete(p11);
897
+ executing.delete(p12);
892
898
  });
893
- executing.add(p11);
899
+ executing.add(p12);
894
900
  if (executing.size >= 5) {
895
901
  await Promise.race(executing);
896
902
  }
@@ -1334,10 +1340,160 @@ async function runAuditFix(reports, servers, skipConfirm) {
1334
1340
  `);
1335
1341
  }
1336
1342
 
1337
- // src/commands/doctor.ts
1343
+ // src/commands/config.ts
1338
1344
  init_cjs_shims();
1339
1345
  var import_citty2 = require("citty");
1340
1346
  var import_picocolors2 = __toESM(require("picocolors"), 1);
1347
+ var p2 = __toESM(require("@clack/prompts"), 1);
1348
+
1349
+ // src/core/config-service.ts
1350
+ init_cjs_shims();
1351
+ var import_node_fs4 = __toESM(require("fs"), 1);
1352
+ var import_node_path5 = __toESM(require("path"), 1);
1353
+ init_paths();
1354
+ var VALID_KEYS = /* @__PURE__ */ new Set([
1355
+ "defaultClient",
1356
+ "updateCheckInterval",
1357
+ "preferredRegistry",
1358
+ "vaultTimeout"
1359
+ ]);
1360
+ function readConfig(configPath = getConfigPath()) {
1361
+ try {
1362
+ const raw = import_node_fs4.default.readFileSync(configPath, "utf-8");
1363
+ const parsed = JSON.parse(raw);
1364
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
1365
+ return {};
1366
+ }
1367
+ return parsed;
1368
+ } catch {
1369
+ return {};
1370
+ }
1371
+ }
1372
+ function writeConfig(data, configPath = getConfigPath()) {
1373
+ const dir = import_node_path5.default.dirname(configPath);
1374
+ import_node_fs4.default.mkdirSync(dir, { recursive: true });
1375
+ const tmp = `${configPath}.tmp`;
1376
+ import_node_fs4.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { encoding: "utf-8" });
1377
+ import_node_fs4.default.renameSync(tmp, configPath);
1378
+ }
1379
+ function getConfigValue(key, configPath = getConfigPath()) {
1380
+ const data = readConfig(configPath);
1381
+ if (!VALID_KEYS.has(key)) return void 0;
1382
+ return data[key];
1383
+ }
1384
+ function setConfigValue(key, value, configPath = getConfigPath()) {
1385
+ const data = readConfig(configPath);
1386
+ if (!VALID_KEYS.has(key)) {
1387
+ throw new Error(`Unknown config key: "${key}". Valid keys: ${[...VALID_KEYS].join(", ")}`);
1388
+ }
1389
+ data[key] = value;
1390
+ writeConfig(data, configPath);
1391
+ }
1392
+
1393
+ // src/commands/config.ts
1394
+ function coerceValue(raw) {
1395
+ if (raw === "true") return true;
1396
+ if (raw === "false") return false;
1397
+ const num = Number(raw);
1398
+ if (!Number.isNaN(num) && raw.trim() !== "") return num;
1399
+ return raw;
1400
+ }
1401
+ var setCommand = (0, import_citty2.defineCommand)({
1402
+ meta: { name: "set", description: "Set a config value" },
1403
+ args: {
1404
+ key: {
1405
+ type: "positional",
1406
+ description: "Config key (e.g. defaultClient)",
1407
+ required: true
1408
+ },
1409
+ value: {
1410
+ type: "positional",
1411
+ description: "Value to set",
1412
+ required: true
1413
+ }
1414
+ },
1415
+ run({ args }) {
1416
+ try {
1417
+ const coerced = coerceValue(args.value);
1418
+ setConfigValue(args.key, coerced);
1419
+ console.log(
1420
+ `${import_picocolors2.default.green("\u2713")} Set ${import_picocolors2.default.bold(args.key)} = ${import_picocolors2.default.cyan(String(coerced))}`
1421
+ );
1422
+ } catch (err) {
1423
+ console.error(`${import_picocolors2.default.red("\u2717")} ${String(err)}`);
1424
+ process.exit(1);
1425
+ }
1426
+ }
1427
+ });
1428
+ var getCommand = (0, import_citty2.defineCommand)({
1429
+ meta: { name: "get", description: "Get a config value" },
1430
+ args: {
1431
+ key: {
1432
+ type: "positional",
1433
+ description: "Config key to read",
1434
+ required: true
1435
+ }
1436
+ },
1437
+ run({ args }) {
1438
+ const val = getConfigValue(args.key);
1439
+ if (val === void 0) {
1440
+ console.log(import_picocolors2.default.dim(`${args.key}: (not set)`));
1441
+ } else {
1442
+ console.log(`${import_picocolors2.default.bold(args.key)}: ${import_picocolors2.default.cyan(String(val))}`);
1443
+ }
1444
+ }
1445
+ });
1446
+ var listCommand = (0, import_citty2.defineCommand)({
1447
+ meta: { name: "list", description: "List all config values" },
1448
+ run() {
1449
+ const data = readConfig();
1450
+ const entries = Object.entries(data);
1451
+ if (entries.length === 0) {
1452
+ console.log(import_picocolors2.default.dim("No config values set. Use `mcpman config set <key> <value>`."));
1453
+ return;
1454
+ }
1455
+ console.log("");
1456
+ console.log(import_picocolors2.default.bold("mcpman config:"));
1457
+ console.log("");
1458
+ for (const [key, val] of entries) {
1459
+ console.log(` ${import_picocolors2.default.green("\u25CF")} ${import_picocolors2.default.bold(key)} ${import_picocolors2.default.cyan(String(val))}`);
1460
+ }
1461
+ console.log("");
1462
+ console.log(import_picocolors2.default.dim(` ${entries.length} key${entries.length !== 1 ? "s" : ""} configured`));
1463
+ }
1464
+ });
1465
+ var resetCommand = (0, import_citty2.defineCommand)({
1466
+ meta: { name: "reset", description: "Reset config to defaults (removes config file)" },
1467
+ async run() {
1468
+ const confirmed = await p2.confirm({
1469
+ message: "Reset all config values to defaults?",
1470
+ initialValue: false
1471
+ });
1472
+ if (p2.isCancel(confirmed) || !confirmed) {
1473
+ p2.cancel("Cancelled.");
1474
+ return;
1475
+ }
1476
+ writeConfig({});
1477
+ console.log(`${import_picocolors2.default.green("\u2713")} Config reset to defaults.`);
1478
+ }
1479
+ });
1480
+ var config_default = (0, import_citty2.defineCommand)({
1481
+ meta: {
1482
+ name: "config",
1483
+ description: "Manage mcpman CLI configuration"
1484
+ },
1485
+ subCommands: {
1486
+ set: setCommand,
1487
+ get: getCommand,
1488
+ list: listCommand,
1489
+ reset: resetCommand
1490
+ }
1491
+ });
1492
+
1493
+ // src/commands/doctor.ts
1494
+ init_cjs_shims();
1495
+ var import_citty3 = require("citty");
1496
+ var import_picocolors3 = __toESM(require("picocolors"), 1);
1341
1497
 
1342
1498
  // src/core/server-inventory.ts
1343
1499
  init_cjs_shims();
@@ -1608,12 +1764,12 @@ async function quickHealthProbe(config, timeoutMs = 3e3) {
1608
1764
 
1609
1765
  // src/commands/doctor.ts
1610
1766
  var CHECK_ICON = {
1611
- pass: import_picocolors2.default.green("\u2713"),
1612
- fail: import_picocolors2.default.red("\u2717"),
1613
- skip: import_picocolors2.default.dim("-"),
1614
- warn: import_picocolors2.default.yellow("\u26A0")
1767
+ pass: import_picocolors3.default.green("\u2713"),
1768
+ fail: import_picocolors3.default.red("\u2717"),
1769
+ skip: import_picocolors3.default.dim("-"),
1770
+ warn: import_picocolors3.default.yellow("\u26A0")
1615
1771
  };
1616
- var doctor_default = (0, import_citty2.defineCommand)({
1772
+ var doctor_default = (0, import_citty3.defineCommand)({
1617
1773
  meta: {
1618
1774
  name: "doctor",
1619
1775
  description: "Check MCP server health and configuration"
@@ -1626,10 +1782,10 @@ var doctor_default = (0, import_citty2.defineCommand)({
1626
1782
  }
1627
1783
  },
1628
1784
  async run({ args }) {
1629
- console.log(import_picocolors2.default.bold("\n mcpman doctor\n"));
1785
+ console.log(import_picocolors3.default.bold("\n mcpman doctor\n"));
1630
1786
  const servers = await getInstalledServers();
1631
1787
  if (servers.length === 0) {
1632
- console.log(import_picocolors2.default.dim(" No MCP servers installed. Run mcpman install <server> to get started."));
1788
+ console.log(import_picocolors3.default.dim(" No MCP servers installed. Run mcpman install <server> to get started."));
1633
1789
  return;
1634
1790
  }
1635
1791
  const tasks = servers.map((s) => () => checkServerHealth(s.name, s.config));
@@ -1641,14 +1797,14 @@ var doctor_default = (0, import_citty2.defineCommand)({
1641
1797
  if (result.status === "healthy") passed++;
1642
1798
  else failed++;
1643
1799
  }
1644
- console.log(import_picocolors2.default.dim(" " + "\u2500".repeat(50)));
1800
+ console.log(import_picocolors3.default.dim(" " + "\u2500".repeat(50)));
1645
1801
  const parts = [];
1646
- if (passed > 0) parts.push(import_picocolors2.default.green(`${passed} healthy`));
1647
- if (failed > 0) parts.push(import_picocolors2.default.red(`${failed} unhealthy`));
1802
+ if (passed > 0) parts.push(import_picocolors3.default.green(`${passed} healthy`));
1803
+ if (failed > 0) parts.push(import_picocolors3.default.red(`${failed} unhealthy`));
1648
1804
  console.log(` Summary: ${parts.join(", ")}`);
1649
1805
  if (failed > 0) {
1650
1806
  if (!args.fix) {
1651
- console.log(import_picocolors2.default.dim(` Run ${import_picocolors2.default.cyan("mcpman doctor --fix")} for fix suggestions.
1807
+ console.log(import_picocolors3.default.dim(` Run ${import_picocolors3.default.cyan("mcpman doctor --fix")} for fix suggestions.
1652
1808
  `));
1653
1809
  }
1654
1810
  process.exit(1);
@@ -1657,13 +1813,13 @@ var doctor_default = (0, import_citty2.defineCommand)({
1657
1813
  }
1658
1814
  });
1659
1815
  function printServerResult(result, showFix) {
1660
- const icon = result.status === "healthy" ? import_picocolors2.default.green("\u25CF") : import_picocolors2.default.red("\u25CF");
1661
- console.log(` ${icon} ${import_picocolors2.default.bold(result.serverName)}`);
1816
+ const icon = result.status === "healthy" ? import_picocolors3.default.green("\u25CF") : import_picocolors3.default.red("\u25CF");
1817
+ console.log(` ${icon} ${import_picocolors3.default.bold(result.serverName)}`);
1662
1818
  for (const check of result.checks) {
1663
1819
  const checkIcon = check.skipped ? CHECK_ICON.skip : check.passed ? CHECK_ICON.pass : CHECK_ICON.fail;
1664
1820
  console.log(` ${checkIcon} ${check.name}: ${check.message}`);
1665
1821
  if (showFix && !check.passed && !check.skipped && check.fix) {
1666
- console.log(` ${import_picocolors2.default.yellow("\u2192")} Fix: ${import_picocolors2.default.cyan(check.fix)}`);
1822
+ console.log(` ${import_picocolors3.default.yellow("\u2192")} Fix: ${import_picocolors3.default.cyan(check.fix)}`);
1667
1823
  }
1668
1824
  }
1669
1825
  console.log();
@@ -1672,11 +1828,11 @@ async function runParallel(tasks, concurrency) {
1672
1828
  const results = [];
1673
1829
  const executing = /* @__PURE__ */ new Set();
1674
1830
  for (const task of tasks) {
1675
- const p11 = task().then((r) => {
1831
+ const p12 = task().then((r) => {
1676
1832
  results.push(r);
1677
- executing.delete(p11);
1833
+ executing.delete(p12);
1678
1834
  });
1679
- executing.add(p11);
1835
+ executing.add(p12);
1680
1836
  if (executing.size >= concurrency) {
1681
1837
  await Promise.race(executing);
1682
1838
  }
@@ -1685,12 +1841,177 @@ async function runParallel(tasks, concurrency) {
1685
1841
  return results;
1686
1842
  }
1687
1843
 
1844
+ // src/commands/info.ts
1845
+ init_cjs_shims();
1846
+ var import_citty4 = require("citty");
1847
+ var import_picocolors4 = __toESM(require("picocolors"), 1);
1848
+ var import_nanospinner2 = require("nanospinner");
1849
+
1850
+ // src/core/package-info.ts
1851
+ init_cjs_shims();
1852
+ init_trust_scorer();
1853
+ async function buildInfo(name, entry, source = "npm") {
1854
+ const resolvedSource = entry?.source ?? source;
1855
+ let weeklyDownloads = 0;
1856
+ let maintainerCount = 0;
1857
+ let packageAge = 0;
1858
+ let lastPublish = "";
1859
+ let deprecated = false;
1860
+ let trustScore = null;
1861
+ let riskLevel = "UNKNOWN";
1862
+ if (resolvedSource === "npm") {
1863
+ const metadata = await fetchNpmMetadata(name);
1864
+ if (!metadata && !entry) {
1865
+ return null;
1866
+ }
1867
+ if (metadata) {
1868
+ weeklyDownloads = metadata.weeklyDownloads;
1869
+ maintainerCount = metadata.maintainerCount;
1870
+ packageAge = metadata.packageAge;
1871
+ lastPublish = metadata.lastPublish;
1872
+ deprecated = metadata.deprecated;
1873
+ const scored = computeTrustScore(metadata, []);
1874
+ trustScore = scored.score;
1875
+ riskLevel = scored.riskLevel;
1876
+ }
1877
+ }
1878
+ return {
1879
+ name,
1880
+ version: entry?.version ?? "unknown",
1881
+ description: "",
1882
+ source: resolvedSource,
1883
+ runtime: entry?.runtime ?? "node",
1884
+ envVars: entry?.envVars ?? [],
1885
+ weeklyDownloads,
1886
+ maintainerCount,
1887
+ packageAge,
1888
+ lastPublish,
1889
+ deprecated,
1890
+ trustScore,
1891
+ riskLevel,
1892
+ installedClients: entry?.clients ?? [],
1893
+ isInstalled: entry !== null
1894
+ };
1895
+ }
1896
+ async function getPackageInfo(serverName) {
1897
+ const lockfile = readLockfile();
1898
+ const entry = lockfile.servers[serverName] ?? null;
1899
+ return buildInfo(serverName, entry);
1900
+ }
1901
+
1902
+ // src/commands/info.ts
1903
+ function colorRisk2(score, riskLevel) {
1904
+ const label = score !== null ? `${score}/100 (${riskLevel})` : riskLevel;
1905
+ if (riskLevel === "LOW") return import_picocolors4.default.green(label);
1906
+ if (riskLevel === "MEDIUM") return import_picocolors4.default.yellow(label);
1907
+ if (riskLevel === "HIGH") return import_picocolors4.default.red(label);
1908
+ if (riskLevel === "CRITICAL") return import_picocolors4.default.bold(import_picocolors4.default.red(label));
1909
+ return import_picocolors4.default.dim(label);
1910
+ }
1911
+ function formatDaysAgo(isoDate) {
1912
+ if (!isoDate) return "unknown";
1913
+ const days = Math.floor((Date.now() - new Date(isoDate).getTime()) / 864e5);
1914
+ if (days === 0) return "today";
1915
+ if (days === 1) return "1 day ago";
1916
+ return `${days} days ago`;
1917
+ }
1918
+ function printInfo(info2) {
1919
+ const installedBadge = info2.isInstalled ? import_picocolors4.default.green(" [installed]") : import_picocolors4.default.dim(" [not installed]");
1920
+ console.log();
1921
+ console.log(import_picocolors4.default.bold(` ${info2.name}@${info2.version}`) + installedBadge);
1922
+ console.log(import_picocolors4.default.dim(" " + "\u2500".repeat(60)));
1923
+ console.log(` ${import_picocolors4.default.dim("Source:")} ${info2.source}`);
1924
+ console.log(` ${import_picocolors4.default.dim("Runtime:")} ${info2.runtime}`);
1925
+ if (info2.description) {
1926
+ console.log(` ${import_picocolors4.default.dim("Description:")} ${info2.description}`);
1927
+ }
1928
+ if (info2.deprecated) {
1929
+ console.log(` ${import_picocolors4.default.red("[DEPRECATED]")} This package is deprecated`);
1930
+ }
1931
+ console.log();
1932
+ console.log(` ${import_picocolors4.default.bold("Trust & Security")}`);
1933
+ console.log(` ${import_picocolors4.default.dim("Trust score:")} ${colorRisk2(info2.trustScore, info2.riskLevel)}`);
1934
+ if (info2.source === "npm") {
1935
+ console.log(
1936
+ ` ${import_picocolors4.default.dim("Downloads:")} ${info2.weeklyDownloads.toLocaleString()}/week ${import_picocolors4.default.dim("|")} ${import_picocolors4.default.dim("Age:")} ${info2.packageAge}d ${import_picocolors4.default.dim("|")} ${import_picocolors4.default.dim("Maintainers:")} ${info2.maintainerCount}`
1937
+ );
1938
+ if (info2.lastPublish) {
1939
+ console.log(` ${import_picocolors4.default.dim("Last publish:")} ${formatDaysAgo(info2.lastPublish)}`);
1940
+ }
1941
+ } else {
1942
+ console.log(import_picocolors4.default.dim(" (Trust data available for npm packages only)"));
1943
+ }
1944
+ console.log();
1945
+ console.log(` ${import_picocolors4.default.bold("Environment Variables")}`);
1946
+ if (info2.envVars.length > 0) {
1947
+ for (const env of info2.envVars) {
1948
+ console.log(` ${import_picocolors4.default.cyan("\u2022")} ${env}`);
1949
+ }
1950
+ } else {
1951
+ console.log(import_picocolors4.default.dim(" none required"));
1952
+ }
1953
+ console.log();
1954
+ console.log(` ${import_picocolors4.default.bold("Installed Clients")}`);
1955
+ if (info2.installedClients.length > 0) {
1956
+ for (const client of info2.installedClients) {
1957
+ console.log(` ${import_picocolors4.default.green("\u2713")} ${client}`);
1958
+ }
1959
+ } else {
1960
+ console.log(import_picocolors4.default.dim(" Not installed in any client"));
1961
+ }
1962
+ console.log();
1963
+ console.log(import_picocolors4.default.dim(" " + "\u2500".repeat(60)));
1964
+ console.log();
1965
+ }
1966
+ var info_default = (0, import_citty4.defineCommand)({
1967
+ meta: {
1968
+ name: "info",
1969
+ description: "Show detailed metadata for an MCP server (installed or from registry)"
1970
+ },
1971
+ args: {
1972
+ server: {
1973
+ type: "positional",
1974
+ description: "Server name (e.g. @modelcontextprotocol/server-filesystem)",
1975
+ required: true
1976
+ },
1977
+ json: {
1978
+ type: "boolean",
1979
+ description: "Output results as JSON",
1980
+ default: false
1981
+ }
1982
+ },
1983
+ async run({ args }) {
1984
+ const spinner5 = (0, import_nanospinner2.createSpinner)(`Fetching info for ${args.server}...`).start();
1985
+ let info2;
1986
+ try {
1987
+ info2 = await getPackageInfo(args.server);
1988
+ } catch (err) {
1989
+ spinner5.error({ text: "Failed to fetch package info" });
1990
+ console.error(import_picocolors4.default.red(String(err)));
1991
+ process.exit(1);
1992
+ }
1993
+ if (!info2) {
1994
+ spinner5.error({ text: `Package not found: ${args.server}` });
1995
+ console.log(import_picocolors4.default.dim(`
1996
+ "${args.server}" was not found in the npm registry or your lockfile.
1997
+ `));
1998
+ process.exit(1);
1999
+ }
2000
+ spinner5.success({ text: `Found ${args.server}` });
2001
+ if (args.json) {
2002
+ console.log(JSON.stringify(info2, null, 2));
2003
+ return;
2004
+ }
2005
+ printInfo(info2);
2006
+ }
2007
+ });
2008
+
1688
2009
  // src/commands/init.ts
1689
2010
  init_cjs_shims();
1690
- var import_citty3 = require("citty");
1691
- var p2 = __toESM(require("@clack/prompts"), 1);
1692
- var import_node_path5 = __toESM(require("path"), 1);
1693
- var init_default = (0, import_citty3.defineCommand)({
2011
+ var import_citty5 = require("citty");
2012
+ var p3 = __toESM(require("@clack/prompts"), 1);
2013
+ var import_node_path6 = __toESM(require("path"), 1);
2014
+ var init_default = (0, import_citty5.defineCommand)({
1694
2015
  meta: {
1695
2016
  name: "init",
1696
2017
  description: "Initialize mcpman.lock in the current project"
@@ -1705,17 +2026,17 @@ var init_default = (0, import_citty3.defineCommand)({
1705
2026
  },
1706
2027
  async run({ args }) {
1707
2028
  const nonInteractive = args.yes || !process.stdout.isTTY;
1708
- p2.intro("mcpman init");
1709
- const targetPath = import_node_path5.default.join(process.cwd(), LOCKFILE_NAME);
2029
+ p3.intro("mcpman init");
2030
+ const targetPath = import_node_path6.default.join(process.cwd(), LOCKFILE_NAME);
1710
2031
  const existing = findLockfile();
1711
2032
  if (existing) {
1712
2033
  if (nonInteractive) {
1713
- p2.log.warn(`Lockfile already exists: ${existing} \u2014 overwriting (non-interactive).`);
2034
+ p3.log.warn(`Lockfile already exists: ${existing} \u2014 overwriting (non-interactive).`);
1714
2035
  } else {
1715
- p2.log.warn(`Lockfile already exists: ${existing}`);
1716
- const overwrite = await p2.confirm({ message: "Overwrite?" });
1717
- if (p2.isCancel(overwrite) || !overwrite) {
1718
- p2.outro("Cancelled.");
2036
+ p3.log.warn(`Lockfile already exists: ${existing}`);
2037
+ const overwrite = await p3.confirm({ message: "Overwrite?" });
2038
+ if (p3.isCancel(overwrite) || !overwrite) {
2039
+ p3.outro("Cancelled.");
1719
2040
  return;
1720
2041
  }
1721
2042
  }
@@ -1725,7 +2046,7 @@ var init_default = (0, import_citty3.defineCommand)({
1725
2046
  const mod = await Promise.resolve().then(() => (init_client_detector(), client_detector_exports));
1726
2047
  clients = await mod.getInstalledClients();
1727
2048
  } catch {
1728
- p2.log.warn("Could not detect AI clients \u2014 creating empty lockfile.");
2049
+ p3.log.warn("Could not detect AI clients \u2014 creating empty lockfile.");
1729
2050
  }
1730
2051
  const clientServers = [];
1731
2052
  for (const client of clients) {
@@ -1739,26 +2060,26 @@ var init_default = (0, import_citty3.defineCommand)({
1739
2060
  }
1740
2061
  createEmptyLockfile(targetPath);
1741
2062
  if (clientServers.length === 0) {
1742
- p2.log.info("No existing servers found in any client config.");
1743
- p2.outro(`Created ${LOCKFILE_NAME} \u2014 add it to version control!`);
2063
+ p3.log.info("No existing servers found in any client config.");
2064
+ p3.outro(`Created ${LOCKFILE_NAME} \u2014 add it to version control!`);
1744
2065
  return;
1745
2066
  }
1746
2067
  let selected;
1747
2068
  if (nonInteractive) {
1748
2069
  selected = clientServers.map((cs) => cs.client.type);
1749
- p2.log.info(`Non-interactive mode: importing all ${clientServers.length} client(s).`);
2070
+ p3.log.info(`Non-interactive mode: importing all ${clientServers.length} client(s).`);
1750
2071
  } else {
1751
2072
  const options = clientServers.map((cs) => ({
1752
2073
  value: cs.client.type,
1753
2074
  label: `${cs.client.displayName} (${Object.keys(cs.servers).length} servers)`
1754
2075
  }));
1755
- const toImport = await p2.multiselect({
2076
+ const toImport = await p3.multiselect({
1756
2077
  message: "Import existing servers into lockfile?",
1757
2078
  options,
1758
2079
  required: false
1759
2080
  });
1760
- if (p2.isCancel(toImport)) {
1761
- p2.outro(`Created empty ${LOCKFILE_NAME}`);
2081
+ if (p3.isCancel(toImport)) {
2082
+ p3.outro(`Created empty ${LOCKFILE_NAME}`);
1762
2083
  return;
1763
2084
  }
1764
2085
  selected = toImport;
@@ -1784,7 +2105,7 @@ var init_default = (0, import_citty3.defineCommand)({
1784
2105
  importCount++;
1785
2106
  }
1786
2107
  }
1787
- p2.outro(
2108
+ p3.outro(
1788
2109
  `Created ${LOCKFILE_NAME} with ${importCount} server(s) \u2014 commit to version control!`
1789
2110
  );
1790
2111
  }
@@ -1792,15 +2113,15 @@ var init_default = (0, import_citty3.defineCommand)({
1792
2113
 
1793
2114
  // src/commands/install.ts
1794
2115
  init_cjs_shims();
1795
- var import_citty4 = require("citty");
2116
+ var import_citty6 = require("citty");
1796
2117
 
1797
2118
  // src/core/installer.ts
1798
2119
  init_cjs_shims();
1799
- var p5 = __toESM(require("@clack/prompts"), 1);
2120
+ var p6 = __toESM(require("@clack/prompts"), 1);
1800
2121
 
1801
2122
  // src/core/installer-vault-helpers.ts
1802
2123
  init_cjs_shims();
1803
- var p4 = __toESM(require("@clack/prompts"), 1);
2124
+ var p5 = __toESM(require("@clack/prompts"), 1);
1804
2125
  init_vault_service();
1805
2126
  async function tryLoadVaultSecrets(serverName) {
1806
2127
  try {
@@ -1818,17 +2139,17 @@ async function offerVaultSave(serverName, newVars, yes) {
1818
2139
  if (Object.keys(newVars).length === 0) return;
1819
2140
  if (yes) return;
1820
2141
  try {
1821
- const save = await p4.confirm({
2142
+ const save = await p5.confirm({
1822
2143
  message: `Save ${Object.keys(newVars).length} env var(s) to encrypted vault for future installs?`
1823
2144
  });
1824
- if (p4.isCancel(save) || !save) return;
2145
+ if (p5.isCancel(save) || !save) return;
1825
2146
  const password2 = await getMasterPassword();
1826
2147
  for (const [key, value] of Object.entries(newVars)) {
1827
2148
  setSecret(serverName, key, value, password2);
1828
2149
  }
1829
- p4.log.success(`Credentials saved to vault for '${serverName}'`);
2150
+ p5.log.success(`Credentials saved to vault for '${serverName}'`);
1830
2151
  } catch (err) {
1831
- p4.log.warn(`Could not save to vault: ${err instanceof Error ? err.message : String(err)}`);
2152
+ p5.log.warn(`Could not save to vault: ${err instanceof Error ? err.message : String(err)}`);
1832
2153
  }
1833
2154
  }
1834
2155
 
@@ -1842,43 +2163,43 @@ async function loadClients2() {
1842
2163
  }
1843
2164
  }
1844
2165
  async function installServer(input, options = {}) {
1845
- p5.intro("mcpman install");
1846
- const spinner5 = p5.spinner();
2166
+ p6.intro("mcpman install");
2167
+ const spinner5 = p6.spinner();
1847
2168
  spinner5.start("Resolving server...");
1848
2169
  let metadata;
1849
2170
  try {
1850
2171
  metadata = await resolveServer(input);
1851
2172
  } catch (err) {
1852
2173
  spinner5.stop("Resolution failed");
1853
- p5.log.error(err instanceof Error ? err.message : String(err));
2174
+ p6.log.error(err instanceof Error ? err.message : String(err));
1854
2175
  process.exit(1);
1855
2176
  }
1856
2177
  spinner5.stop(`Found: ${metadata.name}@${metadata.version}`);
1857
2178
  const clients = await loadClients2();
1858
2179
  if (clients.length === 0) {
1859
- p5.log.warn("No supported AI clients detected on this machine.");
1860
- p5.log.info("Supported: Claude Desktop, Cursor, VS Code, Windsurf");
2180
+ p6.log.warn("No supported AI clients detected on this machine.");
2181
+ p6.log.info("Supported: Claude Desktop, Cursor, VS Code, Windsurf");
1861
2182
  process.exit(1);
1862
2183
  }
1863
2184
  let selectedClients;
1864
2185
  if (options.client) {
1865
2186
  const found = clients.find((c) => c.type === options.client || c.displayName.toLowerCase() === options.client?.toLowerCase());
1866
2187
  if (!found) {
1867
- p5.log.error(`Client '${options.client}' not found or not installed.`);
1868
- p5.log.info(`Available: ${clients.map((c) => c.type).join(", ")}`);
2188
+ p6.log.error(`Client '${options.client}' not found or not installed.`);
2189
+ p6.log.info(`Available: ${clients.map((c) => c.type).join(", ")}`);
1869
2190
  process.exit(1);
1870
2191
  }
1871
2192
  selectedClients = [found];
1872
2193
  } else if (options.yes || clients.length === 1) {
1873
2194
  selectedClients = clients;
1874
2195
  } else {
1875
- const chosen = await p5.multiselect({
2196
+ const chosen = await p6.multiselect({
1876
2197
  message: "Install to which client(s)?",
1877
2198
  options: clients.map((c) => ({ value: c.type, label: c.displayName })),
1878
2199
  required: true
1879
2200
  });
1880
- if (p5.isCancel(chosen)) {
1881
- p5.outro("Cancelled.");
2201
+ if (p6.isCancel(chosen)) {
2202
+ p6.outro("Cancelled.");
1882
2203
  process.exit(0);
1883
2204
  }
1884
2205
  selectedClients = clients.filter((c) => chosen.includes(c.type));
@@ -1893,13 +2214,13 @@ async function installServer(input, options = {}) {
1893
2214
  collectedEnv[envVar.name] = envVar.default;
1894
2215
  continue;
1895
2216
  }
1896
- const val = await p5.text({
2217
+ const val = await p6.text({
1897
2218
  message: `${envVar.name}${envVar.description ? ` \u2014 ${envVar.description}` : ""}`,
1898
2219
  placeholder: envVar.default ?? "",
1899
2220
  validate: (v) => envVar.required && !v ? "Required" : void 0
1900
2221
  });
1901
- if (p5.isCancel(val)) {
1902
- p5.outro("Cancelled.");
2222
+ if (p6.isCancel(val)) {
2223
+ p6.outro("Cancelled.");
1903
2224
  process.exit(0);
1904
2225
  }
1905
2226
  collectedEnv[envVar.name] = val;
@@ -1918,7 +2239,7 @@ async function installServer(input, options = {}) {
1918
2239
  clientTypes.push(client.type);
1919
2240
  } catch (err) {
1920
2241
  spinner5.stop("Partial failure");
1921
- p5.log.warn(`Failed to write to ${client.displayName}: ${err instanceof Error ? err.message : String(err)}`);
2242
+ p6.log.warn(`Failed to write to ${client.displayName}: ${err instanceof Error ? err.message : String(err)}`);
1922
2243
  }
1923
2244
  }
1924
2245
  spinner5.stop("Config written");
@@ -1937,14 +2258,14 @@ async function installServer(input, options = {}) {
1937
2258
  clients: clientTypes
1938
2259
  });
1939
2260
  const lockPath = findLockfile() ?? "mcpman.lock (global)";
1940
- p5.log.success(`Lockfile updated: ${lockPath}`);
2261
+ p6.log.success(`Lockfile updated: ${lockPath}`);
1941
2262
  await offerVaultSave(metadata.name, newlyEnteredVars, options.yes ?? false);
1942
- p5.outro(`${metadata.name}@${metadata.version} installed to ${clientTypes.join(", ")}`);
2263
+ p6.outro(`${metadata.name}@${metadata.version} installed to ${clientTypes.join(", ")}`);
1943
2264
  }
1944
2265
 
1945
2266
  // src/utils/logger.ts
1946
2267
  init_cjs_shims();
1947
- var import_picocolors3 = __toESM(require("picocolors"), 1);
2268
+ var import_picocolors5 = __toESM(require("picocolors"), 1);
1948
2269
  var noColor = process.env.NO_COLOR !== void 0 || process.argv.includes("--no-color");
1949
2270
  var isVerbose = process.argv.includes("--verbose");
1950
2271
  var isJson = process.argv.includes("--json");
@@ -1953,19 +2274,19 @@ function colorize(fn, text2) {
1953
2274
  }
1954
2275
  function info(message) {
1955
2276
  if (isJson) return;
1956
- console.log(`${colorize(import_picocolors3.default.cyan, "i")} ${message}`);
2277
+ console.log(`${colorize(import_picocolors5.default.cyan, "i")} ${message}`);
1957
2278
  }
1958
2279
  function error(message) {
1959
2280
  if (isJson) return;
1960
- console.error(`${colorize(import_picocolors3.default.red, "\u2717")} ${message}`);
2281
+ console.error(`${colorize(import_picocolors5.default.red, "\u2717")} ${message}`);
1961
2282
  }
1962
2283
  function json(data) {
1963
2284
  console.log(JSON.stringify(data, null, 2));
1964
2285
  }
1965
2286
 
1966
2287
  // src/commands/install.ts
1967
- var p6 = __toESM(require("@clack/prompts"), 1);
1968
- var install_default = (0, import_citty4.defineCommand)({
2288
+ var p7 = __toESM(require("@clack/prompts"), 1);
2289
+ var install_default = (0, import_citty6.defineCommand)({
1969
2290
  meta: {
1970
2291
  name: "install",
1971
2292
  description: "Install an MCP server into one or more AI clients"
@@ -2014,8 +2335,8 @@ async function restoreFromLockfile() {
2014
2335
  info("Lockfile is empty \u2014 nothing to restore.");
2015
2336
  return;
2016
2337
  }
2017
- p6.intro(`mcpman install (restore from ${lockPath})`);
2018
- p6.log.info(`Restoring ${entries.length} server(s)...`);
2338
+ p7.intro(`mcpman install (restore from ${lockPath})`);
2339
+ p7.log.info(`Restoring ${entries.length} server(s)...`);
2019
2340
  for (const [name, entry] of entries) {
2020
2341
  const input = entry.source === "smithery" ? `smithery:${name}` : entry.source === "github" ? entry.resolved : name;
2021
2342
  await installServer(input, {
@@ -2023,19 +2344,19 @@ async function restoreFromLockfile() {
2023
2344
  yes: true
2024
2345
  });
2025
2346
  }
2026
- p6.outro("Restore complete.");
2347
+ p7.outro("Restore complete.");
2027
2348
  }
2028
2349
 
2029
2350
  // src/commands/list.ts
2030
2351
  init_cjs_shims();
2031
- var import_citty5 = require("citty");
2032
- var import_picocolors4 = __toESM(require("picocolors"), 1);
2352
+ var import_citty7 = require("citty");
2353
+ var import_picocolors6 = __toESM(require("picocolors"), 1);
2033
2354
  var STATUS_ICON = {
2034
- healthy: import_picocolors4.default.green("\u25CF"),
2035
- unhealthy: import_picocolors4.default.red("\u25CF"),
2036
- unknown: import_picocolors4.default.dim("\u25CB")
2355
+ healthy: import_picocolors6.default.green("\u25CF"),
2356
+ unhealthy: import_picocolors6.default.red("\u25CF"),
2357
+ unknown: import_picocolors6.default.dim("\u25CB")
2037
2358
  };
2038
- var list_default = (0, import_citty5.defineCommand)({
2359
+ var list_default = (0, import_citty7.defineCommand)({
2039
2360
  meta: {
2040
2361
  name: "list",
2041
2362
  description: "List installed MCP servers"
@@ -2055,7 +2376,7 @@ var list_default = (0, import_citty5.defineCommand)({
2055
2376
  const servers = await getInstalledServers(args.client);
2056
2377
  if (servers.length === 0) {
2057
2378
  const filter = args.client ? ` for client "${args.client}"` : "";
2058
- console.log(import_picocolors4.default.dim(`No MCP servers installed${filter}. Run ${import_picocolors4.default.cyan("mcpman install <server>")} to get started.`));
2379
+ console.log(import_picocolors6.default.dim(`No MCP servers installed${filter}. Run ${import_picocolors6.default.cyan("mcpman install <server>")} to get started.`));
2059
2380
  return;
2060
2381
  }
2061
2382
  const withStatus = await Promise.all(
@@ -2079,8 +2400,8 @@ var list_default = (0, import_citty5.defineCommand)({
2079
2400
  const nameWidth = Math.max(4, ...withStatus.map((s) => s.name.length));
2080
2401
  const clientsWidth = Math.max(7, ...withStatus.map((s) => formatClients(s.clients).length));
2081
2402
  const header = ` ${pad("NAME", nameWidth)} ${pad("CLIENT(S)", clientsWidth)} ${pad("COMMAND", 20)} STATUS`;
2082
- console.log(import_picocolors4.default.dim(header));
2083
- console.log(import_picocolors4.default.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientsWidth)} ${"-".repeat(20)} ------`));
2403
+ console.log(import_picocolors6.default.dim(header));
2404
+ console.log(import_picocolors6.default.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientsWidth)} ${"-".repeat(20)} ------`));
2084
2405
  for (const s of withStatus) {
2085
2406
  const icon = STATUS_ICON[s.status];
2086
2407
  const clientsStr = formatClients(s.clients);
@@ -2088,7 +2409,7 @@ var list_default = (0, import_citty5.defineCommand)({
2088
2409
  console.log(` ${pad(s.name, nameWidth)} ${pad(clientsStr, clientsWidth)} ${pad(cmdStr, 20)} ${icon} ${s.status}`);
2089
2410
  }
2090
2411
  const clientSet = new Set(withStatus.flatMap((s) => s.clients));
2091
- console.log(import_picocolors4.default.dim(`
2412
+ console.log(import_picocolors6.default.dim(`
2092
2413
  ${withStatus.length} server${withStatus.length !== 1 ? "s" : ""} \xB7 ${clientSet.size} client${clientSet.size !== 1 ? "s" : ""}`));
2093
2414
  }
2094
2415
  });
@@ -2110,9 +2431,9 @@ function formatClients(clients) {
2110
2431
 
2111
2432
  // src/commands/remove.ts
2112
2433
  init_cjs_shims();
2113
- var import_citty6 = require("citty");
2114
- var p7 = __toESM(require("@clack/prompts"), 1);
2115
- var import_picocolors5 = __toESM(require("picocolors"), 1);
2434
+ var import_citty8 = require("citty");
2435
+ var p8 = __toESM(require("@clack/prompts"), 1);
2436
+ var import_picocolors7 = __toESM(require("picocolors"), 1);
2116
2437
  init_client_detector();
2117
2438
  var CLIENT_DISPLAY2 = {
2118
2439
  "claude-desktop": "Claude",
@@ -2123,7 +2444,7 @@ var CLIENT_DISPLAY2 = {
2123
2444
  function clientDisplayName(type) {
2124
2445
  return CLIENT_DISPLAY2[type] ?? type;
2125
2446
  }
2126
- var remove_default = (0, import_citty6.defineCommand)({
2447
+ var remove_default = (0, import_citty8.defineCommand)({
2127
2448
  meta: {
2128
2449
  name: "remove",
2129
2450
  description: "Remove an MCP server from one or more AI clients"
@@ -2150,17 +2471,17 @@ var remove_default = (0, import_citty6.defineCommand)({
2150
2471
  }
2151
2472
  },
2152
2473
  async run({ args }) {
2153
- p7.intro(import_picocolors5.default.bold("mcpman remove"));
2474
+ p8.intro(import_picocolors7.default.bold("mcpman remove"));
2154
2475
  const serverName = args.server;
2155
2476
  const servers = await getInstalledServers();
2156
2477
  const match = servers.find((s) => s.name === serverName);
2157
2478
  if (!match) {
2158
- p7.log.warn(`Server "${serverName}" is not installed.`);
2479
+ p8.log.warn(`Server "${serverName}" is not installed.`);
2159
2480
  const similar = servers.filter((s) => s.name.includes(serverName) || serverName.includes(s.name));
2160
2481
  if (similar.length > 0) {
2161
- p7.log.info(`Did you mean: ${similar.map((s) => import_picocolors5.default.cyan(s.name)).join(", ")}?`);
2482
+ p8.log.info(`Did you mean: ${similar.map((s) => import_picocolors7.default.cyan(s.name)).join(", ")}?`);
2162
2483
  }
2163
- p7.outro("Nothing to remove.");
2484
+ p8.outro("Nothing to remove.");
2164
2485
  return;
2165
2486
  }
2166
2487
  let targetClients;
@@ -2168,15 +2489,15 @@ var remove_default = (0, import_citty6.defineCommand)({
2168
2489
  targetClients = match.clients;
2169
2490
  } else if (args.client) {
2170
2491
  if (!match.clients.includes(args.client)) {
2171
- p7.log.warn(`Server "${serverName}" is not installed in client "${args.client}".`);
2172
- p7.outro("Nothing to remove.");
2492
+ p8.log.warn(`Server "${serverName}" is not installed in client "${args.client}".`);
2493
+ p8.outro("Nothing to remove.");
2173
2494
  return;
2174
2495
  }
2175
2496
  targetClients = [args.client];
2176
2497
  } else if (match.clients.length === 1) {
2177
2498
  targetClients = match.clients;
2178
2499
  } else {
2179
- const selected = await p7.multiselect({
2500
+ const selected = await p8.multiselect({
2180
2501
  message: `Remove "${serverName}" from which clients?`,
2181
2502
  options: match.clients.map((c) => ({
2182
2503
  value: c,
@@ -2184,19 +2505,19 @@ var remove_default = (0, import_citty6.defineCommand)({
2184
2505
  })),
2185
2506
  required: true
2186
2507
  });
2187
- if (p7.isCancel(selected)) {
2188
- p7.outro("Cancelled.");
2508
+ if (p8.isCancel(selected)) {
2509
+ p8.outro("Cancelled.");
2189
2510
  process.exit(0);
2190
2511
  }
2191
2512
  targetClients = selected;
2192
2513
  }
2193
2514
  if (!args.yes) {
2194
2515
  const clientNames = targetClients.map(clientDisplayName).join(", ");
2195
- const confirmed = await p7.confirm({
2196
- message: `Remove ${import_picocolors5.default.cyan(serverName)} from ${import_picocolors5.default.yellow(clientNames)}?`
2516
+ const confirmed = await p8.confirm({
2517
+ message: `Remove ${import_picocolors7.default.cyan(serverName)} from ${import_picocolors7.default.yellow(clientNames)}?`
2197
2518
  });
2198
- if (p7.isCancel(confirmed) || !confirmed) {
2199
- p7.outro("Cancelled.");
2519
+ if (p8.isCancel(confirmed) || !confirmed) {
2520
+ p8.outro("Cancelled.");
2200
2521
  return;
2201
2522
  }
2202
2523
  }
@@ -2210,26 +2531,273 @@ var remove_default = (0, import_citty6.defineCommand)({
2210
2531
  }
2211
2532
  try {
2212
2533
  await handler.removeServer(serverName);
2213
- p7.log.success(`Removed from ${clientDisplayName(clientType)}`);
2534
+ p8.log.success(`Removed from ${clientDisplayName(clientType)}`);
2214
2535
  } catch (err) {
2215
2536
  const msg = err instanceof Error ? err.message : String(err);
2216
2537
  errors.push(`${clientDisplayName(clientType)}: ${msg}`);
2217
2538
  }
2218
2539
  }
2219
2540
  if (errors.length > 0) {
2220
- for (const e of errors) p7.log.error(e);
2221
- p7.outro(import_picocolors5.default.red("Completed with errors."));
2541
+ for (const e of errors) p8.log.error(e);
2542
+ p8.outro(import_picocolors7.default.red("Completed with errors."));
2543
+ process.exit(1);
2544
+ }
2545
+ p8.outro(import_picocolors7.default.green(`Removed "${serverName}" successfully.`));
2546
+ }
2547
+ });
2548
+
2549
+ // src/commands/run.ts
2550
+ init_cjs_shims();
2551
+ var import_citty9 = require("citty");
2552
+ var import_node_child_process3 = require("child_process");
2553
+ var import_picocolors8 = __toESM(require("picocolors"), 1);
2554
+ init_vault_service();
2555
+ var run_default = (0, import_citty9.defineCommand)({
2556
+ meta: {
2557
+ name: "run",
2558
+ description: "Run an installed MCP server with vault secrets injected"
2559
+ },
2560
+ args: {
2561
+ server: {
2562
+ type: "positional",
2563
+ description: "Server name to run (as installed in lockfile)",
2564
+ required: true
2565
+ },
2566
+ env: {
2567
+ type: "string",
2568
+ description: "Override env var KEY=VAL (repeatable)",
2569
+ alias: "e"
2570
+ }
2571
+ },
2572
+ async run({ args }) {
2573
+ const serverName = args.server;
2574
+ const lockfile = readLockfile();
2575
+ const entry = lockfile.servers[serverName];
2576
+ if (!entry) {
2577
+ console.error(import_picocolors8.default.red(` Error: Server '${serverName}' is not installed.`));
2578
+ console.error(import_picocolors8.default.dim(` Run ${import_picocolors8.default.cyan("mcpman install <server>")} to install it first.`));
2222
2579
  process.exit(1);
2223
2580
  }
2224
- p7.outro(import_picocolors5.default.green(`Removed "${serverName}" successfully.`));
2581
+ const lockfileEnv = parseEnvFlags(entry.envVars);
2582
+ const vaultEnv = await loadVaultSecrets(serverName);
2583
+ const cliEnv = parseEnvFlags(args.env);
2584
+ const finalEnv = {
2585
+ ...process.env,
2586
+ ...lockfileEnv,
2587
+ ...vaultEnv,
2588
+ ...cliEnv
2589
+ };
2590
+ console.log(import_picocolors8.default.dim(` Running ${import_picocolors8.default.cyan(serverName)}...`));
2591
+ const child = (0, import_node_child_process3.spawn)(entry.command, entry.args, {
2592
+ env: finalEnv,
2593
+ stdio: "inherit"
2594
+ });
2595
+ const forwardSignal = (signal) => {
2596
+ if (!child.killed) {
2597
+ child.kill(signal);
2598
+ }
2599
+ };
2600
+ process.on("SIGINT", () => forwardSignal("SIGINT"));
2601
+ process.on("SIGTERM", () => forwardSignal("SIGTERM"));
2602
+ await new Promise((resolve) => {
2603
+ child.on("close", (code) => {
2604
+ process.exit(code ?? 0);
2605
+ resolve();
2606
+ });
2607
+ child.on("error", (err) => {
2608
+ console.error(import_picocolors8.default.red(` Failed to start '${serverName}': ${err.message}`));
2609
+ process.exit(1);
2610
+ resolve();
2611
+ });
2612
+ });
2613
+ }
2614
+ });
2615
+ async function loadVaultSecrets(serverName) {
2616
+ try {
2617
+ const entries = listSecrets(serverName);
2618
+ if (entries.length === 0 || entries[0].keys.length === 0) {
2619
+ return {};
2620
+ }
2621
+ const password2 = await getMasterPassword();
2622
+ return getSecretsForServer(serverName, password2);
2623
+ } catch {
2624
+ console.warn(import_picocolors8.default.yellow(" Warning: Could not load vault secrets, continuing without them."));
2625
+ return {};
2626
+ }
2627
+ }
2628
+
2629
+ // src/commands/search.ts
2630
+ init_cjs_shims();
2631
+ var import_citty10 = require("citty");
2632
+ var import_picocolors9 = __toESM(require("picocolors"), 1);
2633
+ var import_nanospinner3 = require("nanospinner");
2634
+
2635
+ // src/core/registry-search.ts
2636
+ init_cjs_shims();
2637
+ var SEARCH_TIMEOUT_MS = 1e4;
2638
+ async function searchNpm(query, limit = 20) {
2639
+ const cap = Math.min(limit, 100);
2640
+ const url = `https://registry.npmjs.org/-/v1/search?text=mcp+${encodeURIComponent(query)}&size=${cap}`;
2641
+ try {
2642
+ const res = await fetch(url, { signal: AbortSignal.timeout(SEARCH_TIMEOUT_MS) });
2643
+ if (!res.ok) return [];
2644
+ const data = await res.json();
2645
+ const objects = Array.isArray(data["objects"]) ? data["objects"] : [];
2646
+ return objects.map((obj) => {
2647
+ const pkg = obj["package"] ?? {};
2648
+ const dl = obj["downloads"];
2649
+ return {
2650
+ name: typeof pkg["name"] === "string" ? pkg["name"] : "",
2651
+ description: typeof pkg["description"] === "string" ? pkg["description"] : "",
2652
+ version: typeof pkg["version"] === "string" ? pkg["version"] : "",
2653
+ date: typeof pkg["date"] === "string" ? pkg["date"] : "",
2654
+ downloads: typeof dl?.["weekly"] === "number" ? dl["weekly"] : 0,
2655
+ keywords: Array.isArray(pkg["keywords"]) ? pkg["keywords"] : []
2656
+ };
2657
+ }).filter((r) => r.name !== "");
2658
+ } catch {
2659
+ return [];
2660
+ }
2661
+ }
2662
+ async function searchSmithery(query, limit = 20) {
2663
+ const cap = Math.min(limit, 100);
2664
+ const url = `https://api.smithery.ai/v1/servers?q=${encodeURIComponent(query)}&limit=${cap}`;
2665
+ try {
2666
+ const res = await fetch(url, { signal: AbortSignal.timeout(SEARCH_TIMEOUT_MS) });
2667
+ if (!res.ok) return [];
2668
+ const data = await res.json();
2669
+ const servers = Array.isArray(data["servers"]) ? data["servers"] : [];
2670
+ return servers.map((s) => ({
2671
+ name: typeof s["name"] === "string" ? s["name"] : "",
2672
+ description: typeof s["description"] === "string" ? s["description"] : "",
2673
+ version: typeof s["version"] === "string" ? s["version"] : "latest",
2674
+ runtime: typeof s["runtime"] === "string" ? s["runtime"] : "node"
2675
+ })).filter((r) => r.name !== "");
2676
+ } catch {
2677
+ return [];
2678
+ }
2679
+ }
2680
+
2681
+ // src/commands/search.ts
2682
+ function truncate2(s, max) {
2683
+ return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
2684
+ }
2685
+ function pad2(s, width) {
2686
+ return s.length >= width ? s : s + " ".repeat(width - s.length);
2687
+ }
2688
+ function highlightMatch(name, query) {
2689
+ const idx = name.toLowerCase().indexOf(query.toLowerCase());
2690
+ if (idx === -1) return name;
2691
+ return name.slice(0, idx) + import_picocolors9.default.yellow(name.slice(idx, idx + query.length)) + name.slice(idx + query.length);
2692
+ }
2693
+ function formatDownloads(n) {
2694
+ if (!n) return import_picocolors9.default.dim("\u2014");
2695
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
2696
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
2697
+ return String(n);
2698
+ }
2699
+ function printNpmResults(results, query) {
2700
+ const nameWidth = Math.max(4, ...results.map((r) => r.name.length), 20);
2701
+ const verWidth = Math.max(7, ...results.map((r) => r.version.length));
2702
+ const dlWidth = 9;
2703
+ const descMax = 50;
2704
+ const header = ` ${pad2("NAME", nameWidth)} ${pad2("VERSION", verWidth)} ${pad2("DOWNLOADS", dlWidth)} DESCRIPTION`;
2705
+ console.log(import_picocolors9.default.dim(header));
2706
+ console.log(import_picocolors9.default.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(verWidth)} ${"-".repeat(dlWidth)} ${"-".repeat(descMax)}`));
2707
+ for (const r of results) {
2708
+ const name = highlightMatch(pad2(r.name, nameWidth), query);
2709
+ const ver = pad2(r.version, verWidth);
2710
+ const dl = pad2(formatDownloads(r.downloads), dlWidth);
2711
+ const desc = truncate2(r.description || import_picocolors9.default.dim("(no description)"), descMax);
2712
+ console.log(` ${name} ${import_picocolors9.default.dim(ver)} ${dl} ${desc}`);
2713
+ }
2714
+ }
2715
+ function printSmitheryResults(results, query) {
2716
+ const nameWidth = Math.max(4, ...results.map((r) => r.name.length), 20);
2717
+ const verWidth = Math.max(7, ...results.map((r) => r.version.length));
2718
+ const descMax = 50;
2719
+ const header = ` ${pad2("NAME", nameWidth)} ${pad2("VERSION", verWidth)} DESCRIPTION`;
2720
+ console.log(import_picocolors9.default.dim(header));
2721
+ console.log(import_picocolors9.default.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(verWidth)} ${"-".repeat(descMax)}`));
2722
+ for (const r of results) {
2723
+ const name = highlightMatch(pad2(r.name, nameWidth), query);
2724
+ const ver = pad2(r.version, verWidth);
2725
+ const desc = truncate2(r.description || import_picocolors9.default.dim("(no description)"), descMax);
2726
+ console.log(` ${name} ${import_picocolors9.default.dim(ver)} ${desc}`);
2727
+ }
2728
+ }
2729
+ var search_default = (0, import_citty10.defineCommand)({
2730
+ meta: {
2731
+ name: "search",
2732
+ description: "Search for MCP servers on npm or Smithery registry"
2733
+ },
2734
+ args: {
2735
+ query: {
2736
+ type: "positional",
2737
+ description: "Search query",
2738
+ required: true
2739
+ },
2740
+ registry: {
2741
+ type: "string",
2742
+ description: "Registry to search: npm or smithery (default: npm)",
2743
+ default: "npm"
2744
+ },
2745
+ limit: {
2746
+ type: "string",
2747
+ description: "Maximum number of results (default: 20, max: 100)",
2748
+ default: "20"
2749
+ }
2750
+ },
2751
+ async run({ args }) {
2752
+ const query = args.query;
2753
+ const registry = args.registry.toLowerCase();
2754
+ const limit = Math.min(Math.max(1, Number.parseInt(args.limit, 10) || 20), 100);
2755
+ if (registry !== "npm" && registry !== "smithery") {
2756
+ console.error(import_picocolors9.default.red(` Unknown registry "${registry}". Use "npm" or "smithery".`));
2757
+ process.exit(1);
2758
+ }
2759
+ const spinner5 = (0, import_nanospinner3.createSpinner)(`Searching ${registry} for "${query}"...`).start();
2760
+ if (registry === "npm") {
2761
+ const results2 = await searchNpm(query, limit);
2762
+ spinner5.stop();
2763
+ if (results2.length === 0) {
2764
+ console.log(import_picocolors9.default.dim(`
2765
+ No results found for "${query}" on npm.
2766
+ `));
2767
+ return;
2768
+ }
2769
+ console.log(import_picocolors9.default.bold(`
2770
+ mcpman search \u2014 npm (${results2.length} result${results2.length !== 1 ? "s" : ""})
2771
+ `));
2772
+ printNpmResults(results2, query);
2773
+ console.log(import_picocolors9.default.dim(`
2774
+ Install with: mcpman install <name>
2775
+ `));
2776
+ return;
2777
+ }
2778
+ const results = await searchSmithery(query, limit);
2779
+ spinner5.stop();
2780
+ if (results.length === 0) {
2781
+ console.log(import_picocolors9.default.dim(`
2782
+ No results found for "${query}" on Smithery.
2783
+ `));
2784
+ return;
2785
+ }
2786
+ console.log(import_picocolors9.default.bold(`
2787
+ mcpman search \u2014 Smithery (${results.length} result${results.length !== 1 ? "s" : ""})
2788
+ `));
2789
+ printSmitheryResults(results, query);
2790
+ console.log(import_picocolors9.default.dim(`
2791
+ Install with: mcpman install <name>
2792
+ `));
2225
2793
  }
2226
2794
  });
2227
2795
 
2228
2796
  // src/commands/secrets.ts
2229
2797
  init_cjs_shims();
2230
- var import_citty7 = require("citty");
2231
- var import_picocolors6 = __toESM(require("picocolors"), 1);
2232
- var p8 = __toESM(require("@clack/prompts"), 1);
2798
+ var import_citty11 = require("citty");
2799
+ var import_picocolors10 = __toESM(require("picocolors"), 1);
2800
+ var p9 = __toESM(require("@clack/prompts"), 1);
2233
2801
  init_vault_service();
2234
2802
  function maskValue(value) {
2235
2803
  if (value.length <= 8) return "***";
@@ -2240,7 +2808,7 @@ function parseKeyValue(input) {
2240
2808
  if (idx <= 0) return null;
2241
2809
  return { key: input.slice(0, idx), value: input.slice(idx + 1) };
2242
2810
  }
2243
- var setCommand = (0, import_citty7.defineCommand)({
2811
+ var setCommand2 = (0, import_citty11.defineCommand)({
2244
2812
  meta: { name: "set", description: "Store an encrypted secret for a server" },
2245
2813
  args: {
2246
2814
  server: {
@@ -2257,30 +2825,30 @@ var setCommand = (0, import_citty7.defineCommand)({
2257
2825
  async run({ args }) {
2258
2826
  const parsed = parseKeyValue(args.keyvalue);
2259
2827
  if (!parsed) {
2260
- console.error(import_picocolors6.default.red("\u2717") + " Invalid format. Expected KEY=VALUE");
2828
+ console.error(import_picocolors10.default.red("\u2717") + " Invalid format. Expected KEY=VALUE");
2261
2829
  process.exit(1);
2262
2830
  }
2263
- p8.intro(import_picocolors6.default.cyan("mcpman secrets set"));
2831
+ p9.intro(import_picocolors10.default.cyan("mcpman secrets set"));
2264
2832
  const isNew = listSecrets(args.server).length === 0 || !listSecrets(args.server)[0]?.keys.includes(parsed.key);
2265
2833
  const vaultPath = (await Promise.resolve().then(() => (init_vault_service(), vault_service_exports))).getVaultPath();
2266
2834
  const vaultExists = (await import("fs")).existsSync(vaultPath);
2267
2835
  const password2 = await getMasterPassword(!vaultExists && isNew);
2268
- const spin = p8.spinner();
2836
+ const spin = p9.spinner();
2269
2837
  spin.start("Encrypting secret...");
2270
2838
  try {
2271
2839
  setSecret(args.server, parsed.key, parsed.value, password2);
2272
2840
  spin.stop(
2273
- `${import_picocolors6.default.green("\u2713")} Stored ${import_picocolors6.default.bold(parsed.key)} for ${import_picocolors6.default.cyan(args.server)}`
2841
+ `${import_picocolors10.default.green("\u2713")} Stored ${import_picocolors10.default.bold(parsed.key)} for ${import_picocolors10.default.cyan(args.server)}`
2274
2842
  );
2275
2843
  } catch (err) {
2276
- spin.stop(import_picocolors6.default.red("\u2717") + " Failed to store secret");
2277
- console.error(import_picocolors6.default.dim(String(err)));
2844
+ spin.stop(import_picocolors10.default.red("\u2717") + " Failed to store secret");
2845
+ console.error(import_picocolors10.default.dim(String(err)));
2278
2846
  process.exit(1);
2279
2847
  }
2280
- p8.outro(import_picocolors6.default.dim("Secret encrypted and saved to vault."));
2848
+ p9.outro(import_picocolors10.default.dim("Secret encrypted and saved to vault."));
2281
2849
  }
2282
2850
  });
2283
- var listCommand = (0, import_citty7.defineCommand)({
2851
+ var listCommand2 = (0, import_citty11.defineCommand)({
2284
2852
  meta: { name: "list", description: "List secret keys stored in the vault" },
2285
2853
  args: {
2286
2854
  server: {
@@ -2292,23 +2860,23 @@ var listCommand = (0, import_citty7.defineCommand)({
2292
2860
  async run({ args }) {
2293
2861
  const results = listSecrets(args.server || void 0);
2294
2862
  if (results.length === 0) {
2295
- const filter = args.server ? ` for ${import_picocolors6.default.cyan(args.server)}` : "";
2296
- console.log(import_picocolors6.default.dim(`No secrets stored${filter}.`));
2863
+ const filter = args.server ? ` for ${import_picocolors10.default.cyan(args.server)}` : "";
2864
+ console.log(import_picocolors10.default.dim(`No secrets stored${filter}.`));
2297
2865
  return;
2298
2866
  }
2299
2867
  console.log("");
2300
2868
  for (const { server, keys } of results) {
2301
- console.log(import_picocolors6.default.bold(import_picocolors6.default.cyan(server)));
2869
+ console.log(import_picocolors10.default.bold(import_picocolors10.default.cyan(server)));
2302
2870
  for (const key of keys) {
2303
- console.log(` ${import_picocolors6.default.green("\u25CF")} ${import_picocolors6.default.bold(key)} ${import_picocolors6.default.dim(maskValue("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"))}`);
2871
+ console.log(` ${import_picocolors10.default.green("\u25CF")} ${import_picocolors10.default.bold(key)} ${import_picocolors10.default.dim(maskValue("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"))}`);
2304
2872
  }
2305
2873
  console.log("");
2306
2874
  }
2307
2875
  const total = results.reduce((n, r) => n + r.keys.length, 0);
2308
- console.log(import_picocolors6.default.dim(` ${total} secret${total !== 1 ? "s" : ""} in ${results.length} server${results.length !== 1 ? "s" : ""}`));
2876
+ console.log(import_picocolors10.default.dim(` ${total} secret${total !== 1 ? "s" : ""} in ${results.length} server${results.length !== 1 ? "s" : ""}`));
2309
2877
  }
2310
2878
  });
2311
- var removeCommand = (0, import_citty7.defineCommand)({
2879
+ var removeCommand = (0, import_citty11.defineCommand)({
2312
2880
  meta: { name: "remove", description: "Delete a secret from the vault" },
2313
2881
  args: {
2314
2882
  server: {
@@ -2323,41 +2891,41 @@ var removeCommand = (0, import_citty7.defineCommand)({
2323
2891
  }
2324
2892
  },
2325
2893
  async run({ args }) {
2326
- const confirmed = await p8.confirm({
2327
- message: `Remove ${import_picocolors6.default.bold(args.key)} from ${import_picocolors6.default.cyan(args.server)}?`,
2894
+ const confirmed = await p9.confirm({
2895
+ message: `Remove ${import_picocolors10.default.bold(args.key)} from ${import_picocolors10.default.cyan(args.server)}?`,
2328
2896
  initialValue: false
2329
2897
  });
2330
- if (p8.isCancel(confirmed) || !confirmed) {
2331
- p8.cancel("Cancelled.");
2898
+ if (p9.isCancel(confirmed) || !confirmed) {
2899
+ p9.cancel("Cancelled.");
2332
2900
  return;
2333
2901
  }
2334
2902
  try {
2335
2903
  removeSecret(args.server, args.key);
2336
- console.log(`${import_picocolors6.default.green("\u2713")} Removed ${import_picocolors6.default.bold(args.key)} from ${import_picocolors6.default.cyan(args.server)}`);
2904
+ console.log(`${import_picocolors10.default.green("\u2713")} Removed ${import_picocolors10.default.bold(args.key)} from ${import_picocolors10.default.cyan(args.server)}`);
2337
2905
  } catch (err) {
2338
- console.error(import_picocolors6.default.red("\u2717") + " Failed to remove secret");
2339
- console.error(import_picocolors6.default.dim(String(err)));
2906
+ console.error(import_picocolors10.default.red("\u2717") + " Failed to remove secret");
2907
+ console.error(import_picocolors10.default.dim(String(err)));
2340
2908
  process.exit(1);
2341
2909
  }
2342
2910
  }
2343
2911
  });
2344
- var secrets_default = (0, import_citty7.defineCommand)({
2912
+ var secrets_default = (0, import_citty11.defineCommand)({
2345
2913
  meta: {
2346
2914
  name: "secrets",
2347
2915
  description: "Manage encrypted secrets for MCP servers"
2348
2916
  },
2349
2917
  subCommands: {
2350
- set: setCommand,
2351
- list: listCommand,
2918
+ set: setCommand2,
2919
+ list: listCommand2,
2352
2920
  remove: removeCommand
2353
2921
  }
2354
2922
  });
2355
2923
 
2356
2924
  // src/commands/sync.ts
2357
2925
  init_cjs_shims();
2358
- var import_citty8 = require("citty");
2359
- var p9 = __toESM(require("@clack/prompts"), 1);
2360
- var import_picocolors7 = __toESM(require("picocolors"), 1);
2926
+ var import_citty12 = require("citty");
2927
+ var p10 = __toESM(require("@clack/prompts"), 1);
2928
+ var import_picocolors11 = __toESM(require("picocolors"), 1);
2361
2929
 
2362
2930
  // src/core/config-diff.ts
2363
2931
  init_cjs_shims();
@@ -2510,7 +3078,7 @@ var CLIENT_DISPLAY3 = {
2510
3078
  vscode: "VS Code",
2511
3079
  windsurf: "Windsurf"
2512
3080
  };
2513
- var sync_default = (0, import_citty8.defineCommand)({
3081
+ var sync_default = (0, import_citty12.defineCommand)({
2514
3082
  meta: {
2515
3083
  name: "sync",
2516
3084
  description: "Sync MCP server configs across all detected AI clients"
@@ -2537,28 +3105,28 @@ var sync_default = (0, import_citty8.defineCommand)({
2537
3105
  }
2538
3106
  },
2539
3107
  async run({ args }) {
2540
- p9.intro(`${import_picocolors7.default.cyan("mcpman sync")}`);
3108
+ p10.intro(`${import_picocolors11.default.cyan("mcpman sync")}`);
2541
3109
  const sourceClient = args.source;
2542
3110
  if (sourceClient && !VALID_CLIENTS.includes(sourceClient)) {
2543
- p9.log.error(`Invalid --source "${sourceClient}". Must be one of: ${VALID_CLIENTS.join(", ")}`);
3111
+ p10.log.error(`Invalid --source "${sourceClient}". Must be one of: ${VALID_CLIENTS.join(", ")}`);
2544
3112
  process.exit(1);
2545
3113
  }
2546
- const spinner5 = p9.spinner();
3114
+ const spinner5 = p10.spinner();
2547
3115
  spinner5.start("Detecting clients and reading configs...");
2548
3116
  const { configs, handlers } = await getClientConfigs();
2549
3117
  spinner5.stop(`Found ${configs.size} client(s)`);
2550
3118
  if (configs.size === 0) {
2551
- p9.log.warn("No AI clients detected. Install Claude Desktop, Cursor, VS Code, or Windsurf first.");
3119
+ p10.log.warn("No AI clients detected. Install Claude Desktop, Cursor, VS Code, or Windsurf first.");
2552
3120
  process.exit(0);
2553
3121
  }
2554
3122
  const diffOptions = { remove: args.remove };
2555
3123
  let actions;
2556
3124
  if (sourceClient) {
2557
3125
  if (!configs.has(sourceClient)) {
2558
- p9.log.error(`Source client "${sourceClient}" is not detected or its config is unreadable.`);
3126
+ p10.log.error(`Source client "${sourceClient}" is not detected or its config is unreadable.`);
2559
3127
  process.exit(1);
2560
3128
  }
2561
- p9.log.info(`Using ${CLIENT_DISPLAY3[sourceClient]} as source of truth`);
3129
+ p10.log.info(`Using ${CLIENT_DISPLAY3[sourceClient]} as source of truth`);
2562
3130
  actions = computeDiffFromClient(sourceClient, configs, diffOptions);
2563
3131
  } else {
2564
3132
  const lockfile = readLockfile();
@@ -2569,32 +3137,32 @@ var sync_default = (0, import_citty8.defineCommand)({
2569
3137
  const extraCount = actions.filter((a) => a.action === "extra").length;
2570
3138
  const removeCount = actions.filter((a) => a.action === "remove").length;
2571
3139
  if (addCount === 0 && removeCount === 0 && extraCount === 0) {
2572
- p9.outro(import_picocolors7.default.green("All clients are in sync."));
3140
+ p10.outro(import_picocolors11.default.green("All clients are in sync."));
2573
3141
  process.exit(0);
2574
3142
  }
2575
3143
  const parts = [];
2576
- if (addCount > 0) parts.push(import_picocolors7.default.green(`${addCount} to add`));
2577
- if (removeCount > 0) parts.push(import_picocolors7.default.red(`${removeCount} to remove`));
2578
- if (extraCount > 0) parts.push(import_picocolors7.default.yellow(`${extraCount} extra (informational)`));
2579
- p9.log.info(parts.join(" \xB7 "));
3144
+ if (addCount > 0) parts.push(import_picocolors11.default.green(`${addCount} to add`));
3145
+ if (removeCount > 0) parts.push(import_picocolors11.default.red(`${removeCount} to remove`));
3146
+ if (extraCount > 0) parts.push(import_picocolors11.default.yellow(`${extraCount} extra (informational)`));
3147
+ p10.log.info(parts.join(" \xB7 "));
2580
3148
  if (args["dry-run"]) {
2581
- p9.outro(import_picocolors7.default.dim("Dry run \u2014 no changes applied."));
3149
+ p10.outro(import_picocolors11.default.dim("Dry run \u2014 no changes applied."));
2582
3150
  process.exit(1);
2583
3151
  }
2584
3152
  if (addCount === 0 && removeCount === 0) {
2585
- p9.outro(import_picocolors7.default.dim("No additions needed. Extra servers left untouched."));
3153
+ p10.outro(import_picocolors11.default.dim("No additions needed. Extra servers left untouched."));
2586
3154
  process.exit(1);
2587
3155
  }
2588
3156
  if (!args.yes) {
2589
3157
  const actionParts = [];
2590
3158
  if (addCount > 0) actionParts.push(`${addCount} addition(s)`);
2591
3159
  if (removeCount > 0) actionParts.push(`${removeCount} removal(s)`);
2592
- const confirmed = await p9.confirm({
3160
+ const confirmed = await p10.confirm({
2593
3161
  message: `Apply ${actionParts.join(" and ")} to client configs?`,
2594
3162
  initialValue: true
2595
3163
  });
2596
- if (p9.isCancel(confirmed) || !confirmed) {
2597
- p9.outro(import_picocolors7.default.dim("Cancelled \u2014 no changes applied."));
3164
+ if (p10.isCancel(confirmed) || !confirmed) {
3165
+ p10.outro(import_picocolors11.default.dim("Cancelled \u2014 no changes applied."));
2598
3166
  process.exit(0);
2599
3167
  }
2600
3168
  }
@@ -2602,74 +3170,74 @@ var sync_default = (0, import_citty8.defineCommand)({
2602
3170
  const result = await applySyncActions(actions, handlers);
2603
3171
  spinner5.stop("Done");
2604
3172
  if (result.applied > 0) {
2605
- p9.log.success(`Added ${result.applied} server(s) to client configs.`);
3173
+ p10.log.success(`Added ${result.applied} server(s) to client configs.`);
2606
3174
  }
2607
3175
  if (result.removed > 0) {
2608
- p9.log.success(`Removed ${result.removed} server(s) from client configs.`);
3176
+ p10.log.success(`Removed ${result.removed} server(s) from client configs.`);
2609
3177
  }
2610
3178
  if (result.failed > 0) {
2611
3179
  for (const e of result.errors) {
2612
- p9.log.error(`Failed to sync "${e.server}" on ${e.client}: ${e.error}`);
3180
+ p10.log.error(`Failed to sync "${e.server}" on ${e.client}: ${e.error}`);
2613
3181
  }
2614
3182
  }
2615
- p9.outro(result.failed === 0 ? import_picocolors7.default.green("Sync complete.") : import_picocolors7.default.yellow("Sync complete with errors."));
3183
+ p10.outro(result.failed === 0 ? import_picocolors11.default.green("Sync complete.") : import_picocolors11.default.yellow("Sync complete with errors."));
2616
3184
  process.exit(result.failed > 0 ? 1 : 0);
2617
3185
  }
2618
3186
  });
2619
3187
  function printDiffTable(actions) {
2620
3188
  if (actions.length === 0) {
2621
- p9.log.info("No actions to display.");
3189
+ p10.log.info("No actions to display.");
2622
3190
  return;
2623
3191
  }
2624
3192
  const nameWidth = Math.max(6, ...actions.map((a) => a.server.length));
2625
3193
  const clientWidth = Math.max(6, ...actions.map((a) => CLIENT_DISPLAY3[a.client]?.length ?? a.client.length));
2626
- const header = ` ${pad2("SERVER", nameWidth)} ${pad2("CLIENT", clientWidth)} STATUS`;
2627
- console.log(import_picocolors7.default.dim(header));
2628
- console.log(import_picocolors7.default.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientWidth)} ------`));
3194
+ const header = ` ${pad3("SERVER", nameWidth)} ${pad3("CLIENT", clientWidth)} STATUS`;
3195
+ console.log(import_picocolors11.default.dim(header));
3196
+ console.log(import_picocolors11.default.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientWidth)} ------`));
2629
3197
  for (const action of actions) {
2630
3198
  const clientDisplay = CLIENT_DISPLAY3[action.client] ?? action.client;
2631
3199
  const [icon, statusText] = formatAction(action.action);
2632
- console.log(` ${pad2(action.server, nameWidth)} ${pad2(clientDisplay, clientWidth)} ${icon} ${statusText}`);
3200
+ console.log(` ${pad3(action.server, nameWidth)} ${pad3(clientDisplay, clientWidth)} ${icon} ${statusText}`);
2633
3201
  }
2634
3202
  console.log("");
2635
3203
  }
2636
3204
  function formatAction(action) {
2637
3205
  switch (action) {
2638
3206
  case "add":
2639
- return [import_picocolors7.default.green("+"), import_picocolors7.default.green("missing \u2014 will add")];
3207
+ return [import_picocolors11.default.green("+"), import_picocolors11.default.green("missing \u2014 will add")];
2640
3208
  case "extra":
2641
- return [import_picocolors7.default.yellow("?"), import_picocolors7.default.yellow("extra (not in lockfile)")];
3209
+ return [import_picocolors11.default.yellow("?"), import_picocolors11.default.yellow("extra (not in lockfile)")];
2642
3210
  case "remove":
2643
- return [import_picocolors7.default.red("\u2013"), import_picocolors7.default.red("extra \u2014 will remove")];
3211
+ return [import_picocolors11.default.red("\u2013"), import_picocolors11.default.red("extra \u2014 will remove")];
2644
3212
  case "ok":
2645
- return [import_picocolors7.default.dim("\xB7"), import_picocolors7.default.dim("in sync")];
3213
+ return [import_picocolors11.default.dim("\xB7"), import_picocolors11.default.dim("in sync")];
2646
3214
  }
2647
3215
  }
2648
- function pad2(s, width) {
3216
+ function pad3(s, width) {
2649
3217
  return s.length >= width ? s : s + " ".repeat(width - s.length);
2650
3218
  }
2651
3219
 
2652
3220
  // src/commands/update.ts
2653
3221
  init_cjs_shims();
2654
- var import_citty9 = require("citty");
2655
- var p10 = __toESM(require("@clack/prompts"), 1);
2656
- var import_picocolors9 = __toESM(require("picocolors"), 1);
3222
+ var import_citty13 = require("citty");
3223
+ var p11 = __toESM(require("@clack/prompts"), 1);
3224
+ var import_picocolors13 = __toESM(require("picocolors"), 1);
2657
3225
 
2658
3226
  // src/core/update-notifier.ts
2659
3227
  init_cjs_shims();
2660
- var import_node_fs5 = __toESM(require("fs"), 1);
2661
- var import_node_path7 = __toESM(require("path"), 1);
3228
+ var import_node_fs6 = __toESM(require("fs"), 1);
3229
+ var import_node_path8 = __toESM(require("path"), 1);
2662
3230
  var import_node_os5 = __toESM(require("os"), 1);
2663
- var import_picocolors8 = __toESM(require("picocolors"), 1);
2664
- var CACHE_FILE = import_node_path7.default.join(import_node_os5.default.homedir(), ".mcpman", ".update-check");
3231
+ var import_picocolors12 = __toESM(require("picocolors"), 1);
3232
+ var CACHE_FILE = import_node_path8.default.join(import_node_os5.default.homedir(), ".mcpman", ".update-check");
2665
3233
  var TTL_MS = 24 * 60 * 60 * 1e3;
2666
3234
  function writeUpdateCache(data) {
2667
3235
  try {
2668
- const dir = import_node_path7.default.dirname(CACHE_FILE);
2669
- if (!import_node_fs5.default.existsSync(dir)) import_node_fs5.default.mkdirSync(dir, { recursive: true });
3236
+ const dir = import_node_path8.default.dirname(CACHE_FILE);
3237
+ if (!import_node_fs6.default.existsSync(dir)) import_node_fs6.default.mkdirSync(dir, { recursive: true });
2670
3238
  const tmp = `${CACHE_FILE}.tmp`;
2671
- import_node_fs5.default.writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
2672
- import_node_fs5.default.renameSync(tmp, CACHE_FILE);
3239
+ import_node_fs6.default.writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
3240
+ import_node_fs6.default.renameSync(tmp, CACHE_FILE);
2673
3241
  } catch {
2674
3242
  }
2675
3243
  }
@@ -2692,19 +3260,19 @@ function printTable(updates) {
2692
3260
  "LATEST".padEnd(VER_W),
2693
3261
  "STATUS"
2694
3262
  ].join(" ");
2695
- console.log(import_picocolors9.default.bold(`
3263
+ console.log(import_picocolors13.default.bold(`
2696
3264
  ${header}`));
2697
- console.log(import_picocolors9.default.dim(` ${"\u2500".repeat(NAME_W + VER_W * 2 + 20)}`));
3265
+ console.log(import_picocolors13.default.dim(` ${"\u2500".repeat(NAME_W + VER_W * 2 + 20)}`));
2698
3266
  for (const u of updates) {
2699
3267
  const nameCol = u.server.slice(0, NAME_W).padEnd(NAME_W);
2700
3268
  const curCol = u.currentVersion.padEnd(VER_W);
2701
3269
  const latCol = u.latestVersion.padEnd(VER_W);
2702
- const statusCol = u.hasUpdate ? import_picocolors9.default.yellow(`Update available${u.updateType ? ` [${u.updateType}]` : ""}`) : import_picocolors9.default.green("Up to date");
3270
+ const statusCol = u.hasUpdate ? import_picocolors13.default.yellow(`Update available${u.updateType ? ` [${u.updateType}]` : ""}`) : import_picocolors13.default.green("Up to date");
2703
3271
  console.log(` ${nameCol} ${curCol} ${latCol} ${statusCol}`);
2704
3272
  }
2705
3273
  console.log();
2706
3274
  }
2707
- var update_default = (0, import_citty9.defineCommand)({
3275
+ var update_default = (0, import_citty13.defineCommand)({
2708
3276
  meta: {
2709
3277
  name: "update",
2710
3278
  description: "Check for and apply updates to installed MCP servers"
@@ -2743,7 +3311,7 @@ var update_default = (0, import_citty9.defineCommand)({
2743
3311
  }
2744
3312
  process.exit(1);
2745
3313
  }
2746
- const spinner5 = p10.spinner();
3314
+ const spinner5 = p11.spinner();
2747
3315
  spinner5.start("Checking versions...");
2748
3316
  let updates;
2749
3317
  try {
@@ -2765,27 +3333,27 @@ var update_default = (0, import_citty9.defineCommand)({
2765
3333
  printTable(updates);
2766
3334
  const outdated = updates.filter((u) => u.hasUpdate);
2767
3335
  if (outdated.length === 0) {
2768
- console.log(import_picocolors9.default.green(" All servers are up to date."));
3336
+ console.log(import_picocolors13.default.green(" All servers are up to date."));
2769
3337
  return;
2770
3338
  }
2771
3339
  if (args.check) {
2772
- console.log(import_picocolors9.default.yellow(` ${outdated.length} update(s) available. Run mcpman update to apply.`));
3340
+ console.log(import_picocolors13.default.yellow(` ${outdated.length} update(s) available. Run mcpman update to apply.`));
2773
3341
  return;
2774
3342
  }
2775
3343
  if (!args.yes) {
2776
- const confirmed = await p10.confirm({
3344
+ const confirmed = await p11.confirm({
2777
3345
  message: `Apply ${outdated.length} update(s)?`,
2778
3346
  initialValue: true
2779
3347
  });
2780
- if (p10.isCancel(confirmed) || !confirmed) {
2781
- p10.outro("Cancelled.");
3348
+ if (p11.isCancel(confirmed) || !confirmed) {
3349
+ p11.outro("Cancelled.");
2782
3350
  return;
2783
3351
  }
2784
3352
  }
2785
3353
  const clients = await loadClients3();
2786
3354
  let successCount = 0;
2787
3355
  for (const update of outdated) {
2788
- const s = p10.spinner();
3356
+ const s = p11.spinner();
2789
3357
  s.start(`Updating ${update.server}...`);
2790
3358
  const result = await applyServerUpdate(
2791
3359
  update.server,
@@ -2793,23 +3361,23 @@ var update_default = (0, import_citty9.defineCommand)({
2793
3361
  clients
2794
3362
  );
2795
3363
  if (result.success) {
2796
- s.stop(`${import_picocolors9.default.green("\u2713")} ${update.server}: ${result.fromVersion} \u2192 ${result.toVersion}`);
3364
+ s.stop(`${import_picocolors13.default.green("\u2713")} ${update.server}: ${result.fromVersion} \u2192 ${result.toVersion}`);
2797
3365
  successCount++;
2798
3366
  } else {
2799
- s.stop(`${import_picocolors9.default.red("\u2717")} ${update.server}: ${result.error}`);
3367
+ s.stop(`${import_picocolors13.default.red("\u2717")} ${update.server}: ${result.error}`);
2800
3368
  }
2801
3369
  }
2802
3370
  const freshLockfile = readLockfile(resolveLockfilePath());
2803
3371
  const freshUpdates = await checkAllVersions(freshLockfile);
2804
3372
  writeUpdateCache({ lastCheck: (/* @__PURE__ */ new Date()).toISOString(), updates: freshUpdates });
2805
- p10.outro(`${successCount} of ${outdated.length} server(s) updated.`);
3373
+ p11.outro(`${successCount} of ${outdated.length} server(s) updated.`);
2806
3374
  }
2807
3375
  });
2808
3376
 
2809
3377
  // src/utils/constants.ts
2810
3378
  init_cjs_shims();
2811
3379
  var APP_NAME = "mcpman";
2812
- var APP_VERSION = "0.3.0";
3380
+ var APP_VERSION = "0.4.0";
2813
3381
  var APP_DESCRIPTION = "The package manager for MCP servers";
2814
3382
 
2815
3383
  // src/index.ts
@@ -2817,7 +3385,7 @@ process.on("SIGINT", () => {
2817
3385
  console.log("\nAborted.");
2818
3386
  process.exit(130);
2819
3387
  });
2820
- var main = (0, import_citty10.defineCommand)({
3388
+ var main = (0, import_citty14.defineCommand)({
2821
3389
  meta: {
2822
3390
  name: APP_NAME,
2823
3391
  version: APP_VERSION,
@@ -2832,7 +3400,11 @@ var main = (0, import_citty10.defineCommand)({
2832
3400
  secrets: secrets_default,
2833
3401
  sync: sync_default,
2834
3402
  audit: audit_default,
2835
- update: update_default
3403
+ update: update_default,
3404
+ config: config_default,
3405
+ search: search_default,
3406
+ info: info_default,
3407
+ run: run_default
2836
3408
  }
2837
3409
  });
2838
- (0, import_citty10.runMain)(main);
3410
+ (0, import_citty14.runMain)(main);