mcpman 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -37,6 +37,100 @@ var init_cjs_shims = __esm({
37
37
  }
38
38
  });
39
39
 
40
+ // src/core/lockfile.ts
41
+ var lockfile_exports = {};
42
+ __export(lockfile_exports, {
43
+ LOCKFILE_NAME: () => LOCKFILE_NAME,
44
+ addEntry: () => addEntry,
45
+ createEmptyLockfile: () => createEmptyLockfile,
46
+ findLockfile: () => findLockfile,
47
+ getGlobalLockfilePath: () => getGlobalLockfilePath,
48
+ getLockedVersion: () => getLockedVersion,
49
+ readLockfile: () => readLockfile,
50
+ removeEntry: () => removeEntry,
51
+ resolveLockfilePath: () => resolveLockfilePath,
52
+ writeLockfile: () => writeLockfile
53
+ });
54
+ function findLockfile() {
55
+ let dir = process.cwd();
56
+ while (true) {
57
+ const candidate = import_node_path.default.join(dir, LOCKFILE_NAME);
58
+ if (import_node_fs.default.existsSync(candidate)) return candidate;
59
+ const parent = import_node_path.default.dirname(dir);
60
+ if (parent === dir) break;
61
+ dir = parent;
62
+ }
63
+ return null;
64
+ }
65
+ function getGlobalLockfilePath() {
66
+ return import_node_path.default.join(import_node_os.default.homedir(), ".mcpman", LOCKFILE_NAME);
67
+ }
68
+ function resolveLockfilePath() {
69
+ return findLockfile() ?? getGlobalLockfilePath();
70
+ }
71
+ function readLockfile(filePath) {
72
+ const target = filePath ?? resolveLockfilePath();
73
+ if (!import_node_fs.default.existsSync(target)) {
74
+ return { lockfileVersion: 1, servers: {} };
75
+ }
76
+ try {
77
+ const raw = import_node_fs.default.readFileSync(target, "utf-8");
78
+ return JSON.parse(raw);
79
+ } catch {
80
+ return { lockfileVersion: 1, servers: {} };
81
+ }
82
+ }
83
+ function serialize(data) {
84
+ const sorted = {
85
+ lockfileVersion: data.lockfileVersion,
86
+ servers: Object.fromEntries(
87
+ Object.entries(data.servers).sort(([a], [b]) => a.localeCompare(b))
88
+ )
89
+ };
90
+ return `${JSON.stringify(sorted, null, 2)}
91
+ `;
92
+ }
93
+ function writeLockfile(data, filePath) {
94
+ const target = filePath ?? resolveLockfilePath();
95
+ const dir = import_node_path.default.dirname(target);
96
+ if (!import_node_fs.default.existsSync(dir)) {
97
+ import_node_fs.default.mkdirSync(dir, { recursive: true });
98
+ }
99
+ const tmp = `${target}.tmp`;
100
+ import_node_fs.default.writeFileSync(tmp, serialize(data), "utf-8");
101
+ import_node_fs.default.renameSync(tmp, target);
102
+ }
103
+ function addEntry(name, entry, filePath) {
104
+ const data = readLockfile(filePath);
105
+ data.servers[name] = entry;
106
+ writeLockfile(data, filePath);
107
+ }
108
+ function removeEntry(name, filePath) {
109
+ const data = readLockfile(filePath);
110
+ if (name in data.servers) {
111
+ delete data.servers[name];
112
+ writeLockfile(data, filePath);
113
+ }
114
+ }
115
+ function getLockedVersion(name, filePath) {
116
+ const data = readLockfile(filePath);
117
+ return data.servers[name]?.version;
118
+ }
119
+ function createEmptyLockfile(filePath) {
120
+ writeLockfile({ lockfileVersion: 1, servers: {} }, filePath);
121
+ }
122
+ var import_node_fs, import_node_os, import_node_path, LOCKFILE_NAME;
123
+ var init_lockfile = __esm({
124
+ "src/core/lockfile.ts"() {
125
+ "use strict";
126
+ init_cjs_shims();
127
+ import_node_fs = __toESM(require("fs"), 1);
128
+ import_node_os = __toESM(require("os"), 1);
129
+ import_node_path = __toESM(require("path"), 1);
130
+ LOCKFILE_NAME = "mcpman.lock";
131
+ }
132
+ });
133
+
40
134
  // src/core/trust-scorer.ts
41
135
  var trust_scorer_exports = {};
