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.js CHANGED
@@ -1,4 +1,13 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ LOCKFILE_NAME,
4
+ addEntry,
5
+ createEmptyLockfile,
6
+ findLockfile,
7
+ readLockfile,
8
+ resolveLockfilePath,
9
+ writeLockfile
10
+ } from "./chunk-YZNTMR6O.js";
2
11
  import {
3
12
  computeTrustScore
4
13
  } from "./chunk-RGKHLY5G.js";
@@ -20,7 +29,7 @@ import {
20
29
  } from "./chunk-6X6Q6UZC.js";
21
30
 
22
31
  // src/index.ts
23
- import { defineCommand as defineCommand21, runMain } from "citty";
32
+ import { defineCommand as defineCommand27, runMain } from "citty";
24
33
 
25
34
  // src/commands/audit.ts
26
35
  import * as p from "@clack/prompts";
@@ -28,89 +37,26 @@ import { defineCommand } from "citty";
28
37
  import { createSpinner } from "nanospinner";
29
38
  import pc from "picocolors";
30
39
 
31
- // src/core/lockfile.ts
40
+ // src/core/security-scanner.ts
32
41
  import fs from "fs";
33
42
  import os from "os";
34
43
  import path from "path";
35
- var LOCKFILE_NAME = "mcpman.lock";
36
- function findLockfile() {
37
- let dir = process.cwd();
38
- while (true) {
39
- const candidate = path.join(dir, LOCKFILE_NAME);
40
- if (fs.existsSync(candidate)) return candidate;
41
- const parent = path.dirname(dir);
42
- if (parent === dir) break;
43
- dir = parent;
44
- }
45
- return null;
46
- }
47
- function getGlobalLockfilePath() {
48
- return path.join(os.homedir(), ".mcpman", LOCKFILE_NAME);
49
- }
50
- function resolveLockfilePath() {
51
- return findLockfile() ?? getGlobalLockfilePath();
52
- }
53
- function readLockfile(filePath) {
54
- const target = filePath ?? resolveLockfilePath();
55
- if (!fs.existsSync(target)) {
56
- return { lockfileVersion: 1, servers: {} };
57
- }
58
- try {
59
- const raw = fs.readFileSync(target, "utf-8");
60
- return JSON.parse(raw);
61
- } catch {
62
- return { lockfileVersion: 1, servers: {} };
63
- }
64
- }
65
- function serialize(data) {
66
- const sorted = {
67
- lockfileVersion: data.lockfileVersion,
68
- servers: Object.fromEntries(
69
- Object.entries(data.servers).sort(([a], [b]) => a.localeCompare(b))
70
- )
71
- };
72
- return `${JSON.stringify(sorted, null, 2)}
73
- `;
74
- }
75
- function writeLockfile(data, filePath) {
76
- const target = filePath ?? resolveLockfilePath();
77
- const dir = path.dirname(target);
78
- if (!fs.existsSync(dir)) {
79
- fs.mkdirSync(dir, { recursive: true });
80
- }
81
- const tmp = `${target}.tmp`;
82
- fs.writeFileSync(tmp, serialize(data), "utf-8");
83
- fs.renameSync(tmp, target);
84
- }
85
- function addEntry(name, entry, filePath) {
86
- const data = readLockfile(filePath);
87
- data.servers[name] = entry;
88
- writeLockfile(data, filePath);
89
- }
90
- function createEmptyLockfile(filePath) {
91
- writeLockfile({ lockfileVersion: 1, servers: {} }, filePath);
92
- }
93
-
94
- // src/core/security-scanner.ts
95
- import fs2 from "fs";
96
- import os2 from "os";
97
- import path2 from "path";
98
- var CACHE_PATH = path2.join(os2.homedir(), ".mcpman", ".audit-cache.json");
44
+ var CACHE_PATH = path.join(os.homedir(), ".mcpman", ".audit-cache.json");
99
45
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
100
46
  function readCache() {
101
47
  try {
102
- if (!fs2.existsSync(CACHE_PATH)) return {};
103
- return JSON.parse(fs2.readFileSync(CACHE_PATH, "utf-8"));
48
+ if (!fs.existsSync(CACHE_PATH)) return {};
49
+ return JSON.parse(fs.readFileSync(CACHE_PATH, "utf-8"));
104
50
  } catch {
105
51
  return {};
106
52
  }
107
53
  }
108
54
  function writeCache(cache) {
109
- const dir = path2.dirname(CACHE_PATH);
110
- if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
55
+ const dir = path.dirname(CACHE_PATH);
56
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
111
57
  const tmp = `${CACHE_PATH}.tmp`;
112
- fs2.writeFileSync(tmp, JSON.stringify(cache, null, 2), "utf-8");
113
- fs2.renameSync(tmp, CACHE_PATH);
58
+ fs.writeFileSync(tmp, JSON.stringify(cache, null, 2), "utf-8");
59
+ fs.renameSync(tmp, CACHE_PATH);
114
60
  }
