mcpick 0.0.24 → 0.0.26

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.
Files changed (39) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +8 -2
  3. package/dist/{add-Qzd8i-5k.js → add-DwUPxD5U.js} +21 -35
  4. package/dist/{add-json-DGmsjB0O.js → add-json-Dk3C8QSR.js} +18 -34
  5. package/dist/atomic-write-9pfXia-x.js +22 -0
  6. package/dist/{backup-C7fvikFw.js → backup-Ber7st-_.js} +4 -5
  7. package/dist/{cache-D3jjh5dD.js → cache-BVNAOxqk.js} +4 -3
  8. package/dist/{cli-CZOlaqoZ.js → cli-Dfein2li.js} +22 -22
  9. package/dist/{clients-Bh93TGP4.js → clients-C1mWlnsn.js} +3 -3
  10. package/dist/{clone-MI8jJhTz.js → clone-BE904ik7.js} +3 -6
  11. package/dist/{dev-51esdZG9.js → dev-BW_2JDPM.js} +5 -6
  12. package/dist/{disable-csYAn2Vk.js → disable-CVwuJLhT.js} +18 -37
  13. package/dist/{enable-B5GbmhL-.js → enable-BEi8TUd1.js} +18 -37
  14. package/dist/{get-DacRZmwv.js → get-eDOm6sRK.js} +3 -3
  15. package/dist/{hooks-C_x49qap.js → hooks-DzGBYYId.js} +4 -3
  16. package/dist/index.js +752 -645
  17. package/dist/{list-BeBtsiae.js → list-CvpW_mNZ.js} +3 -5
  18. package/dist/{marketplace-BDC2YtvT.js → marketplace-BKGJ3UGr.js} +4 -4
  19. package/dist/mutation-BBMIFB8H.js +19 -0
  20. package/dist/{output-HtT5HCof.js → output-DGPi2IWN.js} +2 -2
  21. package/dist/{plugin-cache-DmLbh89d.js → plugin-cache-Dj-lS1mA.js} +177 -22
  22. package/dist/{plugins-Bkw-SKkZ.js → plugins-elIQYM38.js} +4 -4
  23. package/dist/profile-NVbTnJKI.js +163 -0
  24. package/dist/{reload-Bl1mYK1I.js → reload-BkVtWaf0.js} +2 -2
  25. package/dist/{remove-BSHgva79.js → remove-BC2vomi_.js} +18 -33
  26. package/dist/{reset-project-choices-BNLus9J9.js → reset-project-choices-DhJsWQx2.js} +3 -3
  27. package/dist/{restore-YisgARhc.js → restore-CeYSOFt8.js} +3 -5
  28. package/dist/{rollback-GR1RkpXW.js → rollback-g-WX6b3o.js} +3 -3
  29. package/dist/safe-apply-B5flt8QC.js +185 -0
  30. package/dist/{skills-rDTDqqZA.js → skills-zuQ5kCL1.js} +3 -3
  31. package/package.json +6 -6
  32. package/pnpm-workspace.yaml +1 -0
  33. package/dist/config-DE58Fik_.js +0 -176
  34. package/dist/dry-run-XQ32fxPT.js +0 -20
  35. package/dist/paths-BPISiJi4.js +0 -124
  36. package/dist/profile-DwJTVXiz.js +0 -161
  37. package/dist/redact-wBMtzbno.js +0 -88
  38. package/dist/rolldown-runtime-CiIaOW0V.js +0 -13
  39. package/dist/validation-xMlbgGCF.js +0 -44
@@ -1,7 +1,5 @@
1
- import { o as get_enabled_servers_for_scope } from "./config-DE58Fik_.js";
2
- import { a as redact_url, n as redact_server, t as redact_portable_server } from "./redact-wBMtzbno.js";
3
- import { I as get_client_adapter, k as get_all_available_servers } from "./index.js";
4
- import { n as output, t as error } from "./output-HtT5HCof.js";
1
+ import { F as get_client_adapter, O as get_all_available_servers, S as redact_server, T as redact_url, Y as get_enabled_servers_for_scope, x as redact_portable_server } from "./index.js";
2
+ import { n as output, t as error } from "./output-DGPi2IWN.js";
5
3
  import { defineCommand } from "citty";
6
4
  //#region src/cli/commands/list.ts