42
136
  __export(trust_scorer_exports, {
@@ -437,12 +531,12 @@ __export(vault_service_exports, {
437
531
  writeVault: () => writeVault
438
532
  });
439
533
  function getVaultPath() {
440
- return import_node_path7.default.join(import_node_os4.default.homedir(), ".mcpman", "vault.enc");
534
+ return import_node_path10.default.join(import_node_os5.default.homedir(), ".mcpman", "vault.enc");
441
535
  }
442
536
  function readVault(vaultPath = getVaultPath()) {
443
537
  const empty = { version: 1, servers: {} };
444
538
  try {
445
- const raw = import_node_fs6.default.readFileSync(vaultPath, "utf-8");
539
+ const raw = import_node_fs8.default.readFileSync(vaultPath, "utf-8");
446
540
  const parsed = JSON.parse(raw);
447
541
  if (parsed.version !== 1 || typeof parsed.servers !== "object") return empty;
448
542
  return parsed;
@@ -451,16 +545,16 @@ function readVault(vaultPath = getVaultPath()) {
451
545
  }
452
546
  }
453
547
  function writeVault(data, vaultPath = getVaultPath()) {
454
- const dir = import_node_path7.default.dirname(vaultPath);
455
- import_node_fs6.default.mkdirSync(dir, { recursive: true });
548
+ const dir = import_node_path10.default.dirname(vaultPath);
549
+ import_node_fs8.default.mkdirSync(dir, { recursive: true });
456
550
  const tmp = `${vaultPath}.tmp`;
457
- import_node_fs6.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
551
+ import_node_fs8.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
458
552
  if (process.platform !== "win32") {
459
- import_node_fs6.default.chmodSync(tmp, 384);
553
+ import_node_fs8.default.chmodSync(tmp, 384);
460
554
  }
461
- import_node_fs6.default.renameSync(tmp, vaultPath);
555
+ import_node_fs8.default.renameSync(tmp, vaultPath);
462
556
  if (process.platform !== "win32") {
463
- import_node_fs6.default.chmodSync(vaultPath, 384);
557
+ import_node_fs8.default.chmodSync(vaultPath, 384);
464
558
  }
465
559
  }
466
560
  function encrypt(value, password2) {
@@ -550,15 +644,15 @@ function listSecrets(server, vaultPath = getVaultPath()) {
550
644
  keys: Object.keys(keys)
551
645
  }));
552
646
  }
553
- var import_node_crypto2, import_node_fs6, import_node_os4, import_node_path7, p3, _cachedPassword;
647
+ var import_node_crypto2, import_node_fs8, import_node_os5, import_node_path10, p3, _cachedPassword;
554
648
  var init_vault_service = __esm({
555
649
  "src/core/vault-service.ts"() {
556
650
  "use strict";
557
651
  init_cjs_shims();
558
652
  import_node_crypto2 = __toESM(require("crypto"), 1);
559
- import_node_fs6 = __toESM(require("fs"), 1);
560
- import_node_os4 = __toESM(require("os"), 1);
561
- import_node_path7 = __toESM(require("path"), 1);
653
+ import_node_fs8 = __toESM(require("fs"), 1);
654
+ import_node_os5 = __toESM(require("os"), 1);
655
+ import_node_path10 = __toESM(require("path"), 1);
562
656
  p3 = __toESM(require("@clack/prompts"), 1);
563
657
  _cachedPassword = null;
564
658
  process.on("exit", () => {
@@ -569,7 +663,7 @@ var init_vault_service = __esm({
569
663
 
570
664
  // src/index.ts
571
665
  init_cjs_shims();
572
- var import_citty21 = require("citty");
666
+ var import_citty27 = require("citty");
573
667
 
574
668
  // src/commands/audit.ts
575
669
  init_cjs_shims();
@@ -577,70 +671,7 @@ var p = __toESM(require("@clack/prompts"), 1);
577
671
  var import_citty = require("citty");
578
672
  var import_nanospinner = require("nanospinner");
579
673
  var import_picocolors = __toESM(require("picocolors"), 1);
580
-
581
- // src/core/lockfile.ts
582
- init_cjs_shims();
583
- var import_node_fs = __toESM(require("fs"), 1);
584
- var import_node_os = __toESM(require("os"), 1);
585
- var import_node_path = __toESM(require("path"), 1);
586
- var LOCKFILE_NAME = "mcpman.lock";
587
- function findLockfile() {
588
- let dir = process.cwd();
589
- while (true) {
590
- const candidate = import_node_path.default.join(dir, LOCKFILE_NAME);
591
- if (import_node_fs.default.existsSync(candidate)) return candidate;
592
- const parent = import_node_path.default.dirname(dir);
593
- if (parent === dir) break;
594
- dir = parent;
595
- }
596
- return null;
597
- }
598
- function getGlobalLockfilePath() {
599
- return import_node_path.default.join(import_node_os.default.homedir(), ".mcpman", LOCKFILE_NAME);
600
- }
601
- function resolveLockfilePath() {
602
- return findLockfile() ?? getGlobalLockfilePath();
603
- }
604
- function readLockfile(filePath) {
605
- const target = filePath ?? resolveLockfilePath();
606
- if (!import_node_fs.default.existsSync(target)) {
607
- return { lockfileVersion: 1, servers: {} };
608
- }
609
- try {
610
- const raw = import_node_fs.default.readFileSync(target, "utf-8");
611
- return JSON.parse(raw);
612
- } catch {
613
- return { lockfileVersion: 1, servers: {} };
614
- }
615
- }
616
- function serialize(data) {
617
- const sorted = {
618
- lockfileVersion: data.lockfileVersion,
619
- servers: Object.fromEntries(
620
- Object.entries(data.servers).sort(([a], [b]) => a.localeCompare(b))
621
- )
622
- };
623
- return `${JSON.stringify(sorted, null, 2)}
624
- `;
625
- }
626
- function writeLockfile(data, filePath) {
627
- const target = filePath ?? resolveLockfilePath();
628
- const dir = import_node_path.default.dirname(target);
629
- if (!import_node_fs.default.existsSync(dir)) {
630
- import_node_fs.default.mkdirSync(dir, { recursive: true });
631
- }
632
- const tmp = `${target}.tmp`;
633
- import_node_fs.default.writeFileSync(tmp, serialize(data), "utf-8");
634
- import_node_fs.default.renameSync(tmp, target);
635
- }
636
- function addEntry(name, entry, filePath) {
637
- const data = readLockfile(filePath);
638
- data.servers[name] = entry;
639
- writeLockfile(data, filePath);
640
- }
641
- function createEmptyLockfile(filePath) {
642
- writeLockfile({ lockfileVersion: 1, servers: {} }, filePath);
643
- }
674
+ init_lockfile();
644
675
 
645
676
  // src/core/security-scanner.ts
646
677
  init_cjs_shims();
@@ -786,6 +817,7 @@ async function scanAllServers(servers, concurrency = 3) {
786
817
 
787
818
  // src/core/server-updater.ts
788
819
  init_cjs_shims();
820
+ init_lockfile();
789
821
 
790
822
  // src/core/registry.ts
791
823
  init_cjs_shims();
@@ -1471,11 +1503,290 @@ async function runAuditFix(reports, servers, skipConfirm) {
1471
1503
  `);
1472
1504
  }
1473
1505
 
1474
- // src/commands/config.ts
1506
+ // src/commands/completions.ts
1475
1507
  init_cjs_shims();
1476
- var p2 = __toESM(require("@clack/prompts"), 1);
1508
+ var import_node_fs6 = __toESM(require("fs"), 1);
1509
+ var import_node_os4 = __toESM(require("os"), 1);
1510
+ var import_node_path7 = __toESM(require("path"), 1);
1477
1511
  var import_citty2 = require("citty");
1478
1512
  var import_picocolors2 = __toESM(require("picocolors"), 1);
1513
+
1514
+ // src/core/completion-generator.ts
1515
+ init_cjs_shims();
1516
+ init_lockfile();
1517
+ function getCommandList() {
1518
+ return [
1519
+ "install",
1520
+ "list",
1521
+ "remove",
1522
+ "doctor",
1523
+ "init",
1524
+ "secrets",
1525
+ "sync",
1526
+ "audit",
1527
+ "update",
1528
+ "upgrade",
1529
+ "config",
1530
+ "search",
1531
+ "info",
1532
+ "run",
1533
+ "logs",
1534
+ "test",
1535
+ "profiles",
1536
+ "plugin",
1537
+ "export",
1538
+ "import",
1539
+ "create",
1540
+ "link",
1541
+ "watch",
1542
+ "registry",
1543
+ "completions",
1544
+ "why"
1545
+ ];
1546
+ }
1547
+ var SERVER_ARG_COMMANDS = [
1548
+ "run",
1549
+ "test",
1550
+ "logs",
1551
+ "watch",
1552
+ "remove",
1553
+ "update",
1554
+ "info",
1555
+ "audit",
1556
+ "link",
1557
+ "why"
1558
+ ];
1559
+ function getServerNames(lockfilePath) {
1560
+ try {
1561
+ const data = readLockfile(lockfilePath);
1562
+ return Object.keys(data.servers);
1563
+ } catch {
1564
+ return [];
1565
+ }
1566
+ }
1567
+ function generateBashCompletion() {
1568
+ const serverCmds = SERVER_ARG_COMMANDS.join("|");
1569
+ return `# mcpman bash completion
1570
+ # Add to ~/.bashrc or ~/.bash_profile:
1571
+ # source <(mcpman completions bash)
1572
+ # Or append permanently:
1573
+ # mcpman completions bash >> ~/.bashrc
1574
+
1575
+ _mcpman_completions() {
1576
+ local cur="\${COMP_WORDS[COMP_CWORD]}"
1577
+ local prev="\${COMP_WORDS[COMP_CWORD-1]}"
1578
+
1579
+ if [ "$COMP_CWORD" -eq 1 ]; then
1580
+ COMPREPLY=($(compgen -W "$(mcpman completions --list-commands 2>/dev/null)" -- "$cur"))
1581
+ return
1582
+ fi
1583
+
1584
+ case "$prev" in
1585
+ ${serverCmds})
1586
+ COMPREPLY=($(compgen -W "$(mcpman completions --list-servers 2>/dev/null)" -- "$cur"))
1587
+ ;;
1588
+ --client|-c)
1589
+ COMPREPLY=($(compgen -W "claude-desktop cursor vscode windsurf" -- "$cur"))
1590
+ ;;
1591
+ --runtime|-r)
1592
+ COMPREPLY=($(compgen -W "node python" -- "$cur"))
1593
+ ;;
1594
+ esac
1595
+ }
1596
+
1597
+ complete -F _mcpman_completions mcpman
1598
+ `;
1599
+ }
1600
+ function generateZshCompletion() {
1601
+ const serverCmds = SERVER_ARG_COMMANDS.map((c) => `'${c}'`).join(" ");
1602
+ return `#compdef mcpman
1603
+ # mcpman zsh completion
1604
+ # Add to ~/.zshrc:
1605
+ # source <(mcpman completions zsh)
1606
+ # Or use compinit:
1607
+ # mcpman completions zsh > "\${fpath[1]}/_mcpman"
1608
+
1609
+ _mcpman() {
1610
+ local state
1611
+
1612
+ _arguments \\
1613
+ '1: :->command' \\
1614
+ '*: :->args'
1615
+
1616
+ case $state in
1617
+ command)
1618
+ local commands
1619
+ commands=($(mcpman completions --list-commands 2>/dev/null))
1620
+ _describe 'command' commands
1621
+ ;;
1622
+ args)
1623
+ local cmd="\${words[2]}"
1624
+ local server_cmds=(${serverCmds})
1625
+ if (( server_cmds[(I)$cmd] )); then
1626
+ local servers
1627
+ servers=($(mcpman completions --list-servers 2>/dev/null))
1628
+ _describe 'server' servers
1629
+ fi
1630
+ ;;
1631
+ esac
1632
+ }
1633
+
1634
+ compdef _mcpman mcpman
1635
+ `;
1636
+ }
1637
+ function generateFishCompletion() {
1638
+ const serverCmds = SERVER_ARG_COMMANDS.join(" ");
1639
+ return `# mcpman fish completion
1640
+ # Add to ~/.config/fish/completions/mcpman.fish:
1641
+ # mcpman completions fish > ~/.config/fish/completions/mcpman.fish
1642
+
1643
+ # Disable file completion for mcpman
1644
+ complete -c mcpman -f
1645
+
1646
+ # Subcommand completions (dynamic)
1647
+ complete -c mcpman -n '__fish_use_subcommand' \\
1648
+ -a "(mcpman completions --list-commands 2>/dev/null)"
1649
+
1650
+ # Server name completions for commands that take a server arg
1651
+ for cmd in ${serverCmds}
1652
+ complete -c mcpman -n "__fish_seen_subcommand_from $cmd" \\
1653
+ -a "(mcpman completions --list-servers 2>/dev/null)"
1654
+ end
1655
+
1656
+ # --client flag completions
1657
+ complete -c mcpman -l client -s c \\
1658
+ -a "claude-desktop cursor vscode windsurf" \\
1659
+ -d "Target client"
1660
+
1661
+ # --runtime flag completions
1662
+ complete -c mcpman -l runtime -s r \\
1663
+ -a "node python" \\
1664
+ -d "Runtime"
1665
+ `;
1666
+ }
1667
+
1668
+ // src/commands/completions.ts
1669
+ var completions_default = (0, import_citty2.defineCommand)({
1670
+ meta: {
1671
+ name: "completions",
1672
+ description: "Generate shell completion scripts (bash, zsh, fish)"
1673
+ },
1674
+ args: {
1675
+ shell: {
1676
+ type: "positional",
1677
+ description: "Shell: bash, zsh, fish, or install",
1678
+ required: false
1679
+ },
1680
+ "list-commands": {
1681
+ type: "boolean",
1682
+ description: "Output all command names (used by completion scripts)",
1683
+ default: false
1684
+ },
1685
+ "list-servers": {
1686
+ type: "boolean",
1687
+ description: "Output server names from lockfile (used by completion scripts)",
1688
+ default: false
1689
+ }
1690
+ },
1691
+ async run({ args }) {
1692
+ if (args["list-commands"]) {
1693
+ console.log(getCommandList().join("\n"));
1694
+ return;
1695
+ }
1696
+ if (args["list-servers"]) {
1697
+ console.log(getServerNames().join("\n"));
1698
+ return;
1699
+ }
1700
+ const shell = args.shell;
1701
+ if (!shell) {
1702
+ printUsage();
1703
+ return;
1704
+ }
1705
+ switch (shell.toLowerCase()) {
1706
+ case "bash":
1707
+ process.stdout.write(generateBashCompletion());
1708
+ break;
1709
+ case "zsh":
1710
+ process.stdout.write(generateZshCompletion());
1711
+ break;
1712
+ case "fish":
1713
+ process.stdout.write(generateFishCompletion());
1714
+ break;
1715
+ case "install":
1716
+ await installCompletion();
1717
+ break;
1718
+ default:
1719
+ console.error(import_picocolors2.default.red(` Error: Unknown shell '${shell}'. Use: bash, zsh, or fish.`));
1720
+ process.exit(1);
1721
+ }
1722
+ }
1723
+ });
1724
+ function printUsage() {
1725
+ console.log(import_picocolors2.default.bold("\n mcpman completions \u2014 Shell completion setup\n"));
1726
+ console.log(" Usage:");
1727
+ console.log(` ${import_picocolors2.default.cyan("mcpman completions bash")} Output bash completion script`);
1728
+ console.log(` ${import_picocolors2.default.cyan("mcpman completions zsh")} Output zsh completion script`);
1729
+ console.log(` ${import_picocolors2.default.cyan("mcpman completions fish")} Output fish completion script`);
1730
+ console.log(` ${import_picocolors2.default.cyan("mcpman completions install")} Auto-detect shell and install
1731
+ `);
1732
+ console.log(" Quick setup:");
1733
+ console.log(` ${import_picocolors2.default.dim("# bash")}`);
1734
+ console.log(` ${import_picocolors2.default.cyan("source <(mcpman completions bash)")}`);
1735
+ console.log(` ${import_picocolors2.default.dim("# zsh")}`);
1736
+ console.log(` ${import_picocolors2.default.cyan("source <(mcpman completions zsh)")}
1737
+ `);
1738
+ }
1739
+ async function installCompletion() {
1740
+ const shellBin = process.env.SHELL ?? "";
1741
+ let detectedShell = "";
1742
+ if (shellBin.includes("zsh")) detectedShell = "zsh";
1743
+ else if (shellBin.includes("fish")) detectedShell = "fish";
1744
+ else if (shellBin.includes("bash")) detectedShell = "bash";
1745
+ if (!detectedShell) {
1746
+ console.error(import_picocolors2.default.red(" Could not detect shell from $SHELL. Run manually:"));
1747
+ console.error(import_picocolors2.default.dim(" source <(mcpman completions bash|zsh|fish)"));
1748
+ process.exit(1);
1749
+ }
1750
+ const home = import_node_os4.default.homedir();
1751
+ let rcFile;
1752
+ let script;
1753
+ if (detectedShell === "zsh") {
1754
+ rcFile = import_node_path7.default.join(home, ".zshrc");
1755
+ script = generateZshCompletion();
1756
+ } else if (detectedShell === "fish") {
1757
+ const fishDir = import_node_path7.default.join(home, ".config", "fish", "completions");
1758
+ import_node_fs6.default.mkdirSync(fishDir, { recursive: true });
1759
+ rcFile = import_node_path7.default.join(fishDir, "mcpman.fish");
1760
+ import_node_fs6.default.writeFileSync(rcFile, generateFishCompletion(), "utf-8");
1761
+ console.log(import_picocolors2.default.green(` Installed fish completions to ${rcFile}`));
1762
+ return;
1763
+ } else {
1764
+ rcFile = import_node_path7.default.join(home, ".bashrc");
1765
+ script = generateBashCompletion();
1766
+ }
1767
+ const marker = "# mcpman completions";
1768
+ let existing = "";
1769
+ try {
1770
+ existing = import_node_fs6.default.readFileSync(rcFile, "utf-8");
1771
+ } catch {
1772
+ }
1773
+ if (existing.includes(marker)) {
1774
+ console.log(import_picocolors2.default.yellow(` Completions already installed in ${rcFile}. Skipping.`));
1775
+ return;
1776
+ }
1777
+ import_node_fs6.default.appendFileSync(rcFile, `
1778
+ ${marker}
1779
+ source <(mcpman completions ${detectedShell})
1780
+ `);
1781
+ console.log(import_picocolors2.default.green(` Installed ${detectedShell} completions in ${rcFile}`));
1782
+ console.log(import_picocolors2.default.dim(` Restart your shell or run: source ${rcFile}`));
1783
+ }
1784
+
1785
+ // src/commands/config.ts
1786
+ init_cjs_shims();
1787
+ var p2 = __toESM(require("@clack/prompts"), 1);
1788
+ var import_citty3 = require("citty");
1789
+ var import_picocolors3 = __toESM(require("picocolors"), 1);
1479
1790
  function coerceValue(raw) {
1480
1791
  if (raw === "true") return true;
1481
1792
  if (raw === "false") return false;
@@ -1483,7 +1794,7 @@ function coerceValue(raw) {
1483
1794
  if (!Number.isNaN(num) && raw.trim() !== "") return num;
1484
1795
  return raw;
1485
1796
  }
1486
- var setCommand = (0, import_citty2.defineCommand)({
1797
+ var setCommand = (0, import_citty3.defineCommand)({
1487
1798
  meta: { name: "set", description: "Set a config value" },
1488
1799
  args: {
1489
1800
  key: {
@@ -1501,14 +1812,14 @@ var setCommand = (0, import_citty2.defineCommand)({
1501
1812
  try {
1502
1813
  const coerced = coerceValue(args.value);
1503
1814
  setConfigValue(args.key, coerced);
1504
- console.log(`${import_picocolors2.default.green("\u2713")} Set ${import_picocolors2.default.bold(args.key)} = ${import_picocolors2.default.cyan(String(coerced))}`);
1815
+ console.log(`${import_picocolors3.default.green("\u2713")} Set ${import_picocolors3.default.bold(args.key)} = ${import_picocolors3.default.cyan(String(coerced))}`);
1505
1816
  } catch (err) {
1506
- console.error(`${import_picocolors2.default.red("\u2717")} ${String(err)}`);
1817
+ console.error(`${import_picocolors3.default.red("\u2717")} ${String(err)}`);
1507
1818
  process.exit(1);
1508
1819
  }
1509
1820
  }
1510
1821
  });
1511
- var getCommand = (0, import_citty2.defineCommand)({
1822
+ var getCommand = (0, import_citty3.defineCommand)({
1512
1823
  meta: { name: "get", description: "Get a config value" },
1513
1824
  args: {
1514
1825
  key: {
@@ -1520,32 +1831,32 @@ var getCommand = (0, import_citty2.defineCommand)({
1520
1831
  run({ args }) {
1521
1832
  const val = getConfigValue(args.key);
1522
1833
  if (val === void 0) {
1523
- console.log(import_picocolors2.default.dim(`${args.key}: (not set)`));
1834
+ console.log(import_picocolors3.default.dim(`${args.key}: (not set)`));
1524
1835
  } else {
1525
- console.log(`${import_picocolors2.default.bold(args.key)}: ${import_picocolors2.default.cyan(String(val))}`);
1836
+ console.log(`${import_picocolors3.default.bold(args.key)}: ${import_picocolors3.default.cyan(String(val))}`);
1526
1837
  }
1527
1838
  }
1528
1839
  });
1529
- var listCommand = (0, import_citty2.defineCommand)({
1840
+ var listCommand = (0, import_citty3.defineCommand)({
1530
1841
  meta: { name: "list", description: "List all config values" },
1531
1842
  run() {
1532
1843
  const data = readConfig();
1533
1844
  const entries = Object.entries(data);
1534
1845
  if (entries.length === 0) {
1535
- console.log(import_picocolors2.default.dim("No config values set. Use `mcpman config set <key> <value>`."));
1846
+ console.log(import_picocolors3.default.dim("No config values set. Use `mcpman config set <key> <value>`."));
1536
1847
  return;
1537
1848
  }
1538
1849
  console.log("");
1539
- console.log(import_picocolors2.default.bold("mcpman config:"));
1850
+ console.log(import_picocolors3.default.bold("mcpman config:"));
1540
1851
  console.log("");
1541
1852
  for (const [key, val] of entries) {
1542
- console.log(` ${import_picocolors2.default.green("\u25CF")} ${import_picocolors2.default.bold(key)} ${import_picocolors2.default.cyan(String(val))}`);
1853
+ console.log(` ${import_picocolors3.default.green("\u25CF")} ${import_picocolors3.default.bold(key)} ${import_picocolors3.default.cyan(String(val))}`);
1543
1854
  }
1544
1855
  console.log("");
1545
- console.log(import_picocolors2.default.dim(` ${entries.length} key${entries.length !== 1 ? "s" : ""} configured`));
1856
+ console.log(import_picocolors3.default.dim(` ${entries.length} key${entries.length !== 1 ? "s" : ""} configured`));
1546
1857
  }
1547
1858
  });
1548
- var resetCommand = (0, import_citty2.defineCommand)({
1859
+ var resetCommand = (0, import_citty3.defineCommand)({
1549
1860
  meta: { name: "reset", description: "Reset config to defaults (removes config file)" },
1550
1861
  async run() {
1551
1862
  const confirmed = await p2.confirm({
@@ -1557,10 +1868,10 @@ var resetCommand = (0, import_citty2.defineCommand)({
1557
1868
  return;
1558
1869
  }
1559
1870
  writeConfig({});
1560
- console.log(`${import_picocolors2.default.green("\u2713")} Config reset to defaults.`);
1871
+ console.log(`${import_picocolors3.default.green("\u2713")} Config reset to defaults.`);
1561
1872
  }
1562
1873
  });
1563
- var config_default = (0, import_citty2.defineCommand)({
1874
+ var config_default = (0, import_citty3.defineCommand)({
1564
1875
  meta: {
1565
1876
  name: "config",
1566
1877
  description: "Manage mcpman CLI configuration"
@@ -1573,46 +1884,336 @@ var config_default = (0, import_citty2.defineCommand)({
1573
1884
  }
1574
1885
  });
1575
1886
 
1576
- // src/commands/doctor.ts
1887
+ // src/commands/create.ts
1577
1888
  init_cjs_shims();
1578
- var import_citty3 = require("citty");
1579
- var import_picocolors3 = __toESM(require("picocolors"), 1);
1889
+ var import_node_path9 = __toESM(require("path"), 1);
1890
+ var import_citty4 = require("citty");
1891
+ var import_picocolors4 = __toESM(require("picocolors"), 1);
1580
1892
 
1581
- // src/core/health-checker.ts
1893
+ // src/core/scaffold-service.ts
1582
1894
  init_cjs_shims();
1895
+ var import_node_fs7 = __toESM(require("fs"), 1);
1896
+ var import_node_path8 = __toESM(require("path"), 1);
1897
+ function sanitizeName(name) {
1898
+ return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
1899
+ }
1900
+ function generateNodeProject(options) {
1901
+ const { name, description } = options;
1902
+ const packageJson = JSON.stringify(
1903
+ {
1904
+ name,
1905
+ version: "0.1.0",
1906
+ description,
1907
+ type: "module",
1908
+ bin: { [name]: "./dist/index.js" },
1909
+ main: "./dist/index.js",
1910
+ scripts: {
1911
+ build: "tsc",
1912
+ start: "node dist/index.js",
1913
+ dev: "tsx src/index.ts"
1914
+ },
1915
+ mcp: {
1916
+ name,
1917
+ description,
1918
+ transport: "stdio",
1919
+ env: []
1920
+ },
1921
+ dependencies: {
1922
+ "@modelcontextprotocol/sdk": "^1.0.0"
1923
+ },
1924
+ devDependencies: {
1925
+ typescript: "^5.0.0",
1926
+ tsx: "^4.0.0",
1927
+ "@types/node": "^20.0.0"
1928
+ }
1929
+ },
1930
+ null,
1931
+ 2
1932
+ );
1933
+ const tsconfig = JSON.stringify(
1934
+ {
1935
+ compilerOptions: {
1936
+ target: "ES2022",
1937
+ module: "NodeNext",
1938
+ moduleResolution: "NodeNext",
1939
+ outDir: "./dist",
1940
+ rootDir: "./src",
1941
+ strict: true,
1942
+ esModuleInterop: true,
1943
+ skipLibCheck: true
1944
+ },
1945
+ include: ["src"]
1946
+ },
1947
+ null,
1948
+ 2
1949
+ );
1950
+ const indexTs = `#!/usr/bin/env node
1951
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
1952
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
1583
1953
 
1584
- // src/core/diagnostics.ts
1585
- init_cjs_shims();
1586
- var import_node_child_process3 = require("child_process");
1587
- var import_node_util = require("util");
1954
+ const server = new Server(
1955
+ { name: "${name}", version: "0.1.0" },
1956
+ { capabilities: { tools: {} } }
1957
+ );
1588
1958
 
1589
- // src/core/mcp-process-checks.ts
1590
- init_cjs_shims();
1591
- var import_node_child_process2 = require("child_process");
1592
- async function checkProcessSpawn(command, args, env, timeoutMs = 3e3) {
1593
- return new Promise((resolve) => {
1594
- let settled = false;
1595
- const child = (0, import_node_child_process2.spawn)(command, args, {
1596
- env: { ...process.env, ...env },
1597
- stdio: ["pipe", "pipe", "pipe"]
1598
- });
1599
- const timer = setTimeout(() => {
1600
- if (!settled) {
1601
- settled = true;
1602
- child.kill("SIGTERM");
1603
- resolve({ name: "Process", passed: true, message: "starts successfully (still running)" });
1604
- }
1605
- }, timeoutMs);
1606
- child.on("error", (err) => {
1607
- if (!settled) {
1608
- settled = true;
1609
- clearTimeout(timer);
1610
- resolve({ name: "Process", passed: false, message: `spawn error: ${err.message}` });
1611
- }
1612
- });
1613
- child.on("exit", (code) => {
1614
- if (!settled) {
1615
- settled = true;
1959
+ server.setRequestHandler("tools/list", async () => ({
1960
+ tools: [
1961
+ {
1962
+ name: "hello",
1963
+ description: "Say hello",
1964
+ inputSchema: { type: "object", properties: { name: { type: "string" } } },
1965
+ },
1966
+ ],
1967
+ }));
1968
+
1969
+ server.setRequestHandler("tools/call", async (request) => {
1970
+ if (request.params.name === "hello") {
1971
+ const who = request.params.arguments?.name ?? "world";
1972
+ return { content: [{ type: "text", text: \`Hello, \${who}!\` }] };
1973
+ }
1974
+ throw new Error(\`Unknown tool: \${request.params.name}\`);
1975
+ });
1976
+
1977
+ const transport = new StdioServerTransport();
1978
+ await server.connect(transport);
1979
+ `;
1980
+ return {
1981
+ "package.json": packageJson,
1982
+ "tsconfig.json": tsconfig,
1983
+ "src/index.ts": indexTs
1984
+ };
1985
+ }
1986
+ function generatePythonProject(options) {
1987
+ const { name, description } = options;
1988
+ const pyprojectToml = `[build-system]
1989
+ requires = ["setuptools>=68"]
1990
+ build-backend = "setuptools.backends.legacy:build"
1991
+
1992
+ [project]
1993
+ name = "${name}"
1994
+ version = "0.1.0"
1995
+ description = "${description}"
1996
+ requires-python = ">=3.10"
1997
+ dependencies = ["mcp>=1.0.0"]
1998
+
1999
+ [project.scripts]
2000
+ ${name} = "${name.replace(/-/g, "_")}:main"
2001
+
2002
+ [tool.mcp]
2003
+ name = "${name}"
2004
+ description = "${description}"
2005
+ transport = "stdio"
2006
+ `;
2007
+ const mainPy = `#!/usr/bin/env python3
2008
+ """${description}"""
2009
+ from mcp.server import Server
2010
+ from mcp.server.stdio import stdio_server
2011
+
2012
+ app = Server("${name}")
2013
+
2014
+ @app.list_tools()
2015
+ async def list_tools():
2016
+ return [
2017
+ {
2018
+ "name": "hello",
2019
+ "description": "Say hello",
2020
+ "inputSchema": {
2021
+ "type": "object",
2022
+ "properties": {"name": {"type": "string"}},
2023
+ },
2024
+ }
2025
+ ]
2026
+
2027
+ @app.call_tool()
2028
+ async def call_tool(name: str, arguments: dict):
2029
+ if name == "hello":
2030
+ who = arguments.get("name", "world")
2031
+ return [{"type": "text", "text": f"Hello, {who}!"}]
2032
+ raise ValueError(f"Unknown tool: {name}")
2033
+
2034
+ async def main():
2035
+ async with stdio_server() as (read, write):
2036
+ await app.run(read, write)
2037
+
2038
+ if __name__ == "__main__":
2039
+ import asyncio
2040
+ asyncio.run(main())
2041
+ `;
2042
+ return {
2043
+ "pyproject.toml": pyprojectToml,
2044
+ "main.py": mainPy
2045
+ };
2046
+ }
2047
+ function writeScaffold(dir, files) {
2048
+ if (import_node_fs7.default.existsSync(dir)) {
2049
+ const existing = import_node_fs7.default.readdirSync(dir);
2050
+ if (existing.length > 0) {
2051
+ throw new Error(`Directory '${dir}' already exists and is not empty.`);
2052
+ }
2053
+ }
2054
+ for (const [relativePath, content] of Object.entries(files)) {
2055
+ const fullPath = import_node_path8.default.join(dir, relativePath);
2056
+ const parentDir = import_node_path8.default.dirname(fullPath);
2057
+ import_node_fs7.default.mkdirSync(parentDir, { recursive: true });
2058
+ import_node_fs7.default.writeFileSync(fullPath, content, "utf-8");
2059
+ }
2060
+ }
2061
+
2062
+ // src/commands/create.ts
2063
+ var create_default = (0, import_citty4.defineCommand)({
2064
+ meta: {
2065
+ name: "create",
2066
+ description: "Scaffold a new MCP server project"
2067
+ },
2068
+ args: {
2069
+ name: {
2070
+ type: "positional",
2071
+ description: "Project name",
2072
+ required: false
2073
+ },
2074
+ runtime: {
2075
+ type: "string",
2076
+ description: "Runtime: node or python (default: node)",
2077
+ alias: "r"
2078
+ },
2079
+ description: {
2080
+ type: "string",
2081
+ description: "Project description",
2082
+ alias: "d"
2083
+ },
2084
+ yes: {
2085
+ type: "boolean",
2086
+ description: "Accept all defaults, skip prompts",
2087
+ alias: "y",
2088
+ default: false
2089
+ }
2090
+ },
2091
+ async run({ args }) {
2092
+ let projectName = args.name ?? "";
2093
+ let projectDescription = args.description ?? "";
2094
+ let runtime = args.runtime ?? "node";
2095
+ const acceptDefaults = args.yes;
2096
+ if (!acceptDefaults) {
2097
+ if (!projectName) {
2098
+ const readline = await import("readline");
2099
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
2100
+ projectName = await new Promise((resolve) => {
2101
+ rl.question(import_picocolors4.default.cyan(" Project name: "), (answer) => {
2102
+ rl.close();
2103
+ resolve(answer.trim());
2104
+ });
2105
+ });
2106
+ }
2107
+ if (!projectDescription) {
2108
+ const readline = await import("readline");
2109
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
2110
+ projectDescription = await new Promise((resolve) => {
2111
+ rl.question(import_picocolors4.default.cyan(" Description (optional): "), (answer) => {
2112
+ rl.close();
2113
+ resolve(answer.trim());
2114
+ });
2115
+ });
2116
+ }
2117
+ if (!args.runtime) {
2118
+ const readline = await import("readline");
2119
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
2120
+ const answer = await new Promise((resolve) => {
2121
+ rl.question(import_picocolors4.default.cyan(" Runtime [node/python] (default: node): "), (a) => {
2122
+ rl.close();
2123
+ resolve(a.trim() || "node");
2124
+ });
2125
+ });
2126
+ runtime = answer;
2127
+ }
2128
+ }
2129
+ if (!projectName) {
2130
+ console.error(import_picocolors4.default.red(" Error: Project name is required."));
2131
+ process.exit(1);
2132
+ }
2133
+ const sanitized = sanitizeName(projectName);
2134
+ if (!sanitized) {
2135
+ console.error(import_picocolors4.default.red(` Error: Invalid project name '${projectName}'.`));
2136
+ process.exit(1);
2137
+ }
2138
+ if (runtime !== "node" && runtime !== "python") {
2139
+ console.error(import_picocolors4.default.red(` Error: Unknown runtime '${runtime}'. Use node or python.`));
2140
+ process.exit(1);
2141
+ }
2142
+ const options = {
2143
+ name: sanitized,
2144
+ description: projectDescription || `${sanitized} MCP server`,
2145
+ runtime,
2146
+ transport: "stdio"
2147
+ };
2148
+ const files = runtime === "python" ? generatePythonProject(options) : generateNodeProject(options);
2149
+ const targetDir = import_node_path9.default.resolve(sanitized);
2150
+ try {
2151
+ writeScaffold(targetDir, files);
2152
+ } catch (err) {
2153
+ console.error(import_picocolors4.default.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
2154
+ process.exit(1);
2155
+ }
2156
+ console.log(import_picocolors4.default.green(`
2157
+ Created ${import_picocolors4.default.bold(sanitized)}/
2158
+ `));
2159
+ console.log(import_picocolors4.default.dim(" Files generated:"));
2160
+ for (const file of Object.keys(files)) {
2161
+ console.log(` ${import_picocolors4.default.cyan(file)}`);
2162
+ }
2163
+ console.log("\n Next steps:");
2164
+ if (runtime === "node") {
2165
+ console.log(` ${import_picocolors4.default.bold(`cd ${sanitized}`)}`);
2166
+ console.log(` ${import_picocolors4.default.bold("npm install")}`);
2167
+ console.log(` ${import_picocolors4.default.bold("mcpman link .")}`);
2168
+ } else {
2169
+ console.log(` ${import_picocolors4.default.bold(`cd ${sanitized}`)}`);
2170
+ console.log(` ${import_picocolors4.default.bold("pip install -e .")}`);
2171
+ console.log(` ${import_picocolors4.default.bold("mcpman link .")}`);
2172
+ }
2173
+ console.log();
2174
+ }
2175
+ });
2176
+
2177
+ // src/commands/doctor.ts
2178
+ init_cjs_shims();
2179
+ var import_citty5 = require("citty");
2180
+ var import_picocolors5 = __toESM(require("picocolors"), 1);
2181
+
2182
+ // src/core/health-checker.ts
2183
+ init_cjs_shims();
2184
+
2185
+ // src/core/diagnostics.ts
2186
+ init_cjs_shims();
2187
+ var import_node_child_process3 = require("child_process");
2188
+ var import_node_util = require("util");
2189
+
2190
+ // src/core/mcp-process-checks.ts
2191
+ init_cjs_shims();
2192
+ var import_node_child_process2 = require("child_process");
2193
+ async function checkProcessSpawn(command, args, env, timeoutMs = 3e3) {
2194
+ return new Promise((resolve) => {
2195
+ let settled = false;
2196
+ const child = (0, import_node_child_process2.spawn)(command, args, {
2197
+ env: { ...process.env, ...env },
2198
+ stdio: ["pipe", "pipe", "pipe"]
2199
+ });
2200
+ const timer = setTimeout(() => {
2201
+ if (!settled) {
2202
+ settled = true;
2203
+ child.kill("SIGTERM");
2204
+ resolve({ name: "Process", passed: true, message: "starts successfully (still running)" });
2205
+ }
2206
+ }, timeoutMs);
2207
+ child.on("error", (err) => {
2208
+ if (!settled) {
2209
+ settled = true;
2210
+ clearTimeout(timer);
2211
+ resolve({ name: "Process", passed: false, message: `spawn error: ${err.message}` });
2212
+ }
2213
+ });
2214
+ child.on("exit", (code) => {
2215
+ if (!settled) {
2216
+ settled = true;
1616
2217
  clearTimeout(timer);
1617
2218
  if (code === 0 || code === null) {
1618
2219
  resolve({ name: "Process", passed: true, message: "exits cleanly" });
@@ -1929,12 +2530,12 @@ async function getInstalledServers(clientFilter) {
1929
2530
 
1930
2531
  // src/commands/doctor.ts
1931
2532
  var CHECK_ICON = {
1932
- pass: import_picocolors3.default.green("\u2713"),
1933
- fail: import_picocolors3.default.red("\u2717"),
1934
- skip: import_picocolors3.default.dim("-"),
1935
- warn: import_picocolors3.default.yellow("\u26A0")
2533
+ pass: import_picocolors5.default.green("\u2713"),
2534
+ fail: import_picocolors5.default.red("\u2717"),
2535
+ skip: import_picocolors5.default.dim("-"),
2536
+ warn: import_picocolors5.default.yellow("\u26A0")
1936
2537
  };
1937
- var doctor_default = (0, import_citty3.defineCommand)({
2538
+ var doctor_default = (0, import_citty5.defineCommand)({
1938
2539
  meta: {
1939
2540
  name: "doctor",
1940
2541
  description: "Check MCP server health and configuration"
@@ -1947,11 +2548,11 @@ var doctor_default = (0, import_citty3.defineCommand)({
1947
2548
  }
1948
2549
  },
1949
2550
  async run({ args }) {
1950
- console.log(import_picocolors3.default.bold("\n mcpman doctor\n"));
2551
+ console.log(import_picocolors5.default.bold("\n mcpman doctor\n"));
1951
2552
  const servers = await getInstalledServers();
1952
2553
  if (servers.length === 0) {
1953
2554
  console.log(
1954
- import_picocolors3.default.dim(" No MCP servers installed. Run mcpman install <server> to get started.")
2555
+ import_picocolors5.default.dim(" No MCP servers installed. Run mcpman install <server> to get started.")
1955
2556
  );
1956
2557
  return;
1957
2558
  }
@@ -1968,20 +2569,20 @@ var doctor_default = (0, import_citty3.defineCommand)({
1968
2569
  if (pluginSummary.total > 0) {
1969
2570
  printPluginSection(pluginSummary);
1970
2571
  }
1971
- console.log(import_picocolors3.default.dim(` ${"\u2500".repeat(50)}`));
2572
+ console.log(import_picocolors5.default.dim(` ${"\u2500".repeat(50)}`));
1972
2573
  const parts = [];
1973
- if (passed > 0) parts.push(import_picocolors3.default.green(`${passed} healthy`));
1974
- if (failed > 0) parts.push(import_picocolors3.default.red(`${failed} unhealthy`));
2574
+ if (passed > 0) parts.push(import_picocolors5.default.green(`${passed} healthy`));
2575
+ if (failed > 0) parts.push(import_picocolors5.default.red(`${failed} unhealthy`));
1975
2576
  console.log(` Summary: ${parts.join(", ")}`);
1976
2577
  if (pluginSummary.total > 0) {
1977
2578
  const pParts = [];
1978
- if (pluginSummary.healthy > 0) pParts.push(import_picocolors3.default.green(`${pluginSummary.healthy} ok`));
1979
- if (pluginSummary.unhealthy > 0) pParts.push(import_picocolors3.default.red(`${pluginSummary.unhealthy} broken`));
2579
+ if (pluginSummary.healthy > 0) pParts.push(import_picocolors5.default.green(`${pluginSummary.healthy} ok`));
2580
+ if (pluginSummary.unhealthy > 0) pParts.push(import_picocolors5.default.red(`${pluginSummary.unhealthy} broken`));
1980
2581
  console.log(` Plugins: ${pParts.join(", ")}`);
1981
2582
  }
1982
2583
  if (failed > 0 || pluginSummary.unhealthy > 0) {
1983
2584
  if (!args.fix) {
1984
- console.log(import_picocolors3.default.dim(` Run ${import_picocolors3.default.cyan("mcpman doctor --fix")} for fix suggestions.
2585
+ console.log(import_picocolors5.default.dim(` Run ${import_picocolors5.default.cyan("mcpman doctor --fix")} for fix suggestions.
1985
2586
  `));
1986
2587
  }
1987
2588
  process.exit(1);
@@ -1990,26 +2591,26 @@ var doctor_default = (0, import_citty3.defineCommand)({
1990
2591
  }
1991
2592
  });
1992
2593
  function printPluginSection(summary) {
1993
- console.log(import_picocolors3.default.bold(` Plugins (${summary.total})`));
2594
+ console.log(import_picocolors5.default.bold(` Plugins (${summary.total})`));
1994
2595
  for (const r of summary.results) {
1995
- const icon = r.loadable && r.prefixUnique && r.resolvable ? import_picocolors3.default.green("\u2713") : import_picocolors3.default.red("\u2717");
2596
+ const icon = r.loadable && r.prefixUnique && r.resolvable ? import_picocolors5.default.green("\u2713") : import_picocolors5.default.red("\u2717");
1996
2597
  const label = r.pluginName ? `${r.packageName} (${r.pluginName})` : r.packageName;
1997
- const prefix = r.prefix ? import_picocolors3.default.dim(` [${r.prefix}]`) : "";
2598
+ const prefix = r.prefix ? import_picocolors5.default.dim(` [${r.prefix}]`) : "";
1998
2599
  console.log(` ${icon} ${label}${prefix}`);
1999
2600
  if (r.error) {
2000
- console.log(` ${import_picocolors3.default.yellow("\u2192")} ${r.error}`);
2601
+ console.log(` ${import_picocolors5.default.yellow("\u2192")} ${r.error}`);
2001
2602
  }
2002
2603
  }
2003
2604
  console.log();
2004
2605
  }
2005
2606
  function printServerResult(result, showFix) {
2006
- const icon = result.status === "healthy" ? import_picocolors3.default.green("\u25CF") : import_picocolors3.default.red("\u25CF");
2007
- console.log(` ${icon} ${import_picocolors3.default.bold(result.serverName)}`);
2607
+ const icon = result.status === "healthy" ? import_picocolors5.default.green("\u25CF") : import_picocolors5.default.red("\u25CF");
2608
+ console.log(` ${icon} ${import_picocolors5.default.bold(result.serverName)}`);
2008
2609
  for (const check of result.checks) {
2009
2610
  const checkIcon = check.skipped ? CHECK_ICON.skip : check.passed ? CHECK_ICON.pass : CHECK_ICON.fail;
2010
2611
  console.log(` ${checkIcon} ${check.name}: ${check.message}`);
2011
2612
  if (showFix && !check.passed && !check.skipped && check.fix) {
2012
- console.log(` ${import_picocolors3.default.yellow("\u2192")} Fix: ${import_picocolors3.default.cyan(check.fix)}`);
2613
+ console.log(` ${import_picocolors5.default.yellow("\u2192")} Fix: ${import_picocolors5.default.cyan(check.fix)}`);
2013
2614
  }
2014
2615
  }
2015
2616
  console.log();
@@ -2033,22 +2634,23 @@ async function runParallel(tasks, concurrency) {
2033
2634
 
2034
2635
  // src/commands/export-command.ts
2035
2636
  init_cjs_shims();
2036
- var import_node_fs8 = __toESM(require("fs"), 1);
2037
- var import_node_path8 = __toESM(require("path"), 1);
2038
- var import_citty4 = require("citty");
2039
- var import_picocolors4 = __toESM(require("picocolors"), 1);
2637
+ var import_node_fs10 = __toESM(require("fs"), 1);
2638
+ var import_node_path11 = __toESM(require("path"), 1);
2639
+ var import_citty6 = require("citty");
2640
+ var import_picocolors6 = __toESM(require("picocolors"), 1);
2040
2641
 
2041
2642
  // src/core/export-import-service.ts
2042
2643
  init_cjs_shims();
2043
- var import_node_fs7 = __toESM(require("fs"), 1);
2644
+ var import_node_fs9 = __toESM(require("fs"), 1);
2044
2645
 
2045
2646
  // src/utils/constants.ts
2046
2647
  init_cjs_shims();
2047
2648
  var APP_NAME = "mcpman";
2048
- var APP_VERSION = "0.6.0";
2649
+ var APP_VERSION = "0.7.0";
2049
2650
  var APP_DESCRIPTION = "The package manager for MCP servers";
2050
2651
 
2051
2652
  // src/core/export-import-service.ts
2653
+ init_lockfile();
2052
2654
  init_vault_service();
2053
2655
  function createExportBundle(opts = {}) {
2054
2656
  const { includeVault = true, includePlugins = true } = opts;
@@ -2062,7 +2664,7 @@ function createExportBundle(opts = {}) {
2062
2664
  };
2063
2665
  if (includeVault) {
2064
2666
  const vaultPath = getVaultPath();
2065
- if (import_node_fs7.default.existsSync(vaultPath)) {
2667
+ if (import_node_fs9.default.existsSync(vaultPath)) {
2066
2668
  bundle.vault = readVault();
2067
2669
  }
2068
2670
  }
@@ -2128,7 +2730,7 @@ function importBundle(bundle, opts = {}) {
2128
2730
 
2129
2731
  // src/commands/export-command.ts
2130
2732
  var DEFAULT_OUTPUT = "mcpman-export.json";
2131
- var export_command_default = (0, import_citty4.defineCommand)({
2733
+ var export_command_default = (0, import_citty6.defineCommand)({
2132
2734
  meta: {
2133
2735
  name: "export",
2134
2736
  description: "Export mcpman config, lockfile, vault, and plugins to a portable JSON file"
@@ -2152,30 +2754,30 @@ var export_command_default = (0, import_citty4.defineCommand)({
2152
2754
  },
2153
2755
  run({ args }) {
2154
2756
  const outputFile = args.output || DEFAULT_OUTPUT;
2155
- const outputPath = import_node_path8.default.resolve(outputFile);
2757
+ const outputPath = import_node_path11.default.resolve(outputFile);
2156
2758
  const bundle = createExportBundle({
2157
2759
  includeVault: !args["no-vault"],
2158
2760
  includePlugins: !args["no-plugins"]
2159
2761
  });
2160
2762
  const serverCount = Object.keys(bundle.lockfile.servers).length;
2161
2763
  const configKeys = Object.keys(bundle.config).length;
2162
- import_node_fs8.default.writeFileSync(outputPath, JSON.stringify(bundle, null, 2), "utf-8");
2163
- console.log(`${import_picocolors4.default.green("\u2713")} Exported to ${import_picocolors4.default.bold(outputFile)}`);
2164
- console.log(import_picocolors4.default.dim(` Config keys: ${configKeys}`));
2165
- console.log(import_picocolors4.default.dim(` Servers: ${serverCount}`));
2166
- console.log(import_picocolors4.default.dim(` Vault: ${bundle.vault ? "included" : "excluded"}`));
2167
- console.log(import_picocolors4.default.dim(` Plugins: ${bundle.plugins?.length ?? 0}`));
2764
+ import_node_fs10.default.writeFileSync(outputPath, JSON.stringify(bundle, null, 2), "utf-8");
2765
+ console.log(`${import_picocolors6.default.green("\u2713")} Exported to ${import_picocolors6.default.bold(outputFile)}`);
2766
+ console.log(import_picocolors6.default.dim(` Config keys: ${configKeys}`));
2767
+ console.log(import_picocolors6.default.dim(` Servers: ${serverCount}`));
2768
+ console.log(import_picocolors6.default.dim(` Vault: ${bundle.vault ? "included" : "excluded"}`));
2769
+ console.log(import_picocolors6.default.dim(` Plugins: ${bundle.plugins?.length ?? 0}`));
2168
2770
  }
2169
2771
  });
2170
2772
 
2171
2773
  // src/commands/import-command.ts
2172
2774
  init_cjs_shims();
2173
- var import_node_fs9 = __toESM(require("fs"), 1);
2174
- var import_node_path9 = __toESM(require("path"), 1);
2775
+ var import_node_fs11 = __toESM(require("fs"), 1);
2776
+ var import_node_path12 = __toESM(require("path"), 1);
2175
2777
  var p4 = __toESM(require("@clack/prompts"), 1);
2176
- var import_citty5 = require("citty");
2177
- var import_picocolors5 = __toESM(require("picocolors"), 1);
2178
- var import_command_default = (0, import_citty5.defineCommand)({
2778
+ var import_citty7 = require("citty");
2779
+ var import_picocolors7 = __toESM(require("picocolors"), 1);
2780
+ var import_command_default = (0, import_citty7.defineCommand)({
2179
2781
  meta: {
2180
2782
  name: "import",
2181
2783
  description: "Import mcpman config, lockfile, vault, and plugins from an export file"
@@ -2198,21 +2800,21 @@ var import_command_default = (0, import_citty5.defineCommand)({
2198
2800
  }
2199
2801
  },
2200
2802
  async run({ args }) {
2201
- const filePath = import_node_path9.default.resolve(args.file);
2202
- if (!import_node_fs9.default.existsSync(filePath)) {
2203
- console.error(`${import_picocolors5.default.red("\u2717")} File not found: ${filePath}`);
2803
+ const filePath = import_node_path12.default.resolve(args.file);
2804
+ if (!import_node_fs11.default.existsSync(filePath)) {
2805
+ console.error(`${import_picocolors7.default.red("\u2717")} File not found: ${filePath}`);
2204
2806
  process.exit(1);
2205
2807
  }
2206
2808
  let raw;
2207
2809
  try {
2208
- raw = JSON.parse(import_node_fs9.default.readFileSync(filePath, "utf-8"));
2810
+ raw = JSON.parse(import_node_fs11.default.readFileSync(filePath, "utf-8"));
2209
2811
  } catch {
2210
- console.error(`${import_picocolors5.default.red("\u2717")} Invalid JSON in ${filePath}`);
2812
+ console.error(`${import_picocolors7.default.red("\u2717")} Invalid JSON in ${filePath}`);
2211
2813
  process.exit(1);
2212
2814
  }
2213
2815
  const error2 = validateBundle(raw);
2214
2816
  if (error2) {
2215
- console.error(`${import_picocolors5.default.red("\u2717")} Invalid export bundle: ${error2}`);
2817
+ console.error(`${import_picocolors7.default.red("\u2717")} Invalid export bundle: ${error2}`);
2216
2818
  process.exit(1);
2217
2819
  }
2218
2820
  const bundle = raw;
@@ -2222,16 +2824,16 @@ var import_command_default = (0, import_citty5.defineCommand)({
2222
2824
  const hasVault = !!bundle.vault;
2223
2825
  const isDryRun = !!args["dry-run"];
2224
2826
  console.log("");
2225
- console.log(import_picocolors5.default.bold("Import summary:"));
2226
- console.log(import_picocolors5.default.dim(` Source version: mcpman ${bundle.mcpmanVersion}`));
2227
- console.log(import_picocolors5.default.dim(` Exported at: ${bundle.exportedAt}`));
2228
- console.log(` Config keys: ${import_picocolors5.default.cyan(String(configKeys))}`);
2229
- console.log(` Servers: ${import_picocolors5.default.cyan(String(serverCount))}`);
2230
- console.log(` Vault: ${hasVault ? import_picocolors5.default.green("included") : import_picocolors5.default.dim("not included")}`);
2231
- console.log(` Plugins: ${import_picocolors5.default.cyan(String(pluginCount))}`);
2827
+ console.log(import_picocolors7.default.bold("Import summary:"));
2828
+ console.log(import_picocolors7.default.dim(` Source version: mcpman ${bundle.mcpmanVersion}`));
2829
+ console.log(import_picocolors7.default.dim(` Exported at: ${bundle.exportedAt}`));
2830
+ console.log(` Config keys: ${import_picocolors7.default.cyan(String(configKeys))}`);
2831
+ console.log(` Servers: ${import_picocolors7.default.cyan(String(serverCount))}`);
2832
+ console.log(` Vault: ${hasVault ? import_picocolors7.default.green("included") : import_picocolors7.default.dim("not included")}`);
2833
+ console.log(` Plugins: ${import_picocolors7.default.cyan(String(pluginCount))}`);
2232
2834
  console.log("");
2233
2835
  if (isDryRun) {
2234
- console.log(import_picocolors5.default.yellow(" [dry-run] No changes applied."));
2836
+ console.log(import_picocolors7.default.yellow(" [dry-run] No changes applied."));
2235
2837
  return;
2236
2838
  }
2237
2839
  if (!args.yes) {
@@ -2245,22 +2847,23 @@ var import_command_default = (0, import_citty5.defineCommand)({
2245
2847
  }
2246
2848
  }
2247
2849
  const summary = importBundle(bundle, { dryRun: false });
2248
- console.log(`${import_picocolors5.default.green("\u2713")} Import complete`);
2249
- console.log(import_picocolors5.default.dim(` Config keys restored: ${summary.configKeys}`));
2250
- console.log(import_picocolors5.default.dim(` Servers restored: ${summary.servers}`));
2251
- console.log(import_picocolors5.default.dim(` Vault: ${summary.vaultImported ? "restored" : "skipped"}`));
2252
- console.log(import_picocolors5.default.dim(` Plugins installed: ${summary.pluginsInstalled}`));
2850
+ console.log(`${import_picocolors7.default.green("\u2713")} Import complete`);
2851
+ console.log(import_picocolors7.default.dim(` Config keys restored: ${summary.configKeys}`));
2852
+ console.log(import_picocolors7.default.dim(` Servers restored: ${summary.servers}`));
2853
+ console.log(import_picocolors7.default.dim(` Vault: ${summary.vaultImported ? "restored" : "skipped"}`));
2854
+ console.log(import_picocolors7.default.dim(` Plugins installed: ${summary.pluginsInstalled}`));
2253
2855
  }
2254
2856
  });
2255
2857
 
2256
2858
  // src/commands/info.ts
2257
2859
  init_cjs_shims();
2258
- var import_citty6 = require("citty");
2860
+ var import_citty8 = require("citty");
2259
2861
  var import_nanospinner2 = require("nanospinner");
2260
- var import_picocolors6 = __toESM(require("picocolors"), 1);
2862
+ var import_picocolors8 = __toESM(require("picocolors"), 1);
2261
2863
 
2262
2864
  // src/core/package-info.ts
2263
2865
  init_cjs_shims();
2866
+ init_lockfile();
2264
2867
  init_trust_scorer();
2265
2868
  async function buildInfo(name, entry, source = "npm") {
2266
2869
  const resolvedSource = entry?.source ?? source;
@@ -2314,11 +2917,11 @@ async function getPackageInfo(serverName) {
2314
2917
  // src/commands/info.ts
2315
2918
  function colorRisk2(score, riskLevel) {
2316
2919
  const label = score !== null ? `${score}/100 (${riskLevel})` : riskLevel;
2317
- if (riskLevel === "LOW") return import_picocolors6.default.green(label);
2318
- if (riskLevel === "MEDIUM") return import_picocolors6.default.yellow(label);
2319
- if (riskLevel === "HIGH") return import_picocolors6.default.red(label);
2320
- if (riskLevel === "CRITICAL") return import_picocolors6.default.bold(import_picocolors6.default.red(label));
2321
- return import_picocolors6.default.dim(label);
2920
+ if (riskLevel === "LOW") return import_picocolors8.default.green(label);
2921
+ if (riskLevel === "MEDIUM") return import_picocolors8.default.yellow(label);
2922
+ if (riskLevel === "HIGH") return import_picocolors8.default.red(label);
2923
+ if (riskLevel === "CRITICAL") return import_picocolors8.default.bold(import_picocolors8.default.red(label));
2924
+ return import_picocolors8.default.dim(label);
2322
2925
  }
2323
2926
  function formatDaysAgo(isoDate) {
2324
2927
  if (!isoDate) return "unknown";
@@ -2328,54 +2931,54 @@ function formatDaysAgo(isoDate) {
2328
2931
  return `${days} days ago`;
2329
2932
  }
2330
2933
  function printInfo(info2) {
2331
- const installedBadge = info2.isInstalled ? import_picocolors6.default.green(" [installed]") : import_picocolors6.default.dim(" [not installed]");
2934
+ const installedBadge = info2.isInstalled ? import_picocolors8.default.green(" [installed]") : import_picocolors8.default.dim(" [not installed]");
2332
2935
  console.log();
2333
- console.log(import_picocolors6.default.bold(` ${info2.name}@${info2.version}`) + installedBadge);
2334
- console.log(import_picocolors6.default.dim(` ${"\u2500".repeat(60)}`));
2335
- console.log(` ${import_picocolors6.default.dim("Source:")} ${info2.source}`);
2336
- console.log(` ${import_picocolors6.default.dim("Runtime:")} ${info2.runtime}`);
2936
+ console.log(import_picocolors8.default.bold(` ${info2.name}@${info2.version}`) + installedBadge);
2937
+ console.log(import_picocolors8.default.dim(` ${"\u2500".repeat(60)}`));
2938
+ console.log(` ${import_picocolors8.default.dim("Source:")} ${info2.source}`);
2939
+ console.log(` ${import_picocolors8.default.dim("Runtime:")} ${info2.runtime}`);
2337
2940
  if (info2.description) {
2338
- console.log(` ${import_picocolors6.default.dim("Description:")} ${info2.description}`);
2941
+ console.log(` ${import_picocolors8.default.dim("Description:")} ${info2.description}`);
2339
2942
  }
2340
2943
  if (info2.deprecated) {
2341
- console.log(` ${import_picocolors6.default.red("[DEPRECATED]")} This package is deprecated`);
2944
+ console.log(` ${import_picocolors8.default.red("[DEPRECATED]")} This package is deprecated`);
2342
2945
  }
2343
2946
  console.log();
2344
- console.log(` ${import_picocolors6.default.bold("Trust & Security")}`);
2345
- console.log(` ${import_picocolors6.default.dim("Trust score:")} ${colorRisk2(info2.trustScore, info2.riskLevel)}`);
2947
+ console.log(` ${import_picocolors8.default.bold("Trust & Security")}`);
2948
+ console.log(` ${import_picocolors8.default.dim("Trust score:")} ${colorRisk2(info2.trustScore, info2.riskLevel)}`);
2346
2949
  if (info2.source === "npm") {
2347
2950
  console.log(
2348
- ` ${import_picocolors6.default.dim("Downloads:")} ${info2.weeklyDownloads.toLocaleString()}/week ${import_picocolors6.default.dim("|")} ${import_picocolors6.default.dim("Age:")} ${info2.packageAge}d ${import_picocolors6.default.dim("|")} ${import_picocolors6.default.dim("Maintainers:")} ${info2.maintainerCount}`
2951
+ ` ${import_picocolors8.default.dim("Downloads:")} ${info2.weeklyDownloads.toLocaleString()}/week ${import_picocolors8.default.dim("|")} ${import_picocolors8.default.dim("Age:")} ${info2.packageAge}d ${import_picocolors8.default.dim("|")} ${import_picocolors8.default.dim("Maintainers:")} ${info2.maintainerCount}`
2349
2952
  );
2350
2953
  if (info2.lastPublish) {
2351
- console.log(` ${import_picocolors6.default.dim("Last publish:")} ${formatDaysAgo(info2.lastPublish)}`);
2954
+ console.log(` ${import_picocolors8.default.dim("Last publish:")} ${formatDaysAgo(info2.lastPublish)}`);
2352
2955
  }
2353
2956
  } else {
2354
- console.log(import_picocolors6.default.dim(" (Trust data available for npm packages only)"));
2957
+ console.log(import_picocolors8.default.dim(" (Trust data available for npm packages only)"));
2355
2958
  }
2356
2959
  console.log();
2357
- console.log(` ${import_picocolors6.default.bold("Environment Variables")}`);
2960
+ console.log(` ${import_picocolors8.default.bold("Environment Variables")}`);
2358
2961
  if (info2.envVars.length > 0) {
2359
2962
  for (const env of info2.envVars) {
2360
- console.log(` ${import_picocolors6.default.cyan("\u2022")} ${env}`);
2963
+ console.log(` ${import_picocolors8.default.cyan("\u2022")} ${env}`);
2361
2964
  }
2362
2965
  } else {
2363
- console.log(import_picocolors6.default.dim(" none required"));
2966
+ console.log(import_picocolors8.default.dim(" none required"));
2364
2967
  }
2365
2968
  console.log();
2366
- console.log(` ${import_picocolors6.default.bold("Installed Clients")}`);
2969
+ console.log(` ${import_picocolors8.default.bold("Installed Clients")}`);
2367
2970
  if (info2.installedClients.length > 0) {
2368
2971
  for (const client of info2.installedClients) {
2369
- console.log(` ${import_picocolors6.default.green("\u2713")} ${client}`);
2972
+ console.log(` ${import_picocolors8.default.green("\u2713")} ${client}`);
2370
2973
  }
2371
2974
  } else {
2372
- console.log(import_picocolors6.default.dim(" Not installed in any client"));
2975
+ console.log(import_picocolors8.default.dim(" Not installed in any client"));
2373
2976
  }
2374
2977
  console.log();
2375
- console.log(import_picocolors6.default.dim(` ${"\u2500".repeat(60)}`));
2978
+ console.log(import_picocolors8.default.dim(` ${"\u2500".repeat(60)}`));
2376
2979
  console.log();
2377
2980
  }
2378
- var info_default = (0, import_citty6.defineCommand)({
2981
+ var info_default = (0, import_citty8.defineCommand)({
2379
2982
  meta: {
2380
2983
  name: "info",
2381
2984
  description: "Show detailed metadata for an MCP server (installed or from registry)"
@@ -2399,13 +3002,13 @@ var info_default = (0, import_citty6.defineCommand)({
2399
3002
  info2 = await getPackageInfo(args.server);
2400
3003
  } catch (err) {
2401
3004
  spinner5.error({ text: "Failed to fetch package info" });
2402
- console.error(import_picocolors6.default.red(String(err)));
3005
+ console.error(import_picocolors8.default.red(String(err)));
2403
3006
  process.exit(1);
2404
3007
  }
2405
3008
  if (!info2) {
2406
3009
  spinner5.error({ text: `Package not found: ${args.server}` });
2407
3010
  console.log(
2408
- import_picocolors6.default.dim(`
3011
+ import_picocolors8.default.dim(`
2409
3012
  "${args.server}" was not found in the npm registry or your lockfile.
2410
3013
  `)
2411
3014
  );
@@ -2422,10 +3025,11 @@ var info_default = (0, import_citty6.defineCommand)({
2422
3025
 
2423
3026
  // src/commands/init.ts
2424
3027
  init_cjs_shims();
2425
- var import_node_path10 = __toESM(require("path"), 1);
3028
+ var import_node_path13 = __toESM(require("path"), 1);
2426
3029
  var p5 = __toESM(require("@clack/prompts"), 1);
2427
- var import_citty7 = require("citty");
2428
- var init_default = (0, import_citty7.defineCommand)({
3030
+ var import_citty9 = require("citty");
3031
+ init_lockfile();
3032
+ var init_default = (0, import_citty9.defineCommand)({
2429
3033
  meta: {
2430
3034
  name: "init",
2431
3035
  description: "Initialize mcpman.lock in the current project"
@@ -2441,7 +3045,7 @@ var init_default = (0, import_citty7.defineCommand)({
2441
3045
  async run({ args }) {
2442
3046
  const nonInteractive = args.yes || !process.stdout.isTTY;
2443
3047
  p5.intro("mcpman init");
2444
- const targetPath = import_node_path10.default.join(process.cwd(), LOCKFILE_NAME);
3048
+ const targetPath = import_node_path13.default.join(process.cwd(), LOCKFILE_NAME);
2445
3049
  const existing = findLockfile();
2446
3050
  if (existing) {
2447
3051
  if (nonInteractive) {
@@ -2526,7 +3130,7 @@ var init_default = (0, import_citty7.defineCommand)({
2526
3130
  // src/commands/install.ts
2527
3131
  init_cjs_shims();
2528
3132
  var p8 = __toESM(require("@clack/prompts"), 1);
2529
- var import_citty8 = require("citty");
3133
+ var import_citty10 = require("citty");
2530
3134
 
2531
3135
  // src/core/installer.ts
2532
3136
  init_cjs_shims();
@@ -2567,6 +3171,7 @@ async function offerVaultSave(serverName, newVars, yes) {
2567
3171
  }
2568
3172
 
2569
3173
  // src/core/installer.ts
3174
+ init_lockfile();
2570
3175
  async function loadClients2() {
2571
3176
  try {
2572
3177
  const mod = await Promise.resolve().then(() => (init_client_detector(), client_detector_exports));
@@ -2680,9 +3285,12 @@ async function installServer(input, options = {}) {
2680
3285
  p7.outro(`${metadata.name}@${metadata.version} installed to ${clientTypes.join(", ")}`);
2681
3286
  }
2682
3287
 
3288
+ // src/commands/install.ts
3289
+ init_lockfile();
3290
+
2683
3291
  // src/utils/logger.ts
2684
3292
  init_cjs_shims();
2685
- var import_picocolors7 = __toESM(require("picocolors"), 1);
3293
+ var import_picocolors9 = __toESM(require("picocolors"), 1);
2686
3294
  var noColor = process.env.NO_COLOR !== void 0 || process.argv.includes("--no-color");
2687
3295
  var isVerbose = process.argv.includes("--verbose");
2688
3296
  var isJson = process.argv.includes("--json");
@@ -2691,18 +3299,18 @@ function colorize(fn, text2) {
2691
3299
  }
2692
3300
  function info(message) {
2693
3301
  if (isJson) return;
2694
- console.log(`${colorize(import_picocolors7.default.cyan, "i")} ${message}`);
3302
+ console.log(`${colorize(import_picocolors9.default.cyan, "i")} ${message}`);
2695
3303
  }
2696
3304
  function error(message) {
2697
3305
  if (isJson) return;
2698
- console.error(`${colorize(import_picocolors7.default.red, "\u2717")} ${message}`);
3306
+ console.error(`${colorize(import_picocolors9.default.red, "\u2717")} ${message}`);
2699
3307
  }
2700
3308
  function json(data) {
2701
3309
  console.log(JSON.stringify(data, null, 2));
2702
3310
  }
2703
3311
 
2704
3312
  // src/commands/install.ts
2705
- var install_default = (0, import_citty8.defineCommand)({
3313
+ var install_default = (0, import_citty10.defineCommand)({
2706
3314
  meta: {
2707
3315
  name: "install",
2708
3316
  description: "Install an MCP server into one or more AI clients"
@@ -2763,16 +3371,220 @@ async function restoreFromLockfile() {
2763
3371
  p8.outro("Restore complete.");
2764
3372
  }
2765
3373
 
3374
+ // src/commands/link.ts
3375
+ init_cjs_shims();
3376
+ var import_node_path15 = __toESM(require("path"), 1);
3377
+ var import_citty11 = require("citty");
3378
+ var import_picocolors10 = __toESM(require("picocolors"), 1);
3379
+ init_client_detector();
3380
+
3381
+ // src/core/link-service.ts
3382
+ init_cjs_shims();
3383
+ var import_node_fs12 = __toESM(require("fs"), 1);
3384
+ var import_node_path14 = __toESM(require("path"), 1);
3385
+ init_lockfile();
3386
+ function detectLocalServer(dir) {
3387
+ if (!import_node_fs12.default.existsSync(dir)) {
3388
+ throw new Error(`Directory does not exist: ${dir}`);
3389
+ }
3390
+ const pkgPath = import_node_path14.default.join(dir, "package.json");
3391
+ if (import_node_fs12.default.existsSync(pkgPath)) {
3392
+ return detectNodeServer(dir, pkgPath);
3393
+ }
3394
+ const pyprojectPath = import_node_path14.default.join(dir, "pyproject.toml");
3395
+ if (import_node_fs12.default.existsSync(pyprojectPath)) {
3396
+ return detectPythonServer(dir, pyprojectPath);
3397
+ }
3398
+ throw new Error(
3399
+ `No package.json or pyproject.toml found in '${dir}'. Is this an MCP server project?`
3400
+ );
3401
+ }
3402
+ function detectNodeServer(dir, pkgPath) {
3403
+ const raw = import_node_fs12.default.readFileSync(pkgPath, "utf-8");
3404
+ const pkg = JSON.parse(raw);
3405
+ const name = String(pkg.name ?? import_node_path14.default.basename(dir));
3406
+ const version = String(pkg.version ?? "0.0.0");
3407
+ let entryPoint = null;
3408
+ if (pkg.bin) {
3409
+ if (typeof pkg.bin === "string") {
3410
+ entryPoint = import_node_path14.default.resolve(dir, pkg.bin);
3411
+ } else if (typeof pkg.bin === "object" && pkg.bin !== null) {
3412
+ const binObj = pkg.bin;
3413
+ const firstBin = Object.values(binObj)[0];
3414
+ if (firstBin) entryPoint = import_node_path14.default.resolve(dir, firstBin);
3415
+ }
3416
+ }
3417
+ if (!entryPoint && pkg.main) {
3418
+ entryPoint = import_node_path14.default.resolve(dir, String(pkg.main));
3419
+ }
3420
+ if (!entryPoint) {
3421
+ const candidates = ["src/index.ts", "src/index.js", "index.ts", "index.js"];
3422
+ for (const c of candidates) {
3423
+ const candidate = import_node_path14.default.join(dir, c);
3424
+ if (import_node_fs12.default.existsSync(candidate)) {
3425
+ entryPoint = candidate;
3426
+ break;
3427
+ }
3428
+ }
3429
+ }
3430
+ if (!entryPoint) {
3431
+ throw new Error(`Cannot determine entry point for Node server in '${dir}'.`);
3432
+ }
3433
+ const isTs = entryPoint.endsWith(".ts");
3434
+ const command = isTs ? "npx" : "node";
3435
+ const args = isTs ? ["tsx", entryPoint] : [entryPoint];
3436
+ const mcpField = pkg.mcp;
3437
+ const envVars = [];
3438
+ if (mcpField?.env && Array.isArray(mcpField.env)) {
3439
+ for (const e of mcpField.env) {
3440
+ if (typeof e === "string") envVars.push(e);
3441
+ else if (typeof e === "object" && e !== null && "name" in e) {
3442
+ envVars.push(String(e.name));
3443
+ }
3444
+ }
3445
+ }
3446
+ return { name, version, command, args, envVars, absolutePath: dir, runtime: "node" };
3447
+ }
3448
+ function detectPythonServer(dir, pyprojectPath) {
3449
+ const raw = import_node_fs12.default.readFileSync(pyprojectPath, "utf-8");
3450
+ const name = extractTomlValue(raw, "name") ?? import_node_path14.default.basename(dir);
3451
+ const version = extractTomlValue(raw, "version") ?? "0.0.0";
3452
+ let pythonCmd = "python3";
3453
+ const venvPython = import_node_path14.default.join(dir, ".venv", "bin", "python");
3454
+ if (import_node_fs12.default.existsSync(venvPython)) {
3455
+ pythonCmd = venvPython;
3456
+ }
3457
+ const entryCandidate = [
3458
+ import_node_path14.default.join(dir, "main.py"),
3459
+ import_node_path14.default.join(dir, name.replace(/-/g, "_"), "main.py"),
3460
+ import_node_path14.default.join(dir, "__main__.py")
3461
+ ].find((p13) => import_node_fs12.default.existsSync(p13));
3462
+ const entryPoint = entryCandidate ?? import_node_path14.default.join(dir, "main.py");
3463
+ return {
3464
+ name,
3465
+ version,
3466
+ command: pythonCmd,
3467
+ args: [entryPoint],
3468
+ envVars: [],
3469
+ absolutePath: dir,
3470
+ runtime: "python"
3471
+ };
3472
+ }
3473
+ function extractTomlValue(content, key) {
3474
+ const match = content.match(new RegExp(`^${key}\\s*=\\s*"([^"]+)"`, "m"));
3475
+ return match ? match[1] : null;
3476
+ }
3477
+ async function registerLinkedServer(linkResult, clients, lockfilePath, nameOverride) {
3478
+ const serverName = nameOverride ?? linkResult.name;
3479
+ const registered = [];
3480
+ for (const client of clients) {
3481
+ try {
3482
+ await client.addServer(serverName, {
3483
+ command: linkResult.command,
3484
+ args: linkResult.args
3485
+ });
3486
+ registered.push(client.type);
3487
+ } catch {
3488
+ }
3489
+ }
3490
+ const lockEntry = {
3491
+ version: linkResult.version,
3492
+ source: "local",
3493
+ resolved: linkResult.absolutePath,
3494
+ integrity: "local",
3495
+ runtime: linkResult.runtime,
3496
+ command: linkResult.command,
3497
+ args: linkResult.args,
3498
+ envVars: linkResult.envVars,
3499
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
3500
+ clients: registered
3501
+ };
3502
+ const existing = readLockfile(lockfilePath);
3503
+ existing.servers[serverName] = lockEntry;
3504
+ const { writeLockfile: writeLockfile2 } = await Promise.resolve().then(() => (init_lockfile(), lockfile_exports));
3505
+ writeLockfile2(existing, lockfilePath);
3506
+ return registered;
3507
+ }
3508
+
3509
+ // src/commands/link.ts
3510
+ var link_default = (0, import_citty11.defineCommand)({
3511
+ meta: {
3512
+ name: "link",
3513
+ description: "Register a local MCP server directory with AI clients"
3514
+ },
3515
+ args: {
3516
+ dir: {
3517
+ type: "positional",
3518
+ description: "Path to local MCP server directory (default: .)",
3519
+ required: false
3520
+ },
3521
+ client: {
3522
+ type: "string",
3523
+ description: "Register with specific client only (claude-desktop, cursor, vscode, windsurf)",
3524
+ alias: "c"
3525
+ },
3526
+ name: {
3527
+ type: "string",
3528
+ description: "Override the detected server name",
3529
+ alias: "n"
3530
+ }
3531
+ },
3532
+ async run({ args }) {
3533
+ const dirArg = args.dir ?? ".";
3534
+ const clientFilter = args.client;
3535
+ const nameOverride = args.name;
3536
+ const absoluteDir = import_node_path15.default.resolve(dirArg);
3537
+ let linkResult;
3538
+ try {
3539
+ linkResult = detectLocalServer(absoluteDir);
3540
+ } catch (err) {
3541
+ console.error(import_picocolors10.default.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3542
+ process.exit(1);
3543
+ }
3544
+ const serverName = nameOverride ?? linkResult.name;
3545
+ console.log(import_picocolors10.default.dim(`
3546
+ Detected: ${import_picocolors10.default.cyan(serverName)} (${linkResult.runtime})`));
3547
+ console.log(import_picocolors10.default.dim(` Path: ${absoluteDir}`));
3548
+ console.log(import_picocolors10.default.dim(` Command: ${linkResult.command} ${linkResult.args.join(" ")}`));
3549
+ const allClients = await getInstalledClients();
3550
+ const clients = clientFilter ? allClients.filter((c) => c.type === clientFilter) : allClients;
3551
+ if (clientFilter && clients.length === 0) {
3552
+ console.error(import_picocolors10.default.red(` Error: Unknown client '${clientFilter}'.`));
3553
+ process.exit(1);
3554
+ }
3555
+ let registered;
3556
+ try {
3557
+ registered = await registerLinkedServer(linkResult, clients, void 0, nameOverride);
3558
+ } catch (err) {
3559
+ console.error(import_picocolors10.default.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3560
+ process.exit(1);
3561
+ }
3562
+ if (registered.length === 0) {
3563
+ console.log(import_picocolors10.default.yellow(" Warning: No clients registered. Are any AI clients installed?"));
3564
+ console.log(import_picocolors10.default.dim(` Server saved to lockfile with source "local".`));
3565
+ } else {
3566
+ console.log(import_picocolors10.default.green(`
3567
+ Linked ${import_picocolors10.default.bold(serverName)} to: ${registered.join(", ")}
3568
+ `));
3569
+ console.log(import_picocolors10.default.dim(` Run ${import_picocolors10.default.cyan("mcpman list")} to verify.`));
3570
+ console.log(
3571
+ import_picocolors10.default.dim(` Run ${import_picocolors10.default.cyan(`mcpman watch ${serverName}`)} to start with auto-restart.`)
3572
+ );
3573
+ }
3574
+ console.log();
3575
+ }
3576
+ });
3577
+
2766
3578
  // src/commands/list.ts
2767
3579
  init_cjs_shims();
2768
- var import_citty9 = require("citty");
2769
- var import_picocolors8 = __toESM(require("picocolors"), 1);
3580
+ var import_citty12 = require("citty");
3581
+ var import_picocolors11 = __toESM(require("picocolors"), 1);
2770
3582
  var STATUS_ICON = {
2771
- healthy: import_picocolors8.default.green("\u25CF"),
2772
- unhealthy: import_picocolors8.default.red("\u25CF"),
2773
- unknown: import_picocolors8.default.dim("\u25CB")
3583
+ healthy: import_picocolors11.default.green("\u25CF"),
3584
+ unhealthy: import_picocolors11.default.red("\u25CF"),
3585
+ unknown: import_picocolors11.default.dim("\u25CB")
2774
3586
  };
2775
- var list_default = (0, import_citty9.defineCommand)({
3587
+ var list_default = (0, import_citty12.defineCommand)({
2776
3588
  meta: {
2777
3589
  name: "list",
2778
3590
  description: "List installed MCP servers"
@@ -2793,8 +3605,8 @@ var list_default = (0, import_citty9.defineCommand)({
2793
3605
  if (servers.length === 0) {
2794
3606
  const filter = args.client ? ` for client "${args.client}"` : "";
2795
3607
  console.log(
2796
- import_picocolors8.default.dim(
2797
- `No MCP servers installed${filter}. Run ${import_picocolors8.default.cyan("mcpman install <server>")} to get started.`
3608
+ import_picocolors11.default.dim(
3609
+ `No MCP servers installed${filter}. Run ${import_picocolors11.default.cyan("mcpman install <server>")} to get started.`
2798
3610
  )
2799
3611
  );
2800
3612
  return;
@@ -2820,9 +3632,9 @@ var list_default = (0, import_citty9.defineCommand)({
2820
3632
  const nameWidth = Math.max(4, ...withStatus.map((s) => s.name.length));
2821
3633
  const clientsWidth = Math.max(7, ...withStatus.map((s) => formatClients(s.clients).length));
2822
3634
  const header = ` ${pad("NAME", nameWidth)} ${pad("CLIENT(S)", clientsWidth)} ${pad("COMMAND", 20)} STATUS`;
2823
- console.log(import_picocolors8.default.dim(header));
3635
+ console.log(import_picocolors11.default.dim(header));
2824
3636
  console.log(
2825
- import_picocolors8.default.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientsWidth)} ${"-".repeat(20)} ------`)
3637
+ import_picocolors11.default.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientsWidth)} ${"-".repeat(20)} ------`)
2826
3638
  );
2827
3639
  for (const s of withStatus) {
2828
3640
  const icon = STATUS_ICON[s.status];
@@ -2837,7 +3649,7 @@ var list_default = (0, import_citty9.defineCommand)({
2837
3649
  }
2838
3650
  const clientSet = new Set(withStatus.flatMap((s) => s.clients));
2839
3651
  console.log(
2840
- import_picocolors8.default.dim(
3652
+ import_picocolors11.default.dim(
2841
3653
  `
2842
3654
  ${withStatus.length} server${withStatus.length !== 1 ? "s" : ""} \xB7 ${clientSet.size} client${clientSet.size !== 1 ? "s" : ""}`
2843
3655
  )
@@ -2863,10 +3675,11 @@ function formatClients(clients) {
2863
3675
  // src/commands/logs.ts
2864
3676
  init_cjs_shims();
2865
3677
  var import_node_child_process4 = require("child_process");
2866
- var import_citty10 = require("citty");
2867
- var import_picocolors9 = __toESM(require("picocolors"), 1);
3678
+ var import_citty13 = require("citty");
3679
+ var import_picocolors12 = __toESM(require("picocolors"), 1);
3680
+ init_lockfile();
2868
3681
  init_vault_service();
2869
- var logs_default = (0, import_citty10.defineCommand)({
3682
+ var logs_default = (0, import_citty13.defineCommand)({
2870
3683
  meta: {
2871
3684
  name: "logs",
2872
3685
  description: "Stream stdout/stderr from an MCP server"
@@ -2889,7 +3702,7 @@ var logs_default = (0, import_citty10.defineCommand)({
2889
3702
  const lockfile = readLockfile();
2890
3703
  const entry = lockfile.servers[serverName];
2891
3704
  if (!entry) {
2892
- console.error(import_picocolors9.default.red(` Error: Server '${serverName}' is not installed.`));
3705
+ console.error(import_picocolors12.default.red(` Error: Server '${serverName}' is not installed.`));
2893
3706
  process.exit(1);
2894
3707
  }
2895
3708
  const lockfileEnv = parseEnvFlags(entry.envVars);
@@ -2899,24 +3712,24 @@ var logs_default = (0, import_citty10.defineCommand)({
2899
3712
  ...lockfileEnv,
2900
3713
  ...vaultEnv
2901
3714
  };
2902
- console.log(import_picocolors9.default.dim(` Streaming logs for ${import_picocolors9.default.cyan(serverName)}... (Ctrl+C to stop)
3715
+ console.log(import_picocolors12.default.dim(` Streaming logs for ${import_picocolors12.default.cyan(serverName)}... (Ctrl+C to stop)
2903
3716
  `));
2904
3717
  const child = (0, import_node_child_process4.spawn)(entry.command, entry.args, {
2905
3718
  env: finalEnv,
2906
3719
  stdio: ["pipe", "pipe", "pipe"]
2907
3720
  });
2908
3721
  child.stdout?.on("data", (chunk) => {
2909
- process.stdout.write(import_picocolors9.default.dim("[stdout] ") + chunk.toString());
3722
+ process.stdout.write(import_picocolors12.default.dim("[stdout] ") + chunk.toString());
2910
3723
  });
2911
3724
  child.stderr?.on("data", (chunk) => {
2912
- process.stderr.write(import_picocolors9.default.yellow("[stderr] ") + chunk.toString());
3725
+ process.stderr.write(import_picocolors12.default.yellow("[stderr] ") + chunk.toString());
2913
3726
  });
2914
3727
  child.on("error", (err) => {
2915
- console.error(import_picocolors9.default.red(` Failed to start '${serverName}': ${err.message}`));
3728
+ console.error(import_picocolors12.default.red(` Failed to start '${serverName}': ${err.message}`));
2916
3729
  process.exit(1);
2917
3730
  });
2918
3731
  child.on("close", (code) => {
2919
- console.log(import_picocolors9.default.dim(`
3732
+ console.log(import_picocolors12.default.dim(`
2920
3733
  Process exited with code ${code ?? 0}`));
2921
3734
  process.exit(code ?? 0);
2922
3735
  });
@@ -2942,10 +3755,10 @@ async function loadVaultSecrets(serverName) {
2942
3755
 
2943
3756
  // src/commands/plugin.ts
2944
3757
  init_cjs_shims();
2945
- var import_citty11 = require("citty");
3758
+ var import_citty14 = require("citty");
2946
3759
  var import_nanospinner3 = require("nanospinner");
2947
- var import_picocolors10 = __toESM(require("picocolors"), 1);
2948
- var addCommand = (0, import_citty11.defineCommand)({
3760
+ var import_picocolors13 = __toESM(require("picocolors"), 1);
3761
+ var addCommand = (0, import_citty14.defineCommand)({
2949
3762
  meta: { name: "add", description: "Install a plugin package" },
2950
3763
  args: {
2951
3764
  package: {
@@ -2963,23 +3776,23 @@ var addCommand = (0, import_citty11.defineCommand)({
2963
3776
  spinner5.stop();
2964
3777
  if (loaded) {
2965
3778
  console.log(
2966
- `${import_picocolors10.default.green("\u2713")} Plugin ${import_picocolors10.default.bold(loaded.name)} installed (prefix: ${import_picocolors10.default.cyan(loaded.prefix)})`
3779
+ `${import_picocolors13.default.green("\u2713")} Plugin ${import_picocolors13.default.bold(loaded.name)} installed (prefix: ${import_picocolors13.default.cyan(loaded.prefix)})`
2967
3780
  );
2968
3781
  } else {
2969
3782
  console.log(
2970
- `${import_picocolors10.default.yellow("\u26A0")} Package ${import_picocolors10.default.bold(pkg)} installed but does not export a valid mcpman plugin.`
3783
+ `${import_picocolors13.default.yellow("\u26A0")} Package ${import_picocolors13.default.bold(pkg)} installed but does not export a valid mcpman plugin.`
2971
3784
  );
2972
3785
  }
2973
3786
  } catch (err) {
2974
3787
  spinner5.stop();
2975
3788
  console.error(
2976
- `${import_picocolors10.default.red("\u2717")} Failed to install plugin: ${err instanceof Error ? err.message : String(err)}`
3789
+ `${import_picocolors13.default.red("\u2717")} Failed to install plugin: ${err instanceof Error ? err.message : String(err)}`
2977
3790
  );
2978
3791
  process.exit(1);
2979
3792
  }
2980
3793
  }
2981
3794
  });
2982
- var removeCommand = (0, import_citty11.defineCommand)({
3795
+ var removeCommand = (0, import_citty14.defineCommand)({
2983
3796
  meta: { name: "remove", description: "Uninstall a plugin package" },
2984
3797
  args: {
2985
3798
  package: {
@@ -2992,46 +3805,46 @@ var removeCommand = (0, import_citty11.defineCommand)({
2992
3805
  const pkg = args.package;
2993
3806
  const installed = listPluginPackages();
2994
3807
  if (!installed.includes(pkg)) {
2995
- console.log(import_picocolors10.default.dim(`Plugin "${pkg}" is not installed.`));
3808
+ console.log(import_picocolors13.default.dim(`Plugin "${pkg}" is not installed.`));
2996
3809
  return;
2997
3810
  }
2998
3811
  try {
2999
3812
  removePluginPackage(pkg);
3000
- console.log(`${import_picocolors10.default.green("\u2713")} Plugin ${import_picocolors10.default.bold(pkg)} removed.`);
3813
+ console.log(`${import_picocolors13.default.green("\u2713")} Plugin ${import_picocolors13.default.bold(pkg)} removed.`);
3001
3814
  } catch (err) {
3002
3815
  console.error(
3003
- `${import_picocolors10.default.red("\u2717")} Failed to remove plugin: ${err instanceof Error ? err.message : String(err)}`
3816
+ `${import_picocolors13.default.red("\u2717")} Failed to remove plugin: ${err instanceof Error ? err.message : String(err)}`
3004
3817
  );
3005
3818
  process.exit(1);
3006
3819
  }
3007
3820
  }
3008
3821
  });
3009
- var listCommand2 = (0, import_citty11.defineCommand)({
3822
+ var listCommand2 = (0, import_citty14.defineCommand)({
3010
3823
  meta: { name: "list", description: "List installed plugins" },
3011
3824
  run() {
3012
3825
  const packages = listPluginPackages();
3013
3826
  if (packages.length === 0) {
3014
- console.log(import_picocolors10.default.dim("No plugins installed. Use `mcpman plugin add <package>`."));
3827
+ console.log(import_picocolors13.default.dim("No plugins installed. Use `mcpman plugin add <package>`."));
3015
3828
  return;
3016
3829
  }
3017
3830
  console.log("");
3018
- console.log(import_picocolors10.default.bold("Installed plugins:"));
3831
+ console.log(import_picocolors13.default.bold("Installed plugins:"));
3019
3832
  console.log("");
3020
3833
  for (const pkg of packages) {
3021
3834
  const loaded = loadPlugin(pkg);
3022
3835
  if (loaded) {
3023
3836
  console.log(
3024
- ` ${import_picocolors10.default.green("\u25CF")} ${import_picocolors10.default.bold(loaded.name)} prefix: ${import_picocolors10.default.cyan(loaded.prefix)} pkg: ${import_picocolors10.default.dim(pkg)}`
3837
+ ` ${import_picocolors13.default.green("\u25CF")} ${import_picocolors13.default.bold(loaded.name)} prefix: ${import_picocolors13.default.cyan(loaded.prefix)} pkg: ${import_picocolors13.default.dim(pkg)}`
3025
3838
  );
3026
3839
  } else {
3027
- console.log(` ${import_picocolors10.default.yellow("\u25CF")} ${import_picocolors10.default.dim(pkg)} ${import_picocolors10.default.yellow("(failed to load)")}`);
3840
+ console.log(` ${import_picocolors13.default.yellow("\u25CF")} ${import_picocolors13.default.dim(pkg)} ${import_picocolors13.default.yellow("(failed to load)")}`);
3028
3841
  }
3029
3842
  }
3030
3843
  console.log("");
3031
- console.log(import_picocolors10.default.dim(` ${packages.length} plugin${packages.length !== 1 ? "s" : ""} installed`));
3844
+ console.log(import_picocolors13.default.dim(` ${packages.length} plugin${packages.length !== 1 ? "s" : ""} installed`));
3032
3845
  }
3033
3846
  });
3034
- var plugin_default = (0, import_citty11.defineCommand)({
3847
+ var plugin_default = (0, import_citty14.defineCommand)({
3035
3848
  meta: {
3036
3849
  name: "plugin",
3037
3850
  description: "Manage mcpman plugins for custom registries"
@@ -3045,24 +3858,26 @@ var plugin_default = (0, import_citty11.defineCommand)({
3045
3858
 
3046
3859
  // src/commands/profiles.ts
3047
3860
  init_cjs_shims();
3048
- var import_citty12 = require("citty");
3049
- var import_picocolors11 = __toESM(require("picocolors"), 1);
3861
+ var import_citty15 = require("citty");
3862
+ var import_picocolors14 = __toESM(require("picocolors"), 1);
3863
+ init_lockfile();
3050
3864
 
3051
3865
  // src/core/profile-service.ts
3052
3866
  init_cjs_shims();
3053
- var import_node_fs10 = __toESM(require("fs"), 1);
3054
- var import_node_path11 = __toESM(require("path"), 1);
3867
+ var import_node_fs13 = __toESM(require("fs"), 1);
3868
+ var import_node_path16 = __toESM(require("path"), 1);
3055
3869
  init_paths();
3870
+ init_lockfile();
3056
3871
  function ensureDir(dir = getProfilesDir()) {
3057
- import_node_fs10.default.mkdirSync(dir, { recursive: true });
3872
+ import_node_fs13.default.mkdirSync(dir, { recursive: true });
3058
3873
  }
3059
3874
  function profilePath(name, dir = getProfilesDir()) {
3060
- return import_node_path11.default.join(dir, `${name}.json`);
3875
+ return import_node_path16.default.join(dir, `${name}.json`);
3061
3876
  }
3062
3877
  function createProfile(name, description = "", dir = getProfilesDir()) {
3063
3878
  ensureDir(dir);
3064
3879
  const filePath = profilePath(name, dir);
3065
- if (import_node_fs10.default.existsSync(filePath)) {
3880
+ if (import_node_fs13.default.existsSync(filePath)) {
3066
3881
  throw new Error(`Profile '${name}' already exists. Delete it first or use a different name.`);
3067
3882
  }
3068
3883
  const lockfile = readLockfile();
@@ -3072,16 +3887,16 @@ function createProfile(name, description = "", dir = getProfilesDir()) {
3072
3887
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3073
3888
  servers: lockfile.servers
3074
3889
  };
3075
- import_node_fs10.default.writeFileSync(filePath, JSON.stringify(profile, null, 2), "utf-8");
3890
+ import_node_fs13.default.writeFileSync(filePath, JSON.stringify(profile, null, 2), "utf-8");
3076
3891
  return profile;
3077
3892
  }
3078
3893
  function listProfiles(dir = getProfilesDir()) {
3079
3894
  ensureDir(dir);
3080
- const files = import_node_fs10.default.readdirSync(dir).filter((f) => f.endsWith(".json"));
3895
+ const files = import_node_fs13.default.readdirSync(dir).filter((f) => f.endsWith(".json"));
3081
3896
  const profiles = [];
3082
3897
  for (const file of files) {
3083
3898
  try {
3084
- const raw = import_node_fs10.default.readFileSync(import_node_path11.default.join(dir, file), "utf-8");
3899
+ const raw = import_node_fs13.default.readFileSync(import_node_path16.default.join(dir, file), "utf-8");
3085
3900
  const data = JSON.parse(raw);
3086
3901
  profiles.push(data);
3087
3902
  } catch {
@@ -3091,9 +3906,9 @@ function listProfiles(dir = getProfilesDir()) {
3091
3906
  }
3092
3907
  function loadProfile(name, dir = getProfilesDir()) {
3093
3908
  const filePath = profilePath(name, dir);
3094
- if (!import_node_fs10.default.existsSync(filePath)) return null;
3909
+ if (!import_node_fs13.default.existsSync(filePath)) return null;
3095
3910
  try {
3096
- const raw = import_node_fs10.default.readFileSync(filePath, "utf-8");
3911
+ const raw = import_node_fs13.default.readFileSync(filePath, "utf-8");
3097
3912
  return JSON.parse(raw);
3098
3913
  } catch {
3099
3914
  return null;
@@ -3101,13 +3916,13 @@ function loadProfile(name, dir = getProfilesDir()) {
3101
3916
  }
3102
3917
  function deleteProfile(name, dir = getProfilesDir()) {
3103
3918
  const filePath = profilePath(name, dir);
3104
- if (!import_node_fs10.default.existsSync(filePath)) return false;
3105
- import_node_fs10.default.unlinkSync(filePath);
3919
+ if (!import_node_fs13.default.existsSync(filePath)) return false;
3920
+ import_node_fs13.default.unlinkSync(filePath);
3106
3921
  return true;
3107
3922
  }
3108
3923
 
3109
3924
  // src/commands/profiles.ts
3110
- var profiles_default = (0, import_citty12.defineCommand)({
3925
+ var profiles_default = (0, import_citty15.defineCommand)({
3111
3926
  meta: {
3112
3927
  name: "profiles",
3113
3928
  description: "Manage named server configuration profiles"
@@ -3136,16 +3951,16 @@ var profiles_default = (0, import_citty12.defineCommand)({
3136
3951
  case "create": {
3137
3952
  if (!name) {
3138
3953
  console.error(
3139
- import_picocolors11.default.red(" Error: Profile name required. Usage: mcpman profiles create <name>")
3954
+ import_picocolors14.default.red(" Error: Profile name required. Usage: mcpman profiles create <name>")
3140
3955
  );
3141
3956
  process.exit(1);
3142
3957
  }
3143
3958
  try {
3144
3959
  const profile = createProfile(name, args.description ?? "");
3145
3960
  const count = Object.keys(profile.servers).length;
3146
- console.log(import_picocolors11.default.green(` \u2713 Profile '${name}' created with ${count} server(s).`));
3961
+ console.log(import_picocolors14.default.green(` \u2713 Profile '${name}' created with ${count} server(s).`));
3147
3962
  } catch (err) {
3148
- console.error(import_picocolors11.default.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3963
+ console.error(import_picocolors14.default.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3149
3964
  process.exit(1);
3150
3965
  }
3151
3966
  break;
@@ -3153,38 +3968,38 @@ var profiles_default = (0, import_citty12.defineCommand)({
3153
3968
  case "switch": {
3154
3969
  if (!name) {
3155
3970
  console.error(
3156
- import_picocolors11.default.red(" Error: Profile name required. Usage: mcpman profiles switch <name>")
3971
+ import_picocolors14.default.red(" Error: Profile name required. Usage: mcpman profiles switch <name>")
3157
3972
  );
3158
3973
  process.exit(1);
3159
3974
  }
3160
3975
  const profile = loadProfile(name);
3161
3976
  if (!profile) {
3162
- console.error(import_picocolors11.default.red(` Error: Profile '${name}' not found.`));
3977
+ console.error(import_picocolors14.default.red(` Error: Profile '${name}' not found.`));
3163
3978
  process.exit(1);
3164
3979
  }
3165
3980
  const lockData = { lockfileVersion: 1, servers: profile.servers };
3166
3981
  writeLockfile(lockData);
3167
3982
  const count = Object.keys(profile.servers).length;
3168
- console.log(import_picocolors11.default.green(` \u2713 Switched to profile '${name}' (${count} servers).`));
3169
- console.log(import_picocolors11.default.dim(" Run mcpman sync to apply to all clients."));
3983
+ console.log(import_picocolors14.default.green(` \u2713 Switched to profile '${name}' (${count} servers).`));
3984
+ console.log(import_picocolors14.default.dim(" Run mcpman sync to apply to all clients."));
3170
3985
  break;
3171
3986
  }
3172
3987
  case "list": {
3173
3988
  const profiles = listProfiles();
3174
3989
  if (profiles.length === 0) {
3175
3990
  console.log(
3176
- import_picocolors11.default.dim(" No profiles saved. Create one with: mcpman profiles create <name>")
3991
+ import_picocolors14.default.dim(" No profiles saved. Create one with: mcpman profiles create <name>")
3177
3992
  );
3178
3993
  return;
3179
3994
  }
3180
- console.log(import_picocolors11.default.bold(`
3995
+ console.log(import_picocolors14.default.bold(`
3181
3996
  Profiles (${profiles.length})
3182
3997
  `));
3183
3998
  for (const p13 of profiles) {
3184
3999
  const count = Object.keys(p13.servers).length;
3185
- const desc = p13.description ? import_picocolors11.default.dim(` \u2014 ${p13.description}`) : "";
4000
+ const desc = p13.description ? import_picocolors14.default.dim(` \u2014 ${p13.description}`) : "";
3186
4001
  console.log(
3187
- ` ${import_picocolors11.default.cyan("\u25CF")} ${import_picocolors11.default.bold(p13.name)} ${import_picocolors11.default.dim(`${count} server(s)`)}${desc}`
4002
+ ` ${import_picocolors14.default.cyan("\u25CF")} ${import_picocolors14.default.bold(p13.name)} ${import_picocolors14.default.dim(`${count} server(s)`)}${desc}`
3188
4003
  );
3189
4004
  }
3190
4005
  console.log();
@@ -3193,33 +4008,203 @@ var profiles_default = (0, import_citty12.defineCommand)({
3193
4008
  case "delete": {
3194
4009
  if (!name) {
3195
4010
  console.error(
3196
- import_picocolors11.default.red(" Error: Profile name required. Usage: mcpman profiles delete <name>")
4011
+ import_picocolors14.default.red(" Error: Profile name required. Usage: mcpman profiles delete <name>")
3197
4012
  );
3198
4013
  process.exit(1);
3199
4014
  }
3200
4015
  const deleted = deleteProfile(name);
3201
4016
  if (deleted) {
3202
- console.log(import_picocolors11.default.green(` \u2713 Profile '${name}' deleted.`));
4017
+ console.log(import_picocolors14.default.green(` \u2713 Profile '${name}' deleted.`));
3203
4018
  } else {
3204
- console.error(import_picocolors11.default.red(` Error: Profile '${name}' not found.`));
4019
+ console.error(import_picocolors14.default.red(` Error: Profile '${name}' not found.`));
3205
4020
  process.exit(1);
3206
4021
  }
3207
4022
  break;
3208
4023
  }
3209
4024
  default:
3210
4025
  console.error(
3211
- import_picocolors11.default.red(` Unknown action '${action}'. Use: create, switch, list, or delete.`)
4026
+ import_picocolors14.default.red(` Unknown action '${action}'. Use: create, switch, list, or delete.`)
3212
4027
  );
3213
4028
  process.exit(1);
3214
4029
  }
3215
4030
  }
3216
4031
  });
3217
4032
 
4033
+ // src/commands/registry.ts
4034
+ init_cjs_shims();
4035
+ var import_citty16 = require("citty");
4036
+ var import_picocolors15 = __toESM(require("picocolors"), 1);
4037
+
4038
+ // src/core/registry-manager.ts
4039
+ init_cjs_shims();
4040
+ var BUILTIN_REGISTRIES = [
4041
+ { name: "npm", url: "https://registry.npmjs.org", builtin: true },
4042
+ { name: "smithery", url: "https://registry.smithery.ai", builtin: true }
4043
+ ];
4044
+ function validateUrl(url) {
4045
+ try {
4046
+ new URL(url);
4047
+ } catch {
4048
+ throw new Error(
4049
+ `Invalid URL: "${url}". Must be a valid URL (e.g. https://registry.example.com)`
4050
+ );
4051
+ }
4052
+ }
4053
+ function readCustomRegistries(configPath) {
4054
+ const config = readConfig(configPath);
4055
+ const raw = config.registries;
4056
+ if (!Array.isArray(raw)) return [];
4057
+ return raw.filter((r) => !r.builtin);
4058
+ }
4059
+ function writeCustomRegistries(entries, configPath) {
4060
+ const config = readConfig(configPath);
4061
+ config.registries = entries;
4062
+ writeConfig(config, configPath);
4063
+ }
4064
+ function getRegistries(configPath) {
4065
+ const custom = readCustomRegistries(configPath);
4066
+ return [...BUILTIN_REGISTRIES, ...custom];
4067
+ }
4068
+ function addRegistry(name, url, configPath) {
4069
+ validateUrl(url);
4070
+ const all = getRegistries(configPath);
4071
+ if (all.some((r) => r.name === name)) {
4072
+ throw new Error(`Registry '${name}' already exists. Use a different name or remove it first.`);
4073
+ }
4074
+ const custom = readCustomRegistries(configPath);
4075
+ custom.push({ name, url, builtin: false });
4076
+ writeCustomRegistries(custom, configPath);
4077
+ }
4078
+ function removeRegistry(name, configPath) {
4079
+ if (BUILTIN_REGISTRIES.some((r) => r.name === name)) {
4080
+ throw new Error(`Cannot remove built-in registry '${name}'.`);
4081
+ }
4082
+ const custom = readCustomRegistries(configPath);
4083
+ const idx = custom.findIndex((r) => r.name === name);
4084
+ if (idx === -1) {
4085
+ throw new Error(`Registry '${name}' not found.`);
4086
+ }
4087
+ custom.splice(idx, 1);
4088
+ writeCustomRegistries(custom, configPath);
4089
+ }
4090
+ function setDefaultRegistry(name, configPath) {
4091
+ const all = getRegistries(configPath);
4092
+ if (!all.some((r) => r.name === name)) {
4093
+ throw new Error(
4094
+ `Registry '${name}' not found. Add it first with: mcpman registry add ${name} <url>`
4095
+ );
4096
+ }
4097
+ const config = readConfig(configPath);
4098
+ config.defaultRegistry = name;
4099
+ writeConfig(config, configPath);
4100
+ }
4101
+ function getDefaultRegistry(configPath) {
4102
+ const config = readConfig(configPath);
4103
+ return String(config.defaultRegistry ?? "npm");
4104
+ }
4105
+
4106
+ // src/commands/registry.ts
4107
+ var registry_default = (0, import_citty16.defineCommand)({
4108
+ meta: {
4109
+ name: "registry",
4110
+ description: "Manage custom registry URLs"
4111
+ },
4112
+ args: {
4113
+ action: {
4114
+ type: "positional",
4115
+ description: "Action: list, add, remove, set-default",
4116
+ required: true
4117
+ },
4118
+ name: {
4119
+ type: "positional",
4120
+ description: "Registry name (for add/remove/set-default)",
4121
+ required: false
4122
+ },
4123
+ url: {
4124
+ type: "positional",
4125
+ description: "Registry URL (for add)",
4126
+ required: false
4127
+ }
4128
+ },
4129
+ async run({ args }) {
4130
+ const action = args.action.toLowerCase();
4131
+ const name = args.name;
4132
+ const url = args.url;
4133
+ switch (action) {
4134
+ case "list": {
4135
+ const registries = getRegistries();
4136
+ const defaultName = getDefaultRegistry();
4137
+ console.log(import_picocolors15.default.bold("\n Registries\n"));
4138
+ for (const r of registries) {
4139
+ const isDefault = r.name === defaultName;
4140
+ const defaultTag = isDefault ? import_picocolors15.default.green(" (default)") : "";
4141
+ const builtinTag = r.builtin ? import_picocolors15.default.dim(" [builtin]") : "";
4142
+ console.log(
4143
+ ` ${isDefault ? import_picocolors15.default.green("\u25CF") : import_picocolors15.default.dim("\u25CB")} ${import_picocolors15.default.bold(r.name)}${defaultTag}${builtinTag}`
4144
+ );
4145
+ console.log(` ${import_picocolors15.default.dim(r.url)}`);
4146
+ }
4147
+ console.log();
4148
+ break;
4149
+ }
4150
+ case "add": {
4151
+ if (!name) {
4152
+ console.error(import_picocolors15.default.red(" Error: Usage: mcpman registry add <name> <url>"));
4153
+ process.exit(1);
4154
+ }
4155
+ if (!url) {
4156
+ console.error(import_picocolors15.default.red(" Error: Usage: mcpman registry add <name> <url>"));
4157
+ process.exit(1);
4158
+ }
4159
+ try {
4160
+ addRegistry(name, url);
4161
+ console.log(import_picocolors15.default.green(` Added registry '${name}' \u2192 ${url}`));
4162
+ } catch (err) {
4163
+ console.error(import_picocolors15.default.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
4164
+ process.exit(1);
4165
+ }
4166
+ break;
4167
+ }
4168
+ case "remove": {
4169
+ if (!name) {
4170
+ console.error(import_picocolors15.default.red(" Error: Usage: mcpman registry remove <name>"));
4171
+ process.exit(1);
4172
+ }
4173
+ try {
4174
+ removeRegistry(name);
4175
+ console.log(import_picocolors15.default.green(` Removed registry '${name}'.`));
4176
+ } catch (err) {
4177
+ console.error(import_picocolors15.default.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
4178
+ process.exit(1);
4179
+ }
4180
+ break;
4181
+ }
4182
+ case "set-default": {
4183
+ if (!name) {
4184
+ console.error(import_picocolors15.default.red(" Error: Usage: mcpman registry set-default <name>"));
4185
+ process.exit(1);
4186
+ }
4187
+ try {
4188
+ setDefaultRegistry(name);
4189
+ console.log(import_picocolors15.default.green(` Default registry set to '${name}'.`));
4190
+ } catch (err) {
4191
+ console.error(import_picocolors15.default.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
4192
+ process.exit(1);
4193
+ }
4194
+ break;
4195
+ }
4196
+ default:
4197
+ console.error(import_picocolors15.default.red(` Unknown action '${action}'. Use: list, add, remove, set-default.`));
4198
+ process.exit(1);
4199
+ }
4200
+ }
4201
+ });
4202
+
3218
4203
  // src/commands/remove.ts
3219
4204
  init_cjs_shims();
3220
4205
  var p9 = __toESM(require("@clack/prompts"), 1);
3221
- var import_citty13 = require("citty");
3222
- var import_picocolors12 = __toESM(require("picocolors"), 1);
4206
+ var import_citty17 = require("citty");
4207
+ var import_picocolors16 = __toESM(require("picocolors"), 1);
3223
4208
  init_client_detector();
3224
4209
  var CLIENT_DISPLAY2 = {
3225
4210
  "claude-desktop": "Claude",
@@ -3230,7 +4215,7 @@ var CLIENT_DISPLAY2 = {
3230
4215
  function clientDisplayName(type) {
3231
4216
  return CLIENT_DISPLAY2[type] ?? type;
3232
4217
  }
3233
- var remove_default = (0, import_citty13.defineCommand)({
4218
+ var remove_default = (0, import_citty17.defineCommand)({
3234
4219
  meta: {
3235
4220
  name: "remove",
3236
4221
  description: "Remove an MCP server from one or more AI clients"
@@ -3257,7 +4242,7 @@ var remove_default = (0, import_citty13.defineCommand)({
3257
4242
  }
3258
4243
  },
3259
4244
  async run({ args }) {
3260
- p9.intro(import_picocolors12.default.bold("mcpman remove"));
4245
+ p9.intro(import_picocolors16.default.bold("mcpman remove"));
3261
4246
  const serverName = args.server;
3262
4247
  const servers = await getInstalledServers();
3263
4248
  const match = servers.find((s) => s.name === serverName);
@@ -3267,7 +4252,7 @@ var remove_default = (0, import_citty13.defineCommand)({
3267
4252
  (s) => s.name.includes(serverName) || serverName.includes(s.name)
3268
4253
  );
3269
4254
  if (similar.length > 0) {
3270
- p9.log.info(`Did you mean: ${similar.map((s) => import_picocolors12.default.cyan(s.name)).join(", ")}?`);
4255
+ p9.log.info(`Did you mean: ${similar.map((s) => import_picocolors16.default.cyan(s.name)).join(", ")}?`);
3271
4256
  }
3272
4257
  p9.outro("Nothing to remove.");
3273
4258
  return;
@@ -3302,7 +4287,7 @@ var remove_default = (0, import_citty13.defineCommand)({
3302
4287
  if (!args.yes) {
3303
4288
  const clientNames = targetClients.map(clientDisplayName).join(", ");
3304
4289
  const confirmed = await p9.confirm({
3305
- message: `Remove ${import_picocolors12.default.cyan(serverName)} from ${import_picocolors12.default.yellow(clientNames)}?`
4290
+ message: `Remove ${import_picocolors16.default.cyan(serverName)} from ${import_picocolors16.default.yellow(clientNames)}?`
3306
4291
  });
3307
4292
  if (p9.isCancel(confirmed) || !confirmed) {
3308
4293
  p9.outro("Cancelled.");
@@ -3327,20 +4312,21 @@ var remove_default = (0, import_citty13.defineCommand)({
3327
4312
  }
3328
4313
  if (errors.length > 0) {
3329
4314
  for (const e of errors) p9.log.error(e);
3330
- p9.outro(import_picocolors12.default.red("Completed with errors."));
4315
+ p9.outro(import_picocolors16.default.red("Completed with errors."));
3331
4316
  process.exit(1);
3332
4317
  }
3333
- p9.outro(import_picocolors12.default.green(`Removed "${serverName}" successfully.`));
4318
+ p9.outro(import_picocolors16.default.green(`Removed "${serverName}" successfully.`));
3334
4319
  }
3335
4320
  });
3336
4321
 
3337
4322
  // src/commands/run.ts
3338
4323
  init_cjs_shims();
3339
4324
  var import_node_child_process5 = require("child_process");
3340
- var import_citty14 = require("citty");
3341
- var import_picocolors13 = __toESM(require("picocolors"), 1);
4325
+ var import_citty18 = require("citty");
4326
+ var import_picocolors17 = __toESM(require("picocolors"), 1);
4327
+ init_lockfile();
3342
4328
  init_vault_service();
3343
- var run_default = (0, import_citty14.defineCommand)({
4329
+ var run_default = (0, import_citty18.defineCommand)({
3344
4330
  meta: {
3345
4331
  name: "run",
3346
4332
  description: "Run an installed MCP server with vault secrets injected"
@@ -3362,8 +4348,8 @@ var run_default = (0, import_citty14.defineCommand)({
3362
4348
  const lockfile = readLockfile();
3363
4349
  const entry = lockfile.servers[serverName];
3364
4350
  if (!entry) {
3365
- console.error(import_picocolors13.default.red(` Error: Server '${serverName}' is not installed.`));
3366
- console.error(import_picocolors13.default.dim(` Run ${import_picocolors13.default.cyan("mcpman install <server>")} to install it first.`));
4351
+ console.error(import_picocolors17.default.red(` Error: Server '${serverName}' is not installed.`));
4352
+ console.error(import_picocolors17.default.dim(` Run ${import_picocolors17.default.cyan("mcpman install <server>")} to install it first.`));
3367
4353
  process.exit(1);
3368
4354
  }
3369
4355
  const lockfileEnv = parseEnvFlags(entry.envVars);
@@ -3375,7 +4361,7 @@ var run_default = (0, import_citty14.defineCommand)({
3375
4361
  ...vaultEnv,
3376
4362
  ...cliEnv
3377
4363
  };
3378
- console.log(import_picocolors13.default.dim(` Running ${import_picocolors13.default.cyan(serverName)}...`));
4364
+ console.log(import_picocolors17.default.dim(` Running ${import_picocolors17.default.cyan(serverName)}...`));
3379
4365
  const child = (0, import_node_child_process5.spawn)(entry.command, entry.args, {
3380
4366
  env: finalEnv,
3381
4367
  stdio: "inherit"
@@ -3393,7 +4379,7 @@ var run_default = (0, import_citty14.defineCommand)({
3393
4379
  resolve();
3394
4380
  });
3395
4381
  child.on("error", (err) => {
3396
- console.error(import_picocolors13.default.red(` Failed to start '${serverName}': ${err.message}`));
4382
+ console.error(import_picocolors17.default.red(` Failed to start '${serverName}': ${err.message}`));
3397
4383
  process.exit(1);
3398
4384
  resolve();
3399
4385
  });
@@ -3409,16 +4395,16 @@ async function loadVaultSecrets2(serverName) {
3409
4395
  const password2 = await getMasterPassword();
3410
4396
  return getSecretsForServer(serverName, password2);
3411
4397
  } catch {
3412
- console.warn(import_picocolors13.default.yellow(" Warning: Could not load vault secrets, continuing without them."));
4398
+ console.warn(import_picocolors17.default.yellow(" Warning: Could not load vault secrets, continuing without them."));
3413
4399
  return {};
3414
4400
  }
3415
4401
  }
3416
4402
 
3417
4403
  // src/commands/search.ts
3418
4404
  init_cjs_shims();
3419
- var import_citty15 = require("citty");
4405
+ var import_citty19 = require("citty");
3420
4406
  var import_nanospinner4 = require("nanospinner");
3421
- var import_picocolors14 = __toESM(require("picocolors"), 1);
4407
+ var import_picocolors18 = __toESM(require("picocolors"), 1);
3422
4408
 
3423
4409
  // src/core/registry-search.ts
3424
4410
  init_cjs_shims();
@@ -3492,10 +4478,10 @@ function pad2(s, width) {
3492
4478
  function highlightMatch(name, query) {
3493
4479
  const idx = name.toLowerCase().indexOf(query.toLowerCase());
3494
4480
  if (idx === -1) return name;
3495
- return name.slice(0, idx) + import_picocolors14.default.yellow(name.slice(idx, idx + query.length)) + name.slice(idx + query.length);
4481
+ return name.slice(0, idx) + import_picocolors18.default.yellow(name.slice(idx, idx + query.length)) + name.slice(idx + query.length);
3496
4482
  }
3497
4483
  function formatDownloads(n) {
3498
- if (!n) return import_picocolors14.default.dim("\u2014");
4484
+ if (!n) return import_picocolors18.default.dim("\u2014");
3499
4485
  if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
3500
4486
  if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
3501
4487
  return String(n);
@@ -3506,9 +4492,9 @@ function printNpmResults(results, query) {
3506
4492
  const dlWidth = 9;
3507
4493
  const descMax = 50;
3508
4494
  const header = ` ${pad2("NAME", nameWidth)} ${pad2("VERSION", verWidth)} ${pad2("DOWNLOADS", dlWidth)} DESCRIPTION`;
3509
- console.log(import_picocolors14.default.dim(header));
4495
+ console.log(import_picocolors18.default.dim(header));
3510
4496
  console.log(
3511
- import_picocolors14.default.dim(
4497
+ import_picocolors18.default.dim(
3512
4498
  ` ${"-".repeat(nameWidth)} ${"-".repeat(verWidth)} ${"-".repeat(dlWidth)} ${"-".repeat(descMax)}`
3513
4499
  )
3514
4500
  );
@@ -3516,8 +4502,8 @@ function printNpmResults(results, query) {
3516
4502
  const name = highlightMatch(pad2(r.name, nameWidth), query);
3517
4503
  const ver = pad2(r.version, verWidth);
3518
4504
  const dl = pad2(formatDownloads(r.downloads), dlWidth);
3519
- const desc = truncate2(r.description || import_picocolors14.default.dim("(no description)"), descMax);
3520
- console.log(` ${name} ${import_picocolors14.default.dim(ver)} ${dl} ${desc}`);
4505
+ const desc = truncate2(r.description || import_picocolors18.default.dim("(no description)"), descMax);
4506
+ console.log(` ${name} ${import_picocolors18.default.dim(ver)} ${dl} ${desc}`);
3521
4507
  }
3522
4508
  }
3523
4509
  function printSmitheryResults(results, query) {
@@ -3525,19 +4511,19 @@ function printSmitheryResults(results, query) {
3525
4511
  const usesWidth = 8;
3526
4512
  const descMax = 50;
3527
4513
  const header = ` ${pad2("NAME", nameWidth)} ${pad2("USES", usesWidth)} DESCRIPTION`;
3528
- console.log(import_picocolors14.default.dim(header));
4514
+ console.log(import_picocolors18.default.dim(header));
3529
4515
  console.log(
3530
- import_picocolors14.default.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(usesWidth)} ${"-".repeat(descMax)}`)
4516
+ import_picocolors18.default.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(usesWidth)} ${"-".repeat(descMax)}`)
3531
4517
  );
3532
4518
  for (const r of results) {
3533
4519
  const name = highlightMatch(pad2(r.name, nameWidth), query);
3534
4520
  const uses = pad2(formatDownloads(r.useCount), usesWidth);
3535
- const badge = r.verified ? import_picocolors14.default.green(" \u2713") : "";
3536
- const desc = truncate2(r.description || import_picocolors14.default.dim("(no description)"), descMax);
4521
+ const badge = r.verified ? import_picocolors18.default.green(" \u2713") : "";
4522
+ const desc = truncate2(r.description || import_picocolors18.default.dim("(no description)"), descMax);
3537
4523
  console.log(` ${name}${badge} ${uses} ${desc}`);
3538
4524
  }
3539
4525
  }
3540
- var search_default = (0, import_citty15.defineCommand)({
4526
+ var search_default = (0, import_citty19.defineCommand)({
3541
4527
  meta: {
3542
4528
  name: "search",
3543
4529
  description: "Search for MCP servers on npm or Smithery registry"
@@ -3569,7 +4555,7 @@ var search_default = (0, import_citty15.defineCommand)({
3569
4555
  const registry = args.registry.toLowerCase();
3570
4556
  const limit = Math.min(Math.max(1, Number.parseInt(args.limit, 10) || 20), 100);
3571
4557
  if (registry !== "npm" && registry !== "smithery") {
3572
- console.error(import_picocolors14.default.red(` Unknown registry "${registry}". Use "npm" or "smithery".`));
4558
+ console.error(import_picocolors18.default.red(` Unknown registry "${registry}". Use "npm" or "smithery".`));
3573
4559
  process.exit(1);
3574
4560
  }
3575
4561
  const spinner5 = (0, import_nanospinner4.createSpinner)(`Searching ${registry} for "${query}"...`).start();
@@ -3577,20 +4563,20 @@ var search_default = (0, import_citty15.defineCommand)({
3577
4563
  const results2 = await searchNpm(query, limit);
3578
4564
  spinner5.stop();
3579
4565
  if (results2.length === 0) {
3580
- console.log(import_picocolors14.default.dim(`
4566
+ console.log(import_picocolors18.default.dim(`
3581
4567
  No results found for "${query}" on npm.
3582
4568
  `));
3583
4569
  return;
3584
4570
  }
3585
4571
  console.log(
3586
- import_picocolors14.default.bold(
4572
+ import_picocolors18.default.bold(
3587
4573
  `
3588
4574
  mcpman search \u2014 npm (${results2.length} result${results2.length !== 1 ? "s" : ""})
3589
4575
  `
3590
4576
  )
3591
4577
  );
3592
4578
  printNpmResults(results2, query);
3593
- console.log(import_picocolors14.default.dim("\n Install with: mcpman install <name>\n"));
4579
+ console.log(import_picocolors18.default.dim("\n Install with: mcpman install <name>\n"));
3594
4580
  if (args.all) {
3595
4581
  await printPluginResults(query, limit);
3596
4582
  }
@@ -3599,20 +4585,20 @@ var search_default = (0, import_citty15.defineCommand)({
3599
4585
  const results = await searchSmithery(query, limit);
3600
4586
  spinner5.stop();
3601
4587
  if (results.length === 0) {
3602
- console.log(import_picocolors14.default.dim(`
4588
+ console.log(import_picocolors18.default.dim(`
3603
4589
  No results found for "${query}" on Smithery.
3604
4590
  `));
3605
4591
  return;
3606
4592
  }
3607
4593
  console.log(
3608
- import_picocolors14.default.bold(
4594
+ import_picocolors18.default.bold(
3609
4595
  `
3610
4596
  mcpman search \u2014 Smithery (${results.length} result${results.length !== 1 ? "s" : ""})
3611
4597
  `
3612
4598
  )
3613
4599
  );
3614
4600
  printSmitheryResults(results, query);
3615
- console.log(import_picocolors14.default.dim("\n Install with: mcpman install <name>\n"));
4601
+ console.log(import_picocolors18.default.dim("\n Install with: mcpman install <name>\n"));
3616
4602
  if (args.all) {
3617
4603
  await printPluginResults(query, limit);
3618
4604
  }
@@ -3622,7 +4608,7 @@ async function printPluginResults(query, limit) {
3622
4608
  const pluginResults = await searchPlugins(query, limit);
3623
4609
  if (pluginResults.length === 0) return;
3624
4610
  console.log(
3625
- import_picocolors14.default.bold(
4611
+ import_picocolors18.default.bold(
3626
4612
  `
3627
4613
  Plugins (${pluginResults.length} result${pluginResults.length !== 1 ? "s" : ""})
3628
4614
  `
@@ -3632,23 +4618,23 @@ async function printPluginResults(query, limit) {
3632
4618
  const srcWidth = Math.max(6, ...pluginResults.map((r) => r.source.length));
3633
4619
  const descMax = 50;
3634
4620
  const header = ` ${pad2("NAME", nameWidth)} ${pad2("SOURCE", srcWidth)} DESCRIPTION`;
3635
- console.log(import_picocolors14.default.dim(header));
4621
+ console.log(import_picocolors18.default.dim(header));
3636
4622
  console.log(
3637
- import_picocolors14.default.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(srcWidth)} ${"-".repeat(descMax)}`)
4623
+ import_picocolors18.default.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(srcWidth)} ${"-".repeat(descMax)}`)
3638
4624
  );
3639
4625
  for (const r of pluginResults) {
3640
4626
  const name = highlightMatch(pad2(r.name, nameWidth), query);
3641
4627
  const src = pad2(r.source, srcWidth);
3642
- const desc = truncate2(r.description || import_picocolors14.default.dim("(no description)"), descMax);
3643
- console.log(` ${name} ${import_picocolors14.default.dim(src)} ${desc}`);
4628
+ const desc = truncate2(r.description || import_picocolors18.default.dim("(no description)"), descMax);
4629
+ console.log(` ${name} ${import_picocolors18.default.dim(src)} ${desc}`);
3644
4630
  }
3645
4631
  }
3646
4632
 
3647
4633
  // src/commands/secrets.ts
3648
4634
  init_cjs_shims();
3649
4635
  var p10 = __toESM(require("@clack/prompts"), 1);
3650
- var import_citty16 = require("citty");
3651
- var import_picocolors15 = __toESM(require("picocolors"), 1);
4636
+ var import_citty20 = require("citty");
4637
+ var import_picocolors19 = __toESM(require("picocolors"), 1);
3652
4638
  init_vault_service();
3653
4639
  function maskValue(value) {
3654
4640
  if (value.length <= 8) return "***";
@@ -3659,7 +4645,7 @@ function parseKeyValue(input) {
3659
4645
  if (idx <= 0) return null;
3660
4646
  return { key: input.slice(0, idx), value: input.slice(idx + 1) };
3661
4647
  }
3662
- var setCommand2 = (0, import_citty16.defineCommand)({
4648
+ var setCommand2 = (0, import_citty20.defineCommand)({
3663
4649
  meta: { name: "set", description: "Store an encrypted secret for a server" },
3664
4650
  args: {
3665
4651
  server: {
@@ -3676,10 +4662,10 @@ var setCommand2 = (0, import_citty16.defineCommand)({
3676
4662
  async run({ args }) {
3677
4663
  const parsed = parseKeyValue(args.keyvalue);
3678
4664
  if (!parsed) {
3679
- console.error(`${import_picocolors15.default.red("\u2717")} Invalid format. Expected KEY=VALUE`);
4665
+ console.error(`${import_picocolors19.default.red("\u2717")} Invalid format. Expected KEY=VALUE`);
3680
4666
  process.exit(1);
3681
4667
  }
3682
- p10.intro(import_picocolors15.default.cyan("mcpman secrets set"));
4668
+ p10.intro(import_picocolors19.default.cyan("mcpman secrets set"));
3683
4669
  const isNew = listSecrets(args.server).length === 0 || !listSecrets(args.server)[0]?.keys.includes(parsed.key);
3684
4670
  const vaultPath = (await Promise.resolve().then(() => (init_vault_service(), vault_service_exports))).getVaultPath();
3685
4671
  const vaultExists = (await import("fs")).existsSync(vaultPath);
@@ -3688,16 +4674,16 @@ var setCommand2 = (0, import_citty16.defineCommand)({
3688
4674
  spin.start("Encrypting secret...");
3689
4675
  try {
3690
4676
  setSecret(args.server, parsed.key, parsed.value, password2);
3691
- spin.stop(`${import_picocolors15.default.green("\u2713")} Stored ${import_picocolors15.default.bold(parsed.key)} for ${import_picocolors15.default.cyan(args.server)}`);
4677
+ spin.stop(`${import_picocolors19.default.green("\u2713")} Stored ${import_picocolors19.default.bold(parsed.key)} for ${import_picocolors19.default.cyan(args.server)}`);
3692
4678
  } catch (err) {
3693
- spin.stop(`${import_picocolors15.default.red("\u2717")} Failed to store secret`);
3694
- console.error(import_picocolors15.default.dim(String(err)));
4679
+ spin.stop(`${import_picocolors19.default.red("\u2717")} Failed to store secret`);
4680
+ console.error(import_picocolors19.default.dim(String(err)));
3695
4681
  process.exit(1);
3696
4682
  }
3697
- p10.outro(import_picocolors15.default.dim("Secret encrypted and saved to vault."));
4683
+ p10.outro(import_picocolors19.default.dim("Secret encrypted and saved to vault."));
3698
4684
  }
3699
4685
  });
3700
- var listCommand3 = (0, import_citty16.defineCommand)({
4686
+ var listCommand3 = (0, import_citty20.defineCommand)({
3701
4687
  meta: { name: "list", description: "List secret keys stored in the vault" },
3702
4688
  args: {
3703
4689
  server: {
@@ -3709,27 +4695,27 @@ var listCommand3 = (0, import_citty16.defineCommand)({
3709
4695
  async run({ args }) {
3710
4696
  const results = listSecrets(args.server || void 0);
3711
4697
  if (results.length === 0) {
3712
- const filter = args.server ? ` for ${import_picocolors15.default.cyan(args.server)}` : "";
3713
- console.log(import_picocolors15.default.dim(`No secrets stored${filter}.`));
4698
+ const filter = args.server ? ` for ${import_picocolors19.default.cyan(args.server)}` : "";
4699
+ console.log(import_picocolors19.default.dim(`No secrets stored${filter}.`));
3714
4700
  return;
3715
4701
  }
3716
4702
  console.log("");
3717
4703
  for (const { server, keys } of results) {
3718
- console.log(import_picocolors15.default.bold(import_picocolors15.default.cyan(server)));
4704
+ console.log(import_picocolors19.default.bold(import_picocolors19.default.cyan(server)));
3719
4705
  for (const key of keys) {
3720
- console.log(` ${import_picocolors15.default.green("\u25CF")} ${import_picocolors15.default.bold(key)} ${import_picocolors15.default.dim(maskValue("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"))}`);
4706
+ console.log(` ${import_picocolors19.default.green("\u25CF")} ${import_picocolors19.default.bold(key)} ${import_picocolors19.default.dim(maskValue("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"))}`);
3721
4707
  }
3722
4708
  console.log("");
3723
4709
  }
3724
4710
  const total = results.reduce((n, r) => n + r.keys.length, 0);
3725
4711
  console.log(
3726
- import_picocolors15.default.dim(
4712
+ import_picocolors19.default.dim(
3727
4713
  ` ${total} secret${total !== 1 ? "s" : ""} in ${results.length} server${results.length !== 1 ? "s" : ""}`
3728
4714
  )
3729
4715
  );
3730
4716
  }
3731
4717
  });
3732
- var removeCommand2 = (0, import_citty16.defineCommand)({
4718
+ var removeCommand2 = (0, import_citty20.defineCommand)({
3733
4719
  meta: { name: "remove", description: "Delete a secret from the vault" },
3734
4720
  args: {
3735
4721
  server: {
@@ -3745,7 +4731,7 @@ var removeCommand2 = (0, import_citty16.defineCommand)({
3745
4731
  },
3746
4732
  async run({ args }) {
3747
4733
  const confirmed = await p10.confirm({
3748
- message: `Remove ${import_picocolors15.default.bold(args.key)} from ${import_picocolors15.default.cyan(args.server)}?`,
4734
+ message: `Remove ${import_picocolors19.default.bold(args.key)} from ${import_picocolors19.default.cyan(args.server)}?`,
3749
4735
  initialValue: false
3750
4736
  });
3751
4737
  if (p10.isCancel(confirmed) || !confirmed) {
@@ -3754,15 +4740,15 @@ var removeCommand2 = (0, import_citty16.defineCommand)({
3754
4740
  }
3755
4741
  try {
3756
4742
  removeSecret(args.server, args.key);
3757
- console.log(`${import_picocolors15.default.green("\u2713")} Removed ${import_picocolors15.default.bold(args.key)} from ${import_picocolors15.default.cyan(args.server)}`);
4743
+ console.log(`${import_picocolors19.default.green("\u2713")} Removed ${import_picocolors19.default.bold(args.key)} from ${import_picocolors19.default.cyan(args.server)}`);
3758
4744
  } catch (err) {
3759
- console.error(`${import_picocolors15.default.red("\u2717")} Failed to remove secret`);
3760
- console.error(import_picocolors15.default.dim(String(err)));
4745
+ console.error(`${import_picocolors19.default.red("\u2717")} Failed to remove secret`);
4746
+ console.error(import_picocolors19.default.dim(String(err)));
3761
4747
  process.exit(1);
3762
4748
  }
3763
4749
  }
3764
4750
  });
3765
- var secrets_default = (0, import_citty16.defineCommand)({
4751
+ var secrets_default = (0, import_citty20.defineCommand)({
3766
4752
  meta: {
3767
4753
  name: "secrets",
3768
4754
  description: "Manage encrypted secrets for MCP servers"
@@ -3777,8 +4763,8 @@ var secrets_default = (0, import_citty16.defineCommand)({
3777
4763
  // src/commands/sync.ts
3778
4764
  init_cjs_shims();
3779
4765
  var p11 = __toESM(require("@clack/prompts"), 1);
3780
- var import_citty17 = require("citty");
3781
- var import_picocolors16 = __toESM(require("picocolors"), 1);
4766
+ var import_citty21 = require("citty");
4767
+ var import_picocolors20 = __toESM(require("picocolors"), 1);
3782
4768
 
3783
4769
  // src/core/config-diff.ts
3784
4770
  init_cjs_shims();
@@ -3845,6 +4831,9 @@ function computeDiffFromClient(sourceClient, clientConfigs, options = {}) {
3845
4831
  return actions;
3846
4832
  }
3847
4833
 
4834
+ // src/commands/sync.ts
4835
+ init_lockfile();
4836
+
3848
4837
  // src/core/sync-engine.ts
3849
4838
  init_cjs_shims();
3850
4839
  init_client_detector();
@@ -3933,7 +4922,7 @@ var CLIENT_DISPLAY3 = {
3933
4922
  vscode: "VS Code",
3934
4923
  windsurf: "Windsurf"
3935
4924
  };
3936
- var sync_default = (0, import_citty17.defineCommand)({
4925
+ var sync_default = (0, import_citty21.defineCommand)({
3937
4926
  meta: {
3938
4927
  name: "sync",
3939
4928
  description: "Sync MCP server configs across all detected AI clients"
@@ -3960,7 +4949,7 @@ var sync_default = (0, import_citty17.defineCommand)({
3960
4949
  }
3961
4950
  },
3962
4951
  async run({ args }) {
3963
- p11.intro(`${import_picocolors16.default.cyan("mcpman sync")}`);
4952
+ p11.intro(`${import_picocolors20.default.cyan("mcpman sync")}`);
3964
4953
  const sourceClient = args.source;
3965
4954
  if (sourceClient && !VALID_CLIENTS.includes(sourceClient)) {
3966
4955
  p11.log.error(
@@ -3996,20 +4985,20 @@ var sync_default = (0, import_citty17.defineCommand)({
3996
4985
  const extraCount = actions.filter((a) => a.action === "extra").length;
3997
4986
  const removeCount = actions.filter((a) => a.action === "remove").length;
3998
4987
  if (addCount === 0 && removeCount === 0 && extraCount === 0) {
3999
- p11.outro(import_picocolors16.default.green("All clients are in sync."));
4988
+ p11.outro(import_picocolors20.default.green("All clients are in sync."));
4000
4989
  process.exit(0);
4001
4990
  }
4002
4991
  const parts = [];
4003
- if (addCount > 0) parts.push(import_picocolors16.default.green(`${addCount} to add`));
4004
- if (removeCount > 0) parts.push(import_picocolors16.default.red(`${removeCount} to remove`));
4005
- if (extraCount > 0) parts.push(import_picocolors16.default.yellow(`${extraCount} extra (informational)`));
4992
+ if (addCount > 0) parts.push(import_picocolors20.default.green(`${addCount} to add`));
4993
+ if (removeCount > 0) parts.push(import_picocolors20.default.red(`${removeCount} to remove`));
4994
+ if (extraCount > 0) parts.push(import_picocolors20.default.yellow(`${extraCount} extra (informational)`));
4006
4995
  p11.log.info(parts.join(" \xB7 "));
4007
4996
  if (args["dry-run"]) {
4008
- p11.outro(import_picocolors16.default.dim("Dry run \u2014 no changes applied."));
4997
+ p11.outro(import_picocolors20.default.dim("Dry run \u2014 no changes applied."));
4009
4998
  process.exit(1);
4010
4999
  }
4011
5000
  if (addCount === 0 && removeCount === 0) {
4012
- p11.outro(import_picocolors16.default.dim("No additions needed. Extra servers left untouched."));
5001
+ p11.outro(import_picocolors20.default.dim("No additions needed. Extra servers left untouched."));
4013
5002
  process.exit(1);
4014
5003
  }
4015
5004
  if (!args.yes) {
@@ -4021,7 +5010,7 @@ var sync_default = (0, import_citty17.defineCommand)({
4021
5010
  initialValue: true
4022
5011
  });
4023
5012
  if (p11.isCancel(confirmed) || !confirmed) {
4024
- p11.outro(import_picocolors16.default.dim("Cancelled \u2014 no changes applied."));
5013
+ p11.outro(import_picocolors20.default.dim("Cancelled \u2014 no changes applied."));
4025
5014
  process.exit(0);
4026
5015
  }
4027
5016
  }
@@ -4040,7 +5029,7 @@ var sync_default = (0, import_citty17.defineCommand)({
4040
5029
  }
4041
5030
  }
4042
5031
  p11.outro(
4043
- result.failed === 0 ? import_picocolors16.default.green("Sync complete.") : import_picocolors16.default.yellow("Sync complete with errors.")
5032
+ result.failed === 0 ? import_picocolors20.default.green("Sync complete.") : import_picocolors20.default.yellow("Sync complete with errors.")
4044
5033
  );
4045
5034
  process.exit(result.failed > 0 ? 1 : 0);
4046
5035
  }
@@ -4056,8 +5045,8 @@ function printDiffTable(actions) {
4056
5045
  ...actions.map((a) => CLIENT_DISPLAY3[a.client]?.length ?? a.client.length)
4057
5046
  );
4058
5047
  const header = ` ${pad3("SERVER", nameWidth)} ${pad3("CLIENT", clientWidth)} STATUS`;
4059
- console.log(import_picocolors16.default.dim(header));
4060
- console.log(import_picocolors16.default.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientWidth)} ------`));
5048
+ console.log(import_picocolors20.default.dim(header));
5049
+ console.log(import_picocolors20.default.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientWidth)} ------`));
4061
5050
  for (const action of actions) {
4062
5051
  const clientDisplay = CLIENT_DISPLAY3[action.client] ?? action.client;
4063
5052
  const [icon, statusText] = formatAction(action.action);
@@ -4070,13 +5059,13 @@ function printDiffTable(actions) {
4070
5059
  function formatAction(action) {
4071
5060
  switch (action) {
4072
5061
  case "add":
4073
- return [import_picocolors16.default.green("+"), import_picocolors16.default.green("missing \u2014 will add")];
5062
+ return [import_picocolors20.default.green("+"), import_picocolors20.default.green("missing \u2014 will add")];
4074
5063
  case "extra":
4075
- return [import_picocolors16.default.yellow("?"), import_picocolors16.default.yellow("extra (not in lockfile)")];
5064
+ return [import_picocolors20.default.yellow("?"), import_picocolors20.default.yellow("extra (not in lockfile)")];
4076
5065
  case "remove":
4077
- return [import_picocolors16.default.red("\u2013"), import_picocolors16.default.red("extra \u2014 will remove")];
5066
+ return [import_picocolors20.default.red("\u2013"), import_picocolors20.default.red("extra \u2014 will remove")];
4078
5067
  case "ok":
4079
- return [import_picocolors16.default.dim("\xB7"), import_picocolors16.default.dim("in sync")];
5068
+ return [import_picocolors20.default.dim("\xB7"), import_picocolors20.default.dim("in sync")];
4080
5069
  }
4081
5070
  }
4082
5071
  function pad3(s, width) {
@@ -4085,8 +5074,9 @@ function pad3(s, width) {
4085
5074
 
4086
5075
  // src/commands/test-command.ts
4087
5076
  init_cjs_shims();
4088
- var import_citty18 = require("citty");
4089
- var import_picocolors17 = __toESM(require("picocolors"), 1);
5077
+ var import_citty22 = require("citty");
5078
+ var import_picocolors21 = __toESM(require("picocolors"), 1);
5079
+ init_lockfile();
4090
5080
 
4091
5081
  // src/core/mcp-tester.ts
4092
5082
  init_cjs_shims();
@@ -4186,7 +5176,7 @@ async function testMcpServer(serverName, command, args, env) {
4186
5176
 
4187
5177
  // src/commands/test-command.ts
4188
5178
  init_vault_service();
4189
- var test_command_default = (0, import_citty18.defineCommand)({
5179
+ var test_command_default = (0, import_citty22.defineCommand)({
4190
5180
  meta: {
4191
5181
  name: "test",
4192
5182
  description: "Test MCP server connectivity and capabilities"
@@ -4207,10 +5197,10 @@ var test_command_default = (0, import_citty18.defineCommand)({
4207
5197
  const lockfile = readLockfile();
4208
5198
  const serverNames = args.all ? Object.keys(lockfile.servers) : args.server ? [args.server] : [];
4209
5199
  if (serverNames.length === 0) {
4210
- console.error(import_picocolors17.default.red(" Error: Specify a server name or use --all."));
5200
+ console.error(import_picocolors21.default.red(" Error: Specify a server name or use --all."));
4211
5201
  process.exit(1);
4212
5202
  }
4213
- console.log(import_picocolors17.default.bold(`
5203
+ console.log(import_picocolors21.default.bold(`
4214
5204
  mcpman test \u2014 ${serverNames.length} server(s)
4215
5205
  `));
4216
5206
  let passed = 0;
@@ -4218,7 +5208,7 @@ var test_command_default = (0, import_citty18.defineCommand)({
4218
5208
  for (const name of serverNames) {
4219
5209
  const entry = lockfile.servers[name];
4220
5210
  if (!entry) {
4221
- console.log(` ${import_picocolors17.default.red("\u2717")} ${import_picocolors17.default.bold(name)} \u2014 not installed`);
5211
+ console.log(` ${import_picocolors21.default.red("\u2717")} ${import_picocolors21.default.bold(name)} \u2014 not installed`);
4222
5212
  failed++;
4223
5213
  continue;
4224
5214
  }
@@ -4229,27 +5219,27 @@ var test_command_default = (0, import_citty18.defineCommand)({
4229
5219
  if (result.passed) {
4230
5220
  passed++;
4231
5221
  console.log(
4232
- ` ${import_picocolors17.default.green("\u2713")} ${import_picocolors17.default.bold(name)} ${import_picocolors17.default.dim(`(${result.responseTimeMs}ms)`)}`
5222
+ ` ${import_picocolors21.default.green("\u2713")} ${import_picocolors21.default.bold(name)} ${import_picocolors21.default.dim(`(${result.responseTimeMs}ms)`)}`
4233
5223
  );
4234
5224
  if (result.tools.length > 0) {
4235
- console.log(import_picocolors17.default.dim(` Tools: ${result.tools.join(", ")}`));
5225
+ console.log(import_picocolors21.default.dim(` Tools: ${result.tools.join(", ")}`));
4236
5226
  }
4237
5227
  } else {
4238
5228
  failed++;
4239
- console.log(` ${import_picocolors17.default.red("\u2717")} ${import_picocolors17.default.bold(name)} ${import_picocolors17.default.dim(`(${result.responseTimeMs}ms)`)}`);
5229
+ console.log(` ${import_picocolors21.default.red("\u2717")} ${import_picocolors21.default.bold(name)} ${import_picocolors21.default.dim(`(${result.responseTimeMs}ms)`)}`);
4240
5230
  if (result.error) {
4241
- console.log(` ${import_picocolors17.default.red(result.error)}`);
5231
+ console.log(` ${import_picocolors21.default.red(result.error)}`);
4242
5232
  }
4243
5233
  console.log(
4244
- ` ${import_picocolors17.default.dim("initialize:")} ${result.initializeOk ? import_picocolors17.default.green("ok") : import_picocolors17.default.red("fail")} ${import_picocolors17.default.dim("tools/list:")} ${result.toolsListOk ? import_picocolors17.default.green("ok") : import_picocolors17.default.red("fail")}`
5234
+ ` ${import_picocolors21.default.dim("initialize:")} ${result.initializeOk ? import_picocolors21.default.green("ok") : import_picocolors21.default.red("fail")} ${import_picocolors21.default.dim("tools/list:")} ${result.toolsListOk ? import_picocolors21.default.green("ok") : import_picocolors21.default.red("fail")}`
4245
5235
  );
4246
5236
  }
4247
5237
  }
4248
- console.log(import_picocolors17.default.dim(`
5238
+ console.log(import_picocolors21.default.dim(`
4249
5239
  ${"\u2500".repeat(40)}`));
4250
5240
  const parts = [];
4251
- if (passed > 0) parts.push(import_picocolors17.default.green(`${passed} passed`));
4252
- if (failed > 0) parts.push(import_picocolors17.default.red(`${failed} failed`));
5241
+ if (passed > 0) parts.push(import_picocolors21.default.green(`${passed} passed`));
5242
+ if (failed > 0) parts.push(import_picocolors21.default.red(`${failed} failed`));
4253
5243
  console.log(` ${parts.join(", ")}
4254
5244
  `);
4255
5245
  if (failed > 0) process.exit(1);
@@ -4269,24 +5259,25 @@ async function loadVaultSecrets3(serverName) {
4269
5259
  // src/commands/update.ts
4270
5260
  init_cjs_shims();
4271
5261
  var p12 = __toESM(require("@clack/prompts"), 1);
4272
- var import_citty19 = require("citty");
4273
- var import_picocolors19 = __toESM(require("picocolors"), 1);
5262
+ var import_citty23 = require("citty");
5263
+ var import_picocolors23 = __toESM(require("picocolors"), 1);
5264
+ init_lockfile();
4274
5265
 
4275
5266
  // src/core/update-notifier.ts
4276
5267
  init_cjs_shims();
4277
- var import_node_fs11 = __toESM(require("fs"), 1);
4278
- var import_node_os5 = __toESM(require("os"), 1);
4279
- var import_node_path12 = __toESM(require("path"), 1);
4280
- var import_picocolors18 = __toESM(require("picocolors"), 1);
4281
- var CACHE_FILE = import_node_path12.default.join(import_node_os5.default.homedir(), ".mcpman", ".update-check");
5268
+ var import_node_fs14 = __toESM(require("fs"), 1);
5269
+ var import_node_os6 = __toESM(require("os"), 1);
5270
+ var import_node_path17 = __toESM(require("path"), 1);
5271
+ var import_picocolors22 = __toESM(require("picocolors"), 1);
5272
+ var CACHE_FILE = import_node_path17.default.join(import_node_os6.default.homedir(), ".mcpman", ".update-check");
4282
5273
  var TTL_MS = 24 * 60 * 60 * 1e3;
4283
5274
  function writeUpdateCache(data) {
4284
5275
  try {
4285
- const dir = import_node_path12.default.dirname(CACHE_FILE);
4286
- if (!import_node_fs11.default.existsSync(dir)) import_node_fs11.default.mkdirSync(dir, { recursive: true });
5276
+ const dir = import_node_path17.default.dirname(CACHE_FILE);
5277
+ if (!import_node_fs14.default.existsSync(dir)) import_node_fs14.default.mkdirSync(dir, { recursive: true });
4287
5278
  const tmp = `${CACHE_FILE}.tmp`;
4288
- import_node_fs11.default.writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
4289
- import_node_fs11.default.renameSync(tmp, CACHE_FILE);
5279
+ import_node_fs14.default.writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
5280
+ import_node_fs14.default.renameSync(tmp, CACHE_FILE);
4290
5281
  } catch {
4291
5282
  }
4292
5283
  }
@@ -4309,19 +5300,19 @@ function printTable(updates) {
4309
5300
  "LATEST".padEnd(VER_W),
4310
5301
  "STATUS"
4311
5302
  ].join(" ");
4312
- console.log(import_picocolors19.default.bold(`
5303
+ console.log(import_picocolors23.default.bold(`
4313
5304
  ${header}`));
4314
- console.log(import_picocolors19.default.dim(` ${"\u2500".repeat(NAME_W + VER_W * 2 + 20)}`));
5305
+ console.log(import_picocolors23.default.dim(` ${"\u2500".repeat(NAME_W + VER_W * 2 + 20)}`));
4315
5306
  for (const u of updates) {
4316
5307
  const nameCol = u.server.slice(0, NAME_W).padEnd(NAME_W);
4317
5308
  const curCol = u.currentVersion.padEnd(VER_W);
4318
5309
  const latCol = u.latestVersion.padEnd(VER_W);
4319
- const statusCol = u.hasUpdate ? import_picocolors19.default.yellow(`Update available${u.updateType ? ` [${u.updateType}]` : ""}`) : import_picocolors19.default.green("Up to date");
5310
+ const statusCol = u.hasUpdate ? import_picocolors23.default.yellow(`Update available${u.updateType ? ` [${u.updateType}]` : ""}`) : import_picocolors23.default.green("Up to date");
4320
5311
  console.log(` ${nameCol} ${curCol} ${latCol} ${statusCol}`);
4321
5312
  }
4322
5313
  console.log();
4323
5314
  }
4324
- var update_default = (0, import_citty19.defineCommand)({
5315
+ var update_default = (0, import_citty23.defineCommand)({
4325
5316
  meta: {
4326
5317
  name: "update",
4327
5318
  description: "Check for and apply updates to installed MCP servers"
@@ -4382,12 +5373,12 @@ var update_default = (0, import_citty19.defineCommand)({
4382
5373
  printTable(updates);
4383
5374
  const outdated = updates.filter((u) => u.hasUpdate);
4384
5375
  if (outdated.length === 0) {
4385
- console.log(import_picocolors19.default.green(" All servers are up to date."));
5376
+ console.log(import_picocolors23.default.green(" All servers are up to date."));
4386
5377
  return;
4387
5378
  }
4388
5379
  if (args.check) {
4389
5380
  console.log(
4390
- import_picocolors19.default.yellow(` ${outdated.length} update(s) available. Run mcpman update to apply.`)
5381
+ import_picocolors23.default.yellow(` ${outdated.length} update(s) available. Run mcpman update to apply.`)
4391
5382
  );
4392
5383
  return;
4393
5384
  }
@@ -4408,10 +5399,10 @@ var update_default = (0, import_citty19.defineCommand)({
4408
5399
  s.start(`Updating ${update.server}...`);
4409
5400
  const result = await applyServerUpdate(update.server, servers[update.server], clients);
4410
5401
  if (result.success) {
4411
- s.stop(`${import_picocolors19.default.green("\u2713")} ${update.server}: ${result.fromVersion} \u2192 ${result.toVersion}`);
5402
+ s.stop(`${import_picocolors23.default.green("\u2713")} ${update.server}: ${result.fromVersion} \u2192 ${result.toVersion}`);
4412
5403
  successCount++;
4413
5404
  } else {
4414
- s.stop(`${import_picocolors19.default.red("\u2717")} ${update.server}: ${result.error}`);
5405
+ s.stop(`${import_picocolors23.default.red("\u2717")} ${update.server}: ${result.error}`);
4415
5406
  }
4416
5407
  }
4417
5408
  const freshLockfile = readLockfile(resolveLockfilePath());
@@ -4424,9 +5415,9 @@ var update_default = (0, import_citty19.defineCommand)({
4424
5415
  // src/commands/upgrade.ts
4425
5416
  init_cjs_shims();
4426
5417
  var import_node_child_process7 = require("child_process");
4427
- var import_citty20 = require("citty");
4428
- var import_picocolors20 = __toESM(require("picocolors"), 1);
4429
- var upgrade_default = (0, import_citty20.defineCommand)({
5418
+ var import_citty24 = require("citty");
5419
+ var import_picocolors24 = __toESM(require("picocolors"), 1);
5420
+ var upgrade_default = (0, import_citty24.defineCommand)({
4430
5421
  meta: {
4431
5422
  name: "upgrade",
4432
5423
  description: "Upgrade mcpman to the latest version"
@@ -4439,41 +5430,453 @@ var upgrade_default = (0, import_citty20.defineCommand)({
4439
5430
  }
4440
5431
  },
4441
5432
  async run({ args }) {
4442
- console.log(import_picocolors20.default.dim(` Current version: ${APP_VERSION}`));
5433
+ console.log(import_picocolors24.default.dim(` Current version: ${APP_VERSION}`));
4443
5434
  let latest;
4444
5435
  try {
4445
5436
  latest = (0, import_node_child_process7.execSync)("npm view mcpman version", { encoding: "utf-8", timeout: 15e3 }).trim();
4446
5437
  } catch {
4447
- console.error(import_picocolors20.default.red(" Error: Could not check latest version from npm."));
5438
+ console.error(import_picocolors24.default.red(" Error: Could not check latest version from npm."));
4448
5439
  process.exit(1);
4449
5440
  }
4450
5441
  if (latest === APP_VERSION) {
4451
- console.log(import_picocolors20.default.green(" \u2713 Already on the latest version."));
5442
+ console.log(import_picocolors24.default.green(" \u2713 Already on the latest version."));
4452
5443
  return;
4453
5444
  }
4454
- console.log(import_picocolors20.default.yellow(` Update available: ${APP_VERSION} \u2192 ${latest}`));
5445
+ console.log(import_picocolors24.default.yellow(` Update available: ${APP_VERSION} \u2192 ${latest}`));
4455
5446
  if (args.check) {
4456
- console.log(import_picocolors20.default.dim(" Run mcpman upgrade to install."));
5447
+ console.log(import_picocolors24.default.dim(" Run mcpman upgrade to install."));
4457
5448
  return;
4458
5449
  }
4459
- console.log(import_picocolors20.default.dim(" Installing..."));
5450
+ console.log(import_picocolors24.default.dim(" Installing..."));
4460
5451
  try {
4461
5452
  (0, import_node_child_process7.execSync)(`npm install -g mcpman@${latest}`, { stdio: "inherit", timeout: 6e4 });
4462
- console.log(import_picocolors20.default.green(`
5453
+ console.log(import_picocolors24.default.green(`
4463
5454
  \u2713 Upgraded to mcpman@${latest}`));
4464
5455
  } catch {
4465
- console.error(import_picocolors20.default.red(" Error: Upgrade failed. Try manually: npm install -g mcpman@latest"));
5456
+ console.error(import_picocolors24.default.red(" Error: Upgrade failed. Try manually: npm install -g mcpman@latest"));
4466
5457
  process.exit(1);
4467
5458
  }
4468
5459
  }
4469
5460
  });
4470
5461
 
5462
+ // src/commands/watch.ts
5463
+ init_cjs_shims();
5464
+ var import_citty25 = require("citty");
5465
+ var import_picocolors25 = __toESM(require("picocolors"), 1);
5466
+
5467
+ // src/core/file-watcher-service.ts
5468
+ init_cjs_shims();
5469
+ var import_node_child_process8 = require("child_process");
5470
+ var import_node_fs15 = __toESM(require("fs"), 1);
5471
+ var IGNORE_PATTERNS = [
5472
+ "node_modules",
5473
+ ".git",
5474
+ "dist",
5475
+ "build",
5476
+ "__pycache__",
5477
+ ".pyc",
5478
+ ".egg-info",
5479
+ ".tox"
5480
+ ];
5481
+ function shouldIgnore(filename) {
5482
+ return IGNORE_PATTERNS.some((p13) => filename.includes(p13));
5483
+ }
5484
+ function hasWatchedExtension(filename, extensions) {
5485
+ return extensions.some((ext) => filename.endsWith(`.${ext}`));
5486
+ }
5487
+ function timestamp() {
5488
+ return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
5489
+ }
5490
+ var ServerWatcher = class {
5491
+ child = null;
5492
+ watcher = null;
5493
+ restartCount = 0;
5494
+ debounceTimer = null;
5495
+ options = null;
5496
+ stopping = false;
5497
+ /** Start watching and spawn the initial server process */
5498
+ start(options) {
5499
+ this.options = options;
5500
+ this.stopping = false;
5501
+ console.log(`
5502
+ Watching ${options.serverName} (${options.watchDir})...`);
5503
+ console.log(` Extensions: ${options.extensions.join(", ")}`);
5504
+ console.log(` Debounce: ${options.debounceMs}ms
5505
+ `);
5506
+ this.spawnChild();
5507
+ try {
5508
+ this.watcher = import_node_fs15.default.watch(options.watchDir, { recursive: true }, (_event, filename) => {
5509
+ if (!filename) return;
5510
+ this.onFileChange(filename);
5511
+ });
5512
+ } catch (err) {
5513
+ console.error(
5514
+ ` Warning: Could not watch directory: ${err instanceof Error ? err.message : String(err)}`
5515
+ );
5516
+ }
5517
+ }
5518
+ /** Gracefully stop watcher and child process */
5519
+ stop() {
5520
+ this.stopping = true;
5521
+ if (this.debounceTimer) {
5522
+ clearTimeout(this.debounceTimer);
5523
+ this.debounceTimer = null;
5524
+ }
5525
+ if (this.watcher) {
5526
+ this.watcher.close();
5527
+ this.watcher = null;
5528
+ }
5529
+ if (this.child && !this.child.killed) {
5530
+ this.child.kill("SIGTERM");
5531
+ }
5532
+ console.log(`
5533
+ Stopped. (${this.restartCount} restart${this.restartCount !== 1 ? "s" : ""})`);
5534
+ }
5535
+ /** Spawn the server child process, piping its stdio to parent */
5536
+ spawnChild() {
5537
+ if (!this.options) return;
5538
+ const { command, args, env, serverName, clearOnRestart } = this.options;
5539
+ if (clearOnRestart && this.restartCount > 0) {
5540
+ process.stdout.write("\x1Bc");
5541
+ }
5542
+ console.log(` [${timestamp()}] Starting ${serverName}...`);
5543
+ this.child = (0, import_node_child_process8.spawn)(command, args, { env, stdio: ["pipe", "pipe", "pipe"] });
5544
+ this.child.stdout?.on("data", (data) => {
5545
+ process.stdout.write(` [stdout] ${data.toString().trimEnd()}
5546
+ `);
5547
+ });
5548
+ this.child.stderr?.on("data", (data) => {
5549
+ process.stderr.write(` [stderr] ${data.toString().trimEnd()}
5550
+ `);
5551
+ });
5552
+ this.child.on("error", (err) => {
5553
+ console.error(` [${timestamp()}] Error: ${err.message}`);
5554
+ });
5555
+ this.child.on("close", (code) => {
5556
+ if (!this.stopping) {
5557
+ console.log(` [${timestamp()}] Process exited (code ${code ?? "?"})`);
5558
+ }
5559
+ });
5560
+ }
5561
+ /** Kill child process: SIGTERM first, then SIGKILL after 2s */
5562
+ async killChild() {
5563
+ if (!this.child || this.child.killed) return;
5564
+ const childRef = this.child;
5565
+ return new Promise((resolve) => {
5566
+ const child = childRef;
5567
+ const killTimer = setTimeout(() => {
5568
+ if (!child.killed) {
5569
+ child.kill("SIGKILL");
5570
+ }
5571
+ resolve();
5572
+ }, 2e3);
5573
+ child.on("close", () => {
5574
+ clearTimeout(killTimer);
5575
+ resolve();
5576
+ });
5577
+ child.kill("SIGTERM");
5578
+ });
5579
+ }
5580
+ /** Debounced file change handler */
5581
+ onFileChange(filename) {
5582
+ if (!this.options) return;
5583
+ if (shouldIgnore(filename)) return;
5584
+ if (!hasWatchedExtension(filename, this.options.extensions)) return;
5585
+ if (this.debounceTimer) {
5586
+ clearTimeout(this.debounceTimer);
5587
+ }
5588
+ this.debounceTimer = setTimeout(async () => {
5589
+ this.debounceTimer = null;
5590
+ this.restartCount++;
5591
+ console.log(` [${timestamp()}] File changed: ${filename}`);
5592
+ console.log(
5593
+ ` [${timestamp()}] Restarting ${this.options?.serverName}... (restart #${this.restartCount})`
5594
+ );
5595
+ await this.killChild();
5596
+ this.spawnChild();
5597
+ }, this.options.debounceMs);
5598
+ }
5599
+ getRestartCount() {
5600
+ return this.restartCount;
5601
+ }
5602
+ };
5603
+
5604
+ // src/commands/watch.ts
5605
+ init_lockfile();
5606
+ init_vault_service();
5607
+ var DEFAULT_EXTENSIONS = ["ts", "js", "json", "py", "mjs", "cjs"];
5608
+ var DEFAULT_DEBOUNCE_MS = 300;
5609
+ var watch_default = (0, import_citty25.defineCommand)({
5610
+ meta: {
5611
+ name: "watch",
5612
+ description: "Watch a local MCP server for file changes and auto-restart"
5613
+ },
5614
+ args: {
5615
+ server: {
5616
+ type: "positional",
5617
+ description: "Server name (must be in lockfile)",
5618
+ required: true
5619
+ },
5620
+ dir: {
5621
+ type: "string",
5622
+ description: "Directory to watch (default: resolved path from lockfile)"
5623
+ },
5624
+ ext: {
5625
+ type: "string",
5626
+ description: `File extensions to watch, comma-separated (default: ${DEFAULT_EXTENSIONS.join(",")})`
5627
+ },
5628
+ delay: {
5629
+ type: "string",
5630
+ description: `Debounce delay in ms (default: ${DEFAULT_DEBOUNCE_MS})`
5631
+ },
5632
+ clear: {
5633
+ type: "boolean",
5634
+ description: "Clear terminal on each restart",
5635
+ default: false
5636
+ },
5637
+ env: {
5638
+ type: "string",
5639
+ description: "Override env var KEY=VAL (repeatable)",
5640
+ alias: "e"
5641
+ }
5642
+ },
5643
+ async run({ args }) {
5644
+ const serverName = args.server;
5645
+ const lockfile = readLockfile();
5646
+ const entry = lockfile.servers[serverName];
5647
+ if (!entry) {
5648
+ console.error(import_picocolors25.default.red(` Error: Server '${serverName}' not found in lockfile.`));
5649
+ console.error(import_picocolors25.default.dim(` Run ${import_picocolors25.default.cyan("mcpman link .")} to register a local server.`));
5650
+ process.exit(1);
5651
+ }
5652
+ let watchDir = args.dir;
5653
+ if (!watchDir) {
5654
+ if (entry.source === "local" && entry.resolved) {
5655
+ watchDir = entry.resolved;
5656
+ } else {
5657
+ console.error(import_picocolors25.default.red(` Error: Cannot determine watch directory for '${serverName}'.`));
5658
+ console.error(import_picocolors25.default.dim(" Use --dir to specify the directory to watch."));
5659
+ process.exit(1);
5660
+ }
5661
+ }
5662
+ const extensions = args.ext ? args.ext.split(",").map((e) => e.trim().replace(/^\./, "")) : DEFAULT_EXTENSIONS;
5663
+ const debounceMs = args.delay ? Number.parseInt(args.delay, 10) || DEFAULT_DEBOUNCE_MS : DEFAULT_DEBOUNCE_MS;
5664
+ const lockfileEnv = parseEnvFlags(entry.envVars);
5665
+ const vaultEnv = await loadVaultSecrets4(serverName);
5666
+ const cliEnv = parseEnvFlags(args.env);
5667
+ const finalEnv = {
5668
+ ...process.env,
5669
+ ...lockfileEnv,
5670
+ ...vaultEnv,
5671
+ ...cliEnv
5672
+ };
5673
+ const watcher = new ServerWatcher();
5674
+ const handleStop = () => {
5675
+ watcher.stop();
5676
+ process.exit(0);
5677
+ };
5678
+ process.on("SIGINT", handleStop);
5679
+ process.on("SIGTERM", handleStop);
5680
+ watcher.start({
5681
+ command: entry.command,
5682
+ args: entry.args,
5683
+ env: finalEnv,
5684
+ watchDir,
5685
+ extensions,
5686
+ debounceMs,
5687
+ clearOnRestart: args.clear,
5688
+ serverName
5689
+ });
5690
+ }
5691
+ });
5692
+ async function loadVaultSecrets4(serverName) {
5693
+ try {
5694
+ const entries = listSecrets(serverName);
5695
+ if (entries.length === 0 || entries[0].keys.length === 0) return {};
5696
+ const password2 = await getMasterPassword();
5697
+ return getSecretsForServer(serverName, password2);
5698
+ } catch {
5699
+ return {};
5700
+ }
5701
+ }
5702
+
5703
+ // src/commands/why.ts
5704
+ init_cjs_shims();
5705
+ var import_citty26 = require("citty");
5706
+ var import_picocolors26 = __toESM(require("picocolors"), 1);
5707
+
5708
+ // src/core/why-service.ts
5709
+ init_cjs_shims();
5710
+ var import_node_fs16 = __toESM(require("fs"), 1);
5711
+ var import_node_path18 = __toESM(require("path"), 1);
5712
+ init_paths();
5713
+ init_lockfile();
5714
+ var ALL_CLIENT_TYPES = ["claude-desktop", "cursor", "vscode", "windsurf"];
5715
+ async function getServerProvenance(serverName, lockfilePath, profilesDir) {
5716
+ const lockfile = readLockfile(lockfilePath);
5717
+ const entry = lockfile.servers[serverName];
5718
+ if (!entry) {
5719
+ const orphanedClients = await findOrphanedClients(serverName);
5720
+ const anyRegistered = orphanedClients.some((c) => c.registered);
5721
+ if (!anyRegistered) return null;
5722
+ return {
5723
+ name: serverName,
5724
+ version: "unknown",
5725
+ source: "unknown",
5726
+ resolved: "",
5727
+ integrity: "",
5728
+ installedAt: "",
5729
+ clients: orphanedClients,
5730
+ profiles: [],
5731
+ envVars: [],
5732
+ orphaned: true
5733
+ };
5734
+ }
5735
+ const clientStatuses = await buildClientStatuses(serverName, entry.clients);
5736
+ const profiles = scanProfiles(serverName, profilesDir ?? getProfilesDir());
5737
+ return {
5738
+ name: serverName,
5739
+ version: entry.version,
5740
+ source: entry.source,
5741
+ resolved: entry.resolved,
5742
+ integrity: entry.integrity,
5743
+ installedAt: entry.installedAt,
5744
+ clients: clientStatuses,
5745
+ profiles,
5746
+ envVars: entry.envVars ?? [],
5747
+ orphaned: false
5748
+ };
5749
+ }
5750
+ async function buildClientStatuses(serverName, lockfileClients) {
5751
+ return ALL_CLIENT_TYPES.map((type) => ({
5752
+ type,
5753
+ registered: lockfileClients.includes(type)
5754
+ }));
5755
+ }
5756
+ async function findOrphanedClients(serverName) {
5757
+ const { getInstalledClients: getInstalledClients2 } = await Promise.resolve().then(() => (init_client_detector(), client_detector_exports));
5758
+ const handlers = await getInstalledClients2();
5759
+ const results = [];
5760
+ for (const handler of handlers) {
5761
+ try {
5762
+ const config = await handler.readConfig();
5763
+ const registered = serverName in (config.servers ?? {});
5764
+ results.push({ type: handler.type, registered });
5765
+ } catch {
5766
+ results.push({ type: handler.type, registered: false });
5767
+ }
5768
+ }
5769
+ return results;
5770
+ }
5771
+ function scanProfiles(serverName, profilesDir) {
5772
+ const found = [];
5773
+ if (!import_node_fs16.default.existsSync(profilesDir)) return found;
5774
+ let files;
5775
+ try {
5776
+ files = import_node_fs16.default.readdirSync(profilesDir).filter((f) => f.endsWith(".json"));
5777
+ } catch {
5778
+ return found;
5779
+ }
5780
+ for (const file of files) {
5781
+ try {
5782
+ const raw = import_node_fs16.default.readFileSync(import_node_path18.default.join(profilesDir, file), "utf-8");
5783
+ const profile = JSON.parse(raw);
5784
+ if (serverName in (profile.servers ?? {})) {
5785
+ found.push(profile.name ?? file.replace(".json", ""));
5786
+ }
5787
+ } catch {
5788
+ }
5789
+ }
5790
+ return found.sort();
5791
+ }
5792
+ function formatWhyOutput(result) {
5793
+ const lines = [];
5794
+ lines.push(` Server: ${result.name}`);
5795
+ lines.push(` Version: ${result.version}`);
5796
+ lines.push(` Source: ${result.source}`);
5797
+ if (result.resolved) lines.push(` Resolved: ${result.resolved}`);
5798
+ if (result.integrity && result.integrity !== "local") {
5799
+ lines.push(` Integrity: ${result.integrity}`);
5800
+ }
5801
+ if (result.installedAt) lines.push(` Installed: ${result.installedAt}`);
5802
+ lines.push("");
5803
+ lines.push(" Clients:");
5804
+ for (const c of result.clients) {
5805
+ const status = c.registered ? "registered" : "not registered";
5806
+ lines.push(` ${c.type.padEnd(20)} ${status}`);
5807
+ }
5808
+ if (result.profiles.length > 0) {
5809
+ lines.push("");
5810
+ lines.push(" Profiles:");
5811
+ for (const p13 of result.profiles) {
5812
+ lines.push(` ${p13}`);
5813
+ }
5814
+ }
5815
+ if (result.envVars.length > 0) {
5816
+ lines.push("");
5817
+ lines.push(" Env Vars:");
5818
+ for (const v of result.envVars) {
5819
+ lines.push(` ${v}`);
5820
+ }
5821
+ }
5822
+ return lines.join("\n");
5823
+ }
5824
+
5825
+ // src/commands/why.ts
5826
+ var why_default = (0, import_citty26.defineCommand)({
5827
+ meta: {
5828
+ name: "why",
5829
+ description: "Show why a server is installed (provenance, clients, profiles)"
5830
+ },
5831
+ args: {
5832
+ server: {
5833
+ type: "positional",
5834
+ description: "Server name to inspect",
5835
+ required: true
5836
+ },
5837
+ json: {
5838
+ type: "boolean",
5839
+ description: "Output as JSON for scripting",
5840
+ default: false
5841
+ }
5842
+ },
5843
+ async run({ args }) {
5844
+ const serverName = args.server;
5845
+ const asJson = args.json;
5846
+ const result = await getServerProvenance(serverName);
5847
+ if (!result) {
5848
+ console.error(import_picocolors26.default.red(` Server '${serverName}' not found in lockfile or any client config.`));
5849
+ console.error(import_picocolors26.default.dim(` Run ${import_picocolors26.default.cyan("mcpman list")} to see installed servers.`));
5850
+ process.exit(1);
5851
+ }
5852
+ if (result.orphaned) {
5853
+ console.log(import_picocolors26.default.yellow(`
5854
+ Server '${serverName}' is orphaned:`));
5855
+ console.log(import_picocolors26.default.dim(" Found in client config(s) but not in lockfile."));
5856
+ console.log(import_picocolors26.default.dim(` Run ${import_picocolors26.default.cyan("mcpman sync --remove")} to clean up.
5857
+ `));
5858
+ const registeredClients = result.clients.filter((c) => c.registered).map((c) => c.type);
5859
+ if (registeredClients.length > 0) {
5860
+ console.log(` Registered in: ${registeredClients.join(", ")}`);
5861
+ }
5862
+ return;
5863
+ }
5864
+ if (asJson) {
5865
+ console.log(JSON.stringify(result, null, 2));
5866
+ return;
5867
+ }
5868
+ console.log();
5869
+ console.log(formatWhyOutput(result));
5870
+ console.log();
5871
+ }
5872
+ });
5873
+
4471
5874
  // src/index.ts
4472
5875
  process.on("SIGINT", () => {
4473
5876
  console.log("\nAborted.");
4474
5877
  process.exit(130);
4475
5878
  });
4476
- var main = (0, import_citty21.defineCommand)({
5879
+ var main = (0, import_citty27.defineCommand)({
4477
5880
  meta: {
4478
5881
  name: APP_NAME,
4479
5882
  version: APP_VERSION,
@@ -4499,7 +5902,13 @@ var main = (0, import_citty21.defineCommand)({
4499
5902
  profiles: profiles_default,
4500
5903
  plugin: plugin_default,
4501
5904
  export: export_command_default,
4502
- import: import_command_default
5905
+ import: import_command_default,
5906
+ create: create_default,
5907
+ link: link_default,
5908
+ watch: watch_default,
5909
+ registry: registry_default,
5910
+ completions: completions_default,
5911
+ why: why_default
4503
5912
  }
4504
5913
  });
4505
- (0, import_citty21.runMain)(main);
5914
+ (0, import_citty27.runMain)(main);