mcpman 0.7.0 → 0.9.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,32 @@ 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-6GGMDJQE.js";
11
14
  import {
12
15
  computeTrustScore
13
16
  } from "./chunk-RGKHLY5G.js";
14
17
  import {
18
+ getInstalledClients
19
+ } from "./chunk-CC7ICP7U.js";
20
+ import {
21
+ getAliasesFile,
15
22
  getConfigPath,
16
- getInstalledClients,
23
+ getEnvDir,
24
+ getGroupsFile,
25
+ getHistoryFile,
26
+ getNotifyFile,
27
+ getPinsFile,
17
28
  getPluginDir,
18
- getProfilesDir
19
- } from "./chunk-NS6HV723.js";
29
+ getProfilesDir,
30
+ getTemplatesDir,
31
+ resolveConfigPath
32
+ } from "./chunk-DSCBWQ3W.js";
20
33
  import {
21
34
  getMasterPassword,
22
35
  getSecretsForServer,
@@ -29,34 +42,136 @@ import {
29
42
  } from "./chunk-6X6Q6UZC.js";
30
43
 
31
44
  // src/index.ts
32
- import { defineCommand as defineCommand27, runMain } from "citty";
45
+ import { defineCommand as defineCommand39, runMain } from "citty";
46
+
47
+ // src/commands/alias.ts
48
+ import { defineCommand } from "citty";
49
+ import pc from "picocolors";
50
+
51
+ // src/core/alias-manager.ts
52
+ import fs from "fs";
53
+ function readAliases(file) {
54
+ const target = file ?? getAliasesFile();
55
+ if (!fs.existsSync(target)) return {};
56
+ try {
57
+ return JSON.parse(fs.readFileSync(target, "utf-8"));
58
+ } catch {
59
+ return {};
60
+ }
61
+ }
62
+ function writeAliases(store, file) {
63
+ const target = file ?? getAliasesFile();
64
+ const dir = target.substring(0, target.lastIndexOf("/"));
65
+ if (dir && !fs.existsSync(dir)) {
66
+ fs.mkdirSync(dir, { recursive: true });
67
+ }
68
+ fs.writeFileSync(target, JSON.stringify(store, null, 2), "utf-8");
69
+ }
70
+ function addAlias(name, command, file) {
71
+ const store = readAliases(file);
72
+ store[name] = command;
73
+ writeAliases(store, file);
74
+ }
75
+ function removeAlias(name, file) {
76
+ const store = readAliases(file);
77
+ if (!(name in store)) return;
78
+ delete store[name];
79
+ writeAliases(store, file);
80
+ }
81
+ function listAliases(file) {
82
+ const store = readAliases(file);
83
+ return Object.entries(store).sort(([a], [b]) => a.localeCompare(b)).map(([name, command]) => ({ name, command }));
84
+ }
85
+ function aliasExists(name, file) {
86
+ return name in readAliases(file);
87
+ }
88
+
89
+ // src/commands/alias.ts
90
+ var addCmd = defineCommand({
91
+ meta: { name: "add", description: "Add a command alias" },
92
+ args: {
93
+ name: { type: "positional", description: "Alias name", required: true },
94
+ command: { type: "positional", description: "Full command string to alias", required: true }
95
+ },
96
+ run({ args }) {
97
+ addAlias(args.name, args.command);
98
+ console.log(
99
+ `${pc.green("\u2713")} Alias ${pc.cyan(pc.bold(args.name))} \u2192 ${pc.dim(args.command)} saved`
100
+ );
101
+ }
102
+ });
103
+ var removeCmd = defineCommand({
104
+ meta: { name: "remove", description: "Remove an alias" },
105
+ args: {
106
+ name: { type: "positional", description: "Alias name to remove", required: true }
107
+ },
108
+ run({ args }) {
109
+ if (!aliasExists(args.name)) {
110
+ console.error(`${pc.red("\u2717")} Alias "${args.name}" does not exist.`);
111
+ process.exit(1);
112
+ }
113
+ removeAlias(args.name);
114
+ console.log(`${pc.green("\u2713")} Alias ${pc.cyan(args.name)} removed`);
115
+ }
116
+ });
117
+ var listCmd = defineCommand({
118
+ meta: { name: "list", description: "List all aliases" },
119
+ args: {},
120
+ run() {
121
+ const aliases = listAliases();
122
+ if (aliases.length === 0) {
123
+ console.log(pc.dim("\n No aliases defined. Use `mcpman alias add <name> <command>`.\n"));
124
+ return;
125
+ }
126
+ const nameW = Math.max(4, ...aliases.map((a) => a.name.length));
127
+ console.log(pc.bold("\n mcpman aliases\n"));
128
+ console.log(pc.dim(` ${"\u2500".repeat(nameW + 30)}`));
129
+ for (const { name, command } of aliases) {
130
+ const padded = name.padEnd(nameW);
131
+ console.log(` ${pc.cyan(pc.bold(padded))} ${pc.dim("\u2192")} ${command}`);
132
+ }
133
+ console.log(pc.dim(` ${"\u2500".repeat(nameW + 30)}
134
+ `));
135
+ }
136
+ });
137
+ var alias_default = defineCommand({
138
+ meta: {
139
+ name: "alias",
140
+ description: "Manage command aliases"
141
+ },
142
+ subCommands: {
143
+ add: addCmd,
144
+ remove: removeCmd,
145
+ list: listCmd
146
+ }
147
+ });
33
148
 
34
149
  // src/commands/audit.ts
35
150
  import * as p from "@clack/prompts";
36
- import { defineCommand } from "citty";
151
+ import { defineCommand as defineCommand2 } from "citty";
37
152
  import { createSpinner } from "nanospinner";
38
- import pc from "picocolors";
153
+ import pc2 from "picocolors";
39
154
 
40
155
  // src/core/security-scanner.ts
41
- import fs from "fs";
156
+ import fs2 from "fs";
42
157
  import os from "os";
43
158
  import path from "path";
44
159
  var CACHE_PATH = path.join(os.homedir(), ".mcpman", ".audit-cache.json");
45
160
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
46
161
  function readCache() {
47
162
  try {
48
- if (!fs.existsSync(CACHE_PATH)) return {};
49
- return JSON.parse(fs.readFileSync(CACHE_PATH, "utf-8"));
163
+ if (!fs2.existsSync(CACHE_PATH)) return {};
164
+ return JSON.parse(fs2.readFileSync(CACHE_PATH, "utf-8"));
50
165
  } catch {
51
166
  return {};
52
167
  }
53
168
  }
54
169
  function writeCache(cache) {
55
170
  const dir = path.dirname(CACHE_PATH);
56
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
171
+ if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
57
172
  const tmp = `${CACHE_PATH}.tmp`;
58
- fs.writeFileSync(tmp, JSON.stringify(cache, null, 2), "utf-8");
59
- fs.renameSync(tmp, CACHE_PATH);
173
+ fs2.writeFileSync(tmp, JSON.stringify(cache, null, 2), "utf-8");
174
+ fs2.renameSync(tmp, CACHE_PATH);
60
175
  }
61
176
  function getCachedReport(name, version) {
62
177
  const cache = readCache();
@@ -167,11 +282,11 @@ async function scanAllServers(servers, concurrency = 3) {
167
282
  const results = [];
168
283
  const executing = /* @__PURE__ */ new Set();
169
284
  for (const [name, entry] of entries) {
170
- const p12 = scanServer(name, entry).then((r) => {
285
+ const p13 = scanServer(name, entry).then((r) => {
171
286
  results.push(r);
172
- executing.delete(p12);
287
+ executing.delete(p13);
173
288
  });
174
- executing.add(p12);
289
+ executing.add(p13);
175
290
  if (executing.size >= concurrency) await Promise.race(executing);
176
291
  }
177
292
  await Promise.all(executing);
@@ -286,12 +401,12 @@ async function resolveFromGitHub(githubUrl) {
286
401
 
287
402
  // src/core/plugin-loader.ts
288
403
  import { execSync } from "child_process";
289
- import fs3 from "fs";
404
+ import fs4 from "fs";
290
405
  import { createRequire } from "module";
291
406
  import path3 from "path";
292
407
 
293
408
  // src/core/config-service.ts
294
- import fs2 from "fs";
409
+ import fs3 from "fs";
295
410
  import path2 from "path";
296
411
  var VALID_KEYS = /* @__PURE__ */ new Set([
297
412
  "defaultClient",
@@ -302,7 +417,7 @@ var VALID_KEYS = /* @__PURE__ */ new Set([
302
417
  ]);
303
418
  function readConfig(configPath = getConfigPath()) {
304
419
  try {
305
- const raw = fs2.readFileSync(configPath, "utf-8");
420
+ const raw = fs3.readFileSync(configPath, "utf-8");
306
421
  const parsed = JSON.parse(raw);
307
422
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
308
423
  return {};
@@ -314,10 +429,10 @@ function readConfig(configPath = getConfigPath()) {
314
429
  }
315
430
  function writeConfig(data, configPath = getConfigPath()) {
316
431
  const dir = path2.dirname(configPath);
317
- fs2.mkdirSync(dir, { recursive: true });
432
+ fs3.mkdirSync(dir, { recursive: true });
318
433
  const tmp = `${configPath}.tmp`;
319
- fs2.writeFileSync(tmp, JSON.stringify(data, null, 2), { encoding: "utf-8" });
320
- fs2.renameSync(tmp, configPath);
434
+ fs3.writeFileSync(tmp, JSON.stringify(data, null, 2), { encoding: "utf-8" });
435
+ fs3.renameSync(tmp, configPath);
321
436
  }
322
437
  function getConfigValue(key, configPath = getConfigPath()) {
323
438
  const data = readConfig(configPath);
@@ -336,8 +451,8 @@ function setConfigValue(key, value, configPath = getConfigPath()) {
336
451
  // src/core/plugin-loader.ts
337
452
  function isValidPlugin(obj) {
338
453
  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";
454
+ const p13 = obj;
455
+ return typeof p13.name === "string" && typeof p13.prefix === "string" && typeof p13.resolve === "function";
341
456
  }
342
457
  function loadPlugin(pkg, pluginDir = getPluginDir()) {
343
458
  try {
@@ -362,10 +477,10 @@ function loadAllPlugins(pluginDir = getPluginDir()) {
362
477
  return plugins;
363
478
  }
364
479
  function installPluginPackage(name, pluginDir = getPluginDir()) {
365
- fs3.mkdirSync(pluginDir, { recursive: true });
480
+ fs4.mkdirSync(pluginDir, { recursive: true });
366
481
  const pkgJsonPath = path3.join(pluginDir, "package.json");
367
- if (!fs3.existsSync(pkgJsonPath)) {
368
- fs3.writeFileSync(
482
+ if (!fs4.existsSync(pkgJsonPath)) {
483
+ fs4.writeFileSync(
369
484
  pkgJsonPath,
370
485
  JSON.stringify({ name: "mcpman-plugins", private: true }, null, 2)
371
486
  );
@@ -391,7 +506,7 @@ function removePluginPackage(name, pluginDir = getPluginDir()) {
391
506
  }
392
507
  const config = readConfig();
393
508
  const plugins = config.plugins ?? [];
394
- config.plugins = plugins.filter((p12) => p12 !== name);
509
+ config.plugins = plugins.filter((p13) => p13 !== name);
395
510
  writeConfig(config);
396
511
  }
397
512
  function listPluginPackages() {
@@ -440,7 +555,7 @@ async function resolveServer(input) {
440
555
  if (source.type.startsWith("plugin:")) {
441
556
  const pluginName = source.type.slice(7);
442
557
  const plugins = loadAllPlugins();
443
- const plugin = plugins.find((p12) => p12.name === pluginName);
558
+ const plugin = plugins.find((p13) => p13.name === pluginName);
444
559
  if (plugin) {
445
560
  const resolved = await plugin.resolve(source.input);
446
561
  return resolved;
@@ -494,6 +609,47 @@ async function applyServerUpdate(serverName, lockEntry, clients) {
494
609
  }
495
610
  }
496
611
 
612
+ // src/core/pin-service.ts
613
+ import fs5 from "fs";
614
+ function readPins(file) {
615
+ const target = file ?? getPinsFile();
616
+ if (!fs5.existsSync(target)) return {};
617
+ try {
618
+ return JSON.parse(fs5.readFileSync(target, "utf-8"));
619
+ } catch {
620
+ return {};
621
+ }
622
+ }
623
+ function writePins(store, file) {
624
+ const target = file ?? getPinsFile();
625
+ const dir = target.slice(0, target.lastIndexOf("/"));
626
+ if (dir && !fs5.existsSync(dir)) {
627
+ fs5.mkdirSync(dir, { recursive: true });
628
+ }
629
+ fs5.writeFileSync(target, JSON.stringify(store, null, 2), "utf-8");
630
+ }
631
+ function pinServer(server, version, file) {
632
+ const store = readPins(file);
633
+ store[server] = version;
634
+ writePins(store, file);
635
+ }
636
+ function unpinServer(server, file) {
637
+ const store = readPins(file);
638
+ if (!(server in store)) return;
639
+ delete store[server];
640
+ writePins(store, file);
641
+ }
642
+ function getPinnedVersion(server, file) {
643
+ return readPins(file)[server] ?? null;
644
+ }
645
+ function isPinned(server, file) {
646
+ return server in readPins(file);
647
+ }
648
+ function listPins(file) {
649
+ const store = readPins(file);
650
+ return Object.entries(store).sort(([a], [b]) => a.localeCompare(b)).map(([server, version]) => ({ server, version }));
651
+ }
652
+
497
653
  // src/core/version-checker.ts
498
654
  function compareVersions(a, b) {
499
655
  const aParts = a.replace(/^v/, "").split(".").map(Number);
@@ -595,11 +751,12 @@ async function checkAllVersions(lockfile) {
595
751
  const results = [];
596
752
  const executing = /* @__PURE__ */ new Set();
597
753
  for (const [name, entry] of entries) {
598
- const p12 = checkVersion(name, entry).then((r) => {
754
+ if (isPinned(name)) continue;
755
+ const p13 = checkVersion(name, entry).then((r) => {
599
756
  results.push(r);
600
- executing.delete(p12);
757
+ executing.delete(p13);
601
758
  });
602
- executing.add(p12);
759
+ executing.add(p13);
603
760
  if (executing.size >= 5) {
604
761
  await Promise.race(executing);
605
762
  }
@@ -611,11 +768,11 @@ async function checkAllVersions(lockfile) {
611
768
  // src/commands/audit.ts
612
769
  function colorRisk(level, score) {
613
770
  const label = score !== null ? `${score}/100 (${level})` : level;
614
- if (level === "LOW") return pc.green(label);
615
- if (level === "MEDIUM") return pc.yellow(label);
616
- if (level === "HIGH") return pc.red(label);
617
- if (level === "CRITICAL") return pc.bold(pc.red(label));
618
- return pc.dim(label);
771
+ if (level === "LOW") return pc2.green(label);
772
+ if (level === "MEDIUM") return pc2.yellow(label);
773
+ if (level === "HIGH") return pc2.red(label);
774
+ if (level === "CRITICAL") return pc2.bold(pc2.red(label));
775
+ return pc2.dim(label);
619
776
  }
620
777
  function daysAgo(isoDate) {
621
778
  const days = Math.floor((Date.now() - new Date(isoDate).getTime()) / 864e5);
@@ -626,20 +783,20 @@ function daysAgo(isoDate) {
626
783
  function countVulns(vulns) {
627
784
  const c = { critical: 0, high: 0, moderate: 0, low: 0 };
628
785
  for (const v of vulns) c[v.severity]++;
629
- if (vulns.length === 0) return pc.green("none");
786
+ if (vulns.length === 0) return pc2.green("none");
630
787
  const parts = [];
631
- if (c.critical) parts.push(pc.bold(pc.red(`${c.critical} critical`)));
632
- if (c.high) parts.push(pc.red(`${c.high} high`));
633
- if (c.moderate) parts.push(pc.yellow(`${c.moderate} moderate`));
634
- if (c.low) parts.push(pc.dim(`${c.low} low`));
788
+ if (c.critical) parts.push(pc2.bold(pc2.red(`${c.critical} critical`)));
789
+ if (c.high) parts.push(pc2.red(`${c.high} high`));
790
+ if (c.moderate) parts.push(pc2.yellow(`${c.moderate} moderate`));
791
+ if (c.low) parts.push(pc2.dim(`${c.low} low`));
635
792
  return parts.join(", ");
636
793
  }
637
794
  function printReport(report) {
638
795
  const riskColored = colorRisk(report.riskLevel, report.score);
639
- const icon = report.riskLevel === "LOW" ? pc.green("\u25CF") : report.riskLevel === "MEDIUM" ? pc.yellow("\u25CF") : report.riskLevel === "UNKNOWN" ? pc.dim("\u25CB") : pc.red("\u25CF");
640
- console.log(` ${icon} ${pc.bold(report.server)} Score: ${riskColored}`);
796
+ const icon = report.riskLevel === "LOW" ? pc2.green("\u25CF") : report.riskLevel === "MEDIUM" ? pc2.yellow("\u25CF") : report.riskLevel === "UNKNOWN" ? pc2.dim("\u25CB") : pc2.red("\u25CF");
797
+ console.log(` ${icon} ${pc2.bold(report.server)} Score: ${riskColored}`);
641
798
  if (report.source !== "npm") {
642
- console.log(` ${pc.dim("Non-npm source \u2014 security data unavailable")}`);
799
+ console.log(` ${pc2.dim("Non-npm source \u2014 security data unavailable")}`);
643
800
  console.log();
644
801
  return;
645
802
  }
@@ -647,20 +804,20 @@ function printReport(report) {
647
804
  const { weeklyDownloads, packageAge, lastPublish, maintainerCount, deprecated } = report.metadata;
648
805
  const dlStr = weeklyDownloads.toLocaleString();
649
806
  console.log(
650
- ` ${pc.dim("Downloads:")} ${dlStr}/week ${pc.dim("|")} ${pc.dim("Age:")} ${packageAge}d ${pc.dim("|")} ${pc.dim("Last publish:")} ${daysAgo(lastPublish)} ${pc.dim("|")} ${pc.dim("Maintainers:")} ${maintainerCount}${deprecated ? pc.red(" [DEPRECATED]") : ""}`
807
+ ` ${pc2.dim("Downloads:")} ${dlStr}/week ${pc2.dim("|")} ${pc2.dim("Age:")} ${packageAge}d ${pc2.dim("|")} ${pc2.dim("Last publish:")} ${daysAgo(lastPublish)} ${pc2.dim("|")} ${pc2.dim("Maintainers:")} ${maintainerCount}${deprecated ? pc2.red(" [DEPRECATED]") : ""}`
651
808
  );
652
809
  }
653
- console.log(` ${pc.dim("Vulnerabilities:")} ${countVulns(report.vulnerabilities)}`);
810
+ console.log(` ${pc2.dim("Vulnerabilities:")} ${countVulns(report.vulnerabilities)}`);
654
811
  if (report.vulnerabilities.length > 0) {
655
812
  for (const v of report.vulnerabilities) {
656
- const sevColor = v.severity === "critical" || v.severity === "high" ? pc.red : pc.yellow;
657
- const url = v.url ? pc.dim(` ${v.url}`) : "";
813
+ const sevColor = v.severity === "critical" || v.severity === "high" ? pc2.red : pc2.yellow;
814
+ const url = v.url ? pc2.dim(` ${v.url}`) : "";
658
815
  console.log(` ${sevColor("\u25B8")} [${v.severity}] ${v.title}${url}`);
659
816
  }
660
817
  }
661
818
  console.log();
662
819
  }
663
- var audit_default = defineCommand({
820
+ var audit_default = defineCommand2({
664
821
  meta: {
665
822
  name: "audit",
666
823
  description: "Scan installed MCP servers for security vulnerabilities and trust scores"
@@ -692,14 +849,14 @@ var audit_default = defineCommand({
692
849
  const { servers } = lockfile;
693
850
  if (Object.keys(servers).length === 0) {
694
851
  console.log(
695
- pc.dim("\n No MCP servers installed. Run mcpman install <server> to get started.\n")
852
+ pc2.dim("\n No MCP servers installed. Run mcpman install <server> to get started.\n")
696
853
  );
697
854
  return;
698
855
  }
699
856
  const targets = {};
700
857
  if (args.server) {
701
858
  if (!servers[args.server]) {
702
- console.error(pc.red(`
859
+ console.error(pc2.red(`
703
860
  Server "${args.server}" not found in lockfile.
704
861
  `));
705
862
  process.exit(1);
@@ -714,7 +871,7 @@ var audit_default = defineCommand({
714
871
  reports = args.server ? [await scanServer(args.server, targets[args.server])] : await scanAllServers(targets);
715
872
  } catch (err) {
716
873
  spinner5.error({ text: "Scan failed" });
717
- console.error(pc.red(String(err)));
874
+ console.error(pc2.red(String(err)));
718
875
  process.exit(1);
719
876
  }
720
877
  spinner5.success({ text: `Scanned ${reports.length} server(s)` });
@@ -722,23 +879,23 @@ var audit_default = defineCommand({
722
879
  console.log(JSON.stringify(reports, null, 2));
723
880
  return;
724
881
  }
725
- console.log(pc.bold("\n mcpman audit\n"));
726
- console.log(pc.dim(` ${"\u2500".repeat(60)}`));
882
+ console.log(pc2.bold("\n mcpman audit\n"));
883
+ console.log(pc2.dim(` ${"\u2500".repeat(60)}`));
727
884
  for (const report of reports) {
728
885
  printReport(report);
729
886
  }
730
- console.log(pc.dim(` ${"\u2500".repeat(60)}`));
887
+ console.log(pc2.dim(` ${"\u2500".repeat(60)}`));
731
888
  const withIssues = reports.filter((r) => r.riskLevel !== "LOW" && r.riskLevel !== "UNKNOWN");
732
889
  const npmReports = reports.filter((r) => r.source === "npm");
733
890
  const parts = [];
734
891
  parts.push(`${reports.length} server(s) scanned`);
735
892
  if (npmReports.length < reports.length) {
736
- parts.push(pc.dim(`${reports.length - npmReports.length} non-npm (unverified)`));
893
+ parts.push(pc2.dim(`${reports.length - npmReports.length} non-npm (unverified)`));
737
894
  }
738
895
  if (withIssues.length > 0) {
739
- parts.push(pc.yellow(`${withIssues.length} with issues`));
896
+ parts.push(pc2.yellow(`${withIssues.length} with issues`));
740
897
  } else {
741
- parts.push(pc.green("all clear"));
898
+ parts.push(pc2.green("all clear"));
742
899
  }
743
900
  console.log(`
744
901
  Summary: ${parts.join(" | ")}
@@ -750,7 +907,7 @@ var audit_default = defineCommand({
750
907
  });
751
908
  async function loadClients() {
752
909
  try {
753
- const mod = await import("./client-detector-CY7WPF3K.js");
910
+ const mod = await import("./client-detector-O2HN4MUB.js");
754
911
  return mod.getInstalledClients();
755
912
  } catch {
756
913
  return [];
@@ -760,14 +917,14 @@ async function runAuditFix(reports, servers, skipConfirm) {
760
917
  const npmWithVulns = reports.filter((r) => r.vulnerabilities.length > 0 && r.source === "npm");
761
918
  const nonNpmWithVulns = reports.filter((r) => r.vulnerabilities.length > 0 && r.source !== "npm");
762
919
  if (nonNpmWithVulns.length > 0) {
763
- console.log(pc.yellow(" Non-npm servers require manual update:"));
920
+ console.log(pc2.yellow(" Non-npm servers require manual update:"));
764
921
  for (const r of nonNpmWithVulns) {
765
- console.log(` ${pc.dim("\u2192")} ${r.server} (${r.source})`);
922
+ console.log(` ${pc2.dim("\u2192")} ${r.server} (${r.source})`);
766
923
  }
767
924
  console.log();
768
925
  }
769
926
  if (npmWithVulns.length === 0) {
770
- console.log(pc.green(" No fixable vulnerabilities found.\n"));
927
+ console.log(pc2.green(" No fixable vulnerabilities found.\n"));
771
928
  return;
772
929
  }
773
930
  const versionSpinner = createSpinner("Checking for available updates...").start();
@@ -778,20 +935,20 @@ async function runAuditFix(reports, servers, skipConfirm) {
778
935
  const updatable = versionChecks.filter((u) => u.hasUpdate);
779
936
  if (updatable.length === 0) {
780
937
  console.log(
781
- pc.yellow(
938
+ pc2.yellow(
782
939
  " Vulnerable servers have no newer versions available yet.\n Allow time for registry to publish fixes.\n"
783
940
  )
784
941
  );
785
942
  return;
786
943
  }
787
944
  console.log(
788
- pc.bold(`
945
+ pc2.bold(`
789
946
  ${updatable.length} server(s) can be updated to fix vulnerabilities:
790
947
  `)
791
948
  );
792
949
  for (const u of updatable) {
793
950
  console.log(
794
- ` ${pc.cyan("\u2192")} ${u.server} ${pc.dim(u.currentVersion)} \u2192 ${pc.green(u.latestVersion)}`
951
+ ` ${pc2.cyan("\u2192")} ${u.server} ${pc2.dim(u.currentVersion)} \u2192 ${pc2.green(u.latestVersion)}`
795
952
  );
796
953
  }
797
954
  console.log();
@@ -813,11 +970,11 @@ async function runAuditFix(reports, servers, skipConfirm) {
813
970
  const result = await applyServerUpdate(u.server, servers[u.server], clients);
814
971
  if (result.success) {
815
972
  s.success({
816
- text: `${pc.green("\u2713")} ${u.server}: ${result.fromVersion} \u2192 ${result.toVersion}`
973
+ text: `${pc2.green("\u2713")} ${u.server}: ${result.fromVersion} \u2192 ${result.toVersion}`
817
974
  });
818
975
  successCount++;
819
976
  } else {
820
- s.error({ text: `${pc.red("\u2717")} ${u.server}: ${result.error}` });
977
+ s.error({ text: `${pc2.red("\u2717")} ${u.server}: ${result.error}` });
821
978
  }
822
979
  results.push({
823
980
  server: u.server,
@@ -836,14 +993,14 @@ async function runAuditFix(reports, servers, skipConfirm) {
836
993
  updatedNames.map((name) => scanServer(name, freshLockfile.servers[name]))
837
994
  );
838
995
  rescanSpinner.success({ text: "Re-scan complete" });
839
- console.log(pc.bold("\n Before / After:\n"));
996
+ console.log(pc2.bold("\n Before / After:\n"));
840
997
  for (const after of afterReports) {
841
998
  const before = reports.find((r) => r.server === after.server);
842
999
  const beforeVulns = before?.vulnerabilities.length ?? 0;
843
1000
  const afterVulns = after.vulnerabilities.length;
844
- const improved = afterVulns < beforeVulns ? pc.green("improved") : pc.yellow("unchanged");
1001
+ const improved = afterVulns < beforeVulns ? pc2.green("improved") : pc2.yellow("unchanged");
845
1002
  console.log(
846
- ` ${pc.bold(after.server)} vulns: ${beforeVulns} \u2192 ${afterVulns} [${improved}]`
1003
+ ` ${pc2.bold(after.server)} vulns: ${beforeVulns} \u2192 ${afterVulns} [${improved}]`
847
1004
  );
848
1005
  }
849
1006
  console.log();
@@ -853,12 +1010,204 @@ async function runAuditFix(reports, servers, skipConfirm) {
853
1010
  `);
854
1011
  }
855
1012
 
1013
+ // src/commands/bench.ts
1014
+ import { defineCommand as defineCommand3 } from "citty";
1015
+ import pc3 from "picocolors";
1016
+
1017
+ // src/core/bench-service.ts
1018
+ import { spawn } from "child_process";
1019
+ var DEFAULT_TIMEOUT_MS = 1e4;
1020
+ function measureOneRun(command, args, env, timeoutMs) {
1021
+ return new Promise((resolve, reject) => {
1022
+ const start = Date.now();
1023
+ let settled = false;
1024
+ let stdout = "";
1025
+ const child = spawn(command, args, {
1026
+ env: { ...process.env, ...env },
1027
+ stdio: ["pipe", "pipe", "pipe"]
1028
+ });
1029
+ const finish = (err) => {
1030
+ if (settled) return;
1031
+ settled = true;
1032
+ try {
1033
+ child.kill("SIGTERM");
1034
+ } catch {
1035
+ }
1036
+ if (err) reject(err);
1037
+ else resolve(Date.now() - start);
1038
+ };
1039
+ const timer = setTimeout(() => {
1040
+ finish(new Error("Timeout waiting for initialize response"));
1041
+ }, timeoutMs);
1042
+ child.on("error", (err) => {
1043
+ clearTimeout(timer);
1044
+ finish(err);
1045
+ });
1046
+ child.on("exit", (code) => {
1047
+ clearTimeout(timer);
1048
+ if (!settled) finish(new Error(`Process exited with code ${code}`));
1049
+ });
1050
+ child.stdout?.on("data", (chunk) => {
1051
+ stdout += chunk.toString();
1052
+ for (const line of stdout.split("\n")) {
1053
+ const trimmed = line.trim();
1054
+ if (!trimmed) continue;
1055
+ try {
1056
+ const msg = JSON.parse(trimmed);
1057
+ if (msg.jsonrpc === "2.0" && msg.id === 1) {
1058
+ clearTimeout(timer);
1059
+ finish();
1060
+ }
1061
+ } catch {
1062
+ }
1063
+ }
1064
+ });
1065
+ const initReq = JSON.stringify({
1066
+ jsonrpc: "2.0",
1067
+ id: 1,
1068
+ method: "initialize",
1069
+ params: {
1070
+ protocolVersion: "2024-11-05",
1071
+ capabilities: {},
1072
+ clientInfo: { name: "mcpman-bench", version: "0.8.0" }
1073
+ }
1074
+ });
1075
+ child.stdin?.write(`${initReq}
1076
+ `);
1077
+ });
1078
+ }
1079
+ function percentile(sorted, p13) {
1080
+ if (sorted.length === 0) return 0;
1081
+ const idx = Math.ceil(p13 / 100 * sorted.length) - 1;
1082
+ return sorted[Math.max(0, idx)];
1083
+ }
1084
+ async function benchServer(command, args, env, runs = 5, timeoutMs = DEFAULT_TIMEOUT_MS) {
1085
+ const allTimes = [];
1086
+ for (let i = 0; i < runs; i++) {
1087
+ try {
1088
+ const ms = await measureOneRun(command, args, env, timeoutMs);
1089
+ allTimes.push(ms);
1090
+ } catch (err) {
1091
+ return {
1092
+ runs,
1093
+ min: 0,
1094
+ max: 0,
1095
+ avg: 0,
1096
+ p50: 0,
1097
+ p95: 0,
1098
+ allTimes,
1099
+ error: String(err instanceof Error ? err.message : err)
1100
+ };
1101
+ }
1102
+ }
1103
+ const sorted = [...allTimes].sort((a, b) => a - b);
1104
+ const sum = allTimes.reduce((a, b) => a + b, 0);
1105
+ return {
1106
+ runs,
1107
+ min: sorted[0] ?? 0,
1108
+ max: sorted[sorted.length - 1] ?? 0,
1109
+ avg: Math.round(sum / allTimes.length),
1110
+ p50: percentile(sorted, 50),
1111
+ p95: percentile(sorted, 95),
1112
+ allTimes
1113
+ };
1114
+ }
1115
+
1116
+ // src/commands/bench.ts
1117
+ var bench_default = defineCommand3({
1118
+ meta: {
1119
+ name: "bench",
1120
+ description: "Benchmark MCP server latency (JSON-RPC initialize)"
1121
+ },
1122
+ args: {
1123
+ server: {
1124
+ type: "positional",
1125
+ description: "Server name as stored in lockfile",
1126
+ required: true
1127
+ },
1128
+ runs: {
1129
+ type: "string",
1130
+ description: "Number of benchmark runs (default: 5)",
1131
+ default: "5"
1132
+ },
1133
+ timeout: {
1134
+ type: "string",
1135
+ description: "Per-run timeout in ms (default: 10000)",
1136
+ default: "10000"
1137
+ },
1138
+ json: {
1139
+ type: "boolean",
1140
+ description: "Output results as JSON",
1141
+ default: false
1142
+ }
1143
+ },
1144
+ async run({ args }) {
1145
+ const lockfile = readLockfile();
1146
+ const entry = lockfile.servers[args.server];
1147
+ if (!entry) {
1148
+ console.error(`${pc3.red("\u2717")} Server "${args.server}" not found in lockfile.`);
1149
+ console.error(pc3.dim("Run `mcpman list` to see installed servers."));
1150
+ process.exit(1);
1151
+ }
1152
+ const runs = Math.max(1, Number.parseInt(args.runs, 10) || 5);
1153
+ const timeoutMs = Math.max(1e3, Number.parseInt(args.timeout, 10) || 1e4);
1154
+ if (!args.json) {
1155
+ console.log(`
1156
+ ${pc3.cyan("mcpman bench")} \u2014 ${pc3.bold(args.server)}`);
1157
+ console.log(pc3.dim(` Command: ${entry.command} ${(entry.args ?? []).join(" ")}`));
1158
+ console.log(pc3.dim(` Runs: ${runs} Timeout: ${timeoutMs}ms
1159
+ `));
1160
+ process.stdout.write(pc3.dim(" Running"));
1161
+ }
1162
+ const env = {};
1163
+ for (const ev of entry.envVars ?? []) {
1164
+ const idx = ev.indexOf("=");
1165
+ if (idx > 0) env[ev.slice(0, idx)] = ev.slice(idx + 1);
1166
+ }
1167
+ const result = await benchServer(entry.command, entry.args ?? [], env, runs, timeoutMs);
1168
+ if (!args.json) process.stdout.write("\n");
1169
+ if (result.error) {
1170
+ if (args.json) {
1171
+ console.log(JSON.stringify({ server: args.server, error: result.error }));
1172
+ } else {
1173
+ console.error(`
1174
+ ${pc3.red("\u2717")} Benchmark failed: ${result.error}`);
1175
+ }
1176
+ process.exit(1);
1177
+ }
1178
+ if (args.json) {
1179
+ console.log(JSON.stringify({ server: args.server, ...result }, null, 2));
1180
+ if (result.p95 > timeoutMs) process.exit(1);
1181
+ return;
1182
+ }
1183
+ const pad5 = (s, w) => s.padEnd(w);
1184
+ const ms = (n) => `${n}ms`;
1185
+ console.log(`
1186
+ ${pc3.bold("Latency statistics")} for ${pc3.cyan(args.server)}`);
1187
+ console.log(pc3.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"));
1188
+ console.log(` ${pad5("min", 8)} ${pc3.green(ms(result.min))}`);
1189
+ console.log(` ${pad5("avg", 8)} ${ms(result.avg)}`);
1190
+ console.log(` ${pad5("p50", 8)} ${ms(result.p50)}`);
1191
+ console.log(
1192
+ ` ${pad5("p95", 8)} ${result.p95 > timeoutMs ? pc3.red(ms(result.p95)) : pc3.yellow(ms(result.p95))}`
1193
+ );
1194
+ console.log(` ${pad5("max", 8)} ${ms(result.max)}`);
1195
+ console.log(` ${pad5("runs", 8)} ${result.runs}`);
1196
+ console.log("");
1197
+ if (result.p95 > timeoutMs) {
1198
+ console.error(pc3.red(` \u2717 p95 (${result.p95}ms) exceeds timeout (${timeoutMs}ms)`));
1199
+ process.exit(1);
1200
+ }
1201
+ console.log(pc3.green(" \u2713 Benchmark complete"));
1202
+ }
1203
+ });
1204
+
856
1205
  // src/commands/completions.ts
857
- import fs4 from "fs";
1206
+ import fs6 from "fs";
858
1207
  import os2 from "os";
859
1208
  import path4 from "path";
860
- import { defineCommand as defineCommand2 } from "citty";
861
- import pc2 from "picocolors";
1209
+ import { defineCommand as defineCommand4 } from "citty";
1210
+ import pc4 from "picocolors";
862
1211
 
863
1212
  // src/core/completion-generator.ts
864
1213
  function getCommandList() {
@@ -888,7 +1237,13 @@ function getCommandList() {
888
1237
  "watch",
889
1238
  "registry",
890
1239
  "completions",
891
- "why"
1240
+ "why",
1241
+ "env",
1242
+ "bench",
1243
+ "diff",
1244
+ "group",
1245
+ "pin",
1246
+ "rollback"
892
1247
  ];
893
1248
  }
894
1249
  var SERVER_ARG_COMMANDS = [
@@ -1013,7 +1368,7 @@ complete -c mcpman -l runtime -s r \\
1013
1368
  }
1014
1369
 
1015
1370
  // src/commands/completions.ts
1016
- var completions_default = defineCommand2({
1371
+ var completions_default = defineCommand4({
1017
1372
  meta: {
1018
1373
  name: "completions",
1019
1374
  description: "Generate shell completion scripts (bash, zsh, fish)"
@@ -1063,24 +1418,24 @@ var completions_default = defineCommand2({
1063
1418
  await installCompletion();
1064
1419
  break;
1065
1420
  default:
1066
- console.error(pc2.red(` Error: Unknown shell '${shell}'. Use: bash, zsh, or fish.`));
1421
+ console.error(pc4.red(` Error: Unknown shell '${shell}'. Use: bash, zsh, or fish.`));
1067
1422
  process.exit(1);
1068
1423
  }
1069
1424
  }
1070
1425
  });
1071
1426
  function printUsage() {
1072
- console.log(pc2.bold("\n mcpman completions \u2014 Shell completion setup\n"));
1427
+ console.log(pc4.bold("\n mcpman completions \u2014 Shell completion setup\n"));
1073
1428
  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
1429
+ console.log(` ${pc4.cyan("mcpman completions bash")} Output bash completion script`);
1430
+ console.log(` ${pc4.cyan("mcpman completions zsh")} Output zsh completion script`);
1431
+ console.log(` ${pc4.cyan("mcpman completions fish")} Output fish completion script`);
1432
+ console.log(` ${pc4.cyan("mcpman completions install")} Auto-detect shell and install
1078
1433
  `);
1079
1434
  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)")}
1435
+ console.log(` ${pc4.dim("# bash")}`);
1436
+ console.log(` ${pc4.cyan("source <(mcpman completions bash)")}`);
1437
+ console.log(` ${pc4.dim("# zsh")}`);
1438
+ console.log(` ${pc4.cyan("source <(mcpman completions zsh)")}
1084
1439
  `);
1085
1440
  }
1086
1441
  async function installCompletion() {
@@ -1090,8 +1445,8 @@ async function installCompletion() {
1090
1445
  else if (shellBin.includes("fish")) detectedShell = "fish";
1091
1446
  else if (shellBin.includes("bash")) detectedShell = "bash";
1092
1447
  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)"));
1448
+ console.error(pc4.red(" Could not detect shell from $SHELL. Run manually:"));
1449
+ console.error(pc4.dim(" source <(mcpman completions bash|zsh|fish)"));
1095
1450
  process.exit(1);
1096
1451
  }
1097
1452
  const home = os2.homedir();
@@ -1102,10 +1457,10 @@ async function installCompletion() {
1102
1457
  script = generateZshCompletion();
1103
1458
  } else if (detectedShell === "fish") {
1104
1459
  const fishDir = path4.join(home, ".config", "fish", "completions");
1105
- fs4.mkdirSync(fishDir, { recursive: true });
1460
+ fs6.mkdirSync(fishDir, { recursive: true });
1106
1461
  rcFile = path4.join(fishDir, "mcpman.fish");
1107
- fs4.writeFileSync(rcFile, generateFishCompletion(), "utf-8");
1108
- console.log(pc2.green(` Installed fish completions to ${rcFile}`));
1462
+ fs6.writeFileSync(rcFile, generateFishCompletion(), "utf-8");
1463
+ console.log(pc4.green(` Installed fish completions to ${rcFile}`));
1109
1464
  return;
1110
1465
  } else {
1111
1466
  rcFile = path4.join(home, ".bashrc");
@@ -1114,25 +1469,25 @@ async function installCompletion() {
1114
1469
  const marker = "# mcpman completions";
1115
1470
  let existing = "";
1116
1471
  try {
1117
- existing = fs4.readFileSync(rcFile, "utf-8");
1472
+ existing = fs6.readFileSync(rcFile, "utf-8");
1118
1473
  } catch {
1119
1474
  }
1120
1475
  if (existing.includes(marker)) {
1121
- console.log(pc2.yellow(` Completions already installed in ${rcFile}. Skipping.`));
1476
+ console.log(pc4.yellow(` Completions already installed in ${rcFile}. Skipping.`));
1122
1477
  return;
1123
1478
  }
1124
- fs4.appendFileSync(rcFile, `
1479
+ fs6.appendFileSync(rcFile, `
1125
1480
  ${marker}
1126
1481
  source <(mcpman completions ${detectedShell})
1127
1482
  `);
1128
- console.log(pc2.green(` Installed ${detectedShell} completions in ${rcFile}`));
1129
- console.log(pc2.dim(` Restart your shell or run: source ${rcFile}`));
1483
+ console.log(pc4.green(` Installed ${detectedShell} completions in ${rcFile}`));
1484
+ console.log(pc4.dim(` Restart your shell or run: source ${rcFile}`));
1130
1485
  }
1131
1486
 
1132
1487
  // src/commands/config.ts
1133
1488
  import * as p2 from "@clack/prompts";
1134
- import { defineCommand as defineCommand3 } from "citty";
1135
- import pc3 from "picocolors";
1489
+ import { defineCommand as defineCommand5 } from "citty";
1490
+ import pc5 from "picocolors";
1136
1491
  function coerceValue(raw) {
1137
1492
  if (raw === "true") return true;
1138
1493
  if (raw === "false") return false;
@@ -1140,7 +1495,7 @@ function coerceValue(raw) {
1140
1495
  if (!Number.isNaN(num) && raw.trim() !== "") return num;
1141
1496
  return raw;
1142
1497
  }
1143
- var setCommand = defineCommand3({
1498
+ var setCommand = defineCommand5({
1144
1499
  meta: { name: "set", description: "Set a config value" },
1145
1500
  args: {
1146
1501
  key: {
@@ -1158,14 +1513,14 @@ var setCommand = defineCommand3({
1158
1513
  try {
1159
1514
  const coerced = coerceValue(args.value);
1160
1515
  setConfigValue(args.key, coerced);
1161
- console.log(`${pc3.green("\u2713")} Set ${pc3.bold(args.key)} = ${pc3.cyan(String(coerced))}`);
1516
+ console.log(`${pc5.green("\u2713")} Set ${pc5.bold(args.key)} = ${pc5.cyan(String(coerced))}`);
1162
1517
  } catch (err) {
1163
- console.error(`${pc3.red("\u2717")} ${String(err)}`);
1518
+ console.error(`${pc5.red("\u2717")} ${String(err)}`);
1164
1519
  process.exit(1);
1165
1520
  }
1166
1521
  }
1167
1522
  });
1168
- var getCommand = defineCommand3({
1523
+ var getCommand = defineCommand5({
1169
1524
  meta: { name: "get", description: "Get a config value" },
1170
1525
  args: {
1171
1526
  key: {
@@ -1177,32 +1532,32 @@ var getCommand = defineCommand3({
1177
1532
  run({ args }) {
1178
1533
  const val = getConfigValue(args.key);
1179
1534
  if (val === void 0) {
1180
- console.log(pc3.dim(`${args.key}: (not set)`));
1535
+ console.log(pc5.dim(`${args.key}: (not set)`));
1181
1536
  } else {
1182
- console.log(`${pc3.bold(args.key)}: ${pc3.cyan(String(val))}`);
1537
+ console.log(`${pc5.bold(args.key)}: ${pc5.cyan(String(val))}`);
1183
1538
  }
1184
1539
  }
1185
1540
  });
1186
- var listCommand = defineCommand3({
1541
+ var listCommand = defineCommand5({
1187
1542
  meta: { name: "list", description: "List all config values" },
1188
1543
  run() {
1189
1544
  const data = readConfig();
1190
1545
  const entries = Object.entries(data);
1191
1546
  if (entries.length === 0) {
1192
- console.log(pc3.dim("No config values set. Use `mcpman config set <key> <value>`."));
1547
+ console.log(pc5.dim("No config values set. Use `mcpman config set <key> <value>`."));
1193
1548
  return;
1194
1549
  }
1195
1550
  console.log("");
1196
- console.log(pc3.bold("mcpman config:"));
1551
+ console.log(pc5.bold("mcpman config:"));
1197
1552
  console.log("");
1198
1553
  for (const [key, val] of entries) {
1199
- console.log(` ${pc3.green("\u25CF")} ${pc3.bold(key)} ${pc3.cyan(String(val))}`);
1554
+ console.log(` ${pc5.green("\u25CF")} ${pc5.bold(key)} ${pc5.cyan(String(val))}`);
1200
1555
  }
1201
1556
  console.log("");
1202
- console.log(pc3.dim(` ${entries.length} key${entries.length !== 1 ? "s" : ""} configured`));
1557
+ console.log(pc5.dim(` ${entries.length} key${entries.length !== 1 ? "s" : ""} configured`));
1203
1558
  }
1204
1559
  });
1205
- var resetCommand = defineCommand3({
1560
+ var resetCommand = defineCommand5({
1206
1561
  meta: { name: "reset", description: "Reset config to defaults (removes config file)" },
1207
1562
  async run() {
1208
1563
  const confirmed = await p2.confirm({
@@ -1214,10 +1569,10 @@ var resetCommand = defineCommand3({
1214
1569
  return;
1215
1570
  }
1216
1571
  writeConfig({});
1217
- console.log(`${pc3.green("\u2713")} Config reset to defaults.`);
1572
+ console.log(`${pc5.green("\u2713")} Config reset to defaults.`);
1218
1573
  }
1219
1574
  });
1220
- var config_default = defineCommand3({
1575
+ var config_default = defineCommand5({
1221
1576
  meta: {
1222
1577
  name: "config",
1223
1578
  description: "Manage mcpman CLI configuration"
@@ -1232,11 +1587,11 @@ var config_default = defineCommand3({
1232
1587
 
1233
1588
  // src/commands/create.ts
1234
1589
  import path6 from "path";
1235
- import { defineCommand as defineCommand4 } from "citty";
1236
- import pc4 from "picocolors";
1590
+ import { defineCommand as defineCommand6 } from "citty";
1591
+ import pc6 from "picocolors";
1237
1592
 
1238
1593
  // src/core/scaffold-service.ts
1239
- import fs5 from "fs";
1594
+ import fs7 from "fs";
1240
1595
  import path5 from "path";
1241
1596
  function sanitizeName(name) {
1242
1597
  return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
@@ -1389,8 +1744,8 @@ if __name__ == "__main__":
1389
1744
  };
1390
1745
  }
1391
1746
  function writeScaffold(dir, files) {
1392
- if (fs5.existsSync(dir)) {
1393
- const existing = fs5.readdirSync(dir);
1747
+ if (fs7.existsSync(dir)) {
1748
+ const existing = fs7.readdirSync(dir);
1394
1749
  if (existing.length > 0) {
1395
1750
  throw new Error(`Directory '${dir}' already exists and is not empty.`);
1396
1751
  }
@@ -1398,13 +1753,13 @@ function writeScaffold(dir, files) {
1398
1753
  for (const [relativePath, content] of Object.entries(files)) {
1399
1754
  const fullPath = path5.join(dir, relativePath);
1400
1755
  const parentDir = path5.dirname(fullPath);
1401
- fs5.mkdirSync(parentDir, { recursive: true });
1402
- fs5.writeFileSync(fullPath, content, "utf-8");
1756
+ fs7.mkdirSync(parentDir, { recursive: true });
1757
+ fs7.writeFileSync(fullPath, content, "utf-8");
1403
1758
  }
1404
1759
  }
1405
1760
 
1406
1761
  // src/commands/create.ts
1407
- var create_default = defineCommand4({
1762
+ var create_default = defineCommand6({
1408
1763
  meta: {
1409
1764
  name: "create",
1410
1765
  description: "Scaffold a new MCP server project"
@@ -1442,7 +1797,7 @@ var create_default = defineCommand4({
1442
1797
  const readline = await import("readline");
1443
1798
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1444
1799
  projectName = await new Promise((resolve) => {
1445
- rl.question(pc4.cyan(" Project name: "), (answer) => {
1800
+ rl.question(pc6.cyan(" Project name: "), (answer) => {
1446
1801
  rl.close();
1447
1802
  resolve(answer.trim());
1448
1803
  });
@@ -1452,7 +1807,7 @@ var create_default = defineCommand4({
1452
1807
  const readline = await import("readline");
1453
1808
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1454
1809
  projectDescription = await new Promise((resolve) => {
1455
- rl.question(pc4.cyan(" Description (optional): "), (answer) => {
1810
+ rl.question(pc6.cyan(" Description (optional): "), (answer) => {
1456
1811
  rl.close();
1457
1812
  resolve(answer.trim());
1458
1813
  });
@@ -1462,7 +1817,7 @@ var create_default = defineCommand4({
1462
1817
  const readline = await import("readline");
1463
1818
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1464
1819
  const answer = await new Promise((resolve) => {
1465
- rl.question(pc4.cyan(" Runtime [node/python] (default: node): "), (a) => {
1820
+ rl.question(pc6.cyan(" Runtime [node/python] (default: node): "), (a) => {
1466
1821
  rl.close();
1467
1822
  resolve(a.trim() || "node");
1468
1823
  });
@@ -1471,16 +1826,16 @@ var create_default = defineCommand4({
1471
1826
  }
1472
1827
  }
1473
1828
  if (!projectName) {
1474
- console.error(pc4.red(" Error: Project name is required."));
1829
+ console.error(pc6.red(" Error: Project name is required."));
1475
1830
  process.exit(1);
1476
1831
  }
1477
1832
  const sanitized = sanitizeName(projectName);
1478
1833
  if (!sanitized) {
1479
- console.error(pc4.red(` Error: Invalid project name '${projectName}'.`));
1834
+ console.error(pc6.red(` Error: Invalid project name '${projectName}'.`));
1480
1835
  process.exit(1);
1481
1836
  }
1482
1837
  if (runtime !== "node" && runtime !== "python") {
1483
- console.error(pc4.red(` Error: Unknown runtime '${runtime}'. Use node or python.`));
1838
+ console.error(pc6.red(` Error: Unknown runtime '${runtime}'. Use node or python.`));
1484
1839
  process.exit(1);
1485
1840
  }
1486
1841
  const options = {
@@ -1494,90 +1849,252 @@ var create_default = defineCommand4({
1494
1849
  try {
1495
1850
  writeScaffold(targetDir, files);
1496
1851
  } catch (err) {
1497
- console.error(pc4.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
1852
+ console.error(pc6.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
1498
1853
  process.exit(1);
1499
1854
  }
1500
- console.log(pc4.green(`
1501
- Created ${pc4.bold(sanitized)}/
1855
+ console.log(pc6.green(`
1856
+ Created ${pc6.bold(sanitized)}/
1502
1857
  `));
1503
- console.log(pc4.dim(" Files generated:"));
1858
+ console.log(pc6.dim(" Files generated:"));
1504
1859
  for (const file of Object.keys(files)) {
1505
- console.log(` ${pc4.cyan(file)}`);
1860
+ console.log(` ${pc6.cyan(file)}`);
1506
1861
  }
1507
1862
  console.log("\n Next steps:");
1508
1863
  if (runtime === "node") {
1509
- console.log(` ${pc4.bold(`cd ${sanitized}`)}`);
1510
- console.log(` ${pc4.bold("npm install")}`);
1511
- console.log(` ${pc4.bold("mcpman link .")}`);
1864
+ console.log(` ${pc6.bold(`cd ${sanitized}`)}`);
1865
+ console.log(` ${pc6.bold("npm install")}`);
1866
+ console.log(` ${pc6.bold("mcpman link .")}`);
1512
1867
  } else {
1513
- console.log(` ${pc4.bold(`cd ${sanitized}`)}`);
1514
- console.log(` ${pc4.bold("pip install -e .")}`);
1515
- console.log(` ${pc4.bold("mcpman link .")}`);
1868
+ console.log(` ${pc6.bold(`cd ${sanitized}`)}`);
1869
+ console.log(` ${pc6.bold("pip install -e .")}`);
1870
+ console.log(` ${pc6.bold("mcpman link .")}`);
1516
1871
  }
1517
1872
  console.log();
1518
1873
  }
1519
1874
  });
1520
1875
 
1521
- // src/commands/doctor.ts
1522
- import { defineCommand as defineCommand5 } from "citty";
1523
- import pc5 from "picocolors";
1524
-
1525
- // src/core/diagnostics.ts
1526
- import { exec } from "child_process";
1527
- import { promisify } from "util";
1876
+ // src/commands/diff.ts
1877
+ import { defineCommand as defineCommand7 } from "citty";
1878
+ import pc7 from "picocolors";
1528
1879
 
1529
- // src/core/mcp-process-checks.ts
1530
- import { spawn } from "child_process";
1531
- async function checkProcessSpawn(command, args, env, timeoutMs = 3e3) {
1532
- return new Promise((resolve) => {
1533
- let settled = false;
1534
- const child = spawn(command, args, {
1535
- env: { ...process.env, ...env },
1536
- stdio: ["pipe", "pipe", "pipe"]
1537
- });
1538
- const timer = setTimeout(() => {
1539
- if (!settled) {
1540
- settled = true;
1541
- child.kill("SIGTERM");
1542
- resolve({ name: "Process", passed: true, message: "starts successfully (still running)" });
1543
- }
1544
- }, timeoutMs);
1545
- child.on("error", (err) => {
1546
- if (!settled) {
1547
- settled = true;
1548
- clearTimeout(timer);
1549
- resolve({ name: "Process", passed: false, message: `spawn error: ${err.message}` });
1550
- }
1551
- });
1552
- child.on("exit", (code) => {
1553
- if (!settled) {
1554
- settled = true;
1555
- clearTimeout(timer);
1556
- if (code === 0 || code === null) {
1557
- resolve({ name: "Process", passed: true, message: "exits cleanly" });
1558
- } else {
1559
- resolve({ name: "Process", passed: false, message: `exits with code ${code}` });
1560
- }
1880
+ // src/core/config-differ.ts
1881
+ function entryDiffs(a, b) {
1882
+ const diffs = [];
1883
+ if (a.command !== b.command) {
1884
+ diffs.push(`command: ${a.command} \u2192 ${b.command}`);
1885
+ }
1886
+ const aArgs = JSON.stringify(a.args ?? []);
1887
+ const bArgs = JSON.stringify(b.args ?? []);
1888
+ if (aArgs !== bArgs) {
1889
+ diffs.push(`args: ${aArgs} \u2192 ${bArgs}`);
1890
+ }
1891
+ const aEnv = JSON.stringify(a.env ?? {});
1892
+ const bEnv = JSON.stringify(b.env ?? {});
1893
+ if (aEnv !== bEnv) {
1894
+ diffs.push(`env: ${aEnv} \u2192 ${bEnv}`);
1895
+ }
1896
+ return diffs;
1897
+ }
1898
+ function diffClientConfigs(configA, configB) {
1899
+ const results = [];
1900
+ const serversA = configA.servers;
1901
+ const serversB = configB.servers;
1902
+ for (const name of Object.keys(serversB)) {
1903
+ if (!(name in serversA)) {
1904
+ results.push({ server: name, change: "added" });
1905
+ }
1906
+ }
1907
+ for (const name of Object.keys(serversA)) {
1908
+ if (!(name in serversB)) {
1909
+ results.push({ server: name, change: "removed" });
1910
+ }
1911
+ }
1912
+ for (const name of Object.keys(serversA)) {
1913
+ if (name in serversB) {
1914
+ const details = entryDiffs(serversA[name], serversB[name]);
1915
+ if (details.length > 0) {
1916
+ results.push({ server: name, change: "changed", details });
1561
1917
  }
1562
- });
1918
+ }
1919
+ }
1920
+ const order = { removed: 0, added: 1, changed: 2 };
1921
+ results.sort((a, b) => {
1922
+ const orderDiff = order[a.change] - order[b.change];
1923
+ return orderDiff !== 0 ? orderDiff : a.server.localeCompare(b.server);
1563
1924
  });
1925
+ return results;
1564
1926
  }
1565
- var MCP_INIT_REQUEST = JSON.stringify({
1566
- jsonrpc: "2.0",
1567
- id: 1,
1568
- method: "initialize",
1569
- params: {
1570
- protocolVersion: "2024-11-05",
1571
- capabilities: {},
1572
- clientInfo: { name: "mcpman-doctor", version: "0.1.0" }
1927
+ async function loadClientConfig(type) {
1928
+ try {
1929
+ const { getClient } = await import("./client-detector-O2HN4MUB.js");
1930
+ const handler = getClient(type);
1931
+ return await handler.readConfig();
1932
+ } catch {
1933
+ return null;
1573
1934
  }
1574
- });
1575
- async function checkMcpHandshake(command, args, env, timeoutMs = 5e3) {
1576
- return new Promise((resolve) => {
1577
- let settled = false;
1578
- const start = Date.now();
1935
+ }
1936
+
1937
+ // src/commands/diff.ts
1938
+ var VALID_CLIENTS = ["claude-desktop", "cursor", "vscode", "windsurf"];
1939
+ var CLIENT_DISPLAY = {
1940
+ "claude-desktop": "Claude Desktop",
1941
+ cursor: "Cursor",
1942
+ vscode: "VS Code",
1943
+ windsurf: "Windsurf"
1944
+ };
1945
+ var diff_default = defineCommand7({
1946
+ meta: {
1947
+ name: "diff",
1948
+ description: "Show config diff between two AI clients"
1949
+ },
1950
+ args: {
1951
+ clientA: {
1952
+ type: "positional",
1953
+ description: `Source client (${VALID_CLIENTS.join("|")})`,
1954
+ required: true
1955
+ },
1956
+ clientB: {
1957
+ type: "positional",
1958
+ description: `Target client (${VALID_CLIENTS.join("|")})`,
1959
+ required: true
1960
+ },
1961
+ json: {
1962
+ type: "boolean",
1963
+ description: "Output results as JSON",
1964
+ default: false
1965
+ }
1966
+ },
1967
+ async run({ args }) {
1968
+ const clientA = args.clientA;
1969
+ const clientB = args.clientB;
1970
+ if (!VALID_CLIENTS.includes(clientA)) {
1971
+ console.error(
1972
+ `${pc7.red("\u2717")} Unknown client "${clientA}". Valid: ${VALID_CLIENTS.join(", ")}`
1973
+ );
1974
+ process.exit(1);
1975
+ }
1976
+ if (!VALID_CLIENTS.includes(clientB)) {
1977
+ console.error(
1978
+ `${pc7.red("\u2717")} Unknown client "${clientB}". Valid: ${VALID_CLIENTS.join(", ")}`
1979
+ );
1980
+ process.exit(1);
1981
+ }
1982
+ if (clientA === clientB) {
1983
+ console.error(`${pc7.red("\u2717")} clientA and clientB must be different.`);
1984
+ process.exit(1);
1985
+ }
1986
+ const [configA, configB] = await Promise.all([
1987
+ loadClientConfig(clientA),
1988
+ loadClientConfig(clientB)
1989
+ ]);
1990
+ if (!configA) {
1991
+ console.error(`${pc7.red("\u2717")} Could not read config for ${CLIENT_DISPLAY[clientA]}.`);
1992
+ process.exit(1);
1993
+ }
1994
+ if (!configB) {
1995
+ console.error(`${pc7.red("\u2717")} Could not read config for ${CLIENT_DISPLAY[clientB]}.`);
1996
+ process.exit(1);
1997
+ }
1998
+ const diffs = diffClientConfigs(configA, configB);
1999
+ if (args.json) {
2000
+ console.log(JSON.stringify({ clientA, clientB, diffs }, null, 2));
2001
+ return;
2002
+ }
2003
+ const labelA = CLIENT_DISPLAY[clientA];
2004
+ const labelB = CLIENT_DISPLAY[clientB];
2005
+ console.log(`
2006
+ ${pc7.bold("mcpman diff")} ${pc7.cyan(labelA)} \u2192 ${pc7.cyan(labelB)}
2007
+ `);
2008
+ if (diffs.length === 0) {
2009
+ console.log(pc7.green(" \u2713 No differences \u2014 configs are identical."));
2010
+ console.log("");
2011
+ return;
2012
+ }
2013
+ for (const d of diffs) {
2014
+ if (d.change === "added") {
2015
+ console.log(` ${pc7.green("+")} ${pc7.bold(d.server)} ${pc7.dim(`(only in ${labelB})`)}`);
2016
+ } else if (d.change === "removed") {
2017
+ console.log(` ${pc7.red("-")} ${pc7.bold(d.server)} ${pc7.dim(`(only in ${labelA})`)}`);
2018
+ } else {
2019
+ console.log(` ${pc7.yellow("~")} ${pc7.bold(d.server)} ${pc7.dim("(changed)")}`);
2020
+ for (const detail of d.details ?? []) {
2021
+ console.log(` ${pc7.dim(detail)}`);
2022
+ }
2023
+ }
2024
+ }
2025
+ const added = diffs.filter((d) => d.change === "added").length;
2026
+ const removed = diffs.filter((d) => d.change === "removed").length;
2027
+ const changed = diffs.filter((d) => d.change === "changed").length;
2028
+ const parts = [];
2029
+ if (added > 0) parts.push(pc7.green(`+${added} added`));
2030
+ if (removed > 0) parts.push(pc7.red(`-${removed} removed`));
2031
+ if (changed > 0) parts.push(pc7.yellow(`~${changed} changed`));
2032
+ console.log(`
2033
+ ${parts.join(" ")}
2034
+ `);
2035
+ }
2036
+ });
2037
+
2038
+ // src/commands/doctor.ts
2039
+ import { defineCommand as defineCommand8 } from "citty";
2040
+ import pc8 from "picocolors";
2041
+
2042
+ // src/core/diagnostics.ts
2043
+ import { exec } from "child_process";
2044
+ import { promisify } from "util";
2045
+
2046
+ // src/core/mcp-process-checks.ts
2047
+ import { spawn as spawn2 } from "child_process";
2048
+ async function checkProcessSpawn(command, args, env, timeoutMs = 3e3) {
2049
+ return new Promise((resolve) => {
2050
+ let settled = false;
2051
+ const child = spawn2(command, args, {
2052
+ env: { ...process.env, ...env },
2053
+ stdio: ["pipe", "pipe", "pipe"]
2054
+ });
2055
+ const timer = setTimeout(() => {
2056
+ if (!settled) {
2057
+ settled = true;
2058
+ child.kill("SIGTERM");
2059
+ resolve({ name: "Process", passed: true, message: "starts successfully (still running)" });
2060
+ }
2061
+ }, timeoutMs);
2062
+ child.on("error", (err) => {
2063
+ if (!settled) {
2064
+ settled = true;
2065
+ clearTimeout(timer);
2066
+ resolve({ name: "Process", passed: false, message: `spawn error: ${err.message}` });
2067
+ }
2068
+ });
2069
+ child.on("exit", (code) => {
2070
+ if (!settled) {
2071
+ settled = true;
2072
+ clearTimeout(timer);
2073
+ if (code === 0 || code === null) {
2074
+ resolve({ name: "Process", passed: true, message: "exits cleanly" });
2075
+ } else {
2076
+ resolve({ name: "Process", passed: false, message: `exits with code ${code}` });
2077
+ }
2078
+ }
2079
+ });
2080
+ });
2081
+ }
2082
+ var MCP_INIT_REQUEST = JSON.stringify({
2083
+ jsonrpc: "2.0",
2084
+ id: 1,
2085
+ method: "initialize",
2086
+ params: {
2087
+ protocolVersion: "2024-11-05",
2088
+ capabilities: {},
2089
+ clientInfo: { name: "mcpman-doctor", version: "0.1.0" }
2090
+ }
2091
+ });
2092
+ async function checkMcpHandshake(command, args, env, timeoutMs = 5e3) {
2093
+ return new Promise((resolve) => {
2094
+ let settled = false;
2095
+ const start = Date.now();
1579
2096
  let stdout = "";
1580
- const child = spawn(command, args, {
2097
+ const child = spawn2(command, args, {
1581
2098
  env: { ...process.env, ...env },
1582
2099
  stdio: ["pipe", "pipe", "pipe"]
1583
2100
  });
@@ -1865,12 +2382,12 @@ async function getInstalledServers(clientFilter) {
1865
2382
 
1866
2383
  // src/commands/doctor.ts
1867
2384
  var CHECK_ICON = {
1868
- pass: pc5.green("\u2713"),
1869
- fail: pc5.red("\u2717"),
1870
- skip: pc5.dim("-"),
1871
- warn: pc5.yellow("\u26A0")
2385
+ pass: pc8.green("\u2713"),
2386
+ fail: pc8.red("\u2717"),
2387
+ skip: pc8.dim("-"),
2388
+ warn: pc8.yellow("\u26A0")
1872
2389
  };
1873
- var doctor_default = defineCommand5({
2390
+ var doctor_default = defineCommand8({
1874
2391
  meta: {
1875
2392
  name: "doctor",
1876
2393
  description: "Check MCP server health and configuration"
@@ -1883,11 +2400,11 @@ var doctor_default = defineCommand5({
1883
2400
  }
1884
2401
  },
1885
2402
  async run({ args }) {
1886
- console.log(pc5.bold("\n mcpman doctor\n"));
2403
+ console.log(pc8.bold("\n mcpman doctor\n"));
1887
2404
  const servers = await getInstalledServers();
1888
2405
  if (servers.length === 0) {
1889
2406
  console.log(
1890
- pc5.dim(" No MCP servers installed. Run mcpman install <server> to get started.")
2407
+ pc8.dim(" No MCP servers installed. Run mcpman install <server> to get started.")
1891
2408
  );
1892
2409
  return;
1893
2410
  }
@@ -1904,20 +2421,20 @@ var doctor_default = defineCommand5({
1904
2421
  if (pluginSummary.total > 0) {
1905
2422
  printPluginSection(pluginSummary);
1906
2423
  }
1907
- console.log(pc5.dim(` ${"\u2500".repeat(50)}`));
2424
+ console.log(pc8.dim(` ${"\u2500".repeat(50)}`));
1908
2425
  const parts = [];
1909
- if (passed > 0) parts.push(pc5.green(`${passed} healthy`));
1910
- if (failed > 0) parts.push(pc5.red(`${failed} unhealthy`));
2426
+ if (passed > 0) parts.push(pc8.green(`${passed} healthy`));
2427
+ if (failed > 0) parts.push(pc8.red(`${failed} unhealthy`));
1911
2428
  console.log(` Summary: ${parts.join(", ")}`);
1912
2429
  if (pluginSummary.total > 0) {
1913
2430
  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`));
2431
+ if (pluginSummary.healthy > 0) pParts.push(pc8.green(`${pluginSummary.healthy} ok`));
2432
+ if (pluginSummary.unhealthy > 0) pParts.push(pc8.red(`${pluginSummary.unhealthy} broken`));
1916
2433
  console.log(` Plugins: ${pParts.join(", ")}`);
1917
2434
  }
1918
2435
  if (failed > 0 || pluginSummary.unhealthy > 0) {
1919
2436
  if (!args.fix) {
1920
- console.log(pc5.dim(` Run ${pc5.cyan("mcpman doctor --fix")} for fix suggestions.
2437
+ console.log(pc8.dim(` Run ${pc8.cyan("mcpman doctor --fix")} for fix suggestions.
1921
2438
  `));
1922
2439
  }
1923
2440
  process.exit(1);
@@ -1926,26 +2443,26 @@ var doctor_default = defineCommand5({
1926
2443
  }
1927
2444
  });
1928
2445
  function printPluginSection(summary) {
1929
- console.log(pc5.bold(` Plugins (${summary.total})`));
2446
+ console.log(pc8.bold(` Plugins (${summary.total})`));
1930
2447
  for (const r of summary.results) {
1931
- const icon = r.loadable && r.prefixUnique && r.resolvable ? pc5.green("\u2713") : pc5.red("\u2717");
2448
+ const icon = r.loadable && r.prefixUnique && r.resolvable ? pc8.green("\u2713") : pc8.red("\u2717");
1932
2449
  const label = r.pluginName ? `${r.packageName} (${r.pluginName})` : r.packageName;
1933
- const prefix = r.prefix ? pc5.dim(` [${r.prefix}]`) : "";
2450
+ const prefix = r.prefix ? pc8.dim(` [${r.prefix}]`) : "";
1934
2451
  console.log(` ${icon} ${label}${prefix}`);
1935
2452
  if (r.error) {
1936
- console.log(` ${pc5.yellow("\u2192")} ${r.error}`);
2453
+ console.log(` ${pc8.yellow("\u2192")} ${r.error}`);
1937
2454
  }
1938
2455
  }
1939
2456
  console.log();
1940
2457
  }
1941
2458
  function printServerResult(result, showFix) {
1942
- const icon = result.status === "healthy" ? pc5.green("\u25CF") : pc5.red("\u25CF");
1943
- console.log(` ${icon} ${pc5.bold(result.serverName)}`);
2459
+ const icon = result.status === "healthy" ? pc8.green("\u25CF") : pc8.red("\u25CF");
2460
+ console.log(` ${icon} ${pc8.bold(result.serverName)}`);
1944
2461
  for (const check of result.checks) {
1945
2462
  const checkIcon = check.skipped ? CHECK_ICON.skip : check.passed ? CHECK_ICON.pass : CHECK_ICON.fail;
1946
2463
  console.log(` ${checkIcon} ${check.name}: ${check.message}`);
1947
2464
  if (showFix && !check.passed && !check.skipped && check.fix) {
1948
- console.log(` ${pc5.yellow("\u2192")} Fix: ${pc5.cyan(check.fix)}`);
2465
+ console.log(` ${pc8.yellow("\u2192")} Fix: ${pc8.cyan(check.fix)}`);
1949
2466
  }
1950
2467
  }
1951
2468
  console.log();
@@ -1954,11 +2471,11 @@ async function runParallel(tasks, concurrency) {
1954
2471
  const results = [];
1955
2472
  const executing = /* @__PURE__ */ new Set();
1956
2473
  for (const task of tasks) {
1957
- const p12 = task().then((r) => {
2474
+ const p13 = task().then((r) => {
1958
2475
  results.push(r);
1959
- executing.delete(p12);
2476
+ executing.delete(p13);
1960
2477
  });
1961
- executing.add(p12);
2478
+ executing.add(p13);
1962
2479
  if (executing.size >= concurrency) {
1963
2480
  await Promise.race(executing);
1964
2481
  }
@@ -1967,18 +2484,188 @@ async function runParallel(tasks, concurrency) {
1967
2484
  return results;
1968
2485
  }
1969
2486
 
1970
- // src/commands/export-command.ts
1971
- import fs7 from "fs";
2487
+ // src/commands/env.ts
2488
+ import { defineCommand as defineCommand9 } from "citty";
2489
+ import pc9 from "picocolors";
2490
+
2491
+ // src/core/env-manager.ts
2492
+ import fs8 from "fs";
1972
2493
  import path7 from "path";
1973
- import { defineCommand as defineCommand6 } from "citty";
1974
- import pc6 from "picocolors";
2494
+ function envFilePath(server, dir) {
2495
+ const base = dir ?? getEnvDir();
2496
+ const safe = server.replace(/[/@]/g, "_");
2497
+ return path7.join(base, `${safe}.json`);
2498
+ }
2499
+ function readStore(server, dir) {
2500
+ const file = envFilePath(server, dir);
2501
+ if (!fs8.existsSync(file)) return {};
2502
+ try {
2503
+ return JSON.parse(fs8.readFileSync(file, "utf-8"));
2504
+ } catch {
2505
+ return {};
2506
+ }
2507
+ }
2508
+ function writeStore(server, store, dir) {
2509
+ const base = dir ?? getEnvDir();
2510
+ if (!fs8.existsSync(base)) {
2511
+ fs8.mkdirSync(base, { recursive: true });
2512
+ }
2513
+ const file = envFilePath(server, dir);
2514
+ fs8.writeFileSync(file, JSON.stringify(store, null, 2), "utf-8");
2515
+ }
2516
+ function setEnv(server, key, value, dir) {
2517
+ const store = readStore(server, dir);
2518
+ store[key] = value;
2519
+ writeStore(server, store, dir);
2520
+ }
2521
+ function getEnv(server, key, dir) {
2522
+ const store = readStore(server, dir);
2523
+ return key in store ? store[key] : null;
2524
+ }
2525
+ function listEnv(server, dir) {
2526
+ return readStore(server, dir);
2527
+ }
2528
+ function deleteEnv(server, key, dir) {
2529
+ const store = readStore(server, dir);
2530
+ if (!(key in store)) return;
2531
+ delete store[key];
2532
+ writeStore(server, store, dir);
2533
+ }
2534
+ function clearEnv(server, dir) {
2535
+ const file = envFilePath(server, dir);
2536
+ if (fs8.existsSync(file)) {
2537
+ fs8.unlinkSync(file);
2538
+ }
2539
+ }
2540
+ function listEnvServers(dir) {
2541
+ const base = dir ?? getEnvDir();
2542
+ if (!fs8.existsSync(base)) return [];
2543
+ return fs8.readdirSync(base).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -5));
2544
+ }
2545
+
2546
+ // src/commands/env.ts
2547
+ var setCmd = defineCommand9({
2548
+ meta: { name: "set", description: "Set env var(s) for a server" },
2549
+ args: {
2550
+ server: { type: "positional", description: "Server name", required: true },
2551
+ pairs: { type: "positional", description: "KEY=VALUE pair(s)", required: true }
2552
+ },
2553
+ run({ args }) {
2554
+ const pairs = Array.isArray(args.pairs) ? args.pairs : [args.pairs];
2555
+ for (const pair of pairs) {
2556
+ const idx = pair.indexOf("=");
2557
+ if (idx <= 0) {
2558
+ console.error(`${pc9.red("\u2717")} Invalid format: "${pair}". Expected KEY=VALUE`);
2559
+ process.exit(1);
2560
+ }
2561
+ const key = pair.slice(0, idx);
2562
+ const value = pair.slice(idx + 1);
2563
+ setEnv(args.server, key, value);
2564
+ console.log(
2565
+ `${pc9.green("\u2713")} Set ${pc9.bold(key)}=${pc9.dim(value)} for ${pc9.cyan(args.server)}`
2566
+ );
2567
+ }
2568
+ }
2569
+ });
2570
+ var getCmd = defineCommand9({
2571
+ meta: { name: "get", description: "Get an env var for a server" },
2572
+ args: {
2573
+ server: { type: "positional", description: "Server name", required: true },
2574
+ key: { type: "positional", description: "Variable name", required: true }
2575
+ },
2576
+ run({ args }) {
2577
+ const value = getEnv(args.server, args.key);
2578
+ if (value === null) {
2579
+ console.error(`${pc9.red("\u2717")} No env var "${args.key}" for ${pc9.cyan(args.server)}`);
2580
+ process.exit(1);
2581
+ }
2582
+ console.log(value);
2583
+ }
2584
+ });
2585
+ var listCmd2 = defineCommand9({
2586
+ meta: { name: "list", description: "List env vars for a server (or all servers)" },
2587
+ args: {
2588
+ server: { type: "positional", description: "Server name (optional)", required: false }
2589
+ },
2590
+ run({ args }) {
2591
+ if (args.server) {
2592
+ const store = listEnv(args.server);
2593
+ const keys = Object.keys(store);
2594
+ if (keys.length === 0) {
2595
+ console.log(pc9.dim(`No env vars for ${pc9.cyan(args.server)}.`));
2596
+ return;
2597
+ }
2598
+ console.log(`
2599
+ ${pc9.bold(pc9.cyan(args.server))}`);
2600
+ for (const [k, v] of Object.entries(store)) {
2601
+ console.log(` ${pc9.green("\u25CF")} ${pc9.bold(k)}=${pc9.dim(v)}`);
2602
+ }
2603
+ console.log("");
2604
+ } else {
2605
+ const servers = listEnvServers();
2606
+ if (servers.length === 0) {
2607
+ console.log(pc9.dim("No env vars stored."));
2608
+ return;
2609
+ }
2610
+ for (const srv of servers) {
2611
+ const store = listEnv(srv);
2612
+ console.log(`
2613
+ ${pc9.bold(pc9.cyan(srv))}`);
2614
+ for (const [k, v] of Object.entries(store)) {
2615
+ console.log(` ${pc9.green("\u25CF")} ${pc9.bold(k)}=${pc9.dim(v)}`);
2616
+ }
2617
+ }
2618
+ console.log("");
2619
+ }
2620
+ }
2621
+ });
2622
+ var delCmd = defineCommand9({
2623
+ meta: { name: "del", description: "Delete an env var for a server" },
2624
+ args: {
2625
+ server: { type: "positional", description: "Server name", required: true },
2626
+ key: { type: "positional", description: "Variable name to delete", required: true }
2627
+ },
2628
+ run({ args }) {
2629
+ deleteEnv(args.server, args.key);
2630
+ console.log(`${pc9.green("\u2713")} Deleted ${pc9.bold(args.key)} from ${pc9.cyan(args.server)}`);
2631
+ }
2632
+ });
2633
+ var clearCmd = defineCommand9({
2634
+ meta: { name: "clear", description: "Clear all env vars for a server" },
2635
+ args: {
2636
+ server: { type: "positional", description: "Server name", required: true }
2637
+ },
2638
+ run({ args }) {
2639
+ clearEnv(args.server);
2640
+ console.log(`${pc9.green("\u2713")} Cleared all env vars for ${pc9.cyan(args.server)}`);
2641
+ }
2642
+ });
2643
+ var env_default = defineCommand9({
2644
+ meta: {
2645
+ name: "env",
2646
+ description: "Manage per-server environment variables (non-sensitive)"
2647
+ },
2648
+ subCommands: {
2649
+ set: setCmd,
2650
+ get: getCmd,
2651
+ list: listCmd2,
2652
+ del: delCmd,
2653
+ clear: clearCmd
2654
+ }
2655
+ });
2656
+
2657
+ // src/commands/export-command.ts
2658
+ import fs10 from "fs";
2659
+ import path8 from "path";
2660
+ import { defineCommand as defineCommand10 } from "citty";
2661
+ import pc10 from "picocolors";
1975
2662
 
1976
2663
  // src/core/export-import-service.ts
1977
- import fs6 from "fs";
2664
+ import fs9 from "fs";
1978
2665
 
1979
2666
  // src/utils/constants.ts
1980
2667
  var APP_NAME = "mcpman";
1981
- var APP_VERSION = "0.7.0";
2668
+ var APP_VERSION = "0.9.0";
1982
2669
  var APP_DESCRIPTION = "The package manager for MCP servers";
1983
2670
 
1984
2671
  // src/core/export-import-service.ts
@@ -1994,7 +2681,7 @@ function createExportBundle(opts = {}) {
1994
2681
  };
1995
2682
  if (includeVault) {
1996
2683
  const vaultPath = getVaultPath();
1997
- if (fs6.existsSync(vaultPath)) {
2684
+ if (fs9.existsSync(vaultPath)) {
1998
2685
  bundle.vault = readVault();
1999
2686
  }
2000
2687
  }
@@ -2060,7 +2747,7 @@ function importBundle(bundle, opts = {}) {
2060
2747
 
2061
2748
  // src/commands/export-command.ts
2062
2749
  var DEFAULT_OUTPUT = "mcpman-export.json";
2063
- var export_command_default = defineCommand6({
2750
+ var export_command_default = defineCommand10({
2064
2751
  meta: {
2065
2752
  name: "export",
2066
2753
  description: "Export mcpman config, lockfile, vault, and plugins to a portable JSON file"
@@ -2084,29 +2771,239 @@ var export_command_default = defineCommand6({
2084
2771
  },
2085
2772
  run({ args }) {
2086
2773
  const outputFile = args.output || DEFAULT_OUTPUT;
2087
- const outputPath = path7.resolve(outputFile);
2774
+ const outputPath = path8.resolve(outputFile);
2088
2775
  const bundle = createExportBundle({
2089
2776
  includeVault: !args["no-vault"],
2090
2777
  includePlugins: !args["no-plugins"]
2091
2778
  });
2092
2779
  const serverCount = Object.keys(bundle.lockfile.servers).length;
2093
2780
  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}`));
2781
+ fs10.writeFileSync(outputPath, JSON.stringify(bundle, null, 2), "utf-8");
2782
+ console.log(`${pc10.green("\u2713")} Exported to ${pc10.bold(outputFile)}`);
2783
+ console.log(pc10.dim(` Config keys: ${configKeys}`));
2784
+ console.log(pc10.dim(` Servers: ${serverCount}`));
2785
+ console.log(pc10.dim(` Vault: ${bundle.vault ? "included" : "excluded"}`));
2786
+ console.log(pc10.dim(` Plugins: ${bundle.plugins?.length ?? 0}`));
2787
+ }
2788
+ });
2789
+
2790
+ // src/commands/group.ts
2791
+ import { spawn as spawn3 } from "child_process";
2792
+ import { defineCommand as defineCommand11 } from "citty";
2793
+ import pc11 from "picocolors";
2794
+
2795
+ // src/core/group-manager.ts
2796
+ import fs11 from "fs";
2797
+ function readGroups(file) {
2798
+ const target = file ?? getGroupsFile();
2799
+ if (!fs11.existsSync(target)) return {};
2800
+ try {
2801
+ return JSON.parse(fs11.readFileSync(target, "utf-8"));
2802
+ } catch {
2803
+ return {};
2804
+ }
2805
+ }
2806
+ function writeGroups(store, file) {
2807
+ const target = file ?? getGroupsFile();
2808
+ const dir = target.slice(0, target.lastIndexOf("/"));
2809
+ if (dir && !fs11.existsSync(dir)) {
2810
+ fs11.mkdirSync(dir, { recursive: true });
2811
+ }
2812
+ fs11.writeFileSync(target, JSON.stringify(store, null, 2), "utf-8");
2813
+ }
2814
+ function addToGroup(group, servers, file) {
2815
+ const store = readGroups(file);
2816
+ const existing = new Set(store[group] ?? []);
2817
+ for (const s of servers) existing.add(s);
2818
+ store[group] = [...existing].sort();
2819
+ writeGroups(store, file);
2820
+ }
2821
+ function removeFromGroup(group, servers, file) {
2822
+ const store = readGroups(file);
2823
+ if (!store[group]) return;
2824
+ const toRemove = new Set(servers);
2825
+ store[group] = store[group].filter((s) => !toRemove.has(s));
2826
+ if (store[group].length === 0) delete store[group];
2827
+ writeGroups(store, file);
2828
+ }
2829
+ function getGroup(group, file) {
2830
+ return readGroups(file)[group] ?? [];
2831
+ }
2832
+ function listGroups(file) {
2833
+ return Object.keys(readGroups(file)).sort();
2834
+ }
2835
+ function deleteGroup(group, file) {
2836
+ const store = readGroups(file);
2837
+ if (!(group in store)) return;
2838
+ delete store[group];
2839
+ writeGroups(store, file);
2840
+ }
2841
+ function groupExists(group, file) {
2842
+ return group in readGroups(file);
2843
+ }
2844
+
2845
+ // src/commands/group.ts
2846
+ var addCmd2 = defineCommand11({
2847
+ meta: { name: "add", description: "Add servers to a group" },
2848
+ args: {
2849
+ name: { type: "positional", description: "Group name", required: true },
2850
+ servers: { type: "positional", description: "Server name(s)", required: true }
2851
+ },
2852
+ run({ args }) {
2853
+ const servers = Array.isArray(args.servers) ? args.servers : [args.servers];
2854
+ addToGroup(args.name, servers);
2855
+ console.log(`${pc11.green("\u2713")} Added ${servers.join(", ")} to group ${pc11.cyan(args.name)}`);
2856
+ }
2857
+ });
2858
+ var rmCmd = defineCommand11({
2859
+ meta: { name: "rm", description: "Remove servers from a group" },
2860
+ args: {
2861
+ name: { type: "positional", description: "Group name", required: true },
2862
+ servers: { type: "positional", description: "Server name(s)", required: true }
2863
+ },
2864
+ run({ args }) {
2865
+ const servers = Array.isArray(args.servers) ? args.servers : [args.servers];
2866
+ removeFromGroup(args.name, servers);
2867
+ console.log(`${pc11.green("\u2713")} Removed ${servers.join(", ")} from group ${pc11.cyan(args.name)}`);
2868
+ }
2869
+ });
2870
+ var listCmd3 = defineCommand11({
2871
+ meta: { name: "list", description: "List all groups (or members of a group)" },
2872
+ args: {
2873
+ name: { type: "positional", description: "Group name (optional)", required: false }
2874
+ },
2875
+ run({ args }) {
2876
+ if (args.name) {
2877
+ const members = getGroup(args.name);
2878
+ if (members.length === 0) {
2879
+ console.log(pc11.dim(`Group "${args.name}" is empty or does not exist.`));
2880
+ return;
2881
+ }
2882
+ console.log(`
2883
+ ${pc11.bold(pc11.cyan(args.name))}`);
2884
+ for (const s of members) console.log(` ${pc11.green("\u25CF")} ${s}`);
2885
+ console.log("");
2886
+ } else {
2887
+ const groups = listGroups();
2888
+ if (groups.length === 0) {
2889
+ console.log(pc11.dim("No groups defined. Use `mcpman group add <name> <server>`."));
2890
+ return;
2891
+ }
2892
+ console.log("");
2893
+ for (const g of groups) {
2894
+ const members = getGroup(g);
2895
+ console.log(
2896
+ ` ${pc11.cyan(pc11.bold(g))} ${pc11.dim(`(${members.length} server${members.length !== 1 ? "s" : ""})`)}`
2897
+ );
2898
+ for (const s of members) console.log(` ${pc11.dim("\xB7")} ${s}`);
2899
+ }
2900
+ console.log("");
2901
+ }
2902
+ }
2903
+ });
2904
+ var deleteCmd = defineCommand11({
2905
+ meta: { name: "delete", description: "Delete an entire group" },
2906
+ args: {
2907
+ name: { type: "positional", description: "Group name", required: true }
2908
+ },
2909
+ run({ args }) {
2910
+ if (!groupExists(args.name)) {
2911
+ console.error(`${pc11.red("\u2717")} Group "${args.name}" does not exist.`);
2912
+ process.exit(1);
2913
+ }
2914
+ deleteGroup(args.name);
2915
+ console.log(`${pc11.green("\u2713")} Deleted group ${pc11.cyan(args.name)}`);
2916
+ }
2917
+ });
2918
+ var installCmd = defineCommand11({
2919
+ meta: { name: "install", description: "Install all servers in a group" },
2920
+ args: {
2921
+ name: { type: "positional", description: "Group name", required: true }
2922
+ },
2923
+ async run({ args }) {
2924
+ const members = getGroup(args.name);
2925
+ if (members.length === 0) {
2926
+ console.error(`${pc11.red("\u2717")} Group "${args.name}" is empty or does not exist.`);
2927
+ process.exit(1);
2928
+ }
2929
+ console.log(
2930
+ `${pc11.cyan("Installing")} group ${pc11.bold(args.name)} (${members.length} servers)...`
2931
+ );
2932
+ for (const server of members) {
2933
+ console.log(`
2934
+ ${pc11.dim("\u2192")} Installing ${pc11.bold(server)}...`);
2935
+ await runInstall(server);
2936
+ }
2937
+ console.log(`
2938
+ ${pc11.green("\u2713")} Group install complete.`);
2939
+ }
2940
+ });
2941
+ var runCmd = defineCommand11({
2942
+ meta: { name: "run", description: "Run all servers in a group concurrently" },
2943
+ args: {
2944
+ name: { type: "positional", description: "Group name", required: true }
2945
+ },
2946
+ run({ args }) {
2947
+ const members = getGroup(args.name);
2948
+ if (members.length === 0) {
2949
+ console.error(`${pc11.red("\u2717")} Group "${args.name}" is empty or does not exist.`);
2950
+ process.exit(1);
2951
+ }
2952
+ const lockfile = readLockfile();
2953
+ console.log(
2954
+ `${pc11.cyan("Spawning")} group ${pc11.bold(args.name)} (${members.length} servers)...
2955
+ `
2956
+ );
2957
+ for (const server of members) {
2958
+ const entry = lockfile.servers[server];
2959
+ if (!entry) {
2960
+ console.warn(` ${pc11.yellow("!")} ${server} not in lockfile \u2014 skipping`);
2961
+ continue;
2962
+ }
2963
+ const child = spawn3(entry.command, entry.args ?? [], {
2964
+ env: process.env,
2965
+ stdio: "inherit",
2966
+ detached: false
2967
+ });
2968
+ child.on("error", (err) => {
2969
+ console.error(` ${pc11.red("\u2717")} ${server}: ${err.message}`);
2970
+ });
2971
+ console.log(` ${pc11.green("\u2713")} Spawned ${pc11.bold(server)} (pid ${child.pid ?? "?"})`);
2972
+ }
2973
+ }
2974
+ });
2975
+ function runInstall(server) {
2976
+ return new Promise((resolve, reject) => {
2977
+ const child = spawn3("mcpman", ["install", server], { stdio: "inherit" });
2978
+ child.on("close", (code) => {
2979
+ if (code === 0) resolve();
2980
+ else reject(new Error(`install exited with code ${code}`));
2981
+ });
2982
+ child.on("error", reject);
2983
+ });
2984
+ }
2985
+ var group_default = defineCommand11({
2986
+ meta: {
2987
+ name: "group",
2988
+ description: "Manage named server groups"
2989
+ },
2990
+ subCommands: {
2991
+ add: addCmd2,
2992
+ rm: rmCmd,
2993
+ list: listCmd3,
2994
+ delete: deleteCmd,
2995
+ install: installCmd,
2996
+ run: runCmd
2100
2997
  }
2101
2998
  });
2102
2999
 
2103
3000
  // src/commands/import-command.ts
2104
- import fs8 from "fs";
2105
- import path8 from "path";
3001
+ import fs12 from "fs";
3002
+ import path9 from "path";
2106
3003
  import * as p3 from "@clack/prompts";
2107
- import { defineCommand as defineCommand7 } from "citty";
2108
- import pc7 from "picocolors";
2109
- var import_command_default = defineCommand7({
3004
+ import { defineCommand as defineCommand12 } from "citty";
3005
+ import pc12 from "picocolors";
3006
+ var import_command_default = defineCommand12({
2110
3007
  meta: {
2111
3008
  name: "import",
2112
3009
  description: "Import mcpman config, lockfile, vault, and plugins from an export file"
@@ -2129,21 +3026,21 @@ var import_command_default = defineCommand7({
2129
3026
  }
2130
3027
  },
2131
3028
  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}`);
3029
+ const filePath = path9.resolve(args.file);
3030
+ if (!fs12.existsSync(filePath)) {
3031
+ console.error(`${pc12.red("\u2717")} File not found: ${filePath}`);
2135
3032
  process.exit(1);
2136
3033
  }
2137
3034
  let raw;
2138
3035
  try {
2139
- raw = JSON.parse(fs8.readFileSync(filePath, "utf-8"));
3036
+ raw = JSON.parse(fs12.readFileSync(filePath, "utf-8"));
2140
3037
  } catch {
2141
- console.error(`${pc7.red("\u2717")} Invalid JSON in ${filePath}`);
3038
+ console.error(`${pc12.red("\u2717")} Invalid JSON in ${filePath}`);
2142
3039
  process.exit(1);
2143
3040
  }
2144
3041
  const error2 = validateBundle(raw);
2145
3042
  if (error2) {
2146
- console.error(`${pc7.red("\u2717")} Invalid export bundle: ${error2}`);
3043
+ console.error(`${pc12.red("\u2717")} Invalid export bundle: ${error2}`);
2147
3044
  process.exit(1);
2148
3045
  }
2149
3046
  const bundle = raw;
@@ -2153,16 +3050,16 @@ var import_command_default = defineCommand7({
2153
3050
  const hasVault = !!bundle.vault;
2154
3051
  const isDryRun = !!args["dry-run"];
2155
3052
  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))}`);
3053
+ console.log(pc12.bold("Import summary:"));
3054
+ console.log(pc12.dim(` Source version: mcpman ${bundle.mcpmanVersion}`));
3055
+ console.log(pc12.dim(` Exported at: ${bundle.exportedAt}`));
3056
+ console.log(` Config keys: ${pc12.cyan(String(configKeys))}`);
3057
+ console.log(` Servers: ${pc12.cyan(String(serverCount))}`);
3058
+ console.log(` Vault: ${hasVault ? pc12.green("included") : pc12.dim("not included")}`);
3059
+ console.log(` Plugins: ${pc12.cyan(String(pluginCount))}`);
2163
3060
  console.log("");
2164
3061
  if (isDryRun) {
2165
- console.log(pc7.yellow(" [dry-run] No changes applied."));
3062
+ console.log(pc12.yellow(" [dry-run] No changes applied."));
2166
3063
  return;
2167
3064
  }
2168
3065
  if (!args.yes) {
@@ -2176,18 +3073,18 @@ var import_command_default = defineCommand7({
2176
3073
  }
2177
3074
  }
2178
3075
  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}`));
3076
+ console.log(`${pc12.green("\u2713")} Import complete`);
3077
+ console.log(pc12.dim(` Config keys restored: ${summary.configKeys}`));
3078
+ console.log(pc12.dim(` Servers restored: ${summary.servers}`));
3079
+ console.log(pc12.dim(` Vault: ${summary.vaultImported ? "restored" : "skipped"}`));
3080
+ console.log(pc12.dim(` Plugins installed: ${summary.pluginsInstalled}`));
2184
3081
  }
2185
3082
  });
2186
3083
 
2187
3084
  // src/commands/info.ts
2188
- import { defineCommand as defineCommand8 } from "citty";
3085
+ import { defineCommand as defineCommand13 } from "citty";
2189
3086
  import { createSpinner as createSpinner2 } from "nanospinner";
2190
- import pc8 from "picocolors";
3087
+ import pc13 from "picocolors";
2191
3088
 
2192
3089
  // src/core/package-info.ts
2193
3090
  async function buildInfo(name, entry, source = "npm") {
@@ -2242,11 +3139,11 @@ async function getPackageInfo(serverName) {
2242
3139
  // src/commands/info.ts
2243
3140
  function colorRisk2(score, riskLevel) {
2244
3141
  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);
3142
+ if (riskLevel === "LOW") return pc13.green(label);
3143
+ if (riskLevel === "MEDIUM") return pc13.yellow(label);
3144
+ if (riskLevel === "HIGH") return pc13.red(label);
3145
+ if (riskLevel === "CRITICAL") return pc13.bold(pc13.red(label));
3146
+ return pc13.dim(label);
2250
3147
  }
2251
3148
  function formatDaysAgo(isoDate) {
2252
3149
  if (!isoDate) return "unknown";
@@ -2256,54 +3153,54 @@ function formatDaysAgo(isoDate) {
2256
3153
  return `${days} days ago`;
2257
3154
  }
2258
3155
  function printInfo(info2) {
2259
- const installedBadge = info2.isInstalled ? pc8.green(" [installed]") : pc8.dim(" [not installed]");
3156
+ const installedBadge = info2.isInstalled ? pc13.green(" [installed]") : pc13.dim(" [not installed]");
2260
3157
  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}`);
3158
+ console.log(pc13.bold(` ${info2.name}@${info2.version}`) + installedBadge);
3159
+ console.log(pc13.dim(` ${"\u2500".repeat(60)}`));
3160
+ console.log(` ${pc13.dim("Source:")} ${info2.source}`);
3161
+ console.log(` ${pc13.dim("Runtime:")} ${info2.runtime}`);
2265
3162
  if (info2.description) {
2266
- console.log(` ${pc8.dim("Description:")} ${info2.description}`);
3163
+ console.log(` ${pc13.dim("Description:")} ${info2.description}`);
2267
3164
  }
2268
3165
  if (info2.deprecated) {
2269
- console.log(` ${pc8.red("[DEPRECATED]")} This package is deprecated`);
3166
+ console.log(` ${pc13.red("[DEPRECATED]")} This package is deprecated`);
2270
3167
  }
2271
3168
  console.log();
2272
- console.log(` ${pc8.bold("Trust & Security")}`);
2273
- console.log(` ${pc8.dim("Trust score:")} ${colorRisk2(info2.trustScore, info2.riskLevel)}`);
3169
+ console.log(` ${pc13.bold("Trust & Security")}`);
3170
+ console.log(` ${pc13.dim("Trust score:")} ${colorRisk2(info2.trustScore, info2.riskLevel)}`);
2274
3171
  if (info2.source === "npm") {
2275
3172
  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}`
3173
+ ` ${pc13.dim("Downloads:")} ${info2.weeklyDownloads.toLocaleString()}/week ${pc13.dim("|")} ${pc13.dim("Age:")} ${info2.packageAge}d ${pc13.dim("|")} ${pc13.dim("Maintainers:")} ${info2.maintainerCount}`
2277
3174
  );
2278
3175
  if (info2.lastPublish) {
2279
- console.log(` ${pc8.dim("Last publish:")} ${formatDaysAgo(info2.lastPublish)}`);
3176
+ console.log(` ${pc13.dim("Last publish:")} ${formatDaysAgo(info2.lastPublish)}`);
2280
3177
  }
2281
3178
  } else {
2282
- console.log(pc8.dim(" (Trust data available for npm packages only)"));
3179
+ console.log(pc13.dim(" (Trust data available for npm packages only)"));
2283
3180
  }
2284
3181
  console.log();
2285
- console.log(` ${pc8.bold("Environment Variables")}`);
3182
+ console.log(` ${pc13.bold("Environment Variables")}`);
2286
3183
  if (info2.envVars.length > 0) {
2287
3184
  for (const env of info2.envVars) {
2288
- console.log(` ${pc8.cyan("\u2022")} ${env}`);
3185
+ console.log(` ${pc13.cyan("\u2022")} ${env}`);
2289
3186
  }
2290
3187
  } else {
2291
- console.log(pc8.dim(" none required"));
3188
+ console.log(pc13.dim(" none required"));
2292
3189
  }
2293
3190
  console.log();
2294
- console.log(` ${pc8.bold("Installed Clients")}`);
3191
+ console.log(` ${pc13.bold("Installed Clients")}`);
2295
3192
  if (info2.installedClients.length > 0) {
2296
3193
  for (const client of info2.installedClients) {
2297
- console.log(` ${pc8.green("\u2713")} ${client}`);
3194
+ console.log(` ${pc13.green("\u2713")} ${client}`);
2298
3195
  }
2299
3196
  } else {
2300
- console.log(pc8.dim(" Not installed in any client"));
3197
+ console.log(pc13.dim(" Not installed in any client"));
2301
3198
  }
2302
3199
  console.log();
2303
- console.log(pc8.dim(` ${"\u2500".repeat(60)}`));
3200
+ console.log(pc13.dim(` ${"\u2500".repeat(60)}`));
2304
3201
  console.log();
2305
3202
  }
2306
- var info_default = defineCommand8({
3203
+ var info_default = defineCommand13({
2307
3204
  meta: {
2308
3205
  name: "info",
2309
3206
  description: "Show detailed metadata for an MCP server (installed or from registry)"
@@ -2327,13 +3224,13 @@ var info_default = defineCommand8({
2327
3224
  info2 = await getPackageInfo(args.server);
2328
3225
  } catch (err) {
2329
3226
  spinner5.error({ text: "Failed to fetch package info" });
2330
- console.error(pc8.red(String(err)));
3227
+ console.error(pc13.red(String(err)));
2331
3228
  process.exit(1);
2332
3229
  }
2333
3230
  if (!info2) {
2334
3231
  spinner5.error({ text: `Package not found: ${args.server}` });
2335
3232
  console.log(
2336
- pc8.dim(`
3233
+ pc13.dim(`
2337
3234
  "${args.server}" was not found in the npm registry or your lockfile.
2338
3235
  `)
2339
3236
  );
@@ -2349,10 +3246,10 @@ var info_default = defineCommand8({
2349
3246
  });
2350
3247
 
2351
3248
  // src/commands/init.ts
2352
- import path9 from "path";
3249
+ import path10 from "path";
2353
3250
  import * as p4 from "@clack/prompts";
2354
- import { defineCommand as defineCommand9 } from "citty";
2355
- var init_default = defineCommand9({
3251
+ import { defineCommand as defineCommand14 } from "citty";
3252
+ var init_default = defineCommand14({
2356
3253
  meta: {
2357
3254
  name: "init",
2358
3255
  description: "Initialize mcpman.lock in the current project"
@@ -2368,7 +3265,7 @@ var init_default = defineCommand9({
2368
3265
  async run({ args }) {
2369
3266
  const nonInteractive = args.yes || !process.stdout.isTTY;
2370
3267
  p4.intro("mcpman init");
2371
- const targetPath = path9.join(process.cwd(), LOCKFILE_NAME);
3268
+ const targetPath = path10.join(process.cwd(), LOCKFILE_NAME);
2372
3269
  const existing = findLockfile();
2373
3270
  if (existing) {
2374
3271
  if (nonInteractive) {
@@ -2384,7 +3281,7 @@ var init_default = defineCommand9({
2384
3281
  }
2385
3282
  let clients = [];
2386
3283
  try {
2387
- const mod = await import("./client-detector-CY7WPF3K.js");
3284
+ const mod = await import("./client-detector-O2HN4MUB.js");
2388
3285
  clients = await mod.getInstalledClients();
2389
3286
  } catch {
2390
3287
  p4.log.warn("Could not detect AI clients \u2014 creating empty lockfile.");
@@ -2452,7 +3349,7 @@ var init_default = defineCommand9({
2452
3349
 
2453
3350
  // src/commands/install.ts
2454
3351
  import * as p7 from "@clack/prompts";
2455
- import { defineCommand as defineCommand10 } from "citty";
3352
+ import { defineCommand as defineCommand15 } from "citty";
2456
3353
 
2457
3354
  // src/core/installer.ts
2458
3355
  import * as p6 from "@clack/prompts";
@@ -2492,7 +3389,7 @@ async function offerVaultSave(serverName, newVars, yes) {
2492
3389
  // src/core/installer.ts
2493
3390
  async function loadClients2() {
2494
3391
  try {
2495
- const mod = await import("./client-detector-CY7WPF3K.js");
3392
+ const mod = await import("./client-detector-O2HN4MUB.js");
2496
3393
  return mod.getInstalledClients();
2497
3394
  } catch {
2498
3395
  return [];
@@ -2604,7 +3501,7 @@ async function installServer(input, options = {}) {
2604
3501
  }
2605
3502
 
2606
3503
  // src/utils/logger.ts
2607
- import pc9 from "picocolors";
3504
+ import pc14 from "picocolors";
2608
3505
  var noColor = process.env.NO_COLOR !== void 0 || process.argv.includes("--no-color");
2609
3506
  var isVerbose = process.argv.includes("--verbose");
2610
3507
  var isJson = process.argv.includes("--json");
@@ -2613,18 +3510,18 @@ function colorize(fn, text2) {
2613
3510
  }
2614
3511
  function info(message) {
2615
3512
  if (isJson) return;
2616
- console.log(`${colorize(pc9.cyan, "i")} ${message}`);
3513
+ console.log(`${colorize(pc14.cyan, "i")} ${message}`);
2617
3514
  }
2618
3515
  function error(message) {
2619
3516
  if (isJson) return;
2620
- console.error(`${colorize(pc9.red, "\u2717")} ${message}`);
3517
+ console.error(`${colorize(pc14.red, "\u2717")} ${message}`);
2621
3518
  }
2622
3519
  function json(data) {
2623
3520
  console.log(JSON.stringify(data, null, 2));
2624
3521
  }
2625
3522
 
2626
3523
  // src/commands/install.ts
2627
- var install_default = defineCommand10({
3524
+ var install_default = defineCommand15({
2628
3525
  meta: {
2629
3526
  name: "install",
2630
3527
  description: "Install an MCP server into one or more AI clients"
@@ -2686,23 +3583,23 @@ async function restoreFromLockfile() {
2686
3583
  }
2687
3584
 
2688
3585
  // src/commands/link.ts
2689
- import path11 from "path";
2690
- import { defineCommand as defineCommand11 } from "citty";
2691
- import pc10 from "picocolors";
3586
+ import path12 from "path";
3587
+ import { defineCommand as defineCommand16 } from "citty";
3588
+ import pc15 from "picocolors";
2692
3589
 
2693
3590
  // src/core/link-service.ts
2694
- import fs9 from "fs";
2695
- import path10 from "path";
3591
+ import fs13 from "fs";
3592
+ import path11 from "path";
2696
3593
  function detectLocalServer(dir) {
2697
- if (!fs9.existsSync(dir)) {
3594
+ if (!fs13.existsSync(dir)) {
2698
3595
  throw new Error(`Directory does not exist: ${dir}`);
2699
3596
  }
2700
- const pkgPath = path10.join(dir, "package.json");
2701
- if (fs9.existsSync(pkgPath)) {
3597
+ const pkgPath = path11.join(dir, "package.json");
3598
+ if (fs13.existsSync(pkgPath)) {
2702
3599
  return detectNodeServer(dir, pkgPath);
2703
3600
  }
2704
- const pyprojectPath = path10.join(dir, "pyproject.toml");
2705
- if (fs9.existsSync(pyprojectPath)) {
3601
+ const pyprojectPath = path11.join(dir, "pyproject.toml");
3602
+ if (fs13.existsSync(pyprojectPath)) {
2706
3603
  return detectPythonServer(dir, pyprojectPath);
2707
3604
  }
2708
3605
  throw new Error(
@@ -2710,28 +3607,28 @@ function detectLocalServer(dir) {
2710
3607
  );
2711
3608
  }
2712
3609
  function detectNodeServer(dir, pkgPath) {
2713
- const raw = fs9.readFileSync(pkgPath, "utf-8");
3610
+ const raw = fs13.readFileSync(pkgPath, "utf-8");
2714
3611
  const pkg = JSON.parse(raw);
2715
- const name = String(pkg.name ?? path10.basename(dir));
3612
+ const name = String(pkg.name ?? path11.basename(dir));
2716
3613
  const version = String(pkg.version ?? "0.0.0");
2717
3614
  let entryPoint = null;
2718
3615
  if (pkg.bin) {
2719
3616
  if (typeof pkg.bin === "string") {
2720
- entryPoint = path10.resolve(dir, pkg.bin);
3617
+ entryPoint = path11.resolve(dir, pkg.bin);
2721
3618
  } else if (typeof pkg.bin === "object" && pkg.bin !== null) {
2722
3619
  const binObj = pkg.bin;
2723
3620
  const firstBin = Object.values(binObj)[0];
2724
- if (firstBin) entryPoint = path10.resolve(dir, firstBin);
3621
+ if (firstBin) entryPoint = path11.resolve(dir, firstBin);
2725
3622
  }
2726
3623
  }
2727
3624
  if (!entryPoint && pkg.main) {
2728
- entryPoint = path10.resolve(dir, String(pkg.main));
3625
+ entryPoint = path11.resolve(dir, String(pkg.main));
2729
3626
  }
2730
3627
  if (!entryPoint) {
2731
3628
  const candidates = ["src/index.ts", "src/index.js", "index.ts", "index.js"];
2732
3629
  for (const c of candidates) {
2733
- const candidate = path10.join(dir, c);
2734
- if (fs9.existsSync(candidate)) {
3630
+ const candidate = path11.join(dir, c);
3631
+ if (fs13.existsSync(candidate)) {
2735
3632
  entryPoint = candidate;
2736
3633
  break;
2737
3634
  }
@@ -2756,20 +3653,20 @@ function detectNodeServer(dir, pkgPath) {
2756
3653
  return { name, version, command, args, envVars, absolutePath: dir, runtime: "node" };
2757
3654
  }
2758
3655
  function detectPythonServer(dir, pyprojectPath) {
2759
- const raw = fs9.readFileSync(pyprojectPath, "utf-8");
2760
- const name = extractTomlValue(raw, "name") ?? path10.basename(dir);
3656
+ const raw = fs13.readFileSync(pyprojectPath, "utf-8");
3657
+ const name = extractTomlValue(raw, "name") ?? path11.basename(dir);
2761
3658
  const version = extractTomlValue(raw, "version") ?? "0.0.0";
2762
3659
  let pythonCmd = "python3";
2763
- const venvPython = path10.join(dir, ".venv", "bin", "python");
2764
- if (fs9.existsSync(venvPython)) {
3660
+ const venvPython = path11.join(dir, ".venv", "bin", "python");
3661
+ if (fs13.existsSync(venvPython)) {
2765
3662
  pythonCmd = venvPython;
2766
3663
  }
2767
3664
  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");
3665
+ path11.join(dir, "main.py"),
3666
+ path11.join(dir, name.replace(/-/g, "_"), "main.py"),
3667
+ path11.join(dir, "__main__.py")
3668
+ ].find((p13) => fs13.existsSync(p13));
3669
+ const entryPoint = entryCandidate ?? path11.join(dir, "main.py");
2773
3670
  return {
2774
3671
  name,
2775
3672
  version,
@@ -2811,13 +3708,13 @@ async function registerLinkedServer(linkResult, clients, lockfilePath, nameOverr
2811
3708
  };
2812
3709
  const existing = readLockfile(lockfilePath);
2813
3710
  existing.servers[serverName] = lockEntry;
2814
- const { writeLockfile: writeLockfile2 } = await import("./lockfile-RBA7HB24.js");
3711
+ const { writeLockfile: writeLockfile2 } = await import("./lockfile-ITEBE7HU.js");
2815
3712
  writeLockfile2(existing, lockfilePath);
2816
3713
  return registered;
2817
3714
  }
2818
3715
 
2819
3716
  // src/commands/link.ts
2820
- var link_default = defineCommand11({
3717
+ var link_default = defineCommand16({
2821
3718
  meta: {
2822
3719
  name: "link",
2823
3720
  description: "Register a local MCP server directory with AI clients"
@@ -2843,42 +3740,42 @@ var link_default = defineCommand11({
2843
3740
  const dirArg = args.dir ?? ".";
2844
3741
  const clientFilter = args.client;
2845
3742
  const nameOverride = args.name;
2846
- const absoluteDir = path11.resolve(dirArg);
3743
+ const absoluteDir = path12.resolve(dirArg);
2847
3744
  let linkResult;
2848
3745
  try {
2849
3746
  linkResult = detectLocalServer(absoluteDir);
2850
3747
  } catch (err) {
2851
- console.error(pc10.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3748
+ console.error(pc15.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
2852
3749
  process.exit(1);
2853
3750
  }
2854
3751
  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(" ")}`));
3752
+ console.log(pc15.dim(`
3753
+ Detected: ${pc15.cyan(serverName)} (${linkResult.runtime})`));
3754
+ console.log(pc15.dim(` Path: ${absoluteDir}`));
3755
+ console.log(pc15.dim(` Command: ${linkResult.command} ${linkResult.args.join(" ")}`));
2859
3756
  const allClients = await getInstalledClients();
2860
3757
  const clients = clientFilter ? allClients.filter((c) => c.type === clientFilter) : allClients;
2861
3758
  if (clientFilter && clients.length === 0) {
2862
- console.error(pc10.red(` Error: Unknown client '${clientFilter}'.`));
3759
+ console.error(pc15.red(` Error: Unknown client '${clientFilter}'.`));
2863
3760
  process.exit(1);
2864
3761
  }
2865
3762
  let registered;
2866
3763
  try {
2867
3764
  registered = await registerLinkedServer(linkResult, clients, void 0, nameOverride);
2868
3765
  } catch (err) {
2869
- console.error(pc10.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3766
+ console.error(pc15.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
2870
3767
  process.exit(1);
2871
3768
  }
2872
3769
  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".`));
3770
+ console.log(pc15.yellow(" Warning: No clients registered. Are any AI clients installed?"));
3771
+ console.log(pc15.dim(` Server saved to lockfile with source "local".`));
2875
3772
  } else {
2876
- console.log(pc10.green(`
2877
- Linked ${pc10.bold(serverName)} to: ${registered.join(", ")}
3773
+ console.log(pc15.green(`
3774
+ Linked ${pc15.bold(serverName)} to: ${registered.join(", ")}
2878
3775
  `));
2879
- console.log(pc10.dim(` Run ${pc10.cyan("mcpman list")} to verify.`));
3776
+ console.log(pc15.dim(` Run ${pc15.cyan("mcpman list")} to verify.`));
2880
3777
  console.log(
2881
- pc10.dim(` Run ${pc10.cyan(`mcpman watch ${serverName}`)} to start with auto-restart.`)
3778
+ pc15.dim(` Run ${pc15.cyan(`mcpman watch ${serverName}`)} to start with auto-restart.`)
2882
3779
  );
2883
3780
  }
2884
3781
  console.log();
@@ -2886,14 +3783,14 @@ var link_default = defineCommand11({
2886
3783
  });
2887
3784
 
2888
3785
  // src/commands/list.ts
2889
- import { defineCommand as defineCommand12 } from "citty";
2890
- import pc11 from "picocolors";
3786
+ import { defineCommand as defineCommand17 } from "citty";
3787
+ import pc16 from "picocolors";
2891
3788
  var STATUS_ICON = {
2892
- healthy: pc11.green("\u25CF"),
2893
- unhealthy: pc11.red("\u25CF"),
2894
- unknown: pc11.dim("\u25CB")
3789
+ healthy: pc16.green("\u25CF"),
3790
+ unhealthy: pc16.red("\u25CF"),
3791
+ unknown: pc16.dim("\u25CB")
2895
3792
  };
2896
- var list_default = defineCommand12({
3793
+ var list_default = defineCommand17({
2897
3794
  meta: {
2898
3795
  name: "list",
2899
3796
  description: "List installed MCP servers"
@@ -2914,8 +3811,8 @@ var list_default = defineCommand12({
2914
3811
  if (servers.length === 0) {
2915
3812
  const filter = args.client ? ` for client "${args.client}"` : "";
2916
3813
  console.log(
2917
- pc11.dim(
2918
- `No MCP servers installed${filter}. Run ${pc11.cyan("mcpman install <server>")} to get started.`
3814
+ pc16.dim(
3815
+ `No MCP servers installed${filter}. Run ${pc16.cyan("mcpman install <server>")} to get started.`
2919
3816
  )
2920
3817
  );
2921
3818
  return;
@@ -2941,9 +3838,9 @@ var list_default = defineCommand12({
2941
3838
  const nameWidth = Math.max(4, ...withStatus.map((s) => s.name.length));
2942
3839
  const clientsWidth = Math.max(7, ...withStatus.map((s) => formatClients(s.clients).length));
2943
3840
  const header = ` ${pad("NAME", nameWidth)} ${pad("CLIENT(S)", clientsWidth)} ${pad("COMMAND", 20)} STATUS`;
2944
- console.log(pc11.dim(header));
3841
+ console.log(pc16.dim(header));
2945
3842
  console.log(
2946
- pc11.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientsWidth)} ${"-".repeat(20)} ------`)
3843
+ pc16.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientsWidth)} ${"-".repeat(20)} ------`)
2947
3844
  );
2948
3845
  for (const s of withStatus) {
2949
3846
  const icon = STATUS_ICON[s.status];
@@ -2958,7 +3855,7 @@ var list_default = defineCommand12({
2958
3855
  }
2959
3856
  const clientSet = new Set(withStatus.flatMap((s) => s.clients));
2960
3857
  console.log(
2961
- pc11.dim(
3858
+ pc16.dim(
2962
3859
  `
2963
3860
  ${withStatus.length} server${withStatus.length !== 1 ? "s" : ""} \xB7 ${clientSet.size} client${clientSet.size !== 1 ? "s" : ""}`
2964
3861
  )
@@ -2971,21 +3868,21 @@ function pad(s, width) {
2971
3868
  function truncate(s, max) {
2972
3869
  return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
2973
3870
  }
2974
- var CLIENT_DISPLAY = {
3871
+ var CLIENT_DISPLAY2 = {
2975
3872
  "claude-desktop": "Claude",
2976
3873
  cursor: "Cursor",
2977
3874
  vscode: "VS Code",
2978
3875
  windsurf: "Windsurf"
2979
3876
  };
2980
3877
  function formatClients(clients) {
2981
- return clients.map((c) => CLIENT_DISPLAY[c] ?? c).join(", ");
3878
+ return clients.map((c) => CLIENT_DISPLAY2[c] ?? c).join(", ");
2982
3879
  }
2983
3880
 
2984
3881
  // 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({
3882
+ import { spawn as spawn4 } from "child_process";
3883
+ import { defineCommand as defineCommand18 } from "citty";
3884
+ import pc17 from "picocolors";
3885
+ var logs_default = defineCommand18({
2989
3886
  meta: {
2990
3887
  name: "logs",
2991
3888
  description: "Stream stdout/stderr from an MCP server"
@@ -3008,7 +3905,7 @@ var logs_default = defineCommand13({
3008
3905
  const lockfile = readLockfile();
3009
3906
  const entry = lockfile.servers[serverName];
3010
3907
  if (!entry) {
3011
- console.error(pc12.red(` Error: Server '${serverName}' is not installed.`));
3908
+ console.error(pc17.red(` Error: Server '${serverName}' is not installed.`));
3012
3909
  process.exit(1);
3013
3910
  }
3014
3911
  const lockfileEnv = parseEnvFlags(entry.envVars);
@@ -3018,24 +3915,24 @@ var logs_default = defineCommand13({
3018
3915
  ...lockfileEnv,
3019
3916
  ...vaultEnv
3020
3917
  };
3021
- console.log(pc12.dim(` Streaming logs for ${pc12.cyan(serverName)}... (Ctrl+C to stop)
3918
+ console.log(pc17.dim(` Streaming logs for ${pc17.cyan(serverName)}... (Ctrl+C to stop)
3022
3919
  `));
3023
- const child = spawn2(entry.command, entry.args, {
3920
+ const child = spawn4(entry.command, entry.args, {
3024
3921
  env: finalEnv,
3025
3922
  stdio: ["pipe", "pipe", "pipe"]
3026
3923
  });
3027
3924
  child.stdout?.on("data", (chunk) => {
3028
- process.stdout.write(pc12.dim("[stdout] ") + chunk.toString());
3925
+ process.stdout.write(pc17.dim("[stdout] ") + chunk.toString());
3029
3926
  });
3030
3927
  child.stderr?.on("data", (chunk) => {
3031
- process.stderr.write(pc12.yellow("[stderr] ") + chunk.toString());
3928
+ process.stderr.write(pc17.yellow("[stderr] ") + chunk.toString());
3032
3929
  });
3033
3930
  child.on("error", (err) => {
3034
- console.error(pc12.red(` Failed to start '${serverName}': ${err.message}`));
3931
+ console.error(pc17.red(` Failed to start '${serverName}': ${err.message}`));
3035
3932
  process.exit(1);
3036
3933
  });
3037
3934
  child.on("close", (code) => {
3038
- console.log(pc12.dim(`
3935
+ console.log(pc17.dim(`
3039
3936
  Process exited with code ${code ?? 0}`));
3040
3937
  process.exit(code ?? 0);
3041
3938
  });
@@ -3048,22 +3945,315 @@ var logs_default = defineCommand13({
3048
3945
  });
3049
3946
  }
3050
3947
  });
3051
- async function loadVaultSecrets(serverName) {
3052
- try {
3053
- const entries = listSecrets(serverName);
3054
- if (entries.length === 0 || entries[0].keys.length === 0) return {};
3055
- const password = await getMasterPassword();
3056
- return getSecretsForServer(serverName, password);
3057
- } catch {
3058
- return {};
3059
- }
3060
- }
3948
+ async function loadVaultSecrets(serverName) {
3949
+ try {
3950
+ const entries = listSecrets(serverName);
3951
+ if (entries.length === 0 || entries[0].keys.length === 0) return {};
3952
+ const password = await getMasterPassword();
3953
+ return getSecretsForServer(serverName, password);
3954
+ } catch {
3955
+ return {};
3956
+ }
3957
+ }
3958
+
3959
+ // src/commands/notify.ts
3960
+ import { defineCommand as defineCommand19 } from "citty";
3961
+ import pc18 from "picocolors";
3962
+
3963
+ // src/core/notify-service.ts
3964
+ import { execSync as execSync2 } from "child_process";
3965
+ import fs14 from "fs";
3966
+ function readHooks(file) {
3967
+ const target = file ?? getNotifyFile();
3968
+ if (!fs14.existsSync(target)) return [];
3969
+ try {
3970
+ const parsed = JSON.parse(fs14.readFileSync(target, "utf-8"));
3971
+ return Array.isArray(parsed) ? parsed : [];
3972
+ } catch {
3973
+ return [];
3974
+ }
3975
+ }
3976
+ function writeHooks(hooks, file) {
3977
+ const target = file ?? getNotifyFile();
3978
+ const dir = target.substring(0, target.lastIndexOf("/"));
3979
+ if (dir && !fs14.existsSync(dir)) {
3980
+ fs14.mkdirSync(dir, { recursive: true });
3981
+ }
3982
+ fs14.writeFileSync(target, JSON.stringify(hooks, null, 2), "utf-8");
3983
+ }
3984
+ function addHook(event, type, target, file) {
3985
+ const hooks = readHooks(file);
3986
+ hooks.push({ event, type, target });
3987
+ writeHooks(hooks, file);
3988
+ }
3989
+ function removeHook(index, file) {
3990
+ const hooks = readHooks(file);
3991
+ if (index < 0 || index >= hooks.length) {
3992
+ throw new Error(`Hook index ${index} out of range (0\u2013${hooks.length - 1})`);
3993
+ }
3994
+ hooks.splice(index, 1);
3995
+ writeHooks(hooks, file);
3996
+ }
3997
+ function listHooks(file) {
3998
+ return readHooks(file);
3999
+ }
4000
+ async function fireEvent(event, payload, file) {
4001
+ const hooks = readHooks(file).filter((h) => h.event === event);
4002
+ if (hooks.length === 0) return;
4003
+ const body = JSON.stringify({ event, ...payload });
4004
+ await Promise.allSettled(
4005
+ hooks.map(async (hook) => {
4006
+ if (hook.type === "webhook") {
4007
+ await fireWebhook(hook.target, body);
4008
+ } else {
4009
+ fireShell(hook.target, event, payload);
4010
+ }
4011
+ })
4012
+ );
4013
+ }
4014
+ async function fireWebhook(url, body) {
4015
+ const response = await fetch(url, {
4016
+ method: "POST",
4017
+ headers: { "Content-Type": "application/json" },
4018
+ body
4019
+ });
4020
+ if (!response.ok) {
4021
+ throw new Error(`Webhook ${url} returned ${response.status}`);
4022
+ }
4023
+ }
4024
+ function fireShell(command, event, payload) {
4025
+ const env = {
4026
+ ...process.env,
4027
+ MCPMAN_EVENT: event,
4028
+ MCPMAN_PAYLOAD: JSON.stringify(payload)
4029
+ };
4030
+ execSync2(command, { stdio: "inherit", env });
4031
+ }
4032
+
4033
+ // src/commands/notify.ts
4034
+ var VALID_EVENTS = ["install", "remove", "update", "health-fail"];
4035
+ var addCmd3 = defineCommand19({
4036
+ meta: { name: "add", description: "Add a webhook or shell hook for an event" },
4037
+ args: {
4038
+ event: {
4039
+ type: "string",
4040
+ description: `Event to hook (${VALID_EVENTS.join(", ")})`,
4041
+ required: true
4042
+ },
4043
+ webhook: {
4044
+ type: "string",
4045
+ description: "Webhook URL (POST JSON payload)"
4046
+ },
4047
+ shell: {
4048
+ type: "string",
4049
+ description: "Shell command to run"
4050
+ }
4051
+ },
4052
+ run({ args }) {
4053
+ const event = args.event;
4054
+ if (!VALID_EVENTS.includes(event)) {
4055
+ console.error(`${pc18.red("\u2717")} Invalid event "${event}". Valid: ${VALID_EVENTS.join(", ")}`);
4056
+ process.exit(1);
4057
+ }
4058
+ if (!args.webhook && !args.shell) {
4059
+ console.error(`${pc18.red("\u2717")} Provide --webhook <url> or --shell <command>`);
4060
+ process.exit(1);
4061
+ }
4062
+ const type = args.webhook ? "webhook" : "shell";
4063
+ const target = args.webhook ?? args.shell;
4064
+ addHook(event, type, target);
4065
+ const hooks = listHooks();
4066
+ const idx = hooks.length - 1;
4067
+ console.log(
4068
+ `${pc18.green("\u2713")} Hook [${pc18.cyan(String(idx))}] added: ${pc18.bold(event)} \u2192 ${pc18.dim(type)}:${target}`
4069
+ );
4070
+ }
4071
+ });
4072
+ var removeCmd2 = defineCommand19({
4073
+ meta: { name: "remove", description: "Remove a hook by index" },
4074
+ args: {
4075
+ index: { type: "positional", description: "Hook index (from `notify list`)", required: true }
4076
+ },
4077
+ run({ args }) {
4078
+ const idx = Number(args.index);
4079
+ if (Number.isNaN(idx) || idx < 0) {
4080
+ console.error(`${pc18.red("\u2717")} Invalid index "${args.index}".`);
4081
+ process.exit(1);
4082
+ }
4083
+ try {
4084
+ removeHook(idx);
4085
+ console.log(`${pc18.green("\u2713")} Hook [${idx}] removed`);
4086
+ } catch (err) {
4087
+ console.error(`${pc18.red("\u2717")} ${String(err)}`);
4088
+ process.exit(1);
4089
+ }
4090
+ }
4091
+ });
4092
+ var listCmd4 = defineCommand19({
4093
+ meta: { name: "list", description: "List all configured hooks" },
4094
+ args: {},
4095
+ run() {
4096
+ const hooks = listHooks();
4097
+ if (hooks.length === 0) {
4098
+ console.log(
4099
+ pc18.dim(
4100
+ "\n No hooks configured. Use `mcpman notify add --event <event> --webhook <url>`.\n"
4101
+ )
4102
+ );
4103
+ return;
4104
+ }
4105
+ console.log(pc18.bold("\n mcpman notify hooks\n"));
4106
+ console.log(pc18.dim(` ${"\u2500".repeat(60)}`));
4107
+ hooks.forEach((h, i) => {
4108
+ const typeLabel = h.type === "webhook" ? pc18.blue("webhook") : pc18.magenta("shell");
4109
+ console.log(
4110
+ ` ${pc18.dim(`[${i}]`)} ${pc18.bold(h.event.padEnd(12))} ${typeLabel} ${pc18.dim(h.target)}`
4111
+ );
4112
+ });
4113
+ console.log(pc18.dim(` ${"\u2500".repeat(60)}
4114
+ `));
4115
+ }
4116
+ });
4117
+ var testCmd = defineCommand19({
4118
+ meta: { name: "test", description: "Fire a test event to all matching hooks" },
4119
+ args: {
4120
+ event: {
4121
+ type: "positional",
4122
+ description: `Event to test (${VALID_EVENTS.join(", ")})`,
4123
+ required: true
4124
+ }
4125
+ },
4126
+ async run({ args }) {
4127
+ const event = args.event;
4128
+ if (!VALID_EVENTS.includes(event)) {
4129
+ console.error(`${pc18.red("\u2717")} Invalid event "${event}". Valid: ${VALID_EVENTS.join(", ")}`);
4130
+ process.exit(1);
4131
+ }
4132
+ const hooks = listHooks().filter((h) => h.event === event);
4133
+ if (hooks.length === 0) {
4134
+ console.log(pc18.dim(`
4135
+ No hooks configured for event "${event}".
4136
+ `));
4137
+ return;
4138
+ }
4139
+ console.log(pc18.cyan(`
4140
+ Firing test event: ${pc18.bold(event)} (${hooks.length} hook(s))...
4141
+ `));
4142
+ try {
4143
+ await fireEvent(event, { test: true, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
4144
+ console.log(`${pc18.green("\u2713")} Test event fired
4145
+ `);
4146
+ } catch (err) {
4147
+ console.error(`${pc18.red("\u2717")} ${String(err)}`);
4148
+ process.exit(1);
4149
+ }
4150
+ }
4151
+ });
4152
+ var notify_default = defineCommand19({
4153
+ meta: {
4154
+ name: "notify",
4155
+ description: "Manage event webhooks and shell hooks"
4156
+ },
4157
+ subCommands: {
4158
+ add: addCmd3,
4159
+ remove: removeCmd2,
4160
+ list: listCmd4,
4161
+ test: testCmd
4162
+ }
4163
+ });
4164
+
4165
+ // src/commands/pin.ts
4166
+ import { defineCommand as defineCommand20 } from "citty";
4167
+ import pc19 from "picocolors";
4168
+ var pin_default = defineCommand20({
4169
+ meta: {
4170
+ name: "pin",
4171
+ description: "Pin a server to a specific version"
4172
+ },
4173
+ args: {
4174
+ server: {
4175
+ type: "positional",
4176
+ description: "Server name to pin/unpin",
4177
+ required: false
4178
+ },
4179
+ version: {
4180
+ type: "positional",
4181
+ description: "Version to pin to (defaults to currently installed version)",
4182
+ required: false
4183
+ },
4184
+ unpin: {
4185
+ type: "boolean",
4186
+ description: "Remove the pin for a server",
4187
+ default: false
4188
+ },
4189
+ list: {
4190
+ type: "boolean",
4191
+ description: "List all pinned servers",
4192
+ default: false
4193
+ }
4194
+ },
4195
+ run({ args }) {
4196
+ if (args.list) {
4197
+ const pins = listPins();
4198
+ if (pins.length === 0) {
4199
+ console.log(pc19.dim("No servers are pinned."));
4200
+ return;
4201
+ }
4202
+ console.log(`
4203
+ ${pc19.bold("Pinned servers")}
4204
+ `);
4205
+ for (const { server, version: version2 } of pins) {
4206
+ console.log(` ${pc19.cyan(pc19.bold(server))} ${pc19.dim("@")}${pc19.green(version2)}`);
4207
+ }
4208
+ console.log("");
4209
+ return;
4210
+ }
4211
+ if (args.unpin) {
4212
+ if (!args.server) {
4213
+ console.error(`${pc19.red("\u2717")} Specify a server name to unpin.`);
4214
+ process.exit(1);
4215
+ }
4216
+ if (!isPinned(args.server)) {
4217
+ console.log(pc19.dim(`"${args.server}" is not pinned.`));
4218
+ return;
4219
+ }
4220
+ unpinServer(args.server);
4221
+ console.log(`${pc19.green("\u2713")} Unpinned ${pc19.cyan(args.server)}`);
4222
+ return;
4223
+ }
4224
+ if (!args.server) {
4225
+ console.error(`${pc19.red("\u2717")} Specify a server name to pin. Use --list to see pins.`);
4226
+ process.exit(1);
4227
+ }
4228
+ let version = args.version;
4229
+ if (!version) {
4230
+ const lockfile = readLockfile();
4231
+ version = lockfile.servers[args.server]?.version;
4232
+ if (!version) {
4233
+ console.error(
4234
+ `${pc19.red("\u2717")} "${args.server}" not found in lockfile. Specify a version explicitly.`
4235
+ );
4236
+ process.exit(1);
4237
+ }
4238
+ }
4239
+ pinServer(args.server, version);
4240
+ const prev = getPinnedVersion(args.server);
4241
+ if (prev && prev !== version) {
4242
+ console.log(
4243
+ `${pc19.green("\u2713")} Re-pinned ${pc19.cyan(args.server)} ${pc19.dim(prev)} \u2192 ${pc19.green(version)}`
4244
+ );
4245
+ } else {
4246
+ console.log(`${pc19.green("\u2713")} Pinned ${pc19.cyan(args.server)} @ ${pc19.green(version)}`);
4247
+ }
4248
+ console.log(pc19.dim(" Update notifications will be suppressed for this server."));
4249
+ }
4250
+ });
3061
4251
 
3062
4252
  // src/commands/plugin.ts
3063
- import { defineCommand as defineCommand14 } from "citty";
4253
+ import { defineCommand as defineCommand21 } from "citty";
3064
4254
  import { createSpinner as createSpinner3 } from "nanospinner";
3065
- import pc13 from "picocolors";
3066
- var addCommand = defineCommand14({
4255
+ import pc20 from "picocolors";
4256
+ var addCommand = defineCommand21({
3067
4257
  meta: { name: "add", description: "Install a plugin package" },
3068
4258
  args: {
3069
4259
  package: {
@@ -3081,23 +4271,23 @@ var addCommand = defineCommand14({
3081
4271
  spinner5.stop();
3082
4272
  if (loaded) {
3083
4273
  console.log(
3084
- `${pc13.green("\u2713")} Plugin ${pc13.bold(loaded.name)} installed (prefix: ${pc13.cyan(loaded.prefix)})`
4274
+ `${pc20.green("\u2713")} Plugin ${pc20.bold(loaded.name)} installed (prefix: ${pc20.cyan(loaded.prefix)})`
3085
4275
  );
3086
4276
  } else {
3087
4277
  console.log(
3088
- `${pc13.yellow("\u26A0")} Package ${pc13.bold(pkg)} installed but does not export a valid mcpman plugin.`
4278
+ `${pc20.yellow("\u26A0")} Package ${pc20.bold(pkg)} installed but does not export a valid mcpman plugin.`
3089
4279
  );
3090
4280
  }
3091
4281
  } catch (err) {
3092
4282
  spinner5.stop();
3093
4283
  console.error(
3094
- `${pc13.red("\u2717")} Failed to install plugin: ${err instanceof Error ? err.message : String(err)}`
4284
+ `${pc20.red("\u2717")} Failed to install plugin: ${err instanceof Error ? err.message : String(err)}`
3095
4285
  );
3096
4286
  process.exit(1);
3097
4287
  }
3098
4288
  }
3099
4289
  });
3100
- var removeCommand = defineCommand14({
4290
+ var removeCommand = defineCommand21({
3101
4291
  meta: { name: "remove", description: "Uninstall a plugin package" },
3102
4292
  args: {
3103
4293
  package: {
@@ -3110,46 +4300,46 @@ var removeCommand = defineCommand14({
3110
4300
  const pkg = args.package;
3111
4301
  const installed = listPluginPackages();
3112
4302
  if (!installed.includes(pkg)) {
3113
- console.log(pc13.dim(`Plugin "${pkg}" is not installed.`));
4303
+ console.log(pc20.dim(`Plugin "${pkg}" is not installed.`));
3114
4304
  return;
3115
4305
  }
3116
4306
  try {
3117
4307
  removePluginPackage(pkg);
3118
- console.log(`${pc13.green("\u2713")} Plugin ${pc13.bold(pkg)} removed.`);
4308
+ console.log(`${pc20.green("\u2713")} Plugin ${pc20.bold(pkg)} removed.`);
3119
4309
  } catch (err) {
3120
4310
  console.error(
3121
- `${pc13.red("\u2717")} Failed to remove plugin: ${err instanceof Error ? err.message : String(err)}`
4311
+ `${pc20.red("\u2717")} Failed to remove plugin: ${err instanceof Error ? err.message : String(err)}`
3122
4312
  );
3123
4313
  process.exit(1);
3124
4314
  }
3125
4315
  }
3126
4316
  });
3127
- var listCommand2 = defineCommand14({
4317
+ var listCommand2 = defineCommand21({
3128
4318
  meta: { name: "list", description: "List installed plugins" },
3129
4319
  run() {
3130
4320
  const packages = listPluginPackages();
3131
4321
  if (packages.length === 0) {
3132
- console.log(pc13.dim("No plugins installed. Use `mcpman plugin add <package>`."));
4322
+ console.log(pc20.dim("No plugins installed. Use `mcpman plugin add <package>`."));
3133
4323
  return;
3134
4324
  }
3135
4325
  console.log("");
3136
- console.log(pc13.bold("Installed plugins:"));
4326
+ console.log(pc20.bold("Installed plugins:"));
3137
4327
  console.log("");
3138
4328
  for (const pkg of packages) {
3139
4329
  const loaded = loadPlugin(pkg);
3140
4330
  if (loaded) {
3141
4331
  console.log(
3142
- ` ${pc13.green("\u25CF")} ${pc13.bold(loaded.name)} prefix: ${pc13.cyan(loaded.prefix)} pkg: ${pc13.dim(pkg)}`
4332
+ ` ${pc20.green("\u25CF")} ${pc20.bold(loaded.name)} prefix: ${pc20.cyan(loaded.prefix)} pkg: ${pc20.dim(pkg)}`
3143
4333
  );
3144
4334
  } else {
3145
- console.log(` ${pc13.yellow("\u25CF")} ${pc13.dim(pkg)} ${pc13.yellow("(failed to load)")}`);
4335
+ console.log(` ${pc20.yellow("\u25CF")} ${pc20.dim(pkg)} ${pc20.yellow("(failed to load)")}`);
3146
4336
  }
3147
4337
  }
3148
4338
  console.log("");
3149
- console.log(pc13.dim(` ${packages.length} plugin${packages.length !== 1 ? "s" : ""} installed`));
4339
+ console.log(pc20.dim(` ${packages.length} plugin${packages.length !== 1 ? "s" : ""} installed`));
3150
4340
  }
3151
4341
  });
3152
- var plugin_default = defineCommand14({
4342
+ var plugin_default = defineCommand21({
3153
4343
  meta: {
3154
4344
  name: "plugin",
3155
4345
  description: "Manage mcpman plugins for custom registries"
@@ -3162,22 +4352,22 @@ var plugin_default = defineCommand14({
3162
4352
  });
3163
4353
 
3164
4354
  // src/commands/profiles.ts
3165
- import { defineCommand as defineCommand15 } from "citty";
3166
- import pc14 from "picocolors";
4355
+ import { defineCommand as defineCommand22 } from "citty";
4356
+ import pc21 from "picocolors";
3167
4357
 
3168
4358
  // src/core/profile-service.ts
3169
- import fs10 from "fs";
3170
- import path12 from "path";
4359
+ import fs15 from "fs";
4360
+ import path13 from "path";
3171
4361
  function ensureDir(dir = getProfilesDir()) {
3172
- fs10.mkdirSync(dir, { recursive: true });
4362
+ fs15.mkdirSync(dir, { recursive: true });
3173
4363
  }
3174
4364
  function profilePath(name, dir = getProfilesDir()) {
3175
- return path12.join(dir, `${name}.json`);
4365
+ return path13.join(dir, `${name}.json`);
3176
4366
  }
3177
4367
  function createProfile(name, description = "", dir = getProfilesDir()) {
3178
4368
  ensureDir(dir);
3179
4369
  const filePath = profilePath(name, dir);
3180
- if (fs10.existsSync(filePath)) {
4370
+ if (fs15.existsSync(filePath)) {
3181
4371
  throw new Error(`Profile '${name}' already exists. Delete it first or use a different name.`);
3182
4372
  }
3183
4373
  const lockfile = readLockfile();
@@ -3187,16 +4377,16 @@ function createProfile(name, description = "", dir = getProfilesDir()) {
3187
4377
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3188
4378
  servers: lockfile.servers
3189
4379
  };
3190
- fs10.writeFileSync(filePath, JSON.stringify(profile, null, 2), "utf-8");
4380
+ fs15.writeFileSync(filePath, JSON.stringify(profile, null, 2), "utf-8");
3191
4381
  return profile;
3192
4382
  }
3193
4383
  function listProfiles(dir = getProfilesDir()) {
3194
4384
  ensureDir(dir);
3195
- const files = fs10.readdirSync(dir).filter((f) => f.endsWith(".json"));
4385
+ const files = fs15.readdirSync(dir).filter((f) => f.endsWith(".json"));
3196
4386
  const profiles = [];
3197
4387
  for (const file of files) {
3198
4388
  try {
3199
- const raw = fs10.readFileSync(path12.join(dir, file), "utf-8");
4389
+ const raw = fs15.readFileSync(path13.join(dir, file), "utf-8");
3200
4390
  const data = JSON.parse(raw);
3201
4391
  profiles.push(data);
3202
4392
  } catch {
@@ -3206,9 +4396,9 @@ function listProfiles(dir = getProfilesDir()) {
3206
4396
  }
3207
4397
  function loadProfile(name, dir = getProfilesDir()) {
3208
4398
  const filePath = profilePath(name, dir);
3209
- if (!fs10.existsSync(filePath)) return null;
4399
+ if (!fs15.existsSync(filePath)) return null;
3210
4400
  try {
3211
- const raw = fs10.readFileSync(filePath, "utf-8");
4401
+ const raw = fs15.readFileSync(filePath, "utf-8");
3212
4402
  return JSON.parse(raw);
3213
4403
  } catch {
3214
4404
  return null;
@@ -3216,13 +4406,13 @@ function loadProfile(name, dir = getProfilesDir()) {
3216
4406
  }
3217
4407
  function deleteProfile(name, dir = getProfilesDir()) {
3218
4408
  const filePath = profilePath(name, dir);
3219
- if (!fs10.existsSync(filePath)) return false;
3220
- fs10.unlinkSync(filePath);
4409
+ if (!fs15.existsSync(filePath)) return false;
4410
+ fs15.unlinkSync(filePath);
3221
4411
  return true;
3222
4412
  }
3223
4413
 
3224
4414
  // src/commands/profiles.ts
3225
- var profiles_default = defineCommand15({
4415
+ var profiles_default = defineCommand22({
3226
4416
  meta: {
3227
4417
  name: "profiles",
3228
4418
  description: "Manage named server configuration profiles"
@@ -3251,16 +4441,16 @@ var profiles_default = defineCommand15({
3251
4441
  case "create": {
3252
4442
  if (!name) {
3253
4443
  console.error(
3254
- pc14.red(" Error: Profile name required. Usage: mcpman profiles create <name>")
4444
+ pc21.red(" Error: Profile name required. Usage: mcpman profiles create <name>")
3255
4445
  );
3256
4446
  process.exit(1);
3257
4447
  }
3258
4448
  try {
3259
4449
  const profile = createProfile(name, args.description ?? "");
3260
4450
  const count = Object.keys(profile.servers).length;
3261
- console.log(pc14.green(` \u2713 Profile '${name}' created with ${count} server(s).`));
4451
+ console.log(pc21.green(` \u2713 Profile '${name}' created with ${count} server(s).`));
3262
4452
  } catch (err) {
3263
- console.error(pc14.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
4453
+ console.error(pc21.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3264
4454
  process.exit(1);
3265
4455
  }
3266
4456
  break;
@@ -3268,38 +4458,38 @@ var profiles_default = defineCommand15({
3268
4458
  case "switch": {
3269
4459
  if (!name) {
3270
4460
  console.error(
3271
- pc14.red(" Error: Profile name required. Usage: mcpman profiles switch <name>")
4461
+ pc21.red(" Error: Profile name required. Usage: mcpman profiles switch <name>")
3272
4462
  );
3273
4463
  process.exit(1);
3274
4464
  }
3275
4465
  const profile = loadProfile(name);
3276
4466
  if (!profile) {
3277
- console.error(pc14.red(` Error: Profile '${name}' not found.`));
4467
+ console.error(pc21.red(` Error: Profile '${name}' not found.`));
3278
4468
  process.exit(1);
3279
4469
  }
3280
4470
  const lockData = { lockfileVersion: 1, servers: profile.servers };
3281
4471
  writeLockfile(lockData);
3282
4472
  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."));
4473
+ console.log(pc21.green(` \u2713 Switched to profile '${name}' (${count} servers).`));
4474
+ console.log(pc21.dim(" Run mcpman sync to apply to all clients."));
3285
4475
  break;
3286
4476
  }
3287
4477
  case "list": {
3288
4478
  const profiles = listProfiles();
3289
4479
  if (profiles.length === 0) {
3290
4480
  console.log(
3291
- pc14.dim(" No profiles saved. Create one with: mcpman profiles create <name>")
4481
+ pc21.dim(" No profiles saved. Create one with: mcpman profiles create <name>")
3292
4482
  );
3293
4483
  return;
3294
4484
  }
3295
- console.log(pc14.bold(`
4485
+ console.log(pc21.bold(`
3296
4486
  Profiles (${profiles.length})
3297
4487
  `));
3298
- for (const p12 of profiles) {
3299
- const count = Object.keys(p12.servers).length;
3300
- const desc = p12.description ? pc14.dim(` \u2014 ${p12.description}`) : "";
4488
+ for (const p13 of profiles) {
4489
+ const count = Object.keys(p13.servers).length;
4490
+ const desc = p13.description ? pc21.dim(` \u2014 ${p13.description}`) : "";
3301
4491
  console.log(
3302
- ` ${pc14.cyan("\u25CF")} ${pc14.bold(p12.name)} ${pc14.dim(`${count} server(s)`)}${desc}`
4492
+ ` ${pc21.cyan("\u25CF")} ${pc21.bold(p13.name)} ${pc21.dim(`${count} server(s)`)}${desc}`
3303
4493
  );
3304
4494
  }
3305
4495
  console.log();
@@ -3308,22 +4498,22 @@ var profiles_default = defineCommand15({
3308
4498
  case "delete": {
3309
4499
  if (!name) {
3310
4500
  console.error(
3311
- pc14.red(" Error: Profile name required. Usage: mcpman profiles delete <name>")
4501
+ pc21.red(" Error: Profile name required. Usage: mcpman profiles delete <name>")
3312
4502
  );
3313
4503
  process.exit(1);
3314
4504
  }
3315
4505
  const deleted = deleteProfile(name);
3316
4506
  if (deleted) {
3317
- console.log(pc14.green(` \u2713 Profile '${name}' deleted.`));
4507
+ console.log(pc21.green(` \u2713 Profile '${name}' deleted.`));
3318
4508
  } else {
3319
- console.error(pc14.red(` Error: Profile '${name}' not found.`));
4509
+ console.error(pc21.red(` Error: Profile '${name}' not found.`));
3320
4510
  process.exit(1);
3321
4511
  }
3322
4512
  break;
3323
4513
  }
3324
4514
  default:
3325
4515
  console.error(
3326
- pc14.red(` Unknown action '${action}'. Use: create, switch, list, or delete.`)
4516
+ pc21.red(` Unknown action '${action}'. Use: create, switch, list, or delete.`)
3327
4517
  );
3328
4518
  process.exit(1);
3329
4519
  }
@@ -3331,8 +4521,8 @@ var profiles_default = defineCommand15({
3331
4521
  });
3332
4522
 
3333
4523
  // src/commands/registry.ts
3334
- import { defineCommand as defineCommand16 } from "citty";
3335
- import pc15 from "picocolors";
4524
+ import { defineCommand as defineCommand23 } from "citty";
4525
+ import pc22 from "picocolors";
3336
4526
 
3337
4527
  // src/core/registry-manager.ts
3338
4528
  var BUILTIN_REGISTRIES = [
@@ -3402,7 +4592,7 @@ function getDefaultRegistry(configPath) {
3402
4592
  }
3403
4593
 
3404
4594
  // src/commands/registry.ts
3405
- var registry_default = defineCommand16({
4595
+ var registry_default = defineCommand23({
3406
4596
  meta: {
3407
4597
  name: "registry",
3408
4598
  description: "Manage custom registry URLs"
@@ -3432,67 +4622,67 @@ var registry_default = defineCommand16({
3432
4622
  case "list": {
3433
4623
  const registries = getRegistries();
3434
4624
  const defaultName = getDefaultRegistry();
3435
- console.log(pc15.bold("\n Registries\n"));
4625
+ console.log(pc22.bold("\n Registries\n"));
3436
4626
  for (const r of registries) {
3437
4627
  const isDefault = r.name === defaultName;
3438
- const defaultTag = isDefault ? pc15.green(" (default)") : "";
3439
- const builtinTag = r.builtin ? pc15.dim(" [builtin]") : "";
4628
+ const defaultTag = isDefault ? pc22.green(" (default)") : "";
4629
+ const builtinTag = r.builtin ? pc22.dim(" [builtin]") : "";
3440
4630
  console.log(
3441
- ` ${isDefault ? pc15.green("\u25CF") : pc15.dim("\u25CB")} ${pc15.bold(r.name)}${defaultTag}${builtinTag}`
4631
+ ` ${isDefault ? pc22.green("\u25CF") : pc22.dim("\u25CB")} ${pc22.bold(r.name)}${defaultTag}${builtinTag}`
3442
4632
  );
3443
- console.log(` ${pc15.dim(r.url)}`);
4633
+ console.log(` ${pc22.dim(r.url)}`);
3444
4634
  }
3445
4635
  console.log();
3446
4636
  break;
3447
4637
  }
3448
4638
  case "add": {
3449
4639
  if (!name) {
3450
- console.error(pc15.red(" Error: Usage: mcpman registry add <name> <url>"));
4640
+ console.error(pc22.red(" Error: Usage: mcpman registry add <name> <url>"));
3451
4641
  process.exit(1);
3452
4642
  }
3453
4643
  if (!url) {
3454
- console.error(pc15.red(" Error: Usage: mcpman registry add <name> <url>"));
4644
+ console.error(pc22.red(" Error: Usage: mcpman registry add <name> <url>"));
3455
4645
  process.exit(1);
3456
4646
  }
3457
4647
  try {
3458
4648
  addRegistry(name, url);
3459
- console.log(pc15.green(` Added registry '${name}' \u2192 ${url}`));
4649
+ console.log(pc22.green(` Added registry '${name}' \u2192 ${url}`));
3460
4650
  } catch (err) {
3461
- console.error(pc15.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
4651
+ console.error(pc22.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3462
4652
  process.exit(1);
3463
4653
  }
3464
4654
  break;
3465
4655
  }
3466
4656
  case "remove": {
3467
4657
  if (!name) {
3468
- console.error(pc15.red(" Error: Usage: mcpman registry remove <name>"));
4658
+ console.error(pc22.red(" Error: Usage: mcpman registry remove <name>"));
3469
4659
  process.exit(1);
3470
4660
  }
3471
4661
  try {
3472
4662
  removeRegistry(name);
3473
- console.log(pc15.green(` Removed registry '${name}'.`));
4663
+ console.log(pc22.green(` Removed registry '${name}'.`));
3474
4664
  } catch (err) {
3475
- console.error(pc15.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
4665
+ console.error(pc22.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3476
4666
  process.exit(1);
3477
4667
  }
3478
4668
  break;
3479
4669
  }
3480
4670
  case "set-default": {
3481
4671
  if (!name) {
3482
- console.error(pc15.red(" Error: Usage: mcpman registry set-default <name>"));
4672
+ console.error(pc22.red(" Error: Usage: mcpman registry set-default <name>"));
3483
4673
  process.exit(1);
3484
4674
  }
3485
4675
  try {
3486
4676
  setDefaultRegistry(name);
3487
- console.log(pc15.green(` Default registry set to '${name}'.`));
4677
+ console.log(pc22.green(` Default registry set to '${name}'.`));
3488
4678
  } catch (err) {
3489
- console.error(pc15.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
4679
+ console.error(pc22.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3490
4680
  process.exit(1);
3491
4681
  }
3492
4682
  break;
3493
4683
  }
3494
4684
  default:
3495
- console.error(pc15.red(` Unknown action '${action}'. Use: list, add, remove, set-default.`));
4685
+ console.error(pc22.red(` Unknown action '${action}'. Use: list, add, remove, set-default.`));
3496
4686
  process.exit(1);
3497
4687
  }
3498
4688
  }
@@ -3500,18 +4690,18 @@ var registry_default = defineCommand16({
3500
4690
 
3501
4691
  // src/commands/remove.ts
3502
4692
  import * as p8 from "@clack/prompts";
3503
- import { defineCommand as defineCommand17 } from "citty";
3504
- import pc16 from "picocolors";
3505
- var CLIENT_DISPLAY2 = {
4693
+ import { defineCommand as defineCommand24 } from "citty";
4694
+ import pc23 from "picocolors";
4695
+ var CLIENT_DISPLAY3 = {
3506
4696
  "claude-desktop": "Claude",
3507
4697
  cursor: "Cursor",
3508
4698
  vscode: "VS Code",
3509
4699
  windsurf: "Windsurf"
3510
4700
  };
3511
4701
  function clientDisplayName(type) {
3512
- return CLIENT_DISPLAY2[type] ?? type;
4702
+ return CLIENT_DISPLAY3[type] ?? type;
3513
4703
  }
3514
- var remove_default = defineCommand17({
4704
+ var remove_default = defineCommand24({
3515
4705
  meta: {
3516
4706
  name: "remove",
3517
4707
  description: "Remove an MCP server from one or more AI clients"
@@ -3538,7 +4728,7 @@ var remove_default = defineCommand17({
3538
4728
  }
3539
4729
  },
3540
4730
  async run({ args }) {
3541
- p8.intro(pc16.bold("mcpman remove"));
4731
+ p8.intro(pc23.bold("mcpman remove"));
3542
4732
  const serverName = args.server;
3543
4733
  const servers = await getInstalledServers();
3544
4734
  const match = servers.find((s) => s.name === serverName);
@@ -3548,7 +4738,7 @@ var remove_default = defineCommand17({
3548
4738
  (s) => s.name.includes(serverName) || serverName.includes(s.name)
3549
4739
  );
3550
4740
  if (similar.length > 0) {
3551
- p8.log.info(`Did you mean: ${similar.map((s) => pc16.cyan(s.name)).join(", ")}?`);
4741
+ p8.log.info(`Did you mean: ${similar.map((s) => pc23.cyan(s.name)).join(", ")}?`);
3552
4742
  }
3553
4743
  p8.outro("Nothing to remove.");
3554
4744
  return;
@@ -3583,7 +4773,7 @@ var remove_default = defineCommand17({
3583
4773
  if (!args.yes) {
3584
4774
  const clientNames = targetClients.map(clientDisplayName).join(", ");
3585
4775
  const confirmed = await p8.confirm({
3586
- message: `Remove ${pc16.cyan(serverName)} from ${pc16.yellow(clientNames)}?`
4776
+ message: `Remove ${pc23.cyan(serverName)} from ${pc23.yellow(clientNames)}?`
3587
4777
  });
3588
4778
  if (p8.isCancel(confirmed) || !confirmed) {
3589
4779
  p8.outro("Cancelled.");
@@ -3608,18 +4798,222 @@ var remove_default = defineCommand17({
3608
4798
  }
3609
4799
  if (errors.length > 0) {
3610
4800
  for (const e of errors) p8.log.error(e);
3611
- p8.outro(pc16.red("Completed with errors."));
4801
+ p8.outro(pc23.red("Completed with errors."));
4802
+ process.exit(1);
4803
+ }
4804
+ p8.outro(pc23.green(`Removed "${serverName}" successfully.`));
4805
+ }
4806
+ });
4807
+
4808
+ // src/commands/replay.ts
4809
+ import { defineCommand as defineCommand25 } from "citty";
4810
+ import pc24 from "picocolors";
4811
+
4812
+ // src/core/history-service.ts
4813
+ import { execSync as execSync3 } from "child_process";
4814
+ import fs16 from "fs";
4815
+ function readHistory(file) {
4816
+ const target = file ?? getHistoryFile();
4817
+ if (!fs16.existsSync(target)) return [];
4818
+ try {
4819
+ const parsed = JSON.parse(fs16.readFileSync(target, "utf-8"));
4820
+ return Array.isArray(parsed) ? parsed : [];
4821
+ } catch {
4822
+ return [];
4823
+ }
4824
+ }
4825
+ function getHistory(file) {
4826
+ return readHistory(file);
4827
+ }
4828
+ function replayCommand(index, file) {
4829
+ const history = readHistory(file);
4830
+ if (history.length === 0) {
4831
+ throw new Error("History is empty");
4832
+ }
4833
+ const reversed = [...history].reverse();
4834
+ if (index < 0 || index >= reversed.length) {
4835
+ throw new Error(`Index ${index} out of range (0\u2013${reversed.length - 1})`);
4836
+ }
4837
+ const entry = reversed[index];
4838
+ const fullCommand = ["mcpman", entry.command, ...entry.args].filter(Boolean).join(" ");
4839
+ execSync3(fullCommand, { stdio: "inherit" });
4840
+ }
4841
+
4842
+ // src/commands/replay.ts
4843
+ var replay_default = defineCommand25({
4844
+ meta: {
4845
+ name: "replay",
4846
+ description: "Replay a previous mcpman command from history"
4847
+ },
4848
+ args: {
4849
+ index: {
4850
+ type: "positional",
4851
+ description: "History index to replay (0 = most recent)",
4852
+ required: false
4853
+ },
4854
+ list: {
4855
+ type: "boolean",
4856
+ description: "Show last 20 history entries",
4857
+ default: false
4858
+ }
4859
+ },
4860
+ run({ args }) {
4861
+ const history = getHistory();
4862
+ if (args.list || args.index === void 0) {
4863
+ if (history.length === 0) {
4864
+ console.log(pc24.dim("\n No command history found.\n"));
4865
+ return;
4866
+ }
4867
+ const reversed2 = [...history].reverse().slice(0, 20);
4868
+ console.log(pc24.bold("\n mcpman history\n"));
4869
+ console.log(pc24.dim(` ${"\u2500".repeat(50)}`));
4870
+ reversed2.forEach((entry2, i) => {
4871
+ const cmdParts2 = [entry2.command, ...entry2.args].filter(Boolean).join(" ");
4872
+ const ts = new Date(entry2.timestamp).toLocaleString();
4873
+ console.log(` ${pc24.cyan(String(i).padStart(2))} ${pc24.bold(cmdParts2)} ${pc24.dim(ts)}`);
4874
+ });
4875
+ console.log(pc24.dim(` ${"\u2500".repeat(50)}
4876
+ `));
4877
+ if (!args.list) {
4878
+ console.log(pc24.dim(" Usage: mcpman replay <index> | mcpman replay --list\n"));
4879
+ }
4880
+ return;
4881
+ }
4882
+ const idx = Number(args.index);
4883
+ if (Number.isNaN(idx) || idx < 0) {
4884
+ console.error(pc24.red(`
4885
+ Invalid index "${args.index}". Must be a non-negative number.
4886
+ `));
4887
+ process.exit(1);
4888
+ }
4889
+ const reversed = [...history].reverse();
4890
+ if (idx >= reversed.length) {
4891
+ console.error(
4892
+ pc24.red(`
4893
+ Index ${idx} out of range. History has ${reversed.length} entries.
4894
+ `)
4895
+ );
4896
+ process.exit(1);
4897
+ }
4898
+ const entry = reversed[idx];
4899
+ const cmdParts = [entry.command, ...entry.args].filter(Boolean).join(" ");
4900
+ console.log(pc24.cyan(`
4901
+ Replaying: mcpman ${cmdParts}
4902
+ `));
4903
+ try {
4904
+ replayCommand(idx);
4905
+ } catch (err) {
4906
+ console.error(pc24.red(` ${String(err)}`));
4907
+ process.exit(1);
4908
+ }
4909
+ }
4910
+ });
4911
+
4912
+ // src/commands/rollback.ts
4913
+ import * as p9 from "@clack/prompts";
4914
+ import { defineCommand as defineCommand26 } from "citty";
4915
+ import pc25 from "picocolors";
4916
+ var rollback_default = defineCommand26({
4917
+ meta: {
4918
+ name: "rollback",
4919
+ description: "Restore a previous lockfile snapshot"
4920
+ },
4921
+ args: {
4922
+ index: {
4923
+ type: "positional",
4924
+ description: "Snapshot index to restore (0 = most recent). Omit to use --list.",
4925
+ required: false
4926
+ },
4927
+ list: {
4928
+ type: "boolean",
4929
+ description: "List available snapshots",
4930
+ default: false
4931
+ },
4932
+ yes: {
4933
+ type: "boolean",
4934
+ description: "Skip confirmation prompt",
4935
+ default: false
4936
+ }
4937
+ },
4938
+ async run({ args }) {
4939
+ const snapshots = listSnapshots();
4940
+ if (args.list || args.index === void 0) {
4941
+ if (snapshots.length === 0) {
4942
+ console.log(
4943
+ pc25.dim("No snapshots available. Snapshots are created on each lockfile write.")
4944
+ );
4945
+ return;
4946
+ }
4947
+ console.log(
4948
+ `
4949
+ ${pc25.bold("Lockfile snapshots")} ${pc25.dim(`(${snapshots.length} available, 0 = most recent)`)}
4950
+ `
4951
+ );
4952
+ for (const snap2 of snapshots) {
4953
+ const size = `${Math.ceil(snap2.sizeBytes / 1024)}KB`;
4954
+ const date2 = snap2.createdAt ? new Date(snap2.createdAt).toLocaleString() : "unknown";
4955
+ console.log(` ${pc25.cyan(`[${snap2.index}]`)} ${pc25.dim(date2)} ${pc25.dim(size)}`);
4956
+ }
4957
+ console.log("");
4958
+ if (args.index === void 0) return;
4959
+ }
4960
+ const idx = Number.parseInt(String(args.index), 10);
4961
+ if (Number.isNaN(idx) || idx < 0) {
4962
+ console.error(
4963
+ `${pc25.red("\u2717")} Invalid index "${args.index}". Must be a non-negative integer.`
4964
+ );
4965
+ process.exit(1);
4966
+ }
4967
+ const snap = snapshots[idx];
4968
+ if (!snap) {
4969
+ console.error(
4970
+ `${pc25.red("\u2717")} Snapshot [${idx}] does not exist. Use --list to see available snapshots.`
4971
+ );
4972
+ process.exit(1);
4973
+ }
4974
+ const content = readSnapshot(idx);
4975
+ if (!content) {
4976
+ console.error(`${pc25.red("\u2717")} Could not read snapshot [${idx}].`);
4977
+ process.exit(1);
4978
+ }
4979
+ const date = snap.createdAt ? new Date(snap.createdAt).toLocaleString() : "unknown";
4980
+ console.log(`
4981
+ ${pc25.bold("Restoring snapshot")} ${pc25.cyan(`[${idx}]`)} ${pc25.dim(date)}
4982
+ `);
4983
+ try {
4984
+ const parsed = JSON.parse(content);
4985
+ const count = Object.keys(parsed.servers ?? {}).length;
4986
+ console.log(` ${pc25.dim(`Preview: ${count} server(s) in snapshot`)}
4987
+ `);
4988
+ } catch {
4989
+ }
4990
+ const lockfilePath = resolveLockfilePath();
4991
+ if (!args.yes) {
4992
+ const confirmed = await p9.confirm({
4993
+ message: `Restore snapshot [${idx}] to ${lockfilePath}?`,
4994
+ initialValue: false
4995
+ });
4996
+ if (p9.isCancel(confirmed) || !confirmed) {
4997
+ p9.cancel("Cancelled.");
4998
+ return;
4999
+ }
5000
+ }
5001
+ const restored = restoreSnapshot(idx, lockfilePath);
5002
+ if (!restored) {
5003
+ console.error(`${pc25.red("\u2717")} Restore failed.`);
3612
5004
  process.exit(1);
3613
5005
  }
3614
- p8.outro(pc16.green(`Removed "${serverName}" successfully.`));
5006
+ console.log(`
5007
+ ${pc25.green("\u2713")} Lockfile restored from snapshot [${idx}].`);
5008
+ console.log(pc25.dim(` Written to: ${lockfilePath}`));
3615
5009
  }
3616
5010
  });
3617
5011
 
3618
5012
  // 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({
5013
+ import { spawn as spawn5 } from "child_process";
5014
+ import { defineCommand as defineCommand27 } from "citty";
5015
+ import pc26 from "picocolors";
5016
+ var run_default = defineCommand27({
3623
5017
  meta: {
3624
5018
  name: "run",
3625
5019
  description: "Run an installed MCP server with vault secrets injected"
@@ -3641,8 +5035,8 @@ var run_default = defineCommand18({
3641
5035
  const lockfile = readLockfile();
3642
5036
  const entry = lockfile.servers[serverName];
3643
5037
  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.`));
5038
+ console.error(pc26.red(` Error: Server '${serverName}' is not installed.`));
5039
+ console.error(pc26.dim(` Run ${pc26.cyan("mcpman install <server>")} to install it first.`));
3646
5040
  process.exit(1);
3647
5041
  }
3648
5042
  const lockfileEnv = parseEnvFlags(entry.envVars);
@@ -3654,8 +5048,8 @@ var run_default = defineCommand18({
3654
5048
  ...vaultEnv,
3655
5049
  ...cliEnv
3656
5050
  };
3657
- console.log(pc17.dim(` Running ${pc17.cyan(serverName)}...`));
3658
- const child = spawn3(entry.command, entry.args, {
5051
+ console.log(pc26.dim(` Running ${pc26.cyan(serverName)}...`));
5052
+ const child = spawn5(entry.command, entry.args, {
3659
5053
  env: finalEnv,
3660
5054
  stdio: "inherit"
3661
5055
  });
@@ -3672,7 +5066,7 @@ var run_default = defineCommand18({
3672
5066
  resolve();
3673
5067
  });
3674
5068
  child.on("error", (err) => {
3675
- console.error(pc17.red(` Failed to start '${serverName}': ${err.message}`));
5069
+ console.error(pc26.red(` Failed to start '${serverName}': ${err.message}`));
3676
5070
  process.exit(1);
3677
5071
  resolve();
3678
5072
  });
@@ -3688,15 +5082,15 @@ async function loadVaultSecrets2(serverName) {
3688
5082
  const password = await getMasterPassword();
3689
5083
  return getSecretsForServer(serverName, password);
3690
5084
  } catch {
3691
- console.warn(pc17.yellow(" Warning: Could not load vault secrets, continuing without them."));
5085
+ console.warn(pc26.yellow(" Warning: Could not load vault secrets, continuing without them."));
3692
5086
  return {};
3693
5087
  }
3694
5088
  }
3695
5089
 
3696
5090
  // src/commands/search.ts
3697
- import { defineCommand as defineCommand19 } from "citty";
5091
+ import { defineCommand as defineCommand28 } from "citty";
3698
5092
  import { createSpinner as createSpinner4 } from "nanospinner";
3699
- import pc18 from "picocolors";
5093
+ import pc27 from "picocolors";
3700
5094
 
3701
5095
  // src/core/registry-search.ts
3702
5096
  var SEARCH_TIMEOUT_MS = 1e4;
@@ -3769,10 +5163,10 @@ function pad2(s, width) {
3769
5163
  function highlightMatch(name, query) {
3770
5164
  const idx = name.toLowerCase().indexOf(query.toLowerCase());
3771
5165
  if (idx === -1) return name;
3772
- return name.slice(0, idx) + pc18.yellow(name.slice(idx, idx + query.length)) + name.slice(idx + query.length);
5166
+ return name.slice(0, idx) + pc27.yellow(name.slice(idx, idx + query.length)) + name.slice(idx + query.length);
3773
5167
  }
3774
5168
  function formatDownloads(n) {
3775
- if (!n) return pc18.dim("\u2014");
5169
+ if (!n) return pc27.dim("\u2014");
3776
5170
  if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
3777
5171
  if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
3778
5172
  return String(n);
@@ -3783,9 +5177,9 @@ function printNpmResults(results, query) {
3783
5177
  const dlWidth = 9;
3784
5178
  const descMax = 50;
3785
5179
  const header = ` ${pad2("NAME", nameWidth)} ${pad2("VERSION", verWidth)} ${pad2("DOWNLOADS", dlWidth)} DESCRIPTION`;
3786
- console.log(pc18.dim(header));
5180
+ console.log(pc27.dim(header));
3787
5181
  console.log(
3788
- pc18.dim(
5182
+ pc27.dim(
3789
5183
  ` ${"-".repeat(nameWidth)} ${"-".repeat(verWidth)} ${"-".repeat(dlWidth)} ${"-".repeat(descMax)}`
3790
5184
  )
3791
5185
  );
@@ -3793,8 +5187,8 @@ function printNpmResults(results, query) {
3793
5187
  const name = highlightMatch(pad2(r.name, nameWidth), query);
3794
5188
  const ver = pad2(r.version, verWidth);
3795
5189
  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}`);
5190
+ const desc = truncate2(r.description || pc27.dim("(no description)"), descMax);
5191
+ console.log(` ${name} ${pc27.dim(ver)} ${dl} ${desc}`);
3798
5192
  }
3799
5193
  }
3800
5194
  function printSmitheryResults(results, query) {
@@ -3802,19 +5196,19 @@ function printSmitheryResults(results, query) {
3802
5196
  const usesWidth = 8;
3803
5197
  const descMax = 50;
3804
5198
  const header = ` ${pad2("NAME", nameWidth)} ${pad2("USES", usesWidth)} DESCRIPTION`;
3805
- console.log(pc18.dim(header));
5199
+ console.log(pc27.dim(header));
3806
5200
  console.log(
3807
- pc18.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(usesWidth)} ${"-".repeat(descMax)}`)
5201
+ pc27.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(usesWidth)} ${"-".repeat(descMax)}`)
3808
5202
  );
3809
5203
  for (const r of results) {
3810
5204
  const name = highlightMatch(pad2(r.name, nameWidth), query);
3811
5205
  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);
5206
+ const badge = r.verified ? pc27.green(" \u2713") : "";
5207
+ const desc = truncate2(r.description || pc27.dim("(no description)"), descMax);
3814
5208
  console.log(` ${name}${badge} ${uses} ${desc}`);
3815
5209
  }
3816
5210
  }
3817
- var search_default = defineCommand19({
5211
+ var search_default = defineCommand28({
3818
5212
  meta: {
3819
5213
  name: "search",
3820
5214
  description: "Search for MCP servers on npm or Smithery registry"
@@ -3846,7 +5240,7 @@ var search_default = defineCommand19({
3846
5240
  const registry = args.registry.toLowerCase();
3847
5241
  const limit = Math.min(Math.max(1, Number.parseInt(args.limit, 10) || 20), 100);
3848
5242
  if (registry !== "npm" && registry !== "smithery") {
3849
- console.error(pc18.red(` Unknown registry "${registry}". Use "npm" or "smithery".`));
5243
+ console.error(pc27.red(` Unknown registry "${registry}". Use "npm" or "smithery".`));
3850
5244
  process.exit(1);
3851
5245
  }
3852
5246
  const spinner5 = createSpinner4(`Searching ${registry} for "${query}"...`).start();
@@ -3854,20 +5248,20 @@ var search_default = defineCommand19({
3854
5248
  const results2 = await searchNpm(query, limit);
3855
5249
  spinner5.stop();
3856
5250
  if (results2.length === 0) {
3857
- console.log(pc18.dim(`
5251
+ console.log(pc27.dim(`
3858
5252
  No results found for "${query}" on npm.
3859
5253
  `));
3860
5254
  return;
3861
5255
  }
3862
5256
  console.log(
3863
- pc18.bold(
5257
+ pc27.bold(
3864
5258
  `
3865
5259
  mcpman search \u2014 npm (${results2.length} result${results2.length !== 1 ? "s" : ""})
3866
5260
  `
3867
5261
  )
3868
5262
  );
3869
5263
  printNpmResults(results2, query);
3870
- console.log(pc18.dim("\n Install with: mcpman install <name>\n"));
5264
+ console.log(pc27.dim("\n Install with: mcpman install <name>\n"));
3871
5265
  if (args.all) {
3872
5266
  await printPluginResults(query, limit);
3873
5267
  }
@@ -3876,20 +5270,20 @@ var search_default = defineCommand19({
3876
5270
  const results = await searchSmithery(query, limit);
3877
5271
  spinner5.stop();
3878
5272
  if (results.length === 0) {
3879
- console.log(pc18.dim(`
5273
+ console.log(pc27.dim(`
3880
5274
  No results found for "${query}" on Smithery.
3881
5275
  `));
3882
5276
  return;
3883
5277
  }
3884
5278
  console.log(
3885
- pc18.bold(
5279
+ pc27.bold(
3886
5280
  `
3887
5281
  mcpman search \u2014 Smithery (${results.length} result${results.length !== 1 ? "s" : ""})
3888
5282
  `
3889
5283
  )
3890
5284
  );
3891
5285
  printSmitheryResults(results, query);
3892
- console.log(pc18.dim("\n Install with: mcpman install <name>\n"));
5286
+ console.log(pc27.dim("\n Install with: mcpman install <name>\n"));
3893
5287
  if (args.all) {
3894
5288
  await printPluginResults(query, limit);
3895
5289
  }
@@ -3899,7 +5293,7 @@ async function printPluginResults(query, limit) {
3899
5293
  const pluginResults = await searchPlugins(query, limit);
3900
5294
  if (pluginResults.length === 0) return;
3901
5295
  console.log(
3902
- pc18.bold(
5296
+ pc27.bold(
3903
5297
  `
3904
5298
  Plugins (${pluginResults.length} result${pluginResults.length !== 1 ? "s" : ""})
3905
5299
  `
@@ -3909,22 +5303,22 @@ async function printPluginResults(query, limit) {
3909
5303
  const srcWidth = Math.max(6, ...pluginResults.map((r) => r.source.length));
3910
5304
  const descMax = 50;
3911
5305
  const header = ` ${pad2("NAME", nameWidth)} ${pad2("SOURCE", srcWidth)} DESCRIPTION`;
3912
- console.log(pc18.dim(header));
5306
+ console.log(pc27.dim(header));
3913
5307
  console.log(
3914
- pc18.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(srcWidth)} ${"-".repeat(descMax)}`)
5308
+ pc27.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(srcWidth)} ${"-".repeat(descMax)}`)
3915
5309
  );
3916
5310
  for (const r of pluginResults) {
3917
5311
  const name = highlightMatch(pad2(r.name, nameWidth), query);
3918
5312
  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}`);
5313
+ const desc = truncate2(r.description || pc27.dim("(no description)"), descMax);
5314
+ console.log(` ${name} ${pc27.dim(src)} ${desc}`);
3921
5315
  }
3922
5316
  }
3923
5317
 
3924
5318
  // src/commands/secrets.ts
3925
- import * as p9 from "@clack/prompts";
3926
- import { defineCommand as defineCommand20 } from "citty";
3927
- import pc19 from "picocolors";
5319
+ import * as p10 from "@clack/prompts";
5320
+ import { defineCommand as defineCommand29 } from "citty";
5321
+ import pc28 from "picocolors";
3928
5322
  function maskValue(value) {
3929
5323
  if (value.length <= 8) return "***";
3930
5324
  return `${value.slice(0, 4)}***${value.slice(-3)}`;
@@ -3934,7 +5328,7 @@ function parseKeyValue(input) {
3934
5328
  if (idx <= 0) return null;
3935
5329
  return { key: input.slice(0, idx), value: input.slice(idx + 1) };
3936
5330
  }
3937
- var setCommand2 = defineCommand20({
5331
+ var setCommand2 = defineCommand29({
3938
5332
  meta: { name: "set", description: "Store an encrypted secret for a server" },
3939
5333
  args: {
3940
5334
  server: {
@@ -3951,28 +5345,28 @@ var setCommand2 = defineCommand20({
3951
5345
  async run({ args }) {
3952
5346
  const parsed = parseKeyValue(args.keyvalue);
3953
5347
  if (!parsed) {
3954
- console.error(`${pc19.red("\u2717")} Invalid format. Expected KEY=VALUE`);
5348
+ console.error(`${pc28.red("\u2717")} Invalid format. Expected KEY=VALUE`);
3955
5349
  process.exit(1);
3956
5350
  }
3957
- p9.intro(pc19.cyan("mcpman secrets set"));
5351
+ p10.intro(pc28.cyan("mcpman secrets set"));
3958
5352
  const isNew = listSecrets(args.server).length === 0 || !listSecrets(args.server)[0]?.keys.includes(parsed.key);
3959
5353
  const vaultPath = (await import("./vault-service-UTZAV6N6.js")).getVaultPath();
3960
5354
  const vaultExists = (await import("fs")).existsSync(vaultPath);
3961
5355
  const password = await getMasterPassword(!vaultExists && isNew);
3962
- const spin = p9.spinner();
5356
+ const spin = p10.spinner();
3963
5357
  spin.start("Encrypting secret...");
3964
5358
  try {
3965
5359
  setSecret(args.server, parsed.key, parsed.value, password);
3966
- spin.stop(`${pc19.green("\u2713")} Stored ${pc19.bold(parsed.key)} for ${pc19.cyan(args.server)}`);
5360
+ spin.stop(`${pc28.green("\u2713")} Stored ${pc28.bold(parsed.key)} for ${pc28.cyan(args.server)}`);
3967
5361
  } catch (err) {
3968
- spin.stop(`${pc19.red("\u2717")} Failed to store secret`);
3969
- console.error(pc19.dim(String(err)));
5362
+ spin.stop(`${pc28.red("\u2717")} Failed to store secret`);
5363
+ console.error(pc28.dim(String(err)));
3970
5364
  process.exit(1);
3971
5365
  }
3972
- p9.outro(pc19.dim("Secret encrypted and saved to vault."));
5366
+ p10.outro(pc28.dim("Secret encrypted and saved to vault."));
3973
5367
  }
3974
5368
  });
3975
- var listCommand3 = defineCommand20({
5369
+ var listCommand3 = defineCommand29({
3976
5370
  meta: { name: "list", description: "List secret keys stored in the vault" },
3977
5371
  args: {
3978
5372
  server: {
@@ -3984,75 +5378,270 @@ var listCommand3 = defineCommand20({
3984
5378
  async run({ args }) {
3985
5379
  const results = listSecrets(args.server || void 0);
3986
5380
  if (results.length === 0) {
3987
- const filter = args.server ? ` for ${pc19.cyan(args.server)}` : "";
3988
- console.log(pc19.dim(`No secrets stored${filter}.`));
5381
+ const filter = args.server ? ` for ${pc28.cyan(args.server)}` : "";
5382
+ console.log(pc28.dim(`No secrets stored${filter}.`));
3989
5383
  return;
3990
5384
  }
3991
5385
  console.log("");
3992
5386
  for (const { server, keys } of results) {
3993
- console.log(pc19.bold(pc19.cyan(server)));
5387
+ console.log(pc28.bold(pc28.cyan(server)));
3994
5388
  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"))}`);
5389
+ console.log(` ${pc28.green("\u25CF")} ${pc28.bold(key)} ${pc28.dim(maskValue("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"))}`);
5390
+ }
5391
+ console.log("");
5392
+ }
5393
+ const total = results.reduce((n, r) => n + r.keys.length, 0);
5394
+ console.log(
5395
+ pc28.dim(
5396
+ ` ${total} secret${total !== 1 ? "s" : ""} in ${results.length} server${results.length !== 1 ? "s" : ""}`
5397
+ )
5398
+ );
5399
+ }
5400
+ });
5401
+ var removeCommand2 = defineCommand29({
5402
+ meta: { name: "remove", description: "Delete a secret from the vault" },
5403
+ args: {
5404
+ server: {
5405
+ type: "positional",
5406
+ description: "Server name",
5407
+ required: true
5408
+ },
5409
+ key: {
5410
+ type: "positional",
5411
+ description: "Secret key to remove",
5412
+ required: true
5413
+ }
5414
+ },
5415
+ async run({ args }) {
5416
+ const confirmed = await p10.confirm({
5417
+ message: `Remove ${pc28.bold(args.key)} from ${pc28.cyan(args.server)}?`,
5418
+ initialValue: false
5419
+ });
5420
+ if (p10.isCancel(confirmed) || !confirmed) {
5421
+ p10.cancel("Cancelled.");
5422
+ return;
5423
+ }
5424
+ try {
5425
+ removeSecret(args.server, args.key);
5426
+ console.log(`${pc28.green("\u2713")} Removed ${pc28.bold(args.key)} from ${pc28.cyan(args.server)}`);
5427
+ } catch (err) {
5428
+ console.error(`${pc28.red("\u2717")} Failed to remove secret`);
5429
+ console.error(pc28.dim(String(err)));
5430
+ process.exit(1);
5431
+ }
5432
+ }
5433
+ });
5434
+ var secrets_default = defineCommand29({
5435
+ meta: {
5436
+ name: "secrets",
5437
+ description: "Manage encrypted secrets for MCP servers"
5438
+ },
5439
+ subCommands: {
5440
+ set: setCommand2,
5441
+ list: listCommand3,
5442
+ remove: removeCommand2
5443
+ }
5444
+ });
5445
+
5446
+ // src/commands/status.ts
5447
+ import { defineCommand as defineCommand30 } from "citty";
5448
+ import { createSpinner as createSpinner5 } from "nanospinner";
5449
+ import pc29 from "picocolors";
5450
+
5451
+ // src/core/status-checker.ts
5452
+ import { execSync as execSync4 } from "child_process";
5453
+ import { spawn as spawn6 } from "child_process";
5454
+ var MCP_INIT_REQUEST2 = JSON.stringify({
5455
+ jsonrpc: "2.0",
5456
+ id: 1,
5457
+ method: "initialize",
5458
+ params: {
5459
+ protocolVersion: "2024-11-05",
5460
+ capabilities: {},
5461
+ clientInfo: { name: "mcpman-status", version: "0.9.0" }
5462
+ }
5463
+ });
5464
+ async function probeServer(name, entry, timeoutMs = 3e3) {
5465
+ const start = Date.now();
5466
+ return new Promise((resolve) => {
5467
+ let settled = false;
5468
+ const settle = (status) => {
5469
+ if (!settled) {
5470
+ settled = true;
5471
+ resolve(status);
5472
+ }
5473
+ };
5474
+ let child = null;
5475
+ const timer = setTimeout(() => {
5476
+ child?.kill();
5477
+ settle({
5478
+ name,
5479
+ alive: false,
5480
+ responseTimeMs: null,
5481
+ error: "timeout"
5482
+ });
5483
+ }, timeoutMs);
5484
+ try {
5485
+ child = spawn6(entry.command, entry.args ?? [], {
5486
+ stdio: ["pipe", "pipe", "pipe"],
5487
+ env: { ...process.env }
5488
+ });
5489
+ } catch (err) {
5490
+ clearTimeout(timer);
5491
+ settle({
5492
+ name,
5493
+ alive: false,
5494
+ responseTimeMs: null,
5495
+ error: String(err)
5496
+ });
5497
+ return;
5498
+ }
5499
+ let stdout = "";
5500
+ child.stdout?.on("data", (chunk) => {
5501
+ stdout += chunk.toString();
5502
+ const lines = stdout.split("\n");
5503
+ for (const line of lines) {
5504
+ const trimmed = line.trim();
5505
+ if (!trimmed) continue;
5506
+ try {
5507
+ const parsed = JSON.parse(trimmed);
5508
+ if (parsed.jsonrpc === "2.0" && "result" in parsed) {
5509
+ clearTimeout(timer);
5510
+ child?.kill();
5511
+ settle({
5512
+ name,
5513
+ alive: true,
5514
+ responseTimeMs: Date.now() - start
5515
+ });
5516
+ return;
5517
+ }
5518
+ } catch {
5519
+ }
3996
5520
  }
3997
- console.log("");
5521
+ });
5522
+ child.on("error", (err) => {
5523
+ clearTimeout(timer);
5524
+ settle({
5525
+ name,
5526
+ alive: false,
5527
+ responseTimeMs: null,
5528
+ error: err.message
5529
+ });
5530
+ });
5531
+ child.on("exit", (code) => {
5532
+ if (!settled) {
5533
+ clearTimeout(timer);
5534
+ settle({
5535
+ name,
5536
+ alive: false,
5537
+ responseTimeMs: null,
5538
+ error: `exited with code ${code}`
5539
+ });
5540
+ }
5541
+ });
5542
+ try {
5543
+ child.stdin?.write(`${MCP_INIT_REQUEST2}
5544
+ `);
5545
+ } catch {
3998
5546
  }
3999
- const total = results.reduce((n, r) => n + r.keys.length, 0);
5547
+ });
5548
+ }
5549
+ async function getServerStatuses(serverName) {
5550
+ const lockfile = readLockfile();
5551
+ const { servers } = lockfile;
5552
+ if (serverName) {
5553
+ const entry = servers[serverName];
5554
+ if (!entry) {
5555
+ return [{ name: serverName, alive: false, responseTimeMs: null, error: "not in lockfile" }];
5556
+ }
5557
+ return [await probeServer(serverName, entry)];
5558
+ }
5559
+ if (Object.keys(servers).length === 0) return [];
5560
+ return Promise.all(Object.entries(servers).map(([name, entry]) => probeServer(name, entry)));
5561
+ }
5562
+
5563
+ // src/commands/status.ts
5564
+ function pad3(s, width) {
5565
+ return s.length >= width ? s : s + " ".repeat(width - s.length);
5566
+ }
5567
+ function formatStatus(s) {
5568
+ return s.alive ? pc29.green("alive") : pc29.red("dead");
5569
+ }
5570
+ function formatResponseTime(s) {
5571
+ if (!s.alive || s.responseTimeMs === null) return pc29.dim("\u2014");
5572
+ return pc29.cyan(`${s.responseTimeMs}ms`);
5573
+ }
5574
+ function printTable(statuses) {
5575
+ const nameW = Math.max(6, ...statuses.map((s) => s.name.length));
5576
+ const header = ` ${pad3("SERVER", nameW)} ${pad3("STATUS", 7)} ${pad3("RESPONSE", 10)} ERROR`;
5577
+ console.log(pc29.dim(header));
5578
+ console.log(
5579
+ pc29.dim(` ${"\u2500".repeat(nameW)} ${"\u2500".repeat(7)} ${"\u2500".repeat(10)} ${"\u2500".repeat(20)}`)
5580
+ );
5581
+ for (const s of statuses) {
5582
+ const errStr = s.error ? pc29.dim(s.error) : "";
4000
5583
  console.log(
4001
- pc19.dim(
4002
- ` ${total} secret${total !== 1 ? "s" : ""} in ${results.length} server${results.length !== 1 ? "s" : ""}`
4003
- )
5584
+ ` ${pad3(s.name, nameW)} ${pad3(
5585
+ formatStatus(s),
5586
+ 7 + 10
5587
+ /* color codes */
5588
+ )} ${pad3(formatResponseTime(s), 10 + 10)} ${errStr}`
4004
5589
  );
4005
5590
  }
4006
- });
4007
- var removeCommand2 = defineCommand20({
4008
- meta: { name: "remove", description: "Delete a secret from the vault" },
5591
+ }
5592
+ var status_default = defineCommand30({
5593
+ meta: {
5594
+ name: "status",
5595
+ description: "Show live process status for installed MCP servers"
5596
+ },
4009
5597
  args: {
4010
5598
  server: {
4011
- type: "positional",
4012
- description: "Server name",
4013
- required: true
5599
+ type: "string",
5600
+ description: "Check a specific server by name"
4014
5601
  },
4015
- key: {
4016
- type: "positional",
4017
- description: "Secret key to remove",
4018
- required: true
5602
+ json: {
5603
+ type: "boolean",
5604
+ description: "Output results as JSON",
5605
+ default: false
4019
5606
  }
4020
5607
  },
4021
5608
  async run({ args }) {
4022
- const confirmed = await p9.confirm({
4023
- message: `Remove ${pc19.bold(args.key)} from ${pc19.cyan(args.server)}?`,
4024
- initialValue: false
4025
- });
4026
- if (p9.isCancel(confirmed) || !confirmed) {
4027
- p9.cancel("Cancelled.");
4028
- return;
4029
- }
5609
+ const label = args.server ? `Probing ${args.server}...` : "Probing all servers...";
5610
+ const spinner5 = createSpinner5(label).start();
5611
+ let statuses;
4030
5612
  try {
4031
- removeSecret(args.server, args.key);
4032
- console.log(`${pc19.green("\u2713")} Removed ${pc19.bold(args.key)} from ${pc19.cyan(args.server)}`);
5613
+ statuses = await getServerStatuses(args.server);
4033
5614
  } catch (err) {
4034
- console.error(`${pc19.red("\u2717")} Failed to remove secret`);
4035
- console.error(pc19.dim(String(err)));
5615
+ spinner5.error({ text: "Status check failed" });
5616
+ console.error(pc29.red(String(err)));
4036
5617
  process.exit(1);
4037
5618
  }
4038
- }
4039
- });
4040
- var secrets_default = defineCommand20({
4041
- meta: {
4042
- name: "secrets",
4043
- description: "Manage encrypted secrets for MCP servers"
4044
- },
4045
- subCommands: {
4046
- set: setCommand2,
4047
- list: listCommand3,
4048
- remove: removeCommand2
5619
+ spinner5.success({ text: `Checked ${statuses.length} server(s)` });
5620
+ if (statuses.length === 0) {
5621
+ console.log(pc29.dim("\n No MCP servers installed.\n"));
5622
+ return;
5623
+ }
5624
+ if (args.json) {
5625
+ console.log(JSON.stringify(statuses, null, 2));
5626
+ return;
5627
+ }
5628
+ console.log(pc29.bold("\n mcpman status\n"));
5629
+ printTable(statuses);
5630
+ const alive = statuses.filter((s) => s.alive).length;
5631
+ const dead = statuses.length - alive;
5632
+ const parts = [];
5633
+ if (alive > 0) parts.push(pc29.green(`${alive} alive`));
5634
+ if (dead > 0) parts.push(pc29.red(`${dead} dead`));
5635
+ console.log(`
5636
+ ${parts.join(" \xB7 ")}
5637
+ `);
4049
5638
  }
4050
5639
  });
4051
5640
 
4052
5641
  // src/commands/sync.ts
4053
- import * as p10 from "@clack/prompts";
4054
- import { defineCommand as defineCommand21 } from "citty";
4055
- import pc20 from "picocolors";
5642
+ import * as p11 from "@clack/prompts";
5643
+ import { defineCommand as defineCommand31 } from "citty";
5644
+ import pc30 from "picocolors";
4056
5645
 
4057
5646
  // src/core/config-diff.ts
4058
5647
  function reconstructServerEntry(lockEntry) {
@@ -4197,14 +5786,14 @@ async function getClientConfigs() {
4197
5786
  }
4198
5787
 
4199
5788
  // src/commands/sync.ts
4200
- var VALID_CLIENTS = ["claude-desktop", "cursor", "vscode", "windsurf"];
4201
- var CLIENT_DISPLAY3 = {
5789
+ var VALID_CLIENTS2 = ["claude-desktop", "cursor", "vscode", "windsurf"];
5790
+ var CLIENT_DISPLAY4 = {
4202
5791
  "claude-desktop": "Claude Desktop",
4203
5792
  cursor: "Cursor",
4204
5793
  vscode: "VS Code",
4205
5794
  windsurf: "Windsurf"
4206
5795
  };
4207
- var sync_default = defineCommand21({
5796
+ var sync_default = defineCommand31({
4208
5797
  meta: {
4209
5798
  name: "sync",
4210
5799
  description: "Sync MCP server configs across all detected AI clients"
@@ -4231,20 +5820,20 @@ var sync_default = defineCommand21({
4231
5820
  }
4232
5821
  },
4233
5822
  async run({ args }) {
4234
- p10.intro(`${pc20.cyan("mcpman sync")}`);
5823
+ p11.intro(`${pc30.cyan("mcpman sync")}`);
4235
5824
  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(", ")}`
5825
+ if (sourceClient && !VALID_CLIENTS2.includes(sourceClient)) {
5826
+ p11.log.error(
5827
+ `Invalid --source "${sourceClient}". Must be one of: ${VALID_CLIENTS2.join(", ")}`
4239
5828
  );
4240
5829
  process.exit(1);
4241
5830
  }
4242
- const spinner5 = p10.spinner();
5831
+ const spinner5 = p11.spinner();
4243
5832
  spinner5.start("Detecting clients and reading configs...");
4244
5833
  const { configs, handlers } = await getClientConfigs();
4245
5834
  spinner5.stop(`Found ${configs.size} client(s)`);
4246
5835
  if (configs.size === 0) {
4247
- p10.log.warn(
5836
+ p11.log.warn(
4248
5837
  "No AI clients detected. Install Claude Desktop, Cursor, VS Code, or Windsurf first."
4249
5838
  );
4250
5839
  process.exit(0);
@@ -4253,10 +5842,10 @@ var sync_default = defineCommand21({
4253
5842
  let actions;
4254
5843
  if (sourceClient) {
4255
5844
  if (!configs.has(sourceClient)) {
4256
- p10.log.error(`Source client "${sourceClient}" is not detected or its config is unreadable.`);
5845
+ p11.log.error(`Source client "${sourceClient}" is not detected or its config is unreadable.`);
4257
5846
  process.exit(1);
4258
5847
  }
4259
- p10.log.info(`Using ${CLIENT_DISPLAY3[sourceClient]} as source of truth`);
5848
+ p11.log.info(`Using ${CLIENT_DISPLAY4[sourceClient]} as source of truth`);
4260
5849
  actions = computeDiffFromClient(sourceClient, configs, diffOptions);
4261
5850
  } else {
4262
5851
  const lockfile = readLockfile();
@@ -4267,32 +5856,32 @@ var sync_default = defineCommand21({
4267
5856
  const extraCount = actions.filter((a) => a.action === "extra").length;
4268
5857
  const removeCount = actions.filter((a) => a.action === "remove").length;
4269
5858
  if (addCount === 0 && removeCount === 0 && extraCount === 0) {
4270
- p10.outro(pc20.green("All clients are in sync."));
5859
+ p11.outro(pc30.green("All clients are in sync."));
4271
5860
  process.exit(0);
4272
5861
  }
4273
5862
  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 "));
5863
+ if (addCount > 0) parts.push(pc30.green(`${addCount} to add`));
5864
+ if (removeCount > 0) parts.push(pc30.red(`${removeCount} to remove`));
5865
+ if (extraCount > 0) parts.push(pc30.yellow(`${extraCount} extra (informational)`));
5866
+ p11.log.info(parts.join(" \xB7 "));
4278
5867
  if (args["dry-run"]) {
4279
- p10.outro(pc20.dim("Dry run \u2014 no changes applied."));
5868
+ p11.outro(pc30.dim("Dry run \u2014 no changes applied."));
4280
5869
  process.exit(1);
4281
5870
  }
4282
5871
  if (addCount === 0 && removeCount === 0) {
4283
- p10.outro(pc20.dim("No additions needed. Extra servers left untouched."));
5872
+ p11.outro(pc30.dim("No additions needed. Extra servers left untouched."));
4284
5873
  process.exit(1);
4285
5874
  }
4286
5875
  if (!args.yes) {
4287
5876
  const actionParts = [];
4288
5877
  if (addCount > 0) actionParts.push(`${addCount} addition(s)`);
4289
5878
  if (removeCount > 0) actionParts.push(`${removeCount} removal(s)`);
4290
- const confirmed = await p10.confirm({
5879
+ const confirmed = await p11.confirm({
4291
5880
  message: `Apply ${actionParts.join(" and ")} to client configs?`,
4292
5881
  initialValue: true
4293
5882
  });
4294
- if (p10.isCancel(confirmed) || !confirmed) {
4295
- p10.outro(pc20.dim("Cancelled \u2014 no changes applied."));
5883
+ if (p11.isCancel(confirmed) || !confirmed) {
5884
+ p11.outro(pc30.dim("Cancelled \u2014 no changes applied."));
4296
5885
  process.exit(0);
4297
5886
  }
4298
5887
  }
@@ -4300,40 +5889,40 @@ var sync_default = defineCommand21({
4300
5889
  const result = await applySyncActions(actions, handlers);
4301
5890
  spinner5.stop("Done");
4302
5891
  if (result.applied > 0) {
4303
- p10.log.success(`Added ${result.applied} server(s) to client configs.`);
5892
+ p11.log.success(`Added ${result.applied} server(s) to client configs.`);
4304
5893
  }
4305
5894
  if (result.removed > 0) {
4306
- p10.log.success(`Removed ${result.removed} server(s) from client configs.`);
5895
+ p11.log.success(`Removed ${result.removed} server(s) from client configs.`);
4307
5896
  }
4308
5897
  if (result.failed > 0) {
4309
5898
  for (const e of result.errors) {
4310
- p10.log.error(`Failed to sync "${e.server}" on ${e.client}: ${e.error}`);
5899
+ p11.log.error(`Failed to sync "${e.server}" on ${e.client}: ${e.error}`);
4311
5900
  }
4312
5901
  }
4313
- p10.outro(
4314
- result.failed === 0 ? pc20.green("Sync complete.") : pc20.yellow("Sync complete with errors.")
5902
+ p11.outro(
5903
+ result.failed === 0 ? pc30.green("Sync complete.") : pc30.yellow("Sync complete with errors.")
4315
5904
  );
4316
5905
  process.exit(result.failed > 0 ? 1 : 0);
4317
5906
  }
4318
5907
  });
4319
5908
  function printDiffTable(actions) {
4320
5909
  if (actions.length === 0) {
4321
- p10.log.info("No actions to display.");
5910
+ p11.log.info("No actions to display.");
4322
5911
  return;
4323
5912
  }
4324
5913
  const nameWidth = Math.max(6, ...actions.map((a) => a.server.length));
4325
5914
  const clientWidth = Math.max(
4326
5915
  6,
4327
- ...actions.map((a) => CLIENT_DISPLAY3[a.client]?.length ?? a.client.length)
5916
+ ...actions.map((a) => CLIENT_DISPLAY4[a.client]?.length ?? a.client.length)
4328
5917
  );
4329
- const header = ` ${pad3("SERVER", nameWidth)} ${pad3("CLIENT", clientWidth)} STATUS`;
4330
- console.log(pc20.dim(header));
4331
- console.log(pc20.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientWidth)} ------`));
5918
+ const header = ` ${pad4("SERVER", nameWidth)} ${pad4("CLIENT", clientWidth)} STATUS`;
5919
+ console.log(pc30.dim(header));
5920
+ console.log(pc30.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientWidth)} ------`));
4332
5921
  for (const action of actions) {
4333
- const clientDisplay = CLIENT_DISPLAY3[action.client] ?? action.client;
5922
+ const clientDisplay = CLIENT_DISPLAY4[action.client] ?? action.client;
4334
5923
  const [icon, statusText] = formatAction(action.action);
4335
5924
  console.log(
4336
- ` ${pad3(action.server, nameWidth)} ${pad3(clientDisplay, clientWidth)} ${icon} ${statusText}`
5925
+ ` ${pad4(action.server, nameWidth)} ${pad4(clientDisplay, clientWidth)} ${icon} ${statusText}`
4337
5926
  );
4338
5927
  }
4339
5928
  console.log("");
@@ -4341,25 +5930,193 @@ function printDiffTable(actions) {
4341
5930
  function formatAction(action) {
4342
5931
  switch (action) {
4343
5932
  case "add":
4344
- return [pc20.green("+"), pc20.green("missing \u2014 will add")];
5933
+ return [pc30.green("+"), pc30.green("missing \u2014 will add")];
4345
5934
  case "extra":
4346
- return [pc20.yellow("?"), pc20.yellow("extra (not in lockfile)")];
5935
+ return [pc30.yellow("?"), pc30.yellow("extra (not in lockfile)")];
4347
5936
  case "remove":
4348
- return [pc20.red("\u2013"), pc20.red("extra \u2014 will remove")];
5937
+ return [pc30.red("\u2013"), pc30.red("extra \u2014 will remove")];
4349
5938
  case "ok":
4350
- return [pc20.dim("\xB7"), pc20.dim("in sync")];
5939
+ return [pc30.dim("\xB7"), pc30.dim("in sync")];
4351
5940
  }
4352
5941
  }
4353
- function pad3(s, width) {
5942
+ function pad4(s, width) {
4354
5943
  return s.length >= width ? s : s + " ".repeat(width - s.length);
4355
5944
  }
4356
5945
 
5946
+ // src/commands/template.ts
5947
+ import { defineCommand as defineCommand32 } from "citty";
5948
+ import pc31 from "picocolors";
5949
+
5950
+ // src/core/template-service.ts
5951
+ import fs17 from "fs";
5952
+ import path14 from "path";
5953
+ function templatePath(name, dir) {
5954
+ return path14.join(dir ?? getTemplatesDir(), `${name}.json`);
5955
+ }
5956
+ function ensureDir2(dir) {
5957
+ const target = dir ?? getTemplatesDir();
5958
+ if (!fs17.existsSync(target)) {
5959
+ fs17.mkdirSync(target, { recursive: true });
5960
+ }
5961
+ }
5962
+ function saveTemplate(name, opts = {}) {
5963
+ const lockfile = readLockfile();
5964
+ const servers = Object.entries(lockfile.servers).map(([sName, entry]) => ({
5965
+ name: sName,
5966
+ source: entry.source,
5967
+ version: entry.version,
5968
+ args: entry.args
5969
+ }));
5970
+ const template = {
5971
+ name,
5972
+ description: opts.description ?? "",
5973
+ servers,
5974
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
5975
+ };
5976
+ ensureDir2(opts.dir);
5977
+ fs17.writeFileSync(templatePath(name, opts.dir), JSON.stringify(template, null, 2), "utf-8");
5978
+ }
5979
+ function loadTemplate(name, dir) {
5980
+ const file = templatePath(name, dir);
5981
+ if (!fs17.existsSync(file)) return null;
5982
+ try {
5983
+ return JSON.parse(fs17.readFileSync(file, "utf-8"));
5984
+ } catch {
5985
+ return null;
5986
+ }
5987
+ }
5988
+ function listTemplates(dir) {
5989
+ const target = dir ?? getTemplatesDir();
5990
+ if (!fs17.existsSync(target)) return [];
5991
+ return fs17.readdirSync(target).filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, "")).sort();
5992
+ }
5993
+ function deleteTemplate(name, dir) {
5994
+ const file = templatePath(name, dir);
5995
+ if (fs17.existsSync(file)) {
5996
+ fs17.unlinkSync(file);
5997
+ }
5998
+ }
5999
+ function applyTemplate(name, dir) {
6000
+ const template = loadTemplate(name, dir);
6001
+ if (!template) {
6002
+ throw new Error(`Template "${name}" not found`);
6003
+ }
6004
+ return template.servers.map((s) => {
6005
+ const versionSuffix = s.version ? `@${s.version}` : "";
6006
+ return `mcpman install ${s.name}${versionSuffix}`;
6007
+ });
6008
+ }
6009
+
6010
+ // src/commands/template.ts
6011
+ var saveCmd = defineCommand32({
6012
+ meta: { name: "save", description: "Save current lockfile servers as a named template" },
6013
+ args: {
6014
+ name: { type: "positional", description: "Template name", required: true },
6015
+ description: {
6016
+ type: "string",
6017
+ description: "Optional description for the template"
6018
+ }
6019
+ },
6020
+ run({ args }) {
6021
+ try {
6022
+ saveTemplate(args.name, { description: args.description });
6023
+ const tmpl = loadTemplate(args.name);
6024
+ const count = tmpl?.servers.length ?? 0;
6025
+ console.log(
6026
+ `${pc31.green("\u2713")} Template ${pc31.cyan(pc31.bold(args.name))} saved (${count} server${count !== 1 ? "s" : ""})`
6027
+ );
6028
+ } catch (err) {
6029
+ console.error(`${pc31.red("\u2717")} ${String(err)}`);
6030
+ process.exit(1);
6031
+ }
6032
+ }
6033
+ });
6034
+ var applyCmd = defineCommand32({
6035
+ meta: { name: "apply", description: "Print install commands for a template" },
6036
+ args: {
6037
+ name: { type: "positional", description: "Template name", required: true }
6038
+ },
6039
+ run({ args }) {
6040
+ let commands;
6041
+ try {
6042
+ commands = applyTemplate(args.name);
6043
+ } catch (err) {
6044
+ console.error(`${pc31.red("\u2717")} ${String(err)}`);
6045
+ process.exit(1);
6046
+ }
6047
+ if (commands.length === 0) {
6048
+ console.log(pc31.dim(`
6049
+ Template "${args.name}" has no servers.
6050
+ `));
6051
+ return;
6052
+ }
6053
+ console.log(pc31.bold(`
6054
+ Template: ${pc31.cyan(args.name)}
6055
+ `));
6056
+ console.log(pc31.dim(" Run the following commands to install all servers:\n"));
6057
+ for (const cmd of commands) {
6058
+ console.log(` ${pc31.green("$")} ${cmd}`);
6059
+ }
6060
+ console.log();
6061
+ }
6062
+ });
6063
+ var listCmd5 = defineCommand32({
6064
+ meta: { name: "list", description: "List all saved templates" },
6065
+ args: {},
6066
+ run() {
6067
+ const names = listTemplates();
6068
+ if (names.length === 0) {
6069
+ console.log(pc31.dim("\n No templates saved. Use `mcpman template save <name>`.\n"));
6070
+ return;
6071
+ }
6072
+ console.log(pc31.bold("\n mcpman templates\n"));
6073
+ console.log(pc31.dim(` ${"\u2500".repeat(50)}`));
6074
+ for (const name of names) {
6075
+ const tmpl = loadTemplate(name);
6076
+ const count = tmpl?.servers.length ?? 0;
6077
+ const desc = tmpl?.description ? pc31.dim(` \u2014 ${tmpl.description}`) : "";
6078
+ console.log(
6079
+ ` ${pc31.cyan(pc31.bold(name.padEnd(20)))} ${pc31.dim(`${count} server${count !== 1 ? "s" : ""}`)}${desc}`
6080
+ );
6081
+ }
6082
+ console.log(pc31.dim(` ${"\u2500".repeat(50)}
6083
+ `));
6084
+ }
6085
+ });
6086
+ var deleteCmd2 = defineCommand32({
6087
+ meta: { name: "delete", description: "Delete a saved template" },
6088
+ args: {
6089
+ name: { type: "positional", description: "Template name", required: true }
6090
+ },
6091
+ run({ args }) {
6092
+ const existing = listTemplates();
6093
+ if (!existing.includes(args.name)) {
6094
+ console.error(`${pc31.red("\u2717")} Template "${args.name}" does not exist.`);
6095
+ process.exit(1);
6096
+ }
6097
+ deleteTemplate(args.name);
6098
+ console.log(`${pc31.green("\u2713")} Template ${pc31.cyan(args.name)} deleted`);
6099
+ }
6100
+ });
6101
+ var template_default = defineCommand32({
6102
+ meta: {
6103
+ name: "template",
6104
+ description: "Manage install templates"
6105
+ },
6106
+ subCommands: {
6107
+ save: saveCmd,
6108
+ apply: applyCmd,
6109
+ list: listCmd5,
6110
+ delete: deleteCmd2
6111
+ }
6112
+ });
6113
+
4357
6114
  // src/commands/test-command.ts
4358
- import { defineCommand as defineCommand22 } from "citty";
4359
- import pc21 from "picocolors";
6115
+ import { defineCommand as defineCommand33 } from "citty";
6116
+ import pc32 from "picocolors";
4360
6117
 
4361
6118
  // src/core/mcp-tester.ts
4362
- import { spawn as spawn4 } from "child_process";
6119
+ import { spawn as spawn7 } from "child_process";
4363
6120
  var TIMEOUT_MS = 1e4;
4364
6121
  async function testMcpServer(serverName, command, args, env) {
4365
6122
  const start = Date.now();
@@ -4369,7 +6126,7 @@ async function testMcpServer(serverName, command, args, env) {
4369
6126
  let initOk = false;
4370
6127
  let toolsOk = false;
4371
6128
  let tools = [];
4372
- const child = spawn4(command, args, {
6129
+ const child = spawn7(command, args, {
4373
6130
  env: { ...process.env, ...env },
4374
6131
  stdio: ["pipe", "pipe", "pipe"]
4375
6132
  });
@@ -4454,7 +6211,7 @@ async function testMcpServer(serverName, command, args, env) {
4454
6211
  }
4455
6212
 
4456
6213
  // src/commands/test-command.ts
4457
- var test_command_default = defineCommand22({
6214
+ var test_command_default = defineCommand33({
4458
6215
  meta: {
4459
6216
  name: "test",
4460
6217
  description: "Test MCP server connectivity and capabilities"
@@ -4475,10 +6232,10 @@ var test_command_default = defineCommand22({
4475
6232
  const lockfile = readLockfile();
4476
6233
  const serverNames = args.all ? Object.keys(lockfile.servers) : args.server ? [args.server] : [];
4477
6234
  if (serverNames.length === 0) {
4478
- console.error(pc21.red(" Error: Specify a server name or use --all."));
6235
+ console.error(pc32.red(" Error: Specify a server name or use --all."));
4479
6236
  process.exit(1);
4480
6237
  }
4481
- console.log(pc21.bold(`
6238
+ console.log(pc32.bold(`
4482
6239
  mcpman test \u2014 ${serverNames.length} server(s)
4483
6240
  `));
4484
6241
  let passed = 0;
@@ -4486,7 +6243,7 @@ var test_command_default = defineCommand22({
4486
6243
  for (const name of serverNames) {
4487
6244
  const entry = lockfile.servers[name];
4488
6245
  if (!entry) {
4489
- console.log(` ${pc21.red("\u2717")} ${pc21.bold(name)} \u2014 not installed`);
6246
+ console.log(` ${pc32.red("\u2717")} ${pc32.bold(name)} \u2014 not installed`);
4490
6247
  failed++;
4491
6248
  continue;
4492
6249
  }
@@ -4497,27 +6254,27 @@ var test_command_default = defineCommand22({
4497
6254
  if (result.passed) {
4498
6255
  passed++;
4499
6256
  console.log(
4500
- ` ${pc21.green("\u2713")} ${pc21.bold(name)} ${pc21.dim(`(${result.responseTimeMs}ms)`)}`
6257
+ ` ${pc32.green("\u2713")} ${pc32.bold(name)} ${pc32.dim(`(${result.responseTimeMs}ms)`)}`
4501
6258
  );
4502
6259
  if (result.tools.length > 0) {
4503
- console.log(pc21.dim(` Tools: ${result.tools.join(", ")}`));
6260
+ console.log(pc32.dim(` Tools: ${result.tools.join(", ")}`));
4504
6261
  }
4505
6262
  } else {
4506
6263
  failed++;
4507
- console.log(` ${pc21.red("\u2717")} ${pc21.bold(name)} ${pc21.dim(`(${result.responseTimeMs}ms)`)}`);
6264
+ console.log(` ${pc32.red("\u2717")} ${pc32.bold(name)} ${pc32.dim(`(${result.responseTimeMs}ms)`)}`);
4508
6265
  if (result.error) {
4509
- console.log(` ${pc21.red(result.error)}`);
6266
+ console.log(` ${pc32.red(result.error)}`);
4510
6267
  }
4511
6268
  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")}`
6269
+ ` ${pc32.dim("initialize:")} ${result.initializeOk ? pc32.green("ok") : pc32.red("fail")} ${pc32.dim("tools/list:")} ${result.toolsListOk ? pc32.green("ok") : pc32.red("fail")}`
4513
6270
  );
4514
6271
  }
4515
6272
  }
4516
- console.log(pc21.dim(`
6273
+ console.log(pc32.dim(`
4517
6274
  ${"\u2500".repeat(40)}`));
4518
6275
  const parts = [];
4519
- if (passed > 0) parts.push(pc21.green(`${passed} passed`));
4520
- if (failed > 0) parts.push(pc21.red(`${failed} failed`));
6276
+ if (passed > 0) parts.push(pc32.green(`${passed} passed`));
6277
+ if (failed > 0) parts.push(pc32.red(`${failed} failed`));
4521
6278
  console.log(` ${parts.join(", ")}
4522
6279
  `);
4523
6280
  if (failed > 0) process.exit(1);
@@ -4535,24 +6292,24 @@ async function loadVaultSecrets3(serverName) {
4535
6292
  }
4536
6293
 
4537
6294
  // src/commands/update.ts
4538
- import * as p11 from "@clack/prompts";
4539
- import { defineCommand as defineCommand23 } from "citty";
4540
- import pc23 from "picocolors";
6295
+ import * as p12 from "@clack/prompts";
6296
+ import { defineCommand as defineCommand34 } from "citty";
6297
+ import pc34 from "picocolors";
4541
6298
 
4542
6299
  // src/core/update-notifier.ts
4543
- import fs11 from "fs";
6300
+ import fs18 from "fs";
4544
6301
  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");
6302
+ import path15 from "path";
6303
+ import pc33 from "picocolors";
6304
+ var CACHE_FILE = path15.join(os3.homedir(), ".mcpman", ".update-check");
4548
6305
  var TTL_MS = 24 * 60 * 60 * 1e3;
4549
6306
  function writeUpdateCache(data) {
4550
6307
  try {
4551
- const dir = path13.dirname(CACHE_FILE);
4552
- if (!fs11.existsSync(dir)) fs11.mkdirSync(dir, { recursive: true });
6308
+ const dir = path15.dirname(CACHE_FILE);
6309
+ if (!fs18.existsSync(dir)) fs18.mkdirSync(dir, { recursive: true });
4553
6310
  const tmp = `${CACHE_FILE}.tmp`;
4554
- fs11.writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
4555
- fs11.renameSync(tmp, CACHE_FILE);
6311
+ fs18.writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
6312
+ fs18.renameSync(tmp, CACHE_FILE);
4556
6313
  } catch {
4557
6314
  }
4558
6315
  }
@@ -4560,13 +6317,13 @@ function writeUpdateCache(data) {
4560
6317
  // src/commands/update.ts
4561
6318
  async function loadClients3() {
4562
6319
  try {
4563
- const mod = await import("./client-detector-CY7WPF3K.js");
6320
+ const mod = await import("./client-detector-O2HN4MUB.js");
4564
6321
  return mod.getInstalledClients();
4565
6322
  } catch {
4566
6323
  return [];
4567
6324
  }
4568
6325
  }
4569
- function printTable(updates) {
6326
+ function printTable2(updates) {
4570
6327
  const NAME_W = 28;
4571
6328
  const VER_W = 10;
4572
6329
  const header = [
@@ -4575,19 +6332,19 @@ function printTable(updates) {
4575
6332
  "LATEST".padEnd(VER_W),
4576
6333
  "STATUS"
4577
6334
  ].join(" ");
4578
- console.log(pc23.bold(`
6335
+ console.log(pc34.bold(`
4579
6336
  ${header}`));
4580
- console.log(pc23.dim(` ${"\u2500".repeat(NAME_W + VER_W * 2 + 20)}`));
6337
+ console.log(pc34.dim(` ${"\u2500".repeat(NAME_W + VER_W * 2 + 20)}`));
4581
6338
  for (const u of updates) {
4582
6339
  const nameCol = u.server.slice(0, NAME_W).padEnd(NAME_W);
4583
6340
  const curCol = u.currentVersion.padEnd(VER_W);
4584
6341
  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");
6342
+ const statusCol = u.hasUpdate ? pc34.yellow(`Update available${u.updateType ? ` [${u.updateType}]` : ""}`) : pc34.green("Up to date");
4586
6343
  console.log(` ${nameCol} ${curCol} ${latCol} ${statusCol}`);
4587
6344
  }
4588
6345
  console.log();
4589
6346
  }
4590
- var update_default = defineCommand23({
6347
+ var update_default = defineCommand34({
4591
6348
  meta: {
4592
6349
  name: "update",
4593
6350
  description: "Check for and apply updates to installed MCP servers"
@@ -4626,7 +6383,7 @@ var update_default = defineCommand23({
4626
6383
  }
4627
6384
  process.exit(1);
4628
6385
  }
4629
- const spinner5 = p11.spinner();
6386
+ const spinner5 = p12.spinner();
4630
6387
  spinner5.start("Checking versions...");
4631
6388
  let updates;
4632
6389
  try {
@@ -4645,53 +6402,53 @@ var update_default = defineCommand23({
4645
6402
  console.log(JSON.stringify(updates, null, 2));
4646
6403
  return;
4647
6404
  }
4648
- printTable(updates);
6405
+ printTable2(updates);
4649
6406
  const outdated = updates.filter((u) => u.hasUpdate);
4650
6407
  if (outdated.length === 0) {
4651
- console.log(pc23.green(" All servers are up to date."));
6408
+ console.log(pc34.green(" All servers are up to date."));
4652
6409
  return;
4653
6410
  }
4654
6411
  if (args.check) {
4655
6412
  console.log(
4656
- pc23.yellow(` ${outdated.length} update(s) available. Run mcpman update to apply.`)
6413
+ pc34.yellow(` ${outdated.length} update(s) available. Run mcpman update to apply.`)
4657
6414
  );
4658
6415
  return;
4659
6416
  }
4660
6417
  if (!args.yes) {
4661
- const confirmed = await p11.confirm({
6418
+ const confirmed = await p12.confirm({
4662
6419
  message: `Apply ${outdated.length} update(s)?`,
4663
6420
  initialValue: true
4664
6421
  });
4665
- if (p11.isCancel(confirmed) || !confirmed) {
4666
- p11.outro("Cancelled.");
6422
+ if (p12.isCancel(confirmed) || !confirmed) {
6423
+ p12.outro("Cancelled.");
4667
6424
  return;
4668
6425
  }
4669
6426
  }
4670
6427
  const clients = await loadClients3();
4671
6428
  let successCount = 0;
4672
6429
  for (const update of outdated) {
4673
- const s = p11.spinner();
6430
+ const s = p12.spinner();
4674
6431
  s.start(`Updating ${update.server}...`);
4675
6432
  const result = await applyServerUpdate(update.server, servers[update.server], clients);
4676
6433
  if (result.success) {
4677
- s.stop(`${pc23.green("\u2713")} ${update.server}: ${result.fromVersion} \u2192 ${result.toVersion}`);
6434
+ s.stop(`${pc34.green("\u2713")} ${update.server}: ${result.fromVersion} \u2192 ${result.toVersion}`);
4678
6435
  successCount++;
4679
6436
  } else {
4680
- s.stop(`${pc23.red("\u2717")} ${update.server}: ${result.error}`);
6437
+ s.stop(`${pc34.red("\u2717")} ${update.server}: ${result.error}`);
4681
6438
  }
4682
6439
  }
4683
6440
  const freshLockfile = readLockfile(resolveLockfilePath());
4684
6441
  const freshUpdates = await checkAllVersions(freshLockfile);
4685
6442
  writeUpdateCache({ lastCheck: (/* @__PURE__ */ new Date()).toISOString(), updates: freshUpdates });
4686
- p11.outro(`${successCount} of ${outdated.length} server(s) updated.`);
6443
+ p12.outro(`${successCount} of ${outdated.length} server(s) updated.`);
4687
6444
  }
4688
6445
  });
4689
6446
 
4690
6447
  // src/commands/upgrade.ts
4691
- import { execSync as execSync2 } from "child_process";
4692
- import { defineCommand as defineCommand24 } from "citty";
4693
- import pc24 from "picocolors";
4694
- var upgrade_default = defineCommand24({
6448
+ import { execSync as execSync5 } from "child_process";
6449
+ import { defineCommand as defineCommand35 } from "citty";
6450
+ import pc35 from "picocolors";
6451
+ var upgrade_default = defineCommand35({
4695
6452
  meta: {
4696
6453
  name: "upgrade",
4697
6454
  description: "Upgrade mcpman to the latest version"
@@ -4704,42 +6461,214 @@ var upgrade_default = defineCommand24({
4704
6461
  }
4705
6462
  },
4706
6463
  async run({ args }) {
4707
- console.log(pc24.dim(` Current version: ${APP_VERSION}`));
6464
+ console.log(pc35.dim(` Current version: ${APP_VERSION}`));
4708
6465
  let latest;
4709
6466
  try {
4710
- latest = execSync2("npm view mcpman version", { encoding: "utf-8", timeout: 15e3 }).trim();
6467
+ latest = execSync5("npm view mcpman version", { encoding: "utf-8", timeout: 15e3 }).trim();
4711
6468
  } catch {
4712
- console.error(pc24.red(" Error: Could not check latest version from npm."));
6469
+ console.error(pc35.red(" Error: Could not check latest version from npm."));
4713
6470
  process.exit(1);
4714
6471
  }
4715
6472
  if (latest === APP_VERSION) {
4716
- console.log(pc24.green(" \u2713 Already on the latest version."));
6473
+ console.log(pc35.green(" \u2713 Already on the latest version."));
4717
6474
  return;
4718
6475
  }
4719
- console.log(pc24.yellow(` Update available: ${APP_VERSION} \u2192 ${latest}`));
6476
+ console.log(pc35.yellow(` Update available: ${APP_VERSION} \u2192 ${latest}`));
4720
6477
  if (args.check) {
4721
- console.log(pc24.dim(" Run mcpman upgrade to install."));
6478
+ console.log(pc35.dim(" Run mcpman upgrade to install."));
4722
6479
  return;
4723
6480
  }
4724
- console.log(pc24.dim(" Installing..."));
6481
+ console.log(pc35.dim(" Installing..."));
4725
6482
  try {
4726
- execSync2(`npm install -g mcpman@${latest}`, { stdio: "inherit", timeout: 6e4 });
4727
- console.log(pc24.green(`
6483
+ execSync5(`npm install -g mcpman@${latest}`, { stdio: "inherit", timeout: 6e4 });
6484
+ console.log(pc35.green(`
4728
6485
  \u2713 Upgraded to mcpman@${latest}`));
4729
6486
  } catch {
4730
- console.error(pc24.red(" Error: Upgrade failed. Try manually: npm install -g mcpman@latest"));
6487
+ console.error(pc35.red(" Error: Upgrade failed. Try manually: npm install -g mcpman@latest"));
6488
+ process.exit(1);
6489
+ }
6490
+ }
6491
+ });
6492
+
6493
+ // src/commands/validate.ts
6494
+ import { defineCommand as defineCommand36 } from "citty";
6495
+ import pc36 from "picocolors";
6496
+
6497
+ // src/core/config-validator.ts
6498
+ import fs19 from "fs";
6499
+ var REQUIRED_LOCK_FIELDS = ["version", "source", "command", "args"];
6500
+ var VALID_SOURCES = ["npm", "smithery", "github", "local"];
6501
+ function validateLockfile(filePath) {
6502
+ const target = filePath ?? resolveLockfilePath();
6503
+ const errors = [];
6504
+ if (!fs19.existsSync(target)) {
6505
+ return { file: target, valid: false, errors: ["Lockfile not found"] };
6506
+ }
6507
+ let data;
6508
+ try {
6509
+ data = JSON.parse(fs19.readFileSync(target, "utf-8"));
6510
+ } catch {
6511
+ return { file: target, valid: false, errors: ["Invalid JSON"] };
6512
+ }
6513
+ if (typeof data !== "object" || data === null) {
6514
+ return { file: target, valid: false, errors: ["Root must be an object"] };
6515
+ }
6516
+ const obj = data;
6517
+ if (obj.lockfileVersion !== 1) {
6518
+ errors.push(`lockfileVersion must be 1, got ${JSON.stringify(obj.lockfileVersion)}`);
6519
+ }
6520
+ if (typeof obj.servers !== "object" || obj.servers === null || Array.isArray(obj.servers)) {
6521
+ errors.push("servers must be an object");
6522
+ return { file: target, valid: errors.length === 0, errors };
6523
+ }
6524
+ const servers = obj.servers;
6525
+ for (const [name, entry] of Object.entries(servers)) {
6526
+ if (typeof entry !== "object" || entry === null) {
6527
+ errors.push(`servers.${name}: must be an object`);
6528
+ continue;
6529
+ }
6530
+ const e = entry;
6531
+ for (const field of REQUIRED_LOCK_FIELDS) {
6532
+ if (!(field in e)) {
6533
+ errors.push(`servers.${name}: missing required field "${field}"`);
6534
+ }
6535
+ }
6536
+ if (e.source && !VALID_SOURCES.includes(e.source)) {
6537
+ errors.push(`servers.${name}: invalid source "${e.source}"`);
6538
+ }
6539
+ if (!Array.isArray(e.args)) {
6540
+ errors.push(`servers.${name}: args must be an array`);
6541
+ }
6542
+ }
6543
+ return { file: target, valid: errors.length === 0, errors };
6544
+ }
6545
+ function validateClientConfig(clientName) {
6546
+ let filePath;
6547
+ try {
6548
+ filePath = resolveConfigPath(clientName);
6549
+ } catch {
6550
+ return { file: clientName, valid: false, errors: [`Unknown client: ${clientName}`] };
6551
+ }
6552
+ const errors = [];
6553
+ if (!fs19.existsSync(filePath)) {
6554
+ return { file: filePath, valid: false, errors: ["Config file not found"] };
6555
+ }
6556
+ let data;
6557
+ try {
6558
+ data = JSON.parse(fs19.readFileSync(filePath, "utf-8"));
6559
+ } catch {
6560
+ return { file: filePath, valid: false, errors: ["Invalid JSON"] };
6561
+ }
6562
+ if (typeof data !== "object" || data === null) {
6563
+ return { file: filePath, valid: false, errors: ["Root must be an object"] };
6564
+ }
6565
+ const obj = data;
6566
+ const servers = obj.mcpServers ?? obj["mcp.servers"];
6567
+ if (!servers) {
6568
+ return { file: filePath, valid: true, errors: [] };
6569
+ }
6570
+ if (typeof servers !== "object" || Array.isArray(servers)) {
6571
+ errors.push("mcpServers must be an object");
6572
+ return { file: filePath, valid: false, errors };
6573
+ }
6574
+ for (const [name, entry] of Object.entries(servers)) {
6575
+ if (typeof entry !== "object" || entry === null) {
6576
+ errors.push(`mcpServers.${name}: must be an object`);
6577
+ continue;
6578
+ }
6579
+ const e = entry;
6580
+ if (!("command" in e) || typeof e.command !== "string") {
6581
+ errors.push(`mcpServers.${name}: missing or invalid "command"`);
6582
+ }
6583
+ if (!("args" in e) || !Array.isArray(e.args)) {
6584
+ errors.push(`mcpServers.${name}: missing or invalid "args" (must be array)`);
6585
+ }
6586
+ }
6587
+ return { file: filePath, valid: errors.length === 0, errors };
6588
+ }
6589
+ function validateAll(lockfilePath) {
6590
+ const results = [validateLockfile(lockfilePath)];
6591
+ const clients = ["claude-desktop", "cursor", "vscode", "windsurf"];
6592
+ for (const client of clients) {
6593
+ const r = validateClientConfig(client);
6594
+ if (!r.errors.includes("Config file not found")) {
6595
+ results.push(r);
6596
+ }
6597
+ }
6598
+ return results;
6599
+ }
6600
+
6601
+ // src/commands/validate.ts
6602
+ function printResult(r) {
6603
+ const icon = r.valid ? pc36.green("\u2713") : pc36.red("\u2717");
6604
+ const status = r.valid ? pc36.green("PASS") : pc36.red("FAIL");
6605
+ console.log(` ${icon} ${pc36.bold(r.file)} ${status}`);
6606
+ if (!r.valid) {
6607
+ for (const err of r.errors) {
6608
+ console.log(` ${pc36.dim("\xB7")} ${pc36.yellow(err)}`);
6609
+ }
6610
+ }
6611
+ }
6612
+ var validate_default = defineCommand36({
6613
+ meta: {
6614
+ name: "validate",
6615
+ description: "Validate lockfile and client configs against expected schema"
6616
+ },
6617
+ args: {
6618
+ client: {
6619
+ type: "string",
6620
+ description: "Validate a specific client config (claude-desktop, cursor, vscode, windsurf)"
6621
+ },
6622
+ json: {
6623
+ type: "boolean",
6624
+ description: "Output results as JSON",
6625
+ default: false
6626
+ }
6627
+ },
6628
+ run({ args }) {
6629
+ let results;
6630
+ if (args.client) {
6631
+ results = [validateClientConfig(args.client)];
6632
+ } else {
6633
+ results = validateAll();
6634
+ }
6635
+ if (args.json) {
6636
+ console.log(JSON.stringify(results, null, 2));
6637
+ const anyFail = results.some((r) => !r.valid);
6638
+ if (anyFail) process.exit(1);
6639
+ return;
6640
+ }
6641
+ console.log(pc36.bold("\n mcpman validate\n"));
6642
+ console.log(pc36.dim(` ${"\u2500".repeat(60)}`));
6643
+ for (const r of results) {
6644
+ printResult(r);
6645
+ }
6646
+ console.log(pc36.dim(`
6647
+ ${"\u2500".repeat(60)}`));
6648
+ const passed = results.filter((r) => r.valid).length;
6649
+ const failed = results.filter((r) => !r.valid).length;
6650
+ if (failed === 0) {
6651
+ console.log(`
6652
+ ${pc36.green("All files valid")} (${passed} checked)
6653
+ `);
6654
+ } else {
6655
+ console.log(
6656
+ `
6657
+ ${pc36.red(`${failed} file(s) failed`)} ${pc36.dim("\xB7")} ${pc36.green(`${passed} passed`)}
6658
+ `
6659
+ );
4731
6660
  process.exit(1);
4732
6661
  }
4733
6662
  }
4734
6663
  });
4735
6664
 
4736
6665
  // src/commands/watch.ts
4737
- import { defineCommand as defineCommand25 } from "citty";
4738
- import pc25 from "picocolors";
6666
+ import { defineCommand as defineCommand37 } from "citty";
6667
+ import pc37 from "picocolors";
4739
6668
 
4740
6669
  // src/core/file-watcher-service.ts
4741
- import { spawn as spawn5 } from "child_process";
4742
- import fs12 from "fs";
6670
+ import { spawn as spawn8 } from "child_process";
6671
+ import fs20 from "fs";
4743
6672
  var IGNORE_PATTERNS = [
4744
6673
  "node_modules",
4745
6674
  ".git",
@@ -4751,7 +6680,7 @@ var IGNORE_PATTERNS = [
4751
6680
  ".tox"
4752
6681
  ];
4753
6682
  function shouldIgnore(filename) {
4754
- return IGNORE_PATTERNS.some((p12) => filename.includes(p12));
6683
+ return IGNORE_PATTERNS.some((p13) => filename.includes(p13));
4755
6684
  }
4756
6685
  function hasWatchedExtension(filename, extensions) {
4757
6686
  return extensions.some((ext) => filename.endsWith(`.${ext}`));
@@ -4777,7 +6706,7 @@ var ServerWatcher = class {
4777
6706
  `);
4778
6707
  this.spawnChild();
4779
6708
  try {
4780
- this.watcher = fs12.watch(options.watchDir, { recursive: true }, (_event, filename) => {
6709
+ this.watcher = fs20.watch(options.watchDir, { recursive: true }, (_event, filename) => {
4781
6710
  if (!filename) return;
4782
6711
  this.onFileChange(filename);
4783
6712
  });
@@ -4812,7 +6741,7 @@ var ServerWatcher = class {
4812
6741
  process.stdout.write("\x1Bc");
4813
6742
  }
4814
6743
  console.log(` [${timestamp()}] Starting ${serverName}...`);
4815
- this.child = spawn5(command, args, { env, stdio: ["pipe", "pipe", "pipe"] });
6744
+ this.child = spawn8(command, args, { env, stdio: ["pipe", "pipe", "pipe"] });
4816
6745
  this.child.stdout?.on("data", (data) => {
4817
6746
  process.stdout.write(` [stdout] ${data.toString().trimEnd()}
4818
6747
  `);
@@ -4876,7 +6805,7 @@ var ServerWatcher = class {
4876
6805
  // src/commands/watch.ts
4877
6806
  var DEFAULT_EXTENSIONS = ["ts", "js", "json", "py", "mjs", "cjs"];
4878
6807
  var DEFAULT_DEBOUNCE_MS = 300;
4879
- var watch_default = defineCommand25({
6808
+ var watch_default = defineCommand37({
4880
6809
  meta: {
4881
6810
  name: "watch",
4882
6811
  description: "Watch a local MCP server for file changes and auto-restart"
@@ -4915,8 +6844,8 @@ var watch_default = defineCommand25({
4915
6844
  const lockfile = readLockfile();
4916
6845
  const entry = lockfile.servers[serverName];
4917
6846
  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.`));
6847
+ console.error(pc37.red(` Error: Server '${serverName}' not found in lockfile.`));
6848
+ console.error(pc37.dim(` Run ${pc37.cyan("mcpman link .")} to register a local server.`));
4920
6849
  process.exit(1);
4921
6850
  }
4922
6851
  let watchDir = args.dir;
@@ -4924,8 +6853,8 @@ var watch_default = defineCommand25({
4924
6853
  if (entry.source === "local" && entry.resolved) {
4925
6854
  watchDir = entry.resolved;
4926
6855
  } 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."));
6856
+ console.error(pc37.red(` Error: Cannot determine watch directory for '${serverName}'.`));
6857
+ console.error(pc37.dim(" Use --dir to specify the directory to watch."));
4929
6858
  process.exit(1);
4930
6859
  }
4931
6860
  }
@@ -4971,12 +6900,12 @@ async function loadVaultSecrets4(serverName) {
4971
6900
  }
4972
6901
 
4973
6902
  // src/commands/why.ts
4974
- import { defineCommand as defineCommand26 } from "citty";
4975
- import pc26 from "picocolors";
6903
+ import { defineCommand as defineCommand38 } from "citty";
6904
+ import pc38 from "picocolors";
4976
6905
 
4977
6906
  // src/core/why-service.ts
4978
- import fs13 from "fs";
4979
- import path14 from "path";
6907
+ import fs21 from "fs";
6908
+ import path16 from "path";
4980
6909
  var ALL_CLIENT_TYPES = ["claude-desktop", "cursor", "vscode", "windsurf"];
4981
6910
  async function getServerProvenance(serverName, lockfilePath, profilesDir) {
4982
6911
  const lockfile = readLockfile(lockfilePath);
@@ -5020,7 +6949,7 @@ async function buildClientStatuses(serverName, lockfileClients) {
5020
6949
  }));
5021
6950
  }
5022
6951
  async function findOrphanedClients(serverName) {
5023
- const { getInstalledClients: getInstalledClients2 } = await import("./client-detector-CY7WPF3K.js");
6952
+ const { getInstalledClients: getInstalledClients2 } = await import("./client-detector-O2HN4MUB.js");
5024
6953
  const handlers = await getInstalledClients2();
5025
6954
  const results = [];
5026
6955
  for (const handler of handlers) {
@@ -5036,16 +6965,16 @@ async function findOrphanedClients(serverName) {
5036
6965
  }
5037
6966
  function scanProfiles(serverName, profilesDir) {
5038
6967
  const found = [];
5039
- if (!fs13.existsSync(profilesDir)) return found;
6968
+ if (!fs21.existsSync(profilesDir)) return found;
5040
6969
  let files;
5041
6970
  try {
5042
- files = fs13.readdirSync(profilesDir).filter((f) => f.endsWith(".json"));
6971
+ files = fs21.readdirSync(profilesDir).filter((f) => f.endsWith(".json"));
5043
6972
  } catch {
5044
6973
  return found;
5045
6974
  }
5046
6975
  for (const file of files) {
5047
6976
  try {
5048
- const raw = fs13.readFileSync(path14.join(profilesDir, file), "utf-8");
6977
+ const raw = fs21.readFileSync(path16.join(profilesDir, file), "utf-8");
5049
6978
  const profile = JSON.parse(raw);
5050
6979
  if (serverName in (profile.servers ?? {})) {
5051
6980
  found.push(profile.name ?? file.replace(".json", ""));
@@ -5074,8 +7003,8 @@ function formatWhyOutput(result) {
5074
7003
  if (result.profiles.length > 0) {
5075
7004
  lines.push("");
5076
7005
  lines.push(" Profiles:");
5077
- for (const p12 of result.profiles) {
5078
- lines.push(` ${p12}`);
7006
+ for (const p13 of result.profiles) {
7007
+ lines.push(` ${p13}`);
5079
7008
  }
5080
7009
  }
5081
7010
  if (result.envVars.length > 0) {
@@ -5089,7 +7018,7 @@ function formatWhyOutput(result) {
5089
7018
  }
5090
7019
 
5091
7020
  // src/commands/why.ts
5092
- var why_default = defineCommand26({
7021
+ var why_default = defineCommand38({
5093
7022
  meta: {
5094
7023
  name: "why",
5095
7024
  description: "Show why a server is installed (provenance, clients, profiles)"
@@ -5111,15 +7040,15 @@ var why_default = defineCommand26({
5111
7040
  const asJson = args.json;
5112
7041
  const result = await getServerProvenance(serverName);
5113
7042
  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.`));
7043
+ console.error(pc38.red(` Server '${serverName}' not found in lockfile or any client config.`));
7044
+ console.error(pc38.dim(` Run ${pc38.cyan("mcpman list")} to see installed servers.`));
5116
7045
  process.exit(1);
5117
7046
  }
5118
7047
  if (result.orphaned) {
5119
- console.log(pc26.yellow(`
7048
+ console.log(pc38.yellow(`
5120
7049
  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.
7050
+ console.log(pc38.dim(" Found in client config(s) but not in lockfile."));
7051
+ console.log(pc38.dim(` Run ${pc38.cyan("mcpman sync --remove")} to clean up.
5123
7052
  `));
5124
7053
  const registeredClients = result.clients.filter((c) => c.registered).map((c) => c.type);
5125
7054
  if (registeredClients.length > 0) {
@@ -5142,7 +7071,7 @@ process.on("SIGINT", () => {
5142
7071
  console.log("\nAborted.");
5143
7072
  process.exit(130);
5144
7073
  });
5145
- var main = defineCommand27({
7074
+ var main = defineCommand39({
5146
7075
  meta: {
5147
7076
  name: APP_NAME,
5148
7077
  version: APP_VERSION,
@@ -5174,7 +7103,19 @@ var main = defineCommand27({
5174
7103
  watch: watch_default,
5175
7104
  registry: registry_default,
5176
7105
  completions: completions_default,
5177
- why: why_default
7106
+ why: why_default,
7107
+ env: env_default,
7108
+ bench: bench_default,
7109
+ diff: diff_default,
7110
+ group: group_default,
7111
+ pin: pin_default,
7112
+ rollback: rollback_default,
7113
+ validate: validate_default,
7114
+ status: status_default,
7115
+ replay: replay_default,
7116
+ alias: alias_default,
7117
+ template: template_default,
7118
+ notify: notify_default
5178
7119
  }
5179
7120
  });
5180
7121
  runMain(main);