115
61
  function getCachedReport(name, version) {
116
62
  const cache = readCache();
@@ -340,13 +286,13 @@ async function resolveFromGitHub(githubUrl) {
340
286
 
341
287
  // src/core/plugin-loader.ts
342
288
  import { execSync } from "child_process";
343
- import fs4 from "fs";
289
+ import fs3 from "fs";
344
290
  import { createRequire } from "module";
345
- import path4 from "path";
291
+ import path3 from "path";
346
292
 
347
293
  // src/core/config-service.ts
348
- import fs3 from "fs";
349
- import path3 from "path";
294
+ import fs2 from "fs";
295
+ import path2 from "path";
350
296
  var VALID_KEYS = /* @__PURE__ */ new Set([
351
297
  "defaultClient",
352
298
  "updateCheckInterval",
@@ -356,7 +302,7 @@ var VALID_KEYS = /* @__PURE__ */ new Set([
356
302
  ]);
357
303
  function readConfig(configPath = getConfigPath()) {
358
304
  try {
359
- const raw = fs3.readFileSync(configPath, "utf-8");
305
+ const raw = fs2.readFileSync(configPath, "utf-8");
360
306
  const parsed = JSON.parse(raw);
361
307
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
362
308
  return {};
@@ -367,11 +313,11 @@ function readConfig(configPath = getConfigPath()) {
367
313
  }
368
314
  }
369
315
  function writeConfig(data, configPath = getConfigPath()) {
370
- const dir = path3.dirname(configPath);
371
- fs3.mkdirSync(dir, { recursive: true });
316
+ const dir = path2.dirname(configPath);
317
+ fs2.mkdirSync(dir, { recursive: true });
372
318
  const tmp = `${configPath}.tmp`;
373
- fs3.writeFileSync(tmp, JSON.stringify(data, null, 2), { encoding: "utf-8" });
374
- fs3.renameSync(tmp, configPath);
319
+ fs2.writeFileSync(tmp, JSON.stringify(data, null, 2), { encoding: "utf-8" });
320
+ fs2.renameSync(tmp, configPath);
375
321
  }
376
322
  function getConfigValue(key, configPath = getConfigPath()) {
377
323
  const data = readConfig(configPath);
@@ -395,8 +341,8 @@ function isValidPlugin(obj) {
395
341
  }
396
342
  function loadPlugin(pkg, pluginDir = getPluginDir()) {
397
343
  try {
398
- const requirePath = path4.join(pluginDir, "node_modules", pkg);
399
- const pluginRequire = createRequire(path4.join(pluginDir, "index.js"));
344
+ const requirePath = path3.join(pluginDir, "node_modules", pkg);
345
+ const pluginRequire = createRequire(path3.join(pluginDir, "index.js"));
400
346
  const mod = pluginRequire(requirePath);
401
347
  const exported = mod?.default ?? mod;
402
348
  if (isValidPlugin(exported)) return exported;
@@ -416,10 +362,10 @@ function loadAllPlugins(pluginDir = getPluginDir()) {
416
362
  return plugins;
417
363
  }
418
364
  function installPluginPackage(name, pluginDir = getPluginDir()) {
419
- fs4.mkdirSync(pluginDir, { recursive: true });
420
- const pkgJsonPath = path4.join(pluginDir, "package.json");
421
- if (!fs4.existsSync(pkgJsonPath)) {
422
- fs4.writeFileSync(
365
+ fs3.mkdirSync(pluginDir, { recursive: true });
366
+ const pkgJsonPath = path3.join(pluginDir, "package.json");
367
+ if (!fs3.existsSync(pkgJsonPath)) {
368
+ fs3.writeFileSync(
423
369
  pkgJsonPath,
424
370
  JSON.stringify({ name: "mcpman-plugins", private: true }, null, 2)
425
371
  );
@@ -907,10 +853,286 @@ async function runAuditFix(reports, servers, skipConfirm) {
907
853
  `);
908
854
  }
909
855
 
910
- // src/commands/config.ts
911
- import * as p2 from "@clack/prompts";
856
+ // src/commands/completions.ts
857
+ import fs4 from "fs";
858
+ import os2 from "os";
859
+ import path4 from "path";
912
860
  import { defineCommand as defineCommand2 } from "citty";
913
861
  import pc2 from "picocolors";
862
+
863
+ // src/core/completion-generator.ts
864
+ function getCommandList() {
865
+ return [
866
+ "install",
867
+ "list",
868
+ "remove",
869
+ "doctor",
870
+ "init",
871
+ "secrets",
872
+ "sync",
873
+ "audit",
874
+ "update",
875
+ "upgrade",
876
+ "config",
877
+ "search",
878
+ "info",
879
+ "run",
880
+ "logs",
881
+ "test",
882
+ "profiles",
883
+ "plugin",
884
+ "export",
885
+ "import",
886
+ "create",
887
+ "link",
888
+ "watch",
889
+ "registry",
890
+ "completions",
891
+ "why"
892
+ ];
893
+ }
894
+ var SERVER_ARG_COMMANDS = [
895
+ "run",
896
+ "test",
897
+ "logs",
898
+ "watch",
899
+ "remove",
900
+ "update",
901
+ "info",
902
+ "audit",
903
+ "link",
904
+ "why"
905
+ ];
906
+ function getServerNames(lockfilePath) {
907
+ try {
908
+ const data = readLockfile(lockfilePath);
909
+ return Object.keys(data.servers);
910
+ } catch {
911
+ return [];
912
+ }
913
+ }
914
+ function generateBashCompletion() {
915
+ const serverCmds = SERVER_ARG_COMMANDS.join("|");
916
+ return `# mcpman bash completion
917
+ # Add to ~/.bashrc or ~/.bash_profile:
918
+ # source <(mcpman completions bash)
919
+ # Or append permanently:
920
+ # mcpman completions bash >> ~/.bashrc
921
+
922
+ _mcpman_completions() {
923
+ local cur="\${COMP_WORDS[COMP_CWORD]}"
924
+ local prev="\${COMP_WORDS[COMP_CWORD-1]}"
925
+
926
+ if [ "$COMP_CWORD" -eq 1 ]; then
927
+ COMPREPLY=($(compgen -W "$(mcpman completions --list-commands 2>/dev/null)" -- "$cur"))
928
+ return
929
+ fi
930
+
931
+ case "$prev" in
932
+ ${serverCmds})
933
+ COMPREPLY=($(compgen -W "$(mcpman completions --list-servers 2>/dev/null)" -- "$cur"))
934
+ ;;
935
+ --client|-c)
936
+ COMPREPLY=($(compgen -W "claude-desktop cursor vscode windsurf" -- "$cur"))
937
+ ;;
938
+ --runtime|-r)
939
+ COMPREPLY=($(compgen -W "node python" -- "$cur"))
940
+ ;;
941
+ esac
942
+ }
943
+
944
+ complete -F _mcpman_completions mcpman
945
+ `;
946
+ }
947
+ function generateZshCompletion() {
948
+ const serverCmds = SERVER_ARG_COMMANDS.map((c) => `'${c}'`).join(" ");
949
+ return `#compdef mcpman
950
+ # mcpman zsh completion
951
+ # Add to ~/.zshrc:
952
+ # source <(mcpman completions zsh)
953
+ # Or use compinit:
954
+ # mcpman completions zsh > "\${fpath[1]}/_mcpman"
955
+
956
+ _mcpman() {
957
+ local state
958
+
959
+ _arguments \\
960
+ '1: :->command' \\
961
+ '*: :->args'
962
+
963
+ case $state in
964
+ command)
965
+ local commands
966
+ commands=($(mcpman completions --list-commands 2>/dev/null))
967
+ _describe 'command' commands
968
+ ;;
969
+ args)
970
+ local cmd="\${words[2]}"
971
+ local server_cmds=(${serverCmds})
972
+ if (( server_cmds[(I)$cmd] )); then
973
+ local servers
974
+ servers=($(mcpman completions --list-servers 2>/dev/null))
975
+ _describe 'server' servers
976
+ fi
977
+ ;;
978
+ esac
979
+ }
980
+
981
+ compdef _mcpman mcpman
982
+ `;
983
+ }
984
+ function generateFishCompletion() {
985
+ const serverCmds = SERVER_ARG_COMMANDS.join(" ");
986
+ return `# mcpman fish completion
987
+ # Add to ~/.config/fish/completions/mcpman.fish:
988
+ # mcpman completions fish > ~/.config/fish/completions/mcpman.fish
989
+
990
+ # Disable file completion for mcpman
991
+ complete -c mcpman -f
992
+
993
+ # Subcommand completions (dynamic)
994
+ complete -c mcpman -n '__fish_use_subcommand' \\
995
+ -a "(mcpman completions --list-commands 2>/dev/null)"
996
+
997
+ # Server name completions for commands that take a server arg
998
+ for cmd in ${serverCmds}
999
+ complete -c mcpman -n "__fish_seen_subcommand_from $cmd" \\
1000
+ -a "(mcpman completions --list-servers 2>/dev/null)"
1001
+ end
1002
+
1003
+ # --client flag completions
1004
+ complete -c mcpman -l client -s c \\
1005
+ -a "claude-desktop cursor vscode windsurf" \\
1006
+ -d "Target client"
1007
+
1008
+ # --runtime flag completions
1009
+ complete -c mcpman -l runtime -s r \\
1010
+ -a "node python" \\
1011
+ -d "Runtime"
1012
+ `;
1013
+ }
1014
+
1015
+ // src/commands/completions.ts
1016
+ var completions_default = defineCommand2({
1017
+ meta: {
1018
+ name: "completions",
1019
+ description: "Generate shell completion scripts (bash, zsh, fish)"
1020
+ },
1021
+ args: {
1022
+ shell: {
1023
+ type: "positional",
1024
+ description: "Shell: bash, zsh, fish, or install",
1025
+ required: false
1026
+ },
1027
+ "list-commands": {
1028
+ type: "boolean",
1029
+ description: "Output all command names (used by completion scripts)",
1030
+ default: false
1031
+ },
1032
+ "list-servers": {
1033
+ type: "boolean",
1034
+ description: "Output server names from lockfile (used by completion scripts)",
1035
+ default: false
1036
+ }
1037
+ },
1038
+ async run({ args }) {
1039
+ if (args["list-commands"]) {
1040
+ console.log(getCommandList().join("\n"));
1041
+ return;
1042
+ }
1043
+ if (args["list-servers"]) {
1044
+ console.log(getServerNames().join("\n"));
1045
+ return;
1046
+ }
1047
+ const shell = args.shell;
1048
+ if (!shell) {
1049
+ printUsage();
1050
+ return;
1051
+ }
1052
+ switch (shell.toLowerCase()) {
1053
+ case "bash":
1054
+ process.stdout.write(generateBashCompletion());
1055
+ break;
1056
+ case "zsh":
1057
+ process.stdout.write(generateZshCompletion());
1058
+ break;
1059
+ case "fish":
1060
+ process.stdout.write(generateFishCompletion());
1061
+ break;
1062
+ case "install":
1063
+ await installCompletion();
1064
+ break;
1065
+ default:
1066
+ console.error(pc2.red(` Error: Unknown shell '${shell}'. Use: bash, zsh, or fish.`));
1067
+ process.exit(1);
1068
+ }
1069
+ }
1070
+ });
1071
+ function printUsage() {
1072
+ console.log(pc2.bold("\n mcpman completions \u2014 Shell completion setup\n"));
1073
+ console.log(" Usage:");
1074
+ console.log(` ${pc2.cyan("mcpman completions bash")} Output bash completion script`);
1075
+ console.log(` ${pc2.cyan("mcpman completions zsh")} Output zsh completion script`);
1076
+ console.log(` ${pc2.cyan("mcpman completions fish")} Output fish completion script`);
1077
+ console.log(` ${pc2.cyan("mcpman completions install")} Auto-detect shell and install
1078
+ `);
1079
+ console.log(" Quick setup:");
1080
+ console.log(` ${pc2.dim("# bash")}`);
1081
+ console.log(` ${pc2.cyan("source <(mcpman completions bash)")}`);
1082
+ console.log(` ${pc2.dim("# zsh")}`);
1083
+ console.log(` ${pc2.cyan("source <(mcpman completions zsh)")}
1084
+ `);
1085
+ }
1086
+ async function installCompletion() {
1087
+ const shellBin = process.env.SHELL ?? "";
1088
+ let detectedShell = "";
1089
+ if (shellBin.includes("zsh")) detectedShell = "zsh";
1090
+ else if (shellBin.includes("fish")) detectedShell = "fish";
1091
+ else if (shellBin.includes("bash")) detectedShell = "bash";
1092
+ if (!detectedShell) {
1093
+ console.error(pc2.red(" Could not detect shell from $SHELL. Run manually:"));
1094
+ console.error(pc2.dim(" source <(mcpman completions bash|zsh|fish)"));
1095
+ process.exit(1);
1096
+ }
1097
+ const home = os2.homedir();
1098
+ let rcFile;
1099
+ let script;
1100
+ if (detectedShell === "zsh") {
1101
+ rcFile = path4.join(home, ".zshrc");
1102
+ script = generateZshCompletion();
1103
+ } else if (detectedShell === "fish") {
1104
+ const fishDir = path4.join(home, ".config", "fish", "completions");
1105
+ fs4.mkdirSync(fishDir, { recursive: true });
1106
+ rcFile = path4.join(fishDir, "mcpman.fish");
1107
+ fs4.writeFileSync(rcFile, generateFishCompletion(), "utf-8");
1108
+ console.log(pc2.green(` Installed fish completions to ${rcFile}`));
1109
+ return;
1110
+ } else {
1111
+ rcFile = path4.join(home, ".bashrc");
1112
+ script = generateBashCompletion();
1113
+ }
1114
+ const marker = "# mcpman completions";
1115
+ let existing = "";
1116
+ try {
1117
+ existing = fs4.readFileSync(rcFile, "utf-8");
1118
+ } catch {
1119
+ }
1120
+ if (existing.includes(marker)) {
1121
+ console.log(pc2.yellow(` Completions already installed in ${rcFile}. Skipping.`));
1122
+ return;
1123
+ }
1124
+ fs4.appendFileSync(rcFile, `
1125
+ ${marker}
1126
+ source <(mcpman completions ${detectedShell})
1127
+ `);
1128
+ console.log(pc2.green(` Installed ${detectedShell} completions in ${rcFile}`));
1129
+ console.log(pc2.dim(` Restart your shell or run: source ${rcFile}`));
1130
+ }
1131
+
1132
+ // src/commands/config.ts
1133
+ import * as p2 from "@clack/prompts";
1134
+ import { defineCommand as defineCommand3 } from "citty";
1135
+ import pc3 from "picocolors";
914
1136
  function coerceValue(raw) {
915
1137
  if (raw === "true") return true;
916
1138
  if (raw === "false") return false;
@@ -918,7 +1140,7 @@ function coerceValue(raw) {
918
1140
  if (!Number.isNaN(num) && raw.trim() !== "") return num;
919
1141
  return raw;
920
1142
  }
921
- var setCommand = defineCommand2({
1143
+ var setCommand = defineCommand3({
922
1144
  meta: { name: "set", description: "Set a config value" },
923
1145
  args: {
924
1146
  key: {
@@ -936,14 +1158,14 @@ var setCommand = defineCommand2({
936
1158
  try {
937
1159
  const coerced = coerceValue(args.value);
938
1160
  setConfigValue(args.key, coerced);
939
- console.log(`${pc2.green("\u2713")} Set ${pc2.bold(args.key)} = ${pc2.cyan(String(coerced))}`);
1161
+ console.log(`${pc3.green("\u2713")} Set ${pc3.bold(args.key)} = ${pc3.cyan(String(coerced))}`);
940
1162
  } catch (err) {
941
- console.error(`${pc2.red("\u2717")} ${String(err)}`);
1163
+ console.error(`${pc3.red("\u2717")} ${String(err)}`);
942
1164
  process.exit(1);
943
1165
  }
944
1166
  }
945
1167
  });
946
- var getCommand = defineCommand2({
1168
+ var getCommand = defineCommand3({
947
1169
  meta: { name: "get", description: "Get a config value" },
948
1170
  args: {
949
1171
  key: {
@@ -955,32 +1177,32 @@ var getCommand = defineCommand2({
955
1177
  run({ args }) {
956
1178
  const val = getConfigValue(args.key);
957
1179
  if (val === void 0) {
958
- console.log(pc2.dim(`${args.key}: (not set)`));
1180
+ console.log(pc3.dim(`${args.key}: (not set)`));
959
1181
  } else {
960
- console.log(`${pc2.bold(args.key)}: ${pc2.cyan(String(val))}`);
1182
+ console.log(`${pc3.bold(args.key)}: ${pc3.cyan(String(val))}`);
961
1183
  }
962
1184
  }
963
1185
  });
964
- var listCommand = defineCommand2({
1186
+ var listCommand = defineCommand3({
965
1187
  meta: { name: "list", description: "List all config values" },
966
1188
  run() {
967
1189
  const data = readConfig();
968
1190
  const entries = Object.entries(data);
969
1191
  if (entries.length === 0) {
970
- console.log(pc2.dim("No config values set. Use `mcpman config set <key> <value>`."));
1192
+ console.log(pc3.dim("No config values set. Use `mcpman config set <key> <value>`."));
971
1193
  return;
972
1194
  }
973
1195
  console.log("");
974
- console.log(pc2.bold("mcpman config:"));
1196
+ console.log(pc3.bold("mcpman config:"));
975
1197
  console.log("");
976
1198
  for (const [key, val] of entries) {
977
- console.log(` ${pc2.green("\u25CF")} ${pc2.bold(key)} ${pc2.cyan(String(val))}`);
1199
+ console.log(` ${pc3.green("\u25CF")} ${pc3.bold(key)} ${pc3.cyan(String(val))}`);
978
1200
  }
979
1201
  console.log("");
980
- console.log(pc2.dim(` ${entries.length} key${entries.length !== 1 ? "s" : ""} configured`));
1202
+ console.log(pc3.dim(` ${entries.length} key${entries.length !== 1 ? "s" : ""} configured`));
981
1203
  }
982
1204
  });
983
- var resetCommand = defineCommand2({
1205
+ var resetCommand = defineCommand3({
984
1206
  meta: { name: "reset", description: "Reset config to defaults (removes config file)" },
985
1207
  async run() {
986
1208
  const confirmed = await p2.confirm({
@@ -992,10 +1214,10 @@ var resetCommand = defineCommand2({
992
1214
  return;
993
1215
  }
994
1216
  writeConfig({});
995
- console.log(`${pc2.green("\u2713")} Config reset to defaults.`);
1217
+ console.log(`${pc3.green("\u2713")} Config reset to defaults.`);
996
1218
  }
997
1219
  });
998
- var config_default = defineCommand2({
1220
+ var config_default = defineCommand3({
999
1221
  meta: {
1000
1222
  name: "config",
1001
1223
  description: "Manage mcpman CLI configuration"
@@ -1008,9 +1230,297 @@ var config_default = defineCommand2({
1008
1230
  }
1009
1231
  });
1010
1232
 
1233
+ // src/commands/create.ts
1234
+ import path6 from "path";
1235
+ import { defineCommand as defineCommand4 } from "citty";
1236
+ import pc4 from "picocolors";
1237
+
1238
+ // src/core/scaffold-service.ts
1239
+ import fs5 from "fs";
1240
+ import path5 from "path";
1241
+ function sanitizeName(name) {
1242
+ return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
1243
+ }
1244
+ function generateNodeProject(options) {
1245
+ const { name, description } = options;
1246
+ const packageJson = JSON.stringify(
1247
+ {
1248
+ name,
1249
+ version: "0.1.0",
1250
+ description,
1251
+ type: "module",
1252
+ bin: { [name]: "./dist/index.js" },
1253
+ main: "./dist/index.js",
1254
+ scripts: {
1255
+ build: "tsc",
1256
+ start: "node dist/index.js",
1257
+ dev: "tsx src/index.ts"
1258
+ },
1259
+ mcp: {
1260
+ name,
1261
+ description,
1262
+ transport: "stdio",
1263
+ env: []
1264
+ },
1265
+ dependencies: {
1266
+ "@modelcontextprotocol/sdk": "^1.0.0"
1267
+ },
1268
+ devDependencies: {
1269
+ typescript: "^5.0.0",
1270
+ tsx: "^4.0.0",
1271
+ "@types/node": "^20.0.0"
1272
+ }
1273
+ },
1274
+ null,
1275
+ 2
1276
+ );
1277
+ const tsconfig = JSON.stringify(
1278
+ {
1279
+ compilerOptions: {
1280
+ target: "ES2022",
1281
+ module: "NodeNext",
1282
+ moduleResolution: "NodeNext",
1283
+ outDir: "./dist",
1284
+ rootDir: "./src",
1285
+ strict: true,
1286
+ esModuleInterop: true,
1287
+ skipLibCheck: true
1288
+ },
1289
+ include: ["src"]
1290
+ },
1291
+ null,
1292
+ 2
1293
+ );
1294
+ const indexTs = `#!/usr/bin/env node
1295
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
1296
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
1297
+
1298
+ const server = new Server(
1299
+ { name: "${name}", version: "0.1.0" },
1300
+ { capabilities: { tools: {} } }
1301
+ );
1302
+
1303
+ server.setRequestHandler("tools/list", async () => ({
1304
+ tools: [
1305
+ {
1306
+ name: "hello",
1307
+ description: "Say hello",
1308
+ inputSchema: { type: "object", properties: { name: { type: "string" } } },
1309
+ },
1310
+ ],
1311
+ }));
1312
+
1313
+ server.setRequestHandler("tools/call", async (request) => {
1314
+ if (request.params.name === "hello") {
1315
+ const who = request.params.arguments?.name ?? "world";
1316
+ return { content: [{ type: "text", text: \`Hello, \${who}!\` }] };
1317
+ }
1318
+ throw new Error(\`Unknown tool: \${request.params.name}\`);
1319
+ });
1320
+
1321
+ const transport = new StdioServerTransport();
1322
+ await server.connect(transport);
1323
+ `;
1324
+ return {
1325
+ "package.json": packageJson,
1326
+ "tsconfig.json": tsconfig,
1327
+ "src/index.ts": indexTs
1328
+ };
1329
+ }
1330
+ function generatePythonProject(options) {
1331
+ const { name, description } = options;
1332
+ const pyprojectToml = `[build-system]
1333
+ requires = ["setuptools>=68"]
1334
+ build-backend = "setuptools.backends.legacy:build"
1335
+
1336
+ [project]
1337
+ name = "${name}"
1338
+ version = "0.1.0"
1339
+ description = "${description}"
1340
+ requires-python = ">=3.10"
1341
+ dependencies = ["mcp>=1.0.0"]
1342
+
1343
+ [project.scripts]
1344
+ ${name} = "${name.replace(/-/g, "_")}:main"
1345
+
1346
+ [tool.mcp]
1347
+ name = "${name}"
1348
+ description = "${description}"
1349
+ transport = "stdio"
1350
+ `;
1351
+ const mainPy = `#!/usr/bin/env python3
1352
+ """${description}"""
1353
+ from mcp.server import Server
1354
+ from mcp.server.stdio import stdio_server
1355
+
1356
+ app = Server("${name}")
1357
+
1358
+ @app.list_tools()
1359
+ async def list_tools():
1360
+ return [
1361
+ {
1362
+ "name": "hello",
1363
+ "description": "Say hello",
1364
+ "inputSchema": {
1365
+ "type": "object",
1366
+ "properties": {"name": {"type": "string"}},
1367
+ },
1368
+ }
1369
+ ]
1370
+
1371
+ @app.call_tool()
1372
+ async def call_tool(name: str, arguments: dict):
1373
+ if name == "hello":
1374
+ who = arguments.get("name", "world")
1375
+ return [{"type": "text", "text": f"Hello, {who}!"}]
1376
+ raise ValueError(f"Unknown tool: {name}")
1377
+
1378
+ async def main():
1379
+ async with stdio_server() as (read, write):
1380
+ await app.run(read, write)
1381
+
1382
+ if __name__ == "__main__":
1383
+ import asyncio
1384
+ asyncio.run(main())
1385
+ `;
1386
+ return {
1387
+ "pyproject.toml": pyprojectToml,
1388
+ "main.py": mainPy
1389
+ };
1390
+ }
1391
+ function writeScaffold(dir, files) {
1392
+ if (fs5.existsSync(dir)) {
1393
+ const existing = fs5.readdirSync(dir);
1394
+ if (existing.length > 0) {
1395
+ throw new Error(`Directory '${dir}' already exists and is not empty.`);
1396
+ }
1397
+ }
1398
+ for (const [relativePath, content] of Object.entries(files)) {
1399
+ const fullPath = path5.join(dir, relativePath);
1400
+ const parentDir = path5.dirname(fullPath);
1401
+ fs5.mkdirSync(parentDir, { recursive: true });
1402
+ fs5.writeFileSync(fullPath, content, "utf-8");
1403
+ }
1404
+ }
1405
+
1406
+ // src/commands/create.ts
1407
+ var create_default = defineCommand4({
1408
+ meta: {
1409
+ name: "create",
1410
+ description: "Scaffold a new MCP server project"
1411
+ },
1412
+ args: {
1413
+ name: {
1414
+ type: "positional",
1415
+ description: "Project name",
1416
+ required: false
1417
+ },
1418
+ runtime: {
1419
+ type: "string",
1420
+ description: "Runtime: node or python (default: node)",
1421
+ alias: "r"
1422
+ },
1423
+ description: {
1424
+ type: "string",
1425
+ description: "Project description",
1426
+ alias: "d"
1427
+ },
1428
+ yes: {
1429
+ type: "boolean",
1430
+ description: "Accept all defaults, skip prompts",
1431
+ alias: "y",
1432
+ default: false
1433
+ }
1434
+ },
1435
+ async run({ args }) {
1436
+ let projectName = args.name ?? "";
1437
+ let projectDescription = args.description ?? "";
1438
+ let runtime = args.runtime ?? "node";
1439
+ const acceptDefaults = args.yes;
1440
+ if (!acceptDefaults) {
1441
+ if (!projectName) {
1442
+ const readline = await import("readline");
1443
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1444
+ projectName = await new Promise((resolve) => {
1445
+ rl.question(pc4.cyan(" Project name: "), (answer) => {
1446
+ rl.close();
1447
+ resolve(answer.trim());
1448
+ });
1449
+ });
1450
+ }
1451
+ if (!projectDescription) {
1452
+ const readline = await import("readline");
1453
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1454
+ projectDescription = await new Promise((resolve) => {
1455
+ rl.question(pc4.cyan(" Description (optional): "), (answer) => {
1456
+ rl.close();
1457
+ resolve(answer.trim());
1458
+ });
1459
+ });
1460
+ }
1461
+ if (!args.runtime) {
1462
+ const readline = await import("readline");
1463
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1464
+ const answer = await new Promise((resolve) => {
1465
+ rl.question(pc4.cyan(" Runtime [node/python] (default: node): "), (a) => {
1466
+ rl.close();
1467
+ resolve(a.trim() || "node");
1468
+ });
1469
+ });
1470
+ runtime = answer;
1471
+ }
1472
+ }
1473
+ if (!projectName) {
1474
+ console.error(pc4.red(" Error: Project name is required."));
1475
+ process.exit(1);
1476
+ }
1477
+ const sanitized = sanitizeName(projectName);
1478
+ if (!sanitized) {
1479
+ console.error(pc4.red(` Error: Invalid project name '${projectName}'.`));
1480
+ process.exit(1);
1481
+ }
1482
+ if (runtime !== "node" && runtime !== "python") {
1483
+ console.error(pc4.red(` Error: Unknown runtime '${runtime}'. Use node or python.`));
1484
+ process.exit(1);
1485
+ }
1486
+ const options = {
1487
+ name: sanitized,
1488
+ description: projectDescription || `${sanitized} MCP server`,
1489
+ runtime,
1490
+ transport: "stdio"
1491
+ };
1492
+ const files = runtime === "python" ? generatePythonProject(options) : generateNodeProject(options);
1493
+ const targetDir = path6.resolve(sanitized);
1494
+ try {
1495
+ writeScaffold(targetDir, files);
1496
+ } catch (err) {
1497
+ console.error(pc4.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
1498
+ process.exit(1);
1499
+ }
1500
+ console.log(pc4.green(`
1501
+ Created ${pc4.bold(sanitized)}/
1502
+ `));
1503
+ console.log(pc4.dim(" Files generated:"));
1504
+ for (const file of Object.keys(files)) {
1505
+ console.log(` ${pc4.cyan(file)}`);
1506
+ }
1507
+ console.log("\n Next steps:");
1508
+ if (runtime === "node") {
1509
+ console.log(` ${pc4.bold(`cd ${sanitized}`)}`);
1510
+ console.log(` ${pc4.bold("npm install")}`);
1511
+ console.log(` ${pc4.bold("mcpman link .")}`);
1512
+ } else {
1513
+ console.log(` ${pc4.bold(`cd ${sanitized}`)}`);
1514
+ console.log(` ${pc4.bold("pip install -e .")}`);
1515
+ console.log(` ${pc4.bold("mcpman link .")}`);
1516
+ }
1517
+ console.log();
1518
+ }
1519
+ });
1520
+
1011
1521
  // src/commands/doctor.ts
1012
- import { defineCommand as defineCommand3 } from "citty";
1013
- import pc3 from "picocolors";
1522
+ import { defineCommand as defineCommand5 } from "citty";
1523
+ import pc5 from "picocolors";
1014
1524
 
1015
1525
  // src/core/diagnostics.ts
1016
1526
  import { exec } from "child_process";
@@ -1355,12 +1865,12 @@ async function getInstalledServers(clientFilter) {
1355
1865
 
1356
1866
  // src/commands/doctor.ts
1357
1867
  var CHECK_ICON = {
1358
- pass: pc3.green("\u2713"),
1359
- fail: pc3.red("\u2717"),
1360
- skip: pc3.dim("-"),
1361
- warn: pc3.yellow("\u26A0")
1868
+ pass: pc5.green("\u2713"),
1869
+ fail: pc5.red("\u2717"),
1870
+ skip: pc5.dim("-"),
1871
+ warn: pc5.yellow("\u26A0")
1362
1872
  };
1363
- var doctor_default = defineCommand3({
1873
+ var doctor_default = defineCommand5({
1364
1874
  meta: {
1365
1875
  name: "doctor",
1366
1876
  description: "Check MCP server health and configuration"
@@ -1373,11 +1883,11 @@ var doctor_default = defineCommand3({
1373
1883
  }
1374
1884
  },
1375
1885
  async run({ args }) {
1376
- console.log(pc3.bold("\n mcpman doctor\n"));
1886
+ console.log(pc5.bold("\n mcpman doctor\n"));
1377
1887
  const servers = await getInstalledServers();
1378
1888
  if (servers.length === 0) {
1379
1889
  console.log(
1380
- pc3.dim(" No MCP servers installed. Run mcpman install <server> to get started.")
1890
+ pc5.dim(" No MCP servers installed. Run mcpman install <server> to get started.")
1381
1891
  );
1382
1892
  return;
1383
1893
  }
@@ -1394,20 +1904,20 @@ var doctor_default = defineCommand3({
1394
1904
  if (pluginSummary.total > 0) {
1395
1905
  printPluginSection(pluginSummary);
1396
1906
  }
1397
- console.log(pc3.dim(` ${"\u2500".repeat(50)}`));
1907
+ console.log(pc5.dim(` ${"\u2500".repeat(50)}`));
1398
1908
  const parts = [];
1399
- if (passed > 0) parts.push(pc3.green(`${passed} healthy`));
1400
- if (failed > 0) parts.push(pc3.red(`${failed} unhealthy`));
1909
+ if (passed > 0) parts.push(pc5.green(`${passed} healthy`));
1910
+ if (failed > 0) parts.push(pc5.red(`${failed} unhealthy`));
1401
1911
  console.log(` Summary: ${parts.join(", ")}`);
1402
1912
  if (pluginSummary.total > 0) {
1403
1913
  const pParts = [];
1404
- if (pluginSummary.healthy > 0) pParts.push(pc3.green(`${pluginSummary.healthy} ok`));
1405
- if (pluginSummary.unhealthy > 0) pParts.push(pc3.red(`${pluginSummary.unhealthy} broken`));
1914
+ if (pluginSummary.healthy > 0) pParts.push(pc5.green(`${pluginSummary.healthy} ok`));
1915
+ if (pluginSummary.unhealthy > 0) pParts.push(pc5.red(`${pluginSummary.unhealthy} broken`));
1406
1916
  console.log(` Plugins: ${pParts.join(", ")}`);
1407
1917
  }
1408
1918
  if (failed > 0 || pluginSummary.unhealthy > 0) {
1409
1919
  if (!args.fix) {
1410
- console.log(pc3.dim(` Run ${pc3.cyan("mcpman doctor --fix")} for fix suggestions.
1920
+ console.log(pc5.dim(` Run ${pc5.cyan("mcpman doctor --fix")} for fix suggestions.
1411
1921
  `));
1412
1922
  }
1413
1923
  process.exit(1);
@@ -1416,26 +1926,26 @@ var doctor_default = defineCommand3({
1416
1926
  }
1417
1927
  });
1418
1928
  function printPluginSection(summary) {
1419
- console.log(pc3.bold(` Plugins (${summary.total})`));
1929
+ console.log(pc5.bold(` Plugins (${summary.total})`));
1420
1930
  for (const r of summary.results) {
1421
- const icon = r.loadable && r.prefixUnique && r.resolvable ? pc3.green("\u2713") : pc3.red("\u2717");
1931
+ const icon = r.loadable && r.prefixUnique && r.resolvable ? pc5.green("\u2713") : pc5.red("\u2717");
1422
1932
  const label = r.pluginName ? `${r.packageName} (${r.pluginName})` : r.packageName;
1423
- const prefix = r.prefix ? pc3.dim(` [${r.prefix}]`) : "";
1933
+ const prefix = r.prefix ? pc5.dim(` [${r.prefix}]`) : "";
1424
1934
  console.log(` ${icon} ${label}${prefix}`);
1425
1935
  if (r.error) {
1426
- console.log(` ${pc3.yellow("\u2192")} ${r.error}`);
1936
+ console.log(` ${pc5.yellow("\u2192")} ${r.error}`);
1427
1937
  }
1428
1938
  }
1429
1939
  console.log();
1430
1940
  }
1431
1941
  function printServerResult(result, showFix) {
1432
- const icon = result.status === "healthy" ? pc3.green("\u25CF") : pc3.red("\u25CF");
1433
- console.log(` ${icon} ${pc3.bold(result.serverName)}`);
1942
+ const icon = result.status === "healthy" ? pc5.green("\u25CF") : pc5.red("\u25CF");
1943
+ console.log(` ${icon} ${pc5.bold(result.serverName)}`);
1434
1944
  for (const check of result.checks) {
1435
1945
  const checkIcon = check.skipped ? CHECK_ICON.skip : check.passed ? CHECK_ICON.pass : CHECK_ICON.fail;
1436
1946
  console.log(` ${checkIcon} ${check.name}: ${check.message}`);
1437
1947
  if (showFix && !check.passed && !check.skipped && check.fix) {
1438
- console.log(` ${pc3.yellow("\u2192")} Fix: ${pc3.cyan(check.fix)}`);
1948
+ console.log(` ${pc5.yellow("\u2192")} Fix: ${pc5.cyan(check.fix)}`);
1439
1949
  }
1440
1950
  }
1441
1951
  console.log();
@@ -1458,17 +1968,17 @@ async function runParallel(tasks, concurrency) {
1458
1968
  }
1459
1969
 
1460
1970
  // src/commands/export-command.ts
1461
- import fs6 from "fs";
1462
- import path5 from "path";
1463
- import { defineCommand as defineCommand4 } from "citty";
1464
- import pc4 from "picocolors";
1971
+ import fs7 from "fs";
1972
+ import path7 from "path";
1973
+ import { defineCommand as defineCommand6 } from "citty";
1974
+ import pc6 from "picocolors";
1465
1975
 
1466
1976
  // src/core/export-import-service.ts
1467
- import fs5 from "fs";
1977
+ import fs6 from "fs";
1468
1978
 
1469
1979
  // src/utils/constants.ts
1470
1980
  var APP_NAME = "mcpman";
1471
- var APP_VERSION = "0.6.0";
1981
+ var APP_VERSION = "0.7.0";
1472
1982
  var APP_DESCRIPTION = "The package manager for MCP servers";
1473
1983
 
1474
1984
  // src/core/export-import-service.ts
@@ -1484,7 +1994,7 @@ function createExportBundle(opts = {}) {
1484
1994
  };
1485
1995
  if (includeVault) {
1486
1996
  const vaultPath = getVaultPath();
1487
- if (fs5.existsSync(vaultPath)) {
1997
+ if (fs6.existsSync(vaultPath)) {
1488
1998
  bundle.vault = readVault();
1489
1999
  }
1490
2000
  }
@@ -1550,7 +2060,7 @@ function importBundle(bundle, opts = {}) {
1550
2060
 
1551
2061
  // src/commands/export-command.ts
1552
2062
  var DEFAULT_OUTPUT = "mcpman-export.json";
1553
- var export_command_default = defineCommand4({
2063
+ var export_command_default = defineCommand6({
1554
2064
  meta: {
1555
2065
  name: "export",
1556
2066
  description: "Export mcpman config, lockfile, vault, and plugins to a portable JSON file"
@@ -1574,29 +2084,29 @@ var export_command_default = defineCommand4({
1574
2084
  },
1575
2085
  run({ args }) {
1576
2086
  const outputFile = args.output || DEFAULT_OUTPUT;
1577
- const outputPath = path5.resolve(outputFile);
2087
+ const outputPath = path7.resolve(outputFile);
1578
2088
  const bundle = createExportBundle({
1579
2089
  includeVault: !args["no-vault"],
1580
2090
  includePlugins: !args["no-plugins"]
1581
2091
  });
1582
2092
  const serverCount = Object.keys(bundle.lockfile.servers).length;
1583
2093
  const configKeys = Object.keys(bundle.config).length;
1584
- fs6.writeFileSync(outputPath, JSON.stringify(bundle, null, 2), "utf-8");
1585
- console.log(`${pc4.green("\u2713")} Exported to ${pc4.bold(outputFile)}`);
1586
- console.log(pc4.dim(` Config keys: ${configKeys}`));
1587
- console.log(pc4.dim(` Servers: ${serverCount}`));
1588
- console.log(pc4.dim(` Vault: ${bundle.vault ? "included" : "excluded"}`));
1589
- console.log(pc4.dim(` Plugins: ${bundle.plugins?.length ?? 0}`));
2094
+ fs7.writeFileSync(outputPath, JSON.stringify(bundle, null, 2), "utf-8");
2095
+ console.log(`${pc6.green("\u2713")} Exported to ${pc6.bold(outputFile)}`);
2096
+ console.log(pc6.dim(` Config keys: ${configKeys}`));
2097
+ console.log(pc6.dim(` Servers: ${serverCount}`));
2098
+ console.log(pc6.dim(` Vault: ${bundle.vault ? "included" : "excluded"}`));
2099
+ console.log(pc6.dim(` Plugins: ${bundle.plugins?.length ?? 0}`));
1590
2100
  }
1591
2101
  });
1592
2102
 
1593
2103
  // src/commands/import-command.ts
1594
- import fs7 from "fs";
1595
- import path6 from "path";
2104
+ import fs8 from "fs";
2105
+ import path8 from "path";
1596
2106
  import * as p3 from "@clack/prompts";
1597
- import { defineCommand as defineCommand5 } from "citty";
1598
- import pc5 from "picocolors";
1599
- var import_command_default = defineCommand5({
2107
+ import { defineCommand as defineCommand7 } from "citty";
2108
+ import pc7 from "picocolors";
2109
+ var import_command_default = defineCommand7({
1600
2110
  meta: {
1601
2111
  name: "import",
1602
2112
  description: "Import mcpman config, lockfile, vault, and plugins from an export file"
@@ -1619,21 +2129,21 @@ var import_command_default = defineCommand5({
1619
2129
  }
1620
2130
  },
1621
2131
  async run({ args }) {
1622
- const filePath = path6.resolve(args.file);
1623
- if (!fs7.existsSync(filePath)) {
1624
- console.error(`${pc5.red("\u2717")} File not found: ${filePath}`);
2132
+ const filePath = path8.resolve(args.file);
2133
+ if (!fs8.existsSync(filePath)) {
2134
+ console.error(`${pc7.red("\u2717")} File not found: ${filePath}`);
1625
2135
  process.exit(1);
1626
2136
  }
1627
2137
  let raw;
1628
2138
  try {
1629
- raw = JSON.parse(fs7.readFileSync(filePath, "utf-8"));
2139
+ raw = JSON.parse(fs8.readFileSync(filePath, "utf-8"));
1630
2140
  } catch {
1631
- console.error(`${pc5.red("\u2717")} Invalid JSON in ${filePath}`);
2141
+ console.error(`${pc7.red("\u2717")} Invalid JSON in ${filePath}`);
1632
2142
  process.exit(1);
1633
2143
  }
1634
2144
  const error2 = validateBundle(raw);
1635
2145
  if (error2) {
1636
- console.error(`${pc5.red("\u2717")} Invalid export bundle: ${error2}`);
2146
+ console.error(`${pc7.red("\u2717")} Invalid export bundle: ${error2}`);
1637
2147
  process.exit(1);
1638
2148
  }
1639
2149
  const bundle = raw;
@@ -1643,16 +2153,16 @@ var import_command_default = defineCommand5({
1643
2153
  const hasVault = !!bundle.vault;
1644
2154
  const isDryRun = !!args["dry-run"];
1645
2155
  console.log("");
1646
- console.log(pc5.bold("Import summary:"));
1647
- console.log(pc5.dim(` Source version: mcpman ${bundle.mcpmanVersion}`));
1648
- console.log(pc5.dim(` Exported at: ${bundle.exportedAt}`));
1649
- console.log(` Config keys: ${pc5.cyan(String(configKeys))}`);
1650
- console.log(` Servers: ${pc5.cyan(String(serverCount))}`);
1651
- console.log(` Vault: ${hasVault ? pc5.green("included") : pc5.dim("not included")}`);
1652
- console.log(` Plugins: ${pc5.cyan(String(pluginCount))}`);
2156
+ console.log(pc7.bold("Import summary:"));
2157
+ console.log(pc7.dim(` Source version: mcpman ${bundle.mcpmanVersion}`));
2158
+ console.log(pc7.dim(` Exported at: ${bundle.exportedAt}`));
2159
+ console.log(` Config keys: ${pc7.cyan(String(configKeys))}`);
2160
+ console.log(` Servers: ${pc7.cyan(String(serverCount))}`);
2161
+ console.log(` Vault: ${hasVault ? pc7.green("included") : pc7.dim("not included")}`);
2162
+ console.log(` Plugins: ${pc7.cyan(String(pluginCount))}`);
1653
2163
  console.log("");
1654
2164
  if (isDryRun) {
1655
- console.log(pc5.yellow(" [dry-run] No changes applied."));
2165
+ console.log(pc7.yellow(" [dry-run] No changes applied."));
1656
2166
  return;
1657
2167
  }
1658
2168
  if (!args.yes) {
@@ -1666,18 +2176,18 @@ var import_command_default = defineCommand5({
1666
2176
  }
1667
2177
  }
1668
2178
  const summary = importBundle(bundle, { dryRun: false });
1669
- console.log(`${pc5.green("\u2713")} Import complete`);
1670
- console.log(pc5.dim(` Config keys restored: ${summary.configKeys}`));
1671
- console.log(pc5.dim(` Servers restored: ${summary.servers}`));
1672
- console.log(pc5.dim(` Vault: ${summary.vaultImported ? "restored" : "skipped"}`));
1673
- console.log(pc5.dim(` Plugins installed: ${summary.pluginsInstalled}`));
2179
+ console.log(`${pc7.green("\u2713")} Import complete`);
2180
+ console.log(pc7.dim(` Config keys restored: ${summary.configKeys}`));
2181
+ console.log(pc7.dim(` Servers restored: ${summary.servers}`));
2182
+ console.log(pc7.dim(` Vault: ${summary.vaultImported ? "restored" : "skipped"}`));
2183
+ console.log(pc7.dim(` Plugins installed: ${summary.pluginsInstalled}`));
1674
2184
  }
1675
2185
  });
1676
2186
 
1677
2187
  // src/commands/info.ts
1678
- import { defineCommand as defineCommand6 } from "citty";
2188
+ import { defineCommand as defineCommand8 } from "citty";
1679
2189
  import { createSpinner as createSpinner2 } from "nanospinner";
1680
- import pc6 from "picocolors";
2190
+ import pc8 from "picocolors";
1681
2191
 
1682
2192
  // src/core/package-info.ts
1683
2193
  async function buildInfo(name, entry, source = "npm") {
@@ -1732,11 +2242,11 @@ async function getPackageInfo(serverName) {
1732
2242
  // src/commands/info.ts
1733
2243
  function colorRisk2(score, riskLevel) {
1734
2244
  const label = score !== null ? `${score}/100 (${riskLevel})` : riskLevel;
1735
- if (riskLevel === "LOW") return pc6.green(label);
1736
- if (riskLevel === "MEDIUM") return pc6.yellow(label);
1737
- if (riskLevel === "HIGH") return pc6.red(label);
1738
- if (riskLevel === "CRITICAL") return pc6.bold(pc6.red(label));
1739
- return pc6.dim(label);
2245
+ if (riskLevel === "LOW") return pc8.green(label);
2246
+ if (riskLevel === "MEDIUM") return pc8.yellow(label);
2247
+ if (riskLevel === "HIGH") return pc8.red(label);
2248
+ if (riskLevel === "CRITICAL") return pc8.bold(pc8.red(label));
2249
+ return pc8.dim(label);
1740
2250
  }
1741
2251
  function formatDaysAgo(isoDate) {
1742
2252
  if (!isoDate) return "unknown";
@@ -1746,54 +2256,54 @@ function formatDaysAgo(isoDate) {
1746
2256
  return `${days} days ago`;
1747
2257
  }
1748
2258
  function printInfo(info2) {
1749
- const installedBadge = info2.isInstalled ? pc6.green(" [installed]") : pc6.dim(" [not installed]");
2259
+ const installedBadge = info2.isInstalled ? pc8.green(" [installed]") : pc8.dim(" [not installed]");
1750
2260
  console.log();
1751
- console.log(pc6.bold(` ${info2.name}@${info2.version}`) + installedBadge);
1752
- console.log(pc6.dim(` ${"\u2500".repeat(60)}`));
1753
- console.log(` ${pc6.dim("Source:")} ${info2.source}`);
1754
- console.log(` ${pc6.dim("Runtime:")} ${info2.runtime}`);
2261
+ console.log(pc8.bold(` ${info2.name}@${info2.version}`) + installedBadge);
2262
+ console.log(pc8.dim(` ${"\u2500".repeat(60)}`));
2263
+ console.log(` ${pc8.dim("Source:")} ${info2.source}`);
2264
+ console.log(` ${pc8.dim("Runtime:")} ${info2.runtime}`);
1755
2265
  if (info2.description) {
1756
- console.log(` ${pc6.dim("Description:")} ${info2.description}`);
2266
+ console.log(` ${pc8.dim("Description:")} ${info2.description}`);
1757
2267
  }
1758
2268
  if (info2.deprecated) {
1759
- console.log(` ${pc6.red("[DEPRECATED]")} This package is deprecated`);
2269
+ console.log(` ${pc8.red("[DEPRECATED]")} This package is deprecated`);
1760
2270
  }
1761
2271
  console.log();
1762
- console.log(` ${pc6.bold("Trust & Security")}`);
1763
- console.log(` ${pc6.dim("Trust score:")} ${colorRisk2(info2.trustScore, info2.riskLevel)}`);
2272
+ console.log(` ${pc8.bold("Trust & Security")}`);
2273
+ console.log(` ${pc8.dim("Trust score:")} ${colorRisk2(info2.trustScore, info2.riskLevel)}`);
1764
2274
  if (info2.source === "npm") {
1765
2275
  console.log(
1766
- ` ${pc6.dim("Downloads:")} ${info2.weeklyDownloads.toLocaleString()}/week ${pc6.dim("|")} ${pc6.dim("Age:")} ${info2.packageAge}d ${pc6.dim("|")} ${pc6.dim("Maintainers:")} ${info2.maintainerCount}`
2276
+ ` ${pc8.dim("Downloads:")} ${info2.weeklyDownloads.toLocaleString()}/week ${pc8.dim("|")} ${pc8.dim("Age:")} ${info2.packageAge}d ${pc8.dim("|")} ${pc8.dim("Maintainers:")} ${info2.maintainerCount}`
1767
2277
  );
1768
2278
  if (info2.lastPublish) {
1769
- console.log(` ${pc6.dim("Last publish:")} ${formatDaysAgo(info2.lastPublish)}`);
2279
+ console.log(` ${pc8.dim("Last publish:")} ${formatDaysAgo(info2.lastPublish)}`);
1770
2280
  }
1771
2281
  } else {
1772
- console.log(pc6.dim(" (Trust data available for npm packages only)"));
2282
+ console.log(pc8.dim(" (Trust data available for npm packages only)"));
1773
2283
  }
1774
2284
  console.log();
1775
- console.log(` ${pc6.bold("Environment Variables")}`);
2285
+ console.log(` ${pc8.bold("Environment Variables")}`);
1776
2286
  if (info2.envVars.length > 0) {
1777
2287
  for (const env of info2.envVars) {
1778
- console.log(` ${pc6.cyan("\u2022")} ${env}`);
2288
+ console.log(` ${pc8.cyan("\u2022")} ${env}`);
1779
2289
  }
1780
2290
  } else {
1781
- console.log(pc6.dim(" none required"));
2291
+ console.log(pc8.dim(" none required"));
1782
2292
  }
1783
2293
  console.log();
1784
- console.log(` ${pc6.bold("Installed Clients")}`);
2294
+ console.log(` ${pc8.bold("Installed Clients")}`);
1785
2295
  if (info2.installedClients.length > 0) {
1786
2296
  for (const client of info2.installedClients) {
1787
- console.log(` ${pc6.green("\u2713")} ${client}`);
2297
+ console.log(` ${pc8.green("\u2713")} ${client}`);
1788
2298
  }
1789
2299
  } else {
1790
- console.log(pc6.dim(" Not installed in any client"));
2300
+ console.log(pc8.dim(" Not installed in any client"));
1791
2301
  }
1792
2302
  console.log();
1793
- console.log(pc6.dim(` ${"\u2500".repeat(60)}`));
2303
+ console.log(pc8.dim(` ${"\u2500".repeat(60)}`));
1794
2304
  console.log();
1795
2305
  }
1796
- var info_default = defineCommand6({
2306
+ var info_default = defineCommand8({
1797
2307
  meta: {
1798
2308
  name: "info",
1799
2309
  description: "Show detailed metadata for an MCP server (installed or from registry)"
@@ -1817,13 +2327,13 @@ var info_default = defineCommand6({
1817
2327
  info2 = await getPackageInfo(args.server);
1818
2328
  } catch (err) {
1819
2329
  spinner5.error({ text: "Failed to fetch package info" });
1820
- console.error(pc6.red(String(err)));
2330
+ console.error(pc8.red(String(err)));
1821
2331
  process.exit(1);
1822
2332
  }
1823
2333
  if (!info2) {
1824
2334
  spinner5.error({ text: `Package not found: ${args.server}` });
1825
2335
  console.log(
1826
- pc6.dim(`
2336
+ pc8.dim(`
1827
2337
  "${args.server}" was not found in the npm registry or your lockfile.
1828
2338
  `)
1829
2339
  );
@@ -1839,10 +2349,10 @@ var info_default = defineCommand6({
1839
2349
  });
1840
2350
 
1841
2351
  // src/commands/init.ts
1842
- import path7 from "path";
2352
+ import path9 from "path";
1843
2353
  import * as p4 from "@clack/prompts";
1844
- import { defineCommand as defineCommand7 } from "citty";
1845
- var init_default = defineCommand7({
2354
+ import { defineCommand as defineCommand9 } from "citty";
2355
+ var init_default = defineCommand9({
1846
2356
  meta: {
1847
2357
  name: "init",
1848
2358
  description: "Initialize mcpman.lock in the current project"
@@ -1858,7 +2368,7 @@ var init_default = defineCommand7({
1858
2368
  async run({ args }) {
1859
2369
  const nonInteractive = args.yes || !process.stdout.isTTY;
1860
2370
  p4.intro("mcpman init");
1861
- const targetPath = path7.join(process.cwd(), LOCKFILE_NAME);
2371
+ const targetPath = path9.join(process.cwd(), LOCKFILE_NAME);
1862
2372
  const existing = findLockfile();
1863
2373
  if (existing) {
1864
2374
  if (nonInteractive) {
@@ -1942,7 +2452,7 @@ var init_default = defineCommand7({
1942
2452
 
1943
2453
  // src/commands/install.ts
1944
2454
  import * as p7 from "@clack/prompts";
1945
- import { defineCommand as defineCommand8 } from "citty";
2455
+ import { defineCommand as defineCommand10 } from "citty";
1946
2456
 
1947
2457
  // src/core/installer.ts
1948
2458
  import * as p6 from "@clack/prompts";
@@ -2094,7 +2604,7 @@ async function installServer(input, options = {}) {
2094
2604
  }
2095
2605
 
2096
2606
  // src/utils/logger.ts
2097
- import pc7 from "picocolors";
2607
+ import pc9 from "picocolors";
2098
2608
  var noColor = process.env.NO_COLOR !== void 0 || process.argv.includes("--no-color");
2099
2609
  var isVerbose = process.argv.includes("--verbose");
2100
2610
  var isJson = process.argv.includes("--json");
@@ -2103,18 +2613,18 @@ function colorize(fn, text2) {
2103
2613
  }
2104
2614
  function info(message) {
2105
2615
  if (isJson) return;
2106
- console.log(`${colorize(pc7.cyan, "i")} ${message}`);
2616
+ console.log(`${colorize(pc9.cyan, "i")} ${message}`);
2107
2617
  }
2108
2618
  function error(message) {
2109
2619
  if (isJson) return;
2110
- console.error(`${colorize(pc7.red, "\u2717")} ${message}`);
2620
+ console.error(`${colorize(pc9.red, "\u2717")} ${message}`);
2111
2621
  }
2112
2622
  function json(data) {
2113
2623
  console.log(JSON.stringify(data, null, 2));
2114
2624
  }
2115
2625
 
2116
2626
  // src/commands/install.ts
2117
- var install_default = defineCommand8({
2627
+ var install_default = defineCommand10({
2118
2628
  meta: {
2119
2629
  name: "install",
2120
2630
  description: "Install an MCP server into one or more AI clients"
@@ -2175,28 +2685,228 @@ async function restoreFromLockfile() {
2175
2685
  p7.outro("Restore complete.");
2176
2686
  }
2177
2687
 
2178
- // src/commands/list.ts
2179
- import { defineCommand as defineCommand9 } from "citty";
2180
- import pc8 from "picocolors";
2181
- var STATUS_ICON = {
2182
- healthy: pc8.green("\u25CF"),
2183
- unhealthy: pc8.red("\u25CF"),
2184
- unknown: pc8.dim("\u25CB")
2185
- };
2186
- var list_default = defineCommand9({
2187
- meta: {
2188
- name: "list",
2189
- description: "List installed MCP servers"
2190
- },
2191
- args: {
2192
- client: {
2193
- type: "string",
2194
- description: "Filter by client (claude, cursor, vscode, windsurf)"
2195
- },
2196
- json: {
2197
- type: "boolean",
2198
- description: "Output as JSON",
2199
- default: false
2688
+ // src/commands/link.ts
2689
+ import path11 from "path";
2690
+ import { defineCommand as defineCommand11 } from "citty";
2691
+ import pc10 from "picocolors";
2692
+
2693
+ // src/core/link-service.ts
2694
+ import fs9 from "fs";
2695
+ import path10 from "path";
2696
+ function detectLocalServer(dir) {
2697
+ if (!fs9.existsSync(dir)) {
2698
+ throw new Error(`Directory does not exist: ${dir}`);
2699
+ }
2700
+ const pkgPath = path10.join(dir, "package.json");
2701
+ if (fs9.existsSync(pkgPath)) {
2702
+ return detectNodeServer(dir, pkgPath);
2703
+ }
2704
+ const pyprojectPath = path10.join(dir, "pyproject.toml");
2705
+ if (fs9.existsSync(pyprojectPath)) {
2706
+ return detectPythonServer(dir, pyprojectPath);
2707
+ }
2708
+ throw new Error(
2709
+ `No package.json or pyproject.toml found in '${dir}'. Is this an MCP server project?`
2710
+ );
2711
+ }
2712
+ function detectNodeServer(dir, pkgPath) {
2713
+ const raw = fs9.readFileSync(pkgPath, "utf-8");
2714
+ const pkg = JSON.parse(raw);
2715
+ const name = String(pkg.name ?? path10.basename(dir));
2716
+ const version = String(pkg.version ?? "0.0.0");
2717
+ let entryPoint = null;
2718
+ if (pkg.bin) {
2719
+ if (typeof pkg.bin === "string") {
2720
+ entryPoint = path10.resolve(dir, pkg.bin);
2721
+ } else if (typeof pkg.bin === "object" && pkg.bin !== null) {
2722
+ const binObj = pkg.bin;
2723
+ const firstBin = Object.values(binObj)[0];
2724
+ if (firstBin) entryPoint = path10.resolve(dir, firstBin);
2725
+ }
2726
+ }
2727
+ if (!entryPoint && pkg.main) {
2728
+ entryPoint = path10.resolve(dir, String(pkg.main));
2729
+ }
2730
+ if (!entryPoint) {
2731
+ const candidates = ["src/index.ts", "src/index.js", "index.ts", "index.js"];
2732
+ for (const c of candidates) {
2733
+ const candidate = path10.join(dir, c);
2734
+ if (fs9.existsSync(candidate)) {
2735
+ entryPoint = candidate;
2736
+ break;
2737
+ }
2738
+ }
2739
+ }
2740
+ if (!entryPoint) {
2741
+ throw new Error(`Cannot determine entry point for Node server in '${dir}'.`);
2742
+ }
2743
+ const isTs = entryPoint.endsWith(".ts");
2744
+ const command = isTs ? "npx" : "node";
2745
+ const args = isTs ? ["tsx", entryPoint] : [entryPoint];
2746
+ const mcpField = pkg.mcp;
2747
+ const envVars = [];
2748
+ if (mcpField?.env && Array.isArray(mcpField.env)) {
2749
+ for (const e of mcpField.env) {
2750
+ if (typeof e === "string") envVars.push(e);
2751
+ else if (typeof e === "object" && e !== null && "name" in e) {
2752
+ envVars.push(String(e.name));
2753
+ }
2754
+ }
2755
+ }
2756
+ return { name, version, command, args, envVars, absolutePath: dir, runtime: "node" };
2757
+ }
2758
+ function detectPythonServer(dir, pyprojectPath) {
2759
+ const raw = fs9.readFileSync(pyprojectPath, "utf-8");
2760
+ const name = extractTomlValue(raw, "name") ?? path10.basename(dir);
2761
+ const version = extractTomlValue(raw, "version") ?? "0.0.0";
2762
+ let pythonCmd = "python3";
2763
+ const venvPython = path10.join(dir, ".venv", "bin", "python");
2764
+ if (fs9.existsSync(venvPython)) {
2765
+ pythonCmd = venvPython;
2766
+ }
2767
+ const entryCandidate = [
2768
+ path10.join(dir, "main.py"),
2769
+ path10.join(dir, name.replace(/-/g, "_"), "main.py"),
2770
+ path10.join(dir, "__main__.py")
2771
+ ].find((p12) => fs9.existsSync(p12));
2772
+ const entryPoint = entryCandidate ?? path10.join(dir, "main.py");
2773
+ return {
2774
+ name,
2775
+ version,
2776
+ command: pythonCmd,
2777
+ args: [entryPoint],
2778
+ envVars: [],
2779
+ absolutePath: dir,
2780
+ runtime: "python"
2781
+ };
2782
+ }
2783
+ function extractTomlValue(content, key) {
2784
+ const match = content.match(new RegExp(`^${key}\\s*=\\s*"([^"]+)"`, "m"));
2785
+ return match ? match[1] : null;
2786
+ }
2787
+ async function registerLinkedServer(linkResult, clients, lockfilePath, nameOverride) {
2788
+ const serverName = nameOverride ?? linkResult.name;
2789
+ const registered = [];
2790
+ for (const client of clients) {
2791
+ try {
2792
+ await client.addServer(serverName, {
2793
+ command: linkResult.command,
2794
+ args: linkResult.args
2795
+ });
2796
+ registered.push(client.type);
2797
+ } catch {
2798
+ }
2799
+ }
2800
+ const lockEntry = {
2801
+ version: linkResult.version,
2802
+ source: "local",
2803
+ resolved: linkResult.absolutePath,
2804
+ integrity: "local",
2805
+ runtime: linkResult.runtime,
2806
+ command: linkResult.command,
2807
+ args: linkResult.args,
2808
+ envVars: linkResult.envVars,
2809
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
2810
+ clients: registered
2811
+ };
2812
+ const existing = readLockfile(lockfilePath);
2813
+ existing.servers[serverName] = lockEntry;
2814
+ const { writeLockfile: writeLockfile2 } = await import("./lockfile-RBA7HB24.js");
2815
+ writeLockfile2(existing, lockfilePath);
2816
+ return registered;
2817
+ }
2818
+
2819
+ // src/commands/link.ts
2820
+ var link_default = defineCommand11({
2821
+ meta: {
2822
+ name: "link",
2823
+ description: "Register a local MCP server directory with AI clients"
2824
+ },
2825
+ args: {
2826
+ dir: {
2827
+ type: "positional",
2828
+ description: "Path to local MCP server directory (default: .)",
2829
+ required: false
2830
+ },
2831
+ client: {
2832
+ type: "string",
2833
+ description: "Register with specific client only (claude-desktop, cursor, vscode, windsurf)",
2834
+ alias: "c"
2835
+ },
2836
+ name: {
2837
+ type: "string",
2838
+ description: "Override the detected server name",
2839
+ alias: "n"
2840
+ }
2841
+ },
2842
+ async run({ args }) {
2843
+ const dirArg = args.dir ?? ".";
2844
+ const clientFilter = args.client;
2845
+ const nameOverride = args.name;
2846
+ const absoluteDir = path11.resolve(dirArg);
2847
+ let linkResult;
2848
+ try {
2849
+ linkResult = detectLocalServer(absoluteDir);
2850
+ } catch (err) {
2851
+ console.error(pc10.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
2852
+ process.exit(1);
2853
+ }
2854
+ const serverName = nameOverride ?? linkResult.name;
2855
+ console.log(pc10.dim(`
2856
+ Detected: ${pc10.cyan(serverName)} (${linkResult.runtime})`));
2857
+ console.log(pc10.dim(` Path: ${absoluteDir}`));
2858
+ console.log(pc10.dim(` Command: ${linkResult.command} ${linkResult.args.join(" ")}`));
2859
+ const allClients = await getInstalledClients();
2860
+ const clients = clientFilter ? allClients.filter((c) => c.type === clientFilter) : allClients;
2861
+ if (clientFilter && clients.length === 0) {
2862
+ console.error(pc10.red(` Error: Unknown client '${clientFilter}'.`));
2863
+ process.exit(1);
2864
+ }
2865
+ let registered;
2866
+ try {
2867
+ registered = await registerLinkedServer(linkResult, clients, void 0, nameOverride);
2868
+ } catch (err) {
2869
+ console.error(pc10.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
2870
+ process.exit(1);
2871
+ }
2872
+ if (registered.length === 0) {
2873
+ console.log(pc10.yellow(" Warning: No clients registered. Are any AI clients installed?"));
2874
+ console.log(pc10.dim(` Server saved to lockfile with source "local".`));
2875
+ } else {
2876
+ console.log(pc10.green(`
2877
+ Linked ${pc10.bold(serverName)} to: ${registered.join(", ")}
2878
+ `));
2879
+ console.log(pc10.dim(` Run ${pc10.cyan("mcpman list")} to verify.`));
2880
+ console.log(
2881
+ pc10.dim(` Run ${pc10.cyan(`mcpman watch ${serverName}`)} to start with auto-restart.`)
2882
+ );
2883
+ }
2884
+ console.log();
2885
+ }
2886
+ });
2887
+
2888
+ // src/commands/list.ts
2889
+ import { defineCommand as defineCommand12 } from "citty";
2890
+ import pc11 from "picocolors";
2891
+ var STATUS_ICON = {
2892
+ healthy: pc11.green("\u25CF"),
2893
+ unhealthy: pc11.red("\u25CF"),
2894
+ unknown: pc11.dim("\u25CB")
2895
+ };
2896
+ var list_default = defineCommand12({
2897
+ meta: {
2898
+ name: "list",
2899
+ description: "List installed MCP servers"
2900
+ },
2901
+ args: {
2902
+ client: {
2903
+ type: "string",
2904
+ description: "Filter by client (claude, cursor, vscode, windsurf)"
2905
+ },
2906
+ json: {
2907
+ type: "boolean",
2908
+ description: "Output as JSON",
2909
+ default: false
2200
2910
  }
2201
2911
  },
2202
2912
  async run({ args }) {
@@ -2204,8 +2914,8 @@ var list_default = defineCommand9({
2204
2914
  if (servers.length === 0) {
2205
2915
  const filter = args.client ? ` for client "${args.client}"` : "";
2206
2916
  console.log(
2207
- pc8.dim(
2208
- `No MCP servers installed${filter}. Run ${pc8.cyan("mcpman install <server>")} to get started.`
2917
+ pc11.dim(
2918
+ `No MCP servers installed${filter}. Run ${pc11.cyan("mcpman install <server>")} to get started.`
2209
2919
  )
2210
2920
  );
2211
2921
  return;
@@ -2231,9 +2941,9 @@ var list_default = defineCommand9({
2231
2941
  const nameWidth = Math.max(4, ...withStatus.map((s) => s.name.length));
2232
2942
  const clientsWidth = Math.max(7, ...withStatus.map((s) => formatClients(s.clients).length));
2233
2943
  const header = ` ${pad("NAME", nameWidth)} ${pad("CLIENT(S)", clientsWidth)} ${pad("COMMAND", 20)} STATUS`;
2234
- console.log(pc8.dim(header));
2944
+ console.log(pc11.dim(header));
2235
2945
  console.log(
2236
- pc8.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientsWidth)} ${"-".repeat(20)} ------`)
2946
+ pc11.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientsWidth)} ${"-".repeat(20)} ------`)
2237
2947
  );
2238
2948
  for (const s of withStatus) {
2239
2949
  const icon = STATUS_ICON[s.status];
@@ -2248,7 +2958,7 @@ var list_default = defineCommand9({
2248
2958
  }
2249
2959
  const clientSet = new Set(withStatus.flatMap((s) => s.clients));
2250
2960
  console.log(
2251
- pc8.dim(
2961
+ pc11.dim(
2252
2962
  `
2253
2963
  ${withStatus.length} server${withStatus.length !== 1 ? "s" : ""} \xB7 ${clientSet.size} client${clientSet.size !== 1 ? "s" : ""}`
2254
2964
  )
@@ -2273,9 +2983,9 @@ function formatClients(clients) {
2273
2983
 
2274
2984
  // src/commands/logs.ts
2275
2985
  import { spawn as spawn2 } from "child_process";
2276
- import { defineCommand as defineCommand10 } from "citty";
2277
- import pc9 from "picocolors";
2278
- var logs_default = defineCommand10({
2986
+ import { defineCommand as defineCommand13 } from "citty";
2987
+ import pc12 from "picocolors";
2988
+ var logs_default = defineCommand13({
2279
2989
  meta: {
2280
2990
  name: "logs",
2281
2991
  description: "Stream stdout/stderr from an MCP server"
@@ -2298,7 +3008,7 @@ var logs_default = defineCommand10({
2298
3008
  const lockfile = readLockfile();
2299
3009
  const entry = lockfile.servers[serverName];
2300
3010
  if (!entry) {
2301
- console.error(pc9.red(` Error: Server '${serverName}' is not installed.`));
3011
+ console.error(pc12.red(` Error: Server '${serverName}' is not installed.`));
2302
3012
  process.exit(1);
2303
3013
  }
2304
3014
  const lockfileEnv = parseEnvFlags(entry.envVars);
@@ -2308,24 +3018,24 @@ var logs_default = defineCommand10({
2308
3018
  ...lockfileEnv,
2309
3019
  ...vaultEnv
2310
3020
  };
2311
- console.log(pc9.dim(` Streaming logs for ${pc9.cyan(serverName)}... (Ctrl+C to stop)
3021
+ console.log(pc12.dim(` Streaming logs for ${pc12.cyan(serverName)}... (Ctrl+C to stop)
2312
3022
  `));
2313
3023
  const child = spawn2(entry.command, entry.args, {
2314
3024
  env: finalEnv,
2315
3025
  stdio: ["pipe", "pipe", "pipe"]
2316
3026
  });
2317
3027
  child.stdout?.on("data", (chunk) => {
2318
- process.stdout.write(pc9.dim("[stdout] ") + chunk.toString());
3028
+ process.stdout.write(pc12.dim("[stdout] ") + chunk.toString());
2319
3029
  });
2320
3030
  child.stderr?.on("data", (chunk) => {
2321
- process.stderr.write(pc9.yellow("[stderr] ") + chunk.toString());
3031
+ process.stderr.write(pc12.yellow("[stderr] ") + chunk.toString());
2322
3032
  });
2323
3033
  child.on("error", (err) => {
2324
- console.error(pc9.red(` Failed to start '${serverName}': ${err.message}`));
3034
+ console.error(pc12.red(` Failed to start '${serverName}': ${err.message}`));
2325
3035
  process.exit(1);
2326
3036
  });
2327
3037
  child.on("close", (code) => {
2328
- console.log(pc9.dim(`
3038
+ console.log(pc12.dim(`
2329
3039
  Process exited with code ${code ?? 0}`));
2330
3040
  process.exit(code ?? 0);
2331
3041
  });
@@ -2350,10 +3060,10 @@ async function loadVaultSecrets(serverName) {
2350
3060
  }
2351
3061
 
2352
3062
  // src/commands/plugin.ts
2353
- import { defineCommand as defineCommand11 } from "citty";
3063
+ import { defineCommand as defineCommand14 } from "citty";
2354
3064
  import { createSpinner as createSpinner3 } from "nanospinner";
2355
- import pc10 from "picocolors";
2356
- var addCommand = defineCommand11({
3065
+ import pc13 from "picocolors";
3066
+ var addCommand = defineCommand14({
2357
3067
  meta: { name: "add", description: "Install a plugin package" },
2358
3068
  args: {
2359
3069
  package: {
@@ -2371,23 +3081,23 @@ var addCommand = defineCommand11({
2371
3081
  spinner5.stop();
2372
3082
  if (loaded) {
2373
3083
  console.log(
2374
- `${pc10.green("\u2713")} Plugin ${pc10.bold(loaded.name)} installed (prefix: ${pc10.cyan(loaded.prefix)})`
3084
+ `${pc13.green("\u2713")} Plugin ${pc13.bold(loaded.name)} installed (prefix: ${pc13.cyan(loaded.prefix)})`
2375
3085
  );
2376
3086
  } else {
2377
3087
  console.log(
2378
- `${pc10.yellow("\u26A0")} Package ${pc10.bold(pkg)} installed but does not export a valid mcpman plugin.`
3088
+ `${pc13.yellow("\u26A0")} Package ${pc13.bold(pkg)} installed but does not export a valid mcpman plugin.`
2379
3089
  );
2380
3090
  }
2381
3091
  } catch (err) {
2382
3092
  spinner5.stop();
2383
3093
  console.error(
2384
- `${pc10.red("\u2717")} Failed to install plugin: ${err instanceof Error ? err.message : String(err)}`
3094
+ `${pc13.red("\u2717")} Failed to install plugin: ${err instanceof Error ? err.message : String(err)}`
2385
3095
  );
2386
3096
  process.exit(1);
2387
3097
  }
2388
3098
  }
2389
3099
  });
2390
- var removeCommand = defineCommand11({
3100
+ var removeCommand = defineCommand14({
2391
3101
  meta: { name: "remove", description: "Uninstall a plugin package" },
2392
3102
  args: {
2393
3103
  package: {
@@ -2400,46 +3110,46 @@ var removeCommand = defineCommand11({
2400
3110
  const pkg = args.package;
2401
3111
  const installed = listPluginPackages();
2402
3112
  if (!installed.includes(pkg)) {
2403
- console.log(pc10.dim(`Plugin "${pkg}" is not installed.`));
3113
+ console.log(pc13.dim(`Plugin "${pkg}" is not installed.`));
2404
3114
  return;
2405
3115
  }
2406
3116
  try {
2407
3117
  removePluginPackage(pkg);
2408
- console.log(`${pc10.green("\u2713")} Plugin ${pc10.bold(pkg)} removed.`);
3118
+ console.log(`${pc13.green("\u2713")} Plugin ${pc13.bold(pkg)} removed.`);
2409
3119
  } catch (err) {
2410
3120
  console.error(
2411
- `${pc10.red("\u2717")} Failed to remove plugin: ${err instanceof Error ? err.message : String(err)}`
3121
+ `${pc13.red("\u2717")} Failed to remove plugin: ${err instanceof Error ? err.message : String(err)}`
2412
3122
  );
2413
3123
  process.exit(1);
2414
3124
  }
2415
3125
  }
2416
3126
  });
2417
- var listCommand2 = defineCommand11({
3127
+ var listCommand2 = defineCommand14({
2418
3128
  meta: { name: "list", description: "List installed plugins" },
2419
3129
  run() {
2420
3130
  const packages = listPluginPackages();
2421
3131
  if (packages.length === 0) {
2422
- console.log(pc10.dim("No plugins installed. Use `mcpman plugin add <package>`."));
3132
+ console.log(pc13.dim("No plugins installed. Use `mcpman plugin add <package>`."));
2423
3133
  return;
2424
3134
  }
2425
3135
  console.log("");
2426
- console.log(pc10.bold("Installed plugins:"));
3136
+ console.log(pc13.bold("Installed plugins:"));
2427
3137
  console.log("");
2428
3138
  for (const pkg of packages) {
2429
3139
  const loaded = loadPlugin(pkg);
2430
3140
  if (loaded) {
2431
3141
  console.log(
2432
- ` ${pc10.green("\u25CF")} ${pc10.bold(loaded.name)} prefix: ${pc10.cyan(loaded.prefix)} pkg: ${pc10.dim(pkg)}`
3142
+ ` ${pc13.green("\u25CF")} ${pc13.bold(loaded.name)} prefix: ${pc13.cyan(loaded.prefix)} pkg: ${pc13.dim(pkg)}`
2433
3143
  );
2434
3144
  } else {
2435
- console.log(` ${pc10.yellow("\u25CF")} ${pc10.dim(pkg)} ${pc10.yellow("(failed to load)")}`);
3145
+ console.log(` ${pc13.yellow("\u25CF")} ${pc13.dim(pkg)} ${pc13.yellow("(failed to load)")}`);
2436
3146
  }
2437
3147
  }
2438
3148
  console.log("");
2439
- console.log(pc10.dim(` ${packages.length} plugin${packages.length !== 1 ? "s" : ""} installed`));
3149
+ console.log(pc13.dim(` ${packages.length} plugin${packages.length !== 1 ? "s" : ""} installed`));
2440
3150
  }
2441
3151
  });
2442
- var plugin_default = defineCommand11({
3152
+ var plugin_default = defineCommand14({
2443
3153
  meta: {
2444
3154
  name: "plugin",
2445
3155
  description: "Manage mcpman plugins for custom registries"
@@ -2452,22 +3162,22 @@ var plugin_default = defineCommand11({
2452
3162
  });
2453
3163
 
2454
3164
  // src/commands/profiles.ts
2455
- import { defineCommand as defineCommand12 } from "citty";
2456
- import pc11 from "picocolors";
3165
+ import { defineCommand as defineCommand15 } from "citty";
3166
+ import pc14 from "picocolors";
2457
3167
 
2458
3168
  // src/core/profile-service.ts
2459
- import fs8 from "fs";
2460
- import path8 from "path";
3169
+ import fs10 from "fs";
3170
+ import path12 from "path";
2461
3171
  function ensureDir(dir = getProfilesDir()) {
2462
- fs8.mkdirSync(dir, { recursive: true });
3172
+ fs10.mkdirSync(dir, { recursive: true });
2463
3173
  }
2464
3174
  function profilePath(name, dir = getProfilesDir()) {
2465
- return path8.join(dir, `${name}.json`);
3175
+ return path12.join(dir, `${name}.json`);
2466
3176
  }
2467
3177
  function createProfile(name, description = "", dir = getProfilesDir()) {
2468
3178
  ensureDir(dir);
2469
3179
  const filePath = profilePath(name, dir);
2470
- if (fs8.existsSync(filePath)) {
3180
+ if (fs10.existsSync(filePath)) {
2471
3181
  throw new Error(`Profile '${name}' already exists. Delete it first or use a different name.`);
2472
3182
  }
2473
3183
  const lockfile = readLockfile();
@@ -2477,16 +3187,16 @@ function createProfile(name, description = "", dir = getProfilesDir()) {
2477
3187
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2478
3188
  servers: lockfile.servers
2479
3189
  };
2480
- fs8.writeFileSync(filePath, JSON.stringify(profile, null, 2), "utf-8");
3190
+ fs10.writeFileSync(filePath, JSON.stringify(profile, null, 2), "utf-8");
2481
3191
  return profile;
2482
3192
  }
2483
3193
  function listProfiles(dir = getProfilesDir()) {
2484
3194
  ensureDir(dir);
2485
- const files = fs8.readdirSync(dir).filter((f) => f.endsWith(".json"));
3195
+ const files = fs10.readdirSync(dir).filter((f) => f.endsWith(".json"));
2486
3196
  const profiles = [];
2487
3197
  for (const file of files) {
2488
3198
  try {
2489
- const raw = fs8.readFileSync(path8.join(dir, file), "utf-8");
3199
+ const raw = fs10.readFileSync(path12.join(dir, file), "utf-8");
2490
3200
  const data = JSON.parse(raw);
2491
3201
  profiles.push(data);
2492
3202
  } catch {
@@ -2496,9 +3206,9 @@ function listProfiles(dir = getProfilesDir()) {
2496
3206
  }
2497
3207
  function loadProfile(name, dir = getProfilesDir()) {
2498
3208
  const filePath = profilePath(name, dir);
2499
- if (!fs8.existsSync(filePath)) return null;
3209
+ if (!fs10.existsSync(filePath)) return null;
2500
3210
  try {
2501
- const raw = fs8.readFileSync(filePath, "utf-8");
3211
+ const raw = fs10.readFileSync(filePath, "utf-8");
2502
3212
  return JSON.parse(raw);
2503
3213
  } catch {
2504
3214
  return null;
@@ -2506,13 +3216,13 @@ function loadProfile(name, dir = getProfilesDir()) {
2506
3216
  }
2507
3217
  function deleteProfile(name, dir = getProfilesDir()) {
2508
3218
  const filePath = profilePath(name, dir);
2509
- if (!fs8.existsSync(filePath)) return false;
2510
- fs8.unlinkSync(filePath);
3219
+ if (!fs10.existsSync(filePath)) return false;
3220
+ fs10.unlinkSync(filePath);
2511
3221
  return true;
2512
3222
  }
2513
3223
 
2514
3224
  // src/commands/profiles.ts
2515
- var profiles_default = defineCommand12({
3225
+ var profiles_default = defineCommand15({
2516
3226
  meta: {
2517
3227
  name: "profiles",
2518
3228
  description: "Manage named server configuration profiles"
@@ -2541,16 +3251,16 @@ var profiles_default = defineCommand12({
2541
3251
  case "create": {
2542
3252
  if (!name) {
2543
3253
  console.error(
2544
- pc11.red(" Error: Profile name required. Usage: mcpman profiles create <name>")
3254
+ pc14.red(" Error: Profile name required. Usage: mcpman profiles create <name>")
2545
3255
  );
2546
3256
  process.exit(1);
2547
3257
  }
2548
3258
  try {
2549
3259
  const profile = createProfile(name, args.description ?? "");
2550
3260
  const count = Object.keys(profile.servers).length;
2551
- console.log(pc11.green(` \u2713 Profile '${name}' created with ${count} server(s).`));
3261
+ console.log(pc14.green(` \u2713 Profile '${name}' created with ${count} server(s).`));
2552
3262
  } catch (err) {
2553
- console.error(pc11.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3263
+ console.error(pc14.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
2554
3264
  process.exit(1);
2555
3265
  }
2556
3266
  break;
@@ -2558,38 +3268,38 @@ var profiles_default = defineCommand12({
2558
3268
  case "switch": {
2559
3269
  if (!name) {
2560
3270
  console.error(
2561
- pc11.red(" Error: Profile name required. Usage: mcpman profiles switch <name>")
3271
+ pc14.red(" Error: Profile name required. Usage: mcpman profiles switch <name>")
2562
3272
  );
2563
3273
  process.exit(1);
2564
3274
  }
2565
3275
  const profile = loadProfile(name);
2566
3276
  if (!profile) {
2567
- console.error(pc11.red(` Error: Profile '${name}' not found.`));
3277
+ console.error(pc14.red(` Error: Profile '${name}' not found.`));
2568
3278
  process.exit(1);
2569
3279
  }
2570
3280
  const lockData = { lockfileVersion: 1, servers: profile.servers };
2571
3281
  writeLockfile(lockData);
2572
3282
  const count = Object.keys(profile.servers).length;
2573
- console.log(pc11.green(` \u2713 Switched to profile '${name}' (${count} servers).`));
2574
- console.log(pc11.dim(" Run mcpman sync to apply to all clients."));
3283
+ console.log(pc14.green(` \u2713 Switched to profile '${name}' (${count} servers).`));
3284
+ console.log(pc14.dim(" Run mcpman sync to apply to all clients."));
2575
3285
  break;
2576
3286
  }
2577
3287
  case "list": {
2578
3288
  const profiles = listProfiles();
2579
3289
  if (profiles.length === 0) {
2580
3290
  console.log(
2581
- pc11.dim(" No profiles saved. Create one with: mcpman profiles create <name>")
3291
+ pc14.dim(" No profiles saved. Create one with: mcpman profiles create <name>")
2582
3292
  );
2583
3293
  return;
2584
3294
  }
2585
- console.log(pc11.bold(`
3295
+ console.log(pc14.bold(`
2586
3296
  Profiles (${profiles.length})
2587
3297
  `));
2588
3298
  for (const p12 of profiles) {
2589
3299
  const count = Object.keys(p12.servers).length;
2590
- const desc = p12.description ? pc11.dim(` \u2014 ${p12.description}`) : "";
3300
+ const desc = p12.description ? pc14.dim(` \u2014 ${p12.description}`) : "";
2591
3301
  console.log(
2592
- ` ${pc11.cyan("\u25CF")} ${pc11.bold(p12.name)} ${pc11.dim(`${count} server(s)`)}${desc}`
3302
+ ` ${pc14.cyan("\u25CF")} ${pc14.bold(p12.name)} ${pc14.dim(`${count} server(s)`)}${desc}`
2593
3303
  );
2594
3304
  }
2595
3305
  console.log();
@@ -2598,32 +3308,200 @@ var profiles_default = defineCommand12({
2598
3308
  case "delete": {
2599
3309
  if (!name) {
2600
3310
  console.error(
2601
- pc11.red(" Error: Profile name required. Usage: mcpman profiles delete <name>")
3311
+ pc14.red(" Error: Profile name required. Usage: mcpman profiles delete <name>")
2602
3312
  );
2603
3313
  process.exit(1);
2604
3314
  }
2605
3315
  const deleted = deleteProfile(name);
2606
3316
  if (deleted) {
2607
- console.log(pc11.green(` \u2713 Profile '${name}' deleted.`));
3317
+ console.log(pc14.green(` \u2713 Profile '${name}' deleted.`));
2608
3318
  } else {
2609
- console.error(pc11.red(` Error: Profile '${name}' not found.`));
3319
+ console.error(pc14.red(` Error: Profile '${name}' not found.`));
2610
3320
  process.exit(1);
2611
3321
  }
2612
3322
  break;
2613
3323
  }
2614
3324
  default:
2615
3325
  console.error(
2616
- pc11.red(` Unknown action '${action}'. Use: create, switch, list, or delete.`)
3326
+ pc14.red(` Unknown action '${action}'. Use: create, switch, list, or delete.`)
2617
3327
  );
2618
3328
  process.exit(1);
2619
3329
  }
2620
3330
  }
2621
3331
  });
2622
3332
 
3333
+ // src/commands/registry.ts
3334
+ import { defineCommand as defineCommand16 } from "citty";
3335
+ import pc15 from "picocolors";
3336
+
3337
+ // src/core/registry-manager.ts
3338
+ var BUILTIN_REGISTRIES = [
3339
+ { name: "npm", url: "https://registry.npmjs.org", builtin: true },
3340
+ { name: "smithery", url: "https://registry.smithery.ai", builtin: true }
3341
+ ];
3342
+ function validateUrl(url) {
3343
+ try {
3344
+ new URL(url);
3345
+ } catch {
3346
+ throw new Error(
3347
+ `Invalid URL: "${url}". Must be a valid URL (e.g. https://registry.example.com)`
3348
+ );
3349
+ }
3350
+ }
3351
+ function readCustomRegistries(configPath) {
3352
+ const config = readConfig(configPath);
3353
+ const raw = config.registries;
3354
+ if (!Array.isArray(raw)) return [];
3355
+ return raw.filter((r) => !r.builtin);
3356
+ }
3357
+ function writeCustomRegistries(entries, configPath) {
3358
+ const config = readConfig(configPath);
3359
+ config.registries = entries;
3360
+ writeConfig(config, configPath);
3361
+ }
3362
+ function getRegistries(configPath) {
3363
+ const custom = readCustomRegistries(configPath);
3364
+ return [...BUILTIN_REGISTRIES, ...custom];
3365
+ }
3366
+ function addRegistry(name, url, configPath) {
3367
+ validateUrl(url);
3368
+ const all = getRegistries(configPath);
3369
+ if (all.some((r) => r.name === name)) {
3370
+ throw new Error(`Registry '${name}' already exists. Use a different name or remove it first.`);
3371
+ }
3372
+ const custom = readCustomRegistries(configPath);
3373
+ custom.push({ name, url, builtin: false });
3374
+ writeCustomRegistries(custom, configPath);
3375
+ }
3376
+ function removeRegistry(name, configPath) {
3377
+ if (BUILTIN_REGISTRIES.some((r) => r.name === name)) {
3378
+ throw new Error(`Cannot remove built-in registry '${name}'.`);
3379
+ }
3380
+ const custom = readCustomRegistries(configPath);
3381
+ const idx = custom.findIndex((r) => r.name === name);
3382
+ if (idx === -1) {
3383
+ throw new Error(`Registry '${name}' not found.`);
3384
+ }
3385
+ custom.splice(idx, 1);
3386
+ writeCustomRegistries(custom, configPath);
3387
+ }
3388
+ function setDefaultRegistry(name, configPath) {
3389
+ const all = getRegistries(configPath);
3390
+ if (!all.some((r) => r.name === name)) {
3391
+ throw new Error(
3392
+ `Registry '${name}' not found. Add it first with: mcpman registry add ${name} <url>`
3393
+ );
3394
+ }
3395
+ const config = readConfig(configPath);
3396
+ config.defaultRegistry = name;
3397
+ writeConfig(config, configPath);
3398
+ }
3399
+ function getDefaultRegistry(configPath) {
3400
+ const config = readConfig(configPath);
3401
+ return String(config.defaultRegistry ?? "npm");
3402
+ }
3403
+
3404
+ // src/commands/registry.ts
3405
+ var registry_default = defineCommand16({
3406
+ meta: {
3407
+ name: "registry",
3408
+ description: "Manage custom registry URLs"
3409
+ },
3410
+ args: {
3411
+ action: {
3412
+ type: "positional",
3413
+ description: "Action: list, add, remove, set-default",
3414
+ required: true
3415
+ },
3416
+ name: {
3417
+ type: "positional",
3418
+ description: "Registry name (for add/remove/set-default)",
3419
+ required: false
3420
+ },
3421
+ url: {
3422
+ type: "positional",
3423
+ description: "Registry URL (for add)",
3424
+ required: false
3425
+ }
3426
+ },
3427
+ async run({ args }) {
3428
+ const action = args.action.toLowerCase();
3429
+ const name = args.name;
3430
+ const url = args.url;
3431
+ switch (action) {
3432
+ case "list": {
3433
+ const registries = getRegistries();
3434
+ const defaultName = getDefaultRegistry();
3435
+ console.log(pc15.bold("\n Registries\n"));
3436
+ for (const r of registries) {
3437
+ const isDefault = r.name === defaultName;
3438
+ const defaultTag = isDefault ? pc15.green(" (default)") : "";
3439
+ const builtinTag = r.builtin ? pc15.dim(" [builtin]") : "";
3440
+ console.log(
3441
+ ` ${isDefault ? pc15.green("\u25CF") : pc15.dim("\u25CB")} ${pc15.bold(r.name)}${defaultTag}${builtinTag}`
3442
+ );
3443
+ console.log(` ${pc15.dim(r.url)}`);
3444
+ }
3445
+ console.log();
3446
+ break;
3447
+ }
3448
+ case "add": {
3449
+ if (!name) {
3450
+ console.error(pc15.red(" Error: Usage: mcpman registry add <name> <url>"));
3451
+ process.exit(1);
3452
+ }
3453
+ if (!url) {
3454
+ console.error(pc15.red(" Error: Usage: mcpman registry add <name> <url>"));
3455
+ process.exit(1);
3456
+ }
3457
+ try {
3458
+ addRegistry(name, url);
3459
+ console.log(pc15.green(` Added registry '${name}' \u2192 ${url}`));
3460
+ } catch (err) {
3461
+ console.error(pc15.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3462
+ process.exit(1);
3463
+ }
3464
+ break;
3465
+ }
3466
+ case "remove": {
3467
+ if (!name) {
3468
+ console.error(pc15.red(" Error: Usage: mcpman registry remove <name>"));
3469
+ process.exit(1);
3470
+ }
3471
+ try {
3472
+ removeRegistry(name);
3473
+ console.log(pc15.green(` Removed registry '${name}'.`));
3474
+ } catch (err) {
3475
+ console.error(pc15.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3476
+ process.exit(1);
3477
+ }
3478
+ break;
3479
+ }
3480
+ case "set-default": {
3481
+ if (!name) {
3482
+ console.error(pc15.red(" Error: Usage: mcpman registry set-default <name>"));
3483
+ process.exit(1);
3484
+ }
3485
+ try {
3486
+ setDefaultRegistry(name);
3487
+ console.log(pc15.green(` Default registry set to '${name}'.`));
3488
+ } catch (err) {
3489
+ console.error(pc15.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
3490
+ process.exit(1);
3491
+ }
3492
+ break;
3493
+ }
3494
+ default:
3495
+ console.error(pc15.red(` Unknown action '${action}'. Use: list, add, remove, set-default.`));
3496
+ process.exit(1);
3497
+ }
3498
+ }
3499
+ });
3500
+
2623
3501
  // src/commands/remove.ts
2624
3502
  import * as p8 from "@clack/prompts";
2625
- import { defineCommand as defineCommand13 } from "citty";
2626
- import pc12 from "picocolors";
3503
+ import { defineCommand as defineCommand17 } from "citty";
3504
+ import pc16 from "picocolors";
2627
3505
  var CLIENT_DISPLAY2 = {
2628
3506
  "claude-desktop": "Claude",
2629
3507
  cursor: "Cursor",
@@ -2633,7 +3511,7 @@ var CLIENT_DISPLAY2 = {
2633
3511
  function clientDisplayName(type) {
2634
3512
  return CLIENT_DISPLAY2[type] ?? type;
2635
3513
  }
2636
- var remove_default = defineCommand13({
3514
+ var remove_default = defineCommand17({
2637
3515
  meta: {
2638
3516
  name: "remove",
2639
3517
  description: "Remove an MCP server from one or more AI clients"
@@ -2660,7 +3538,7 @@ var remove_default = defineCommand13({
2660
3538
  }
2661
3539
  },
2662
3540
  async run({ args }) {
2663
- p8.intro(pc12.bold("mcpman remove"));
3541
+ p8.intro(pc16.bold("mcpman remove"));
2664
3542
  const serverName = args.server;
2665
3543
  const servers = await getInstalledServers();
2666
3544
  const match = servers.find((s) => s.name === serverName);
@@ -2670,7 +3548,7 @@ var remove_default = defineCommand13({
2670
3548
  (s) => s.name.includes(serverName) || serverName.includes(s.name)
2671
3549
  );
2672
3550
  if (similar.length > 0) {
2673
- p8.log.info(`Did you mean: ${similar.map((s) => pc12.cyan(s.name)).join(", ")}?`);
3551
+ p8.log.info(`Did you mean: ${similar.map((s) => pc16.cyan(s.name)).join(", ")}?`);
2674
3552
  }
2675
3553
  p8.outro("Nothing to remove.");
2676
3554
  return;
@@ -2705,7 +3583,7 @@ var remove_default = defineCommand13({
2705
3583
  if (!args.yes) {
2706
3584
  const clientNames = targetClients.map(clientDisplayName).join(", ");
2707
3585
  const confirmed = await p8.confirm({
2708
- message: `Remove ${pc12.cyan(serverName)} from ${pc12.yellow(clientNames)}?`
3586
+ message: `Remove ${pc16.cyan(serverName)} from ${pc16.yellow(clientNames)}?`
2709
3587
  });
2710
3588
  if (p8.isCancel(confirmed) || !confirmed) {
2711
3589
  p8.outro("Cancelled.");
@@ -2730,18 +3608,18 @@ var remove_default = defineCommand13({
2730
3608
  }
2731
3609
  if (errors.length > 0) {
2732
3610
  for (const e of errors) p8.log.error(e);
2733
- p8.outro(pc12.red("Completed with errors."));
3611
+ p8.outro(pc16.red("Completed with errors."));
2734
3612
  process.exit(1);
2735
3613
  }
2736
- p8.outro(pc12.green(`Removed "${serverName}" successfully.`));
3614
+ p8.outro(pc16.green(`Removed "${serverName}" successfully.`));
2737
3615
  }
2738
3616
  });
2739
3617
 
2740
3618
  // src/commands/run.ts
2741
3619
  import { spawn as spawn3 } from "child_process";
2742
- import { defineCommand as defineCommand14 } from "citty";
2743
- import pc13 from "picocolors";
2744
- var run_default = defineCommand14({
3620
+ import { defineCommand as defineCommand18 } from "citty";
3621
+ import pc17 from "picocolors";
3622
+ var run_default = defineCommand18({
2745
3623
  meta: {
2746
3624
  name: "run",
2747
3625
  description: "Run an installed MCP server with vault secrets injected"
@@ -2763,8 +3641,8 @@ var run_default = defineCommand14({
2763
3641
  const lockfile = readLockfile();
2764
3642
  const entry = lockfile.servers[serverName];
2765
3643
  if (!entry) {
2766
- console.error(pc13.red(` Error: Server '${serverName}' is not installed.`));
2767
- console.error(pc13.dim(` Run ${pc13.cyan("mcpman install <server>")} to install it first.`));
3644
+ console.error(pc17.red(` Error: Server '${serverName}' is not installed.`));
3645
+ console.error(pc17.dim(` Run ${pc17.cyan("mcpman install <server>")} to install it first.`));
2768
3646
  process.exit(1);
2769
3647
  }
2770
3648
  const lockfileEnv = parseEnvFlags(entry.envVars);
@@ -2776,7 +3654,7 @@ var run_default = defineCommand14({
2776
3654
  ...vaultEnv,
2777
3655
  ...cliEnv
2778
3656
  };
2779
- console.log(pc13.dim(` Running ${pc13.cyan(serverName)}...`));
3657
+ console.log(pc17.dim(` Running ${pc17.cyan(serverName)}...`));
2780
3658
  const child = spawn3(entry.command, entry.args, {
2781
3659
  env: finalEnv,
2782
3660
  stdio: "inherit"
@@ -2794,7 +3672,7 @@ var run_default = defineCommand14({
2794
3672
  resolve();
2795
3673
  });
2796
3674
  child.on("error", (err) => {
2797
- console.error(pc13.red(` Failed to start '${serverName}': ${err.message}`));
3675
+ console.error(pc17.red(` Failed to start '${serverName}': ${err.message}`));
2798
3676
  process.exit(1);
2799
3677
  resolve();
2800
3678
  });
@@ -2810,15 +3688,15 @@ async function loadVaultSecrets2(serverName) {
2810
3688
  const password = await getMasterPassword();
2811
3689
  return getSecretsForServer(serverName, password);
2812
3690
  } catch {
2813
- console.warn(pc13.yellow(" Warning: Could not load vault secrets, continuing without them."));
3691
+ console.warn(pc17.yellow(" Warning: Could not load vault secrets, continuing without them."));
2814
3692
  return {};
2815
3693
  }
2816
3694
  }
2817
3695
 
2818
3696
  // src/commands/search.ts
2819
- import { defineCommand as defineCommand15 } from "citty";
3697
+ import { defineCommand as defineCommand19 } from "citty";
2820
3698
  import { createSpinner as createSpinner4 } from "nanospinner";
2821
- import pc14 from "picocolors";
3699
+ import pc18 from "picocolors";
2822
3700
 
2823
3701
  // src/core/registry-search.ts
2824
3702
  var SEARCH_TIMEOUT_MS = 1e4;
@@ -2891,10 +3769,10 @@ function pad2(s, width) {
2891
3769
  function highlightMatch(name, query) {
2892
3770
  const idx = name.toLowerCase().indexOf(query.toLowerCase());
2893
3771
  if (idx === -1) return name;
2894
- return name.slice(0, idx) + pc14.yellow(name.slice(idx, idx + query.length)) + name.slice(idx + query.length);
3772
+ return name.slice(0, idx) + pc18.yellow(name.slice(idx, idx + query.length)) + name.slice(idx + query.length);
2895
3773
  }
2896
3774
  function formatDownloads(n) {
2897
- if (!n) return pc14.dim("\u2014");
3775
+ if (!n) return pc18.dim("\u2014");
2898
3776
  if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
2899
3777
  if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
2900
3778
  return String(n);
@@ -2905,9 +3783,9 @@ function printNpmResults(results, query) {
2905
3783
  const dlWidth = 9;
2906
3784
  const descMax = 50;
2907
3785
  const header = ` ${pad2("NAME", nameWidth)} ${pad2("VERSION", verWidth)} ${pad2("DOWNLOADS", dlWidth)} DESCRIPTION`;
2908
- console.log(pc14.dim(header));
3786
+ console.log(pc18.dim(header));
2909
3787
  console.log(
2910
- pc14.dim(
3788
+ pc18.dim(
2911
3789
  ` ${"-".repeat(nameWidth)} ${"-".repeat(verWidth)} ${"-".repeat(dlWidth)} ${"-".repeat(descMax)}`
2912
3790
  )
2913
3791
  );
@@ -2915,8 +3793,8 @@ function printNpmResults(results, query) {
2915
3793
  const name = highlightMatch(pad2(r.name, nameWidth), query);
2916
3794
  const ver = pad2(r.version, verWidth);
2917
3795
  const dl = pad2(formatDownloads(r.downloads), dlWidth);
2918
- const desc = truncate2(r.description || pc14.dim("(no description)"), descMax);
2919
- console.log(` ${name} ${pc14.dim(ver)} ${dl} ${desc}`);
3796
+ const desc = truncate2(r.description || pc18.dim("(no description)"), descMax);
3797
+ console.log(` ${name} ${pc18.dim(ver)} ${dl} ${desc}`);
2920
3798
  }
2921
3799
  }
2922
3800
  function printSmitheryResults(results, query) {
@@ -2924,19 +3802,19 @@ function printSmitheryResults(results, query) {
2924
3802
  const usesWidth = 8;
2925
3803
  const descMax = 50;
2926
3804
  const header = ` ${pad2("NAME", nameWidth)} ${pad2("USES", usesWidth)} DESCRIPTION`;
2927
- console.log(pc14.dim(header));
3805
+ console.log(pc18.dim(header));
2928
3806
  console.log(
2929
- pc14.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(usesWidth)} ${"-".repeat(descMax)}`)
3807
+ pc18.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(usesWidth)} ${"-".repeat(descMax)}`)
2930
3808
  );
2931
3809
  for (const r of results) {
2932
3810
  const name = highlightMatch(pad2(r.name, nameWidth), query);
2933
3811
  const uses = pad2(formatDownloads(r.useCount), usesWidth);
2934
- const badge = r.verified ? pc14.green(" \u2713") : "";
2935
- const desc = truncate2(r.description || pc14.dim("(no description)"), descMax);
3812
+ const badge = r.verified ? pc18.green(" \u2713") : "";
3813
+ const desc = truncate2(r.description || pc18.dim("(no description)"), descMax);
2936
3814
  console.log(` ${name}${badge} ${uses} ${desc}`);
2937
3815
  }
2938
3816
  }
2939
- var search_default = defineCommand15({
3817
+ var search_default = defineCommand19({
2940
3818
  meta: {
2941
3819
  name: "search",
2942
3820
  description: "Search for MCP servers on npm or Smithery registry"
@@ -2968,7 +3846,7 @@ var search_default = defineCommand15({
2968
3846
  const registry = args.registry.toLowerCase();
2969
3847
  const limit = Math.min(Math.max(1, Number.parseInt(args.limit, 10) || 20), 100);
2970
3848
  if (registry !== "npm" && registry !== "smithery") {
2971
- console.error(pc14.red(` Unknown registry "${registry}". Use "npm" or "smithery".`));
3849
+ console.error(pc18.red(` Unknown registry "${registry}". Use "npm" or "smithery".`));
2972
3850
  process.exit(1);
2973
3851
  }
2974
3852
  const spinner5 = createSpinner4(`Searching ${registry} for "${query}"...`).start();
@@ -2976,20 +3854,20 @@ var search_default = defineCommand15({
2976
3854
  const results2 = await searchNpm(query, limit);
2977
3855
  spinner5.stop();
2978
3856
  if (results2.length === 0) {
2979
- console.log(pc14.dim(`
3857
+ console.log(pc18.dim(`
2980
3858
  No results found for "${query}" on npm.
2981
3859
  `));
2982
3860
  return;
2983
3861
  }
2984
3862
  console.log(
2985
- pc14.bold(
3863
+ pc18.bold(
2986
3864
  `
2987
3865
  mcpman search \u2014 npm (${results2.length} result${results2.length !== 1 ? "s" : ""})
2988
3866
  `
2989
3867
  )
2990
3868
  );
2991
3869
  printNpmResults(results2, query);
2992
- console.log(pc14.dim("\n Install with: mcpman install <name>\n"));
3870
+ console.log(pc18.dim("\n Install with: mcpman install <name>\n"));
2993
3871
  if (args.all) {
2994
3872
  await printPluginResults(query, limit);
2995
3873
  }
@@ -2998,20 +3876,20 @@ var search_default = defineCommand15({
2998
3876
  const results = await searchSmithery(query, limit);
2999
3877
  spinner5.stop();
3000
3878
  if (results.length === 0) {
3001
- console.log(pc14.dim(`
3879
+ console.log(pc18.dim(`
3002
3880
  No results found for "${query}" on Smithery.
3003
3881
  `));
3004
3882
  return;
3005
3883
  }
3006
3884
  console.log(
3007
- pc14.bold(
3885
+ pc18.bold(
3008
3886
  `
3009
3887
  mcpman search \u2014 Smithery (${results.length} result${results.length !== 1 ? "s" : ""})
3010
3888
  `
3011
3889
  )
3012
3890
  );
3013
3891
  printSmitheryResults(results, query);
3014
- console.log(pc14.dim("\n Install with: mcpman install <name>\n"));
3892
+ console.log(pc18.dim("\n Install with: mcpman install <name>\n"));
3015
3893
  if (args.all) {
3016
3894
  await printPluginResults(query, limit);
3017
3895
  }
@@ -3021,7 +3899,7 @@ async function printPluginResults(query, limit) {
3021
3899
  const pluginResults = await searchPlugins(query, limit);
3022
3900
  if (pluginResults.length === 0) return;
3023
3901
  console.log(
3024
- pc14.bold(
3902
+ pc18.bold(
3025
3903
  `
3026
3904
  Plugins (${pluginResults.length} result${pluginResults.length !== 1 ? "s" : ""})
3027
3905
  `
@@ -3031,22 +3909,22 @@ async function printPluginResults(query, limit) {
3031
3909
  const srcWidth = Math.max(6, ...pluginResults.map((r) => r.source.length));
3032
3910
  const descMax = 50;
3033
3911
  const header = ` ${pad2("NAME", nameWidth)} ${pad2("SOURCE", srcWidth)} DESCRIPTION`;
3034
- console.log(pc14.dim(header));
3912
+ console.log(pc18.dim(header));
3035
3913
  console.log(
3036
- pc14.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(srcWidth)} ${"-".repeat(descMax)}`)
3914
+ pc18.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(srcWidth)} ${"-".repeat(descMax)}`)
3037
3915
  );
3038
3916
  for (const r of pluginResults) {
3039
3917
  const name = highlightMatch(pad2(r.name, nameWidth), query);
3040
3918
  const src = pad2(r.source, srcWidth);
3041
- const desc = truncate2(r.description || pc14.dim("(no description)"), descMax);
3042
- console.log(` ${name} ${pc14.dim(src)} ${desc}`);
3919
+ const desc = truncate2(r.description || pc18.dim("(no description)"), descMax);
3920
+ console.log(` ${name} ${pc18.dim(src)} ${desc}`);
3043
3921
  }
3044
3922
  }
3045
3923
 
3046
3924
  // src/commands/secrets.ts
3047
3925
  import * as p9 from "@clack/prompts";
3048
- import { defineCommand as defineCommand16 } from "citty";
3049
- import pc15 from "picocolors";
3926
+ import { defineCommand as defineCommand20 } from "citty";
3927
+ import pc19 from "picocolors";
3050
3928
  function maskValue(value) {
3051
3929
  if (value.length <= 8) return "***";
3052
3930
  return `${value.slice(0, 4)}***${value.slice(-3)}`;
@@ -3056,7 +3934,7 @@ function parseKeyValue(input) {
3056
3934
  if (idx <= 0) return null;
3057
3935
  return { key: input.slice(0, idx), value: input.slice(idx + 1) };
3058
3936
  }
3059
- var setCommand2 = defineCommand16({
3937
+ var setCommand2 = defineCommand20({
3060
3938
  meta: { name: "set", description: "Store an encrypted secret for a server" },
3061
3939
  args: {
3062
3940
  server: {
@@ -3073,10 +3951,10 @@ var setCommand2 = defineCommand16({
3073
3951
  async run({ args }) {
3074
3952
  const parsed = parseKeyValue(args.keyvalue);
3075
3953
  if (!parsed) {
3076
- console.error(`${pc15.red("\u2717")} Invalid format. Expected KEY=VALUE`);
3954
+ console.error(`${pc19.red("\u2717")} Invalid format. Expected KEY=VALUE`);
3077
3955
  process.exit(1);
3078
3956
  }
3079
- p9.intro(pc15.cyan("mcpman secrets set"));
3957
+ p9.intro(pc19.cyan("mcpman secrets set"));
3080
3958
  const isNew = listSecrets(args.server).length === 0 || !listSecrets(args.server)[0]?.keys.includes(parsed.key);
3081
3959
  const vaultPath = (await import("./vault-service-UTZAV6N6.js")).getVaultPath();
3082
3960
  const vaultExists = (await import("fs")).existsSync(vaultPath);
@@ -3085,16 +3963,16 @@ var setCommand2 = defineCommand16({
3085
3963
  spin.start("Encrypting secret...");
3086
3964
  try {
3087
3965
  setSecret(args.server, parsed.key, parsed.value, password);
3088
- spin.stop(`${pc15.green("\u2713")} Stored ${pc15.bold(parsed.key)} for ${pc15.cyan(args.server)}`);
3966
+ spin.stop(`${pc19.green("\u2713")} Stored ${pc19.bold(parsed.key)} for ${pc19.cyan(args.server)}`);
3089
3967
  } catch (err) {
3090
- spin.stop(`${pc15.red("\u2717")} Failed to store secret`);
3091
- console.error(pc15.dim(String(err)));
3968
+ spin.stop(`${pc19.red("\u2717")} Failed to store secret`);
3969
+ console.error(pc19.dim(String(err)));
3092
3970
  process.exit(1);
3093
3971
  }
3094
- p9.outro(pc15.dim("Secret encrypted and saved to vault."));
3972
+ p9.outro(pc19.dim("Secret encrypted and saved to vault."));
3095
3973
  }
3096
3974
  });
3097
- var listCommand3 = defineCommand16({
3975
+ var listCommand3 = defineCommand20({
3098
3976
  meta: { name: "list", description: "List secret keys stored in the vault" },
3099
3977
  args: {
3100
3978
  server: {
@@ -3106,27 +3984,27 @@ var listCommand3 = defineCommand16({
3106
3984
  async run({ args }) {
3107
3985
  const results = listSecrets(args.server || void 0);
3108
3986
  if (results.length === 0) {
3109
- const filter = args.server ? ` for ${pc15.cyan(args.server)}` : "";
3110
- console.log(pc15.dim(`No secrets stored${filter}.`));
3987
+ const filter = args.server ? ` for ${pc19.cyan(args.server)}` : "";
3988
+ console.log(pc19.dim(`No secrets stored${filter}.`));
3111
3989
  return;
3112
3990
  }
3113
3991
  console.log("");
3114
3992
  for (const { server, keys } of results) {
3115
- console.log(pc15.bold(pc15.cyan(server)));
3993
+ console.log(pc19.bold(pc19.cyan(server)));
3116
3994
  for (const key of keys) {
3117
- console.log(` ${pc15.green("\u25CF")} ${pc15.bold(key)} ${pc15.dim(maskValue("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"))}`);
3995
+ console.log(` ${pc19.green("\u25CF")} ${pc19.bold(key)} ${pc19.dim(maskValue("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"))}`);
3118
3996
  }
3119
3997
  console.log("");
3120
3998
  }
3121
3999
  const total = results.reduce((n, r) => n + r.keys.length, 0);
3122
4000
  console.log(
3123
- pc15.dim(
4001
+ pc19.dim(
3124
4002
  ` ${total} secret${total !== 1 ? "s" : ""} in ${results.length} server${results.length !== 1 ? "s" : ""}`
3125
4003
  )
3126
4004
  );
3127
4005
  }
3128
4006
  });
3129
- var removeCommand2 = defineCommand16({
4007
+ var removeCommand2 = defineCommand20({
3130
4008
  meta: { name: "remove", description: "Delete a secret from the vault" },
3131
4009
  args: {
3132
4010
  server: {
@@ -3142,7 +4020,7 @@ var removeCommand2 = defineCommand16({
3142
4020
  },
3143
4021
  async run({ args }) {
3144
4022
  const confirmed = await p9.confirm({
3145
- message: `Remove ${pc15.bold(args.key)} from ${pc15.cyan(args.server)}?`,
4023
+ message: `Remove ${pc19.bold(args.key)} from ${pc19.cyan(args.server)}?`,
3146
4024
  initialValue: false
3147
4025
  });
3148
4026
  if (p9.isCancel(confirmed) || !confirmed) {
@@ -3151,15 +4029,15 @@ var removeCommand2 = defineCommand16({
3151
4029
  }
3152
4030
  try {
3153
4031
  removeSecret(args.server, args.key);
3154
- console.log(`${pc15.green("\u2713")} Removed ${pc15.bold(args.key)} from ${pc15.cyan(args.server)}`);
4032
+ console.log(`${pc19.green("\u2713")} Removed ${pc19.bold(args.key)} from ${pc19.cyan(args.server)}`);
3155
4033
  } catch (err) {
3156
- console.error(`${pc15.red("\u2717")} Failed to remove secret`);
3157
- console.error(pc15.dim(String(err)));
4034
+ console.error(`${pc19.red("\u2717")} Failed to remove secret`);
4035
+ console.error(pc19.dim(String(err)));
3158
4036
  process.exit(1);
3159
4037
  }
3160
4038
  }
3161
4039
  });
3162
- var secrets_default = defineCommand16({
4040
+ var secrets_default = defineCommand20({
3163
4041
  meta: {
3164
4042
  name: "secrets",
3165
4043
  description: "Manage encrypted secrets for MCP servers"
@@ -3173,8 +4051,8 @@ var secrets_default = defineCommand16({
3173
4051
 
3174
4052
  // src/commands/sync.ts
3175
4053
  import * as p10 from "@clack/prompts";
3176
- import { defineCommand as defineCommand17 } from "citty";
3177
- import pc16 from "picocolors";
4054
+ import { defineCommand as defineCommand21 } from "citty";
4055
+ import pc20 from "picocolors";
3178
4056
 
3179
4057
  // src/core/config-diff.ts
3180
4058
  function reconstructServerEntry(lockEntry) {
@@ -3326,7 +4204,7 @@ var CLIENT_DISPLAY3 = {
3326
4204
  vscode: "VS Code",
3327
4205
  windsurf: "Windsurf"
3328
4206
  };
3329
- var sync_default = defineCommand17({
4207
+ var sync_default = defineCommand21({
3330
4208
  meta: {
3331
4209
  name: "sync",
3332
4210
  description: "Sync MCP server configs across all detected AI clients"
@@ -3353,7 +4231,7 @@ var sync_default = defineCommand17({
3353
4231
  }
3354
4232
  },
3355
4233
  async run({ args }) {
3356
- p10.intro(`${pc16.cyan("mcpman sync")}`);
4234
+ p10.intro(`${pc20.cyan("mcpman sync")}`);
3357
4235
  const sourceClient = args.source;
3358
4236
  if (sourceClient && !VALID_CLIENTS.includes(sourceClient)) {
3359
4237
  p10.log.error(
@@ -3389,20 +4267,20 @@ var sync_default = defineCommand17({
3389
4267
  const extraCount = actions.filter((a) => a.action === "extra").length;
3390
4268
  const removeCount = actions.filter((a) => a.action === "remove").length;
3391
4269
  if (addCount === 0 && removeCount === 0 && extraCount === 0) {
3392
- p10.outro(pc16.green("All clients are in sync."));
4270
+ p10.outro(pc20.green("All clients are in sync."));
3393
4271
  process.exit(0);
3394
4272
  }
3395
4273
  const parts = [];
3396
- if (addCount > 0) parts.push(pc16.green(`${addCount} to add`));
3397
- if (removeCount > 0) parts.push(pc16.red(`${removeCount} to remove`));
3398
- if (extraCount > 0) parts.push(pc16.yellow(`${extraCount} extra (informational)`));
4274
+ if (addCount > 0) parts.push(pc20.green(`${addCount} to add`));
4275
+ if (removeCount > 0) parts.push(pc20.red(`${removeCount} to remove`));
4276
+ if (extraCount > 0) parts.push(pc20.yellow(`${extraCount} extra (informational)`));
3399
4277
  p10.log.info(parts.join(" \xB7 "));
3400
4278
  if (args["dry-run"]) {
3401
- p10.outro(pc16.dim("Dry run \u2014 no changes applied."));
4279
+ p10.outro(pc20.dim("Dry run \u2014 no changes applied."));
3402
4280
  process.exit(1);
3403
4281
  }
3404
4282
  if (addCount === 0 && removeCount === 0) {
3405
- p10.outro(pc16.dim("No additions needed. Extra servers left untouched."));
4283
+ p10.outro(pc20.dim("No additions needed. Extra servers left untouched."));
3406
4284
  process.exit(1);
3407
4285
  }
3408
4286
  if (!args.yes) {
@@ -3414,7 +4292,7 @@ var sync_default = defineCommand17({
3414
4292
  initialValue: true
3415
4293
  });
3416
4294
  if (p10.isCancel(confirmed) || !confirmed) {
3417
- p10.outro(pc16.dim("Cancelled \u2014 no changes applied."));
4295
+ p10.outro(pc20.dim("Cancelled \u2014 no changes applied."));
3418
4296
  process.exit(0);
3419
4297
  }
3420
4298
  }
@@ -3433,7 +4311,7 @@ var sync_default = defineCommand17({
3433
4311
  }
3434
4312
  }
3435
4313
  p10.outro(
3436
- result.failed === 0 ? pc16.green("Sync complete.") : pc16.yellow("Sync complete with errors.")
4314
+ result.failed === 0 ? pc20.green("Sync complete.") : pc20.yellow("Sync complete with errors.")
3437
4315
  );
3438
4316
  process.exit(result.failed > 0 ? 1 : 0);
3439
4317
  }
@@ -3449,8 +4327,8 @@ function printDiffTable(actions) {
3449
4327
  ...actions.map((a) => CLIENT_DISPLAY3[a.client]?.length ?? a.client.length)
3450
4328
  );
3451
4329
  const header = ` ${pad3("SERVER", nameWidth)} ${pad3("CLIENT", clientWidth)} STATUS`;
3452
- console.log(pc16.dim(header));
3453
- console.log(pc16.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientWidth)} ------`));
4330
+ console.log(pc20.dim(header));
4331
+ console.log(pc20.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientWidth)} ------`));
3454
4332
  for (const action of actions) {
3455
4333
  const clientDisplay = CLIENT_DISPLAY3[action.client] ?? action.client;
3456
4334
  const [icon, statusText] = formatAction(action.action);
@@ -3463,13 +4341,13 @@ function printDiffTable(actions) {
3463
4341
  function formatAction(action) {
3464
4342
  switch (action) {
3465
4343
  case "add":
3466
- return [pc16.green("+"), pc16.green("missing \u2014 will add")];
4344
+ return [pc20.green("+"), pc20.green("missing \u2014 will add")];
3467
4345
  case "extra":
3468
- return [pc16.yellow("?"), pc16.yellow("extra (not in lockfile)")];
4346
+ return [pc20.yellow("?"), pc20.yellow("extra (not in lockfile)")];
3469
4347
  case "remove":
3470
- return [pc16.red("\u2013"), pc16.red("extra \u2014 will remove")];
4348
+ return [pc20.red("\u2013"), pc20.red("extra \u2014 will remove")];
3471
4349
  case "ok":
3472
- return [pc16.dim("\xB7"), pc16.dim("in sync")];
4350
+ return [pc20.dim("\xB7"), pc20.dim("in sync")];
3473
4351
  }
3474
4352
  }
3475
4353
  function pad3(s, width) {
@@ -3477,8 +4355,8 @@ function pad3(s, width) {
3477
4355
  }
3478
4356
 
3479
4357
  // src/commands/test-command.ts
3480
- import { defineCommand as defineCommand18 } from "citty";
3481
- import pc17 from "picocolors";
4358
+ import { defineCommand as defineCommand22 } from "citty";
4359
+ import pc21 from "picocolors";
3482
4360
 
3483
4361
  // src/core/mcp-tester.ts
3484
4362
  import { spawn as spawn4 } from "child_process";
@@ -3576,7 +4454,7 @@ async function testMcpServer(serverName, command, args, env) {
3576
4454
  }
3577
4455
 
3578
4456
  // src/commands/test-command.ts
3579
- var test_command_default = defineCommand18({
4457
+ var test_command_default = defineCommand22({
3580
4458
  meta: {
3581
4459
  name: "test",
3582
4460
  description: "Test MCP server connectivity and capabilities"
@@ -3597,10 +4475,10 @@ var test_command_default = defineCommand18({
3597
4475
  const lockfile = readLockfile();
3598
4476
  const serverNames = args.all ? Object.keys(lockfile.servers) : args.server ? [args.server] : [];
3599
4477
  if (serverNames.length === 0) {
3600
- console.error(pc17.red(" Error: Specify a server name or use --all."));
4478
+ console.error(pc21.red(" Error: Specify a server name or use --all."));
3601
4479
  process.exit(1);
3602
4480
  }
3603
- console.log(pc17.bold(`
4481
+ console.log(pc21.bold(`
3604
4482
  mcpman test \u2014 ${serverNames.length} server(s)
3605
4483
  `));
3606
4484
  let passed = 0;
@@ -3608,7 +4486,7 @@ var test_command_default = defineCommand18({
3608
4486
  for (const name of serverNames) {
3609
4487
  const entry = lockfile.servers[name];
3610
4488
  if (!entry) {
3611
- console.log(` ${pc17.red("\u2717")} ${pc17.bold(name)} \u2014 not installed`);
4489
+ console.log(` ${pc21.red("\u2717")} ${pc21.bold(name)} \u2014 not installed`);
3612
4490
  failed++;
3613
4491
  continue;
3614
4492
  }
@@ -3619,27 +4497,27 @@ var test_command_default = defineCommand18({
3619
4497
  if (result.passed) {
3620
4498
  passed++;
3621
4499
  console.log(
3622
- ` ${pc17.green("\u2713")} ${pc17.bold(name)} ${pc17.dim(`(${result.responseTimeMs}ms)`)}`
4500
+ ` ${pc21.green("\u2713")} ${pc21.bold(name)} ${pc21.dim(`(${result.responseTimeMs}ms)`)}`
3623
4501
  );
3624
4502
  if (result.tools.length > 0) {
3625
- console.log(pc17.dim(` Tools: ${result.tools.join(", ")}`));
4503
+ console.log(pc21.dim(` Tools: ${result.tools.join(", ")}`));
3626
4504
  }
3627
4505
  } else {
3628
4506
  failed++;
3629
- console.log(` ${pc17.red("\u2717")} ${pc17.bold(name)} ${pc17.dim(`(${result.responseTimeMs}ms)`)}`);
4507
+ console.log(` ${pc21.red("\u2717")} ${pc21.bold(name)} ${pc21.dim(`(${result.responseTimeMs}ms)`)}`);
3630
4508
  if (result.error) {
3631
- console.log(` ${pc17.red(result.error)}`);
4509
+ console.log(` ${pc21.red(result.error)}`);
3632
4510
  }
3633
4511
  console.log(
3634
- ` ${pc17.dim("initialize:")} ${result.initializeOk ? pc17.green("ok") : pc17.red("fail")} ${pc17.dim("tools/list:")} ${result.toolsListOk ? pc17.green("ok") : pc17.red("fail")}`
4512
+ ` ${pc21.dim("initialize:")} ${result.initializeOk ? pc21.green("ok") : pc21.red("fail")} ${pc21.dim("tools/list:")} ${result.toolsListOk ? pc21.green("ok") : pc21.red("fail")}`
3635
4513
  );
3636
4514
  }
3637
4515
  }
3638
- console.log(pc17.dim(`
4516
+ console.log(pc21.dim(`
3639
4517
  ${"\u2500".repeat(40)}`));
3640
4518
  const parts = [];
3641
- if (passed > 0) parts.push(pc17.green(`${passed} passed`));
3642
- if (failed > 0) parts.push(pc17.red(`${failed} failed`));
4519
+ if (passed > 0) parts.push(pc21.green(`${passed} passed`));
4520
+ if (failed > 0) parts.push(pc21.red(`${failed} failed`));
3643
4521
  console.log(` ${parts.join(", ")}
3644
4522
  `);
3645
4523
  if (failed > 0) process.exit(1);
@@ -3658,23 +4536,23 @@ async function loadVaultSecrets3(serverName) {
3658
4536
 
3659
4537
  // src/commands/update.ts
3660
4538
  import * as p11 from "@clack/prompts";
3661
- import { defineCommand as defineCommand19 } from "citty";
3662
- import pc19 from "picocolors";
4539
+ import { defineCommand as defineCommand23 } from "citty";
4540
+ import pc23 from "picocolors";
3663
4541
 
3664
4542
  // src/core/update-notifier.ts
3665
- import fs9 from "fs";
4543
+ import fs11 from "fs";
3666
4544
  import os3 from "os";
3667
- import path9 from "path";
3668
- import pc18 from "picocolors";
3669
- var CACHE_FILE = path9.join(os3.homedir(), ".mcpman", ".update-check");
4545
+ import path13 from "path";
4546
+ import pc22 from "picocolors";
4547
+ var CACHE_FILE = path13.join(os3.homedir(), ".mcpman", ".update-check");
3670
4548
  var TTL_MS = 24 * 60 * 60 * 1e3;
3671
4549
  function writeUpdateCache(data) {
3672
4550
  try {
3673
- const dir = path9.dirname(CACHE_FILE);
3674
- if (!fs9.existsSync(dir)) fs9.mkdirSync(dir, { recursive: true });
4551
+ const dir = path13.dirname(CACHE_FILE);
4552
+ if (!fs11.existsSync(dir)) fs11.mkdirSync(dir, { recursive: true });
3675
4553
  const tmp = `${CACHE_FILE}.tmp`;
3676
- fs9.writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
3677
- fs9.renameSync(tmp, CACHE_FILE);
4554
+ fs11.writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
4555
+ fs11.renameSync(tmp, CACHE_FILE);
3678
4556
  } catch {
3679
4557
  }
3680
4558
  }
@@ -3697,19 +4575,19 @@ function printTable(updates) {
3697
4575
  "LATEST".padEnd(VER_W),
3698
4576
  "STATUS"
3699
4577
  ].join(" ");
3700
- console.log(pc19.bold(`
4578
+ console.log(pc23.bold(`
3701
4579
  ${header}`));
3702
- console.log(pc19.dim(` ${"\u2500".repeat(NAME_W + VER_W * 2 + 20)}`));
4580
+ console.log(pc23.dim(` ${"\u2500".repeat(NAME_W + VER_W * 2 + 20)}`));
3703
4581
  for (const u of updates) {
3704
4582
  const nameCol = u.server.slice(0, NAME_W).padEnd(NAME_W);
3705
4583
  const curCol = u.currentVersion.padEnd(VER_W);
3706
4584
  const latCol = u.latestVersion.padEnd(VER_W);
3707
- const statusCol = u.hasUpdate ? pc19.yellow(`Update available${u.updateType ? ` [${u.updateType}]` : ""}`) : pc19.green("Up to date");
4585
+ const statusCol = u.hasUpdate ? pc23.yellow(`Update available${u.updateType ? ` [${u.updateType}]` : ""}`) : pc23.green("Up to date");
3708
4586
  console.log(` ${nameCol} ${curCol} ${latCol} ${statusCol}`);
3709
4587
  }
3710
4588
  console.log();
3711
4589
  }
3712
- var update_default = defineCommand19({
4590
+ var update_default = defineCommand23({
3713
4591
  meta: {
3714
4592
  name: "update",
3715
4593
  description: "Check for and apply updates to installed MCP servers"
@@ -3770,12 +4648,12 @@ var update_default = defineCommand19({
3770
4648
  printTable(updates);
3771
4649
  const outdated = updates.filter((u) => u.hasUpdate);
3772
4650
  if (outdated.length === 0) {
3773
- console.log(pc19.green(" All servers are up to date."));
4651
+ console.log(pc23.green(" All servers are up to date."));
3774
4652
  return;
3775
4653
  }
3776
4654
  if (args.check) {
3777
4655
  console.log(
3778
- pc19.yellow(` ${outdated.length} update(s) available. Run mcpman update to apply.`)
4656
+ pc23.yellow(` ${outdated.length} update(s) available. Run mcpman update to apply.`)
3779
4657
  );
3780
4658
  return;
3781
4659
  }
@@ -3796,10 +4674,10 @@ var update_default = defineCommand19({
3796
4674
  s.start(`Updating ${update.server}...`);
3797
4675
  const result = await applyServerUpdate(update.server, servers[update.server], clients);
3798
4676
  if (result.success) {
3799
- s.stop(`${pc19.green("\u2713")} ${update.server}: ${result.fromVersion} \u2192 ${result.toVersion}`);
4677
+ s.stop(`${pc23.green("\u2713")} ${update.server}: ${result.fromVersion} \u2192 ${result.toVersion}`);
3800
4678
  successCount++;
3801
4679
  } else {
3802
- s.stop(`${pc19.red("\u2717")} ${update.server}: ${result.error}`);
4680
+ s.stop(`${pc23.red("\u2717")} ${update.server}: ${result.error}`);
3803
4681
  }
3804
4682
  }
3805
4683
  const freshLockfile = readLockfile(resolveLockfilePath());
@@ -3811,9 +4689,9 @@ var update_default = defineCommand19({
3811
4689
 
3812
4690
  // src/commands/upgrade.ts
3813
4691
  import { execSync as execSync2 } from "child_process";
3814
- import { defineCommand as defineCommand20 } from "citty";
3815
- import pc20 from "picocolors";
3816
- var upgrade_default = defineCommand20({
4692
+ import { defineCommand as defineCommand24 } from "citty";
4693
+ import pc24 from "picocolors";
4694
+ var upgrade_default = defineCommand24({
3817
4695
  meta: {
3818
4696
  name: "upgrade",
3819
4697
  description: "Upgrade mcpman to the latest version"
@@ -3826,41 +4704,445 @@ var upgrade_default = defineCommand20({
3826
4704
  }
3827
4705
  },
3828
4706
  async run({ args }) {
3829
- console.log(pc20.dim(` Current version: ${APP_VERSION}`));
4707
+ console.log(pc24.dim(` Current version: ${APP_VERSION}`));
3830
4708
  let latest;
3831
4709
  try {
3832
4710
  latest = execSync2("npm view mcpman version", { encoding: "utf-8", timeout: 15e3 }).trim();
3833
4711
  } catch {
3834
- console.error(pc20.red(" Error: Could not check latest version from npm."));
4712
+ console.error(pc24.red(" Error: Could not check latest version from npm."));
3835
4713
  process.exit(1);
3836
4714
  }
3837
4715
  if (latest === APP_VERSION) {
3838
- console.log(pc20.green(" \u2713 Already on the latest version."));
4716
+ console.log(pc24.green(" \u2713 Already on the latest version."));
3839
4717
  return;
3840
4718
  }
3841
- console.log(pc20.yellow(` Update available: ${APP_VERSION} \u2192 ${latest}`));
4719
+ console.log(pc24.yellow(` Update available: ${APP_VERSION} \u2192 ${latest}`));
3842
4720
  if (args.check) {
3843
- console.log(pc20.dim(" Run mcpman upgrade to install."));
4721
+ console.log(pc24.dim(" Run mcpman upgrade to install."));
3844
4722
  return;
3845
4723
  }
3846
- console.log(pc20.dim(" Installing..."));
4724
+ console.log(pc24.dim(" Installing..."));
3847
4725
  try {
3848
4726
  execSync2(`npm install -g mcpman@${latest}`, { stdio: "inherit", timeout: 6e4 });
3849
- console.log(pc20.green(`
4727
+ console.log(pc24.green(`
3850
4728
  \u2713 Upgraded to mcpman@${latest}`));
3851
4729
  } catch {
3852
- console.error(pc20.red(" Error: Upgrade failed. Try manually: npm install -g mcpman@latest"));
4730
+ console.error(pc24.red(" Error: Upgrade failed. Try manually: npm install -g mcpman@latest"));
3853
4731
  process.exit(1);
3854
4732
  }
3855
4733
  }
3856
4734
  });
3857
4735
 
4736
+ // src/commands/watch.ts
4737
+ import { defineCommand as defineCommand25 } from "citty";
4738
+ import pc25 from "picocolors";
4739
+
4740
+ // src/core/file-watcher-service.ts
4741
+ import { spawn as spawn5 } from "child_process";
4742
+ import fs12 from "fs";
4743
+ var IGNORE_PATTERNS = [
4744
+ "node_modules",
4745
+ ".git",
4746
+ "dist",
4747
+ "build",
4748
+ "__pycache__",
4749
+ ".pyc",
4750
+ ".egg-info",
4751
+ ".tox"
4752
+ ];
4753
+ function shouldIgnore(filename) {
4754
+ return IGNORE_PATTERNS.some((p12) => filename.includes(p12));
4755
+ }
4756
+ function hasWatchedExtension(filename, extensions) {
4757
+ return extensions.some((ext) => filename.endsWith(`.${ext}`));
4758
+ }
4759
+ function timestamp() {
4760
+ return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
4761
+ }
4762
+ var ServerWatcher = class {
4763
+ child = null;
4764
+ watcher = null;
4765
+ restartCount = 0;
4766
+ debounceTimer = null;
4767
+ options = null;
4768
+ stopping = false;
4769
+ /** Start watching and spawn the initial server process */
4770
+ start(options) {
4771
+ this.options = options;
4772
+ this.stopping = false;
4773
+ console.log(`
4774
+ Watching ${options.serverName} (${options.watchDir})...`);
4775
+ console.log(` Extensions: ${options.extensions.join(", ")}`);
4776
+ console.log(` Debounce: ${options.debounceMs}ms
4777
+ `);
4778
+ this.spawnChild();
4779
+ try {
4780
+ this.watcher = fs12.watch(options.watchDir, { recursive: true }, (_event, filename) => {
4781
+ if (!filename) return;
4782
+ this.onFileChange(filename);
4783
+ });
4784
+ } catch (err) {
4785
+ console.error(
4786
+ ` Warning: Could not watch directory: ${err instanceof Error ? err.message : String(err)}`
4787
+ );
4788
+ }
4789
+ }
4790
+ /** Gracefully stop watcher and child process */
4791
+ stop() {
4792
+ this.stopping = true;
4793
+ if (this.debounceTimer) {
4794
+ clearTimeout(this.debounceTimer);
4795
+ this.debounceTimer = null;
4796
+ }
4797
+ if (this.watcher) {
4798
+ this.watcher.close();
4799
+ this.watcher = null;
4800
+ }
4801
+ if (this.child && !this.child.killed) {
4802
+ this.child.kill("SIGTERM");
4803
+ }
4804
+ console.log(`
4805
+ Stopped. (${this.restartCount} restart${this.restartCount !== 1 ? "s" : ""})`);
4806
+ }
4807
+ /** Spawn the server child process, piping its stdio to parent */
4808
+ spawnChild() {
4809
+ if (!this.options) return;
4810
+ const { command, args, env, serverName, clearOnRestart } = this.options;
4811
+ if (clearOnRestart && this.restartCount > 0) {
4812
+ process.stdout.write("\x1Bc");
4813
+ }
4814
+ console.log(` [${timestamp()}] Starting ${serverName}...`);
4815
+ this.child = spawn5(command, args, { env, stdio: ["pipe", "pipe", "pipe"] });
4816
+ this.child.stdout?.on("data", (data) => {
4817
+ process.stdout.write(` [stdout] ${data.toString().trimEnd()}
4818
+ `);
4819
+ });
4820
+ this.child.stderr?.on("data", (data) => {
4821
+ process.stderr.write(` [stderr] ${data.toString().trimEnd()}
4822
+ `);
4823
+ });
4824
+ this.child.on("error", (err) => {
4825
+ console.error(` [${timestamp()}] Error: ${err.message}`);
4826
+ });
4827
+ this.child.on("close", (code) => {
4828
+ if (!this.stopping) {
4829
+ console.log(` [${timestamp()}] Process exited (code ${code ?? "?"})`);
4830
+ }
4831
+ });
4832
+ }
4833
+ /** Kill child process: SIGTERM first, then SIGKILL after 2s */
4834
+ async killChild() {
4835
+ if (!this.child || this.child.killed) return;
4836
+ const childRef = this.child;
4837
+ return new Promise((resolve) => {
4838
+ const child = childRef;
4839
+ const killTimer = setTimeout(() => {
4840
+ if (!child.killed) {
4841
+ child.kill("SIGKILL");
4842
+ }
4843
+ resolve();
4844
+ }, 2e3);
4845
+ child.on("close", () => {
4846
+ clearTimeout(killTimer);
4847
+ resolve();
4848
+ });
4849
+ child.kill("SIGTERM");
4850
+ });
4851
+ }
4852
+ /** Debounced file change handler */
4853
+ onFileChange(filename) {
4854
+ if (!this.options) return;
4855
+ if (shouldIgnore(filename)) return;
4856
+ if (!hasWatchedExtension(filename, this.options.extensions)) return;
4857
+ if (this.debounceTimer) {
4858
+ clearTimeout(this.debounceTimer);
4859
+ }
4860
+ this.debounceTimer = setTimeout(async () => {
4861
+ this.debounceTimer = null;
4862
+ this.restartCount++;
4863
+ console.log(` [${timestamp()}] File changed: ${filename}`);
4864
+ console.log(
4865
+ ` [${timestamp()}] Restarting ${this.options?.serverName}... (restart #${this.restartCount})`
4866
+ );
4867
+ await this.killChild();
4868
+ this.spawnChild();
4869
+ }, this.options.debounceMs);
4870
+ }
4871
+ getRestartCount() {
4872
+ return this.restartCount;
4873
+ }
4874
+ };
4875
+
4876
+ // src/commands/watch.ts
4877
+ var DEFAULT_EXTENSIONS = ["ts", "js", "json", "py", "mjs", "cjs"];
4878
+ var DEFAULT_DEBOUNCE_MS = 300;
4879
+ var watch_default = defineCommand25({
4880
+ meta: {
4881
+ name: "watch",
4882
+ description: "Watch a local MCP server for file changes and auto-restart"
4883
+ },
4884
+ args: {
4885
+ server: {
4886
+ type: "positional",
4887
+ description: "Server name (must be in lockfile)",
4888
+ required: true
4889
+ },
4890
+ dir: {
4891
+ type: "string",
4892
+ description: "Directory to watch (default: resolved path from lockfile)"
4893
+ },
4894
+ ext: {
4895
+ type: "string",
4896
+ description: `File extensions to watch, comma-separated (default: ${DEFAULT_EXTENSIONS.join(",")})`
4897
+ },
4898
+ delay: {
4899
+ type: "string",
4900
+ description: `Debounce delay in ms (default: ${DEFAULT_DEBOUNCE_MS})`
4901
+ },
4902
+ clear: {
4903
+ type: "boolean",
4904
+ description: "Clear terminal on each restart",
4905
+ default: false
4906
+ },
4907
+ env: {
4908
+ type: "string",
4909
+ description: "Override env var KEY=VAL (repeatable)",
4910
+ alias: "e"
4911
+ }
4912
+ },
4913
+ async run({ args }) {
4914
+ const serverName = args.server;
4915
+ const lockfile = readLockfile();
4916
+ const entry = lockfile.servers[serverName];
4917
+ if (!entry) {
4918
+ console.error(pc25.red(` Error: Server '${serverName}' not found in lockfile.`));
4919
+ console.error(pc25.dim(` Run ${pc25.cyan("mcpman link .")} to register a local server.`));
4920
+ process.exit(1);
4921
+ }
4922
+ let watchDir = args.dir;
4923
+ if (!watchDir) {
4924
+ if (entry.source === "local" && entry.resolved) {
4925
+ watchDir = entry.resolved;
4926
+ } else {
4927
+ console.error(pc25.red(` Error: Cannot determine watch directory for '${serverName}'.`));
4928
+ console.error(pc25.dim(" Use --dir to specify the directory to watch."));
4929
+ process.exit(1);
4930
+ }
4931
+ }
4932
+ const extensions = args.ext ? args.ext.split(",").map((e) => e.trim().replace(/^\./, "")) : DEFAULT_EXTENSIONS;
4933
+ const debounceMs = args.delay ? Number.parseInt(args.delay, 10) || DEFAULT_DEBOUNCE_MS : DEFAULT_DEBOUNCE_MS;
4934
+ const lockfileEnv = parseEnvFlags(entry.envVars);
4935
+ const vaultEnv = await loadVaultSecrets4(serverName);
4936
+ const cliEnv = parseEnvFlags(args.env);
4937
+ const finalEnv = {
4938
+ ...process.env,
4939
+ ...lockfileEnv,
4940
+ ...vaultEnv,
4941
+ ...cliEnv
4942
+ };
4943
+ const watcher = new ServerWatcher();
4944
+ const handleStop = () => {
4945
+ watcher.stop();
4946
+ process.exit(0);
4947
+ };
4948
+ process.on("SIGINT", handleStop);
4949
+ process.on("SIGTERM", handleStop);
4950
+ watcher.start({
4951
+ command: entry.command,
4952
+ args: entry.args,
4953
+ env: finalEnv,
4954
+ watchDir,
4955
+ extensions,
4956
+ debounceMs,
4957
+ clearOnRestart: args.clear,
4958
+ serverName
4959
+ });
4960
+ }
4961
+ });
4962
+ async function loadVaultSecrets4(serverName) {
4963
+ try {
4964
+ const entries = listSecrets(serverName);
4965
+ if (entries.length === 0 || entries[0].keys.length === 0) return {};
4966
+ const password = await getMasterPassword();
4967
+ return getSecretsForServer(serverName, password);
4968
+ } catch {
4969
+ return {};
4970
+ }
4971
+ }
4972
+
4973
+ // src/commands/why.ts
4974
+ import { defineCommand as defineCommand26 } from "citty";
4975
+ import pc26 from "picocolors";
4976
+
4977
+ // src/core/why-service.ts
4978
+ import fs13 from "fs";
4979
+ import path14 from "path";
4980
+ var ALL_CLIENT_TYPES = ["claude-desktop", "cursor", "vscode", "windsurf"];
4981
+ async function getServerProvenance(serverName, lockfilePath, profilesDir) {
4982
+ const lockfile = readLockfile(lockfilePath);
4983
+ const entry = lockfile.servers[serverName];
4984
+ if (!entry) {
4985
+ const orphanedClients = await findOrphanedClients(serverName);
4986
+ const anyRegistered = orphanedClients.some((c) => c.registered);
4987
+ if (!anyRegistered) return null;
4988
+ return {
4989
+ name: serverName,
4990
+ version: "unknown",
4991
+ source: "unknown",
4992
+ resolved: "",
4993
+ integrity: "",
4994
+ installedAt: "",
4995
+ clients: orphanedClients,
4996
+ profiles: [],
4997
+ envVars: [],
4998
+ orphaned: true
4999
+ };
5000
+ }
5001
+ const clientStatuses = await buildClientStatuses(serverName, entry.clients);
5002
+ const profiles = scanProfiles(serverName, profilesDir ?? getProfilesDir());
5003
+ return {
5004
+ name: serverName,
5005
+ version: entry.version,
5006
+ source: entry.source,
5007
+ resolved: entry.resolved,
5008
+ integrity: entry.integrity,
5009
+ installedAt: entry.installedAt,
5010
+ clients: clientStatuses,
5011
+ profiles,
5012
+ envVars: entry.envVars ?? [],
5013
+ orphaned: false
5014
+ };
5015
+ }
5016
+ async function buildClientStatuses(serverName, lockfileClients) {
5017
+ return ALL_CLIENT_TYPES.map((type) => ({
5018
+ type,
5019
+ registered: lockfileClients.includes(type)
5020
+ }));
5021
+ }
5022
+ async function findOrphanedClients(serverName) {
5023
+ const { getInstalledClients: getInstalledClients2 } = await import("./client-detector-CY7WPF3K.js");
5024
+ const handlers = await getInstalledClients2();
5025
+ const results = [];
5026
+ for (const handler of handlers) {
5027
+ try {
5028
+ const config = await handler.readConfig();
5029
+ const registered = serverName in (config.servers ?? {});
5030
+ results.push({ type: handler.type, registered });
5031
+ } catch {
5032
+ results.push({ type: handler.type, registered: false });
5033
+ }
5034
+ }
5035
+ return results;
5036
+ }
5037
+ function scanProfiles(serverName, profilesDir) {
5038
+ const found = [];
5039
+ if (!fs13.existsSync(profilesDir)) return found;
5040
+ let files;
5041
+ try {
5042
+ files = fs13.readdirSync(profilesDir).filter((f) => f.endsWith(".json"));
5043
+ } catch {
5044
+ return found;
5045
+ }
5046
+ for (const file of files) {
5047
+ try {
5048
+ const raw = fs13.readFileSync(path14.join(profilesDir, file), "utf-8");
5049
+ const profile = JSON.parse(raw);
5050
+ if (serverName in (profile.servers ?? {})) {
5051
+ found.push(profile.name ?? file.replace(".json", ""));
5052
+ }
5053
+ } catch {
5054
+ }
5055
+ }
5056
+ return found.sort();
5057
+ }
5058
+ function formatWhyOutput(result) {
5059
+ const lines = [];
5060
+ lines.push(` Server: ${result.name}`);
5061
+ lines.push(` Version: ${result.version}`);
5062
+ lines.push(` Source: ${result.source}`);
5063
+ if (result.resolved) lines.push(` Resolved: ${result.resolved}`);
5064
+ if (result.integrity && result.integrity !== "local") {
5065
+ lines.push(` Integrity: ${result.integrity}`);
5066
+ }
5067
+ if (result.installedAt) lines.push(` Installed: ${result.installedAt}`);
5068
+ lines.push("");
5069
+ lines.push(" Clients:");
5070
+ for (const c of result.clients) {
5071
+ const status = c.registered ? "registered" : "not registered";
5072
+ lines.push(` ${c.type.padEnd(20)} ${status}`);
5073
+ }
5074
+ if (result.profiles.length > 0) {
5075
+ lines.push("");
5076
+ lines.push(" Profiles:");
5077
+ for (const p12 of result.profiles) {
5078
+ lines.push(` ${p12}`);
5079
+ }
5080
+ }
5081
+ if (result.envVars.length > 0) {
5082
+ lines.push("");
5083
+ lines.push(" Env Vars:");
5084
+ for (const v of result.envVars) {
5085
+ lines.push(` ${v}`);
5086
+ }
5087
+ }
5088
+ return lines.join("\n");
5089
+ }
5090
+
5091
+ // src/commands/why.ts
5092
+ var why_default = defineCommand26({
5093
+ meta: {
5094
+ name: "why",
5095
+ description: "Show why a server is installed (provenance, clients, profiles)"
5096
+ },
5097
+ args: {
5098
+ server: {
5099
+ type: "positional",
5100
+ description: "Server name to inspect",
5101
+ required: true
5102
+ },
5103
+ json: {
5104
+ type: "boolean",
5105
+ description: "Output as JSON for scripting",
5106
+ default: false
5107
+ }
5108
+ },
5109
+ async run({ args }) {
5110
+ const serverName = args.server;
5111
+ const asJson = args.json;
5112
+ const result = await getServerProvenance(serverName);
5113
+ if (!result) {
5114
+ console.error(pc26.red(` Server '${serverName}' not found in lockfile or any client config.`));
5115
+ console.error(pc26.dim(` Run ${pc26.cyan("mcpman list")} to see installed servers.`));
5116
+ process.exit(1);
5117
+ }
5118
+ if (result.orphaned) {
5119
+ console.log(pc26.yellow(`
5120
+ Server '${serverName}' is orphaned:`));
5121
+ console.log(pc26.dim(" Found in client config(s) but not in lockfile."));
5122
+ console.log(pc26.dim(` Run ${pc26.cyan("mcpman sync --remove")} to clean up.
5123
+ `));
5124
+ const registeredClients = result.clients.filter((c) => c.registered).map((c) => c.type);
5125
+ if (registeredClients.length > 0) {
5126
+ console.log(` Registered in: ${registeredClients.join(", ")}`);
5127
+ }
5128
+ return;
5129
+ }
5130
+ if (asJson) {
5131
+ console.log(JSON.stringify(result, null, 2));
5132
+ return;
5133
+ }
5134
+ console.log();
5135
+ console.log(formatWhyOutput(result));
5136
+ console.log();
5137
+ }
5138
+ });
5139
+
3858
5140
  // src/index.ts
3859
5141
  process.on("SIGINT", () => {
3860
5142
  console.log("\nAborted.");
3861
5143
  process.exit(130);
3862
5144
  });
3863
- var main = defineCommand21({
5145
+ var main = defineCommand27({
3864
5146
  meta: {
3865
5147
  name: APP_NAME,
3866
5148
  version: APP_VERSION,
@@ -3886,7 +5168,13 @@ var main = defineCommand21({
3886
5168
  profiles: profiles_default,
3887
5169
  plugin: plugin_default,
3888
5170
  export: export_command_default,
3889
- import: import_command_default
5171
+ import: import_command_default,
5172
+ create: create_default,
5173
+ link: link_default,
5174
+ watch: watch_default,
5175
+ registry: registry_default,
5176
+ completions: completions_default,
5177
+ why: why_default
3890
5178
  }
3891
5179
  });
3892
5180
  runMain(main);