mcpick 0.0.23 → 0.0.25

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 (32) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +8 -2
  3. package/dist/{add-LJQa2my2.js → add-7mhUpbrt.js} +20 -13
  4. package/dist/{add-json-TEdYweZ5.js → add-json-BMM2L4hv.js} +16 -12
  5. package/dist/{backup-kyS5IVIr.js → backup-C-YJmgps.js} +4 -4
  6. package/dist/{cache-DTfzTsEE.js → cache-BOYZhUF6.js} +4 -3
  7. package/dist/{cli-By-0nYNQ.js → cli-sOeHH4CK.js} +22 -22
  8. package/dist/{clients-qMozizys.js → clients-D5KAuQ5U.js} +3 -3
  9. package/dist/{clone-BVhYjRGO.js → clone-BRJA55js.js} +5 -5
  10. package/dist/{config-DzMmTJYL.js → config-Bzh374VP.js} +4 -13
  11. package/dist/{dev-Cst8WkQ-.js → dev-B-WlQSqY.js} +4 -4
  12. package/dist/{disable-BaOs9lrm.js → disable-Br0aVG3u.js} +16 -12
  13. package/dist/{enable--3mjSmTq.js → enable-DUolKCEH.js} +16 -12
  14. package/dist/{get-CjhNWyRj.js → get-D-6Cl_CO.js} +3 -3
  15. package/dist/{hooks-DFmxgD0t.js → hooks-BKPmZViU.js} +3 -3
  16. package/dist/index.js +595 -393
  17. package/dist/{list-D5CkCXpP.js → list-DMcaHDfM.js} +4 -4
  18. package/dist/{marketplace-C3EGyIG0.js → marketplace-DTW7Ys8k.js} +4 -4
  19. package/dist/mutation-ukRPw3qM.js +19 -0
  20. package/dist/{output-HtT5HCof.js → output-BS1TMOWt.js} +1 -1
  21. package/dist/{plugin-cache-BSgB42wa.js → plugin-cache-Dw1I2YuO.js} +10 -3
  22. package/dist/{plugins-Dn2mPFKm.js → plugins-BzLD4og0.js} +4 -4
  23. package/dist/profile-CmIWUJH_.js +163 -0
  24. package/dist/{reload-257iU7Z7.js → reload-Di28s_rY.js} +2 -2
  25. package/dist/{remove-26XFzkPd.js → remove-B32EuYRC.js} +16 -11
  26. package/dist/{reset-project-choices-D2F04LfC.js → reset-project-choices-DX4TnZ2i.js} +3 -3
  27. package/dist/{restore-BYYsoNqF.js → restore-ByS4xi0y.js} +5 -5
  28. package/dist/{rollback-CPdaME91.js → rollback-DdDJrA8y.js} +3 -3
  29. package/dist/{skills-DfWk9mpk.js → skills-pvyQ17XU.js} +3 -3
  30. package/dist/{validation-xMlbgGCF.js → validation-CfPAjPJ5.js} +21 -2
  31. package/package.json +1 -1
  32. package/dist/profile-Dq3ORPil.js +0 -119
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { _ as get_profiles_dir, a as get_claude_settings_path, c as get_disabled_hooks_path, f as get_marketplaces_dir, g as get_profile_path, i as get_claude_config_path, m as get_plugin_backup_filename, n as get_backup_filename, p as get_mcpick_dir, r as get_backups_dir, t as ensure_directory_exists, y as get_server_registry_path } from "./paths-BPISiJi4.js";
3
- import { r as validate_server_registry, t as validate_claude_config } from "./validation-xMlbgGCF.js";
4
- import { a as get_enabled_servers, c as write_claude_config, n as create_config_from_servers, o as get_enabled_servers_for_scope, s as read_claude_config } from "./config-DzMmTJYL.js";
3
+ import { n as validate_mcp_server, r as validate_server_registry, t as validate_claude_config } from "./validation-CfPAjPJ5.js";
4
+ import { i as get_enabled_servers, o as read_claude_config, s as write_claude_config } from "./config-Bzh374VP.js";
5
5
  import { a as redact_url, i as redact_text } from "./redact-wBMtzbno.js";
6
- import { d as refresh_all_marketplaces, l as read_known_marketplaces, n as clear_plugin_caches, r as get_cached_plugins_info, t as clean_orphaned_versions, u as read_marketplace_manifest } from "./plugin-cache-BSgB42wa.js";
6
+ import { d as refresh_all_marketplaces, l as read_known_marketplaces, n as clear_plugin_caches, r as get_cached_plugins_info, t as clean_orphaned_versions, u as read_marketplace_manifest } from "./plugin-cache-Dw1I2YuO.js";
7
7
  import { cancel, confirm, intro, isCancel, log, multiselect, note, outro, select, text } from "@clack/prompts";
8
8
  import { access, mkdir, readFile, readdir, rename, rm, unlink, writeFile } from "node:fs/promises";
9
9
  import { basename, dirname, join, resolve } from "node:path";
