mcpick 0.0.17 → 0.0.19

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 (75) hide show
  1. package/.vscode/settings.json +5 -0
  2. package/CHANGELOG.md +15 -0
  3. package/dist/add-B9nVyh8T.js +113 -0
  4. package/dist/add-json-CXNDl3al.js +58 -0
  5. package/dist/atomic-write-BqEykHp9.js +26 -0
  6. package/dist/backup-DSDhHI5f.js +64 -0
  7. package/dist/cache-D6kd7qE8.js +226 -0
  8. package/dist/claude-cli-BeA-bmoW.js +394 -0
  9. package/dist/cli-DNNZjJYL.js +84 -0
  10. package/dist/clone-DLFLewBY.js +88 -0
  11. package/dist/config-DijVdEFn.js +176 -0
  12. package/dist/dev-DRJRNp7y.js +265 -0
  13. package/dist/disable-BA8tXPJN.js +39 -0
  14. package/dist/enable-Bdnnn_Cq.js +40 -0
  15. package/dist/get-BPjMXTMc.js +41 -0
  16. package/dist/hook-state-Di8lUsPr.js +171 -0
  17. package/dist/hooks-Bmn7pUZa.js +280 -0
  18. package/dist/index.js +1230 -303
  19. package/dist/list-B8YeDWt6.js +64 -0
  20. package/dist/marketplace-Br89Tg-Z.js +168 -0
  21. package/dist/output-BchYq0mR.js +15 -0
  22. package/dist/paths-BPISiJi4.js +124 -0
  23. package/dist/plugin-cache-Bby9Dxm9.js +405 -0
  24. package/dist/plugins-DHYJF5CP.js +212 -0
  25. package/dist/profile-CX97sMGp.js +120 -0
  26. package/dist/profile-DkY_lBEm.js +70 -0
  27. package/dist/redact-O35tjnRD.js +26 -0
  28. package/dist/registry-CfUKT7_C.js +92 -0
  29. package/dist/reload-CYDhkCVZ.js +31 -0
  30. package/dist/remove-DIPWYMpk.js +31 -0
  31. package/dist/reset-project-choices-DRM5KByw.js +28 -0
  32. package/dist/restore-DdMfUljI.js +84 -0
  33. package/dist/rolldown-runtime-CiIaOW0V.js +13 -0
  34. package/dist/settings-DEcWtzLE.js +201 -0
  35. package/dist/validation-xMlbgGCF.js +44 -0
  36. package/package.json +20 -19
  37. package/dist/cli/commands/add-json.js +0 -60
  38. package/dist/cli/commands/add.js +0 -135
  39. package/dist/cli/commands/backup.js +0 -83
  40. package/dist/cli/commands/cache.js +0 -296
  41. package/dist/cli/commands/dev.js +0 -161
  42. package/dist/cli/commands/disable.js +0 -36
  43. package/dist/cli/commands/enable.js +0 -39
  44. package/dist/cli/commands/get.js +0 -45
  45. package/dist/cli/commands/hooks.js +0 -314
  46. package/dist/cli/commands/list.js +0 -63
  47. package/dist/cli/commands/marketplace.js +0 -211
  48. package/dist/cli/commands/plugins.js +0 -265
  49. package/dist/cli/commands/profile.js +0 -134
  50. package/dist/cli/commands/reload.js +0 -36
  51. package/dist/cli/commands/remove.js +0 -35
  52. package/dist/cli/commands/reset-project-choices.js +0 -32
  53. package/dist/cli/commands/restore.js +0 -105
  54. package/dist/cli/index.js +0 -28
  55. package/dist/cli/output.js +0 -21
  56. package/dist/commands/add-server.js +0 -310
  57. package/dist/commands/backup.js +0 -60
  58. package/dist/commands/edit-config.js +0 -109
  59. package/dist/commands/edit-plugins.js +0 -201
  60. package/dist/commands/manage-cache.js +0 -155
  61. package/dist/commands/manage-hooks.js +0 -99
  62. package/dist/commands/manage-marketplace.js +0 -293
  63. package/dist/commands/restore.js +0 -118
  64. package/dist/core/config.js +0 -146
  65. package/dist/core/dev-override.js +0 -210
  66. package/dist/core/hook-state.js +0 -220
  67. package/dist/core/plugin-cache.js +0 -506
  68. package/dist/core/profile.js +0 -94
  69. package/dist/core/registry.js +0 -121
  70. package/dist/core/settings.js +0 -243
  71. package/dist/core/validation.js +0 -49
  72. package/dist/types.js +0 -2
  73. package/dist/utils/atomic-write.js +0 -27
  74. package/dist/utils/claude-cli.js +0 -483
  75. package/dist/utils/paths.js +0 -114
package/dist/index.js CHANGED
@@ -1,322 +1,1249 @@
1
1
  #!/usr/bin/env node
