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.
- package/CHANGELOG.md +30 -0
- package/README.md +8 -2
- package/dist/{add-LJQa2my2.js → add-7mhUpbrt.js} +20 -13
- package/dist/{add-json-TEdYweZ5.js → add-json-BMM2L4hv.js} +16 -12
- package/dist/{backup-kyS5IVIr.js → backup-C-YJmgps.js} +4 -4
- package/dist/{cache-DTfzTsEE.js → cache-BOYZhUF6.js} +4 -3
- package/dist/{cli-By-0nYNQ.js → cli-sOeHH4CK.js} +22 -22
- package/dist/{clients-qMozizys.js → clients-D5KAuQ5U.js} +3 -3
- package/dist/{clone-BVhYjRGO.js → clone-BRJA55js.js} +5 -5
- package/dist/{config-DzMmTJYL.js → config-Bzh374VP.js} +4 -13
- package/dist/{dev-Cst8WkQ-.js → dev-B-WlQSqY.js} +4 -4
- package/dist/{disable-BaOs9lrm.js → disable-Br0aVG3u.js} +16 -12
- package/dist/{enable--3mjSmTq.js → enable-DUolKCEH.js} +16 -12
- package/dist/{get-CjhNWyRj.js → get-D-6Cl_CO.js} +3 -3
- package/dist/{hooks-DFmxgD0t.js → hooks-BKPmZViU.js} +3 -3
- package/dist/index.js +595 -393
- package/dist/{list-D5CkCXpP.js → list-DMcaHDfM.js} +4 -4
- package/dist/{marketplace-C3EGyIG0.js → marketplace-DTW7Ys8k.js} +4 -4
- package/dist/mutation-ukRPw3qM.js +19 -0
- package/dist/{output-HtT5HCof.js → output-BS1TMOWt.js} +1 -1
- package/dist/{plugin-cache-BSgB42wa.js → plugin-cache-Dw1I2YuO.js} +10 -3
- package/dist/{plugins-Dn2mPFKm.js → plugins-BzLD4og0.js} +4 -4
- package/dist/profile-CmIWUJH_.js +163 -0
- package/dist/{reload-257iU7Z7.js → reload-Di28s_rY.js} +2 -2
- package/dist/{remove-26XFzkPd.js → remove-B32EuYRC.js} +16 -11
- package/dist/{reset-project-choices-D2F04LfC.js → reset-project-choices-DX4TnZ2i.js} +3 -3
- package/dist/{restore-BYYsoNqF.js → restore-ByS4xi0y.js} +5 -5
- package/dist/{rollback-CPdaME91.js → rollback-DdDJrA8y.js} +3 -3
- package/dist/{skills-DfWk9mpk.js → skills-pvyQ17XU.js} +3 -3
- package/dist/{validation-xMlbgGCF.js → validation-CfPAjPJ5.js} +21 -2
- package/package.json +1 -1
- 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-
|
|
4
|
-
import {
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
749
|
-
|
|
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
|
-
|
|
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 = {
|
|
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] =
|
|
786
|
-
else registry.servers.push(
|
|
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-
|
|
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/
|
|
1701
|
-
function
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
2669
|
+
const files = (await readdir(profiles_dir)).filter((file) => file.endsWith(".json"));
|
|
2544
2670
|
const profiles = [];
|
|
2545
|
-
for (const file of
|
|
2671
|
+
for (const file of files) try {
|
|
2672
|
+
const name = file.replace(/\.json$/, "");
|
|
2546
2673
|
const path = get_profile_path(file);
|
|
2547
|
-
const
|
|
2548
|
-
const
|
|
2549
|
-
const
|
|
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
|
|
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.
|
|
2701
|
+
const servers = get_enabled_servers(config).map((server) => normalize_mcp_server(server.name, server));
|
|
2567
2702
|
const plugins = settings.enabledPlugins || {};
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
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
|
-
|
|
2577
|
-
|
|
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
|
|
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
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
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
|
|
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
|
|
2625
|
-
const parts = [`${
|
|
2626
|
-
if (
|
|
2627
|
-
log.success(`Profile '${
|
|
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
|
|
2656
|
-
|
|
2657
|
-
const
|
|
2658
|
-
if (
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
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
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
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:
|
|
2884
|
+
options: adapters.map((adapter) => ({
|
|
2683
2885
|
value: adapter.id,
|
|
2684
2886
|
label: adapter.label
|
|
2685
2887
|
})),
|
|
2686
|
-
initialValue:
|
|
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
|
|
2947
|
+
await create_claude_profile(args.saveProfile);
|
|
2746
2948
|
return;
|
|
2747
2949
|
}
|
|
2748
2950
|
if (args.profile) {
|
|
2749
|
-
await
|
|
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
|
|
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-
|
|
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 {
|
|
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
|