mcpman 0.7.0 → 0.8.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.js CHANGED
@@ -4,19 +4,27 @@ import {
4
4
  addEntry,
5
5
  createEmptyLockfile,
6
6
  findLockfile,
7
+ listSnapshots,
7
8
  readLockfile,
9
+ readSnapshot,
8
10
  resolveLockfilePath,
11
+ restoreSnapshot,
9
12
  writeLockfile
10
- } from "./chunk-YZNTMR6O.js";
13
+ } from "./chunk-DZ3D5RZQ.js";
11
14
  import {
12
15
  computeTrustScore
13
16
  } from "./chunk-RGKHLY5G.js";
17
+ import {
18
+ getInstalledClients
19
+ } from "./chunk-XPYCEHZZ.js";
14
20
  import {
15
21
  getConfigPath,
16
- getInstalledClients,
22
+ getEnvDir,
23
+ getGroupsFile,
24
+ getPinsFile,
17
25
  getPluginDir,
18
26
  getProfilesDir
19
- } from "./chunk-NS6HV723.js";
27
+ } from "./chunk-AOCGFOA6.js";
20
28
  import {
21
29
  getMasterPassword,
22
30
  getSecretsForServer,
@@ -29,7 +37,7 @@ import {
29
37
  } from "./chunk-6X6Q6UZC.js";
30
38
 
31
39
  // src/index.ts
32
- import { defineCommand as defineCommand27, runMain } from "citty";
40
+ import { defineCommand as defineCommand33, runMain } from "citty";
33
41
 
34
42
  // src/commands/audit.ts
35
43
  import * as p from "@clack/prompts";
@@ -167,11 +175,11 @@ async function scanAllServers(servers, concurrency = 3) {
167
175
  const results = [];
168
176
  const executing = /* @__PURE__ */ new Set();
169
177
  for (const [name, entry] of entries) {
170
- const p12 = scanServer(name, entry).then((r) => {
178
+ const p13 = scanServer(name, entry).then((r) => {
171
179
  results.push(r);
172
- executing.delete(p12);
180
+ executing.delete(p13);
173
181
  });
174
- executing.add(p12);
182
+ executing.add(p13);
175
183
  if (executing.size >= concurrency) await Promise.race(executing);
176
184
  }
177
185
  await Promise.all(executing);
@@ -336,8 +344,8 @@ function setConfigValue(key, value, configPath = getConfigPath()) {
336
344
  // src/core/plugin-loader.ts
337
345
  function isValidPlugin(obj) {
338
346
  if (typeof obj !== "object" || obj === null) return false;
339
- const p12 = obj;
340
- return typeof p12.name === "string" && typeof p12.prefix === "string" && typeof p12.resolve === "function";
347
+ const p13 = obj;
348
+ return typeof p13.name === "string" && typeof p13.prefix === "string" && typeof p13.resolve === "function";
341
349
  }
342
350
  function loadPlugin(pkg, pluginDir = getPluginDir()) {
343
351
  try {
@@ -391,7 +399,7 @@ function removePluginPackage(name, pluginDir = getPluginDir()) {
391
399
  }
392
400
  const config = readConfig();
393
401
  const plugins = config.plugins ?? [];
394
- config.plugins = plugins.filter((p12) => p12 !== name);
402
+ config.plugins = plugins.filter((p13) => p13 !== name);
395
403
  writeConfig(config);
396
404
  }
397
405
  function listPluginPackages() {
@@ -440,7 +448,7 @@ async function resolveServer(input) {
440
448
  if (source.type.startsWith("plugin:")) {
441
449
  const pluginName = source.type.slice(7);
442
450
  const plugins = loadAllPlugins();
443
- const plugin = plugins.find((p12) => p12.name === pluginName);
451
+ const plugin = plugins.find((p13) => p13.name === pluginName);
444
452
  if (plugin) {
445
453
  const resolved = await plugin.resolve(source.input);
446
454
  return resolved;
@@ -494,6 +502,47 @@ async function applyServerUpdate(serverName, lockEntry, clients) {
494
502
  }
495
503
  }
496
504
 
505
+ // src/core/pin-service.ts
506
+ import fs4 from "fs";
507
+ function readPins(file) {
508
+ const target = file ?? getPinsFile();
509
+ if (!fs4.existsSync(target)) return {};
510
+ try {
511
+ return JSON.parse(fs4.readFileSync(target, "utf-8"));
512
+ } catch {
513
+ return {};
514
+ }
515
+ }
516
+ function writePins(store, file) {
517
+ const target = file ?? getPinsFile();
518
+ const dir = target.slice(0, target.lastIndexOf("/"));
519
+ if (dir && !fs4.existsSync(dir)) {
520
+ fs4.mkdirSync(dir, { recursive: true });
521
+ }
522
+ fs4.writeFileSync(target, JSON.stringify(store, null, 2), "utf-8");
523
+ }
524
+ function pinServer(server, version, file) {
525
+ const store = readPins(file);
526
+ store[server] = version;
527
+ writePins(store, file);
528
+ }
529
+ function unpinServer(server, file) {
530
+ const store = readPins(file);
531
+ if (!(server in store)) return;
532
+ delete store[server];
533
+ writePins(store, file);
534
+ }
535
+ function getPinnedVersion(server, file) {
536
+ return readPins(file)[server] ?? null;
537
+ }
538
+ function isPinned(server, file) {
539
+ return server in readPins(file);
540
+ }
541
+ function listPins(file) {
542
+ const store = readPins(file);
543
+ return Object.entries(store).sort(([a], [b]) => a.localeCompare(b)).map(([server, version]) => ({ server, version }));
544
+ }
545
+
497
546
  // src/core/version-checker.ts
498
547
  function compareVersions(a, b) {
499
548
  const aParts = a.replace(/^v/, "").split(".").map(Number);
@@ -595,11 +644,12 @@ async function checkAllVersions(lockfile) {
595
644
  const results = [];
596
645
  const executing = /* @__PURE__ */ new Set();
597
646
  for (const [name, entry] of entries) {
598
- const p12 = checkVersion(name, entry).then((r) => {
647
+ if (isPinned(name)) continue;
648
+ const p13 = checkVersion(name, entry).then((r) => {
599
649
  results.push(r);
600
- executing.delete(p12);
650
+ executing.delete(p13);
601
651
  });
602
- executing.add(p12);
652
+ executing.add(p13);
603
653
  if (executing.size >= 5) {
604
654
  await Promise.race(executing);
605
655
  }
@@ -750,7 +800,7 @@ var audit_default = defineCommand({
750
800
  });
751
801
  async function loadClients() {
752
802
  try {
753
- const mod = await import("./client-detector-CY7WPF3K.js");
803
+ const mod = await import("./client-detector-NHXBDNMY.js");
754
804
  return mod.getInstalledClients();
755
805
  } catch {
756
806
  return [];
@@ -853,12 +903,204 @@ async function runAuditFix(reports, servers, skipConfirm) {
853
903
  `);
854
904
  }
855
905
 
906
+ // src/commands/bench.ts
907
+ import { defineCommand as defineCommand2 } from "citty";
908
+ import pc2 from "picocolors";
909
+
910
+ // src/core/bench-service.ts
911
+ import { spawn } from "child_process";
912
+ var DEFAULT_TIMEOUT_MS = 1e4;
913
+ function measureOneRun(command, args, env, timeoutMs) {
914
+ return new Promise((resolve, reject) => {
915
+ const start = Date.now();
916
+ let settled = false;
917
+ let stdout = "";
918
+ const child = spawn(command, args, {
919
+ env: { ...process.env, ...env },
920
+ stdio: ["pipe", "pipe", "pipe"]
921
+ });
922
+ const finish = (err) => {
923
+ if (settled) return;
924
+ settled = true;
925
+ try {
926
+ child.kill("SIGTERM");
927
+ } catch {
928
+ }
929
+ if (err) reject(err);
930
+ else resolve(Date.now() - start);
931
+ };
932
+ const timer = setTimeout(() => {
933
+ finish(new Error("Timeout waiting for initialize response"));
934
+ }, timeoutMs);
935
+ child.on("error", (err) => {
936
+ clearTimeout(timer);
937
+ finish(err);
938
+ });
939
+ child.on("exit", (code) => {
940
+ clearTimeout(timer);
941
+ if (!settled) finish(new Error(`Process exited with code ${code}`));
942
+ });
943
+ child.stdout?.on("data", (chunk) => {
944
+ stdout += chunk.toString();
945
+ for (const line of stdout.split("\n")) {
946
+ const trimmed = line.trim();
947
+ if (!trimmed) continue;
948
+ try {
949
+ const msg = JSON.parse(trimmed);
950
+ if (msg.jsonrpc === "2.0" && msg.id === 1) {
951
+ clearTimeout(timer);
952
+ finish();
953
+ }
954
+ } catch {
955
+ }
956
+ }
957
+ });
958
+ const initReq = JSON.stringify({
959
+ jsonrpc: "2.0",
960
+ id: 1,
961
+ method: "initialize",
962
+ params: {
963
+ protocolVersion: "2024-11-05",
964
+ capabilities: {},
965
+ clientInfo: { name: "mcpman-bench", version: "0.8.0" }
966
+ }
967
+ });
968
+ child.stdin?.write(`${initReq}
969
+ `);
970
+ });
971
+ }
972
+ function percentile(sorted, p13) {
973
+ if (sorted.length === 0) return 0;
974
+ const idx = Math.ceil(p13 / 100 * sorted.length) - 1;
975
+ return sorted[Math.max(0, idx)];
976
+ }
977
+ async function benchServer(command, args, env, runs = 5, timeoutMs = DEFAULT_TIMEOUT_MS) {
978
+ const allTimes = [];
979
+ for (let i = 0; i < runs; i++) {
980
+ try {
981
+ const ms = await measureOneRun(command, args, env, timeoutMs);
982
+ allTimes.push(ms);
983
+ } catch (err) {
984
+ return {
985
+ runs,
986
+ min: 0,
987
+ max: 0,
988
+ avg: 0,
989
+ p50: 0,
990
+ p95: 0,
991
+ allTimes,
992
+ error: String(err instanceof Error ? err.message : err)
993
+ };
994
+ }
995
+ }
996
+ const sorted = [...allTimes].sort((a, b) => a - b);
997
+ const sum = allTimes.reduce((a, b) => a + b, 0);
998
+ return {
999
+ runs,
1000
+ min: sorted[0] ?? 0,
1001
+ max: sorted[sorted.length - 1] ?? 0,
1002
+ avg: Math.round(sum / allTimes.length),
1003
+ p50: percentile(sorted, 50),
1004
+ p95: percentile(sorted, 95),
1005
+ allTimes
1006
+ };
1007
+ }
1008
+
1009
+ // src/commands/bench.ts
1010
+ var bench_default = defineCommand2({
1011
+ meta: {
1012
+ name: "bench",
1013
+ description: "Benchmark MCP server latency (JSON-RPC initialize)"
1014
+ },
1015
+ args: {
1016
+ server: {
1017
+ type: "positional",
1018
+ description: "Server name as stored in lockfile",
1019
+ required: true
1020
+ },
1021
+ runs: {
1022
+ type: "string",
1023
+ description: "Number of benchmark runs (default: 5)",
1024
+ default: "5"
1025
+ },
1026
+ timeout: {
1027
+ type: "string",
1028
+ description: "Per-run timeout in ms (default: 10000)",
1029
+ default: "10000"
1030
+ },
1031
+ json: {
1032
+ type: "boolean",
1033
+ description: "Output results as JSON",
1034
+ default: false
1035
+ }
1036
+ },
1037
+ async run({ args }) {
1038
+ const lockfile = readLockfile();
1039
+ const entry = lockfile.servers[args.server];
1040
+ if (!entry) {
1041
+ console.error(`${pc2.red("\u2717")} Server "${args.server}" not found in lockfile.`);
1042
+ console.error(pc2.dim("Run `mcpman list` to see installed servers."));
1043
+ process.exit(1);
1044
+ }
1045
+ const runs = Math.max(1, Number.parseInt(args.runs, 10) || 5);
1046
+ const timeoutMs = Math.max(1e3, Number.parseInt(args.timeout, 10) || 1e4);
1047
+ if (!args.json) {
1048
+ console.log(`
1049
+ ${pc2.cyan("mcpman bench")} \u2014 ${pc2.bold(args.server)}`);
1050
+ console.log(pc2.dim(` Command: ${entry.command} ${(entry.args ?? []).join(" ")}`));
1051
+ console.log(pc2.dim(` Runs: ${runs} Timeout: ${timeoutMs}ms
1052
+ `));
1053
+ process.stdout.write(pc2.dim(" Running"));
1054
+ }
1055
+ const env = {};
1056
+ for (const ev of entry.envVars ?? []) {
1057
+ const idx = ev.indexOf("=");
1058
+ if (idx > 0) env[ev.slice(0, idx)] = ev.slice(idx + 1);
1059
+ }
1060
+ const result = await benchServer(entry.command, entry.args ?? [], env, runs, timeoutMs);
1061
+ if (!args.json) process.stdout.write("\n");
1062
+ if (result.error) {
1063
+ if (args.json) {
1064
+ console.log(JSON.stringify({ server: args.server, error: result.error }));
1065
+ } else {
1066
+ console.error(`
1067
+ ${pc2.red("\u2717")} Benchmark failed: ${result.error}`);
1068
+ }
1069
+ process.exit(1);
1070
+ }
1071
+ if (args.json) {
1072
+ console.log(JSON.stringify({ server: args.server, ...result }, null, 2));
1073
+ if (result.p95 > timeoutMs) process.exit(1);
1074
+ return;
1075
+ }
1076
+ const pad4 = (s, w) => s.padEnd(w);
1077
+ const ms = (n) => `${n}ms`;
1078
+ console.log(`
1079
+ ${pc2.bold("Latency statistics")} for ${pc2.cyan(args.server)}`);
1080
+ console.log(pc2.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1081
+ console.log(` ${pad4("min", 8)} ${pc2.green(ms(result.min))}`);
1082
+ console.log(` ${pad4("avg", 8)} ${ms(result.avg)}`);
1083
+ console.log(` ${pad4("p50", 8)} ${ms(result.p50)}`);
1084
+ console.log(
1085
+ ` ${pad4("p95", 8)} ${result.p95 > timeoutMs ? pc2.red(ms(result.p95)) : pc2.yellow(ms(result.p95))}`
1086
+ );
1087
+ console.log(` ${pad4("max", 8)} ${ms(result.max)}`);
1088
+ console.log(` ${pad4("runs", 8)} ${result.runs}`);
1089
+ console.log("");
1090
+ if (result.p95 > timeoutMs) {
1091
+ console.error(pc2.red(` \u2717 p95 (${result.p95}ms) exceeds timeout (${timeoutMs}ms)`));
1092
+ process.exit(1);
1093
+ }
1094
+ console.log(pc2.green(" \u2713 Benchmark complete"));
1095
+ }
1096
+ });
1097
+
856
1098
  // src/commands/completions.ts
857
- import fs4 from "fs";
1099
+ import fs5 from "fs";
858
1100
  import os2 from "os";
859
1101
  import path4 from "path";
860
- import { defineCommand as defineCommand2 } from "citty";
861
- import pc2 from "picocolors";
1102
+ import { defineCommand as defineCommand3 } from "citty";
1103
+ import pc3 from "picocolors";
862
1104
 
863
1105
  // src/core/completion-generator.ts
864
1106
  function getCommandList() {
@@ -888,7 +1130,13 @@ function getCommandList() {
888
1130
  "watch",
889
1131
  "registry",
890
1132
  "completions",
891
- "why"
1133
+ "why",
1134
+ "env",
1135
+ "bench",
1136
+ "diff",
1137
+ "group",
1138
+ "pin",
1139
+ "rollback"
892
1140
  ];
893
1141
  }
894
1142
  var SERVER_ARG_COMMANDS = [
@@ -1013,7 +1261,7 @@ complete -c mcpman -l runtime -s r \\
1013
1261
  }
1014
1262
 
1015
1263
  // src/commands/completions.ts
1016
- var completions_default = defineCommand2({
1264
+ var completions_default = defineCommand3({
1017
1265
  meta: {
1018
1266
  name: "completions",
1019
1267
  description: "Generate shell completion scripts (bash, zsh, fish)"
@@ -1063,24 +1311,24 @@ var completions_default = defineCommand2({
1063
1311
  await installCompletion();
1064
1312
  break;
1065
1313
  default:
1066
- console.error(pc2.red(` Error: Unknown shell '${shell}'. Use: bash, zsh, or fish.`));
1314
+ console.error(pc3.red(` Error: Unknown shell '${shell}'. Use: bash, zsh, or fish.`));
1067
1315
  process.exit(1);
1068
1316
  }
1069
1317
  }
1070
1318
  });
1071
1319
  function printUsage() {
1072
- console.log(pc2.bold("\n mcpman completions \u2014 Shell completion setup\n"));
1320
+ console.log(pc3.bold("\n mcpman completions \u2014 Shell completion setup\n"));
1073
1321
  console.log(" Usage:");
1074
- console.log(` ${pc2.cyan("mcpman completions bash")} Output bash completion script`);
1075
- console.log(` ${pc2.cyan("mcpman completions zsh")} Output zsh completion script`);
1076
- console.log(` ${pc2.cyan("mcpman completions fish")} Output fish completion script`);
1077
- console.log(` ${pc2.cyan("mcpman completions install")} Auto-detect shell and install
1322
+ console.log(` ${pc3.cyan("mcpman completions bash")} Output bash completion script`);
1323
+ console.log(` ${pc3.cyan("mcpman completions zsh")} Output zsh completion script`);
1324
+ console.log(` ${pc3.cyan("mcpman completions fish")} Output fish completion script`);
1325
+ console.log(` ${pc3.cyan("mcpman completions install")} Auto-detect shell and install
1078
1326
  `);
1079
1327
  console.log(" Quick setup:");
1080
- console.log(` ${pc2.dim("# bash")}`);
1081
- console.log(` ${pc2.cyan("source <(mcpman completions bash)")}`);
1082
- console.log(` ${pc2.dim("# zsh")}`);
1083
- console.log(` ${pc2.cyan("source <(mcpman completions zsh)")}
1328
+ console.log(` ${pc3.dim("# bash")}`);
1329
+ console.log(` ${pc3.cyan("source <(mcpman completions bash)")}`);
1330
+ console.log(` ${pc3.dim("# zsh")}`);
1331
+ console.log(` ${pc3.cyan("source <(mcpman completions zsh)")}
1084
1332
  `);
1085
1333
  }
1086
1334
  async function installCompletion() {
@@ -1090,8 +1338,8 @@ async function installCompletion() {
1090
1338
  else if (shellBin.includes("fish")) detectedShell = "fish";
1091
1339
  else if (shellBin.includes("bash")) detectedShell = "bash";
1092
1340
  if (!detectedShell) {
1093
- console.error(pc2.red(" Could not detect shell from $SHELL. Run manually:"));
1094
- console.error(pc2.dim(" source <(mcpman completions bash|zsh|fish)"));
1341
+ console.error(pc3.red(" Could not detect shell from $SHELL. Run manually:"));
1342
+ console.error(pc3.dim(" source <(mcpman completions bash|zsh|fish)"));
1095
1343
  process.exit(1);
1096
1344
  }
1097
1345
  const home = os2.homedir();
@@ -1102,10 +1350,10 @@ async function installCompletion() {
1102
1350
  script = generateZshCompletion();
1103
1351
  } else if (detectedShell === "fish") {
1104
1352
  const fishDir = path4.join(home, ".config", "fish", "completions");
1105
- fs4.mkdirSync(fishDir, { recursive: true });
1353
+ fs5.mkdirSync(fishDir, { recursive: true });
1106
1354
  rcFile = path4.join(fishDir, "mcpman.fish");
1107
- fs4.writeFileSync(rcFile, generateFishCompletion(), "utf-8");
1108
- console.log(pc2.green(` Installed fish completions to ${rcFile}`));
1355
+ fs5.writeFileSync(rcFile, generateFishCompletion(), "utf-8");
1356
+ console.log(pc3.green(` Installed fish completions to ${rcFile}`));
1109
1357
  return;
1110
1358
  } else {
1111
1359
  rcFile = path4.join(home, ".bashrc");
@@ -1114,25 +1362,25 @@ async function installCompletion() {
1114
1362
  const marker = "# mcpman completions";
1115
1363
  let existing = "";
1116
1364
  try {
1117
- existing = fs4.readFileSync(rcFile, "utf-8");
1365
+ existing = fs5.readFileSync(rcFile, "utf-8");
1118
1366
  } catch {
1119
1367
  }
1120
1368
  if (existing.includes(marker)) {
1121
- console.log(pc2.yellow(` Completions already installed in ${rcFile}. Skipping.`));
1369
+ console.log(pc3.yellow(` Completions already installed in ${rcFile}. Skipping.`));
1122
1370
  return;
1123
1371
  }
1124
- fs4.appendFileSync(rcFile, `
1372
+ fs5.appendFileSync(rcFile, `
1125
1373
  ${marker}
1126
1374
  source <(mcpman completions ${detectedShell})
1127
1375
  `);
1128
- console.log(pc2.green(` Installed ${detectedShell} completions in ${rcFile}`));
1129
- console.log(pc2.dim(` Restart your shell or run: source ${rcFile}`));
1376
+ console.log(pc3.green(` Installed ${detectedShell} completions in ${rcFile}`));
1377
+ console.log(pc3.dim(` Restart your shell or run: source ${rcFile}`));
1130
1378
  }
1131
1379
 
1132
1380
  // src/commands/config.ts
1133
1381
  import * as p2 from "@clack/prompts";
1134
- import { defineCommand as defineCommand3 } from "citty";
1135
- import pc3 from "picocolors";
1382
+ import { defineCommand as defineCommand4 } from "citty";
1383
+ import pc4 from "picocolors";
1136
1384
  function coerceValue(raw) {
1137
1385
  if (raw === "true") return true;
1138
1386
  if (raw === "false") return false;
@@ -1140,7 +1388,7 @@ function coerceValue(raw) {
1140
1388
  if (!Number.isNaN(num) && raw.trim() !== "") return num;
1141
1389
  return raw;
1142
1390
  }
1143
- var setCommand = defineCommand3({
1391
+ var setCommand = defineCommand4({
1144
1392
  meta: { name: "set", description: "Set a config value" },
1145
1393
  args: {
1146
1394
  key: {
@@ -1158,14 +1406,14 @@ var setCommand = defineCommand3({
1158
1406
  try {
1159
1407
  const coerced = coerceValue(args.value);
1160
1408
  setConfigValue(args.key, coerced);
1161
- console.log(`${pc3.green("\u2713")} Set ${pc3.bold(args.key)} = ${pc3.cyan(String(coerced))}`);
1409
+ console.log(`${pc4.green("\u2713")} Set ${pc4.bold(args.key)} = ${pc4.cyan(String(coerced))}`);
1162
1410
  } catch (err) {
1163
- console.error(`${pc3.red("\u2717")} ${String(err)}`);
1411
+ console.error(`${pc4.red("\u2717")} ${String(err)}`);
1164
1412
  process.exit(1);
1165
1413
  }
1166
1414
  }
1167
1415
  });
1168
- var getCommand = defineCommand3({
1416
+ var getCommand = defineCommand4({
1169
1417
  meta: { name: "get", description: "Get a config value" },
1170
1418
  args: {
1171
1419
  key: {
@@ -1177,32 +1425,32 @@ var getCommand = defineCommand3({
1177
1425
  run({ args }) {
1178
1426
  const val = getConfigValue(args.key);
1179
1427
  if (val === void 0) {
1180
- console.log(pc3.dim(`${args.key}: (not set)`));
1428
+ console.log(pc4.dim(`${args.key}: (not set)`));
1181
1429
  } else {
1182
- console.log(`${pc3.bold(args.key)}: ${pc3.cyan(String(val))}`);
1430
+ console.log(`${pc4.bold(args.key)}: ${pc4.cyan(String(val))}`);
1183
1431
  }
1184
1432
  }
1185
1433
  });
1186
- var listCommand = defineCommand3({
1434
+ var listCommand = defineCommand4({
1187
1435
  meta: { name: "list", description: "List all config values" },
1188
1436
  run() {
1189
1437
  const data = readConfig();
1190
1438
  const entries = Object.entries(data);
1191
1439
  if (entries.length === 0) {
1192
- console.log(pc3.dim("No config values set. Use `mcpman config set <key> <value>`."));
1440
+ console.log(pc4.dim("No config values set. Use `mcpman config set <key> <value>`."));
1193
1441
  return;
1194
1442
  }
1195
1443
  console.log("");
1196
- console.log(pc3.bold("mcpman config:"));
1444
+ console.log(pc4.bold("mcpman config:"));
1197
1445
  console.log("");
1198
1446
  for (const [key, val] of entries) {
1199
- console.log(` ${pc3.green("\u25CF")} ${pc3.bold(key)} ${pc3.cyan(String(val))}`);
1447
+ console.log(` ${pc4.green("\u25CF")} ${pc4.bold(key)} ${pc4.cyan(String(val))}`);
1200
1448
  }
1201
1449
  console.log("");
1202
- console.log(pc3.dim(` ${entries.length} key${entries.length !== 1 ? "s" : ""} configured`));
1450
+ console.log(pc4.dim(` ${entries.length} key${entries.length !== 1 ? "s" : ""} configured`));
1203
1451
  }
1204
1452
  });
1205
- var resetCommand = defineCommand3({
1453
+ var resetCommand = defineCommand4({
1206
1454
  meta: { name: "reset", description: "Reset config to defaults (removes config file)" },
1207
1455
  async run() {
1208
1456
  const confirmed = await p2.confirm({
@@ -1214,10 +1462,10 @@ var resetCommand = defineCommand3({
1214
1462
  return;
1215
1463
  }
1216
1464
  writeConfig({});
1217
- console.log(`${pc3.green("\u2713")} Config reset to defaults.`);
1465
+ console.log(`${pc4.green("\u2713")} Config reset to defaults.`);
1218
1466
  }
1219
1467
  });
1220
- var config_default = defineCommand3({
1468
+ var config_default = defineCommand4({
1221
1469
  meta: {
1222
1470
  name: "config",
1223
1471
  description: "Manage mcpman CLI configuration"
@@ -1232,11 +1480,11 @@ var config_default = defineCommand3({
1232
1480
 
1233
1481
  // src/commands/create.ts
1234
1482
  import path6 from "path";
1235
- import { defineCommand as defineCommand4 } from "citty";
1236
- import pc4 from "picocolors";
1483
+ import { defineCommand as defineCommand5 } from "citty";
1484
+ import pc5 from "picocolors";
1237
1485
 
1238
1486
  // src/core/scaffold-service.ts
1239
- import fs5 from "fs";
1487
+ import fs6 from "fs";
1240
1488
  import path5 from "path";
1241
1489
  function sanitizeName(name) {
1242
1490
  return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
@@ -1389,8 +1637,8 @@ if __name__ == "__main__":
1389
1637
  };
1390
1638
  }
1391
1639
  function writeScaffold(dir, files) {
1392
- if (fs5.existsSync(dir)) {
1393
- const existing = fs5.readdirSync(dir);
1640
+ if (fs6.existsSync(dir)) {
1641
+ const existing = fs6.readdirSync(dir);
1394
1642
  if (existing.length > 0) {
1395
1643
  throw new Error(`Directory '${dir}' already exists and is not empty.`);
1396
1644
  }
@@ -1398,13 +1646,13 @@ function writeScaffold(dir, files) {
1398
1646
  for (const [relativePath, content] of Object.entries(files)) {
1399
1647
  const fullPath = path5.join(dir, relativePath);
1400
1648
  const parentDir = path5.dirname(fullPath);
1401
- fs5.mkdirSync(parentDir, { recursive: true });
1402
- fs5.writeFileSync(fullPath, content, "utf-8");
1649
+ fs6.mkdirSync(parentDir, { recursive: true });
1650
+ fs6.writeFileSync(fullPath, content, "utf-8");
1403
1651
  }
1404
1652
  }
1405
1653
 
1406
1654
  // src/commands/create.ts
1407
- var create_default = defineCommand4({
1655
+ var create_default = defineCommand5({
1408
1656
  meta: {
1409
1657
  name: "create",
1410
1658
  description: "Scaffold a new MCP server project"
@@ -1442,7 +1690,7 @@ var create_default = defineCommand4({
1442
1690
  const readline = await import("readline");
1443
1691
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1444
1692
  projectName = await new Promise((resolve) => {
1445
- rl.question(pc4.cyan(" Project name: "), (answer) => {
1693
+ rl.question(pc5.cyan(" Project name: "), (answer) => {
1446
1694
  rl.close();
1447
1695
  resolve(answer.trim());
1448
1696
  });
@@ -1452,7 +1700,7 @@ var create_default = defineCommand4({
1452
1700
  const readline = await import("readline");
1453
1701
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1454
1702
  projectDescription = await new Promise((resolve) => {
1455
- rl.question(pc4.cyan(" Description (optional): "), (answer) => {
1703
+ rl.question(pc5.cyan(" Description (optional): "), (answer) => {
1456
1704
  rl.close();
1457
1705
  resolve(answer.trim());
1458
1706
  });
@@ -1462,7 +1710,7 @@ var create_default = defineCommand4({
1462
1710
  const readline = await import("readline");
1463
1711
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1464
1712
  const answer = await new Promise((resolve) => {
1465
- rl.question(pc4.cyan(" Runtime [node/python] (default: node): "), (a) => {
1713
+ rl.question(pc5.cyan(" Runtime [node/python] (default: node): "), (a) => {
1466
1714
  rl.close();
1467
1715
  resolve(a.trim() || "node");
1468
1716
  });
@@ -1471,16 +1719,16 @@ var create_default = defineCommand4({
1471
1719
  }
1472
1720
  }
1473
1721
  if (!projectName) {
1474
- console.error(pc4.red(" Error: Project name is required."));
1722
+ console.error(pc5.red(" Error: Project name is required."));
1475
1723
  process.exit(1);
1476
1724
  }
1477
1725
  const sanitized = sanitizeName(projectName);
1478
1726
  if (!sanitized) {
1479
- console.error(pc4.red(` Error: Invalid project name '${projectName}'.`));
1727
+ console.error(pc5.red(` Error: Invalid project name '${projectName}'.`));
1480
1728
  process.exit(1);
1481
1729
  }
1482
1730
  if (runtime !== "node" && runtime !== "python") {
1483
- console.error(pc4.red(` Error: Unknown runtime '${runtime}'. Use node or python.`));
1731
+ console.error(pc5.red(` Error: Unknown runtime '${runtime}'. Use node or python.`));
1484
1732
  process.exit(1);
1485
1733
  }
1486
1734
  const options = {
@@ -1494,44 +1742,206 @@ var create_default = defineCommand4({
1494
1742
  try {
1495
1743
  writeScaffold(targetDir, files);
1496
1744
  } catch (err) {
1497
- console.error(pc4.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
1745
+ console.error(pc5.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
1498
1746
  process.exit(1);
1499
1747
  }
1500
- console.log(pc4.green(`
1501
- Created ${pc4.bold(sanitized)}/
1748
+ console.log(pc5.green(`
1749
+ Created ${pc5.bold(sanitized)}/
1502
1750
  `));
1503
- console.log(pc4.dim(" Files generated:"));
1751
+ console.log(pc5.dim(" Files generated:"));
1504
1752
  for (const file of Object.keys(files)) {
1505
- console.log(` ${pc4.cyan(file)}`);
1753
+ console.log(` ${pc5.cyan(file)}`);
1506
1754
  }
1507
1755
  console.log("\n Next steps:");
1508
1756
  if (runtime === "node") {
1509
- console.log(` ${pc4.bold(`cd ${sanitized}`)}`);
1510
- console.log(` ${pc4.bold("npm install")}`);
1511
- console.log(` ${pc4.bold("mcpman link .")}`);
1757
+ console.log(` ${pc5.bold(`cd ${sanitized}`)}`);
1758
+ console.log(` ${pc5.bold("npm install")}`);
1759
+ console.log(` ${pc5.bold("mcpman link .")}`);
1512
1760
  } else {
1513
- console.log(` ${pc4.bold(`cd ${sanitized}`)}`);
1514
- console.log(` ${pc4.bold("pip install -e .")}`);
1515
- console.log(` ${pc4.bold("mcpman link .")}`);
1761
+ console.log(` ${pc5.bold(`cd ${sanitized}`)}`);
1762
+ console.log(` ${pc5.bold("pip install -e .")}`);
1763
+ console.log(` ${pc5.bold("mcpman link .")}`);
1516
1764
  }
1517
1765
  console.log();
1518
1766
  }
1519
1767
  });
1520
1768
 
1769
+ // src/commands/diff.ts
1770
+ import { defineCommand as defineCommand6 } from "citty";
1771
+ import pc6 from "picocolors";
1772
+
1773
+ // src/core/config-differ.ts
1774
+ function entryDiffs(a, b) {
1775
+ const diffs = [];
1776
+ if (a.command !== b.command) {
1777
+ diffs.push(`command: ${a.command} \u2192 ${b.command}`);
1778
+ }
1779
+ const aArgs = JSON.stringify(a.args ?? []);
1780
+ const bArgs = JSON.stringify(b.args ?? []);
1781
+ if (aArgs !== bArgs) {
1782
+ diffs.push(`args: ${aArgs} \u2192 ${bArgs}`);
1783
+ }
1784
+ const aEnv = JSON.stringify(a.env ?? {});
1785
+ const bEnv = JSON.stringify(b.env ?? {});
1786
+ if (aEnv !== bEnv) {
1787
+ diffs.push(`env: ${aEnv} \u2192 ${bEnv}`);
1788
+ }
1789
+ return diffs;
1790
+ }
1791
+ function diffClientConfigs(configA, configB) {
1792
+ const results = [];
1793
+ const serversA = configA.servers;
1794
+ const serversB = configB.servers;
1795
+ for (const name of Object.keys(serversB)) {
1796
+ if (!(name in serversA)) {
1797
+ results.push({ server: name, change: "added" });
1798
+ }
1799
+ }
1800
+ for (const name of Object.keys(serversA)) {
1801
+ if (!(name in serversB)) {
1802
+ results.push({ server: name, change: "removed" });
1803
+ }
1804
+ }
1805
+ for (const name of Object.keys(serversA)) {
1806
+ if (name in serversB) {
1807
+ const details = entryDiffs(serversA[name], serversB[name]);
1808
+ if (details.length > 0) {
1809
+ results.push({ server: name, change: "changed", details });
1810
+ }
1811
+ }
1812
+ }
1813
+ const order = { removed: 0, added: 1, changed: 2 };
1814
+ results.sort((a, b) => {
1815
+ const orderDiff = order[a.change] - order[b.change];
1816
+ return orderDiff !== 0 ? orderDiff : a.server.localeCompare(b.server);
1817
+ });
1818
+ return results;
1819
+ }
1820
+ async function loadClientConfig(type) {
1821
+ try {
1822
+ const { getClient } = await import("./client-detector-NHXBDNMY.js");
1823
+ const handler = getClient(type);
1824
+ return await handler.readConfig();
1825
+ } catch {
1826
+ return null;
1827
+ }
1828
+ }
1829
+
1830
+ // src/commands/diff.ts
1831
+ var VALID_CLIENTS = ["claude-desktop", "cursor", "vscode", "windsurf"];
1832
+ var CLIENT_DISPLAY = {
1833
+ "claude-desktop": "Claude Desktop",
1834
+ cursor: "Cursor",
1835
+ vscode: "VS Code",
1836
+ windsurf: "Windsurf"
1837
+ };
1838
+ var diff_default = defineCommand6({
1839
+ meta: {
1840
+ name: "diff",
1841
+ description: "Show config diff between two AI clients"
1842
+ },
1843
+ args: {
1844
+ clientA: {
1845
+ type: "positional",
1846
+ description: `Source client (${VALID_CLIENTS.join("|")})`,
1847
+ required: true
1848
+ },
1849
+ clientB: {
1850
+ type: "positional",
1851
+ description: `Target client (${VALID_CLIENTS.join("|")})`,
1852
+ required: true
1853
+ },
1854
+ json: {
1855
+ type: "boolean",
1856
+ description: "Output results as JSON",
1857
+ default: false
1858
+ }
1859
+ },
1860
+ async run({ args }) {
1861
+ const clientA = args.clientA;
1862
+ const clientB = args.clientB;
1863
+ if (!VALID_CLIENTS.includes(clientA)) {
1864
+ console.error(
1865
+ `${pc6.red("\u2717")} Unknown client "${clientA}". Valid: ${VALID_CLIENTS.join(", ")}`
1866
+ );
1867
+ process.exit(1);
1868
+ }
1869
+ if (!VALID_CLIENTS.includes(clientB)) {
1870
+ console.error(
1871
+ `${pc6.red("\u2717")} Unknown client "${clientB}". Valid: ${VALID_CLIENTS.join(", ")}`
1872
+ );
1873
+ process.exit(1);
1874
+ }
1875
+ if (clientA === clientB) {
1876
+ console.error(`${pc6.red("\u2717")} clientA and clientB must be different.`);
1877
+ process.exit(1);
1878
+ }
1879
+ const [configA, configB] = await Promise.all([
1880
+ loadClientConfig(clientA),
1881
+ loadClientConfig(clientB)
1882
+ ]);
1883
+ if (!configA) {
1884
+ console.error(`${pc6.red("\u2717")} Could not read config for ${CLIENT_DISPLAY[clientA]}.`);
1885
+ process.exit(1);
1886
+ }
1887
+ if (!configB) {
1888
+ console.error(`${pc6.red("\u2717")} Could not read config for ${CLIENT_DISPLAY[clientB]}.`);
1889
+ process.exit(1);
1890
+ }
1891
+ const diffs = diffClientConfigs(configA, configB);
1892
+ if (args.json) {
1893
+ console.log(JSON.stringify({ clientA, clientB, diffs }, null, 2));
1894
+ return;
1895
+ }
1896
+ const labelA = CLIENT_DISPLAY[clientA];
1897
+ const labelB = CLIENT_DISPLAY[clientB];
1898
+ console.log(`
1899
+ ${pc6.bold("mcpman diff")} ${pc6.cyan(labelA)} \u2192 ${pc6.cyan(labelB)}
1900
+ `);
1901
+ if (diffs.length === 0) {
1902
+ console.log(pc6.green(" \u2713 No differences \u2014 configs are identical."));
1903
+ console.log("");
1904
+ return;
1905
+ }
1906
+ for (const d of diffs) {
1907
+ if (d.change === "added") {
1908
+ console.log(` ${pc6.green("+")} ${pc6.bold(d.server)} ${pc6.dim(`(only in ${labelB})`)}`);
1909
+ } else if (d.change === "removed") {
1910
+ console.log(` ${pc6.red("-")} ${pc6.bold(d.server)} ${pc6.dim(`(only in ${labelA})`)}`);
1911
+ } else {
1912
+ console.log(` ${pc6.yellow("~")} ${pc6.bold(d.server)} ${pc6.dim("(changed)")}`);
1913
+ for (const detail of d.details ?? []) {
1914
+ console.log(` ${pc6.dim(detail)}`);
1915
+ }
1916
+ }
1917
+ }
1918
+ const added = diffs.filter((d) => d.change === "added").length;
1919
+ const removed = diffs.filter((d) => d.change === "removed").length;
1920
+ const changed = diffs.filter((d) => d.change === "changed").length;
1921
+ const parts = [];
1922
+ if (added > 0) parts.push(pc6.green(`+${added} added`));
1923
+ if (removed > 0) parts.push(pc6.red(`-${removed} removed`));
1924
+ if (changed > 0) parts.push(pc6.yellow(`~${changed} changed`));
1925
+ console.log(`
1926
+ ${parts.join(" ")}
1927
+ `);
1928
+ }
1929
+ });
1930
+
1521
1931
  // src/commands/doctor.ts
1522
- import { defineCommand as defineCommand5 } from "citty";
1523
- import pc5 from "picocolors";
1932
+ import { defineCommand as defineCommand7 } from "citty";
1933
+ import pc7 from "picocolors";
1524
1934
 
1525
1935
  // src/core/diagnostics.ts
1526
1936
  import { exec } from "child_process";
1527
1937
  import { promisify } from "util";
1528
1938
 
1529
1939
  // src/core/mcp-process-checks.ts
1530
- import { spawn } from "child_process";
1940
+ import { spawn as spawn2 } from "child_process";
1531
1941
  async function checkProcessSpawn(command, args, env, timeoutMs = 3e3) {
1532
1942
  return new Promise((resolve) => {
1533
1943
  let settled = false;
1534
- const child = spawn(command, args, {
1944
+ const child = spawn2(command, args, {
1535
1945
  env: { ...process.env, ...env },
1536
1946
  stdio: ["pipe", "pipe", "pipe"]
1537
1947
  });
@@ -1577,7 +1987,7 @@ async function checkMcpHandshake(command, args, env, timeoutMs = 5e3) {
1577
1987
  let settled = false;
1578
1988
  const start = Date.now();
1579
1989
  let stdout = "";
1580
- const child = spawn(command, args, {
1990
+ const child = spawn2(command, args, {
1581
1991
  env: { ...process.env, ...env },
1582
1992
  stdio: ["pipe", "pipe", "pipe"]
1583
1993
  });
@@ -1865,12 +2275,12 @@ async function getInstalledServers(clientFilter) {
1865
2275
 
1866
2276
  // src/commands/doctor.ts
1867
2277
  var CHECK_ICON = {
1868
- pass: pc5.green("\u2713"),
1869
- fail: pc5.red("\u2717"),
1870
- skip: pc5.dim("-"),
1871
- warn: pc5.yellow("\u26A0")
2278
+ pass: pc7.green("\u2713"),
2279
+ fail: pc7.red("\u2717"),
2280
+ skip: pc7.dim("-"),
2281
+ warn: pc7.yellow("\u26A0")
1872
2282
  };
1873
- var doctor_default = defineCommand5({
2283
+ var doctor_default = defineCommand7({
1874
2284
  meta: {
1875
2285
  name: "doctor",
1876
2286
  description: "Check MCP server health and configuration"
@@ -1883,11 +2293,11 @@ var doctor_default = defineCommand5({
1883
2293
  }
1884
2294
  },
1885
2295
  async run({ args }) {
1886
- console.log(pc5.bold("\n mcpman doctor\n"));
2296
+ console.log(pc7.bold("\n mcpman doctor\n"));
1887
2297
  const servers = await getInstalledServers();
1888
2298
  if (servers.length === 0) {
1889
2299
  console.log(
1890
- pc5.dim(" No MCP servers installed. Run mcpman install <server> to get started.")
2300
+ pc7.dim(" No MCP servers installed. Run mcpman install <server> to get started.")
1891
2301
  );
1892
2302
  return;
1893
2303
  }
@@ -1904,20 +2314,20 @@ var doctor_default = defineCommand5({
1904
2314
  if (pluginSummary.total > 0) {
1905
2315
  printPluginSection(pluginSummary);
1906
2316
  }
1907
- console.log(pc5.dim(` ${"\u2500".repeat(50)}`));
2317
+ console.log(pc7.dim(` ${"\u2500".repeat(50)}`));
1908
2318
  const parts = [];
1909
- if (passed > 0) parts.push(pc5.green(`${passed} healthy`));
1910
- if (failed > 0) parts.push(pc5.red(`${failed} unhealthy`));
2319
+ if (passed > 0) parts.push(pc7.green(`${passed} healthy`));
2320
+ if (failed > 0) parts.push(pc7.red(`${failed} unhealthy`));
1911
2321
  console.log(` Summary: ${parts.join(", ")}`);
1912
2322
  if (pluginSummary.total > 0) {
1913
2323
  const pParts = [];
1914
- if (pluginSummary.healthy > 0) pParts.push(pc5.green(`${pluginSummary.healthy} ok`));
1915
- if (pluginSummary.unhealthy > 0) pParts.push(pc5.red(`${pluginSummary.unhealthy} broken`));
2324
+ if (pluginSummary.healthy > 0) pParts.push(pc7.green(`${pluginSummary.healthy} ok`));
2325
+ if (pluginSummary.unhealthy > 0) pParts.push(pc7.red(`${pluginSummary.unhealthy} broken`));
1916
2326
  console.log(` Plugins: ${pParts.join(", ")}`);
1917
2327
  }
1918
2328
  if (failed > 0 || pluginSummary.unhealthy > 0) {
1919
2329
  if (!args.fix) {
1920
- console.log(pc5.dim(` Run ${pc5.cyan("mcpman doctor --fix")} for fix suggestions.
2330
+ console.log(pc7.dim(` Run ${pc7.cyan("mcpman doctor --fix")} for fix suggestions.
1921
2331
  `));
1922
2332
  }
1923
2333
  process.exit(1);
@@ -1926,26 +2336,26 @@ var doctor_default = defineCommand5({
1926
2336
  }
1927
2337
  });
1928
2338
  function printPluginSection(summary) {
1929
- console.log(pc5.bold(` Plugins (${summary.total})`));
2339
+ console.log(pc7.bold(` Plugins (${summary.total})`));
1930
2340
  for (const r of summary.results) {
1931
- const icon = r.loadable && r.prefixUnique && r.resolvable ? pc5.green("\u2713") : pc5.red("\u2717");
2341
+ const icon = r.loadable && r.prefixUnique && r.resolvable ? pc7.green("\u2713") : pc7.red("\u2717");
1932
2342
  const label = r.pluginName ? `${r.packageName} (${r.pluginName})` : r.packageName;
1933
- const prefix = r.prefix ? pc5.dim(` [${r.prefix}]`) : "";
2343
+ const prefix = r.prefix ? pc7.dim(` [${r.prefix}]`) : "";
1934
2344
  console.log(` ${icon} ${label}${prefix}`);
1935
2345
  if (r.error) {
1936
- console.log(` ${pc5.yellow("\u2192")} ${r.error}`);
2346
+ console.log(` ${pc7.yellow("\u2192")} ${r.error}`);
1937
2347
  }
1938
2348
  }
1939
2349
  console.log();
1940
2350
  }
1941
2351
  function printServerResult(result, showFix) {
1942
- const icon = result.status === "healthy" ? pc5.green("\u25CF") : pc5.red("\u25CF");
1943
- console.log(` ${icon} ${pc5.bold(result.serverName)}`);
2352
+ const icon = result.status === "healthy" ? pc7.green("\u25CF") : pc7.red("\u25CF");
2353
+ console.log(` ${icon} ${pc7.bold(result.serverName)}`);
1944
2354
  for (const check of result.checks) {
1945
2355
  const checkIcon = check.skipped ? CHECK_ICON.skip : check.passed ? CHECK_ICON.pass : CHECK_ICON.fail;
1946
2356
  console.log(` ${checkIcon} ${check.name}: ${check.message}`);
1947
2357
  if (showFix && !check.passed && !check.skipped && check.fix) {
1948
- console.log(` ${pc5.yellow("\u2192")} Fix: ${pc5.cyan(check.fix)}`);
2358
+ console.log(` ${pc7.yellow("\u2192")} Fix: ${pc7.cyan(check.fix)}`);
1949
2359
  }
1950
2360
  }
1951
2361
  console.log();
@@ -1954,11 +2364,11 @@ async function runParallel(tasks, concurrency) {
1954
2364
  const results = [];
1955
2365
  const executing = /* @__PURE__ */ new Set();
1956
2366
  for (const task of tasks) {
1957
- const p12 = task().then((r) => {
2367
+ const p13 = task().then((r) => {
1958
2368
  results.push(r);
1959
- executing.delete(p12);
2369
+ executing.delete(p13);
1960
2370
  });
1961
- executing.add(p12);
2371
+ executing.add(p13);
1962
2372
  if (executing.size >= concurrency) {
1963
2373
  await Promise.race(executing);
1964
2374
  }
@@ -1967,18 +2377,188 @@ async function runParallel(tasks, concurrency) {
1967
2377
  return results;
1968
2378
  }
1969
2379
 
1970
- // src/commands/export-command.ts
2380
+ // src/commands/env.ts
2381
+ import { defineCommand as defineCommand8 } from "citty";
2382
+ import pc8 from "picocolors";
2383
+
2384
+ // src/core/env-manager.ts
1971
2385
  import fs7 from "fs";
1972
2386
  import path7 from "path";
1973
- import { defineCommand as defineCommand6 } from "citty";
1974
- import pc6 from "picocolors";
2387
+ function envFilePath(server, dir) {
2388
+ const base = dir ?? getEnvDir();
2389
+ const safe = server.replace(/[/@]/g, "_");
2390
+ return path7.join(base, `${safe}.json`);
2391
+ }
2392
+ function readStore(server, dir) {
2393
+ const file = envFilePath(server, dir);
2394
+ if (!fs7.existsSync(file)) return {};
2395
+ try {
2396
+ return JSON.parse(fs7.readFileSync(file, "utf-8"));
2397
+ } catch {
2398
+ return {};
2399
+ }
2400
+ }
2401
+ function writeStore(server, store, dir) {
2402
+ const base = dir ?? getEnvDir();
2403
+ if (!fs7.existsSync(base)) {
2404
+ fs7.mkdirSync(base, { recursive: true });
2405
+ }
2406
+ const file = envFilePath(server, dir);
2407
+ fs7.writeFileSync(file, JSON.stringify(store, null, 2), "utf-8");
2408
+ }
2409
+ function setEnv(server, key, value, dir) {
2410
+ const store = readStore(server, dir);
2411
+ store[key] = value;
2412
+ writeStore(server, store, dir);
2413
+ }
2414
+ function getEnv(server, key, dir) {
2415
+ const store = readStore(server, dir);
2416
+ return key in store ? store[key] : null;
2417
+ }
2418
+ function listEnv(server, dir) {
2419
+ return readStore(server, dir);
2420
+ }
2421
+ function deleteEnv(server, key, dir) {
2422
+ const store = readStore(server, dir);
2423
+ if (!(key in store)) return;
2424
+ delete store[key];
2425
+ writeStore(server, store, dir);
2426
+ }
2427
+ function clearEnv(server, dir) {
2428
+ const file = envFilePath(server, dir);
2429
+ if (fs7.existsSync(file)) {
2430
+ fs7.unlinkSync(file);
2431
+ }
2432
+ }
2433
+ function listEnvServers(dir) {
2434
+ const base = dir ?? getEnvDir();
2435
+ if (!fs7.existsSync(base)) return [];
2436
+ return fs7.readdirSync(base).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -5));
2437
+ }
2438
+
2439
+ // src/commands/env.ts
2440
+ var setCmd = defineCommand8({
2441
+ meta: { name: "set", description: "Set env var(s) for a server" },
2442
+ args: {
2443
+ server: { type: "positional", description: "Server name", required: true },
2444
+ pairs: { type: "positional", description: "KEY=VALUE pair(s)", required: true }
2445
+ },
2446
+ run({ args }) {
2447
+ const pairs = Array.isArray(args.pairs) ? args.pairs : [args.pairs];
2448
+ for (const pair of pairs) {
2449
+ const idx = pair.indexOf("=");
2450
+ if (idx <= 0) {
2451
+ console.error(`${pc8.red("\u2717")} Invalid format: "${pair}". Expected KEY=VALUE`);
2452
+ process.exit(1);
2453
+ }
2454
+ const key = pair.slice(0, idx);
2455
+ const value = pair.slice(idx + 1);
2456
+ setEnv(args.server, key, value);
2457
+ console.log(
2458
+ `${pc8.green("\u2713")} Set ${pc8.bold(key)}=${pc8.dim(value)} for ${pc8.cyan(args.server)}`
2459
+ );
2460
+ }
2461
+ }
2462
+ });
2463
+ var getCmd = defineCommand8({
2464
+ meta: { name: "get", description: "Get an env var for a server" },
2465
+ args: {
2466
+ server: { type: "positional", description: "Server name", required: true },
2467
+ key: { type: "positional", description: "Variable name", required: true }
2468
+ },
2469
+ run({ args }) {
2470
+ const value = getEnv(args.server, args.key);
2471
+ if (value === null) {
2472
+ console.error(`${pc8.red("\u2717")} No env var "${args.key}" for ${pc8.cyan(args.server)}`);
2473
+ process.exit(1);
2474
+ }
2475
+ console.log(value);
2476
+ }
2477
+ });
2478
+ var listCmd = defineCommand8({
2479
+ meta: { name: "list", description: "List env vars for a server (or all servers)" },
2480
+ args: {
2481
+ server: { type: "positional", description: "Server name (optional)", required: false }
2482
+ },
2483
+ run({ args }) {
2484
+ if (args.server) {
2485
+ const store = listEnv(args.server);
2486
+ const keys = Object.keys(store);
2487
+ if (keys.length === 0) {
2488
+ console.log(pc8.dim(`No env vars for ${pc8.cyan(args.server)}.`));
2489
+ return;
2490
+ }
2491
+ console.log(`
2492
+ ${pc8.bold(pc8.cyan(args.server))}`);
2493
+ for (const [k, v] of Object.entries(store)) {
2494
+ console.log(` ${pc8.green("\u25CF")} ${pc8.bold(k)}=${pc8.dim(v)}`);
2495
+ }
2496
+ console.log("");
2497
+ } else {
2498
+ const servers = listEnvServers();
2499
+ if (servers.length === 0) {
2500
+ console.log(pc8.dim("No env vars stored."));
2501
+ return;
2502
+ }
2503
+ for (const srv of servers) {
2504
+ const store = listEnv(srv);
2505
+ console.log(`
2506
+ ${pc8.bold(pc8.cyan(srv))}`);
2507
+ for (const [k, v] of Object.entries(store)) {
2508
+ console.log(` ${pc8.green("\u25CF")} ${pc8.bold(k)}=${pc8.dim(v)}`);
2509
+ }
2510
+ }
2511
+ console.log("");
2512
+ }
2513
+ }
2514
+ });
2515
+ var delCmd = defineCommand8({
2516
+ meta: { name: "del", description: "Delete an env var for a server" },
2517
+ args: {
2518
+ server: { type: "positional", description: "Server name", required: true },
2519
+ key: { type: "positional", description: "Variable name to delete", required: true }
2520
+ },
2521
+ run({ args }) {
2522
+ deleteEnv(args.server, args.key);
2523
+ console.log(`${pc8.green("\u2713")} Deleted ${pc8.bold(args.key)} from ${pc8.cyan(args.server)}`);
2524
+ }
2525
+ });
2526
+ var clearCmd = defineCommand8({
2527
+ meta: { name: "clear", description: "Clear all env vars for a server" },
2528
+ args: {
2529
+ server: { type: "positional", description: "Server name", required: true }
2530
+ },
2531
+ run({ args }) {
2532
+ clearEnv(args.server);
2533
+ console.log(`${pc8.green("\u2713")} Cleared all env vars for ${pc8.cyan(args.server)}`);
2534
+ }
2535
+ });
2536
+ var env_default = defineCommand8({
2537
+ meta: {
2538
+ name: "env",
2539
+ description: "Manage per-server environment variables (non-sensitive)"
2540
+ },
2541
+ subCommands: {
2542
+ set: setCmd,
2543
+ get: getCmd,
2544
+ list: listCmd,
2545
+ del: delCmd,
2546
+ clear: clearCmd
2547
+ }
2548
+ });
2549
+
2550
+ // src/commands/export-command.ts
2551
+ import fs9 from "fs";
2552
+ import path8 from "path";
2553
+ import { defineCommand as defineCommand9 } from "citty";
2554
+ import pc9 from "picocolors";
1975
2555
 
1976
2556
  // src/core/export-import-service.ts
1977
- import fs6 from "fs";
2557
+ import fs8 from "fs";
1978
2558
 
1979
2559
  // src/utils/constants.ts
1980
2560
  var APP_NAME = "mcpman";
1981
- var APP_VERSION = "0.7.0";
2561
+ var APP_VERSION = "0.8.0";
1982
2562
  var APP_DESCRIPTION = "The package manager for MCP servers";
1983
2563
 
1984
2564
  // src/core/export-import-service.ts
@@ -1994,7 +2574,7 @@ function createExportBundle(opts = {}) {
1994
2574
  };
1995
2575
  if (includeVault) {
1996
2576
  const vaultPath = getVaultPath();
1997
- if (fs6.existsSync(vaultPath)) {
2577
+ if (fs8.existsSync(vaultPath)) {
1998
2578
  bundle.vault = readVault();
1999
2579
  }
2000
2580
  }
@@ -2055,58 +2635,268 @@ function importBundle(bundle, opts = {}) {
2055
2635
  }
2056
2636
  }
2057
2637
  }
2058
- return summary;
2638
+ return summary;
2639
+ }
2640
+
2641
+ // src/commands/export-command.ts
2642
+ var DEFAULT_OUTPUT = "mcpman-export.json";
2643
+ var export_command_default = defineCommand9({
2644
+ meta: {
2645
+ name: "export",
2646
+ description: "Export mcpman config, lockfile, vault, and plugins to a portable JSON file"
2647
+ },
2648
+ args: {
2649
+ output: {
2650
+ type: "positional",
2651
+ description: `Output file path (default: ${DEFAULT_OUTPUT})`,
2652
+ required: false
2653
+ },
2654
+ "no-vault": {
2655
+ type: "boolean",
2656
+ description: "Exclude encrypted vault from export",
2657
+ default: false
2658
+ },
2659
+ "no-plugins": {
2660
+ type: "boolean",
2661
+ description: "Exclude plugin list from export",
2662
+ default: false
2663
+ }
2664
+ },
2665
+ run({ args }) {
2666
+ const outputFile = args.output || DEFAULT_OUTPUT;
2667
+ const outputPath = path8.resolve(outputFile);
2668
+ const bundle = createExportBundle({
2669
+ includeVault: !args["no-vault"],
2670
+ includePlugins: !args["no-plugins"]
2671
+ });
2672
+ const serverCount = Object.keys(bundle.lockfile.servers).length;
2673
+ const configKeys = Object.keys(bundle.config).length;
2674
+ fs9.writeFileSync(outputPath, JSON.stringify(bundle, null, 2), "utf-8");
2675
+ console.log(`${pc9.green("\u2713")} Exported to ${pc9.bold(outputFile)}`);
2676
+ console.log(pc9.dim(` Config keys: ${configKeys}`));
2677
+ console.log(pc9.dim(` Servers: ${serverCount}`));
2678
+ console.log(pc9.dim(` Vault: ${bundle.vault ? "included" : "excluded"}`));
2679
+ console.log(pc9.dim(` Plugins: ${bundle.plugins?.length ?? 0}`));
2680
+ }
2681
+ });
2682
+
2683
+ // src/commands/group.ts
2684
+ import { spawn as spawn3 } from "child_process";
2685
+ import { defineCommand as defineCommand10 } from "citty";
2686
+ import pc10 from "picocolors";
2687
+
2688
+ // src/core/group-manager.ts
2689
+ import fs10 from "fs";
2690
+ function readGroups(file) {
2691
+ const target = file ?? getGroupsFile();
2692
+ if (!fs10.existsSync(target)) return {};
2693
+ try {
2694
+ return JSON.parse(fs10.readFileSync(target, "utf-8"));
2695
+ } catch {
2696
+ return {};
2697
+ }
2698
+ }
2699
+ function writeGroups(store, file) {
2700
+ const target = file ?? getGroupsFile();
2701
+ const dir = target.slice(0, target.lastIndexOf("/"));
2702
+ if (dir && !fs10.existsSync(dir)) {
2703
+ fs10.mkdirSync(dir, { recursive: true });
2704
+ }
2705
+ fs10.writeFileSync(target, JSON.stringify(store, null, 2), "utf-8");
2706
+ }
2707
+ function addToGroup(group, servers, file) {
2708
+ const store = readGroups(file);
2709
+ const existing = new Set(store[group] ?? []);
2710
+ for (const s of servers) existing.add(s);
2711
+ store[group] = [...existing].sort();
2712
+ writeGroups(store, file);
2713
+ }
2714
+ function removeFromGroup(group, servers, file) {
2715
+ const store = readGroups(file);
2716
+ if (!store[group]) return;
2717
+ const toRemove = new Set(servers);
2718
+ store[group] = store[group].filter((s) => !toRemove.has(s));
2719
+ if (store[group].length === 0) delete store[group];
2720
+ writeGroups(store, file);
2721
+ }
2722
+ function getGroup(group, file) {
2723
+ return readGroups(file)[group] ?? [];
2724
+ }
2725
+ function listGroups(file) {
2726
+ return Object.keys(readGroups(file)).sort();
2727
+ }
2728
+ function deleteGroup(group, file) {
2729
+ const store = readGroups(file);
2730
+ if (!(group in store)) return;
2731
+ delete store[group];
2732
+ writeGroups(store, file);
2733
+ }
2734
+ function groupExists(group, file) {
2735
+ return group in readGroups(file);
2736
+ }
2737
+
2738
+ // src/commands/group.ts
2739
+ var addCmd = defineCommand10({
2740
+ meta: { name: "add", description: "Add servers to a group" },
2741
+ args: {
2742
+ name: { type: "positional", description: "Group name", required: true },
2743
+ servers: { type: "positional", description: "Server name(s)", required: true }
2744
+ },
2745
+ run({ args }) {
2746
+ const servers = Array.isArray(args.servers) ? args.servers : [args.servers];
2747
+ addToGroup(args.name, servers);
2748
+ console.log(`${pc10.green("\u2713")} Added ${servers.join(", ")} to group ${pc10.cyan(args.name)}`);
2749
+ }
2750
+ });
2751
+ var rmCmd = defineCommand10({
2752
+ meta: { name: "rm", description: "Remove servers from a group" },
2753
+ args: {
2754
+ name: { type: "positional", description: "Group name", required: true },
2755
+ servers: { type: "positional", description: "Server name(s)", required: true }
2756
+ },
2757
+ run({ args }) {
2758
+ const servers = Array.isArray(args.servers) ? args.servers : [args.servers];
2759
+ removeFromGroup(args.name, servers);
2760
+ console.log(`${pc10.green("\u2713")} Removed ${servers.join(", ")} from group ${pc10.cyan(args.name)}`);
2761
+ }
2762
+ });
2763
+ var listCmd2 = defineCommand10({
2764
+ meta: { name: "list", description: "List all groups (or members of a group)" },
2765
+ args: {
2766
+ name: { type: "positional", description: "Group name (optional)", required: false }
2767
+ },
2768
+ run({ args }) {
2769
+ if (args.name) {
2770
+ const members = getGroup(args.name);
2771
+ if (members.length === 0) {
2772
+ console.log(pc10.dim(`Group "${args.name}" is empty or does not exist.`));
2773
+ return;
2774
+ }
2775
+ console.log(`
2776
+ ${pc10.bold(pc10.cyan(args.name))}`);
2777
+ for (const s of members) console.log(` ${pc10.green("\u25CF")} ${s}`);
2778
+ console.log("");
2779
+ } else {
2780
+ const groups = listGroups();
2781
+ if (groups.length === 0) {
2782
+ console.log(pc10.dim("No groups defined. Use `mcpman group add <name> <server>`."));
2783
+ return;
2784
+ }
2785
+ console.log("");
2786
+ for (const g of groups) {
2787
+ const members = getGroup(g);
2788
+ console.log(
2789
+ ` ${pc10.cyan(pc10.bold(g))} ${pc10.dim(`(${members.length} server${members.length !== 1 ? "s" : ""})`)}`
2790
+ );
2791
+ for (const s of members) console.log(` ${pc10.dim("\xB7")} ${s}`);
2792
+ }
2793
+ console.log("");
2794
+ }
2795
+ }
2796
+ });
2797
+ var deleteCmd = defineCommand10({
2798
+ meta: { name: "delete", description: "Delete an entire group" },
2799
+ args: {
2800
+ name: { type: "positional", description: "Group name", required: true }
2801
+ },
2802
+ run({ args }) {
2803
+ if (!groupExists(args.name)) {
2804
+ console.error(`${pc10.red("\u2717")} Group "${args.name}" does not exist.`);
2805
+ process.exit(1);
2806
+ }
2807
+ deleteGroup(args.name);
2808
+ console.log(`${pc10.green("\u2713")} Deleted group ${pc10.cyan(args.name)}`);
2809
+ }
2810
+ });
2811
+ var installCmd = defineCommand10({
2812
+ meta: { name: "install", description: "Install all servers in a group" },
2813
+ args: {
2814
+ name: { type: "positional", description: "Group name", required: true }
2815
+ },
2816
+ async run({ args }) {
2817
+ const members = getGroup(args.name);
2818
+ if (members.length === 0) {
2819
+ console.error(`${pc10.red("\u2717")} Group "${args.name}" is empty or does not exist.`);
2820
+ process.exit(1);
2821
+ }
2822
+ console.log(
2823
+ `${pc10.cyan("Installing")} group ${pc10.bold(args.name)} (${members.length} servers)...`
2824
+ );
2825
+ for (const server of members) {
2826
+ console.log(`
2827
+ ${pc10.dim("\u2192")} Installing ${pc10.bold(server)}...`);
2828
+ await runInstall(server);
2829
+ }
2830
+ console.log(`
2831
+ ${pc10.green("\u2713")} Group install complete.`);
2832
+ }
2833
+ });
2834
+ var runCmd = defineCommand10({
2835
+ meta: { name: "run", description: "Run all servers in a group concurrently" },
2836
+ args: {
2837
+ name: { type: "positional", description: "Group name", required: true }
2838
+ },
2839
+ run({ args }) {
2840
+ const members = getGroup(args.name);
2841
+ if (members.length === 0) {
2842
+ console.error(`${pc10.red("\u2717")} Group "${args.name}" is empty or does not exist.`);
2843
+ process.exit(1);
2844
+ }
2845
+ const lockfile = readLockfile();
2846
+ console.log(
2847
+ `${pc10.cyan("Spawning")} group ${pc10.bold(args.name)} (${members.length} servers)...
2848
+ `
2849
+ );
2850
+ for (const server of members) {
2851
+ const entry = lockfile.servers[server];
2852
+ if (!entry) {
2853
+ console.warn(` ${pc10.yellow("!")} ${server} not in lockfile \u2014 skipping`);
2854
+ continue;
2855
+ }
2856
+ const child = spawn3(entry.command, entry.args ?? [], {
2857
+ env: process.env,
2858
+ stdio: "inherit",
2859
+ detached: false
2860
+ });
2861
+ child.on("error", (err) => {
2862
+ console.error(` ${pc10.red("\u2717")} ${server}: ${err.message}`);
2863
+ });
2864
+ console.log(` ${pc10.green("\u2713")} Spawned ${pc10.bold(server)} (pid ${child.pid ?? "?"})`);
2865
+ }
2866
+ }
2867
+ });
2868
+ function runInstall(server) {
2869
+ return new Promise((resolve, reject) => {
2870
+ const child = spawn3("mcpman", ["install", server], { stdio: "inherit" });
2871
+ child.on("close", (code) => {
2872
+ if (code === 0) resolve();
2873
+ else reject(new Error(`install exited with code ${code}`));
2874
+ });
2875
+ child.on("error", reject);
2876
+ });
2059
2877
  }
2060
-
2061
- // src/commands/export-command.ts
2062
- var DEFAULT_OUTPUT = "mcpman-export.json";
2063
- var export_command_default = defineCommand6({
2878
+ var group_default = defineCommand10({
2064
2879
  meta: {
2065
- name: "export",
2066
- description: "Export mcpman config, lockfile, vault, and plugins to a portable JSON file"
2067
- },
2068
- args: {
2069
- output: {
2070
- type: "positional",
2071
- description: `Output file path (default: ${DEFAULT_OUTPUT})`,
2072
- required: false
2073
- },
2074
- "no-vault": {
2075
- type: "boolean",
2076
- description: "Exclude encrypted vault from export",
2077
- default: false
2078
- },
2079
- "no-plugins": {
2080
- type: "boolean",
2081
- description: "Exclude plugin list from export",
2082
- default: false
2083
- }
2880
+ name: "group",
2881
+ description: "Manage named server groups"
2084
2882
  },
2085
- run({ args }) {
2086
- const outputFile = args.output || DEFAULT_OUTPUT;
2087
- const outputPath = path7.resolve(outputFile);
2088
- const bundle = createExportBundle({
2089
- includeVault: !args["no-vault"],
2090
- includePlugins: !args["no-plugins"]
2091
- });
2092
- const serverCount = Object.keys(bundle.lockfile.servers).length;
2093
- const configKeys = Object.keys(bundle.config).length;
2094
- fs7.writeFileSync(outputPath, JSON.stringify(bundle, null, 2), "utf-8");
2095
- console.log(`${pc6.green("\u2713")} Exported to ${pc6.bold(outputFile)}`);
2096
- console.log(pc6.dim(` Config keys: ${configKeys}`));
2097
- console.log(pc6.dim(` Servers: ${serverCount}`));
2098
- console.log(pc6.dim(` Vault: ${bundle.vault ? "included" : "excluded"}`));
2099
- console.log(pc6.dim(` Plugins: ${bundle.plugins?.length ?? 0}`));
2883
+ subCommands: {
2884
+ add: addCmd,
2885
+ rm: rmCmd,
2886
+ list: listCmd2,
2887
+ delete: deleteCmd,
2888
+ install: installCmd,
2889
+ run: runCmd
2100
2890
  }
2101
2891
  });
2102
2892
 
2103
2893
  // src/commands/import-command.ts
2104
- import fs8 from "fs";
2105
- import path8 from "path";
2894
+ import fs11 from "fs";
2895
+ import path9 from "path";
2106
2896
  import * as p3 from "@clack/prompts";
2107
- import { defineCommand as defineCommand7 } from "citty";
2108
- import pc7 from "picocolors";
2109
- var import_command_default = defineCommand7({
2897
+ import { defineCommand as defineCommand11 } from "citty";
2898
+ import pc11 from "picocolors";
2899
+ var import_command_default = defineCommand11({
2110
2900
  meta: {
2111
2901
  name: "import",
2112
2902
  description: "Import mcpman config, lockfile, vault, and plugins from an export file"
@@ -2129,21 +2919,21 @@ var import_command_default = defineCommand7({
2129
2919
  }
2130
2920
  },
2131
2921
  async run({ args }) {
2132
- const filePath = path8.resolve(args.file);
2133
- if (!fs8.existsSync(filePath)) {
2134
- console.error(`${pc7.red("\u2717")} File not found: ${filePath}`);
2922
+ const filePath = path9.resolve(args.file);
2923
+ if (!fs11.existsSync(filePath)) {
2924
+ console.error(`${pc11.red("\u2717")} File not found: ${filePath}`);
2135
2925
  process.exit(1);
2136
2926
  }
2137
2927
  let raw;
2138
2928
  try {
2139
- raw = JSON.parse(fs8.readFileSync(filePath, "utf-8"));
2929
+ raw = JSON.parse(fs11.readFileSync(filePath, "utf-8"));
2140
2930
  } catch {
2141
- console.error(`${pc7.red("\u2717")} Invalid JSON in ${filePath}`);
2931
+ console.error(`${pc11.red("\u2717")} Invalid JSON in ${filePath}`);
2142
2932
  process.exit(1);
2143
2933
  }
2144
2934
  const error2 = validateBundle(raw);
2145
2935
  if (error2) {
2146
- console.error(`${pc7.red("\u2717")} Invalid export bundle: ${error2}`);
2936
+ console.error(`${pc11.red("\u2717")} Invalid export bundle: ${error2}`);
2147
2937
  process.exit(1);
2148
2938
  }
2149
2939
  const bundle = raw;
@@ -2153,16 +2943,16 @@ var import_command_default = defineCommand7({
2153
2943
  const hasVault = !!bundle.vault;
2154
2944
  const isDryRun = !!args["dry-run"];
2155
2945
  console.log("");
2156
- console.log(pc7.bold("Import summary:"));
2157
- console.log(pc7.dim(` Source version: mcpman ${bundle.mcpmanVersion}`));
2158
- console.log(pc7.dim(` Exported at: ${bundle.exportedAt}`));
2159
- console.log(` Config keys: ${pc7.cyan(String(configKeys))}`);
2160
- console.log(` Servers: ${pc7.cyan(String(serverCount))}`);
2161
- console.log(` Vault: ${hasVault ? pc7.green("included") : pc7.dim("not included")}`);
2162
- console.log(` Plugins: ${pc7.cyan(String(pluginCount))}`);
2946
+ console.log(pc11.bold("Import summary:"));
2947
+ console.log(pc11.dim(` Source version: mcpman ${bundle.mcpmanVersion}`));
2948
+ console.log(pc11.dim(` Exported at: ${bundle.exportedAt}`));
2949
+ console.log(` Config keys: ${pc11.cyan(String(configKeys))}`);
2950
+ console.log(` Servers: ${pc11.cyan(String(serverCount))}`);
2951
+ console.log(` Vault: ${hasVault ? pc11.green("included") : pc11.dim("not included")}`);
2952
+ console.log(` Plugins: ${pc11.cyan(String(pluginCount))}`);
2163
2953
  console.log("");
2164
2954
  if (isDryRun) {
2165
- console.log(pc7.yellow(" [dry-run] No changes applied."));
2955
+ console.log(pc11.yellow(" [dry-run] No changes applied."));
2166
2956
  return;
2167
2957
  }
2168
2958
  if (!args.yes) {
@@ -2176,18 +2966,18 @@ var import_command_default = defineCommand7({
2176
2966
  }
2177
2967
  }
2178
2968
  const summary = importBundle(bundle, { dryRun: false });
2179
- console.log(`${pc7.green("\u2713")} Import complete`);
2180
- console.log(pc7.dim(` Config keys restored: ${summary.configKeys}`));
2181
- console.log(pc7.dim(` Servers restored: ${summary.servers}`));
2182
- console.log(pc7.dim(` Vault: ${summary.vaultImported ? "restored" : "skipped"}`));
2183
- console.log(pc7.dim(` Plugins installed: ${summary.pluginsInstalled}`));
2969
+ console.log(`${pc11.green("\u2713")} Import complete`);
2970
+ console.log(pc11.dim(` Config keys restored: ${summary.configKeys}`));
2971
+ console.log(pc11.dim(` Servers restored: ${summary.servers}`));
2972
+ console.log(pc11.dim(` Vault: ${summary.vaultImported ? "restored" : "skipped"}`));
2973
+ console.log(pc11.dim(` Plugins installed: ${summary.pluginsInstalled}`));
2184
2974
  }
2185
2975
  });
2186
2976
 
2187
2977
  // src/commands/info.ts
2188
- import { defineCommand as defineCommand8 } from "citty";
2978
+ import { defineCommand as defineCommand12 } from "citty";
2189
2979
  import { createSpinner as createSpinner2 } from "nanospinner";
2190
- import pc8 from "picocolors";
2980
+ import pc12 from "picocolors";
2191
2981
 
2192
2982
  // src/core/package-info.ts
2193
2983
  async function buildInfo(name, entry, source = "npm") {
@@ -2242,11 +3032,11 @@ async function getPackageInfo(serverName) {
2242
3032
  // src/commands/info.ts
2243
3033
  function colorRisk2(score, riskLevel) {
2244
3034
  const label = score !== null ? `${score}/100 (${riskLevel})` : riskLevel;
2245
- if (riskLevel === "LOW") return pc8.green(label);
2246
- if (riskLevel === "MEDIUM") return pc8.yellow(label);
2247
- if (riskLevel === "HIGH") return pc8.red(label);
2248
- if (riskLevel === "CRITICAL") return pc8.bold(pc8.red(label));
2249
- return pc8.dim(label);
3035
+ if (riskLevel === "LOW") return pc12.green(label);
3036
+ if (riskLevel === "MEDIUM") return pc12.yellow(label);
3037
+ if (riskLevel === "HIGH") return pc12.red(label);
3038
+ if (riskLevel === "CRITICAL") return pc12.bold(pc12.red(label));
3039
+ return pc12.dim(label);
2250
3040
  }
2251
3041
  function formatDaysAgo(isoDate) {
2252
3042
  if (!isoDate) return "unknown";
@@ -2256,54 +3046,54 @@ function formatDaysAgo(isoDate) {
2256
3046
  return `${days} days ago`;
2257
3047
  }
2258
3048
  function printInfo(info2) {
2259
- const installedBadge = info2.isInstalled ? pc8.green(" [installed]") : pc8.dim(" [not installed]");
3049
+ const installedBadge = info2.isInstalled ? pc12.green(" [installed]") : pc12.dim(" [not installed]");
2260
3050
  console.log();
2261
- console.log(pc8.bold(` ${info2.name}@${info2.version}`) + installedBadge);
2262
- console.log(pc8.dim(` ${"\u2500".repeat(60)}`));
2263
- console.log(` ${pc8.dim("Source:")} ${info2.source}`);
2264
- console.log(` ${pc8.dim("Runtime:")} ${info2.runtime}`);
3051
+ console.log(pc12.bold(` ${info2.name}@${info2.version}`) + installedBadge);
3052
+ console.log(pc12.dim(` ${"\u2500".repeat(60)}`));
3053
+ console.log(` ${pc12.dim("Source:")} ${info2.source}`);
3054
+ console.log(` ${pc12.dim("Runtime:")} ${info2.runtime}`);
2265
3055
  if (info2.description) {
2266
- console.log(` ${pc8.dim("Description:")} ${info2.description}`);
3056
+ console.log(` ${pc12.dim("Description:")} ${info2.description}`);
2267
3057
  }
2268
3058
  if (info2.deprecated) {
2269
- console.log(` ${pc8.red("[DEPRECATED]")} This package is deprecated`);
3059
+ console.log(` ${pc12.red("[DEPRECATED]")} This package is deprecated`);
2270
3060
  }
2271
3061
  console.log();
2272
- console.log(` ${pc8.bold("Trust & Security")}`);
2273
- console.log(` ${pc8.dim("Trust score:")} ${colorRisk2(info2.trustScore, info2.riskLevel)}`);
3062
+ console.log(` ${pc12.bold("Trust & Security")}`);
3063
+ console.log(` ${pc12.dim("Trust score:")} ${colorRisk2(info2.trustScore, info2.riskLevel)}`);
2274
3064
  if (info2.source === "npm") {
2275
3065
  console.log(
2276
- ` ${pc8.dim("Downloads:")} ${info2.weeklyDownloads.toLocaleString()}/week ${pc8.dim("|")} ${pc8.dim("Age:")} ${info2.packageAge}d ${pc8.dim("|")} ${pc8.dim("Maintainers:")} ${info2.maintainerCount}`
3066
+ ` ${pc12.dim("Downloads:")} ${info2.weeklyDownloads.toLocaleString()}/week ${pc12.dim("|")} ${pc12.dim("Age:")} ${info2.packageAge}d ${pc12.dim("|")} ${pc12.dim("Maintainers:")} ${info2.maintainerCount}`
2277
3067
  );
2278
3068
  if (info2.lastPublish) {
2279
- console.log(` ${pc8.dim("Last publish:")} ${formatDaysAgo(info2.lastPublish)}`);
3069
+ console.log(` ${pc12.dim("Last publish:")} ${formatDaysAgo(info2.lastPublish)}`);
2280
3070
  }
2281
3071
  } else {
2282
- console.log(pc8.dim(" (Trust data available for npm packages only)"));
3072
+ console.log(pc12.dim(" (Trust data available for npm packages only)"));
2283
3073
  }
2284
3074
  console.log();
2285
- console.log(` ${pc8.bold("Environment Variables")}`);
3075
+ console.log(` ${pc12.bold("Environment Variables")}`);
2286
3076
  if (info2.envVars.length > 0) {
2287
3077
  for (const env of info2.envVars) {
2288
- console.log(` ${pc8.cyan("\u2022")} ${env}`);
3078
+ console.log(` ${pc12.cyan("\u2022")} ${env}`);
2289
3079
  }
2290
3080
  } else {
2291
- console.log(pc8.dim(" none required"));
3081
+ console.log(pc12.dim(" none required"));
2292
3082
  }
2293
3083
  console.log();
2294
- console.log(` ${pc8.bold("Installed Clients")}`);
3084
+ console.log(` ${pc12.bold("Installed Clients")}`);
2295
3085
  if (info2.installedClients.length > 0) {
2296
3086
  for (const client of info2.installedClients) {
2297
- console.log(` ${pc8.green("\u2713")} ${client}`);
3087
+ console.log(` ${pc12.green("\u2713")} ${client}`);
2298
3088
  }
2299
3089
  } else {
2300
- console.log(pc8.dim(" Not installed in any client"));
3090
+ console.log(pc12.dim(" Not installed in any client"));
2301
3091
  }
2302
3092
  console.log();
2303
- console.log(pc8.dim(` ${"\u2500".repeat(60)}`));
3093
+ console.log(pc12.dim(` ${"\u2500".repeat(60)}`));
2304
3094
  console.log();
2305
3095
  }
2306
- var info_default = defineCommand8({
3096
+ var info_default = defineCommand12({
2307
3097
  meta: {
2308
3098
  name: "info",
2309
3099
  description: "Show detailed metadata for an MCP server (installed or from registry)"
@@ -2327,13 +3117,13 @@ var info_default = defineCommand8({
2327
3117
  info2 = await getPackageInfo(args.server);
2328
3118
  } catch (err) {
2329
3119
  spinner5.error({ text: "Failed to fetch package info" });
2330
- console.error(pc8.red(String(err)));
3120
+ console.error(pc12.red(String(err)));
2331
3121
  process.exit(1);
2332
3122
  }
2333
3123
  if (!info2) {
2334
3124
  spinner5.error({ text: `Package not found: ${args.server}` });
2335
3125
  console.log(
2336
- pc8.dim(`
3126
+ pc12.dim(`
2337
3127
  "${args.server}" was not found in the npm registry or your lockfile.
2338
3128
  `)
2339
3129
  );
@@ -2349,10 +3139,10 @@ var info_default = defineCommand8({
2349
3139
  });
2350
3140
 
2351
3141
  // src/commands/init.ts
2352
- import path9 from "path";
3142
+ import path10 from "path";
2353
3143
  import * as p4 from "@clack/prompts";
2354
- import { defineCommand as defineCommand9 } from "citty";
2355
- var init_default = defineCommand9({
3144
+ import { defineCommand as defineCommand13 } from "citty";
3145
+ var init_default = defineCommand13({
2356
3146
  meta: {
2357
3147
  name: "init",
2358
3148
  description: "Initialize mcpman.lock in the current project"
@@ -2368,7 +3158,7 @@ var init_default = defineCommand9({
2368
3158
  async run({ args }) {
2369
3159
  const nonInteractive = args.yes || !process.stdout.isTTY;
2370
3160
  p4.intro("mcpman init");
2371
- const targetPath = path9.join(process.cwd(), LOCKFILE_NAME);
3161
+ const targetPath = path10.join(process.cwd(), LOCKFILE_NAME);
2372
3162
  const existing = findLockfile();
2373
3163
  if (existing) {
2374
3164
  if (nonInteractive) {
@@ -2384,7 +3174,7 @@ var init_default = defineCommand9({
2384
3174
  }
2385
3175
  let clients = [];
2386
3176
  try {
2387
- const mod = await import("./client-detector-CY7WPF3K.js");
3177
+ const mod = await import("./client-detector-NHXBDNMY.js");
2388
3178
  clients = await mod.getInstalledClients();
2389
3179
  } catch {
2390
3180
  p4.log.warn("Could not detect AI clients \u2014 creating empty lockfile.");
@@ -2452,7 +3242,7 @@ var init_default = defineCommand9({
2452
3242
 
2453
3243
  // src/commands/install.ts
2454
3244
  import * as p7 from "@clack/prompts";
2455
- import { defineCommand as defineCommand10 } from "citty";
3245
+ import { defineCommand as defineCommand14 } from "citty";
2456
3246
 
2457
3247
  // src/core/installer.ts
2458
3248
  import * as p6 from "@clack/prompts";
@@ -2492,7 +3282,7 @@ async function offerVaultSave(serverName, newVars, yes) {
2492
3282
  // src/core/installer.ts
2493
3283
  async function loadClients2() {
2494
3284
  try {
2495
- const mod = await import("./client-detector-CY7WPF3K.js");
3285
+ const mod = await import("./client-detector-NHXBDNMY.js");
2496
3286
  return mod.getInstalledClients();
2497
3287
  } catch {
2498
3288
  return [];
@@ -2604,7 +3394,7 @@ async function installServer(input, options = {}) {
2604
3394
  }
2605
3395
 
2606
3396
  // src/utils/logger.ts
2607
- import pc9 from "picocolors";
3397
+ import pc13 from "picocolors";
2608
3398
  var noColor = process.env.NO_COLOR !== void 0 || process.argv.includes("--no-color");
2609
3399
  var isVerbose = process.argv.includes("--verbose");
2610
3400
  var isJson = process.argv.includes("--json");
@@ -2613,18 +3403,18 @@ function colorize(fn, text2) {
2613
3403
  }
2614
3404
  function info(message) {
2615
3405
  if (isJson) return;
2616
- console.log(`${colorize(pc9.cyan, "i")} ${message}`);
3406
+ console.log(`${colorize(pc13.cyan, "i")} ${message}`);
2617
3407
  }
2618
3408
  function error(message) {
2619
3409
  if (isJson) return;
2620
- console.error(`${colorize(pc9.red, "\u2717")} ${message}`);
3410
+ console.error(`${colorize(pc13.red, "\u2717")} ${message}`);
2621
3411
  }
2622
3412
  function json(data) {
2623
3413
  console.log(JSON.stringify(data, null, 2));
2624
3414
  }
2625
3415
 
2626
3416
  // src/commands/install.ts
2627
- var install_default = defineCommand10({
3417
+ var install_default = defineCommand14({
2628
3418
  meta: {
2629
3419
  name: "install",
2630
3420
  description: "Install an MCP server into one or more AI clients"
@@ -2686,23 +3476,23 @@ async function restoreFromLockfile() {
2686
3476
  }
2687
3477
 
2688
3478
  // src/commands/link.ts
2689
- import path11 from "path";
2690
- import { defineCommand as defineCommand11 } from "citty";
2691
- import pc10 from "picocolors";
3479
+ import path12 from "path";
3480
+ import { defineCommand as defineCommand15 } from "citty";
3481
+ import pc14 from "picocolors";
2692
3482
 
2693
3483
  // src/core/link-service.ts
2694
- import fs9 from "fs";
2695
- import path10 from "path";
3484
+ import fs12 from "fs";
3485
+ import path11 from "path";
2696
3486
  function detectLocalServer(dir) {
2697
- if (!fs9.existsSync(dir)) {
3487
+ if (!fs12.existsSync(dir)) {
2698
3488
  throw new Error(`Directory does not exist: ${dir}`);
2699
3489
  }
2700
- const pkgPath = path10.join(dir, "package.json");
2701
- if (fs9.existsSync(pkgPath)) {
3490
+ const pkgPath = path11.join(dir, "package.json");
3491
+ if (fs12.existsSync(pkgPath)) {
2702
3492
  return detectNodeServer(dir, pkgPath);
2703
3493
  }
2704
- const pyprojectPath = path10.join(dir, "pyproject.toml");
2705
- if (fs9.existsSync(pyprojectPath)) {
3494
+ const pyprojectPath = path11.join(dir, "pyproject.toml");
3495
+ if (fs12.existsSync(pyprojectPath)) {
2706
3496
  return detectPythonServer(dir, pyprojectPath);
2707
3497
  }
2708
3498
  throw new Error(
@@ -2710,28 +3500,28 @@ function detectLocalServer(dir) {
2710
3500
  );
2711
3501
  }
2712
3502
  function detectNodeServer(dir, pkgPath) {
2713
- const raw = fs9.readFileSync(pkgPath, "utf-8");
3503
+ const raw = fs12.readFileSync(pkgPath, "utf-8");
2714
3504
  const pkg = JSON.parse(raw);
2715
- const name = String(pkg.name ?? path10.basename(dir));
3505
+ const name = String(pkg.name ?? path11.basename(dir));
2716
3506
  const version = String(pkg.version ?? "0.0.0");
2717
3507
  let entryPoint = null;
2718
3508
  if (pkg.bin) {
2719
3509
  if (typeof pkg.bin === "string") {
2720
- entryPoint = path10.resolve(dir, pkg.bin);
3510
+ entryPoint = path11.resolve(dir, pkg.bin);
2721
3511
  } else if (typeof pkg.bin === "object" && pkg.bin !== null) {
2722
3512
  const binObj = pkg.bin;
2723
3513
  const firstBin = Object.values(binObj)[0];
2724
- if (firstBin) entryPoint = path10.resolve(dir, firstBin);
3514
+ if (firstBin) entryPoint = path11.resolve(dir, firstBin);
2725
3515
  }
2726
3516
  }
2727
3517
  if (!entryPoint && pkg.main) {
2728
- entryPoint = path10.resolve(dir, String(pkg.main));
3518
+ entryPoint = path11.resolve(dir, String(pkg.main));
2729
3519
  }
2730
3520
  if (!entryPoint) {
2731
3521
  const candidates = ["src/index.ts", "src/index.js", "index.ts", "index.js"];
2732
3522
  for (const c of candidates) {
2733
- const candidate = path10.join(dir, c);
2734
- if (fs9.existsSync(candidate)) {
3523
+ const candidate = path11.join(dir, c);
3524
+ if (fs12.existsSync(candidate)) {
2735
3525
  entryPoint = candidate;
2736
3526
  break;
2737
3527
  }
@@ -2756,20 +3546,20 @@ function detectNodeServer(dir, pkgPath) {
2756
3546
  return { name, version, command, args, envVars, absolutePath: dir, runtime: "node" };
2757
3547
  }
2758
3548
  function detectPythonServer(dir, pyprojectPath) {
2759
- const raw = fs9.readFileSync(pyprojectPath, "utf-8");
2760
- const name = extractTomlValue(raw, "name") ?? path10.basename(dir);
3549
+ const raw = fs12.readFileSync(pyprojectPath, "utf-8");
3550
+ const name = extractTomlValue(raw, "name") ?? path11.basename(dir);
2761
3551
  const version = extractTomlValue(raw, "version") ?? "0.0.0";
2762
3552
  let pythonCmd = "python3";
2763
- const venvPython = path10.join(dir, ".venv", "bin", "python");
2764
- if (fs9.existsSync(venvPython)) {
3553
+ const venvPython = path11.join(dir, ".venv", "bin", "python");
3554
+ if (fs12.existsSync(venvPython)) {
2765
3555
  pythonCmd = venvPython;
2766
3556
  }
2767
3557
  const entryCandidate = [
2768
- path10.join(dir, "main.py"),
2769
- path10.join(dir, name.replace(/-/g, "_"), "main.py"),
2770
- path10.join(dir, "__main__.py")
2771
- ].find((p12) => fs9.existsSync(p12));
2772
- const entryPoint = entryCandidate ?? path10.join(dir, "main.py");
3558
+ path11.join(dir, "main.py"),
3559
+ path11.join(dir, name.replace(/-/g, "_"), "main.py"),
3560
+ path11.join(dir, "__main__.py")
3561
+ ].find((p13) => fs12.existsSync(p13));
3562
+ const entryPoint = entryCandidate ?? path11.join(dir, "main.py");
2773
3563
  return {
2774
3564
  name,
2775
3565
  version,
@@ -2811,13 +3601,13 @@ async function registerLinkedServer(linkResult, clients, lockfilePath, nameOverr
2811
3601
  };
2812
3602
  const existing = readLockfile(lockfilePath);
2813
3603
  existing.servers[serverName] = lockEntry;
2814
- const { writeLockfile: writeLockfile2 } = await import("./lockfile-RBA7HB24.js");
3604
+ const { writeLockfile: writeLockfile2 } = await import("./lockfile-5XXA26FM.js");
2815
3605
  writeLockfile2(existing, lockfilePath);
2816
3606
  return registered;
2817
3607
  }
2818
3608
 
2819
3609
  // src/commands/link.ts
2820
- var link_default = defineCommand11({
3610
+ var link_default = defineCommand15({
2821
3611
  meta: {
2822
3612
  name: "link",
2823
3613
  description: "Register a local MCP server directory with AI clients"
@@ -2843,42 +3633,42 @@ var link_default = defineCommand11({
2843
3633
  const dirArg = args.dir ?? ".";
2844
3634
  const clientFilter = args.client;
2845
3635
  const nameOverride = args.name;
2846
- const absoluteDir = path11.resolve(dirArg);
3636
+ const absoluteDir = path12.resolve(dirArg);
2847
3637
  let linkResult;
2848
3638
  try {
2849
3639
  linkResult = detectLocalServer(absoluteDir);
2850
3640
  } catch (err) {
2851
- console.error(pc10.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3641
+ console.error(pc14.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
2852
3642
  process.exit(1);
2853
3643
  }
2854
3644
  const serverName = nameOverride ?? linkResult.name;
2855
- console.log(pc10.dim(`
2856
- Detected: ${pc10.cyan(serverName)} (${linkResult.runtime})`));
2857
- console.log(pc10.dim(` Path: ${absoluteDir}`));
2858
- console.log(pc10.dim(` Command: ${linkResult.command} ${linkResult.args.join(" ")}`));
3645
+ console.log(pc14.dim(`
3646
+ Detected: ${pc14.cyan(serverName)} (${linkResult.runtime})`));
3647
+ console.log(pc14.dim(` Path: ${absoluteDir}`));
3648
+ console.log(pc14.dim(` Command: ${linkResult.command} ${linkResult.args.join(" ")}`));
2859
3649
  const allClients = await getInstalledClients();
2860
3650
  const clients = clientFilter ? allClients.filter((c) => c.type === clientFilter) : allClients;
2861
3651
  if (clientFilter && clients.length === 0) {
2862
- console.error(pc10.red(` Error: Unknown client '${clientFilter}'.`));
3652
+ console.error(pc14.red(` Error: Unknown client '${clientFilter}'.`));
2863
3653
  process.exit(1);
2864
3654
  }
2865
3655
  let registered;
2866
3656
  try {
2867
3657
  registered = await registerLinkedServer(linkResult, clients, void 0, nameOverride);
2868
3658
  } catch (err) {
2869
- console.error(pc10.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3659
+ console.error(pc14.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
2870
3660
  process.exit(1);
2871
3661
  }
2872
3662
  if (registered.length === 0) {
2873
- console.log(pc10.yellow(" Warning: No clients registered. Are any AI clients installed?"));
2874
- console.log(pc10.dim(` Server saved to lockfile with source "local".`));
3663
+ console.log(pc14.yellow(" Warning: No clients registered. Are any AI clients installed?"));
3664
+ console.log(pc14.dim(` Server saved to lockfile with source "local".`));
2875
3665
  } else {
2876
- console.log(pc10.green(`
2877
- Linked ${pc10.bold(serverName)} to: ${registered.join(", ")}
3666
+ console.log(pc14.green(`
3667
+ Linked ${pc14.bold(serverName)} to: ${registered.join(", ")}
2878
3668
  `));
2879
- console.log(pc10.dim(` Run ${pc10.cyan("mcpman list")} to verify.`));
3669
+ console.log(pc14.dim(` Run ${pc14.cyan("mcpman list")} to verify.`));
2880
3670
  console.log(
2881
- pc10.dim(` Run ${pc10.cyan(`mcpman watch ${serverName}`)} to start with auto-restart.`)
3671
+ pc14.dim(` Run ${pc14.cyan(`mcpman watch ${serverName}`)} to start with auto-restart.`)
2882
3672
  );
2883
3673
  }
2884
3674
  console.log();
@@ -2886,14 +3676,14 @@ var link_default = defineCommand11({
2886
3676
  });
2887
3677
 
2888
3678
  // src/commands/list.ts
2889
- import { defineCommand as defineCommand12 } from "citty";
2890
- import pc11 from "picocolors";
3679
+ import { defineCommand as defineCommand16 } from "citty";
3680
+ import pc15 from "picocolors";
2891
3681
  var STATUS_ICON = {
2892
- healthy: pc11.green("\u25CF"),
2893
- unhealthy: pc11.red("\u25CF"),
2894
- unknown: pc11.dim("\u25CB")
3682
+ healthy: pc15.green("\u25CF"),
3683
+ unhealthy: pc15.red("\u25CF"),
3684
+ unknown: pc15.dim("\u25CB")
2895
3685
  };
2896
- var list_default = defineCommand12({
3686
+ var list_default = defineCommand16({
2897
3687
  meta: {
2898
3688
  name: "list",
2899
3689
  description: "List installed MCP servers"
@@ -2914,8 +3704,8 @@ var list_default = defineCommand12({
2914
3704
  if (servers.length === 0) {
2915
3705
  const filter = args.client ? ` for client "${args.client}"` : "";
2916
3706
  console.log(
2917
- pc11.dim(
2918
- `No MCP servers installed${filter}. Run ${pc11.cyan("mcpman install <server>")} to get started.`
3707
+ pc15.dim(
3708
+ `No MCP servers installed${filter}. Run ${pc15.cyan("mcpman install <server>")} to get started.`
2919
3709
  )
2920
3710
  );
2921
3711
  return;
@@ -2941,9 +3731,9 @@ var list_default = defineCommand12({
2941
3731
  const nameWidth = Math.max(4, ...withStatus.map((s) => s.name.length));
2942
3732
  const clientsWidth = Math.max(7, ...withStatus.map((s) => formatClients(s.clients).length));
2943
3733
  const header = ` ${pad("NAME", nameWidth)} ${pad("CLIENT(S)", clientsWidth)} ${pad("COMMAND", 20)} STATUS`;
2944
- console.log(pc11.dim(header));
3734
+ console.log(pc15.dim(header));
2945
3735
  console.log(
2946
- pc11.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientsWidth)} ${"-".repeat(20)} ------`)
3736
+ pc15.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientsWidth)} ${"-".repeat(20)} ------`)
2947
3737
  );
2948
3738
  for (const s of withStatus) {
2949
3739
  const icon = STATUS_ICON[s.status];
@@ -2958,7 +3748,7 @@ var list_default = defineCommand12({
2958
3748
  }
2959
3749
  const clientSet = new Set(withStatus.flatMap((s) => s.clients));
2960
3750
  console.log(
2961
- pc11.dim(
3751
+ pc15.dim(
2962
3752
  `
2963
3753
  ${withStatus.length} server${withStatus.length !== 1 ? "s" : ""} \xB7 ${clientSet.size} client${clientSet.size !== 1 ? "s" : ""}`
2964
3754
  )
@@ -2971,21 +3761,21 @@ function pad(s, width) {
2971
3761
  function truncate(s, max) {
2972
3762
  return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
2973
3763
  }
2974
- var CLIENT_DISPLAY = {
3764
+ var CLIENT_DISPLAY2 = {
2975
3765
  "claude-desktop": "Claude",
2976
3766
  cursor: "Cursor",
2977
3767
  vscode: "VS Code",
2978
3768
  windsurf: "Windsurf"
2979
3769
  };
2980
3770
  function formatClients(clients) {
2981
- return clients.map((c) => CLIENT_DISPLAY[c] ?? c).join(", ");
3771
+ return clients.map((c) => CLIENT_DISPLAY2[c] ?? c).join(", ");
2982
3772
  }
2983
3773
 
2984
3774
  // src/commands/logs.ts
2985
- import { spawn as spawn2 } from "child_process";
2986
- import { defineCommand as defineCommand13 } from "citty";
2987
- import pc12 from "picocolors";
2988
- var logs_default = defineCommand13({
3775
+ import { spawn as spawn4 } from "child_process";
3776
+ import { defineCommand as defineCommand17 } from "citty";
3777
+ import pc16 from "picocolors";
3778
+ var logs_default = defineCommand17({
2989
3779
  meta: {
2990
3780
  name: "logs",
2991
3781
  description: "Stream stdout/stderr from an MCP server"
@@ -3008,7 +3798,7 @@ var logs_default = defineCommand13({
3008
3798
  const lockfile = readLockfile();
3009
3799
  const entry = lockfile.servers[serverName];
3010
3800
  if (!entry) {
3011
- console.error(pc12.red(` Error: Server '${serverName}' is not installed.`));
3801
+ console.error(pc16.red(` Error: Server '${serverName}' is not installed.`));
3012
3802
  process.exit(1);
3013
3803
  }
3014
3804
  const lockfileEnv = parseEnvFlags(entry.envVars);
@@ -3018,24 +3808,24 @@ var logs_default = defineCommand13({
3018
3808
  ...lockfileEnv,
3019
3809
  ...vaultEnv
3020
3810
  };
3021
- console.log(pc12.dim(` Streaming logs for ${pc12.cyan(serverName)}... (Ctrl+C to stop)
3811
+ console.log(pc16.dim(` Streaming logs for ${pc16.cyan(serverName)}... (Ctrl+C to stop)
3022
3812
  `));
3023
- const child = spawn2(entry.command, entry.args, {
3813
+ const child = spawn4(entry.command, entry.args, {
3024
3814
  env: finalEnv,
3025
3815
  stdio: ["pipe", "pipe", "pipe"]
3026
3816
  });
3027
3817
  child.stdout?.on("data", (chunk) => {
3028
- process.stdout.write(pc12.dim("[stdout] ") + chunk.toString());
3818
+ process.stdout.write(pc16.dim("[stdout] ") + chunk.toString());
3029
3819
  });
3030
3820
  child.stderr?.on("data", (chunk) => {
3031
- process.stderr.write(pc12.yellow("[stderr] ") + chunk.toString());
3821
+ process.stderr.write(pc16.yellow("[stderr] ") + chunk.toString());
3032
3822
  });
3033
3823
  child.on("error", (err) => {
3034
- console.error(pc12.red(` Failed to start '${serverName}': ${err.message}`));
3824
+ console.error(pc16.red(` Failed to start '${serverName}': ${err.message}`));
3035
3825
  process.exit(1);
3036
3826
  });
3037
3827
  child.on("close", (code) => {
3038
- console.log(pc12.dim(`
3828
+ console.log(pc16.dim(`
3039
3829
  Process exited with code ${code ?? 0}`));
3040
3830
  process.exit(code ?? 0);
3041
3831
  });
@@ -3059,11 +3849,98 @@ async function loadVaultSecrets(serverName) {
3059
3849
  }
3060
3850
  }
3061
3851
 
3852
+ // src/commands/pin.ts
3853
+ import { defineCommand as defineCommand18 } from "citty";
3854
+ import pc17 from "picocolors";
3855
+ var pin_default = defineCommand18({
3856
+ meta: {
3857
+ name: "pin",
3858
+ description: "Pin a server to a specific version"
3859
+ },
3860
+ args: {
3861
+ server: {
3862
+ type: "positional",
3863
+ description: "Server name to pin/unpin",
3864
+ required: false
3865
+ },
3866
+ version: {
3867
+ type: "positional",
3868
+ description: "Version to pin to (defaults to currently installed version)",
3869
+ required: false
3870
+ },
3871
+ unpin: {
3872
+ type: "boolean",
3873
+ description: "Remove the pin for a server",
3874
+ default: false
3875
+ },
3876
+ list: {
3877
+ type: "boolean",
3878
+ description: "List all pinned servers",
3879
+ default: false
3880
+ }
3881
+ },
3882
+ run({ args }) {
3883
+ if (args.list) {
3884
+ const pins = listPins();
3885
+ if (pins.length === 0) {
3886
+ console.log(pc17.dim("No servers are pinned."));
3887
+ return;
3888
+ }
3889
+ console.log(`
3890
+ ${pc17.bold("Pinned servers")}
3891
+ `);
3892
+ for (const { server, version: version2 } of pins) {
3893
+ console.log(` ${pc17.cyan(pc17.bold(server))} ${pc17.dim("@")}${pc17.green(version2)}`);
3894
+ }
3895
+ console.log("");
3896
+ return;
3897
+ }
3898
+ if (args.unpin) {
3899
+ if (!args.server) {
3900
+ console.error(`${pc17.red("\u2717")} Specify a server name to unpin.`);
3901
+ process.exit(1);
3902
+ }
3903
+ if (!isPinned(args.server)) {
3904
+ console.log(pc17.dim(`"${args.server}" is not pinned.`));
3905
+ return;
3906
+ }
3907
+ unpinServer(args.server);
3908
+ console.log(`${pc17.green("\u2713")} Unpinned ${pc17.cyan(args.server)}`);
3909
+ return;
3910
+ }
3911
+ if (!args.server) {
3912
+ console.error(`${pc17.red("\u2717")} Specify a server name to pin. Use --list to see pins.`);
3913
+ process.exit(1);
3914
+ }
3915
+ let version = args.version;
3916
+ if (!version) {
3917
+ const lockfile = readLockfile();
3918
+ version = lockfile.servers[args.server]?.version;
3919
+ if (!version) {
3920
+ console.error(
3921
+ `${pc17.red("\u2717")} "${args.server}" not found in lockfile. Specify a version explicitly.`
3922
+ );
3923
+ process.exit(1);
3924
+ }
3925
+ }
3926
+ pinServer(args.server, version);
3927
+ const prev = getPinnedVersion(args.server);
3928
+ if (prev && prev !== version) {
3929
+ console.log(
3930
+ `${pc17.green("\u2713")} Re-pinned ${pc17.cyan(args.server)} ${pc17.dim(prev)} \u2192 ${pc17.green(version)}`
3931
+ );
3932
+ } else {
3933
+ console.log(`${pc17.green("\u2713")} Pinned ${pc17.cyan(args.server)} @ ${pc17.green(version)}`);
3934
+ }
3935
+ console.log(pc17.dim(" Update notifications will be suppressed for this server."));
3936
+ }
3937
+ });
3938
+
3062
3939
  // src/commands/plugin.ts
3063
- import { defineCommand as defineCommand14 } from "citty";
3940
+ import { defineCommand as defineCommand19 } from "citty";
3064
3941
  import { createSpinner as createSpinner3 } from "nanospinner";
3065
- import pc13 from "picocolors";
3066
- var addCommand = defineCommand14({
3942
+ import pc18 from "picocolors";
3943
+ var addCommand = defineCommand19({
3067
3944
  meta: { name: "add", description: "Install a plugin package" },
3068
3945
  args: {
3069
3946
  package: {
@@ -3081,23 +3958,23 @@ var addCommand = defineCommand14({
3081
3958
  spinner5.stop();
3082
3959
  if (loaded) {
3083
3960
  console.log(
3084
- `${pc13.green("\u2713")} Plugin ${pc13.bold(loaded.name)} installed (prefix: ${pc13.cyan(loaded.prefix)})`
3961
+ `${pc18.green("\u2713")} Plugin ${pc18.bold(loaded.name)} installed (prefix: ${pc18.cyan(loaded.prefix)})`
3085
3962
  );
3086
3963
  } else {
3087
3964
  console.log(
3088
- `${pc13.yellow("\u26A0")} Package ${pc13.bold(pkg)} installed but does not export a valid mcpman plugin.`
3965
+ `${pc18.yellow("\u26A0")} Package ${pc18.bold(pkg)} installed but does not export a valid mcpman plugin.`
3089
3966
  );
3090
3967
  }
3091
3968
  } catch (err) {
3092
3969
  spinner5.stop();
3093
3970
  console.error(
3094
- `${pc13.red("\u2717")} Failed to install plugin: ${err instanceof Error ? err.message : String(err)}`
3971
+ `${pc18.red("\u2717")} Failed to install plugin: ${err instanceof Error ? err.message : String(err)}`
3095
3972
  );
3096
3973
  process.exit(1);
3097
3974
  }
3098
3975
  }
3099
3976
  });
3100
- var removeCommand = defineCommand14({
3977
+ var removeCommand = defineCommand19({
3101
3978
  meta: { name: "remove", description: "Uninstall a plugin package" },
3102
3979
  args: {
3103
3980
  package: {
@@ -3110,46 +3987,46 @@ var removeCommand = defineCommand14({
3110
3987
  const pkg = args.package;
3111
3988
  const installed = listPluginPackages();
3112
3989
  if (!installed.includes(pkg)) {
3113
- console.log(pc13.dim(`Plugin "${pkg}" is not installed.`));
3990
+ console.log(pc18.dim(`Plugin "${pkg}" is not installed.`));
3114
3991
  return;
3115
3992
  }
3116
3993
  try {
3117
3994
  removePluginPackage(pkg);
3118
- console.log(`${pc13.green("\u2713")} Plugin ${pc13.bold(pkg)} removed.`);
3995
+ console.log(`${pc18.green("\u2713")} Plugin ${pc18.bold(pkg)} removed.`);
3119
3996
  } catch (err) {
3120
3997
  console.error(
3121
- `${pc13.red("\u2717")} Failed to remove plugin: ${err instanceof Error ? err.message : String(err)}`
3998
+ `${pc18.red("\u2717")} Failed to remove plugin: ${err instanceof Error ? err.message : String(err)}`
3122
3999
  );
3123
4000
  process.exit(1);
3124
4001
  }
3125
4002
  }
3126
4003
  });
3127
- var listCommand2 = defineCommand14({
4004
+ var listCommand2 = defineCommand19({
3128
4005
  meta: { name: "list", description: "List installed plugins" },
3129
4006
  run() {
3130
4007
  const packages = listPluginPackages();
3131
4008
  if (packages.length === 0) {
3132
- console.log(pc13.dim("No plugins installed. Use `mcpman plugin add <package>`."));
4009
+ console.log(pc18.dim("No plugins installed. Use `mcpman plugin add <package>`."));
3133
4010
  return;
3134
4011
  }
3135
4012
  console.log("");
3136
- console.log(pc13.bold("Installed plugins:"));
4013
+ console.log(pc18.bold("Installed plugins:"));
3137
4014
  console.log("");
3138
4015
  for (const pkg of packages) {
3139
4016
  const loaded = loadPlugin(pkg);
3140
4017
  if (loaded) {
3141
4018
  console.log(
3142
- ` ${pc13.green("\u25CF")} ${pc13.bold(loaded.name)} prefix: ${pc13.cyan(loaded.prefix)} pkg: ${pc13.dim(pkg)}`
4019
+ ` ${pc18.green("\u25CF")} ${pc18.bold(loaded.name)} prefix: ${pc18.cyan(loaded.prefix)} pkg: ${pc18.dim(pkg)}`
3143
4020
  );
3144
4021
  } else {
3145
- console.log(` ${pc13.yellow("\u25CF")} ${pc13.dim(pkg)} ${pc13.yellow("(failed to load)")}`);
4022
+ console.log(` ${pc18.yellow("\u25CF")} ${pc18.dim(pkg)} ${pc18.yellow("(failed to load)")}`);
3146
4023
  }
3147
4024
  }
3148
4025
  console.log("");
3149
- console.log(pc13.dim(` ${packages.length} plugin${packages.length !== 1 ? "s" : ""} installed`));
4026
+ console.log(pc18.dim(` ${packages.length} plugin${packages.length !== 1 ? "s" : ""} installed`));
3150
4027
  }
3151
4028
  });
3152
- var plugin_default = defineCommand14({
4029
+ var plugin_default = defineCommand19({
3153
4030
  meta: {
3154
4031
  name: "plugin",
3155
4032
  description: "Manage mcpman plugins for custom registries"
@@ -3162,22 +4039,22 @@ var plugin_default = defineCommand14({
3162
4039
  });
3163
4040
 
3164
4041
  // src/commands/profiles.ts
3165
- import { defineCommand as defineCommand15 } from "citty";
3166
- import pc14 from "picocolors";
4042
+ import { defineCommand as defineCommand20 } from "citty";
4043
+ import pc19 from "picocolors";
3167
4044
 
3168
4045
  // src/core/profile-service.ts
3169
- import fs10 from "fs";
3170
- import path12 from "path";
4046
+ import fs13 from "fs";
4047
+ import path13 from "path";
3171
4048
  function ensureDir(dir = getProfilesDir()) {
3172
- fs10.mkdirSync(dir, { recursive: true });
4049
+ fs13.mkdirSync(dir, { recursive: true });
3173
4050
  }
3174
4051
  function profilePath(name, dir = getProfilesDir()) {
3175
- return path12.join(dir, `${name}.json`);
4052
+ return path13.join(dir, `${name}.json`);
3176
4053
  }
3177
4054
  function createProfile(name, description = "", dir = getProfilesDir()) {
3178
4055
  ensureDir(dir);
3179
4056
  const filePath = profilePath(name, dir);
3180
- if (fs10.existsSync(filePath)) {
4057
+ if (fs13.existsSync(filePath)) {
3181
4058
  throw new Error(`Profile '${name}' already exists. Delete it first or use a different name.`);
3182
4059
  }
3183
4060
  const lockfile = readLockfile();
@@ -3187,16 +4064,16 @@ function createProfile(name, description = "", dir = getProfilesDir()) {
3187
4064
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3188
4065
  servers: lockfile.servers
3189
4066
  };
3190
- fs10.writeFileSync(filePath, JSON.stringify(profile, null, 2), "utf-8");
4067
+ fs13.writeFileSync(filePath, JSON.stringify(profile, null, 2), "utf-8");
3191
4068
  return profile;
3192
4069
  }
3193
4070
  function listProfiles(dir = getProfilesDir()) {
3194
4071
  ensureDir(dir);
3195
- const files = fs10.readdirSync(dir).filter((f) => f.endsWith(".json"));
4072
+ const files = fs13.readdirSync(dir).filter((f) => f.endsWith(".json"));
3196
4073
  const profiles = [];
3197
4074
  for (const file of files) {
3198
4075
  try {
3199
- const raw = fs10.readFileSync(path12.join(dir, file), "utf-8");
4076
+ const raw = fs13.readFileSync(path13.join(dir, file), "utf-8");
3200
4077
  const data = JSON.parse(raw);
3201
4078
  profiles.push(data);
3202
4079
  } catch {
@@ -3206,9 +4083,9 @@ function listProfiles(dir = getProfilesDir()) {
3206
4083
  }
3207
4084
  function loadProfile(name, dir = getProfilesDir()) {
3208
4085
  const filePath = profilePath(name, dir);
3209
- if (!fs10.existsSync(filePath)) return null;
4086
+ if (!fs13.existsSync(filePath)) return null;
3210
4087
  try {
3211
- const raw = fs10.readFileSync(filePath, "utf-8");
4088
+ const raw = fs13.readFileSync(filePath, "utf-8");
3212
4089
  return JSON.parse(raw);
3213
4090
  } catch {
3214
4091
  return null;
@@ -3216,13 +4093,13 @@ function loadProfile(name, dir = getProfilesDir()) {
3216
4093
  }
3217
4094
  function deleteProfile(name, dir = getProfilesDir()) {
3218
4095
  const filePath = profilePath(name, dir);
3219
- if (!fs10.existsSync(filePath)) return false;
3220
- fs10.unlinkSync(filePath);
4096
+ if (!fs13.existsSync(filePath)) return false;
4097
+ fs13.unlinkSync(filePath);
3221
4098
  return true;
3222
4099
  }
3223
4100
 
3224
4101
  // src/commands/profiles.ts
3225
- var profiles_default = defineCommand15({
4102
+ var profiles_default = defineCommand20({
3226
4103
  meta: {
3227
4104
  name: "profiles",
3228
4105
  description: "Manage named server configuration profiles"
@@ -3251,16 +4128,16 @@ var profiles_default = defineCommand15({
3251
4128
  case "create": {
3252
4129
  if (!name) {
3253
4130
  console.error(
3254
- pc14.red(" Error: Profile name required. Usage: mcpman profiles create <name>")
4131
+ pc19.red(" Error: Profile name required. Usage: mcpman profiles create <name>")
3255
4132
  );
3256
4133
  process.exit(1);
3257
4134
  }
3258
4135
  try {
3259
4136
  const profile = createProfile(name, args.description ?? "");
3260
4137
  const count = Object.keys(profile.servers).length;
3261
- console.log(pc14.green(` \u2713 Profile '${name}' created with ${count} server(s).`));
4138
+ console.log(pc19.green(` \u2713 Profile '${name}' created with ${count} server(s).`));
3262
4139
  } catch (err) {
3263
- console.error(pc14.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
4140
+ console.error(pc19.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3264
4141
  process.exit(1);
3265
4142
  }
3266
4143
  break;
@@ -3268,38 +4145,38 @@ var profiles_default = defineCommand15({
3268
4145
  case "switch": {
3269
4146
  if (!name) {
3270
4147
  console.error(
3271
- pc14.red(" Error: Profile name required. Usage: mcpman profiles switch <name>")
4148
+ pc19.red(" Error: Profile name required. Usage: mcpman profiles switch <name>")
3272
4149
  );
3273
4150
  process.exit(1);
3274
4151
  }
3275
4152
  const profile = loadProfile(name);
3276
4153
  if (!profile) {
3277
- console.error(pc14.red(` Error: Profile '${name}' not found.`));
4154
+ console.error(pc19.red(` Error: Profile '${name}' not found.`));
3278
4155
  process.exit(1);
3279
4156
  }
3280
4157
  const lockData = { lockfileVersion: 1, servers: profile.servers };
3281
4158
  writeLockfile(lockData);
3282
4159
  const count = Object.keys(profile.servers).length;
3283
- console.log(pc14.green(` \u2713 Switched to profile '${name}' (${count} servers).`));
3284
- console.log(pc14.dim(" Run mcpman sync to apply to all clients."));
4160
+ console.log(pc19.green(` \u2713 Switched to profile '${name}' (${count} servers).`));
4161
+ console.log(pc19.dim(" Run mcpman sync to apply to all clients."));
3285
4162
  break;
3286
4163
  }
3287
4164
  case "list": {
3288
4165
  const profiles = listProfiles();
3289
4166
  if (profiles.length === 0) {
3290
4167
  console.log(
3291
- pc14.dim(" No profiles saved. Create one with: mcpman profiles create <name>")
4168
+ pc19.dim(" No profiles saved. Create one with: mcpman profiles create <name>")
3292
4169
  );
3293
4170
  return;
3294
4171
  }
3295
- console.log(pc14.bold(`
4172
+ console.log(pc19.bold(`
3296
4173
  Profiles (${profiles.length})
3297
4174
  `));
3298
- for (const p12 of profiles) {
3299
- const count = Object.keys(p12.servers).length;
3300
- const desc = p12.description ? pc14.dim(` \u2014 ${p12.description}`) : "";
4175
+ for (const p13 of profiles) {
4176
+ const count = Object.keys(p13.servers).length;
4177
+ const desc = p13.description ? pc19.dim(` \u2014 ${p13.description}`) : "";
3301
4178
  console.log(
3302
- ` ${pc14.cyan("\u25CF")} ${pc14.bold(p12.name)} ${pc14.dim(`${count} server(s)`)}${desc}`
4179
+ ` ${pc19.cyan("\u25CF")} ${pc19.bold(p13.name)} ${pc19.dim(`${count} server(s)`)}${desc}`
3303
4180
  );
3304
4181
  }
3305
4182
  console.log();
@@ -3308,22 +4185,22 @@ var profiles_default = defineCommand15({
3308
4185
  case "delete": {
3309
4186
  if (!name) {
3310
4187
  console.error(
3311
- pc14.red(" Error: Profile name required. Usage: mcpman profiles delete <name>")
4188
+ pc19.red(" Error: Profile name required. Usage: mcpman profiles delete <name>")
3312
4189
  );
3313
4190
  process.exit(1);
3314
4191
  }
3315
4192
  const deleted = deleteProfile(name);
3316
4193
  if (deleted) {
3317
- console.log(pc14.green(` \u2713 Profile '${name}' deleted.`));
4194
+ console.log(pc19.green(` \u2713 Profile '${name}' deleted.`));
3318
4195
  } else {
3319
- console.error(pc14.red(` Error: Profile '${name}' not found.`));
4196
+ console.error(pc19.red(` Error: Profile '${name}' not found.`));
3320
4197
  process.exit(1);
3321
4198
  }
3322
4199
  break;
3323
4200
  }
3324
4201
  default:
3325
4202
  console.error(
3326
- pc14.red(` Unknown action '${action}'. Use: create, switch, list, or delete.`)
4203
+ pc19.red(` Unknown action '${action}'. Use: create, switch, list, or delete.`)
3327
4204
  );
3328
4205
  process.exit(1);
3329
4206
  }
@@ -3331,8 +4208,8 @@ var profiles_default = defineCommand15({
3331
4208
  });
3332
4209
 
3333
4210
  // src/commands/registry.ts
3334
- import { defineCommand as defineCommand16 } from "citty";
3335
- import pc15 from "picocolors";
4211
+ import { defineCommand as defineCommand21 } from "citty";
4212
+ import pc20 from "picocolors";
3336
4213
 
3337
4214
  // src/core/registry-manager.ts
3338
4215
  var BUILTIN_REGISTRIES = [
@@ -3402,7 +4279,7 @@ function getDefaultRegistry(configPath) {
3402
4279
  }
3403
4280
 
3404
4281
  // src/commands/registry.ts
3405
- var registry_default = defineCommand16({
4282
+ var registry_default = defineCommand21({
3406
4283
  meta: {
3407
4284
  name: "registry",
3408
4285
  description: "Manage custom registry URLs"
@@ -3432,67 +4309,67 @@ var registry_default = defineCommand16({
3432
4309
  case "list": {
3433
4310
  const registries = getRegistries();
3434
4311
  const defaultName = getDefaultRegistry();
3435
- console.log(pc15.bold("\n Registries\n"));
4312
+ console.log(pc20.bold("\n Registries\n"));
3436
4313
  for (const r of registries) {
3437
4314
  const isDefault = r.name === defaultName;
3438
- const defaultTag = isDefault ? pc15.green(" (default)") : "";
3439
- const builtinTag = r.builtin ? pc15.dim(" [builtin]") : "";
4315
+ const defaultTag = isDefault ? pc20.green(" (default)") : "";
4316
+ const builtinTag = r.builtin ? pc20.dim(" [builtin]") : "";
3440
4317
  console.log(
3441
- ` ${isDefault ? pc15.green("\u25CF") : pc15.dim("\u25CB")} ${pc15.bold(r.name)}${defaultTag}${builtinTag}`
4318
+ ` ${isDefault ? pc20.green("\u25CF") : pc20.dim("\u25CB")} ${pc20.bold(r.name)}${defaultTag}${builtinTag}`
3442
4319
  );
3443
- console.log(` ${pc15.dim(r.url)}`);
4320
+ console.log(` ${pc20.dim(r.url)}`);
3444
4321
  }
3445
4322
  console.log();
3446
4323
  break;
3447
4324
  }
3448
4325
  case "add": {
3449
4326
  if (!name) {
3450
- console.error(pc15.red(" Error: Usage: mcpman registry add <name> <url>"));
4327
+ console.error(pc20.red(" Error: Usage: mcpman registry add <name> <url>"));
3451
4328
  process.exit(1);
3452
4329
  }
3453
4330
  if (!url) {
3454
- console.error(pc15.red(" Error: Usage: mcpman registry add <name> <url>"));
4331
+ console.error(pc20.red(" Error: Usage: mcpman registry add <name> <url>"));
3455
4332
  process.exit(1);
3456
4333
  }
3457
4334
  try {
3458
4335
  addRegistry(name, url);
3459
- console.log(pc15.green(` Added registry '${name}' \u2192 ${url}`));
4336
+ console.log(pc20.green(` Added registry '${name}' \u2192 ${url}`));
3460
4337
  } catch (err) {
3461
- console.error(pc15.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
4338
+ console.error(pc20.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3462
4339
  process.exit(1);
3463
4340
  }
3464
4341
  break;
3465
4342
  }
3466
4343
  case "remove": {
3467
4344
  if (!name) {
3468
- console.error(pc15.red(" Error: Usage: mcpman registry remove <name>"));
4345
+ console.error(pc20.red(" Error: Usage: mcpman registry remove <name>"));
3469
4346
  process.exit(1);
3470
4347
  }
3471
4348
  try {
3472
4349
  removeRegistry(name);
3473
- console.log(pc15.green(` Removed registry '${name}'.`));
4350
+ console.log(pc20.green(` Removed registry '${name}'.`));
3474
4351
  } catch (err) {
3475
- console.error(pc15.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
4352
+ console.error(pc20.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3476
4353
  process.exit(1);
3477
4354
  }
3478
4355
  break;
3479
4356
  }
3480
4357
  case "set-default": {
3481
4358
  if (!name) {
3482
- console.error(pc15.red(" Error: Usage: mcpman registry set-default <name>"));
4359
+ console.error(pc20.red(" Error: Usage: mcpman registry set-default <name>"));
3483
4360
  process.exit(1);
3484
4361
  }
3485
4362
  try {
3486
4363
  setDefaultRegistry(name);
3487
- console.log(pc15.green(` Default registry set to '${name}'.`));
4364
+ console.log(pc20.green(` Default registry set to '${name}'.`));
3488
4365
  } catch (err) {
3489
- console.error(pc15.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
4366
+ console.error(pc20.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3490
4367
  process.exit(1);
3491
4368
  }
3492
4369
  break;
3493
4370
  }
3494
4371
  default:
3495
- console.error(pc15.red(` Unknown action '${action}'. Use: list, add, remove, set-default.`));
4372
+ console.error(pc20.red(` Unknown action '${action}'. Use: list, add, remove, set-default.`));
3496
4373
  process.exit(1);
3497
4374
  }
3498
4375
  }
@@ -3500,18 +4377,18 @@ var registry_default = defineCommand16({
3500
4377
 
3501
4378
  // src/commands/remove.ts
3502
4379
  import * as p8 from "@clack/prompts";
3503
- import { defineCommand as defineCommand17 } from "citty";
3504
- import pc16 from "picocolors";
3505
- var CLIENT_DISPLAY2 = {
4380
+ import { defineCommand as defineCommand22 } from "citty";
4381
+ import pc21 from "picocolors";
4382
+ var CLIENT_DISPLAY3 = {
3506
4383
  "claude-desktop": "Claude",
3507
4384
  cursor: "Cursor",
3508
4385
  vscode: "VS Code",
3509
4386
  windsurf: "Windsurf"
3510
4387
  };
3511
4388
  function clientDisplayName(type) {
3512
- return CLIENT_DISPLAY2[type] ?? type;
4389
+ return CLIENT_DISPLAY3[type] ?? type;
3513
4390
  }
3514
- var remove_default = defineCommand17({
4391
+ var remove_default = defineCommand22({
3515
4392
  meta: {
3516
4393
  name: "remove",
3517
4394
  description: "Remove an MCP server from one or more AI clients"
@@ -3538,7 +4415,7 @@ var remove_default = defineCommand17({
3538
4415
  }
3539
4416
  },
3540
4417
  async run({ args }) {
3541
- p8.intro(pc16.bold("mcpman remove"));
4418
+ p8.intro(pc21.bold("mcpman remove"));
3542
4419
  const serverName = args.server;
3543
4420
  const servers = await getInstalledServers();
3544
4421
  const match = servers.find((s) => s.name === serverName);
@@ -3548,7 +4425,7 @@ var remove_default = defineCommand17({
3548
4425
  (s) => s.name.includes(serverName) || serverName.includes(s.name)
3549
4426
  );
3550
4427
  if (similar.length > 0) {
3551
- p8.log.info(`Did you mean: ${similar.map((s) => pc16.cyan(s.name)).join(", ")}?`);
4428
+ p8.log.info(`Did you mean: ${similar.map((s) => pc21.cyan(s.name)).join(", ")}?`);
3552
4429
  }
3553
4430
  p8.outro("Nothing to remove.");
3554
4431
  return;
@@ -3583,7 +4460,7 @@ var remove_default = defineCommand17({
3583
4460
  if (!args.yes) {
3584
4461
  const clientNames = targetClients.map(clientDisplayName).join(", ");
3585
4462
  const confirmed = await p8.confirm({
3586
- message: `Remove ${pc16.cyan(serverName)} from ${pc16.yellow(clientNames)}?`
4463
+ message: `Remove ${pc21.cyan(serverName)} from ${pc21.yellow(clientNames)}?`
3587
4464
  });
3588
4465
  if (p8.isCancel(confirmed) || !confirmed) {
3589
4466
  p8.outro("Cancelled.");
@@ -3608,18 +4485,118 @@ var remove_default = defineCommand17({
3608
4485
  }
3609
4486
  if (errors.length > 0) {
3610
4487
  for (const e of errors) p8.log.error(e);
3611
- p8.outro(pc16.red("Completed with errors."));
4488
+ p8.outro(pc21.red("Completed with errors."));
4489
+ process.exit(1);
4490
+ }
4491
+ p8.outro(pc21.green(`Removed "${serverName}" successfully.`));
4492
+ }
4493
+ });
4494
+
4495
+ // src/commands/rollback.ts
4496
+ import * as p9 from "@clack/prompts";
4497
+ import { defineCommand as defineCommand23 } from "citty";
4498
+ import pc22 from "picocolors";
4499
+ var rollback_default = defineCommand23({
4500
+ meta: {
4501
+ name: "rollback",
4502
+ description: "Restore a previous lockfile snapshot"
4503
+ },
4504
+ args: {
4505
+ index: {
4506
+ type: "positional",
4507
+ description: "Snapshot index to restore (0 = most recent). Omit to use --list.",
4508
+ required: false
4509
+ },
4510
+ list: {
4511
+ type: "boolean",
4512
+ description: "List available snapshots",
4513
+ default: false
4514
+ },
4515
+ yes: {
4516
+ type: "boolean",
4517
+ description: "Skip confirmation prompt",
4518
+ default: false
4519
+ }
4520
+ },
4521
+ async run({ args }) {
4522
+ const snapshots = listSnapshots();
4523
+ if (args.list || args.index === void 0) {
4524
+ if (snapshots.length === 0) {
4525
+ console.log(
4526
+ pc22.dim("No snapshots available. Snapshots are created on each lockfile write.")
4527
+ );
4528
+ return;
4529
+ }
4530
+ console.log(
4531
+ `
4532
+ ${pc22.bold("Lockfile snapshots")} ${pc22.dim(`(${snapshots.length} available, 0 = most recent)`)}
4533
+ `
4534
+ );
4535
+ for (const snap2 of snapshots) {
4536
+ const size = `${Math.ceil(snap2.sizeBytes / 1024)}KB`;
4537
+ const date2 = snap2.createdAt ? new Date(snap2.createdAt).toLocaleString() : "unknown";
4538
+ console.log(` ${pc22.cyan(`[${snap2.index}]`)} ${pc22.dim(date2)} ${pc22.dim(size)}`);
4539
+ }
4540
+ console.log("");
4541
+ if (args.index === void 0) return;
4542
+ }
4543
+ const idx = Number.parseInt(String(args.index), 10);
4544
+ if (Number.isNaN(idx) || idx < 0) {
4545
+ console.error(
4546
+ `${pc22.red("\u2717")} Invalid index "${args.index}". Must be a non-negative integer.`
4547
+ );
4548
+ process.exit(1);
4549
+ }
4550
+ const snap = snapshots[idx];
4551
+ if (!snap) {
4552
+ console.error(
4553
+ `${pc22.red("\u2717")} Snapshot [${idx}] does not exist. Use --list to see available snapshots.`
4554
+ );
4555
+ process.exit(1);
4556
+ }
4557
+ const content = readSnapshot(idx);
4558
+ if (!content) {
4559
+ console.error(`${pc22.red("\u2717")} Could not read snapshot [${idx}].`);
3612
4560
  process.exit(1);
3613
4561
  }
3614
- p8.outro(pc16.green(`Removed "${serverName}" successfully.`));
4562
+ const date = snap.createdAt ? new Date(snap.createdAt).toLocaleString() : "unknown";
4563
+ console.log(`
4564
+ ${pc22.bold("Restoring snapshot")} ${pc22.cyan(`[${idx}]`)} ${pc22.dim(date)}
4565
+ `);
4566
+ try {
4567
+ const parsed = JSON.parse(content);
4568
+ const count = Object.keys(parsed.servers ?? {}).length;
4569
+ console.log(` ${pc22.dim(`Preview: ${count} server(s) in snapshot`)}
4570
+ `);
4571
+ } catch {
4572
+ }
4573
+ const lockfilePath = resolveLockfilePath();
4574
+ if (!args.yes) {
4575
+ const confirmed = await p9.confirm({
4576
+ message: `Restore snapshot [${idx}] to ${lockfilePath}?`,
4577
+ initialValue: false
4578
+ });
4579
+ if (p9.isCancel(confirmed) || !confirmed) {
4580
+ p9.cancel("Cancelled.");
4581
+ return;
4582
+ }
4583
+ }
4584
+ const restored = restoreSnapshot(idx, lockfilePath);
4585
+ if (!restored) {
4586
+ console.error(`${pc22.red("\u2717")} Restore failed.`);
4587
+ process.exit(1);
4588
+ }
4589
+ console.log(`
4590
+ ${pc22.green("\u2713")} Lockfile restored from snapshot [${idx}].`);
4591
+ console.log(pc22.dim(` Written to: ${lockfilePath}`));
3615
4592
  }
3616
4593
  });
3617
4594
 
3618
4595
  // src/commands/run.ts
3619
- import { spawn as spawn3 } from "child_process";
3620
- import { defineCommand as defineCommand18 } from "citty";
3621
- import pc17 from "picocolors";
3622
- var run_default = defineCommand18({
4596
+ import { spawn as spawn5 } from "child_process";
4597
+ import { defineCommand as defineCommand24 } from "citty";
4598
+ import pc23 from "picocolors";
4599
+ var run_default = defineCommand24({
3623
4600
  meta: {
3624
4601
  name: "run",
3625
4602
  description: "Run an installed MCP server with vault secrets injected"
@@ -3641,8 +4618,8 @@ var run_default = defineCommand18({
3641
4618
  const lockfile = readLockfile();
3642
4619
  const entry = lockfile.servers[serverName];
3643
4620
  if (!entry) {
3644
- console.error(pc17.red(` Error: Server '${serverName}' is not installed.`));
3645
- console.error(pc17.dim(` Run ${pc17.cyan("mcpman install <server>")} to install it first.`));
4621
+ console.error(pc23.red(` Error: Server '${serverName}' is not installed.`));
4622
+ console.error(pc23.dim(` Run ${pc23.cyan("mcpman install <server>")} to install it first.`));
3646
4623
  process.exit(1);
3647
4624
  }
3648
4625
  const lockfileEnv = parseEnvFlags(entry.envVars);
@@ -3654,8 +4631,8 @@ var run_default = defineCommand18({
3654
4631
  ...vaultEnv,
3655
4632
  ...cliEnv
3656
4633
  };
3657
- console.log(pc17.dim(` Running ${pc17.cyan(serverName)}...`));
3658
- const child = spawn3(entry.command, entry.args, {
4634
+ console.log(pc23.dim(` Running ${pc23.cyan(serverName)}...`));
4635
+ const child = spawn5(entry.command, entry.args, {
3659
4636
  env: finalEnv,
3660
4637
  stdio: "inherit"
3661
4638
  });
@@ -3672,7 +4649,7 @@ var run_default = defineCommand18({
3672
4649
  resolve();
3673
4650
  });
3674
4651
  child.on("error", (err) => {
3675
- console.error(pc17.red(` Failed to start '${serverName}': ${err.message}`));
4652
+ console.error(pc23.red(` Failed to start '${serverName}': ${err.message}`));
3676
4653
  process.exit(1);
3677
4654
  resolve();
3678
4655
  });
@@ -3688,15 +4665,15 @@ async function loadVaultSecrets2(serverName) {
3688
4665
  const password = await getMasterPassword();
3689
4666
  return getSecretsForServer(serverName, password);
3690
4667
  } catch {
3691
- console.warn(pc17.yellow(" Warning: Could not load vault secrets, continuing without them."));
4668
+ console.warn(pc23.yellow(" Warning: Could not load vault secrets, continuing without them."));
3692
4669
  return {};
3693
4670
  }
3694
4671
  }
3695
4672
 
3696
4673
  // src/commands/search.ts
3697
- import { defineCommand as defineCommand19 } from "citty";
4674
+ import { defineCommand as defineCommand25 } from "citty";
3698
4675
  import { createSpinner as createSpinner4 } from "nanospinner";
3699
- import pc18 from "picocolors";
4676
+ import pc24 from "picocolors";
3700
4677
 
3701
4678
  // src/core/registry-search.ts
3702
4679
  var SEARCH_TIMEOUT_MS = 1e4;
@@ -3769,10 +4746,10 @@ function pad2(s, width) {
3769
4746
  function highlightMatch(name, query) {
3770
4747
  const idx = name.toLowerCase().indexOf(query.toLowerCase());
3771
4748
  if (idx === -1) return name;
3772
- return name.slice(0, idx) + pc18.yellow(name.slice(idx, idx + query.length)) + name.slice(idx + query.length);
4749
+ return name.slice(0, idx) + pc24.yellow(name.slice(idx, idx + query.length)) + name.slice(idx + query.length);
3773
4750
  }
3774
4751
  function formatDownloads(n) {
3775
- if (!n) return pc18.dim("\u2014");
4752
+ if (!n) return pc24.dim("\u2014");
3776
4753
  if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
3777
4754
  if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
3778
4755
  return String(n);
@@ -3783,9 +4760,9 @@ function printNpmResults(results, query) {
3783
4760
  const dlWidth = 9;
3784
4761
  const descMax = 50;
3785
4762
  const header = ` ${pad2("NAME", nameWidth)} ${pad2("VERSION", verWidth)} ${pad2("DOWNLOADS", dlWidth)} DESCRIPTION`;
3786
- console.log(pc18.dim(header));
4763
+ console.log(pc24.dim(header));
3787
4764
  console.log(
3788
- pc18.dim(
4765
+ pc24.dim(
3789
4766
  ` ${"-".repeat(nameWidth)} ${"-".repeat(verWidth)} ${"-".repeat(dlWidth)} ${"-".repeat(descMax)}`
3790
4767
  )
3791
4768
  );
@@ -3793,8 +4770,8 @@ function printNpmResults(results, query) {
3793
4770
  const name = highlightMatch(pad2(r.name, nameWidth), query);
3794
4771
  const ver = pad2(r.version, verWidth);
3795
4772
  const dl = pad2(formatDownloads(r.downloads), dlWidth);
3796
- const desc = truncate2(r.description || pc18.dim("(no description)"), descMax);
3797
- console.log(` ${name} ${pc18.dim(ver)} ${dl} ${desc}`);
4773
+ const desc = truncate2(r.description || pc24.dim("(no description)"), descMax);
4774
+ console.log(` ${name} ${pc24.dim(ver)} ${dl} ${desc}`);
3798
4775
  }
3799
4776
  }
3800
4777
  function printSmitheryResults(results, query) {
@@ -3802,19 +4779,19 @@ function printSmitheryResults(results, query) {
3802
4779
  const usesWidth = 8;
3803
4780
  const descMax = 50;
3804
4781
  const header = ` ${pad2("NAME", nameWidth)} ${pad2("USES", usesWidth)} DESCRIPTION`;
3805
- console.log(pc18.dim(header));
4782
+ console.log(pc24.dim(header));
3806
4783
  console.log(
3807
- pc18.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(usesWidth)} ${"-".repeat(descMax)}`)
4784
+ pc24.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(usesWidth)} ${"-".repeat(descMax)}`)
3808
4785
  );
3809
4786
  for (const r of results) {
3810
4787
  const name = highlightMatch(pad2(r.name, nameWidth), query);
3811
4788
  const uses = pad2(formatDownloads(r.useCount), usesWidth);
3812
- const badge = r.verified ? pc18.green(" \u2713") : "";
3813
- const desc = truncate2(r.description || pc18.dim("(no description)"), descMax);
4789
+ const badge = r.verified ? pc24.green(" \u2713") : "";
4790
+ const desc = truncate2(r.description || pc24.dim("(no description)"), descMax);
3814
4791
  console.log(` ${name}${badge} ${uses} ${desc}`);
3815
4792
  }
3816
4793
  }
3817
- var search_default = defineCommand19({
4794
+ var search_default = defineCommand25({
3818
4795
  meta: {
3819
4796
  name: "search",
3820
4797
  description: "Search for MCP servers on npm or Smithery registry"
@@ -3846,7 +4823,7 @@ var search_default = defineCommand19({
3846
4823
  const registry = args.registry.toLowerCase();
3847
4824
  const limit = Math.min(Math.max(1, Number.parseInt(args.limit, 10) || 20), 100);
3848
4825
  if (registry !== "npm" && registry !== "smithery") {
3849
- console.error(pc18.red(` Unknown registry "${registry}". Use "npm" or "smithery".`));
4826
+ console.error(pc24.red(` Unknown registry "${registry}". Use "npm" or "smithery".`));
3850
4827
  process.exit(1);
3851
4828
  }
3852
4829
  const spinner5 = createSpinner4(`Searching ${registry} for "${query}"...`).start();
@@ -3854,20 +4831,20 @@ var search_default = defineCommand19({
3854
4831
  const results2 = await searchNpm(query, limit);
3855
4832
  spinner5.stop();
3856
4833
  if (results2.length === 0) {
3857
- console.log(pc18.dim(`
4834
+ console.log(pc24.dim(`
3858
4835
  No results found for "${query}" on npm.
3859
4836
  `));
3860
4837
  return;
3861
4838
  }
3862
4839
  console.log(
3863
- pc18.bold(
4840
+ pc24.bold(
3864
4841
  `
3865
4842
  mcpman search \u2014 npm (${results2.length} result${results2.length !== 1 ? "s" : ""})
3866
4843
  `
3867
4844
  )
3868
4845
  );
3869
4846
  printNpmResults(results2, query);
3870
- console.log(pc18.dim("\n Install with: mcpman install <name>\n"));
4847
+ console.log(pc24.dim("\n Install with: mcpman install <name>\n"));
3871
4848
  if (args.all) {
3872
4849
  await printPluginResults(query, limit);
3873
4850
  }
@@ -3876,20 +4853,20 @@ var search_default = defineCommand19({
3876
4853
  const results = await searchSmithery(query, limit);
3877
4854
  spinner5.stop();
3878
4855
  if (results.length === 0) {
3879
- console.log(pc18.dim(`
4856
+ console.log(pc24.dim(`
3880
4857
  No results found for "${query}" on Smithery.
3881
4858
  `));
3882
4859
  return;
3883
4860
  }
3884
4861
  console.log(
3885
- pc18.bold(
4862
+ pc24.bold(
3886
4863
  `
3887
4864
  mcpman search \u2014 Smithery (${results.length} result${results.length !== 1 ? "s" : ""})
3888
4865
  `
3889
4866
  )
3890
4867
  );
3891
4868
  printSmitheryResults(results, query);
3892
- console.log(pc18.dim("\n Install with: mcpman install <name>\n"));
4869
+ console.log(pc24.dim("\n Install with: mcpman install <name>\n"));
3893
4870
  if (args.all) {
3894
4871
  await printPluginResults(query, limit);
3895
4872
  }
@@ -3899,7 +4876,7 @@ async function printPluginResults(query, limit) {
3899
4876
  const pluginResults = await searchPlugins(query, limit);
3900
4877
  if (pluginResults.length === 0) return;
3901
4878
  console.log(
3902
- pc18.bold(
4879
+ pc24.bold(
3903
4880
  `
3904
4881
  Plugins (${pluginResults.length} result${pluginResults.length !== 1 ? "s" : ""})
3905
4882
  `
@@ -3909,22 +4886,22 @@ async function printPluginResults(query, limit) {
3909
4886
  const srcWidth = Math.max(6, ...pluginResults.map((r) => r.source.length));
3910
4887
  const descMax = 50;
3911
4888
  const header = ` ${pad2("NAME", nameWidth)} ${pad2("SOURCE", srcWidth)} DESCRIPTION`;
3912
- console.log(pc18.dim(header));
4889
+ console.log(pc24.dim(header));
3913
4890
  console.log(
3914
- pc18.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(srcWidth)} ${"-".repeat(descMax)}`)
4891
+ pc24.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(srcWidth)} ${"-".repeat(descMax)}`)
3915
4892
  );
3916
4893
  for (const r of pluginResults) {
3917
4894
  const name = highlightMatch(pad2(r.name, nameWidth), query);
3918
4895
  const src = pad2(r.source, srcWidth);
3919
- const desc = truncate2(r.description || pc18.dim("(no description)"), descMax);
3920
- console.log(` ${name} ${pc18.dim(src)} ${desc}`);
4896
+ const desc = truncate2(r.description || pc24.dim("(no description)"), descMax);
4897
+ console.log(` ${name} ${pc24.dim(src)} ${desc}`);
3921
4898
  }
3922
4899
  }
3923
4900
 
3924
4901
  // src/commands/secrets.ts
3925
- import * as p9 from "@clack/prompts";
3926
- import { defineCommand as defineCommand20 } from "citty";
3927
- import pc19 from "picocolors";
4902
+ import * as p10 from "@clack/prompts";
4903
+ import { defineCommand as defineCommand26 } from "citty";
4904
+ import pc25 from "picocolors";
3928
4905
  function maskValue(value) {
3929
4906
  if (value.length <= 8) return "***";
3930
4907
  return `${value.slice(0, 4)}***${value.slice(-3)}`;
@@ -3934,7 +4911,7 @@ function parseKeyValue(input) {
3934
4911
  if (idx <= 0) return null;
3935
4912
  return { key: input.slice(0, idx), value: input.slice(idx + 1) };
3936
4913
  }
3937
- var setCommand2 = defineCommand20({
4914
+ var setCommand2 = defineCommand26({
3938
4915
  meta: { name: "set", description: "Store an encrypted secret for a server" },
3939
4916
  args: {
3940
4917
  server: {
@@ -3951,28 +4928,28 @@ var setCommand2 = defineCommand20({
3951
4928
  async run({ args }) {
3952
4929
  const parsed = parseKeyValue(args.keyvalue);
3953
4930
  if (!parsed) {
3954
- console.error(`${pc19.red("\u2717")} Invalid format. Expected KEY=VALUE`);
4931
+ console.error(`${pc25.red("\u2717")} Invalid format. Expected KEY=VALUE`);
3955
4932
  process.exit(1);
3956
4933
  }
3957
- p9.intro(pc19.cyan("mcpman secrets set"));
4934
+ p10.intro(pc25.cyan("mcpman secrets set"));
3958
4935
  const isNew = listSecrets(args.server).length === 0 || !listSecrets(args.server)[0]?.keys.includes(parsed.key);
3959
4936
  const vaultPath = (await import("./vault-service-UTZAV6N6.js")).getVaultPath();
3960
4937
  const vaultExists = (await import("fs")).existsSync(vaultPath);
3961
4938
  const password = await getMasterPassword(!vaultExists && isNew);
3962
- const spin = p9.spinner();
4939
+ const spin = p10.spinner();
3963
4940
  spin.start("Encrypting secret...");
3964
4941
  try {
3965
4942
  setSecret(args.server, parsed.key, parsed.value, password);
3966
- spin.stop(`${pc19.green("\u2713")} Stored ${pc19.bold(parsed.key)} for ${pc19.cyan(args.server)}`);
4943
+ spin.stop(`${pc25.green("\u2713")} Stored ${pc25.bold(parsed.key)} for ${pc25.cyan(args.server)}`);
3967
4944
  } catch (err) {
3968
- spin.stop(`${pc19.red("\u2717")} Failed to store secret`);
3969
- console.error(pc19.dim(String(err)));
4945
+ spin.stop(`${pc25.red("\u2717")} Failed to store secret`);
4946
+ console.error(pc25.dim(String(err)));
3970
4947
  process.exit(1);
3971
4948
  }
3972
- p9.outro(pc19.dim("Secret encrypted and saved to vault."));
4949
+ p10.outro(pc25.dim("Secret encrypted and saved to vault."));
3973
4950
  }
3974
4951
  });
3975
- var listCommand3 = defineCommand20({
4952
+ var listCommand3 = defineCommand26({
3976
4953
  meta: { name: "list", description: "List secret keys stored in the vault" },
3977
4954
  args: {
3978
4955
  server: {
@@ -3984,27 +4961,27 @@ var listCommand3 = defineCommand20({
3984
4961
  async run({ args }) {
3985
4962
  const results = listSecrets(args.server || void 0);
3986
4963
  if (results.length === 0) {
3987
- const filter = args.server ? ` for ${pc19.cyan(args.server)}` : "";
3988
- console.log(pc19.dim(`No secrets stored${filter}.`));
4964
+ const filter = args.server ? ` for ${pc25.cyan(args.server)}` : "";
4965
+ console.log(pc25.dim(`No secrets stored${filter}.`));
3989
4966
  return;
3990
4967
  }
3991
4968
  console.log("");
3992
4969
  for (const { server, keys } of results) {
3993
- console.log(pc19.bold(pc19.cyan(server)));
4970
+ console.log(pc25.bold(pc25.cyan(server)));
3994
4971
  for (const key of keys) {
3995
- console.log(` ${pc19.green("\u25CF")} ${pc19.bold(key)} ${pc19.dim(maskValue("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"))}`);
4972
+ console.log(` ${pc25.green("\u25CF")} ${pc25.bold(key)} ${pc25.dim(maskValue("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"))}`);
3996
4973
  }
3997
4974
  console.log("");
3998
4975
  }
3999
4976
  const total = results.reduce((n, r) => n + r.keys.length, 0);
4000
4977
  console.log(
4001
- pc19.dim(
4978
+ pc25.dim(
4002
4979
  ` ${total} secret${total !== 1 ? "s" : ""} in ${results.length} server${results.length !== 1 ? "s" : ""}`
4003
4980
  )
4004
4981
  );
4005
4982
  }
4006
4983
  });
4007
- var removeCommand2 = defineCommand20({
4984
+ var removeCommand2 = defineCommand26({
4008
4985
  meta: { name: "remove", description: "Delete a secret from the vault" },
4009
4986
  args: {
4010
4987
  server: {
@@ -4019,25 +4996,25 @@ var removeCommand2 = defineCommand20({
4019
4996
  }
4020
4997
  },
4021
4998
  async run({ args }) {
4022
- const confirmed = await p9.confirm({
4023
- message: `Remove ${pc19.bold(args.key)} from ${pc19.cyan(args.server)}?`,
4999
+ const confirmed = await p10.confirm({
5000
+ message: `Remove ${pc25.bold(args.key)} from ${pc25.cyan(args.server)}?`,
4024
5001
  initialValue: false
4025
5002
  });
4026
- if (p9.isCancel(confirmed) || !confirmed) {
4027
- p9.cancel("Cancelled.");
5003
+ if (p10.isCancel(confirmed) || !confirmed) {
5004
+ p10.cancel("Cancelled.");
4028
5005
  return;
4029
5006
  }
4030
5007
  try {
4031
5008
  removeSecret(args.server, args.key);
4032
- console.log(`${pc19.green("\u2713")} Removed ${pc19.bold(args.key)} from ${pc19.cyan(args.server)}`);
5009
+ console.log(`${pc25.green("\u2713")} Removed ${pc25.bold(args.key)} from ${pc25.cyan(args.server)}`);
4033
5010
  } catch (err) {
4034
- console.error(`${pc19.red("\u2717")} Failed to remove secret`);
4035
- console.error(pc19.dim(String(err)));
5011
+ console.error(`${pc25.red("\u2717")} Failed to remove secret`);
5012
+ console.error(pc25.dim(String(err)));
4036
5013
  process.exit(1);
4037
5014
  }
4038
5015
  }
4039
5016
  });
4040
- var secrets_default = defineCommand20({
5017
+ var secrets_default = defineCommand26({
4041
5018
  meta: {
4042
5019
  name: "secrets",
4043
5020
  description: "Manage encrypted secrets for MCP servers"
@@ -4050,9 +5027,9 @@ var secrets_default = defineCommand20({
4050
5027
  });
4051
5028
 
4052
5029
  // src/commands/sync.ts
4053
- import * as p10 from "@clack/prompts";
4054
- import { defineCommand as defineCommand21 } from "citty";
4055
- import pc20 from "picocolors";
5030
+ import * as p11 from "@clack/prompts";
5031
+ import { defineCommand as defineCommand27 } from "citty";
5032
+ import pc26 from "picocolors";
4056
5033
 
4057
5034
  // src/core/config-diff.ts
4058
5035
  function reconstructServerEntry(lockEntry) {
@@ -4197,14 +5174,14 @@ async function getClientConfigs() {
4197
5174
  }
4198
5175
 
4199
5176
  // src/commands/sync.ts
4200
- var VALID_CLIENTS = ["claude-desktop", "cursor", "vscode", "windsurf"];
4201
- var CLIENT_DISPLAY3 = {
5177
+ var VALID_CLIENTS2 = ["claude-desktop", "cursor", "vscode", "windsurf"];
5178
+ var CLIENT_DISPLAY4 = {
4202
5179
  "claude-desktop": "Claude Desktop",
4203
5180
  cursor: "Cursor",
4204
5181
  vscode: "VS Code",
4205
5182
  windsurf: "Windsurf"
4206
5183
  };
4207
- var sync_default = defineCommand21({
5184
+ var sync_default = defineCommand27({
4208
5185
  meta: {
4209
5186
  name: "sync",
4210
5187
  description: "Sync MCP server configs across all detected AI clients"
@@ -4231,20 +5208,20 @@ var sync_default = defineCommand21({
4231
5208
  }
4232
5209
  },
4233
5210
  async run({ args }) {
4234
- p10.intro(`${pc20.cyan("mcpman sync")}`);
5211
+ p11.intro(`${pc26.cyan("mcpman sync")}`);
4235
5212
  const sourceClient = args.source;
4236
- if (sourceClient && !VALID_CLIENTS.includes(sourceClient)) {
4237
- p10.log.error(
4238
- `Invalid --source "${sourceClient}". Must be one of: ${VALID_CLIENTS.join(", ")}`
5213
+ if (sourceClient && !VALID_CLIENTS2.includes(sourceClient)) {
5214
+ p11.log.error(
5215
+ `Invalid --source "${sourceClient}". Must be one of: ${VALID_CLIENTS2.join(", ")}`
4239
5216
  );
4240
5217
  process.exit(1);
4241
5218
  }
4242
- const spinner5 = p10.spinner();
5219
+ const spinner5 = p11.spinner();
4243
5220
  spinner5.start("Detecting clients and reading configs...");
4244
5221
  const { configs, handlers } = await getClientConfigs();
4245
5222
  spinner5.stop(`Found ${configs.size} client(s)`);
4246
5223
  if (configs.size === 0) {
4247
- p10.log.warn(
5224
+ p11.log.warn(
4248
5225
  "No AI clients detected. Install Claude Desktop, Cursor, VS Code, or Windsurf first."
4249
5226
  );
4250
5227
  process.exit(0);
@@ -4253,10 +5230,10 @@ var sync_default = defineCommand21({
4253
5230
  let actions;
4254
5231
  if (sourceClient) {
4255
5232
  if (!configs.has(sourceClient)) {
4256
- p10.log.error(`Source client "${sourceClient}" is not detected or its config is unreadable.`);
5233
+ p11.log.error(`Source client "${sourceClient}" is not detected or its config is unreadable.`);
4257
5234
  process.exit(1);
4258
5235
  }
4259
- p10.log.info(`Using ${CLIENT_DISPLAY3[sourceClient]} as source of truth`);
5236
+ p11.log.info(`Using ${CLIENT_DISPLAY4[sourceClient]} as source of truth`);
4260
5237
  actions = computeDiffFromClient(sourceClient, configs, diffOptions);
4261
5238
  } else {
4262
5239
  const lockfile = readLockfile();
@@ -4267,32 +5244,32 @@ var sync_default = defineCommand21({
4267
5244
  const extraCount = actions.filter((a) => a.action === "extra").length;
4268
5245
  const removeCount = actions.filter((a) => a.action === "remove").length;
4269
5246
  if (addCount === 0 && removeCount === 0 && extraCount === 0) {
4270
- p10.outro(pc20.green("All clients are in sync."));
5247
+ p11.outro(pc26.green("All clients are in sync."));
4271
5248
  process.exit(0);
4272
5249
  }
4273
5250
  const parts = [];
4274
- if (addCount > 0) parts.push(pc20.green(`${addCount} to add`));
4275
- if (removeCount > 0) parts.push(pc20.red(`${removeCount} to remove`));
4276
- if (extraCount > 0) parts.push(pc20.yellow(`${extraCount} extra (informational)`));
4277
- p10.log.info(parts.join(" \xB7 "));
5251
+ if (addCount > 0) parts.push(pc26.green(`${addCount} to add`));
5252
+ if (removeCount > 0) parts.push(pc26.red(`${removeCount} to remove`));
5253
+ if (extraCount > 0) parts.push(pc26.yellow(`${extraCount} extra (informational)`));
5254
+ p11.log.info(parts.join(" \xB7 "));
4278
5255
  if (args["dry-run"]) {
4279
- p10.outro(pc20.dim("Dry run \u2014 no changes applied."));
5256
+ p11.outro(pc26.dim("Dry run \u2014 no changes applied."));
4280
5257
  process.exit(1);
4281
5258
  }
4282
5259
  if (addCount === 0 && removeCount === 0) {
4283
- p10.outro(pc20.dim("No additions needed. Extra servers left untouched."));
5260
+ p11.outro(pc26.dim("No additions needed. Extra servers left untouched."));
4284
5261
  process.exit(1);
4285
5262
  }
4286
5263
  if (!args.yes) {
4287
5264
  const actionParts = [];
4288
5265
  if (addCount > 0) actionParts.push(`${addCount} addition(s)`);
4289
5266
  if (removeCount > 0) actionParts.push(`${removeCount} removal(s)`);
4290
- const confirmed = await p10.confirm({
5267
+ const confirmed = await p11.confirm({
4291
5268
  message: `Apply ${actionParts.join(" and ")} to client configs?`,
4292
5269
  initialValue: true
4293
5270
  });
4294
- if (p10.isCancel(confirmed) || !confirmed) {
4295
- p10.outro(pc20.dim("Cancelled \u2014 no changes applied."));
5271
+ if (p11.isCancel(confirmed) || !confirmed) {
5272
+ p11.outro(pc26.dim("Cancelled \u2014 no changes applied."));
4296
5273
  process.exit(0);
4297
5274
  }
4298
5275
  }
@@ -4300,37 +5277,37 @@ var sync_default = defineCommand21({
4300
5277
  const result = await applySyncActions(actions, handlers);
4301
5278
  spinner5.stop("Done");
4302
5279
  if (result.applied > 0) {
4303
- p10.log.success(`Added ${result.applied} server(s) to client configs.`);
5280
+ p11.log.success(`Added ${result.applied} server(s) to client configs.`);
4304
5281
  }
4305
5282
  if (result.removed > 0) {
4306
- p10.log.success(`Removed ${result.removed} server(s) from client configs.`);
5283
+ p11.log.success(`Removed ${result.removed} server(s) from client configs.`);
4307
5284
  }
4308
5285
  if (result.failed > 0) {
4309
5286
  for (const e of result.errors) {
4310
- p10.log.error(`Failed to sync "${e.server}" on ${e.client}: ${e.error}`);
5287
+ p11.log.error(`Failed to sync "${e.server}" on ${e.client}: ${e.error}`);
4311
5288
  }
4312
5289
  }
4313
- p10.outro(
4314
- result.failed === 0 ? pc20.green("Sync complete.") : pc20.yellow("Sync complete with errors.")
5290
+ p11.outro(
5291
+ result.failed === 0 ? pc26.green("Sync complete.") : pc26.yellow("Sync complete with errors.")
4315
5292
  );
4316
5293
  process.exit(result.failed > 0 ? 1 : 0);
4317
5294
  }
4318
5295
  });
4319
5296
  function printDiffTable(actions) {
4320
5297
  if (actions.length === 0) {
4321
- p10.log.info("No actions to display.");
5298
+ p11.log.info("No actions to display.");
4322
5299
  return;
4323
5300
  }
4324
5301
  const nameWidth = Math.max(6, ...actions.map((a) => a.server.length));
4325
5302
  const clientWidth = Math.max(
4326
5303
  6,
4327
- ...actions.map((a) => CLIENT_DISPLAY3[a.client]?.length ?? a.client.length)
5304
+ ...actions.map((a) => CLIENT_DISPLAY4[a.client]?.length ?? a.client.length)
4328
5305
  );
4329
5306
  const header = ` ${pad3("SERVER", nameWidth)} ${pad3("CLIENT", clientWidth)} STATUS`;
4330
- console.log(pc20.dim(header));
4331
- console.log(pc20.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientWidth)} ------`));
5307
+ console.log(pc26.dim(header));
5308
+ console.log(pc26.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientWidth)} ------`));
4332
5309
  for (const action of actions) {
4333
- const clientDisplay = CLIENT_DISPLAY3[action.client] ?? action.client;
5310
+ const clientDisplay = CLIENT_DISPLAY4[action.client] ?? action.client;
4334
5311
  const [icon, statusText] = formatAction(action.action);
4335
5312
  console.log(
4336
5313
  ` ${pad3(action.server, nameWidth)} ${pad3(clientDisplay, clientWidth)} ${icon} ${statusText}`
@@ -4341,13 +5318,13 @@ function printDiffTable(actions) {
4341
5318
  function formatAction(action) {
4342
5319
  switch (action) {
4343
5320
  case "add":
4344
- return [pc20.green("+"), pc20.green("missing \u2014 will add")];
5321
+ return [pc26.green("+"), pc26.green("missing \u2014 will add")];
4345
5322
  case "extra":
4346
- return [pc20.yellow("?"), pc20.yellow("extra (not in lockfile)")];
5323
+ return [pc26.yellow("?"), pc26.yellow("extra (not in lockfile)")];
4347
5324
  case "remove":
4348
- return [pc20.red("\u2013"), pc20.red("extra \u2014 will remove")];
5325
+ return [pc26.red("\u2013"), pc26.red("extra \u2014 will remove")];
4349
5326
  case "ok":
4350
- return [pc20.dim("\xB7"), pc20.dim("in sync")];
5327
+ return [pc26.dim("\xB7"), pc26.dim("in sync")];
4351
5328
  }
4352
5329
  }
4353
5330
  function pad3(s, width) {
@@ -4355,11 +5332,11 @@ function pad3(s, width) {
4355
5332
  }
4356
5333
 
4357
5334
  // src/commands/test-command.ts
4358
- import { defineCommand as defineCommand22 } from "citty";
4359
- import pc21 from "picocolors";
5335
+ import { defineCommand as defineCommand28 } from "citty";
5336
+ import pc27 from "picocolors";
4360
5337
 
4361
5338
  // src/core/mcp-tester.ts
4362
- import { spawn as spawn4 } from "child_process";
5339
+ import { spawn as spawn6 } from "child_process";
4363
5340
  var TIMEOUT_MS = 1e4;
4364
5341
  async function testMcpServer(serverName, command, args, env) {
4365
5342
  const start = Date.now();
@@ -4369,7 +5346,7 @@ async function testMcpServer(serverName, command, args, env) {
4369
5346
  let initOk = false;
4370
5347
  let toolsOk = false;
4371
5348
  let tools = [];
4372
- const child = spawn4(command, args, {
5349
+ const child = spawn6(command, args, {
4373
5350
  env: { ...process.env, ...env },
4374
5351
  stdio: ["pipe", "pipe", "pipe"]
4375
5352
  });
@@ -4454,7 +5431,7 @@ async function testMcpServer(serverName, command, args, env) {
4454
5431
  }
4455
5432
 
4456
5433
  // src/commands/test-command.ts
4457
- var test_command_default = defineCommand22({
5434
+ var test_command_default = defineCommand28({
4458
5435
  meta: {
4459
5436
  name: "test",
4460
5437
  description: "Test MCP server connectivity and capabilities"
@@ -4475,10 +5452,10 @@ var test_command_default = defineCommand22({
4475
5452
  const lockfile = readLockfile();
4476
5453
  const serverNames = args.all ? Object.keys(lockfile.servers) : args.server ? [args.server] : [];
4477
5454
  if (serverNames.length === 0) {
4478
- console.error(pc21.red(" Error: Specify a server name or use --all."));
5455
+ console.error(pc27.red(" Error: Specify a server name or use --all."));
4479
5456
  process.exit(1);
4480
5457
  }
4481
- console.log(pc21.bold(`
5458
+ console.log(pc27.bold(`
4482
5459
  mcpman test \u2014 ${serverNames.length} server(s)
4483
5460
  `));
4484
5461
  let passed = 0;
@@ -4486,7 +5463,7 @@ var test_command_default = defineCommand22({
4486
5463
  for (const name of serverNames) {
4487
5464
  const entry = lockfile.servers[name];
4488
5465
  if (!entry) {
4489
- console.log(` ${pc21.red("\u2717")} ${pc21.bold(name)} \u2014 not installed`);
5466
+ console.log(` ${pc27.red("\u2717")} ${pc27.bold(name)} \u2014 not installed`);
4490
5467
  failed++;
4491
5468
  continue;
4492
5469
  }
@@ -4497,27 +5474,27 @@ var test_command_default = defineCommand22({
4497
5474
  if (result.passed) {
4498
5475
  passed++;
4499
5476
  console.log(
4500
- ` ${pc21.green("\u2713")} ${pc21.bold(name)} ${pc21.dim(`(${result.responseTimeMs}ms)`)}`
5477
+ ` ${pc27.green("\u2713")} ${pc27.bold(name)} ${pc27.dim(`(${result.responseTimeMs}ms)`)}`
4501
5478
  );
4502
5479
  if (result.tools.length > 0) {
4503
- console.log(pc21.dim(` Tools: ${result.tools.join(", ")}`));
5480
+ console.log(pc27.dim(` Tools: ${result.tools.join(", ")}`));
4504
5481
  }
4505
5482
  } else {
4506
5483
  failed++;
4507
- console.log(` ${pc21.red("\u2717")} ${pc21.bold(name)} ${pc21.dim(`(${result.responseTimeMs}ms)`)}`);
5484
+ console.log(` ${pc27.red("\u2717")} ${pc27.bold(name)} ${pc27.dim(`(${result.responseTimeMs}ms)`)}`);
4508
5485
  if (result.error) {
4509
- console.log(` ${pc21.red(result.error)}`);
5486
+ console.log(` ${pc27.red(result.error)}`);
4510
5487
  }
4511
5488
  console.log(
4512
- ` ${pc21.dim("initialize:")} ${result.initializeOk ? pc21.green("ok") : pc21.red("fail")} ${pc21.dim("tools/list:")} ${result.toolsListOk ? pc21.green("ok") : pc21.red("fail")}`
5489
+ ` ${pc27.dim("initialize:")} ${result.initializeOk ? pc27.green("ok") : pc27.red("fail")} ${pc27.dim("tools/list:")} ${result.toolsListOk ? pc27.green("ok") : pc27.red("fail")}`
4513
5490
  );
4514
5491
  }
4515
5492
  }
4516
- console.log(pc21.dim(`
5493
+ console.log(pc27.dim(`
4517
5494
  ${"\u2500".repeat(40)}`));
4518
5495
  const parts = [];
4519
- if (passed > 0) parts.push(pc21.green(`${passed} passed`));
4520
- if (failed > 0) parts.push(pc21.red(`${failed} failed`));
5496
+ if (passed > 0) parts.push(pc27.green(`${passed} passed`));
5497
+ if (failed > 0) parts.push(pc27.red(`${failed} failed`));
4521
5498
  console.log(` ${parts.join(", ")}
4522
5499
  `);
4523
5500
  if (failed > 0) process.exit(1);
@@ -4535,24 +5512,24 @@ async function loadVaultSecrets3(serverName) {
4535
5512
  }
4536
5513
 
4537
5514
  // src/commands/update.ts
4538
- import * as p11 from "@clack/prompts";
4539
- import { defineCommand as defineCommand23 } from "citty";
4540
- import pc23 from "picocolors";
5515
+ import * as p12 from "@clack/prompts";
5516
+ import { defineCommand as defineCommand29 } from "citty";
5517
+ import pc29 from "picocolors";
4541
5518
 
4542
5519
  // src/core/update-notifier.ts
4543
- import fs11 from "fs";
5520
+ import fs14 from "fs";
4544
5521
  import os3 from "os";
4545
- import path13 from "path";
4546
- import pc22 from "picocolors";
4547
- var CACHE_FILE = path13.join(os3.homedir(), ".mcpman", ".update-check");
5522
+ import path14 from "path";
5523
+ import pc28 from "picocolors";
5524
+ var CACHE_FILE = path14.join(os3.homedir(), ".mcpman", ".update-check");
4548
5525
  var TTL_MS = 24 * 60 * 60 * 1e3;
4549
5526
  function writeUpdateCache(data) {
4550
5527
  try {
4551
- const dir = path13.dirname(CACHE_FILE);
4552
- if (!fs11.existsSync(dir)) fs11.mkdirSync(dir, { recursive: true });
5528
+ const dir = path14.dirname(CACHE_FILE);
5529
+ if (!fs14.existsSync(dir)) fs14.mkdirSync(dir, { recursive: true });
4553
5530
  const tmp = `${CACHE_FILE}.tmp`;
4554
- fs11.writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
4555
- fs11.renameSync(tmp, CACHE_FILE);
5531
+ fs14.writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
5532
+ fs14.renameSync(tmp, CACHE_FILE);
4556
5533
  } catch {
4557
5534
  }
4558
5535
  }
@@ -4560,7 +5537,7 @@ function writeUpdateCache(data) {
4560
5537
  // src/commands/update.ts
4561
5538
  async function loadClients3() {
4562
5539
  try {
4563
- const mod = await import("./client-detector-CY7WPF3K.js");
5540
+ const mod = await import("./client-detector-NHXBDNMY.js");
4564
5541
  return mod.getInstalledClients();
4565
5542
  } catch {
4566
5543
  return [];
@@ -4575,19 +5552,19 @@ function printTable(updates) {
4575
5552
  "LATEST".padEnd(VER_W),
4576
5553
  "STATUS"
4577
5554
  ].join(" ");
4578
- console.log(pc23.bold(`
5555
+ console.log(pc29.bold(`
4579
5556
  ${header}`));
4580
- console.log(pc23.dim(` ${"\u2500".repeat(NAME_W + VER_W * 2 + 20)}`));
5557
+ console.log(pc29.dim(` ${"\u2500".repeat(NAME_W + VER_W * 2 + 20)}`));
4581
5558
  for (const u of updates) {
4582
5559
  const nameCol = u.server.slice(0, NAME_W).padEnd(NAME_W);
4583
5560
  const curCol = u.currentVersion.padEnd(VER_W);
4584
5561
  const latCol = u.latestVersion.padEnd(VER_W);
4585
- const statusCol = u.hasUpdate ? pc23.yellow(`Update available${u.updateType ? ` [${u.updateType}]` : ""}`) : pc23.green("Up to date");
5562
+ const statusCol = u.hasUpdate ? pc29.yellow(`Update available${u.updateType ? ` [${u.updateType}]` : ""}`) : pc29.green("Up to date");
4586
5563
  console.log(` ${nameCol} ${curCol} ${latCol} ${statusCol}`);
4587
5564
  }
4588
5565
  console.log();
4589
5566
  }
4590
- var update_default = defineCommand23({
5567
+ var update_default = defineCommand29({
4591
5568
  meta: {
4592
5569
  name: "update",
4593
5570
  description: "Check for and apply updates to installed MCP servers"
@@ -4626,7 +5603,7 @@ var update_default = defineCommand23({
4626
5603
  }
4627
5604
  process.exit(1);
4628
5605
  }
4629
- const spinner5 = p11.spinner();
5606
+ const spinner5 = p12.spinner();
4630
5607
  spinner5.start("Checking versions...");
4631
5608
  let updates;
4632
5609
  try {
@@ -4648,50 +5625,50 @@ var update_default = defineCommand23({
4648
5625
  printTable(updates);
4649
5626
  const outdated = updates.filter((u) => u.hasUpdate);
4650
5627
  if (outdated.length === 0) {
4651
- console.log(pc23.green(" All servers are up to date."));
5628
+ console.log(pc29.green(" All servers are up to date."));
4652
5629
  return;
4653
5630
  }
4654
5631
  if (args.check) {
4655
5632
  console.log(
4656
- pc23.yellow(` ${outdated.length} update(s) available. Run mcpman update to apply.`)
5633
+ pc29.yellow(` ${outdated.length} update(s) available. Run mcpman update to apply.`)
4657
5634
  );
4658
5635
  return;
4659
5636
  }
4660
5637
  if (!args.yes) {
4661
- const confirmed = await p11.confirm({
5638
+ const confirmed = await p12.confirm({
4662
5639
  message: `Apply ${outdated.length} update(s)?`,
4663
5640
  initialValue: true
4664
5641
  });
4665
- if (p11.isCancel(confirmed) || !confirmed) {
4666
- p11.outro("Cancelled.");
5642
+ if (p12.isCancel(confirmed) || !confirmed) {
5643
+ p12.outro("Cancelled.");
4667
5644
  return;
4668
5645
  }
4669
5646
  }
4670
5647
  const clients = await loadClients3();
4671
5648
  let successCount = 0;
4672
5649
  for (const update of outdated) {
4673
- const s = p11.spinner();
5650
+ const s = p12.spinner();
4674
5651
  s.start(`Updating ${update.server}...`);
4675
5652
  const result = await applyServerUpdate(update.server, servers[update.server], clients);
4676
5653
  if (result.success) {
4677
- s.stop(`${pc23.green("\u2713")} ${update.server}: ${result.fromVersion} \u2192 ${result.toVersion}`);
5654
+ s.stop(`${pc29.green("\u2713")} ${update.server}: ${result.fromVersion} \u2192 ${result.toVersion}`);
4678
5655
  successCount++;
4679
5656
  } else {
4680
- s.stop(`${pc23.red("\u2717")} ${update.server}: ${result.error}`);
5657
+ s.stop(`${pc29.red("\u2717")} ${update.server}: ${result.error}`);
4681
5658
  }
4682
5659
  }
4683
5660
  const freshLockfile = readLockfile(resolveLockfilePath());
4684
5661
  const freshUpdates = await checkAllVersions(freshLockfile);
4685
5662
  writeUpdateCache({ lastCheck: (/* @__PURE__ */ new Date()).toISOString(), updates: freshUpdates });
4686
- p11.outro(`${successCount} of ${outdated.length} server(s) updated.`);
5663
+ p12.outro(`${successCount} of ${outdated.length} server(s) updated.`);
4687
5664
  }
4688
5665
  });
4689
5666
 
4690
5667
  // src/commands/upgrade.ts
4691
5668
  import { execSync as execSync2 } from "child_process";
4692
- import { defineCommand as defineCommand24 } from "citty";
4693
- import pc24 from "picocolors";
4694
- var upgrade_default = defineCommand24({
5669
+ import { defineCommand as defineCommand30 } from "citty";
5670
+ import pc30 from "picocolors";
5671
+ var upgrade_default = defineCommand30({
4695
5672
  meta: {
4696
5673
  name: "upgrade",
4697
5674
  description: "Upgrade mcpman to the latest version"
@@ -4704,42 +5681,42 @@ var upgrade_default = defineCommand24({
4704
5681
  }
4705
5682
  },
4706
5683
  async run({ args }) {
4707
- console.log(pc24.dim(` Current version: ${APP_VERSION}`));
5684
+ console.log(pc30.dim(` Current version: ${APP_VERSION}`));
4708
5685
  let latest;
4709
5686
  try {
4710
5687
  latest = execSync2("npm view mcpman version", { encoding: "utf-8", timeout: 15e3 }).trim();
4711
5688
  } catch {
4712
- console.error(pc24.red(" Error: Could not check latest version from npm."));
5689
+ console.error(pc30.red(" Error: Could not check latest version from npm."));
4713
5690
  process.exit(1);
4714
5691
  }
4715
5692
  if (latest === APP_VERSION) {
4716
- console.log(pc24.green(" \u2713 Already on the latest version."));
5693
+ console.log(pc30.green(" \u2713 Already on the latest version."));
4717
5694
  return;
4718
5695
  }
4719
- console.log(pc24.yellow(` Update available: ${APP_VERSION} \u2192 ${latest}`));
5696
+ console.log(pc30.yellow(` Update available: ${APP_VERSION} \u2192 ${latest}`));
4720
5697
  if (args.check) {
4721
- console.log(pc24.dim(" Run mcpman upgrade to install."));
5698
+ console.log(pc30.dim(" Run mcpman upgrade to install."));
4722
5699
  return;
4723
5700
  }
4724
- console.log(pc24.dim(" Installing..."));
5701
+ console.log(pc30.dim(" Installing..."));
4725
5702
  try {
4726
5703
  execSync2(`npm install -g mcpman@${latest}`, { stdio: "inherit", timeout: 6e4 });
4727
- console.log(pc24.green(`
5704
+ console.log(pc30.green(`
4728
5705
  \u2713 Upgraded to mcpman@${latest}`));
4729
5706
  } catch {
4730
- console.error(pc24.red(" Error: Upgrade failed. Try manually: npm install -g mcpman@latest"));
5707
+ console.error(pc30.red(" Error: Upgrade failed. Try manually: npm install -g mcpman@latest"));
4731
5708
  process.exit(1);
4732
5709
  }
4733
5710
  }
4734
5711
  });
4735
5712
 
4736
5713
  // src/commands/watch.ts
4737
- import { defineCommand as defineCommand25 } from "citty";
4738
- import pc25 from "picocolors";
5714
+ import { defineCommand as defineCommand31 } from "citty";
5715
+ import pc31 from "picocolors";
4739
5716
 
4740
5717
  // src/core/file-watcher-service.ts
4741
- import { spawn as spawn5 } from "child_process";
4742
- import fs12 from "fs";
5718
+ import { spawn as spawn7 } from "child_process";
5719
+ import fs15 from "fs";
4743
5720
  var IGNORE_PATTERNS = [
4744
5721
  "node_modules",
4745
5722
  ".git",
@@ -4751,7 +5728,7 @@ var IGNORE_PATTERNS = [
4751
5728
  ".tox"
4752
5729
  ];
4753
5730
  function shouldIgnore(filename) {
4754
- return IGNORE_PATTERNS.some((p12) => filename.includes(p12));
5731
+ return IGNORE_PATTERNS.some((p13) => filename.includes(p13));
4755
5732
  }
4756
5733
  function hasWatchedExtension(filename, extensions) {
4757
5734
  return extensions.some((ext) => filename.endsWith(`.${ext}`));
@@ -4777,7 +5754,7 @@ var ServerWatcher = class {
4777
5754
  `);
4778
5755
  this.spawnChild();
4779
5756
  try {
4780
- this.watcher = fs12.watch(options.watchDir, { recursive: true }, (_event, filename) => {
5757
+ this.watcher = fs15.watch(options.watchDir, { recursive: true }, (_event, filename) => {
4781
5758
  if (!filename) return;
4782
5759
  this.onFileChange(filename);
4783
5760
  });
@@ -4812,7 +5789,7 @@ var ServerWatcher = class {
4812
5789
  process.stdout.write("\x1Bc");
4813
5790
  }
4814
5791
  console.log(` [${timestamp()}] Starting ${serverName}...`);
4815
- this.child = spawn5(command, args, { env, stdio: ["pipe", "pipe", "pipe"] });
5792
+ this.child = spawn7(command, args, { env, stdio: ["pipe", "pipe", "pipe"] });
4816
5793
  this.child.stdout?.on("data", (data) => {
4817
5794
  process.stdout.write(` [stdout] ${data.toString().trimEnd()}
4818
5795
  `);
@@ -4876,7 +5853,7 @@ var ServerWatcher = class {
4876
5853
  // src/commands/watch.ts
4877
5854
  var DEFAULT_EXTENSIONS = ["ts", "js", "json", "py", "mjs", "cjs"];
4878
5855
  var DEFAULT_DEBOUNCE_MS = 300;
4879
- var watch_default = defineCommand25({
5856
+ var watch_default = defineCommand31({
4880
5857
  meta: {
4881
5858
  name: "watch",
4882
5859
  description: "Watch a local MCP server for file changes and auto-restart"
@@ -4915,8 +5892,8 @@ var watch_default = defineCommand25({
4915
5892
  const lockfile = readLockfile();
4916
5893
  const entry = lockfile.servers[serverName];
4917
5894
  if (!entry) {
4918
- console.error(pc25.red(` Error: Server '${serverName}' not found in lockfile.`));
4919
- console.error(pc25.dim(` Run ${pc25.cyan("mcpman link .")} to register a local server.`));
5895
+ console.error(pc31.red(` Error: Server '${serverName}' not found in lockfile.`));
5896
+ console.error(pc31.dim(` Run ${pc31.cyan("mcpman link .")} to register a local server.`));
4920
5897
  process.exit(1);
4921
5898
  }
4922
5899
  let watchDir = args.dir;
@@ -4924,8 +5901,8 @@ var watch_default = defineCommand25({
4924
5901
  if (entry.source === "local" && entry.resolved) {
4925
5902
  watchDir = entry.resolved;
4926
5903
  } else {
4927
- console.error(pc25.red(` Error: Cannot determine watch directory for '${serverName}'.`));
4928
- console.error(pc25.dim(" Use --dir to specify the directory to watch."));
5904
+ console.error(pc31.red(` Error: Cannot determine watch directory for '${serverName}'.`));
5905
+ console.error(pc31.dim(" Use --dir to specify the directory to watch."));
4929
5906
  process.exit(1);
4930
5907
  }
4931
5908
  }
@@ -4971,12 +5948,12 @@ async function loadVaultSecrets4(serverName) {
4971
5948
  }
4972
5949
 
4973
5950
  // src/commands/why.ts
4974
- import { defineCommand as defineCommand26 } from "citty";
4975
- import pc26 from "picocolors";
5951
+ import { defineCommand as defineCommand32 } from "citty";
5952
+ import pc32 from "picocolors";
4976
5953
 
4977
5954
  // src/core/why-service.ts
4978
- import fs13 from "fs";
4979
- import path14 from "path";
5955
+ import fs16 from "fs";
5956
+ import path15 from "path";
4980
5957
  var ALL_CLIENT_TYPES = ["claude-desktop", "cursor", "vscode", "windsurf"];
4981
5958
  async function getServerProvenance(serverName, lockfilePath, profilesDir) {
4982
5959
  const lockfile = readLockfile(lockfilePath);
@@ -5020,7 +5997,7 @@ async function buildClientStatuses(serverName, lockfileClients) {
5020
5997
  }));
5021
5998
  }
5022
5999
  async function findOrphanedClients(serverName) {
5023
- const { getInstalledClients: getInstalledClients2 } = await import("./client-detector-CY7WPF3K.js");
6000
+ const { getInstalledClients: getInstalledClients2 } = await import("./client-detector-NHXBDNMY.js");
5024
6001
  const handlers = await getInstalledClients2();
5025
6002
  const results = [];
5026
6003
  for (const handler of handlers) {
@@ -5036,16 +6013,16 @@ async function findOrphanedClients(serverName) {
5036
6013
  }
5037
6014
  function scanProfiles(serverName, profilesDir) {
5038
6015
  const found = [];
5039
- if (!fs13.existsSync(profilesDir)) return found;
6016
+ if (!fs16.existsSync(profilesDir)) return found;
5040
6017
  let files;
5041
6018
  try {
5042
- files = fs13.readdirSync(profilesDir).filter((f) => f.endsWith(".json"));
6019
+ files = fs16.readdirSync(profilesDir).filter((f) => f.endsWith(".json"));
5043
6020
  } catch {
5044
6021
  return found;
5045
6022
  }
5046
6023
  for (const file of files) {
5047
6024
  try {
5048
- const raw = fs13.readFileSync(path14.join(profilesDir, file), "utf-8");
6025
+ const raw = fs16.readFileSync(path15.join(profilesDir, file), "utf-8");
5049
6026
  const profile = JSON.parse(raw);
5050
6027
  if (serverName in (profile.servers ?? {})) {
5051
6028
  found.push(profile.name ?? file.replace(".json", ""));
@@ -5074,8 +6051,8 @@ function formatWhyOutput(result) {
5074
6051
  if (result.profiles.length > 0) {
5075
6052
  lines.push("");
5076
6053
  lines.push(" Profiles:");
5077
- for (const p12 of result.profiles) {
5078
- lines.push(` ${p12}`);
6054
+ for (const p13 of result.profiles) {
6055
+ lines.push(` ${p13}`);
5079
6056
  }
5080
6057
  }
5081
6058
  if (result.envVars.length > 0) {
@@ -5089,7 +6066,7 @@ function formatWhyOutput(result) {
5089
6066
  }
5090
6067
 
5091
6068
  // src/commands/why.ts
5092
- var why_default = defineCommand26({
6069
+ var why_default = defineCommand32({
5093
6070
  meta: {
5094
6071
  name: "why",
5095
6072
  description: "Show why a server is installed (provenance, clients, profiles)"
@@ -5111,15 +6088,15 @@ var why_default = defineCommand26({
5111
6088
  const asJson = args.json;
5112
6089
  const result = await getServerProvenance(serverName);
5113
6090
  if (!result) {
5114
- console.error(pc26.red(` Server '${serverName}' not found in lockfile or any client config.`));
5115
- console.error(pc26.dim(` Run ${pc26.cyan("mcpman list")} to see installed servers.`));
6091
+ console.error(pc32.red(` Server '${serverName}' not found in lockfile or any client config.`));
6092
+ console.error(pc32.dim(` Run ${pc32.cyan("mcpman list")} to see installed servers.`));
5116
6093
  process.exit(1);
5117
6094
  }
5118
6095
  if (result.orphaned) {
5119
- console.log(pc26.yellow(`
6096
+ console.log(pc32.yellow(`
5120
6097
  Server '${serverName}' is orphaned:`));
5121
- console.log(pc26.dim(" Found in client config(s) but not in lockfile."));
5122
- console.log(pc26.dim(` Run ${pc26.cyan("mcpman sync --remove")} to clean up.
6098
+ console.log(pc32.dim(" Found in client config(s) but not in lockfile."));
6099
+ console.log(pc32.dim(` Run ${pc32.cyan("mcpman sync --remove")} to clean up.
5123
6100
  `));
5124
6101
  const registeredClients = result.clients.filter((c) => c.registered).map((c) => c.type);
5125
6102
  if (registeredClients.length > 0) {
@@ -5142,7 +6119,7 @@ process.on("SIGINT", () => {
5142
6119
  console.log("\nAborted.");
5143
6120
  process.exit(130);
5144
6121
  });
5145
- var main = defineCommand27({
6122
+ var main = defineCommand33({
5146
6123
  meta: {
5147
6124
  name: APP_NAME,
5148
6125
  version: APP_VERSION,
@@ -5174,7 +6151,13 @@ var main = defineCommand27({
5174
6151
  watch: watch_default,
5175
6152
  registry: registry_default,
5176
6153
  completions: completions_default,
5177
- why: why_default
6154
+ why: why_default,
6155
+ env: env_default,
6156
+ bench: bench_default,
6157
+ diff: diff_default,
6158
+ group: group_default,
6159
+ pin: pin_default,
6160
+ rollback: rollback_default
5178
6161
  }
5179
6162
  });
5180
6163
  runMain(main);