@@ -215,7 +215,7 @@ async function get_all_hooks() {
215
215
  * Checks both cache and marketplace source paths since Claude Code reads from both.
216
216
  */
217
217
  async function get_all_plugin_hooks() {
218
- const { read_installed_plugins } = await import("./plugin-cache-BSgB42wa.js").then((n) => n.s);
218
+ const { read_installed_plugins } = await import("./plugin-cache-Dw1I2YuO.js").then((n) => n.s);
219
219
  const { get_marketplaces_dir } = await import("./paths-BPISiJi4.js").then((n) => n.b);
220
220
  const installed = await read_installed_plugins();
221
221
  const entries = [];
@@ -377,7 +377,7 @@ async function read_json_file(path) {
377
377
  }
378
378
  }
379
379
  async function write_json_file(path, data) {
380
- await safe_json_write(path, data, 2);
380
+ return safe_json_write(path, data, 2);
381
381
  }
382
382
  function parse_json_or_jsonc(content) {
383
383
  try {
@@ -466,13 +466,13 @@ function remove_trailing_commas(content) {
466
466
  }
467
467
  return result;
468
468
  }
469
- function string_record(value) {
469
+ function string_record$1(value) {
470
470
  if (!value || typeof value !== "object" || Array.isArray(value)) return;
471
471
  const result = {};
472
472
  for (const [key, item] of Object.entries(value)) if (typeof item === "string") result[key] = item;
473
473
  return Object.keys(result).length > 0 ? result : void 0;
474
474
  }
475
- function string_array(value) {
475
+ function string_array$1(value) {
476
476
  if (!Array.isArray(value)) return void 0;
477
477
  const values = value.filter((item) => typeof item === "string");
478
478
  return values.length > 0 ? values : void 0;
@@ -488,10 +488,10 @@ function normalize_mcp_server(name, config) {
488
488
  const url = typeof config.httpUrl === "string" ? config.httpUrl : typeof config.serverUrl === "string" ? config.serverUrl : typeof config.url === "string" ? config.url : void 0;
489
489
  const client_options = {};
490
490
  for (const [key, value] of Object.entries(config)) if (!client_options_to_skip.has(key)) client_options[key] = value;
491
- const command_array = string_array(config.command);
491
+ const command_array = string_array$1(config.command);
492
492
  const command = typeof config.command === "string" ? config.command : command_array?.[0];
493
- const args = string_array(config.args) ?? command_array?.slice(1);
494
- const env = string_record(config.env) ?? string_record(config.environment);
493
+ const args = string_array$1(config.args) ?? command_array?.slice(1);
494
+ const env = string_record$1(config.env) ?? string_record$1(config.environment);
495
495
  const disabled = typeof config.disabled === "boolean" ? config.disabled : typeof config.enabled === "boolean" ? !config.enabled : void 0;
496
496
  return {
497
497
  name,
@@ -500,7 +500,7 @@ function normalize_mcp_server(name, config) {
500
500
  ...args && args.length > 0 ? { args } : {},
501
501
  ...url ? { url } : {},
502
502
  ...env ? { env } : {},
503
- ...string_record(config.headers) ? { headers: string_record(config.headers) } : {},
503
+ ...string_record$1(config.headers) ? { headers: string_record$1(config.headers) } : {},
504
504
  ...typeof config.description === "string" ? { description: config.description } : {},
505
505
  ...typeof disabled === "boolean" ? { disabled } : {},
506
506
  ...Object.keys(client_options).length > 0 ? { client_options } : {}
@@ -537,6 +537,9 @@ function portable_to_json(server, mode) {
537
537
  if (typeof server.disabled === "boolean") set_server_enabled(result, !server.disabled, mode);
538
538
  return result;
539
539
  }
540
+ function portable_server_map(servers, mode) {
541
+ return Object.fromEntries(servers.map((server) => [server.name, portable_to_json(server, mode)]));
542
+ }
540
543
  function create_json_adapter(options) {
541
544
  return {
542
545
  id: options.id,
@@ -557,28 +560,33 @@ function create_json_adapter(options) {
557
560
  const enabled = new Set(enabled_names);
558
561
  for (const [name, config] of Object.entries(servers)) set_server_enabled(config, enabled.has(name), options.disabledMode ?? "disabled");
559
562
  data[options.serverKey] = servers;
560
- await write_json_file(location.path, data);
563
+ return write_json_file(location.path, data);
561
564
  },
562
565
  async write_server(location, server) {
563
566
  const data = await read_json_file(location.path) ?? {};
564
567
  const servers = get_server_record(data, options.serverKey);
565
568
  servers[server.name] = portable_to_json(server, options.disabledMode ?? "disabled");
566
569
  data[options.serverKey] = servers;
567
- await write_json_file(location.path, data);
570
+ return write_json_file(location.path, data);
568
571
  },
569
572
  async write_server_config(location, name, config) {
570
573
  const data = await read_json_file(location.path) ?? {};
571
574
  const servers = get_server_record(data, options.serverKey);
572
575
  servers[name] = config;
573
576
  data[options.serverKey] = servers;
574
- await write_json_file(location.path, data);
577
+ return write_json_file(location.path, data);
575
578
  },
576
579
  async remove_server(location, name) {
577
580
  const data = await read_json_file(location.path) ?? {};
578
581
  const servers = get_server_record(data, options.serverKey);
579
582
  delete servers[name];
580
583
  data[options.serverKey] = servers;
581
- await write_json_file(location.path, data);
584
+ return write_json_file(location.path, data);
585
+ },
586
+ async write_servers(location, servers) {
587
+ const data = await read_json_file(location.path) ?? {};
588
+ data[options.serverKey] = portable_server_map(servers, options.disabledMode ?? "disabled");
589
+ return write_json_file(location.path, data);
582
590
  }
583
591
  };
584
592
  }
@@ -621,6 +629,24 @@ const client_adapters = [
621
629
  const project_config = projects[process.cwd()];
622
630
  if (!project_config || typeof project_config !== "object" || Array.isArray(project_config)) return [];
623
631
  return read_server_map(project_config, "mcpServers");
632
+ },
633
+ async write_servers(location, servers) {
634
+ const data = await read_json_file(location.path) ?? (location.scope === "project" ? {} : { projects: {} });
635
+ const mcp_servers = portable_server_map(servers, "disabled");
636
+ if (location.scope === "project") {
637
+ data.mcpServers = mcp_servers;
638
+ return write_json_file(location.path, data);
639
+ }
640
+ if (location.scope === "user") {
641
+ data.mcpServers = mcp_servers;
642
+ return write_json_file(get_claude_config_path(), data);
643
+ }
644
+ const projects = data.projects && typeof data.projects === "object" && !Array.isArray(data.projects) ? data.projects : {};
645
+ const project_config = projects[process.cwd()] && typeof projects[process.cwd()] === "object" && !Array.isArray(projects[process.cwd()]) ? projects[process.cwd()] : {};
646
+ project_config.mcpServers = mcp_servers;
647
+ projects[process.cwd()] = project_config;
648
+ data.projects = projects;
649
+ return write_json_file(get_claude_config_path(), data);
624
650
  }
625
651
  },
626
652
  create_json_adapter({
@@ -717,6 +743,17 @@ const client_adapters = [
717
743
  function get_client_adapter(id) {
718
744
  return client_adapters.find((adapter) => adapter.id === id) ?? null;
719
745
  }
746
+ function mutation_result(adapter, location, operation, servers, write_result, enabledCount) {
747
+ return {
748
+ operation,
749
+ client: adapter.id,
750
+ scope: location.scope,
751
+ location: write_result.path,
752
+ servers,
753
+ ...enabledCount !== void 0 ? { enabledCount } : {},
754
+ ...write_result.backup_path ? { backup_path: write_result.backup_path } : {}
755
+ };
756
+ }
720
757
  function resolve_client_location(adapter, scope, path) {
721
758
  let locations = adapter.locations();
722
759
  if (path) locations = locations.filter((location) => location.path === path);
@@ -727,26 +764,44 @@ function resolve_client_location(adapter, scope, path) {
727
764
  }
728
765
  async function add_client_server(adapter, location, server) {
729
766
  if (!adapter.write_server) throw new Error(`${adapter.label} support cannot add servers yet.`);
730
- await adapter.write_server(location, server);
767
+ const write_result = await adapter.write_server(location, server);
768
+ return mutation_result(adapter, location, "add", [server.name], write_result);
731
769
  }
732
770
  async function add_client_server_config(adapter, location, name, config) {
733
771
  if (!adapter.write_server_config) throw new Error(`${adapter.label} support cannot add servers yet.`);
734
- await adapter.write_server_config(location, name, config);
772
+ const write_result = await adapter.write_server_config(location, name, config);
773
+ return mutation_result(adapter, location, "add", [name], write_result);
735
774
  }
736
775
  async function remove_client_server(adapter, location, server_name) {
737
776
  if (!adapter.remove_server) throw new Error(`${adapter.label} support cannot remove servers yet.`);
738
- await adapter.remove_server(location, server_name);
777
+ const write_result = await adapter.remove_server(location, server_name);
778
+ return mutation_result(adapter, location, "remove", [server_name], write_result);
739
779
  }
740
- async function set_client_server_enabled(adapter, location, server_name, enabled) {
780
+ async function set_client_enabled_servers(adapter, location, enabled_names) {
741
781
  if (!adapter.writeEnabled) throw new Error(`${adapter.label} support is read-only.`);
782
+ const servers = await adapter.readLocation(location);
783
+ const known_names = new Set(servers.map((server) => server.name));
784
+ const unknown_names = enabled_names.filter((name) => !known_names.has(name));
785
+ if (unknown_names.length > 0) throw new Error(`Server '${unknown_names[0]}' not found at ${location.path}.`);
786
+ return mutation_result(adapter, location, "set-enabled", enabled_names, await adapter.writeEnabled(location, enabled_names), enabled_names.length);
787
+ }
788
+ async function replace_client_servers(adapter, location, servers) {
789
+ if (!adapter.write_servers) throw new Error(`${adapter.label} support cannot replace server profiles yet.`);
790
+ const write_result = await adapter.write_servers(location, servers);
791
+ return mutation_result(adapter, location, "replace", servers.map((server) => server.name), write_result, servers.filter((server) => server.disabled !== true).length);
792
+ }
793
+ async function set_client_server_enabled(adapter, location, server_name, enabled) {
742
794
  const servers = await adapter.readLocation(location);
743
795
  const server = servers.find((candidate) => candidate.name === server_name);
744
796
  if (!server) throw new Error(`Server '${server_name}' not found at ${location.path}.`);
745
797
  const enabled_names = new Set(servers.filter((candidate) => candidate.disabled !== true).map((candidate) => candidate.name));
746
798
  if (enabled) enabled_names.add(server.name);
747
799
  else enabled_names.delete(server.name);
748
- await adapter.writeEnabled(location, [...enabled_names]);
749
- return enabled_names.size;
800
+ return {
801
+ ...await set_client_enabled_servers(adapter, location, [...enabled_names]),
802
+ operation: enabled ? "enable" : "disable",
803
+ servers: [server_name]
804
+ };
750
805
  }
751
806
  async function list_client_locations() {
752
807
  return await Promise.all(client_adapters.flatMap((adapter) => adapter.locations().map(async (location) => ({
@@ -758,16 +813,56 @@ async function list_client_locations() {
758
813
  }
759
814
  //#endregion
760
815
  //#region src/core/registry.ts
816
+ function is_object$1(value) {
817
+ return !!value && typeof value === "object" && !Array.isArray(value);
818
+ }
819
+ function portable_to_mcp_server(server) {
820
+ return validate_mcp_server({
821
+ name: server.name,
822
+ ...server.transport !== "stdio" ? { type: server.transport } : {},
823
+ ...server.command ? { command: server.command } : {},
824
+ ...server.args ? { args: server.args } : {},
825
+ ...server.url ? { url: server.url } : {},
826
+ ...server.env ? { env: server.env } : {},
827
+ ...server.headers ? { headers: server.headers } : {},
828
+ ...server.description ? { description: server.description } : {}
829
+ });
830
+ }
831
+ function mcp_server_to_portable(server) {
832
+ const { name, ...config } = server;
833
+ return normalize_mcp_server(name, config);
834
+ }
835
+ function parse_portable_registry(data) {
836
+ if (!is_object$1(data) || !Array.isArray(data.servers)) throw new Error("Invalid server registry: expected servers array");
837
+ if (data.version === 3) return validate_server_registry(data);
838
+ return validate_server_registry({
839
+ version: 3,
840
+ servers: data.servers.map((server) => {
841
+ if (!is_object$1(server)) throw new Error("Invalid server registry entry");
842
+ if (typeof server.transport === "string") {
843
+ if (typeof server.name !== "string") throw new Error("Invalid server registry entry");
844
+ return normalize_mcp_server(server.name, server);
845
+ }
846
+ return mcp_server_to_portable(validate_mcp_server(server));
847
+ })
848
+ });
849
+ }
761
850
  async function read_server_registry() {
762
851
  const registry_path = get_server_registry_path();
763
852
  try {
764
853
  await access(registry_path);
765
854
  const registry_content = await readFile(registry_path, "utf-8");
766
- return validate_server_registry(JSON.parse(registry_content));
855
+ const parsed_registry = JSON.parse(registry_content);
856
+ const registry = parse_portable_registry(parsed_registry);
857
+ if (parsed_registry.version !== 3) await write_server_registry(registry);
858
+ return registry;
767
859
  } catch (error) {
768
860
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
769
861
  await ensure_directory_exists(get_mcpick_dir());
770
- const default_registry = { servers: [] };
862
+ const default_registry = {
863
+ version: 3,
864
+ servers: []
865
+ };
771
866
  await write_server_registry(default_registry);
772
867
  return default_registry;
773
868
  }
@@ -781,13 +876,14 @@ async function write_server_registry(registry) {
781
876
  }
782
877
  async function add_server_to_registry(server) {
783
878
  const registry = await read_server_registry();
879
+ const portable = mcp_server_to_portable(server);
784
880
  const existing_index = registry.servers.findIndex((s) => s.name === server.name);
785
- if (existing_index >= 0) registry.servers[existing_index] = server;
786
- else registry.servers.push(server);
881
+ if (existing_index >= 0) registry.servers[existing_index] = portable;
882
+ else registry.servers.push(portable);
787
883
  await write_server_registry(registry);
788
884
  }
789
885
  async function get_all_available_servers() {
790
- const { get_enabled_servers, read_claude_config } = await import("./config-DzMmTJYL.js").then((n) => n.t);
886
+ const { get_enabled_servers, read_claude_config } = await import("./config-Bzh374VP.js").then((n) => n.t);
791
887
  const registry = await read_server_registry();
792
888
  const config_servers = get_enabled_servers(await read_claude_config());
793
889
  const config_by_name = new Map(config_servers.map((s) => [s.name, s]));
@@ -798,25 +894,16 @@ async function get_all_available_servers() {
798
894
  known_names.add(name);
799
895
  const config_server = config_by_name.get(name);
800
896
  if (config_server) {
801
- registry.servers[i] = config_server;
897
+ registry.servers[i] = mcp_server_to_portable(config_server);
802
898
  registry_updated = true;
803
899
  }
804
900
  }
805
901
  for (const server of config_servers) if (!known_names.has(server.name)) {
806
- registry.servers.push(server);
902
+ registry.servers.push(mcp_server_to_portable(server));
807
903
  registry_updated = true;
808
904
  }
809
905
  if (registry_updated) await write_server_registry(registry);
810
- return registry.servers;
811
- }
812
- async function sync_servers_to_registry(servers) {
813
- const registry = await read_server_registry();
814
- servers.forEach((server) => {
815
- const existing_index = registry.servers.findIndex((s) => s.name === server.name);
816
- if (existing_index >= 0) registry.servers[existing_index] = server;
817
- else registry.servers.push(server);
818
- });
819
- await write_server_registry(registry);
906
+ return registry.servers.map(portable_to_mcp_server);
820
907
  }
821
908
  function parse_backups(prefix, pattern) {
822
909
  return async () => {
@@ -842,6 +929,110 @@ function parse_backups(prefix, pattern) {
842
929
  const list_backups = parse_backups("mcp-servers-", /mcp-servers-(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})(\d{2})\.json/);
843
930
  const list_plugin_backups = parse_backups("plugins-", /plugins-(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})(\d{2})\.json/);
844
931
  //#endregion
932
+ //#region src/commands/edit-config.ts
933
+ async function edit_config() {
934
+ try {
935
+ const sorted_adapters = [...client_adapters].sort((a, b) => b.label.localeCompare(a.label));
936
+ const client_id = await select({
937
+ message: "Which MCP client do you want to edit?",
938
+ options: sorted_adapters.map((adapter) => ({
939
+ value: adapter.id,
940
+ label: adapter.label
941
+ })),
942
+ initialValue: sorted_adapters[0]?.id
943
+ });
944
+ if (typeof client_id === "symbol") return;
945
+ const adapter = client_adapters.find((candidate) => candidate.id === client_id);
946
+ if (!adapter) return;
947
+ if (adapter.id === "claude-code") {
948
+ await edit_claude_config(adapter);
949
+ return;
950
+ }
951
+ await edit_client_config(adapter);
952
+ } catch (error) {
953
+ throw new Error(`Failed to edit configuration: ${error instanceof Error ? error.message : "Unknown error"}`);
954
+ }
955
+ }
956
+ async function edit_client_config(adapter) {
957
+ if (!adapter.writeEnabled) {
958
+ note(`${adapter.label} support is read-only for now.`);
959
+ return;
960
+ }
961
+ const location = await select_config_location(adapter);
962
+ if (!location) return;
963
+ const servers = await adapter.readLocation(location);
964
+ if (servers.length === 0) {
965
+ note(`No MCP servers found at ${location.path}.`);
966
+ return;
967
+ }
968
+ const selected_names = await multiselect({
969
+ message: `Toggle MCP servers for ${adapter.label}:`,
970
+ options: servers.map((server) => ({
971
+ value: server.name,
972
+ label: server.name,
973
+ hint: server_hint(server)
974
+ })),
975
+ initialValues: servers.filter((server) => server.disabled !== true).map((server) => server.name),
976
+ required: false
977
+ });
978
+ if (typeof selected_names === "symbol") return;
979
+ const mutation = await set_client_enabled_servers(adapter, location, selected_names);
980
+ note(`Configuration updated!\nClient: ${adapter.label}\nConfig: ${mutation.location}\n` + (mutation.backup_path ? `Backup: ${mutation.backup_path}\n` : "") + `Enabled servers: ${selected_names.length}`);
981
+ }
982
+ async function select_config_location(adapter) {
983
+ const locations = adapter.locations();
984
+ if (locations.length === 1) return locations[0];
985
+ const location_path = await select({
986
+ message: `Which ${adapter.label} configuration do you want to edit?`,
987
+ options: locations.map((location) => ({
988
+ value: location.path,
989
+ label: `${location.scope} — ${location.description}`,
990
+ hint: location.path
991
+ }))
992
+ });
993
+ if (typeof location_path === "symbol") return null;
994
+ return locations.find((location) => location.path === location_path) ?? null;
995
+ }
996
+ function server_hint(server) {
997
+ return [
998
+ server.disabled === true ? "off" : "on",
999
+ server.command ? [server.command, ...server.args ?? []].join(" ") : server.url ? redact_url(server.url) : server.transport,
1000
+ server.description
1001
+ ].filter(Boolean).join(" · ");
1002
+ }
1003
+ async function edit_claude_config(adapter) {
1004
+ const location = await select_config_location(adapter);
1005
+ if (!location) return;
1006
+ const registry_servers = (await get_all_available_servers()).map((server) => {
1007
+ const { name, ...config } = server;
1008
+ return normalize_mcp_server(name, config);
1009
+ });
1010
+ const current_servers = await adapter.readLocation(location);
1011
+ const servers_by_name = /* @__PURE__ */ new Map();
1012
+ for (const server of registry_servers) servers_by_name.set(server.name, server);
1013
+ for (const server of current_servers) if (!servers_by_name.has(server.name)) servers_by_name.set(server.name, server);
1014
+ const all_servers = [...servers_by_name.values()];
1015
+ if (all_servers.length === 0) {
1016
+ note("No MCP servers found in this Claude Code config or registry. Add servers with the CLI first.");
1017
+ return;
1018
+ }
1019
+ const currently_enabled = current_servers.filter((server) => server.disabled !== true).map((server) => server.name);
1020
+ const selected_server_names = await multiselect({
1021
+ message: `Select MCP servers for ${location.description}:`,
1022
+ options: all_servers.map((server) => ({
1023
+ value: server.name,
1024
+ label: server.name,
1025
+ hint: server_hint(server)
1026
+ })),
1027
+ initialValues: currently_enabled,
1028
+ required: false
1029
+ });
1030
+ if (typeof selected_server_names === "symbol") return;
1031
+ const selected_servers = all_servers.filter((server) => selected_server_names.includes(server.name));
1032
+ const mutation = await replace_client_servers(adapter, location, selected_servers);
1033
+ note(`Configuration updated!\nClient: ${adapter.label}\nConfig: ${mutation.location}\n` + (mutation.backup_path ? `Backup: ${mutation.backup_path}\n` : "") + `Enabled servers: ${selected_servers.length}`);
1034
+ }
1035
+ //#endregion
845
1036
  //#region src/utils/claude-cli.ts
846
1037
  const exec_file_async$1 = promisify(execFile);
847
1038
  /**
@@ -1272,16 +1463,6 @@ async function marketplace_list_via_cli() {
1272
1463
  }
1273
1464
  }
1274
1465
  /**
1275
- * Get the scope description for display
1276
- */
1277
- function get_scope_description(scope) {
1278
- switch (scope) {
1279
- case "local": return "This project only (default)";
1280
- case "project": return "Shared via .mcp.json (version controlled)";
1281
- case "user": return "Global - all projects";
1282
- }
1283
- }
1284
- /**
1285
1466
  * Validate a plugin or marketplace manifest via Claude CLI
1286
1467
  */
1287
1468
  async function validate_plugin_via_cli(path) {
@@ -1374,165 +1555,6 @@ async function mcp_reset_project_choices_via_cli() {
1374
1555
  };
1375
1556
  }
1376
1557
  }
1377
- /**
1378
- * Get scope options for select prompt
1379
- */
1380
- function get_scope_options() {
1381
- return [
1382
- {
1383
- value: "local",
1384
- label: "Local",
1385
- hint: "This project only (default)"
1386
- },
1387
- {
1388
- value: "project",
1389
- label: "Project",
1390
- hint: "Shared via .mcp.json (git)"
1391
- },
1392
- {
1393
- value: "user",
1394
- label: "User (Global)",
1395
- hint: "Available in all projects"
1396
- }
1397
- ];
1398
- }
1399
- //#endregion
1400
- //#region src/commands/edit-config.ts
1401
- async function edit_config() {
1402
- try {
1403
- const client_id = await select({
1404
- message: "Which MCP client do you want to edit?",
1405
- options: client_adapters.map((adapter) => ({
1406
- value: adapter.id,
1407
- label: adapter.label
1408
- })),
1409
- initialValue: "claude-code"
1410
- });
1411
- if (typeof client_id === "symbol") return;
1412
- const adapter = client_adapters.find((candidate) => candidate.id === client_id);
1413
- if (!adapter) return;
1414
- if (adapter.id === "claude-code") {
1415
- await edit_claude_config();
1416
- return;
1417
- }
1418
- await edit_client_config(adapter);
1419
- } catch (error) {
1420
- throw new Error(`Failed to edit configuration: ${error instanceof Error ? error.message : "Unknown error"}`);
1421
- }
1422
- }
1423
- async function edit_client_config(adapter) {
1424
- if (!adapter.writeEnabled) {
1425
- note(`${adapter.label} support is read-only for now.`);
1426
- return;
1427
- }
1428
- const location = await select_config_location(adapter);
1429
- if (!location) return;
1430
- const servers = await adapter.readLocation(location);
1431
- if (servers.length === 0) {
1432
- note(`No MCP servers found at ${location.path}.`);
1433
- return;
1434
- }
1435
- const selected_names = await multiselect({
1436
- message: `Toggle MCP servers for ${adapter.label}:`,
1437
- options: servers.map((server) => ({
1438
- value: server.name,
1439
- label: server.name,
1440
- hint: server_hint(server)
1441
- })),
1442
- initialValues: servers.filter((server) => server.disabled !== true).map((server) => server.name),
1443
- required: false
1444
- });
1445
- if (typeof selected_names === "symbol") return;
1446
- await adapter.writeEnabled(location, selected_names);
1447
- note(`Configuration updated!\nClient: ${adapter.label}\nConfig: ${location.path}\nEnabled servers: ${selected_names.length}`);
1448
- }
1449
- async function select_config_location(adapter) {
1450
- const locations = adapter.locations();
1451
- if (locations.length === 1) return locations[0];
1452
- const location_path = await select({
1453
- message: `Which ${adapter.label} configuration do you want to edit?`,
1454
- options: locations.map((location) => ({
1455
- value: location.path,
1456
- label: `${location.scope} — ${location.description}`,
1457
- hint: location.path
1458
- }))
1459
- });
1460
- if (typeof location_path === "symbol") return null;
1461
- return locations.find((location) => location.path === location_path) ?? null;
1462
- }
1463
- function server_hint(server) {
1464
- return [
1465
- server.disabled === true ? "off" : "on",
1466
- server.command ? [server.command, ...server.args ?? []].join(" ") : server.url ? redact_url(server.url) : server.transport,
1467
- server.description
1468
- ].filter(Boolean).join(" · ");
1469
- }
1470
- async function edit_claude_config() {
1471
- const cli_available = await check_claude_cli();
1472
- const scope = await select({
1473
- message: "Which Claude Code configuration do you want to edit?",
1474
- options: get_scope_options(),
1475
- initialValue: "local"
1476
- });
1477
- if (typeof scope === "symbol") return;
1478
- const current_config = await read_claude_config();
1479
- let all_servers = await get_all_available_servers();
1480
- if (all_servers.length === 0 && current_config.mcpServers) {
1481
- const current_servers = get_enabled_servers(current_config);
1482
- if (current_servers.length > 0) {
1483
- await sync_servers_to_registry(current_servers);
1484
- all_servers = current_servers;
1485
- note(`Imported ${current_servers.length} servers from your .claude.json file into registry.`);
1486
- }
1487
- }
1488
- if (all_servers.length === 0) {
1489
- note("No MCP servers found in .claude.json or registry. Add servers with the CLI first.");
1490
- return;
1491
- }
1492
- const currently_enabled = await get_enabled_servers_for_scope(scope);
1493
- const selected_server_names = await multiselect({
1494
- message: `Select MCP servers for ${get_scope_description(scope)}:`,
1495
- options: all_servers.map((server) => ({
1496
- value: server.name,
1497
- label: server.name,
1498
- hint: server.description || ""
1499
- })),
1500
- initialValues: currently_enabled,
1501
- required: false
1502
- });
1503
- if (typeof selected_server_names === "symbol") return;
1504
- const selected_servers = all_servers.filter((server) => selected_server_names.includes(server.name));
1505
- const servers_to_add = selected_server_names.filter((name) => !currently_enabled.includes(name));
1506
- const servers_to_remove = currently_enabled.filter((name) => !selected_server_names.includes(name));
1507
- if (cli_available && (scope === "local" || scope === "project")) {
1508
- let error_count = 0;
1509
- for (const name of servers_to_add) {
1510
- const server = all_servers.find((s) => s.name === name);
1511
- if (server) {
1512
- const result = await add_mcp_via_cli(server, scope);
1513
- if (!result.success) {
1514
- error_count++;
1515
- log.warn(`Failed to add ${name}: ${result.error}`);
1516
- }
1517
- }
1518
- }
1519
- for (const name of servers_to_remove) {
1520
- const result = await remove_mcp_via_cli(name, scope);
1521
- if (!result.success) {
1522
- error_count++;
1523
- log.warn(`Failed to remove ${name}: ${result.error}`);
1524
- }
1525
- }
1526
- await sync_servers_to_registry(selected_servers);
1527
- if (error_count > 0) note(`Configuration updated with ${error_count} errors.\nScope: ${get_scope_description(scope)}\nAdded: ${servers_to_add.length}, Removed: ${servers_to_remove.length}`);
1528
- else note(`Configuration updated!\nScope: ${get_scope_description(scope)}\nEnabled servers: ${selected_servers.length}`);
1529
- } else {
1530
- await write_claude_config(create_config_from_servers(selected_servers));
1531
- await sync_servers_to_registry(selected_servers);
1532
- if (!cli_available && scope !== "user") log.warn(`Claude CLI not available. Changes written to ~/.claude.json (user scope) instead of ${scope} scope.`);
1533
- note(`Configuration updated!\nEnabled servers: ${selected_servers.length}`);
1534
- }
1535
- }
1536
1558
  //#endregion
1537
1559
  //#region src/commands/edit-plugins.ts
1538
1560
  async function handle_toggle() {
@@ -1697,127 +1719,13 @@ async function edit_plugins() {
1697
1719
  }
1698
1720
  }
1699
1721
  //#endregion
1700
- //#region src/commands/manage-cache.ts
1701
- function format_status_line(p) {
1702
- const markers = [];
1703
- if (p.isVersionStale) markers.push(`version: ${p.installedVersion} ${p.latestVersion}`);
1704
- if (p.isShaStale) markers.push("commits behind");
1705
- if (p.orphanedVersions.length > 0) markers.push(`${p.orphanedVersions.length} orphaned`);
1706
- const status = markers.length > 0 ? `[stale: ${markers.join(", ")}]` : "[up to date]";
1707
- return `${p.name}@${p.marketplace} v${p.installedVersion} ${status}`;
1708
- }
1709
- async function handle_status() {
1710
- const plugins = await get_cached_plugins_info();
1711
- if (plugins.length === 0) {
1712
- log.info("No cached plugins found.");
1713
- return;
1714
- }
1715
- note(plugins.map(format_status_line).join("\n"), "Plugin Cache Status");
1716
- }
1717
- async function handle_clear() {
1718
- const plugins = await get_cached_plugins_info();
1719
- if (plugins.length === 0) {
1720
- log.info("No cached plugins to clear.");
1721
- return;
1722
- }
1723
- const selected = await multiselect({
1724
- message: "Select plugins to clear cache for:",
1725
- options: plugins.map((p) => {
1726
- const stale = p.isVersionStale || p.isShaStale;
1727
- return {
1728
- value: p.key,
1729
- label: `${p.name}@${p.marketplace}`,
1730
- hint: stale ? `v${p.installedVersion} → ${p.latestVersion ?? "unknown"} (stale)` : `v${p.installedVersion}`
1731
- };
1732
- }),
1733
- initialValues: plugins.filter((p) => p.isVersionStale || p.isShaStale).map((p) => p.key)
1734
- });
1735
- if (isCancel(selected) || selected.length === 0) return;
1736
- const should_clear = await confirm({ message: `Clear cache for ${selected.length} plugin(s)? This will also refresh the marketplace.` });
1737
- if (isCancel(should_clear) || !should_clear) return;
1738
- const result = await clear_plugin_caches(selected);
1739
- for (const key of result.cleared) log.success(`Cleared: ${key}`);
1740
- for (const err of result.errors) log.error(`Error: ${err}`);
1741
- if (result.cleared.length > 0) note("Run /reload-plugins in Claude Code or restart your session to apply changes.", "Next Steps");
1742
- }
1743
- async function handle_clean_orphaned() {
1744
- const should_clean = await confirm({ message: "Remove all orphaned plugin version directories?" });
1745
- if (isCancel(should_clean) || !should_clean) return;
1746
- const result = await clean_orphaned_versions();
1747
- if (result.cleaned === 0) log.info("No orphaned versions found.");
1748
- else {
1749
- for (const p of result.paths) log.success(`Removed: ${p}`);
1750
- log.info(`Cleaned ${result.cleaned} orphaned version(s).`);
1751
- }
1752
- }
1753
- async function handle_refresh() {
1754
- const should_refresh = await confirm({ message: "Refresh all marketplace clones (git pull)?" });
1755
- if (isCancel(should_refresh) || !should_refresh) return;
1756
- const results = await refresh_all_marketplaces();
1757
- if (results.size === 0) {
1758
- log.info("No marketplaces configured.");
1759
- return;
1760
- }
1761
- for (const [name, result] of results) if (result.success) log.success(`${name}: refreshed`);
1762
- else log.error(`${name}: ${result.error}`);
1763
- }
1764
- async function manage_cache() {
1765
- while (true) {
1766
- const action = await select({
1767
- message: "Plugin cache management:",
1768
- options: [
1769
- {
1770
- value: "status",
1771
- label: "View cache status",
1772
- hint: "Show plugins with staleness info"
1773
- },
1774
- {
1775
- value: "clear",
1776
- label: "Clear plugin caches",
1777
- hint: "Refresh marketplace + clear selected caches"
1778
- },
1779
- {
1780
- value: "clean-orphaned",
1781
- label: "Clean orphaned versions",
1782
- hint: "Remove old version directories"
1783
- },
1784
- {
1785
- value: "refresh",
1786
- label: "Refresh marketplaces",
1787
- hint: "Git pull all marketplace clones"
1788
- },
1789
- {
1790
- value: "back",
1791
- label: "Back",
1792
- hint: "Return to main menu"
1793
- }
1794
- ]
1795
- });
1796
- if (isCancel(action) || action === "back") return;
1797
- switch (action) {
1798
- case "status":
1799
- await handle_status();
1800
- break;
1801
- case "clear":
1802
- await handle_clear();
1803
- break;
1804
- case "clean-orphaned":
1805
- await handle_clean_orphaned();
1806
- break;
1807
- case "refresh":
1808
- await handle_refresh();
1809
- break;
1810
- }
1811
- }
1812
- }
1813
- //#endregion
1814
- //#region src/core/hook-state.ts
1815
- async function read_disabled_hooks() {
1816
- try {
1817
- const content = await readFile(get_disabled_hooks_path(), "utf-8");
1818
- return JSON.parse(content);
1819
- } catch {
1820
- return [];
1722
+ //#region src/core/hook-state.ts
1723
+ async function read_disabled_hooks() {
1724
+ try {
1725
+ const content = await readFile(get_disabled_hooks_path(), "utf-8");
1726
+ return JSON.parse(content);
1727
+ } catch {
1728
+ return [];
1821
1729
  }
1822
1730
  }
1823
1731
  async function write_disabled_hooks(entries) {
@@ -1976,6 +1884,121 @@ async function redisable_restored_hooks(restored) {
1976
1884
  };
1977
1885
  }
1978
1886
  //#endregion
1887
+ //#region src/commands/manage-cache.ts
1888
+ function format_status_line(p) {
1889
+ const markers = [];
1890
+ if (p.isVersionStale) markers.push(`version: ${p.installedVersion} → ${p.latestVersion}`);
1891
+ if (p.isShaStale) markers.push("commits behind");
1892
+ if (p.orphanedVersions.length > 0) markers.push(`${p.orphanedVersions.length} orphaned`);
1893
+ const status = markers.length > 0 ? `[stale: ${markers.join(", ")}]` : "[up to date]";
1894
+ return `${p.name}@${p.marketplace} v${p.installedVersion} ${status}`;
1895
+ }
1896
+ async function handle_status() {
1897
+ const plugins = await get_cached_plugins_info();
1898
+ if (plugins.length === 0) {
1899
+ log.info("No cached plugins found.");
1900
+ return;
1901
+ }
1902
+ note(plugins.map(format_status_line).join("\n"), "Plugin Cache Status");
1903
+ }
1904
+ async function handle_clear() {
1905
+ const plugins = await get_cached_plugins_info();
1906
+ if (plugins.length === 0) {
1907
+ log.info("No cached plugins to clear.");
1908
+ return;
1909
+ }
1910
+ const selected = await multiselect({
1911
+ message: "Select plugins to clear cache for:",
1912
+ options: plugins.map((p) => {
1913
+ const stale = p.isVersionStale || p.isShaStale;
1914
+ return {
1915
+ value: p.key,
1916
+ label: `${p.name}@${p.marketplace}`,
1917
+ hint: stale ? `v${p.installedVersion} → ${p.latestVersion ?? "unknown"} (stale)` : `v${p.installedVersion}`
1918
+ };
1919
+ }),
1920
+ initialValues: plugins.filter((p) => p.isVersionStale || p.isShaStale).map((p) => p.key)
1921
+ });
1922
+ if (isCancel(selected) || selected.length === 0) return;
1923
+ const should_clear = await confirm({ message: `Clear cache for ${selected.length} plugin(s)? This will also refresh the marketplace.` });
1924
+ if (isCancel(should_clear) || !should_clear) return;
1925
+ const result = await clear_plugin_caches(selected);
1926
+ for (const key of result.cleared) log.success(`Cleared: ${key}`);
1927
+ if (result.redisabledHooks?.success) log.success(`Re-disabled restored hooks: ${result.redisabledHooks.success}`);
1928
+ for (const err of result.errors) log.error(`Error: ${err}`);
1929
+ if (result.cleared.length > 0) note("Run /reload-plugins in Claude Code or restart your session to apply changes.", "Next Steps");
1930
+ }
1931
+ async function handle_clean_orphaned() {
1932
+ const should_clean = await confirm({ message: "Remove all orphaned plugin version directories?" });
1933
+ if (isCancel(should_clean) || !should_clean) return;
1934
+ const result = await clean_orphaned_versions();
1935
+ if (result.cleaned === 0) log.info("No orphaned versions found.");
1936
+ else {
1937
+ for (const p of result.paths) log.success(`Removed: ${p}`);
1938
+ log.info(`Cleaned ${result.cleaned} orphaned version(s).`);
1939
+ }
1940
+ }
1941
+ async function handle_refresh() {
1942
+ const should_refresh = await confirm({ message: "Refresh all marketplace clones (git pull)?" });
1943
+ if (isCancel(should_refresh) || !should_refresh) return;
1944
+ const results = await refresh_all_marketplaces();
1945
+ if (results.size === 0) {
1946
+ log.info("No marketplaces configured.");
1947
+ return;
1948
+ }
1949
+ for (const [name, result] of results) if (result.success) log.success(`${name}: refreshed`);
1950
+ else log.error(`${name}: ${result.error}`);
1951
+ }
1952
+ async function manage_cache() {
1953
+ while (true) {
1954
+ const action = await select({
1955
+ message: "Plugin cache management:",
1956
+ options: [
1957
+ {
1958
+ value: "status",
1959
+ label: "View cache status",
1960
+ hint: "Show plugins with staleness info"
1961
+ },
1962
+ {
1963
+ value: "clear",
1964
+ label: "Clear plugin caches",
1965
+ hint: "Refresh marketplace + clear selected caches"
1966
+ },
1967
+ {
1968
+ value: "clean-orphaned",
1969
+ label: "Clean orphaned versions",
1970
+ hint: "Remove old version directories"
1971
+ },
1972
+ {
1973
+ value: "refresh",
1974
+ label: "Refresh marketplaces",
1975
+ hint: "Git pull all marketplace clones"
1976
+ },
1977
+ {
1978
+ value: "back",
1979
+ label: "Back",
1980
+ hint: "Return to main menu"
1981
+ }
1982
+ ]
1983
+ });
1984
+ if (isCancel(action) || action === "back") return;
1985
+ switch (action) {
1986
+ case "status":
1987
+ await handle_status();
1988
+ break;
1989
+ case "clear":
1990
+ await handle_clear();
1991
+ break;
1992
+ case "clean-orphaned":
1993
+ await handle_clean_orphaned();
1994
+ break;
1995
+ case "refresh":
1996
+ await handle_refresh();
1997
+ break;
1998
+ }
1999
+ }
2000
+ }
2001
+ //#endregion
1979
2002
  //#region src/commands/manage-hooks.ts
1980
2003
  function format_hook(entry) {
1981
2004
  const detail = entry.handler.command || entry.handler.url || entry.handler.prompt || "(unknown)";
@@ -2517,41 +2540,144 @@ function format_time_ago(date) {
2517
2540
  }
2518
2541
  //#endregion
2519
2542
  //#region src/core/profile.ts
2520
- async function load_profile(name) {
2543
+ function is_object(value) {
2544
+ return !!value && typeof value === "object" && !Array.isArray(value);
2545
+ }
2546
+ function string_record(value) {
2547
+ if (!is_object(value)) return void 0;
2548
+ const result = {};
2549
+ for (const [key, item] of Object.entries(value)) if (typeof item === "string") result[key] = item;
2550
+ return Object.keys(result).length > 0 ? result : void 0;
2551
+ }
2552
+ function boolean_record(value) {
2553
+ if (!is_object(value)) return void 0;
2554
+ const result = {};
2555
+ for (const [key, item] of Object.entries(value)) if (typeof item === "boolean") result[key] = item;
2556
+ return Object.keys(result).length > 0 ? result : void 0;
2557
+ }
2558
+ function string_array(value) {
2559
+ if (!Array.isArray(value)) return void 0;
2560
+ const result = value.filter((item) => typeof item === "string");
2561
+ return result.length > 0 ? result : void 0;
2562
+ }
2563
+ function parse_portable_server(value) {
2564
+ if (!is_object(value) || typeof value.name !== "string") return null;
2565
+ const transport = value.transport === "http" || value.transport === "sse" || value.transport === "stdio" ? value.transport : "stdio";
2566
+ const client_options = is_object(value.client_options) ? value.client_options : void 0;
2567
+ return {
2568
+ name: value.name,
2569
+ transport,
2570
+ ...typeof value.command === "string" ? { command: value.command } : {},
2571
+ ...string_array(value.args) ? { args: string_array(value.args) } : {},
2572
+ ...typeof value.url === "string" ? { url: value.url } : {},
2573
+ ...string_record(value.env) ? { env: string_record(value.env) } : {},
2574
+ ...string_record(value.headers) ? { headers: string_record(value.headers) } : {},
2575
+ ...typeof value.description === "string" ? { description: value.description } : {},
2576
+ ...typeof value.disabled === "boolean" ? { disabled: value.disabled } : {},
2577
+ ...client_options ? { client_options } : {}
2578
+ };
2579
+ }
2580
+ async function read_profile_json(name) {
2521
2581
  const profile_path = get_profile_path(name);
2522
2582
  try {
2523
2583
  await access(profile_path);
2524
- const content = await readFile(profile_path, "utf-8");
2525
- const parsed = JSON.parse(content);
2526
- let config;
2527
- if (parsed.mcpServers) config = validate_claude_config(parsed);
2528
- else if (!parsed.enabledPlugins) config = validate_claude_config({ mcpServers: parsed });
2529
- else config = validate_claude_config({ mcpServers: parsed.mcpServers || {} });
2530
- return {
2531
- config,
2532
- enabledPlugins: parsed.enabledPlugins
2533
- };
2584
+ return JSON.parse(await readFile(profile_path, "utf-8"));
2534
2585
  } catch (error) {
2535
2586
  if (error instanceof Error && "code" in error && error.code === "ENOENT") throw new Error(`Profile '${name}' not found at ${profile_path}`);
2536
2587
  throw error;
2537
2588
  }
2538
2589
  }
2590
+ function legacy_profile_to_claude_config(parsed) {
2591
+ if (parsed.mcpServers) return validate_claude_config(parsed);
2592
+ if (!parsed.enabledPlugins && !parsed.plugins) return validate_claude_config({ mcpServers: parsed });
2593
+ return validate_claude_config({ mcpServers: parsed.mcpServers || {} });
2594
+ }
2595
+ function claude_config_to_portable(config) {
2596
+ return Object.entries(config.mcpServers || {}).map(([name, server]) => normalize_mcp_server(name, server));
2597
+ }
2598
+ function portable_to_claude_config(servers) {
2599
+ const mcpServers = {};
2600
+ for (const server of servers) {
2601
+ const config = {};
2602
+ if (server.transport !== "stdio") config.type = server.transport;
2603
+ if (server.command) config.command = server.command;
2604
+ if (server.args) config.args = server.args;
2605
+ if (server.url) config.url = server.url;
2606
+ if (server.env) config.env = server.env;
2607
+ if (server.headers) config.headers = server.headers;
2608
+ if (server.description) config.description = server.description;
2609
+ mcpServers[server.name] = config;
2610
+ }
2611
+ return validate_claude_config({ mcpServers });
2612
+ }
2613
+ async function load_portable_profile(name) {
2614
+ const parsed = await read_profile_json(name);
2615
+ const plugins = boolean_record(parsed.plugins) ?? boolean_record(parsed.enabledPlugins);
2616
+ if (Array.isArray(parsed.servers)) return {
2617
+ version: 2,
2618
+ servers: parsed.servers.map(parse_portable_server).filter((server) => !!server),
2619
+ ...plugins ? { plugins } : {},
2620
+ ...is_object(parsed.client_overrides) ? { client_overrides: parsed.client_overrides } : {}
2621
+ };
2622
+ return {
2623
+ version: 2,
2624
+ servers: claude_config_to_portable(legacy_profile_to_claude_config(parsed)),
2625
+ ...plugins ? { plugins } : {}
2626
+ };
2627
+ }
2628
+ async function apply_profile_to_claude(name) {
2629
+ const profile = await load_portable_profile(name);
2630
+ await write_claude_config(portable_to_claude_config(profile.servers));
2631
+ let pluginCount = 0;
2632
+ if (profile.plugins) {
2633
+ await write_claude_settings({ enabledPlugins: profile.plugins });
2634
+ pluginCount = Object.keys(profile.plugins).length;
2635
+ }
2636
+ return {
2637
+ profile: name,
2638
+ serverCount: profile.servers.length,
2639
+ pluginCount,
2640
+ client: "claude-code",
2641
+ scope: "user"
2642
+ };
2643
+ }
2644
+ async function apply_profile_to_client(input) {
2645
+ const adapter = get_client_adapter(input.client);
2646
+ if (!adapter) throw new Error(`Invalid client: ${input.client}`);
2647
+ const location = resolve_client_location(adapter, input.scope, input.location);
2648
+ const profile = await load_portable_profile(input.name);
2649
+ const mutation = await replace_client_servers(adapter, location, profile.servers);
2650
+ let pluginCount = 0;
2651
+ if (adapter.id === "claude-code" && profile.plugins) {
2652
+ await write_claude_settings({ enabledPlugins: profile.plugins });
2653
+ pluginCount = Object.keys(profile.plugins).length;
2654
+ }
2655
+ return {
2656
+ profile: input.name,
2657
+ serverCount: profile.servers.length,
2658
+ pluginCount,
2659
+ client: adapter.id,
2660
+ scope: location.scope,
2661
+ location: mutation.location,
2662
+ ...mutation.backup_path ? { backup_path: mutation.backup_path } : {}
2663
+ };
2664
+ }
2539
2665
  async function list_profiles() {
2540
2666
  const profiles_dir = get_profiles_dir();
2541
2667
  try {
2542
2668
  await access(profiles_dir);
2543
- const json_files = (await readdir(profiles_dir)).filter((f) => f.endsWith(".json"));
2669
+ const files = (await readdir(profiles_dir)).filter((file) => file.endsWith(".json"));
2544
2670
  const profiles = [];
2545
- for (const file of json_files) try {
2671
+ for (const file of files) try {
2672
+ const name = file.replace(/\.json$/, "");
2546
2673
  const path = get_profile_path(file);
2547
- const content = await readFile(path, "utf-8");
2548
- const parsed = JSON.parse(content);
2549
- const servers = parsed.mcpServers || parsed;
2550
- const plugins = parsed.enabledPlugins || {};
2674
+ const parsed = JSON.parse(await readFile(path, "utf-8"));
2675
+ const servers = Array.isArray(parsed.servers) ? parsed.servers : parsed.mcpServers || parsed;
2676
+ const plugins = parsed.plugins || parsed.enabledPlugins || {};
2551
2677
  profiles.push({
2552
- name: file.replace(".json", ""),
2678
+ name,
2553
2679
  path,
2554
- serverCount: Object.keys(servers).length,
2680
+ serverCount: Array.isArray(servers) ? servers.length : Object.keys(servers).length,
2555
2681
  pluginCount: Object.keys(plugins).length
2556
2682
  });
2557
2683
  } catch {}
@@ -2560,21 +2686,53 @@ async function list_profiles() {
2560
2686
  return [];
2561
2687
  }
2562
2688
  }
2689
+ async function save_portable_profile(name, servers, plugins) {
2690
+ await ensure_directory_exists(get_profiles_dir());
2691
+ const profile_data = {
2692
+ version: 2,
2693
+ servers,
2694
+ ...plugins && Object.keys(plugins).length > 0 ? { plugins } : {}
2695
+ };
2696
+ await safe_json_write(get_profile_path(name), profile_data, 2);
2697
+ }
2563
2698
  async function save_profile(name) {
2564
2699
  const config = await read_claude_config();
2565
2700
  const settings = await read_claude_settings();
2566
- const servers = config.mcpServers || {};
2701
+ const servers = get_enabled_servers(config).map((server) => normalize_mcp_server(server.name, server));
2567
2702
  const plugins = settings.enabledPlugins || {};
2568
- const server_count = Object.keys(servers).length;
2569
- const plugin_count = Object.keys(plugins).length;
2570
- if (server_count === 0 && plugin_count === 0) throw new Error("No MCP servers or plugins configured to save");
2571
- await ensure_directory_exists(get_profiles_dir());
2572
- const profile_data = { mcpServers: servers };
2573
- if (plugin_count > 0) profile_data.enabledPlugins = plugins;
2574
- await safe_json_write(get_profile_path(name), profile_data, 2);
2703
+ if (servers.length === 0 && Object.keys(plugins).length === 0) throw new Error("No MCP servers or plugins configured to save");
2704
+ await save_portable_profile(name, servers, plugins);
2705
+ return {
2706
+ serverCount: servers.length,
2707
+ pluginCount: Object.keys(plugins).length
2708
+ };
2709
+ }
2710
+ async function save_profile_for_client(input) {
2711
+ const adapter = get_client_adapter(input.client);
2712
+ if (!adapter) throw new Error(`Invalid client: ${input.client}`);
2713
+ const location = resolve_client_location(adapter, input.scope, input.location);
2714
+ const servers = await adapter.readLocation(location);
2715
+ let plugins;
2716
+ if (adapter.id === "claude-code") plugins = (await read_claude_settings()).enabledPlugins;
2717
+ if (servers.length === 0 && !plugins) throw new Error("No MCP servers or plugins configured to save");
2718
+ await save_portable_profile(input.name, servers, plugins);
2575
2719
  return {
2576
- serverCount: server_count,
2577
- pluginCount: plugin_count
2720
+ profile: input.name,
2721
+ serverCount: servers.length,
2722
+ pluginCount: Object.keys(plugins || {}).length,
2723
+ client: adapter.id,
2724
+ scope: location.scope,
2725
+ location: location.path
2726
+ };
2727
+ }
2728
+ async function save_current_claude_profile(name) {
2729
+ const counts = await save_profile(name);
2730
+ return {
2731
+ profile: name,
2732
+ serverCount: counts.serverCount,
2733
+ pluginCount: counts.pluginCount,
2734
+ client: "claude-code",
2735
+ scope: "user"
2578
2736
  };
2579
2737
  }
2580
2738
  //#endregion
@@ -2591,17 +2749,13 @@ function parse_args() {
2591
2749
  } else if (args[i] === "--list-profiles" || args[i] === "-l") result.listProfiles = true;
2592
2750
  return result;
2593
2751
  }
2594
- async function apply_profile(name) {
2595
- intro(`MCPick - Loading profile: ${name}`);
2752
+ async function apply_claude_profile(name) {
2753
+ intro(`MCPick - Loading Claude Code profile: ${name}`);
2596
2754
  try {
2597
- const profile = await load_profile(name);
2598
- await write_claude_config(profile.config);
2599
- const parts = [`${Object.keys(profile.config.mcpServers || {}).length} servers`];
2600
- if (profile.enabledPlugins) {
2601
- await write_claude_settings({ enabledPlugins: profile.enabledPlugins });
2602
- parts.push(`${Object.keys(profile.enabledPlugins).length} plugins`);
2603
- }
2604
- log.success(`Profile '${name}' applied (${parts.join(", ")})`);
2755
+ const result = await apply_profile_to_claude(name);
2756
+ const parts = [`${result.serverCount} servers`];
2757
+ if (result.pluginCount > 0) parts.push(`${result.pluginCount} plugins`);
2758
+ log.success(`Profile '${result.profile}' applied (${parts.join(", ")})`);
2605
2759
  outro("Done!");
2606
2760
  } catch (error) {
2607
2761
  if (error instanceof Error) cancel(error.message);
@@ -2618,13 +2772,13 @@ async function show_profiles() {
2618
2772
  } else for (const p of profiles) log.info(`${p.name} (${p.serverCount} servers)`);
2619
2773
  outro("");
2620
2774
  }
2621
- async function create_profile(name) {
2622
- intro(`MCPick - Saving profile: ${name}`);
2775
+ async function create_claude_profile(name) {
2776
+ intro(`MCPick - Saving Claude Code profile: ${name}`);
2623
2777
  try {
2624
- const counts = await save_profile(name);
2625
- const parts = [`${counts.serverCount} servers`];
2626
- if (counts.pluginCount > 0) parts.push(`${counts.pluginCount} plugins`);
2627
- log.success(`Profile '${name}' saved (${parts.join(", ")})`);
2778
+ const result = await save_current_claude_profile(name);
2779
+ const parts = [`${result.serverCount} servers`];
2780
+ if (result.pluginCount > 0) parts.push(`${result.pluginCount} plugins`);
2781
+ log.success(`Profile '${result.profile}' saved (${parts.join(", ")})`);
2628
2782
  outro("Done!");
2629
2783
  } catch (error) {
2630
2784
  if (error instanceof Error) cancel(error.message);
@@ -2632,6 +2786,36 @@ async function create_profile(name) {
2632
2786
  process.exit(1);
2633
2787
  }
2634
2788
  }
2789
+ function sorted_client_adapters() {
2790
+ return [...client_adapters].sort((a, b) => b.label.localeCompare(a.label));
2791
+ }
2792
+ async function select_client_adapter() {
2793
+ const adapters = sorted_client_adapters();
2794
+ const client_id = await select({
2795
+ message: "Which MCP client?",
2796
+ options: adapters.map((adapter) => ({
2797
+ value: adapter.id,
2798
+ label: adapter.label
2799
+ })),
2800
+ initialValue: adapters[0]?.id
2801
+ });
2802
+ if (isCancel(client_id)) return null;
2803
+ return client_adapters.find((adapter) => adapter.id === client_id) ?? null;
2804
+ }
2805
+ async function select_client_location(adapter) {
2806
+ const locations = adapter.locations();
2807
+ if (locations.length === 1) return locations[0];
2808
+ const location_path = await select({
2809
+ message: `Which ${adapter.label} configuration?`,
2810
+ options: locations.map((location) => ({
2811
+ value: location.path,
2812
+ label: `${location.scope} — ${location.description}`,
2813
+ hint: location.path
2814
+ }))
2815
+ });
2816
+ if (isCancel(location_path)) return null;
2817
+ return locations.find((location) => location.path === location_path) ?? null;
2818
+ }
2635
2819
  async function handle_load_profile() {
2636
2820
  const profiles = await list_profiles();
2637
2821
  if (profiles.length === 0) {
@@ -2652,14 +2836,21 @@ async function handle_load_profile() {
2652
2836
  })
2653
2837
  });
2654
2838
  if (isCancel(profile_name)) return;
2655
- const profile = await load_profile(profile_name);
2656
- await write_claude_config(profile.config);
2657
- const parts = [`${Object.keys(profile.config.mcpServers || {}).length} servers`];
2658
- if (profile.enabledPlugins) {
2659
- await write_claude_settings({ enabledPlugins: profile.enabledPlugins });
2660
- parts.push(`${Object.keys(profile.enabledPlugins).length} plugins`);
2661
- }
2662
- log.success(`Profile '${profile_name}' applied (${parts.join(", ")})`);
2839
+ const adapter = await select_client_adapter();
2840
+ if (!adapter) return;
2841
+ const location = await select_client_location(adapter);
2842
+ if (!location) return;
2843
+ const result = await apply_profile_to_client({
2844
+ name: profile_name,
2845
+ client: adapter.id,
2846
+ scope: location.scope,
2847
+ location: location.path
2848
+ });
2849
+ const parts = [`${result.serverCount} servers`];
2850
+ if (result.pluginCount > 0) parts.push(`${result.pluginCount} Claude plugins`);
2851
+ log.success(`Profile '${result.profile}' applied to ${adapter.label}:${location.scope} (${parts.join(", ")})`);
2852
+ if (result.location) log.info(`Config: ${result.location}`);
2853
+ if (result.backup_path) log.info(`Backup: ${result.backup_path}`);
2663
2854
  }
2664
2855
  async function handle_save_profile() {
2665
2856
  const name = await text({
@@ -2671,19 +2862,30 @@ async function handle_save_profile() {
2671
2862
  }
2672
2863
  });
2673
2864
  if (isCancel(name)) return;
2674
- const counts = await save_profile(name);
2675
- const parts = [`${counts.serverCount} servers`];
2676
- if (counts.pluginCount > 0) parts.push(`${counts.pluginCount} plugins`);
2677
- log.success(`Profile '${name}' saved (${parts.join(", ")})`);
2865
+ const adapter = await select_client_adapter();
2866
+ if (!adapter) return;
2867
+ const location = await select_client_location(adapter);
2868
+ if (!location) return;
2869
+ const result = await save_profile_for_client({
2870
+ name,
2871
+ client: adapter.id,
2872
+ scope: location.scope,
2873
+ location: location.path
2874
+ });
2875
+ const parts = [`${result.serverCount} servers`];
2876
+ if (result.pluginCount > 0) parts.push(`${result.pluginCount} Claude plugins`);
2877
+ log.success(`Profile '${result.profile}' saved from ${adapter.label}:${location.scope} (${parts.join(", ")})`);
2878
+ if (result.location) log.info(`Config: ${result.location}`);
2678
2879
  }
2679
2880
  async function handle_client_tools() {
2881
+ const adapters = sorted_client_adapters();
2680
2882
  const client_id = await select({
2681
2883
  message: "Which client?",
2682
- options: client_adapters.map((adapter) => ({
2884
+ options: adapters.map((adapter) => ({
2683
2885
  value: adapter.id,
2684
2886
  label: adapter.label
2685
2887
  })),
2686
- initialValue: "claude-code"
2888
+ initialValue: adapters[0]?.id
2687
2889
  });
2688
2890
  if (isCancel(client_id)) return;
2689
2891
  if (client_id !== "claude-code") {
@@ -2742,11 +2944,11 @@ async function main() {
2742
2944
  return;
2743
2945
  }
2744
2946
  if (args.saveProfile) {
2745
- await create_profile(args.saveProfile);
2947
+ await create_claude_profile(args.saveProfile);
2746
2948
  return;
2747
2949
  }
2748
2950
  if (args.profile) {
2749
- await apply_profile(args.profile);
2951
+ await apply_claude_profile(args.profile);
2750
2952
  return;
2751
2953
  }
2752
2954
  intro("MCPick - MCP Configuration Manager");
@@ -2773,12 +2975,12 @@ async function main() {
2773
2975
  {
2774
2976
  value: "load-profile",
2775
2977
  label: "Load profile",
2776
- hint: "Apply a saved profile"
2978
+ hint: "Apply a saved profile to a selected client"
2777
2979
  },
2778
2980
  {
2779
2981
  value: "save-profile",
2780
2982
  label: "Save profile",
2781
- hint: "Save current config as profile"
2983
+ hint: "Save selected client config as a portable profile"
2782
2984
  },
2783
2985
  {
2784
2986
  value: "backup",
@@ -2872,12 +3074,12 @@ const SUBCOMMANDS = new Set([
2872
3074
  const arg = process.argv[2];
2873
3075
  if (arg && SUBCOMMANDS.has(arg) || arg === "--help" || arg === "-h" || !process.stdout.isTTY) {
2874
3076
  if (!arg && !process.stdout.isTTY) process.argv.push("--help");
2875
- import("./cli-By-0nYNQ.js").then((m) => m.run());
3077
+ import("./cli-sOeHH4CK.js").then((m) => m.run());
2876
3078
  } else main().catch((error) => {
2877
3079
  console.error("Fatal error:", error);
2878
3080
  process.exit(1);
2879
3081
  });
2880
3082
  //#endregion
2881
- export { add_client_server as A, get_all_plugins as B, validate_plugin_via_cli as C, list_plugin_backups as D, list_backups as E, resolve_client_location as F, list_config_backups as G, remove_hook as H, set_client_server_enabled as I, restore_config_backup as K, add_hook as L, get_client_adapter as M, list_client_locations as N, read_server_registry as O, remove_client_server as P, build_enabled_plugins as R, update_plugin_via_cli as S, get_all_available_servers as T, write_claude_settings as U, read_claude_settings as V, atomic_json_write as W, mcp_add_json_via_cli as _, split_cli_list as a, remove_mcp_via_cli as b, enable_plugin_hook as c, add_mcp_via_cli as d, install_plugin_via_cli as f, marketplace_update_via_cli as g, marketplace_remove_via_cli as h, run_skills_cli as i, add_client_server_config as j, write_server_registry as k, read_disabled_hooks as l, marketplace_list_via_cli as m, load_profile as n, check_restored_hooks as o, marketplace_add_via_cli as p, save_profile as r, disable_plugin_hook as s, list_profiles as t, redisable_restored_hooks as u, mcp_get_via_cli as v, add_server_to_registry as w, uninstall_plugin_via_cli as x, mcp_reset_project_choices_via_cli as y, get_all_hooks as z };
3083
+ export { read_server_registry as A, build_enabled_plugins as B, uninstall_plugin_via_cli as C, get_all_available_servers as D, add_server_to_registry as E, list_client_locations as F, write_claude_settings as G, get_all_plugins as H, remove_client_server as I, restore_config_backup as J, atomic_json_write as K, resolve_client_location as L, add_client_server as M, add_client_server_config as N, list_backups as O, get_client_adapter as P, set_client_server_enabled as R, remove_mcp_via_cli as S, validate_plugin_via_cli as T, read_claude_settings as U, get_all_hooks as V, remove_hook as W, marketplace_remove_via_cli as _, save_profile_for_client as a, mcp_get_via_cli as b, check_restored_hooks as c, read_disabled_hooks as d, redisable_restored_hooks as f, marketplace_list_via_cli as g, marketplace_add_via_cli as h, save_current_claude_profile as i, write_server_registry as j, list_plugin_backups as k, disable_plugin_hook as l, install_plugin_via_cli as m, apply_profile_to_client as n, run_skills_cli as o, add_mcp_via_cli as p, list_config_backups as q, list_profiles as r, split_cli_list as s, apply_profile_to_claude as t, enable_plugin_hook as u, marketplace_update_via_cli as v, update_plugin_via_cli as w, mcp_reset_project_choices_via_cli as x, mcp_add_json_via_cli as y, add_hook as z };
2882
3084
 
2883
3085
  //# sourceMappingURL=index.js.map