7
5
  var list_default = defineCommand({
@@ -97,4 +95,4 @@ async function list_client_servers(client, scope, json) {
97
95
  //#endregion
98
96
  export { list_default as default };
99
97
 
100
- //# sourceMappingURL=list-BeBtsiae.js.map
98
+ //# sourceMappingURL=list-CvpW_mNZ.js.map
@@ -1,6 +1,6 @@
1
- import { u as read_marketplace_manifest } from "./plugin-cache-DmLbh89d.js";
2
- import { _ as marketplace_add_via_cli, b as marketplace_update_via_cli, d as redisable_restored_hooks, s as check_restored_hooks, v as marketplace_list_via_cli, y as marketplace_remove_via_cli } from "./index.js";
3
- import { n as output, t as error } from "./output-HtT5HCof.js";
1
+ import { _ as redisable_restored_hooks, l as read_marketplace_manifest, p as check_restored_hooks } from "./plugin-cache-Dj-lS1mA.js";
2
+ import { d as marketplace_list_via_cli, f as marketplace_remove_via_cli, p as marketplace_update_via_cli, u as marketplace_add_via_cli } from "./index.js";
3
+ import { n as output, t as error } from "./output-DGPi2IWN.js";
4
4
  import { defineCommand } from "citty";
5
5
  //#region src/cli/commands/marketplace.ts
6
6
  const list = defineCommand({
@@ -164,4 +164,4 @@ var marketplace_default = defineCommand({
164
164
  //#endregion
165
165
  export { marketplace_default as default };
166
166
 
167
- //# sourceMappingURL=marketplace-BDC2YtvT.js.map
167
+ //# sourceMappingURL=marketplace-BKGJ3UGr.js.map
@@ -0,0 +1,19 @@
1
+ import { s as get_claude_config_path, x as get_project_mcp_json_path } from "./safe-apply-B5flt8QC.js";
2
+ //#region src/cli/mutation.ts
3
+ function claude_mutation_context(operation, scope, servers) {
4
+ return {
5
+ operation,
6
+ client: "claude-code",
7
+ scope,
8
+ location: scope === "project" ? get_project_mcp_json_path() : get_claude_config_path(),
9
+ servers
10
+ };
11
+ }
12
+ function print_mutation_details(input) {
13
+ if (input.location) console.log(`Config: ${input.location}`);
14
+ if (input.backup_path) console.log(`Backup: ${input.backup_path}`);
15
+ }
16
+ //#endregion
17
+ export { print_mutation_details as n, claude_mutation_context as t };
18
+
19
+ //# sourceMappingURL=mutation-BBMIFB8H.js.map
@@ -1,4 +1,4 @@
1
- import { i as redact_text, o as redact_value } from "./redact-wBMtzbno.js";
1
+ import { E as redact_value, w as redact_text } from "./index.js";
2
2
  //#region src/cli/output.ts
3
3
  function output(data, json) {
4
4
  const safe_data = redact_value(data);
@@ -14,4 +14,4 @@ function error(message) {
14
14
  //#endregion
15
15
  export { output as n, error as t };
16
16
 
17
- //# sourceMappingURL=output-HtT5HCof.js.map
17
+ //# sourceMappingURL=output-DGPi2IWN.js.map
@@ -1,27 +1,175 @@
1
- import { t as __exportAll } from "./rolldown-runtime-CiIaOW0V.js";
2
- import { d as get_marketplace_manifest_path, h as get_plugin_cache_dir, l as get_installed_plugins_path, t as ensure_directory_exists, u as get_known_marketplaces_path } from "./paths-BPISiJi4.js";
3
- import { et as atomic_json_write } from "./index.js";
1
+ import { d as get_disabled_hooks_path, f as get_installed_plugins_path, g as get_mcpick_dir, h as get_marketplaces_dir, i as ensure_directory_exists, m as get_marketplace_manifest_path, p as get_known_marketplaces_path, r as safe_json_write, v as get_plugin_cache_dir } from "./safe-apply-B5flt8QC.js";
2
+ import { t as atomic_json_write } from "./atomic-write-9pfXia-x.js";
4
3
  import { lstat, readFile, readdir, readlink, rename, rm, symlink } from "node:fs/promises";
5
4
  import { join, resolve } from "node:path";
6
5
  import { execFile } from "node:child_process";
7
6
  import { promisify } from "node:util";
7
+ //#region src/core/hook-state.ts
8
+ async function read_disabled_hooks() {
9
+ try {
10
+ const content = await readFile(get_disabled_hooks_path(), "utf-8");
11
+ return JSON.parse(content);
12
+ } catch {
13
+ return [];
14
+ }
15
+ }
16
+ async function write_disabled_hooks(entries) {
17
+ await ensure_directory_exists(get_mcpick_dir());
18
+ await safe_json_write(get_disabled_hooks_path(), entries, " ");
19
+ }
20
+ /**
21
+ * Remove a specific hook handler from a hooks.json file by matching the handler.
22
+ * Returns true if the hook was found and removed.
23
+ */
24
+ async function remove_hook_from_file(hooks_path, event, handler) {
25
+ let content;
26
+ try {
27
+ content = await readFile(hooks_path, "utf-8");
28
+ } catch {
29
+ return false;
30
+ }
31
+ const hooks_data = JSON.parse(content);
32
+ const hooks_obj = hooks_data.hooks || hooks_data;
33
+ const matchers = hooks_obj[event];
34
+ if (!matchers) return false;
35
+ let removed = false;
36
+ for (const m of matchers) {
37
+ const idx = m.hooks?.findIndex((h) => h.type === handler.type && h.command === handler.command && h.url === handler.url && h.prompt === handler.prompt);
38
+ if (idx !== void 0 && idx >= 0) {
39
+ m.hooks.splice(idx, 1);
40
+ removed = true;
41
+ if (m.hooks.length === 0) matchers.splice(matchers.indexOf(m), 1);
42
+ break;
43
+ }
44
+ }
45
+ if (!removed) return false;
46
+ if (matchers.length === 0) delete hooks_obj[event];
47
+ await safe_json_write(hooks_path, hooks_data, " ");
48
+ return true;
49
+ }
50
+ /**
51
+ * Get all hooks.json paths for a plugin (cache + marketplace source).
52
+ */
53
+ function get_all_hooks_paths(plugin_key, primary_path) {
54
+ const paths = [primary_path];
55
+ const at_index = plugin_key.lastIndexOf("@");
56
+ if (at_index > 0) {
57
+ const plugin_name = plugin_key.substring(0, at_index);
58
+ const marketplace_name = plugin_key.substring(at_index + 1);
59
+ paths.push(join(get_marketplaces_dir(), marketplace_name, "plugins", plugin_name, "hooks", "hooks.json"));
60
+ }
61
+ return [...new Set(paths)];
62
+ }
63
+ /**
64
+ * Disable a specific hook from a plugin.
65
+ * Removes from both cache and marketplace source hooks.json files.
66
+ */
67
+ async function disable_plugin_hook(entry) {
68
+ if (!entry.hooks_json_path || !entry.plugin_key) throw new Error("Not a plugin hook");
69
+ const disabled = await read_disabled_hooks();
70
+ disabled.push({
71
+ plugin_key: entry.plugin_key,
72
+ hooks_json_path: entry.hooks_json_path,
73
+ event: entry.event,
74
+ matcher: entry.matcher,
75
+ matcher_index: entry.matcher_index,
76
+ hook_index: entry.hook_index,
77
+ original_handler: entry.handler,
78
+ disabled_at: (/* @__PURE__ */ new Date()).toISOString()
79
+ });
80
+ await write_disabled_hooks(disabled);
81
+ const all_paths = get_all_hooks_paths(entry.plugin_key, entry.hooks_json_path);
82
+ for (const hooks_path of all_paths) await remove_hook_from_file(hooks_path, entry.event, entry.handler);
83
+ }
84
+ /**
85
+ * Add a hook handler back into a hooks.json file.
86
+ */
87
+ async function add_hook_to_file(hooks_path, event, matcher_pattern, handler) {
88
+ let hooks_data;
89
+ try {
90
+ const content = await readFile(hooks_path, "utf-8");
91
+ hooks_data = JSON.parse(content);
92
+ } catch {
93
+ hooks_data = { hooks: {} };
94
+ }
95
+ const hooks_obj = hooks_data.hooks || (hooks_data.hooks = {});
96
+ if (!hooks_obj[event]) hooks_obj[event] = [];
97
+ const matchers = hooks_obj[event];
98
+ let matcher = matchers.find((m) => (m.matcher || void 0) === matcher_pattern);
99
+ if (!matcher) {
100
+ matcher = { hooks: [] };
101
+ if (matcher_pattern) matcher.matcher = matcher_pattern;
102
+ matchers.push(matcher);
103
+ }
104
+ if (matcher.hooks.some((h) => h.type === handler.type && h.command === handler.command && h.url === handler.url && h.prompt === handler.prompt)) return;
105
+ matcher.hooks.push(handler);
106
+ await safe_json_write(hooks_path, hooks_data, " ");
107
+ }
108
+ /**
109
+ * Re-enable a previously disabled plugin hook.
110
+ * Restores to both cache and marketplace source hooks.json files.
111
+ */
112
+ async function enable_plugin_hook(disabled_entry) {
113
+ const all_paths = get_all_hooks_paths(disabled_entry.plugin_key, disabled_entry.hooks_json_path);
114
+ for (const hooks_path of all_paths) await add_hook_to_file(hooks_path, disabled_entry.event, disabled_entry.matcher, disabled_entry.original_handler);
115
+ await write_disabled_hooks((await read_disabled_hooks()).filter((d) => !(d.plugin_key === disabled_entry.plugin_key && d.event === disabled_entry.event && d.disabled_at === disabled_entry.disabled_at)));
116
+ }
117
+ /**
118
+ * Check if any previously disabled hooks have been restored (e.g. by marketplace update).
119
+ * Returns entries that were re-added and need to be re-disabled.
120
+ */
121
+ async function check_restored_hooks() {
122
+ const disabled = await read_disabled_hooks();
123
+ if (disabled.length === 0) return [];
124
+ const restored = [];
125
+ for (const entry of disabled) {
126
+ const all_paths = get_all_hooks_paths(entry.plugin_key, entry.hooks_json_path);
127
+ let found = false;
128
+ for (const hooks_path of all_paths) {
129
+ let hooks_data;
130
+ try {
131
+ const content = await readFile(hooks_path, "utf-8");
132
+ hooks_data = JSON.parse(content);
133
+ } catch {
134
+ continue;
135
+ }
136
+ const matchers = (hooks_data.hooks || hooks_data)[entry.event];
137
+ if (!matchers) continue;
138
+ for (const m of matchers) {
139
+ if ((m.matcher || void 0) !== entry.matcher) continue;
140
+ if (m.hooks?.some((h) => h.type === entry.original_handler.type && (h.command === entry.original_handler.command || h.url === entry.original_handler.url || h.prompt === entry.original_handler.prompt))) {
141
+ found = true;
142
+ break;
143
+ }
144
+ }
145
+ if (found) break;
146
+ }
147
+ if (found) restored.push(entry);
148
+ }
149
+ return restored;
150
+ }
151
+ /**
152
+ * Re-disable hooks that were restored by a marketplace update.
153
+ */
154
+ async function redisable_restored_hooks(restored) {
155
+ let success = 0;
156
+ let failed = 0;
157
+ for (const entry of restored) try {
158
+ const all_paths = get_all_hooks_paths(entry.plugin_key, entry.hooks_json_path);
159
+ let any_removed = false;
160
+ for (const hooks_path of all_paths) if (await remove_hook_from_file(hooks_path, entry.event, entry.original_handler)) any_removed = true;
161
+ if (any_removed) success++;
162
+ else failed++;
163
+ } catch {
164
+ failed++;
165
+ }
166
+ return {
167
+ success,
168
+ failed
169
+ };
170
+ }
171
+ //#endregion
8
172
  //#region src/core/plugin-cache.ts
9
- var plugin_cache_exports = /* @__PURE__ */ __exportAll({
10
- clean_orphaned_versions: () => clean_orphaned_versions,
11
- clear_plugin_caches: () => clear_plugin_caches,
12
- get_cached_plugins_info: () => get_cached_plugins_info,
13
- link_local_plugin: () => link_local_plugin,
14
- list_linked_plugins: () => list_linked_plugins,
15
- parse_plugin_key: () => parse_plugin_key,
16
- read_installed_plugins: () => read_installed_plugins,
17
- read_known_marketplaces: () => read_known_marketplaces,
18
- read_marketplace_manifest: () => read_marketplace_manifest,
19
- refresh_all_marketplaces: () => refresh_all_marketplaces,
20
- refresh_marketplace: () => refresh_marketplace,
21
- scan_all_cache_keys: () => scan_all_cache_keys,
22
- unlink_local_plugin: () => unlink_local_plugin,
23
- write_installed_plugins: () => write_installed_plugins
24
- });
25
173
  const execFileAsync = promisify(execFile);
26
174
  function git(dir, args, timeout) {
27
175
  return execFileAsync("git", [
@@ -226,6 +374,7 @@ async function clear_plugin_caches(keys) {
226
374
  const marketplaces = await read_known_marketplaces();
227
375
  const cleared = [];
228
376
  const errors = [];
377
+ let redisabledHooks;
229
378
  const marketplace_names = /* @__PURE__ */ new Set();
230
379
  for (const key of keys) {
231
380
  const { marketplace } = parse_plugin_key(key);
@@ -238,6 +387,11 @@ async function clear_plugin_caches(keys) {
238
387
  if (!result.success) errors.push(`Marketplace refresh failed: ${result.error}`);
239
388
  }
240
389
  }
390
+ const restored_hooks = await check_restored_hooks();
391
+ if (restored_hooks.length > 0) {
392
+ redisabledHooks = await redisable_restored_hooks(restored_hooks);
393
+ if (redisabledHooks.failed > 0) errors.push(`Failed to re-disable ${redisabledHooks.failed} restored hook(s).`);
394
+ }
241
395
  const cache_dir = get_plugin_cache_dir();
242
396
  for (const key of keys) {
243
397
  const { name, marketplace } = parse_plugin_key(key);
@@ -261,7 +415,8 @@ async function clear_plugin_caches(keys) {
261
415
  await write_installed_plugins(installed);
262
416
  return {
263
417
  cleared,
264
- errors
418
+ errors,
419
+ ...redisabledHooks ? { redisabledHooks } : {}
265
420
  };
266
421
  }
267
422
  async function clean_orphaned_versions() {
@@ -419,6 +574,6 @@ async function list_linked_plugins() {
419
574
  return links;
420
575
  }
421
576
  //#endregion
422
- export { list_linked_plugins as a, read_installed_plugins as c, refresh_all_marketplaces as d, scan_all_cache_keys as f, link_local_plugin as i, read_known_marketplaces as l, clear_plugin_caches as n, parse_plugin_key as o, unlink_local_plugin as p, get_cached_plugins_info as r, plugin_cache_exports as s, clean_orphaned_versions as t, read_marketplace_manifest as u };
577
+ export { redisable_restored_hooks as _, list_linked_plugins as a, read_known_marketplaces as c, scan_all_cache_keys as d, unlink_local_plugin as f, read_disabled_hooks as g, enable_plugin_hook as h, link_local_plugin as i, read_marketplace_manifest as l, disable_plugin_hook as m, clear_plugin_caches as n, parse_plugin_key as o, check_restored_hooks as p, get_cached_plugins_info as r, read_installed_plugins as s, clean_orphaned_versions as t, refresh_all_marketplaces as u };
423
578
 
424
- //# sourceMappingURL=plugin-cache-DmLbh89d.js.map
579
+ //# sourceMappingURL=plugin-cache-Dj-lS1mA.js.map
@@ -1,6 +1,6 @@
1
- import { l as read_known_marketplaces, o as parse_plugin_key } from "./plugin-cache-DmLbh89d.js";
2
- import { $ as write_claude_settings, D as validate_plugin_via_cli, E as update_plugin_via_cli, J as build_enabled_plugins, T as uninstall_plugin_via_cli, X as get_all_plugins, Z as read_claude_settings, g as install_plugin_via_cli } from "./index.js";
3
- import { n as output, t as error } from "./output-HtT5HCof.js";
1
+ import { c as read_known_marketplaces, o as parse_plugin_key } from "./plugin-cache-Dj-lS1mA.js";
2
+ import { K as write_claude_settings, U as get_all_plugins, V as build_enabled_plugins, W as read_claude_settings, b as validate_plugin_via_cli, l as install_plugin_via_cli, v as uninstall_plugin_via_cli, y as update_plugin_via_cli } from "./index.js";
3
+ import { n as output, t as error } from "./output-DGPi2IWN.js";
4
4
  import { defineCommand } from "citty";
5
5
  var plugins_default = defineCommand({
6
6
  meta: {
@@ -208,4 +208,4 @@ var plugins_default = defineCommand({
208
208
  //#endregion
209
209
  export { plugins_default as default };
210
210
 
211
- //# sourceMappingURL=plugins-Bkw-SKkZ.js.map
211
+ //# sourceMappingURL=plugins-elIQYM38.js.map
@@ -0,0 +1,163 @@
1
+ import { F as get_client_adapter, R as resolve_client_location, a as save_profile_for_client, i as save_current_claude_profile, n as apply_profile_to_client, r as list_profiles, t as apply_profile_to_claude } from "./index.js";
2
+ import { n as print_mutation_details } from "./mutation-BBMIFB8H.js";
3
+ import { n as output, t as error } from "./output-DGPi2IWN.js";
4
+ import { defineCommand } from "citty";
5
+ //#region src/cli/commands/profile.ts
6
+ const CLIENTS = "claude-code, gemini-cli, vscode, cursor, windsurf, opencode, or pi";
7
+ var profile_default = defineCommand({
8
+ meta: {
9
+ name: "profile",
10
+ description: "Manage portable profiles (MCP servers + optional Claude plugins)"
11
+ },
12
+ subCommands: {
13
+ list: defineCommand({
14
+ meta: {
15
+ name: "list",
16
+ description: "List all saved profiles"
17
+ },
18
+ args: { json: {
19
+ type: "boolean",
20
+ description: "Output as JSON",
21
+ default: false
22
+ } },
23
+ async run({ args }) {
24
+ const profiles = await list_profiles();
25
+ if (args.json) output(profiles, true);
26
+ else {
27
+ if (profiles.length === 0) {
28
+ console.log("No profiles found.");
29
+ return;
30
+ }
31
+ for (const p of profiles) {
32
+ const parts = [`${p.serverCount} servers`];
33
+ if (p.pluginCount > 0) parts.push(`${p.pluginCount} plugins`);
34
+ console.log(`${p.name} (${parts.join(", ")})`);
35
+ }
36
+ }
37
+ }
38
+ }),
39
+ load: defineCommand({
40
+ meta: {
41
+ name: "load",
42
+ description: "Load and apply a saved profile"
43
+ },
44
+ args: {
45
+ name: {
46
+ type: "positional",
47
+ description: "Profile name",
48
+ required: true
49
+ },
50
+ client: {
51
+ type: "string",
52
+ description: `Client to apply to: ${CLIENTS}`
53
+ },
54
+ scope: {
55
+ type: "string",
56
+ description: "Scope: local, project, or user"
57
+ },
58
+ location: {
59
+ type: "string",
60
+ description: "Exact config path when a client has multiple matching locations"
61
+ },
62
+ json: {
63
+ type: "boolean",
64
+ description: "Output as JSON",
65
+ default: false
66
+ }
67
+ },
68
+ async run({ args }) {
69
+ try {
70
+ if (args.client || args.scope || args.location) {
71
+ const client = args.client || "claude-code";
72
+ const adapter = get_client_adapter(client);
73
+ if (!adapter) error(`Invalid client: ${client}. Use ${CLIENTS}.`);
74
+ resolve_client_location(adapter, args.scope, args.location);
75
+ const result = await apply_profile_to_client({
76
+ name: args.name,
77
+ client,
78
+ scope: args.scope,
79
+ location: args.location
80
+ });
81
+ if (args.json) output(result, true);
82
+ else {
83
+ console.log(`Profile '${result.profile}' applied to ${result.client}:${result.scope} (${result.serverCount} servers)`);
84
+ print_mutation_details(result);
85
+ }
86
+ return;
87
+ }
88
+ const result = await apply_profile_to_claude(args.name);
89
+ if (args.json) output(result, true);
90
+ else {
91
+ const parts = [`${result.serverCount} servers`];
92
+ if (result.pluginCount > 0) parts.push(`${result.pluginCount} plugins`);
93
+ console.log(`Profile '${result.profile}' applied (${parts.join(", ")})`);
94
+ }
95
+ } catch (err) {
96
+ error(err instanceof Error ? err.message : "Failed to load profile");
97
+ }
98
+ }
99
+ }),
100
+ save: defineCommand({
101
+ meta: {
102
+ name: "save",
103
+ description: "Save current config as a portable profile"
104
+ },
105
+ args: {
106
+ name: {
107
+ type: "positional",
108
+ description: "Profile name",
109
+ required: true
110
+ },
111
+ client: {
112
+ type: "string",
113
+ description: `Client to save from: ${CLIENTS}`
114
+ },
115
+ scope: {
116
+ type: "string",
117
+ description: "Scope: local, project, or user"
118
+ },
119
+ location: {
120
+ type: "string",
121
+ description: "Exact config path when a client has multiple matching locations"
122
+ },
123
+ json: {
124
+ type: "boolean",
125
+ description: "Output as JSON",
126
+ default: false
127
+ }
128
+ },
129
+ async run({ args }) {
130
+ try {
131
+ if (args.client || args.scope || args.location) {
132
+ const client = args.client || "claude-code";
133
+ const adapter = get_client_adapter(client);
134
+ if (!adapter) error(`Invalid client: ${client}. Use ${CLIENTS}.`);
135
+ resolve_client_location(adapter, args.scope, args.location);
136
+ const result = await save_profile_for_client({
137
+ name: args.name,
138
+ client,
139
+ scope: args.scope,
140
+ location: args.location
141
+ });
142
+ if (args.json) output(result, true);
143
+ else console.log(`Profile '${result.profile}' saved from ${result.client}:${result.scope} (${result.serverCount} servers)`);
144
+ return;
145
+ }
146
+ const result = await save_current_claude_profile(args.name);
147
+ if (args.json) output(result, true);
148
+ else {
149
+ const parts = [`${result.serverCount} servers`];
150
+ if (result.pluginCount > 0) parts.push(`${result.pluginCount} plugins`);
151
+ console.log(`Profile '${result.profile}' saved (${parts.join(", ")})`);
152
+ }
153
+ } catch (err) {
154
+ error(err instanceof Error ? err.message : "Failed to save profile");
155
+ }
156
+ }
157
+ })
158
+ }
159
+ });
160
+ //#endregion
161
+ export { profile_default as default };
162
+
163
+ //# sourceMappingURL=profile-NVbTnJKI.js.map
@@ -1,4 +1,4 @@
1
- import { n as output } from "./output-HtT5HCof.js";
1
+ import { n as output } from "./output-DGPi2IWN.js";
2
2
  import { defineCommand } from "citty";
3
3
  //#region src/cli/commands/reload.ts
4
4
  var reload_default = defineCommand({
@@ -28,4 +28,4 @@ var reload_default = defineCommand({
28
28
  //#endregion
29
29
  export { reload_default as default };
30
30
 
31
- //# sourceMappingURL=reload-Bl1mYK1I.js.map
31
+ //# sourceMappingURL=reload-BkVtWaf0.js.map
@@ -1,6 +1,6 @@
1
- import { B as preview_remove_client_server, G as build_command_preview, H as remove_client_server, I as get_client_adapter, M as read_server_registry, N as write_server_registry, U as resolve_client_location, h as build_remove_args, k as get_all_available_servers, w as remove_mcp_via_cli } from "./index.js";
2
- import { n as output, t as error } from "./output-HtT5HCof.js";
3
- import { t as print_dry_run } from "./dry-run-XQ32fxPT.js";
1
+ import { F as get_client_adapter, L as remove_client_server, M as write_server_registry, O as get_all_available_servers, R as resolve_client_location, _ as remove_mcp_via_cli, j as read_server_registry } from "./index.js";
2
+ import { n as print_mutation_details, t as claude_mutation_context } from "./mutation-BBMIFB8H.js";
3
+ import { n as output, t as error } from "./output-DGPi2IWN.js";
4
4
  import { defineCommand } from "citty";
5
5
  //#region src/cli/commands/remove.ts
6
6
  var remove_default = defineCommand({
@@ -27,11 +27,6 @@ var remove_default = defineCommand({
27
27
  type: "string",
28
28
  description: "Exact config path when a client has multiple matching locations"
29
29
  },
30
- dryRun: {
31
- type: "boolean",
32
- description: "Preview changes without writing",
33
- default: false
34
- },
35
30
  json: {
36
31
  type: "boolean",
37
32
  description: "Output as JSON",
@@ -40,7 +35,7 @@ var remove_default = defineCommand({
40
35
  },
41
36
  async run({ args }) {
42
37
  if (args.client && args.client !== "claude-code") {
43
- await remove_from_client(args.client, args.server, args.scope, args.location, args.json, args.dryRun);
38
+ await remove_from_client(args.client, args.server, args.scope, args.location, args.json);
44
39
  return;
45
40
  }
46
41
  const scope = args.scope || "local";
@@ -50,16 +45,6 @@ var remove_default = defineCommand({
50
45
  "user"
51
46
  ].includes(scope)) error(`Invalid scope: ${scope}. Use local, project, or user.`);
52
47
  if (!(await get_all_available_servers()).find((s) => s.name === args.server)) error(`Server '${args.server}' not found. Run 'mcpick list' to see available servers.`);
53
- if (args.dryRun) {
54
- print_dry_run(build_command_preview({
55
- operation: "remove-server",
56
- client: "claude-code",
57
- scope,
58
- location: "Claude Code CLI + mcpick registry",
59
- command: ["claude", ...build_remove_args(args.server, scope)]
60
- }), args.json);
61
- return;
62
- }
63
48
  const registry = await read_server_registry();
64
49
  const index = registry.servers.findIndex((s) => s.name === args.server);
65
50
  if (index >= 0) {
@@ -67,15 +52,18 @@ var remove_default = defineCommand({
67
52
  await write_server_registry(registry);
68
53
  }
69
54
  await remove_mcp_via_cli(args.server, scope);
55
+ const mutation = claude_mutation_context("remove", scope, [args.server]);
70
56
  if (args.json) output({
71
57
  removed: args.server,
72
- client: "claude-code",
73
- scope
58
+ ...mutation
74
59
  }, true);
75
- else console.log(`Removed '${args.server}'`);
60
+ else {
61
+ console.log(`Removed '${args.server}'`);
62
+ print_mutation_details(mutation);
63
+ }
76
64
  }
77
65
  });
78
- async function remove_from_client(client, server, scope, location_path, json, dry_run) {
66
+ async function remove_from_client(client, server, scope, location_path, json) {
79
67
  const adapter = get_client_adapter(client);
80
68
  if (!adapter) error(`Invalid client: ${client}. Use claude-code, gemini-cli, vscode, cursor, windsurf, opencode, or pi.`);
81
69
  if (scope && ![
@@ -85,18 +73,15 @@ async function remove_from_client(client, server, scope, location_path, json, dr
85
73
  ].includes(scope)) error(`Invalid scope: ${scope}. Use local, project, or user.`);
86
74
  try {
87
75
  const location = resolve_client_location(adapter, scope, location_path);
88
- if (dry_run) {
89
- print_dry_run(await preview_remove_client_server(adapter, location, server), json);
90
- return;
91
- }
92
- await remove_client_server(adapter, location, server);
76
+ const mutation = await remove_client_server(adapter, location, server);
93
77
  if (json) output({
94
78
  removed: server,
95
- client: adapter.id,
96
- scope: location.scope,
97
- location: location.path
79
+ ...mutation
98
80
  }, true);
99
- else console.log(`Removed '${server}' (${adapter.id}:${location.scope})`);
81
+ else {
82
+ console.log(`Removed '${server}' (${adapter.id}:${location.scope})`);
83
+ print_mutation_details(mutation);
84
+ }
100
85
  } catch (err) {
101
86
  error(err instanceof Error ? err.message : "Failed to remove server");
102
87
  }
@@ -104,4 +89,4 @@ async function remove_from_client(client, server, scope, location_path, json, dr
104
89
  //#endregion
105
90
  export { remove_default as default };
106
91
 
107
- //# sourceMappingURL=remove-BSHgva79.js.map
92
+ //# sourceMappingURL=remove-BC2vomi_.js.map
@@ -1,5 +1,5 @@
1
- import { C as mcp_reset_project_choices_via_cli } from "./index.js";
2
- import { n as output, t as error } from "./output-HtT5HCof.js";
1
+ import { g as mcp_reset_project_choices_via_cli } from "./index.js";
2
+ import { n as output, t as error } from "./output-DGPi2IWN.js";
3
3
  import { defineCommand } from "citty";
4
4
  //#region src/cli/commands/reset-project-choices.ts
5
5
  var reset_project_choices_default = defineCommand({
@@ -25,4 +25,4 @@ var reset_project_choices_default = defineCommand({
25
25
  //#endregion
26
26
  export { reset_project_choices_default as default };
27
27
 
28
- //# sourceMappingURL=reset-project-choices-BNLus9J9.js.map
28
+ //# sourceMappingURL=reset-project-choices-DhJsWQx2.js.map
@@ -1,7 +1,5 @@
1
- import { t as validate_claude_config } from "./validation-xMlbgGCF.js";
2
- import { c as write_claude_config } from "./config-DE58Fik_.js";
3
- import { $ as write_claude_settings, A as list_backups, j as list_plugin_backups } from "./index.js";
4
- import { n as output, t as error } from "./output-HtT5HCof.js";
1
+ import { A as list_plugin_backups, K as write_claude_settings, Q as validate_claude_config, Z as write_claude_config, k as list_backups } from "./index.js";
2
+ import { n as output, t as error } from "./output-DGPi2IWN.js";
5
3
  import { readFile } from "node:fs/promises";
6
4
  import { defineCommand } from "citty";
7
5
  //#region src/cli/commands/restore.ts
@@ -80,4 +78,4 @@ var restore_default = defineCommand({
80
78
  //#endregion
81
79
  export { restore_default as default };
82
80
 
83
- //# sourceMappingURL=restore-YisgARhc.js.map
81
+ //# sourceMappingURL=restore-CeYSOFt8.js.map
@@ -1,5 +1,5 @@
1
- import { nt as restore_config_backup, tt as list_config_backups } from "./index.js";
2
- import { n as output, t as error } from "./output-HtT5HCof.js";
1
+ import { n as restore_config_backup, t as list_config_backups } from "./safe-apply-B5flt8QC.js";
2
+ import { n as output, t as error } from "./output-DGPi2IWN.js";
3
3
  import { defineCommand } from "citty";
4
4
  //#region src/cli/commands/rollback.ts
5
5
  var rollback_default = defineCommand({
@@ -52,4 +52,4 @@ var rollback_default = defineCommand({
52
52
  //#endregion
53
53
  export { rollback_default as default };
54
54
 
55
- //# sourceMappingURL=rollback-GR1RkpXW.js.map
55
+ //# sourceMappingURL=rollback-g-WX6b3o.js.map