2
- import { cancel, intro, isCancel, log, outro, select, text, } from '@clack/prompts';
3
- import { add_server } from './commands/add-server.js';
4
- import { backup_config } from './commands/backup.js';
5
- import { edit_config } from './commands/edit-config.js';
6
- import { edit_plugins } from './commands/edit-plugins.js';
7
- import { manage_cache } from './commands/manage-cache.js';
8
- import { manage_hooks } from './commands/manage-hooks.js';
9
- import { manage_marketplace } from './commands/manage-marketplace.js';
10
- import { restore_config } from './commands/restore.js';
11
- import { write_claude_config } from './core/config.js';
12
- import { list_profiles, load_profile, save_profile, } from './core/profile.js';
13
- import { write_claude_settings } from './core/settings.js';
2
+ import { m as get_plugin_backup_filename, n as get_backup_filename, r as get_backups_dir, t as ensure_directory_exists } from "./paths-BPISiJi4.js";
3
+ import { n as validate_mcp_server } from "./validation-xMlbgGCF.js";
4
+ import { i as list_plugin_backups, n as get_all_available_servers, o as sync_servers_to_registry, r as list_backups, t as add_server_to_registry } from "./registry-CfUKT7_C.js";
5
+ import { a as install_plugin_via_cli, c as marketplace_remove_via_cli, h as update_plugin_via_cli, i as get_scope_options, l as marketplace_update_via_cli, m as uninstall_plugin_via_cli, n as check_claude_cli, o as marketplace_add_via_cli, p as remove_mcp_via_cli, r as get_scope_description, t as add_mcp_via_cli } from "./claude-cli-BeA-bmoW.js";
6
+ 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-DijVdEFn.js";
7
+ import { a as read_claude_settings, i as get_all_plugins, n as build_enabled_plugins, r as get_all_hooks, s as write_claude_settings } from "./settings-DEcWtzLE.js";
8
+ 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-Bby9Dxm9.js";
9
+ import { a as redisable_restored_hooks, i as read_disabled_hooks, n as disable_plugin_hook, r as enable_plugin_hook, t as check_restored_hooks } from "./hook-state-Di8lUsPr.js";
10
+ import { n as load_profile, r as save_profile, t as list_profiles } from "./profile-DkY_lBEm.js";
11
+ import { cancel, confirm, intro, isCancel, log, multiselect, note, outro, select, text } from "@clack/prompts";
12
+ import { readFile, readdir, unlink, writeFile } from "node:fs/promises";
13
+ import { join } from "node:path";
14
+ //#region src/commands/add-server.ts
15
+ function format_server_details(server) {
16
+ const details = [`Name: ${server.name}`];
17
+ if ("command" in server) details.push(`Command: ${server.command} ${(server.args || []).join(" ")}`);
18
+ if ("url" in server) details.push(`URL: ${server.url}`);
19
+ details.push(`Description: ${server.description || "None"}`);
20
+ if (server.type) details.push(`Transport: ${server.type}`);
21
+ if (server.env) details.push(`Environment: ${Object.keys(server.env).length} variables`);
22
+ if ("headers" in server && server.headers) details.push(`Headers: ${Object.keys(server.headers).length} headers`);
23
+ return details;
24
+ }
25
+ async function add_server() {
26
+ try {
27
+ const cli_available = await check_claude_cli();
28
+ const scope = await select({
29
+ message: "Where should this server be installed?",
30
+ options: get_scope_options(),
31
+ initialValue: "local"
32
+ });
33
+ if (typeof scope === "symbol") return;
34
+ const config_method = await select({
35
+ message: "How would you like to add the server?",
36
+ options: [{
37
+ value: "json",
38
+ label: "Paste JSON configuration",
39
+ hint: "Paste complete server config as JSON"
40
+ }, {
41
+ value: "form",
42
+ label: "Step-by-step form",
43
+ hint: "Fill out fields one by one"
44
+ }],
45
+ initialValue: "json"
46
+ });
47
+ if (typeof config_method === "symbol") return;
48
+ if (config_method === "json") return await add_server_from_json(scope, cli_available);
49
+ const name = await text({
50
+ message: "Server name:",
51
+ placeholder: "e.g., mcp-sqlite-tools",
52
+ validate: (value) => {
53
+ if (!value || value.trim().length === 0) return "Server name is required";
54
+ }
55
+ });
56
+ if (typeof name === "symbol") return;
57
+ const command = await text({
58
+ message: "Command to run:",
59
+ placeholder: "e.g., uvx, npx, node",
60
+ validate: (value) => {
61
+ if (!value || value.trim().length === 0) return "Command is required";
62
+ }
63
+ });
64
+ if (typeof command === "symbol") return;
65
+ const args_input = await text({
66
+ message: "Arguments (comma-separated):",
67
+ placeholder: "e.g., mcp-sqlite-tools, --port, 3000",
68
+ defaultValue: ""
69
+ });
70
+ if (typeof args_input === "symbol") return;
71
+ const args = args_input.split(",").map((arg) => arg.trim()).filter((arg) => arg.length > 0);
72
+ const description = await text({
73
+ message: "Description (optional):",
74
+ placeholder: "Brief description of what this server provides"
75
+ });
76
+ if (typeof description === "symbol") return;
77
+ const configure_advanced = await confirm({
78
+ message: "Configure advanced settings (env variables, transport, etc.)?",
79
+ initialValue: false
80
+ });
81
+ if (typeof configure_advanced === "symbol") return;
82
+ let server_data = {
83
+ name: name.trim(),
84
+ type: "stdio",
85
+ command: command.trim(),
86
+ args,
87
+ ...description && description.trim() && { description: description.trim() }
88
+ };
89
+ if (configure_advanced) {
90
+ const transport_type = await select({
91
+ message: "Transport type:",
92
+ options: [
93
+ {
94
+ value: "stdio",
95
+ label: "stdio (default)",
96
+ hint: "Standard input/output"
97
+ },
98
+ {
99
+ value: "sse",
100
+ label: "sse",
101
+ hint: "Server-sent events"
102
+ },
103
+ {
104
+ value: "http",
105
+ label: "http",
106
+ hint: "HTTP transport"
107
+ }
108
+ ],
109
+ initialValue: "stdio"
110
+ });
111
+ if (typeof transport_type === "symbol") return;
112
+ server_data.type = transport_type;
113
+ if (transport_type === "sse" || transport_type === "http") {
114
+ delete server_data.command;
115
+ delete server_data.args;
116
+ const url = await text({
117
+ message: "Server URL:",
118
+ placeholder: "e.g., http://localhost:3000",
119
+ validate: (value) => {
120
+ if (!value || value.trim().length === 0) return "URL is required for non-stdio transport";
121
+ }
122
+ });
123
+ if (typeof url === "symbol") return;
124
+ server_data.url = url.trim();
125
+ }
126
+ const env_input = await text({
127
+ message: "Environment variables (KEY=value, comma-separated):",
128
+ placeholder: "e.g., API_KEY=abc123, TIMEOUT=30"
129
+ });
130
+ if (typeof env_input === "symbol") return;
131
+ if (env_input && env_input.trim()) {
132
+ const env = {};
133
+ env_input.split(",").forEach((pair) => {
134
+ const [key, ...valueParts] = pair.split("=");
135
+ if (key && valueParts.length > 0) env[key.trim()] = valueParts.join("=").trim();
136
+ });
137
+ if (Object.keys(env).length > 0) server_data.env = env;
138
+ }
139
+ if (transport_type === "http") {
140
+ const headers_input = await text({
141
+ message: "HTTP headers (KEY=value, comma-separated):",
142
+ placeholder: "e.g., Authorization=Bearer token, Content-Type=application/json"
143
+ });
144
+ if (typeof headers_input === "symbol") return;
145
+ if (headers_input && headers_input.trim()) {
146
+ const headers = {};
147
+ headers_input.split(",").forEach((pair) => {
148
+ const [key, ...valueParts] = pair.split("=");
149
+ if (key && valueParts.length > 0) headers[key.trim()] = valueParts.join("=").trim();
150
+ });
151
+ if (Object.keys(headers).length > 0) server_data.headers = headers;
152
+ }
153
+ }
154
+ }
155
+ const validated_server = validate_mcp_server(server_data);
156
+ const details = format_server_details(validated_server);
157
+ details.push(`Scope: ${get_scope_description(scope)}`);
158
+ note(`Server to add:\n${details.join("\n")}`);
159
+ const should_add = await confirm({ message: "Add this server?" });
160
+ if (typeof should_add === "symbol" || !should_add) return;
161
+ await add_server_to_registry(validated_server);
162
+ if (cli_available) {
163
+ const result = await add_mcp_via_cli(validated_server, scope);
164
+ if (result.success) note(`Server "${validated_server.name}" installed successfully!\nScope: ${get_scope_description(scope)}\nAlso added to mcpick registry for profile management.`);
165
+ else log.warn(`CLI installation failed: ${result.error}\nServer added to registry only. Use 'claude mcp add' manually.`);
166
+ } else log.warn("Claude CLI not found. Server added to registry only.\nInstall Claude Code CLI and run 'claude mcp add' to activate.");
167
+ } catch (error) {
168
+ throw new Error(`Failed to add server: ${error instanceof Error ? error.message : "Unknown error"}`);
169
+ }
170
+ }
171
+ async function add_server_from_json(scope, cli_available) {
172
+ const json_input = await text({
173
+ message: "Paste JSON configuration:",
174
+ placeholder: "{ \"name\": \"mcp-sqlite-tools\", \"command\": \"npx\", \"args\": [\"-y\", \"mcp-sqlite-tools\"] }",
175
+ validate: (value) => {
176
+ if (!value || value.trim().length === 0) return "JSON configuration is required";
177
+ let jsonString = value.trim();
178
+ if (!jsonString.startsWith("{")) jsonString = `{${jsonString}}`;
179
+ try {
180
+ const parsed = JSON.parse(jsonString);
181
+ if (typeof parsed !== "object" || parsed === null) return "JSON must be an object";
182
+ if (!parsed.command) return "Server configuration must include a \"command\" field";
183
+ } catch {
184
+ return "Invalid JSON format";
185
+ }
186
+ }
187
+ });
188
+ if (typeof json_input === "symbol") return;
189
+ try {
190
+ let jsonString = json_input.trim();
191
+ if (!jsonString.startsWith("{")) jsonString = `{${jsonString}}`;
192
+ const server_data = JSON.parse(jsonString);
193
+ if (!server_data.type && server_data.command) server_data.type = "stdio";
194
+ if (server_data.type !== "stdio") {
195
+ delete server_data.command;
196
+ delete server_data.args;
197
+ }
198
+ if (server_data.command && !server_data.args) server_data.args = [];
199
+ const validated_server = validate_mcp_server(server_data);
200
+ const details = format_server_details(validated_server);
201
+ details.push(`Scope: ${get_scope_description(scope)}`);
202
+ note(`Server to add:\n${details.join("\n")}`);
203
+ const should_add = await confirm({ message: "Add this server?" });
204
+ if (typeof should_add === "symbol" || !should_add) return;
205
+ await add_server_to_registry(validated_server);
206
+ if (cli_available) {
207
+ const result = await add_mcp_via_cli(validated_server, scope);
208
+ if (result.success) note(`Server "${validated_server.name}" installed successfully!\nScope: ${get_scope_description(scope)}\nAlso added to mcpick registry for profile management.`);
209
+ else log.warn(`CLI installation failed: ${result.error}\nServer added to registry only. Use 'claude mcp add' manually.`);
210
+ } else log.warn("Claude CLI not found. Server added to registry only.\nInstall Claude Code CLI and run 'claude mcp add' to activate.");
211
+ } catch (error) {
212
+ throw new Error(`Failed to parse or validate JSON: ${error instanceof Error ? error.message : "Unknown error"}`);
213
+ }
214
+ }
215
+ //#endregion
216
+ //#region src/commands/backup.ts
217
+ const MAX_BACKUPS = 10;
218
+ async function backup_config() {
219
+ try {
220
+ const current_config = await read_claude_config();
221
+ const current_settings = await read_claude_settings();
222
+ const backups_dir = get_backups_dir();
223
+ await ensure_directory_exists(backups_dir);
224
+ const mcp_path = join(backups_dir, get_backup_filename());
225
+ const mcp_backup = { mcpServers: current_config.mcpServers || {} };
226
+ await writeFile(mcp_path, JSON.stringify(mcp_backup, null, 2), "utf-8");
227
+ const plugins = current_settings.enabledPlugins || {};
228
+ const plugin_count = Object.keys(plugins).length;
229
+ let plugin_msg = "";
230
+ if (plugin_count > 0) {
231
+ const plugin_path = join(backups_dir, get_plugin_backup_filename());
232
+ await writeFile(plugin_path, JSON.stringify({ enabledPlugins: plugins }, null, 2), "utf-8");
233
+ plugin_msg = `\nPlugins backup: ${plugin_path}\n(${plugin_count} plugins backed up)`;
234
+ }
235
+ await cleanup_old_backups("mcp-servers-");
236
+ await cleanup_old_backups("plugins-");
237
+ const server_count = Object.keys(current_config.mcpServers || {}).length;
238
+ note(`MCP servers backup: ${mcp_path}\n(${server_count} servers backed up)${plugin_msg}`);
239
+ } catch (error) {
240
+ throw new Error(`Failed to create backup: ${error instanceof Error ? error.message : "Unknown error"}`);
241
+ }
242
+ }
243
+ async function cleanup_old_backups(prefix) {
244
+ try {
245
+ const backups_dir = get_backups_dir();
246
+ const backup_files = (await readdir(backups_dir)).filter((file) => file.startsWith(prefix) && file.endsWith(".json")).sort().reverse();
247
+ if (backup_files.length > MAX_BACKUPS) {
248
+ const files_to_delete = backup_files.slice(MAX_BACKUPS);
249
+ for (const file of files_to_delete) await unlink(join(backups_dir, file));
250
+ }
251
+ } catch {}
252
+ }
253
+ //#endregion
254
+ //#region src/commands/edit-config.ts
255
+ async function edit_config() {
256
+ try {
257
+ const cli_available = await check_claude_cli();
258
+ const scope = await select({
259
+ message: "Which configuration do you want to edit?",
260
+ options: get_scope_options(),
261
+ initialValue: "local"
262
+ });
263
+ if (typeof scope === "symbol") return;
264
+ const current_config = await read_claude_config();
265
+ let all_servers = await get_all_available_servers();
266
+ if (all_servers.length === 0 && current_config.mcpServers) {
267
+ const current_servers = get_enabled_servers(current_config);
268
+ if (current_servers.length > 0) {
269
+ await sync_servers_to_registry(current_servers);
270
+ all_servers = current_servers;
271
+ note(`Imported ${current_servers.length} servers from your .claude.json file into registry.`);
272
+ }
273
+ }
274
+ if (all_servers.length === 0) {
275
+ note("No MCP servers found in .claude.json or registry. Add servers first.");
276
+ return;
277
+ }
278
+ const currently_enabled = await get_enabled_servers_for_scope(scope);
279
+ const server_choices = all_servers.map((server) => ({
280
+ value: server.name,
281
+ label: server.name,
282
+ hint: server.description || ""
283
+ }));
284
+ const selected_server_names = await multiselect({
285
+ message: `Select MCP servers for ${get_scope_description(scope)}:`,
286
+ options: server_choices,
287
+ initialValues: currently_enabled,
288
+ required: false
289
+ });
290
+ if (typeof selected_server_names === "symbol") return;
291
+ const selected_servers = all_servers.filter((server) => selected_server_names.includes(server.name));
292
+ const servers_to_add = selected_server_names.filter((name) => !currently_enabled.includes(name));
293
+ const servers_to_remove = currently_enabled.filter((name) => !selected_server_names.includes(name));
294
+ if (cli_available && (scope === "local" || scope === "project")) {
295
+ let success_count = 0;
296
+ let error_count = 0;
297
+ for (const name of servers_to_add) {
298
+ const server = all_servers.find((s) => s.name === name);
299
+ if (server) {
300
+ const result = await add_mcp_via_cli(server, scope);
301
+ if (result.success) success_count++;
302
+ else {
303
+ error_count++;
304
+ log.warn(`Failed to add ${name}: ${result.error}`);
305
+ }
306
+ }
307
+ }
308
+ for (const name of servers_to_remove) {
309
+ const result = await remove_mcp_via_cli(name);
310
+ if (result.success) success_count++;
311
+ else {
312
+ error_count++;
313
+ log.warn(`Failed to remove ${name}: ${result.error}`);
314
+ }
315
+ }
316
+ await sync_servers_to_registry(selected_servers);
317
+ 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}`);
318
+ else note(`Configuration updated!\nScope: ${get_scope_description(scope)}\nEnabled servers: ${selected_servers.length}`);
319
+ } else {
320
+ await write_claude_config(create_config_from_servers(selected_servers));
321
+ await sync_servers_to_registry(selected_servers);
322
+ if (!cli_available && scope !== "user") log.warn(`Claude CLI not available. Changes written to ~/.claude.json (user scope) instead of ${scope} scope.`);
323
+ note(`Configuration updated!\nEnabled servers: ${selected_servers.length}`);
324
+ }
325
+ } catch (error) {
326
+ throw new Error(`Failed to edit configuration: ${error instanceof Error ? error.message : "Unknown error"}`);
327
+ }
328
+ }
329
+ //#endregion
330
+ //#region src/commands/edit-plugins.ts
331
+ async function handle_toggle() {
332
+ const plugins = get_all_plugins(await read_claude_settings());
333
+ if (plugins.length === 0) {
334
+ note("No plugins found in ~/.claude/settings.json.\nInstall plugins via Claude Code: /plugin");
335
+ return;
336
+ }
337
+ const plugin_choices = plugins.map((plugin) => ({
338
+ value: `${plugin.name}@${plugin.marketplace}`,
339
+ label: plugin.name,
340
+ hint: plugin.marketplace
341
+ }));
342
+ const currently_enabled = plugins.filter((p) => p.enabled).map((p) => `${p.name}@${p.marketplace}`);
343
+ const selected = await multiselect({
344
+ message: "Select plugins to enable:",
345
+ options: plugin_choices,
346
+ initialValues: currently_enabled,
347
+ required: false
348
+ });
349
+ if (typeof selected === "symbol") return;
350
+ const selected_set = new Set(selected);
351
+ const updated_plugins = plugins.map((plugin) => ({
352
+ ...plugin,
353
+ enabled: selected_set.has(`${plugin.name}@${plugin.marketplace}`)
354
+ }));
355
+ await write_claude_settings({ enabledPlugins: build_enabled_plugins(updated_plugins) });
356
+ const enabled_count = updated_plugins.filter((p) => p.enabled).length;
357
+ const disabled_count = updated_plugins.filter((p) => !p.enabled).length;
358
+ note(`Plugins updated!\nEnabled: ${enabled_count}, Disabled: ${disabled_count}`);
359
+ const newly_enabled = updated_plugins.filter((p) => p.enabled && !currently_enabled.includes(`${p.name}@${p.marketplace}`));
360
+ const newly_disabled = updated_plugins.filter((p) => !p.enabled && currently_enabled.includes(`${p.name}@${p.marketplace}`));
361
+ if (newly_enabled.length > 0) log.success(`Enabled: ${newly_enabled.map((p) => p.name).join(", ")}`);
362
+ if (newly_disabled.length > 0) log.warn(`Disabled: ${newly_disabled.map((p) => p.name).join(", ")}`);
363
+ }
364
+ async function handle_install() {
365
+ const key = await text({
366
+ message: "Plugin to install (name@marketplace):",
367
+ placeholder: "e.g. commit-commands@claude-plugins-official",
368
+ validate: (value) => {
369
+ if (!value || value.trim().length === 0) return "Plugin key is required";
370
+ if (!value.includes("@")) return "Format: plugin-name@marketplace-name";
371
+ }
372
+ });
373
+ if (isCancel(key)) return;
374
+ const scope = await select({
375
+ message: "Installation scope:",
376
+ options: [
377
+ {
378
+ value: "user",
379
+ label: "User",
380
+ hint: "Global (default)"
381
+ },
382
+ {
383
+ value: "project",
384
+ label: "Project",
385
+ hint: "Shared with team"
386
+ },
387
+ {
388
+ value: "local",
389
+ label: "Local",
390
+ hint: "This project only (gitignored)"
391
+ }
392
+ ]
393
+ });
394
+ if (isCancel(scope)) return;
395
+ const result = await install_plugin_via_cli(key, scope);
396
+ if (result.success) log.success(`Installed '${key}' (scope: ${scope})`);
397
+ else log.error(result.error ?? "Unknown error");
398
+ }
399
+ async function handle_uninstall() {
400
+ const plugins = get_all_plugins(await read_claude_settings());
401
+ if (plugins.length === 0) {
402
+ log.info("No plugins installed.");
403
+ return;
404
+ }
405
+ const selected = await select({
406
+ message: "Select plugin to uninstall:",
407
+ options: plugins.map((p) => ({
408
+ value: `${p.name}@${p.marketplace}`,
409
+ label: p.name,
410
+ hint: p.marketplace
411
+ }))
412
+ });
413
+ if (isCancel(selected)) return;
414
+ const should_uninstall = await confirm({ message: `Uninstall '${selected}'?` });
415
+ if (isCancel(should_uninstall) || !should_uninstall) return;
416
+ const result = await uninstall_plugin_via_cli(selected);
417
+ if (result.success) log.success(`Uninstalled '${selected}'`);
418
+ else log.error(result.error ?? "Unknown error");
419
+ }
420
+ async function handle_update$1() {
421
+ const plugins = get_all_plugins(await read_claude_settings());
422
+ if (plugins.length === 0) {
423
+ log.info("No plugins installed.");
424
+ return;
425
+ }
426
+ const selected = await select({
427
+ message: "Select plugin to update:",
428
+ options: plugins.map((p) => ({
429
+ value: `${p.name}@${p.marketplace}`,
430
+ label: p.name,
431
+ hint: p.marketplace
432
+ }))
433
+ });
434
+ if (isCancel(selected)) return;
435
+ const result = await update_plugin_via_cli(selected);
436
+ if (result.success) log.success(`Updated '${selected}'`);
437
+ else log.error(result.error ?? "Unknown error");
438
+ }
439
+ async function edit_plugins() {
440
+ while (true) {
441
+ const action = await select({
442
+ message: "Plugin management:",
443
+ options: [
444
+ {
445
+ value: "toggle",
446
+ label: "Enable / Disable plugins",
447
+ hint: "Toggle plugins on/off"
448
+ },
449
+ {
450
+ value: "install",
451
+ label: "Install plugin",
452
+ hint: "Install from a marketplace"
453
+ },
454
+ {
455
+ value: "uninstall",
456
+ label: "Uninstall plugin",
457
+ hint: "Remove a plugin entirely"
458
+ },
459
+ {
460
+ value: "update",
461
+ label: "Update plugin",
462
+ hint: "Update to latest version"
463
+ },
464
+ {
465
+ value: "back",
466
+ label: "Back",
467
+ hint: "Return to main menu"
468
+ }
469
+ ]
470
+ });
471
+ if (isCancel(action) || action === "back") return;
472
+ try {
473
+ switch (action) {
474
+ case "toggle":
475
+ await handle_toggle();
476
+ break;
477
+ case "install":
478
+ await handle_install();
479
+ break;
480
+ case "uninstall":
481
+ await handle_uninstall();
482
+ break;
483
+ case "update":
484
+ await handle_update$1();
485
+ break;
486
+ }
487
+ } catch (err) {
488
+ log.error(err instanceof Error ? err.message : "Unknown error");
489
+ }
490
+ }
491
+ }
492
+ //#endregion
493
+ //#region src/commands/manage-cache.ts
494
+ function format_status_line(p) {
495
+ const markers = [];
496
+ if (p.isVersionStale) markers.push(`version: ${p.installedVersion} → ${p.latestVersion}`);
497
+ if (p.isShaStale) markers.push("commits behind");
498
+ if (p.orphanedVersions.length > 0) markers.push(`${p.orphanedVersions.length} orphaned`);
499
+ const status = markers.length > 0 ? `[stale: ${markers.join(", ")}]` : "[up to date]";
500
+ return `${p.name}@${p.marketplace} v${p.installedVersion} ${status}`;
501
+ }
502
+ async function handle_status() {
503
+ const plugins = await get_cached_plugins_info();
504
+ if (plugins.length === 0) {
505
+ log.info("No cached plugins found.");
506
+ return;
507
+ }
508
+ note(plugins.map(format_status_line).join("\n"), "Plugin Cache Status");
509
+ }
510
+ async function handle_clear() {
511
+ const plugins = await get_cached_plugins_info();
512
+ if (plugins.length === 0) {
513
+ log.info("No cached plugins to clear.");
514
+ return;
515
+ }
516
+ const selected = await multiselect({
517
+ message: "Select plugins to clear cache for:",
518
+ options: plugins.map((p) => {
519
+ const stale = p.isVersionStale || p.isShaStale;
520
+ return {
521
+ value: p.key,
522
+ label: `${p.name}@${p.marketplace}`,
523
+ hint: stale ? `v${p.installedVersion} → ${p.latestVersion ?? "unknown"} (stale)` : `v${p.installedVersion}`
524
+ };
525
+ }),
526
+ initialValues: plugins.filter((p) => p.isVersionStale || p.isShaStale).map((p) => p.key)
527
+ });
528
+ if (isCancel(selected) || selected.length === 0) return;
529
+ const should_clear = await confirm({ message: `Clear cache for ${selected.length} plugin(s)? This will also refresh the marketplace.` });
530
+ if (isCancel(should_clear) || !should_clear) return;
531
+ const result = await clear_plugin_caches(selected);
532
+ for (const key of result.cleared) log.success(`Cleared: ${key}`);
533
+ for (const err of result.errors) log.error(`Error: ${err}`);
534
+ if (result.cleared.length > 0) note("Run /reload-plugins in Claude Code or restart your session to apply changes.", "Next Steps");
535
+ }
536
+ async function handle_clean_orphaned() {
537
+ const should_clean = await confirm({ message: "Remove all orphaned plugin version directories?" });
538
+ if (isCancel(should_clean) || !should_clean) return;
539
+ const result = await clean_orphaned_versions();
540
+ if (result.cleaned === 0) log.info("No orphaned versions found.");
541
+ else {
542
+ for (const p of result.paths) log.success(`Removed: ${p}`);
543
+ log.info(`Cleaned ${result.cleaned} orphaned version(s).`);
544
+ }
545
+ }
546
+ async function handle_refresh() {
547
+ const should_refresh = await confirm({ message: "Refresh all marketplace clones (git pull)?" });
548
+ if (isCancel(should_refresh) || !should_refresh) return;
549
+ const results = await refresh_all_marketplaces();
550
+ if (results.size === 0) {
551
+ log.info("No marketplaces configured.");
552
+ return;
553
+ }
554
+ for (const [name, result] of results) if (result.success) log.success(`${name}: refreshed`);
555
+ else log.error(`${name}: ${result.error}`);
556
+ }
557
+ async function manage_cache() {
558
+ while (true) {
559
+ const action = await select({
560
+ message: "Plugin cache management:",
561
+ options: [
562
+ {
563
+ value: "status",
564
+ label: "View cache status",
565
+ hint: "Show plugins with staleness info"
566
+ },
567
+ {
568
+ value: "clear",
569
+ label: "Clear plugin caches",
570
+ hint: "Refresh marketplace + clear selected caches"
571
+ },
572
+ {
573
+ value: "clean-orphaned",
574
+ label: "Clean orphaned versions",
575
+ hint: "Remove old version directories"
576
+ },
577
+ {
578
+ value: "refresh",
579
+ label: "Refresh marketplaces",
580
+ hint: "Git pull all marketplace clones"
581
+ },
582
+ {
583
+ value: "back",
584
+ label: "Back",
585
+ hint: "Return to main menu"
586
+ }
587
+ ]
588
+ });
589
+ if (isCancel(action) || action === "back") return;
590
+ switch (action) {
591
+ case "status":
592
+ await handle_status();
593
+ break;
594
+ case "clear":
595
+ await handle_clear();
596
+ break;
597
+ case "clean-orphaned":
598
+ await handle_clean_orphaned();
599
+ break;
600
+ case "refresh":
601
+ await handle_refresh();
602
+ break;
603
+ }
604
+ }
605
+ }
606
+ //#endregion
607
+ //#region src/commands/manage-hooks.ts
608
+ function format_hook(entry) {
609
+ const detail = entry.handler.command || entry.handler.url || entry.handler.prompt || "(unknown)";
610
+ const truncated = detail.length > 50 ? detail.substring(0, 47) + "..." : detail;
611
+ return `${entry.event} → ${entry.handler.type}: ${truncated}`;
612
+ }
613
+ function format_source(entry) {
614
+ if (entry.source === "plugin" && entry.plugin_key) return entry.plugin_key;
615
+ return entry.scope;
616
+ }
617
+ async function manage_hooks() {
618
+ const active_hooks = await get_all_hooks();
619
+ const disabled = await read_disabled_hooks();
620
+ const items = [];
621
+ for (let i = 0; i < active_hooks.length; i++) {
622
+ const h = active_hooks[i];
623
+ items.push({
624
+ id: `active:${i}`,
625
+ active_entry: h,
626
+ label: format_hook(h),
627
+ hint: format_source(h)
628
+ });
629
+ }
630
+ for (let i = 0; i < disabled.length; i++) {
631
+ const d = disabled[i];
632
+ const detail = d.original_handler.command || d.original_handler.url || d.original_handler.prompt || "(unknown)";
633
+ const truncated = detail.length > 50 ? detail.substring(0, 47) + "..." : detail;
634
+ items.push({
635
+ id: `disabled:${i}`,
636
+ disabled_index: i,
637
+ label: `${d.event} → ${d.original_handler.type}: ${truncated}`,
638
+ hint: `${d.plugin_key} (disabled)`
639
+ });
640
+ }
641
+ if (items.length === 0) {
642
+ note("No hooks found (settings or plugins).");
643
+ return;
644
+ }
645
+ const currently_enabled = items.filter((item) => item.active_entry).map((item) => item.id);
646
+ const selected = await multiselect({
647
+ message: "Toggle hooks on/off:",
648
+ options: items.map((item) => ({
649
+ value: item.id,
650
+ label: item.label,
651
+ hint: item.hint
652
+ })),
653
+ initialValues: currently_enabled,
654
+ required: false
655
+ });
656
+ if (isCancel(selected)) return;
657
+ const selected_set = new Set(selected);
658
+ let changes = 0;
659
+ for (const item of items) {
660
+ if (!item.active_entry) continue;
661
+ if (selected_set.has(item.id)) continue;
662
+ if (item.active_entry.source === "plugin") {
663
+ await disable_plugin_hook(item.active_entry);
664
+ changes++;
665
+ }
666
+ }
667
+ for (const item of items) {
668
+ if (item.disabled_index === void 0) continue;
669
+ if (!selected_set.has(item.id)) continue;
670
+ await enable_plugin_hook(disabled[item.disabled_index]);
671
+ changes++;
672
+ }
673
+ if (changes > 0) {
674
+ log.success(`${changes} hook(s) updated.`);
675
+ log.info("Restart Claude Code for changes to take effect.");
676
+ } else log.info("No changes.");
677
+ }
678
+ //#endregion
679
+ //#region src/commands/manage-marketplace.ts
680
+ /**
681
+ * Browse all available plugins across all marketplaces.
682
+ * Shows a multiselect with installed plugins pre-selected — toggle to install/uninstall.
683
+ */
684
+ async function handle_browse() {
685
+ const known = await read_known_marketplaces();
686
+ const marketplace_names = Object.keys(known);
687
+ if (marketplace_names.length === 0) {
688
+ note("No marketplaces configured.\nAdd one first to browse plugins.");
689
+ return;
690
+ }
691
+ const all_available = [];
692
+ for (const mkt_name of marketplace_names) {
693
+ const manifest = await read_marketplace_manifest(mkt_name);
694
+ if (!manifest?.plugins?.length) continue;
695
+ for (const p of manifest.plugins) all_available.push({
696
+ key: `${p.name}@${mkt_name}`,
697
+ name: p.name,
698
+ marketplace: mkt_name,
699
+ description: p.description
700
+ });
701
+ }
702
+ if (all_available.length === 0) {
703
+ note("No plugins found in any marketplace.");
704
+ return;
705
+ }
706
+ const installed = get_all_plugins(await read_claude_settings());
707
+ const installed_keys = new Set(installed.map((p) => `${p.name}@${p.marketplace}`));
708
+ const selected = await multiselect({
709
+ message: `Available plugins (${all_available.length}) — toggle to install/uninstall:`,
710
+ options: all_available.map((p) => ({
711
+ value: p.key,
712
+ label: p.name,
713
+ hint: `${p.marketplace}${p.description ? ` · ${p.description}` : ""}`
714
+ })),
715
+ initialValues: all_available.filter((p) => installed_keys.has(p.key)).map((p) => p.key),
716
+ required: false
717
+ });
718
+ if (isCancel(selected)) return;
719
+ const selected_set = new Set(selected);
720
+ const to_install = all_available.filter((p) => selected_set.has(p.key) && !installed_keys.has(p.key));
721
+ const to_uninstall = all_available.filter((p) => !selected_set.has(p.key) && installed_keys.has(p.key));
722
+ if (to_install.length === 0 && to_uninstall.length === 0) {
723
+ log.info("No changes.");
724
+ return;
725
+ }
726
+ for (const p of to_install) {
727
+ log.info(`Installing ${p.key}...`);
728
+ const result = await install_plugin_via_cli(p.key);
729
+ if (result.success) log.success(`Installed: ${p.key}`);
730
+ else log.error(`Failed: ${p.key} - ${result.error}`);
731
+ }
732
+ for (const p of to_uninstall) {
733
+ log.info(`Uninstalling ${p.key}...`);
734
+ const result = await uninstall_plugin_via_cli(p.key);
735
+ if (result.success) log.success(`Uninstalled: ${p.key}`);
736
+ else log.error(`Failed: ${p.key} - ${result.error}`);
737
+ }
738
+ const parts = [];
739
+ if (to_install.length > 0) parts.push(`${to_install.length} installed`);
740
+ if (to_uninstall.length > 0) parts.push(`${to_uninstall.length} uninstalled`);
741
+ note(parts.join(", "), "Plugins updated");
742
+ }
743
+ async function handle_add() {
744
+ const source = await text({
745
+ message: "Marketplace source:",
746
+ placeholder: "e.g. owner/repo or https://github.com/owner/repo",
747
+ validate: (value) => {
748
+ if (!value || value.trim().length === 0) return "Marketplace source is required";
749
+ }
750
+ });
751
+ if (isCancel(source)) return;
752
+ log.info(`Adding marketplace: ${source}`);
753
+ const result = await marketplace_add_via_cli(source);
754
+ if (!result.success) {
755
+ log.error(result.error || "Unknown error");
756
+ return;
757
+ }
758
+ log.success(`Marketplace added: ${source}`);
759
+ const marketplace_name = derive_marketplace_name(source);
760
+ const manifest = marketplace_name ? await read_marketplace_manifest(marketplace_name) : null;
761
+ if (!manifest?.plugins?.length) {
762
+ log.info("Install plugins with: mcpick plugins install <name>@<marketplace>");
763
+ return;
764
+ }
765
+ const should_install = await confirm({ message: `${manifest.plugins.length} plugins available. Install now?` });
766
+ if (isCancel(should_install) || !should_install) return;
767
+ const to_install = await multiselect({
768
+ message: "Select plugins to install:",
769
+ options: manifest.plugins.map((p) => ({
770
+ value: `${p.name}@${marketplace_name}`,
771
+ label: p.name,
772
+ hint: p.description
773
+ })),
774
+ required: false
775
+ });
776
+ if (isCancel(to_install) || to_install.length === 0) return;
777
+ for (const key of to_install) {
778
+ log.info(`Installing ${key}...`);
779
+ const install_result = await install_plugin_via_cli(key);
780
+ if (install_result.success) log.success(`Installed: ${key}`);
781
+ else log.error(`Failed: ${key} - ${install_result.error}`);
782
+ }
783
+ }
784
+ function derive_marketplace_name(source) {
785
+ const match = source.match(/([^/]+?)(?:\.git)?$/);
786
+ return match ? match[1].toLowerCase() : null;
787
+ }
788
+ async function handle_remove() {
789
+ const known = await read_known_marketplaces();
790
+ const names = Object.keys(known);
791
+ if (names.length === 0) {
792
+ note("No marketplaces configured.");
793
+ return;
794
+ }
795
+ const name = await select({
796
+ message: "Select marketplace to remove:",
797
+ options: names.map((n) => ({
798
+ value: n,
799
+ label: n,
800
+ hint: known[n].source.repo || known[n].source.url
801
+ }))
802
+ });
803
+ if (isCancel(name)) return;
804
+ const should_remove = await confirm({ message: `Remove marketplace '${name}'? Its plugins will also be removed.` });
805
+ if (isCancel(should_remove) || !should_remove) return;
806
+ const remove_result = await marketplace_remove_via_cli(name);
807
+ if (remove_result.success) log.success(`Marketplace removed: ${name}`);
808
+ else log.error(remove_result.error || "Unknown error");
809
+ }
810
+ async function handle_update() {
811
+ const known = await read_known_marketplaces();
812
+ const names = Object.keys(known);
813
+ if (names.length === 0) {
814
+ note("No marketplaces configured.");
815
+ return;
816
+ }
817
+ const choice = await select({
818
+ message: "What to update:",
819
+ options: [{
820
+ value: "__all__",
821
+ label: "All marketplaces"
822
+ }, ...names.map((n) => ({
823
+ value: n,
824
+ label: n,
825
+ hint: known[n].source.repo || known[n].source.url
826
+ }))]
827
+ });
828
+ if (isCancel(choice)) return;
829
+ if (choice === "__all__") {
830
+ log.info("Updating all marketplaces...");
831
+ const result = await marketplace_update_via_cli();
832
+ if (result.success) log.success("All marketplaces updated.");
833
+ else log.error(result.error || "Unknown error");
834
+ } else {
835
+ log.info(`Updating ${choice}...`);
836
+ const result = await marketplace_update_via_cli(choice);
837
+ if (result.success) log.success(`Marketplace updated: ${choice}`);
838
+ else log.error(result.error || "Unknown error");
839
+ }
840
+ const restored = await check_restored_hooks();
841
+ if (restored.length > 0) {
842
+ log.warn(`${restored.length} disabled hook(s) were restored by the update.`);
843
+ const should_redisable = await confirm({ message: "Re-disable these hooks?" });
844
+ if (!isCancel(should_redisable) && should_redisable) {
845
+ const redisable_result = await redisable_restored_hooks(restored);
846
+ if (redisable_result.success > 0) log.success(`Re-disabled ${redisable_result.success} hook(s).`);
847
+ if (redisable_result.failed > 0) log.error(`Failed to re-disable ${redisable_result.failed} hook(s).`);
848
+ }
849
+ }
850
+ }
851
+ async function manage_marketplace() {
852
+ while (true) {
853
+ const action = await select({
854
+ message: "Marketplace & plugins:",
855
+ options: [
856
+ {
857
+ value: "browse",
858
+ label: "Browse & install plugins",
859
+ hint: "Toggle plugins on/off across all marketplaces"
860
+ },
861
+ {
862
+ value: "add",
863
+ label: "Add marketplace",
864
+ hint: "Add a plugin catalog, then install plugins from it"
865
+ },
866
+ {
867
+ value: "remove",
868
+ label: "Remove marketplace",
869
+ hint: "Remove a marketplace and its plugins"
870
+ },
871
+ {
872
+ value: "update",
873
+ label: "Update marketplace(s)",
874
+ hint: "Pull latest from source"
875
+ },
876
+ {
877
+ value: "back",
878
+ label: "Back",
879
+ hint: "Return to main menu"
880
+ }
881
+ ]
882
+ });
883
+ if (isCancel(action) || action === "back") return;
884
+ try {
885
+ switch (action) {
886
+ case "browse":
887
+ await handle_browse();
888
+ break;
889
+ case "add":
890
+ await handle_add();
891
+ break;
892
+ case "remove":
893
+ await handle_remove();
894
+ break;
895
+ case "update":
896
+ await handle_update();
897
+ break;
898
+ }
899
+ } catch (err) {
900
+ log.error(err instanceof Error ? err.message : "Unknown error");
901
+ }
902
+ }
903
+ }
904
+ //#endregion
905
+ //#region src/commands/restore.ts
906
+ async function restore_config() {
907
+ try {
908
+ const restore_type = await select({
909
+ message: "What would you like to restore?",
910
+ options: [{
911
+ value: "mcp",
912
+ label: "MCP servers",
913
+ hint: "Restore server configuration"
914
+ }, {
915
+ value: "plugins",
916
+ label: "Plugins",
917
+ hint: "Restore plugin enabled/disabled state"
918
+ }]
919
+ });
920
+ if (isCancel(restore_type)) return;
921
+ if (restore_type === "mcp") await restore_mcp();
922
+ else await restore_plugins();
923
+ } catch (error) {
924
+ throw new Error(`Failed to restore configuration: ${error instanceof Error ? error.message : "Unknown error"}`);
925
+ }
926
+ }
927
+ async function restore_mcp() {
928
+ const backups = await list_backups();
929
+ if (backups.length === 0) {
930
+ note("No MCP server backups found.");
931
+ return;
932
+ }
933
+ const selected_backup_path = await select({
934
+ message: "Select backup to restore:",
935
+ options: backups.map((backup) => ({
936
+ value: backup.path,
937
+ label: `${backup.filename} (${backup.timestamp.toLocaleString()})`,
938
+ hint: format_time_ago(backup.timestamp)
939
+ }))
940
+ });
941
+ if (isCancel(selected_backup_path)) return;
942
+ const should_restore = await confirm({ message: "This will replace your current MCP servers configuration. Continue?" });
943
+ if (isCancel(should_restore) || !should_restore) return;
944
+ const backup_content = await readFile(selected_backup_path, "utf-8");
945
+ const backup_data = JSON.parse(backup_content);
946
+ await write_claude_config({
947
+ ...await read_claude_config(),
948
+ mcpServers: backup_data.mcpServers || {}
949
+ });
950
+ const server_count = Object.keys(backup_data.mcpServers || {}).length;
951
+ log.success(`MCP servers restored (${server_count} servers)`);
952
+ }
953
+ async function restore_plugins() {
954
+ const backups = await list_plugin_backups();
955
+ if (backups.length === 0) {
956
+ note("No plugin backups found.");
957
+ return;
958
+ }
959
+ const selected_backup_path = await select({
960
+ message: "Select plugin backup to restore:",
961
+ options: backups.map((backup) => ({
962
+ value: backup.path,
963
+ label: `${backup.filename} (${backup.timestamp.toLocaleString()})`,
964
+ hint: format_time_ago(backup.timestamp)
965
+ }))
966
+ });
967
+ if (isCancel(selected_backup_path)) return;
968
+ const should_restore = await confirm({ message: "This will replace your current plugin configuration. Continue?" });
969
+ if (isCancel(should_restore) || !should_restore) return;
970
+ const backup_content = await readFile(selected_backup_path, "utf-8");
971
+ const backup_data = JSON.parse(backup_content);
972
+ await write_claude_settings({ enabledPlugins: backup_data.enabledPlugins || {} });
973
+ const plugin_count = Object.keys(backup_data.enabledPlugins || {}).length;
974
+ log.success(`Plugins restored (${plugin_count} plugins)`);
975
+ }
976
+ function format_time_ago(date) {
977
+ const diff = (/* @__PURE__ */ new Date()).getTime() - date.getTime();
978
+ const minutes = Math.floor(diff / 6e4);
979
+ const hours = Math.floor(minutes / 60);
980
+ const days = Math.floor(hours / 24);
981
+ if (days > 0) return `${days} day${days > 1 ? "s" : ""} ago`;
982
+ else if (hours > 0) return `${hours} hour${hours > 1 ? "s" : ""} ago`;
983
+ else if (minutes > 0) return `${minutes} minute${minutes > 1 ? "s" : ""} ago`;
984
+ else return "just now";
985
+ }
986
+ //#endregion
987
+ //#region src/index.ts
14
988
  function parse_args() {
15
- const args = process.argv.slice(2);
16
- const result = {};
17
- for (let i = 0; i < args.length; i++) {
18
- if (args[i] === '--profile' || args[i] === '-p') {
19
- result.profile = args[i + 1];
20
- i++;
21
- }
22
- else if (args[i] === '--save-profile' || args[i] === '-s') {
23
- result.saveProfile = args[i + 1];
24
- i++;
25
- }
26
- else if (args[i] === '--list-profiles' || args[i] === '-l') {
27
- result.listProfiles = true;
28
- }
29
- }
30
- return result;
989
+ const args = process.argv.slice(2);
990
+ const result = {};
991
+ for (let i = 0; i < args.length; i++) if (args[i] === "--profile" || args[i] === "-p") {
992
+ result.profile = args[i + 1];
993
+ i++;
994
+ } else if (args[i] === "--save-profile" || args[i] === "-s") {
995
+ result.saveProfile = args[i + 1];
996
+ i++;
997
+ } else if (args[i] === "--list-profiles" || args[i] === "-l") result.listProfiles = true;
998
+ return result;
31
999
  }
32
1000
  async function apply_profile(name) {
33
- intro(`MCPick - Loading profile: ${name}`);
34
- try {
35
- const profile = await load_profile(name);
36
- await write_claude_config(profile.config);
37
- const server_count = Object.keys(profile.config.mcpServers || {}).length;
38
- const parts = [`${server_count} servers`];
39
- if (profile.enabledPlugins) {
40
- await write_claude_settings({
41
- enabledPlugins: profile.enabledPlugins,
42
- });
43
- parts.push(`${Object.keys(profile.enabledPlugins).length} plugins`);
44
- }
45
- log.success(`Profile '${name}' applied (${parts.join(', ')})`);
46
- outro('Done!');
47
- }
48
- catch (error) {
49
- if (error instanceof Error) {
50
- cancel(error.message);
51
- }
52
- else {
53
- cancel('Failed to load profile');
54
- }
55
- process.exit(1);
56
- }
1001
+ intro(`MCPick - Loading profile: ${name}`);
1002
+ try {
1003
+ const profile = await load_profile(name);
1004
+ await write_claude_config(profile.config);
1005
+ const parts = [`${Object.keys(profile.config.mcpServers || {}).length} servers`];
1006
+ if (profile.enabledPlugins) {
1007
+ await write_claude_settings({ enabledPlugins: profile.enabledPlugins });
1008
+ parts.push(`${Object.keys(profile.enabledPlugins).length} plugins`);
1009
+ }
1010
+ log.success(`Profile '${name}' applied (${parts.join(", ")})`);
1011
+ outro("Done!");
1012
+ } catch (error) {
1013
+ if (error instanceof Error) cancel(error.message);
1014
+ else cancel("Failed to load profile");
1015
+ process.exit(1);
1016
+ }
57
1017
  }
58
1018
  async function show_profiles() {
59
- intro('MCPick - Available Profiles');
60
- const profiles = await list_profiles();
61
- if (profiles.length === 0) {
62
- log.warn('No profiles found in ~/.claude/mcpick/profiles/');
63
- log.info('Create .json files there to use profiles');
64
- }
65
- else {
66
- for (const p of profiles) {
67
- log.info(`${p.name} (${p.serverCount} servers)`);
68
- }
69
- }
70
- outro('');
1019
+ intro("MCPick - Available Profiles");
1020
+ const profiles = await list_profiles();
1021
+ if (profiles.length === 0) {
1022
+ log.warn("No profiles found in ~/.claude/mcpick/profiles/");
1023
+ log.info("Create .json files there to use profiles");
1024
+ } else for (const p of profiles) log.info(`${p.name} (${p.serverCount} servers)`);
1025
+ outro("");
71
1026
  }
72
1027
  async function create_profile(name) {
73
- intro(`MCPick - Saving profile: ${name}`);
74
- try {
75
- const counts = await save_profile(name);
76
- const parts = [`${counts.serverCount} servers`];
77
- if (counts.pluginCount > 0) {
78
- parts.push(`${counts.pluginCount} plugins`);
79
- }
80
- log.success(`Profile '${name}' saved (${parts.join(', ')})`);
81
- outro('Done!');
82
- }
83
- catch (error) {
84
- if (error instanceof Error) {
85
- cancel(error.message);
86
- }
87
- else {
88
- cancel('Failed to save profile');
89
- }
90
- process.exit(1);
91
- }
1028
+ intro(`MCPick - Saving profile: ${name}`);
1029
+ try {
1030
+ const counts = await save_profile(name);
1031
+ const parts = [`${counts.serverCount} servers`];
1032
+ if (counts.pluginCount > 0) parts.push(`${counts.pluginCount} plugins`);
1033
+ log.success(`Profile '${name}' saved (${parts.join(", ")})`);
1034
+ outro("Done!");
1035
+ } catch (error) {
1036
+ if (error instanceof Error) cancel(error.message);
1037
+ else cancel("Failed to save profile");
1038
+ process.exit(1);
1039
+ }
92
1040
  }
93
1041
  async function handle_load_profile() {
94
- const profiles = await list_profiles();
95
- if (profiles.length === 0) {
96
- log.warn('No profiles found');
97
- log.info('Save a profile first or create one in ~/.claude/mcpick/profiles/');
98
- return;
99
- }
100
- const profile_name = await select({
101
- message: 'Select a profile to load:',
102
- options: profiles.map((p) => {
103
- const parts = [`${p.serverCount} servers`];
104
- if (p.pluginCount > 0) {
105
- parts.push(`${p.pluginCount} plugins`);
106
- }
107
- return {
108
- value: p.name,
109
- label: p.name,
110
- hint: parts.join(', '),
111
- };
112
- }),
113
- });
114
- if (isCancel(profile_name))
115
- return;
116
- const profile = await load_profile(profile_name);
117
- await write_claude_config(profile.config);
118
- const server_count = Object.keys(profile.config.mcpServers || {}).length;
119
- const parts = [`${server_count} servers`];
120
- if (profile.enabledPlugins) {
121
- await write_claude_settings({
122
- enabledPlugins: profile.enabledPlugins,
123
- });
124
- parts.push(`${Object.keys(profile.enabledPlugins).length} plugins`);
125
- }
126
- log.success(`Profile '${profile_name}' applied (${parts.join(', ')})`);
1042
+ const profiles = await list_profiles();
1043
+ if (profiles.length === 0) {
1044
+ log.warn("No profiles found");
1045
+ log.info("Save a profile first or create one in ~/.claude/mcpick/profiles/");
1046
+ return;
1047
+ }
1048
+ const profile_name = await select({
1049
+ message: "Select a profile to load:",
1050
+ options: profiles.map((p) => {
1051
+ const parts = [`${p.serverCount} servers`];
1052
+ if (p.pluginCount > 0) parts.push(`${p.pluginCount} plugins`);
1053
+ return {
1054
+ value: p.name,
1055
+ label: p.name,
1056
+ hint: parts.join(", ")
1057
+ };
1058
+ })
1059
+ });
1060
+ if (isCancel(profile_name)) return;
1061
+ const profile = await load_profile(profile_name);
1062
+ await write_claude_config(profile.config);
1063
+ const parts = [`${Object.keys(profile.config.mcpServers || {}).length} servers`];
1064
+ if (profile.enabledPlugins) {
1065
+ await write_claude_settings({ enabledPlugins: profile.enabledPlugins });
1066
+ parts.push(`${Object.keys(profile.enabledPlugins).length} plugins`);
1067
+ }
1068
+ log.success(`Profile '${profile_name}' applied (${parts.join(", ")})`);
127
1069
  }
128
1070
  async function handle_save_profile() {
129
- const name = await text({
130
- message: 'Profile name:',
131
- placeholder: 'e.g. database, web-dev, minimal',
132
- validate: (value) => {
133
- if (!value || value.trim().length === 0) {
134
- return 'Profile name is required';
135
- }
136
- if (!/^[\w-]+$/.test(value)) {
137
- return 'Use only letters, numbers, underscores, hyphens';
138
- }
139
- },
140
- });
141
- if (isCancel(name))
142
- return;
143
- const counts = await save_profile(name);
144
- const parts = [`${counts.serverCount} servers`];
145
- if (counts.pluginCount > 0) {
146
- parts.push(`${counts.pluginCount} plugins`);
147
- }
148
- log.success(`Profile '${name}' saved (${parts.join(', ')})`);
1071
+ const name = await text({
1072
+ message: "Profile name:",
1073
+ placeholder: "e.g. database, web-dev, minimal",
1074
+ validate: (value) => {
1075
+ if (!value || value.trim().length === 0) return "Profile name is required";
1076
+ if (!/^[\w-]+$/.test(value)) return "Use only letters, numbers, underscores, hyphens";
1077
+ }
1078
+ });
1079
+ if (isCancel(name)) return;
1080
+ const counts = await save_profile(name);
1081
+ const parts = [`${counts.serverCount} servers`];
1082
+ if (counts.pluginCount > 0) parts.push(`${counts.pluginCount} plugins`);
1083
+ log.success(`Profile '${name}' saved (${parts.join(", ")})`);
149
1084
  }
150
1085
  async function main() {
151
- const args = parse_args();
152
- // Handle --list-profiles
153
- if (args.listProfiles) {
154
- await show_profiles();
155
- return;
156
- }
157
- // Handle --save-profile <name>
158
- if (args.saveProfile) {
159
- await create_profile(args.saveProfile);
160
- return;
161
- }
162
- // Handle --profile <name>
163
- if (args.profile) {
164
- await apply_profile(args.profile);
165
- return;
166
- }
167
- intro('MCPick - MCP Server Configuration Manager');
168
- while (true) {
169
- try {
170
- const action = await select({
171
- message: 'What would you like to do?',
172
- options: [
173
- {
174
- value: 'edit-config',
175
- label: 'Enable / Disable MCP servers',
176
- hint: 'Toggle MCP servers on/off',
177
- },
178
- {
179
- value: 'edit-plugins',
180
- label: 'Manage plugins',
181
- hint: 'Toggle, install, uninstall, or update plugins',
182
- },
183
- {
184
- value: 'manage-marketplace',
185
- label: 'Manage marketplaces',
186
- hint: 'Add, remove, or update plugin marketplaces',
187
- },
188
- {
189
- value: 'manage-hooks',
190
- label: 'Manage hooks',
191
- hint: 'List, add, or remove event hooks',
192
- },
193
- {
194
- value: 'manage-cache',
195
- label: 'Manage plugin cache',
196
- hint: 'View, clear, or refresh plugin caches',
197
- },
198
- {
199
- value: 'backup',
200
- label: 'Backup config',
201
- hint: 'Create a timestamped backup',
202
- },
203
- {
204
- value: 'add-server',
205
- label: 'Add MCP server',
206
- hint: 'Register a new MCP server',
207
- },
208
- {
209
- value: 'restore',
210
- label: 'Restore from backup',
211
- hint: 'Restore from a previous backup',
212
- },
213
- {
214
- value: 'load-profile',
215
- label: 'Load profile',
216
- hint: 'Apply a saved profile',
217
- },
218
- {
219
- value: 'save-profile',
220
- label: 'Save profile',
221
- hint: 'Save current config as profile',
222
- },
223
- {
224
- value: 'exit',
225
- label: 'Exit',
226
- hint: 'Quit MCPick (Esc)',
227
- },
228
- ],
229
- });
230
- if (isCancel(action)) {
231
- cancel('Operation cancelled');
232
- break;
233
- }
234
- switch (action) {
235
- case 'edit-config':
236
- await edit_config();
237
- break;
238
- case 'edit-plugins':
239
- await edit_plugins();
240
- break;
241
- case 'manage-marketplace':
242
- await manage_marketplace();
243
- break;
244
- case 'manage-hooks':
245
- await manage_hooks();
246
- break;
247
- case 'manage-cache':
248
- await manage_cache();
249
- break;
250
- case 'backup':
251
- await backup_config();
252
- break;
253
- case 'add-server':
254
- await add_server();
255
- break;
256
- case 'restore':
257
- await restore_config();
258
- break;
259
- case 'load-profile':
260
- await handle_load_profile();
261
- break;
262
- case 'save-profile':
263
- await handle_save_profile();
264
- break;
265
- case 'exit':
266
- outro('Goodbye!');
267
- process.exit(0);
268
- }
269
- }
270
- catch (error) {
271
- if (error instanceof Error) {
272
- cancel(error.message);
273
- }
274
- else {
275
- cancel('An unexpected error occurred');
276
- }
277
- const should_continue = await select({
278
- message: 'Would you like to continue?',
279
- options: [
280
- { value: true, label: 'Yes, return to main menu' },
281
- { value: false, label: 'No, exit' },
282
- ],
283
- });
284
- if (isCancel(should_continue) || !should_continue) {
285
- outro('Goodbye!');
286
- break;
287
- }
288
- }
289
- }
1086
+ const args = parse_args();
1087
+ if (args.listProfiles) {
1088
+ await show_profiles();
1089
+ return;
1090
+ }
1091
+ if (args.saveProfile) {
1092
+ await create_profile(args.saveProfile);
1093
+ return;
1094
+ }
1095
+ if (args.profile) {
1096
+ await apply_profile(args.profile);
1097
+ return;
1098
+ }
1099
+ intro("MCPick - Claude Code Extension Manager (MCP servers, plugins, skills, marketplaces)");
1100
+ log.info("CLI: mcpick --help | Commands: list, add, enable, disable, plugins, marketplace, hooks, profile, backup, restore");
1101
+ while (true) try {
1102
+ const action = await select({
1103
+ message: "What would you like to do?",
1104
+ options: [
1105
+ {
1106
+ value: "edit-config",
1107
+ label: "Enable / Disable MCP servers",
1108
+ hint: "Toggle MCP servers on/off"
1109
+ },
1110
+ {
1111
+ value: "edit-plugins",
1112
+ label: "Manage plugins",
1113
+ hint: "Toggle, install, uninstall, or update plugins"
1114
+ },
1115
+ {
1116
+ value: "manage-marketplace",
1117
+ label: "Manage marketplaces",
1118
+ hint: "Add, remove, or update plugin marketplaces"
1119
+ },
1120
+ {
1121
+ value: "manage-hooks",
1122
+ label: "Manage hooks",
1123
+ hint: "List, add, or remove event hooks"
1124
+ },
1125
+ {
1126
+ value: "manage-cache",
1127
+ label: "Manage plugin cache",
1128
+ hint: "View, clear, or refresh plugin caches"
1129
+ },
1130
+ {
1131
+ value: "backup",
1132
+ label: "Backup config",
1133
+ hint: "Create a timestamped backup"
1134
+ },
1135
+ {
1136
+ value: "add-server",
1137
+ label: "Add MCP server",
1138
+ hint: "Register a new MCP server"
1139
+ },
1140
+ {
1141
+ value: "restore",
1142
+ label: "Restore from backup",
1143
+ hint: "Restore from a previous backup"
1144
+ },
1145
+ {
1146
+ value: "load-profile",
1147
+ label: "Load profile",
1148
+ hint: "Apply a saved profile"
1149
+ },
1150
+ {
1151
+ value: "save-profile",
1152
+ label: "Save profile",
1153
+ hint: "Save current config as profile"
1154
+ },
1155
+ {
1156
+ value: "exit",
1157
+ label: "Exit",
1158
+ hint: "Quit MCPick (Esc)"
1159
+ }
1160
+ ]
1161
+ });
1162
+ if (isCancel(action)) {
1163
+ cancel("Operation cancelled");
1164
+ break;
1165
+ }
1166
+ switch (action) {
1167
+ case "edit-config":
1168
+ await edit_config();
1169
+ break;
1170
+ case "edit-plugins":
1171
+ await edit_plugins();
1172
+ break;
1173
+ case "manage-marketplace":
1174
+ await manage_marketplace();
1175
+ break;
1176
+ case "manage-hooks":
1177
+ await manage_hooks();
1178
+ break;
1179
+ case "manage-cache":
1180
+ await manage_cache();
1181
+ break;
1182
+ case "backup":
1183
+ await backup_config();
1184
+ break;
1185
+ case "add-server":
1186
+ await add_server();
1187
+ break;
1188
+ case "restore":
1189
+ await restore_config();
1190
+ break;
1191
+ case "load-profile":
1192
+ await handle_load_profile();
1193
+ break;
1194
+ case "save-profile":
1195
+ await handle_save_profile();
1196
+ break;
1197
+ case "exit":
1198
+ outro("Goodbye!");
1199
+ process.exit(0);
1200
+ }
1201
+ } catch (error) {
1202
+ if (error instanceof Error) cancel(error.message);
1203
+ else cancel("An unexpected error occurred");
1204
+ const should_continue = await select({
1205
+ message: "Would you like to continue?",
1206
+ options: [{
1207
+ value: true,
1208
+ label: "Yes, return to main menu"
1209
+ }, {
1210
+ value: false,
1211
+ label: "No, exit"
1212
+ }]
1213
+ });
1214
+ if (isCancel(should_continue) || !should_continue) {
1215
+ outro("Goodbye!");
1216
+ break;
1217
+ }
1218
+ }
290
1219
  }
291
1220
  const SUBCOMMANDS = new Set([
292
- 'list',
293
- 'enable',
294
- 'disable',
295
- 'remove',
296
- 'add',
297
- 'add-json',
298
- 'get',
299
- 'reset-project-choices',
300
- 'hooks',
301
- 'backup',
302
- 'restore',
303
- 'profile',
304
- 'plugins',
305
- 'cache',
306
- 'dev',
307
- 'marketplace',
308
- 'reload',
1221
+ "list",
1222
+ "enable",
1223
+ "disable",
1224
+ "remove",
1225
+ "add",
1226
+ "add-json",
1227
+ "clone",
1228
+ "get",
1229
+ "reset-project-choices",
1230
+ "hooks",
1231
+ "backup",
1232
+ "restore",
1233
+ "profile",
1234
+ "plugins",
1235
+ "cache",
1236
+ "dev",
1237
+ "marketplace",
1238
+ "reload"
309
1239
  ]);
310
1240
  const arg = process.argv[2];
311
- if ((arg && SUBCOMMANDS.has(arg)) ||
312
- arg === '--help' ||
313
- arg === '-h') {
314
- import('./cli/index.js').then((m) => m.run());
315
- }
316
- else {
317
- main().catch((error) => {
318
- console.error('Fatal error:', error);
319
- process.exit(1);
320
- });
321
- }
1241
+ if (arg && SUBCOMMANDS.has(arg) || arg === "--help" || arg === "-h") import("./cli-DNNZjJYL.js").then((m) => m.run());
1242
+ else main().catch((error) => {
1243
+ console.error("Fatal error:", error);
1244
+ process.exit(1);
1245
+ });
1246
+ //#endregion
1247
+ export {};
1248
+
322
1249
  //# sourceMappingURL=index.js.map