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.
- package/.vscode/settings.json +5 -0
- package/CHANGELOG.md +15 -0
- package/dist/add-B9nVyh8T.js +113 -0
- package/dist/add-json-CXNDl3al.js +58 -0
- package/dist/atomic-write-BqEykHp9.js +26 -0
- package/dist/backup-DSDhHI5f.js +64 -0
- package/dist/cache-D6kd7qE8.js +226 -0
- package/dist/claude-cli-BeA-bmoW.js +394 -0
- package/dist/cli-DNNZjJYL.js +84 -0
- package/dist/clone-DLFLewBY.js +88 -0
- package/dist/config-DijVdEFn.js +176 -0
- package/dist/dev-DRJRNp7y.js +265 -0
- package/dist/disable-BA8tXPJN.js +39 -0
- package/dist/enable-Bdnnn_Cq.js +40 -0
- package/dist/get-BPjMXTMc.js +41 -0
- package/dist/hook-state-Di8lUsPr.js +171 -0
- package/dist/hooks-Bmn7pUZa.js +280 -0
- package/dist/index.js +1230 -303
- package/dist/list-B8YeDWt6.js +64 -0
- package/dist/marketplace-Br89Tg-Z.js +168 -0
- package/dist/output-BchYq0mR.js +15 -0
- package/dist/paths-BPISiJi4.js +124 -0
- package/dist/plugin-cache-Bby9Dxm9.js +405 -0
- package/dist/plugins-DHYJF5CP.js +212 -0
- package/dist/profile-CX97sMGp.js +120 -0
- package/dist/profile-DkY_lBEm.js +70 -0
- package/dist/redact-O35tjnRD.js +26 -0
- package/dist/registry-CfUKT7_C.js +92 -0
- package/dist/reload-CYDhkCVZ.js +31 -0
- package/dist/remove-DIPWYMpk.js +31 -0
- package/dist/reset-project-choices-DRM5KByw.js +28 -0
- package/dist/restore-DdMfUljI.js +84 -0
- package/dist/rolldown-runtime-CiIaOW0V.js +13 -0
- package/dist/settings-DEcWtzLE.js +201 -0
- package/dist/validation-xMlbgGCF.js +44 -0
- package/package.json +20 -19
- package/dist/cli/commands/add-json.js +0 -60
- package/dist/cli/commands/add.js +0 -135
- package/dist/cli/commands/backup.js +0 -83
- package/dist/cli/commands/cache.js +0 -296
- package/dist/cli/commands/dev.js +0 -161
- package/dist/cli/commands/disable.js +0 -36
- package/dist/cli/commands/enable.js +0 -39
- package/dist/cli/commands/get.js +0 -45
- package/dist/cli/commands/hooks.js +0 -314
- package/dist/cli/commands/list.js +0 -63
- package/dist/cli/commands/marketplace.js +0 -211
- package/dist/cli/commands/plugins.js +0 -265
- package/dist/cli/commands/profile.js +0 -134
- package/dist/cli/commands/reload.js +0 -36
- package/dist/cli/commands/remove.js +0 -35
- package/dist/cli/commands/reset-project-choices.js +0 -32
- package/dist/cli/commands/restore.js +0 -105
- package/dist/cli/index.js +0 -28
- package/dist/cli/output.js +0 -21
- package/dist/commands/add-server.js +0 -310
- package/dist/commands/backup.js +0 -60
- package/dist/commands/edit-config.js +0 -109
- package/dist/commands/edit-plugins.js +0 -201
- package/dist/commands/manage-cache.js +0 -155
- package/dist/commands/manage-hooks.js +0 -99
- package/dist/commands/manage-marketplace.js +0 -293
- package/dist/commands/restore.js +0 -118
- package/dist/core/config.js +0 -146
- package/dist/core/dev-override.js +0 -210
- package/dist/core/hook-state.js +0 -220
- package/dist/core/plugin-cache.js +0 -506
- package/dist/core/profile.js +0 -94
- package/dist/core/registry.js +0 -121
- package/dist/core/settings.js +0 -243
- package/dist/core/validation.js +0 -49
- package/dist/types.js +0 -2
- package/dist/utils/atomic-write.js +0 -27
- package/dist/utils/claude-cli.js +0 -483
- package/dist/utils/paths.js +0 -114
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
import { confirm, log, note, select, text } from '@clack/prompts';
|
|
2
|
-
import { add_server_to_registry } from '../core/registry.js';
|
|
3
|
-
import { validate_mcp_server } from '../core/validation.js';
|
|
4
|
-
import { add_mcp_via_cli, check_claude_cli, get_scope_options, get_scope_description, } from '../utils/claude-cli.js';
|
|
5
|
-
function format_server_details(server) {
|
|
6
|
-
const details = [`Name: ${server.name}`];
|
|
7
|
-
if ('command' in server) {
|
|
8
|
-
details.push(`Command: ${server.command} ${(server.args || []).join(' ')}`);
|
|
9
|
-
}
|
|
10
|
-
if ('url' in server) {
|
|
11
|
-
details.push(`URL: ${server.url}`);
|
|
12
|
-
}
|
|
13
|
-
details.push(`Description: ${server.description || 'None'}`);
|
|
14
|
-
if (server.type) {
|
|
15
|
-
details.push(`Transport: ${server.type}`);
|
|
16
|
-
}
|
|
17
|
-
if (server.env) {
|
|
18
|
-
details.push(`Environment: ${Object.keys(server.env).length} variables`);
|
|
19
|
-
}
|
|
20
|
-
if ('headers' in server && server.headers) {
|
|
21
|
-
details.push(`Headers: ${Object.keys(server.headers).length} headers`);
|
|
22
|
-
}
|
|
23
|
-
return details;
|
|
24
|
-
}
|
|
25
|
-
export async function add_server() {
|
|
26
|
-
try {
|
|
27
|
-
// Check if Claude CLI is available
|
|
28
|
-
const cli_available = await check_claude_cli();
|
|
29
|
-
// First, ask where to install the server (scope)
|
|
30
|
-
const scope = await select({
|
|
31
|
-
message: 'Where should this server be installed?',
|
|
32
|
-
options: get_scope_options(),
|
|
33
|
-
initialValue: 'local',
|
|
34
|
-
});
|
|
35
|
-
if (typeof scope === 'symbol')
|
|
36
|
-
return;
|
|
37
|
-
// Then ask how they want to configure the server
|
|
38
|
-
const config_method = await select({
|
|
39
|
-
message: 'How would you like to add the server?',
|
|
40
|
-
options: [
|
|
41
|
-
{
|
|
42
|
-
value: 'json',
|
|
43
|
-
label: 'Paste JSON configuration',
|
|
44
|
-
hint: 'Paste complete server config as JSON',
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
value: 'form',
|
|
48
|
-
label: 'Step-by-step form',
|
|
49
|
-
hint: 'Fill out fields one by one',
|
|
50
|
-
},
|
|
51
|
-
],
|
|
52
|
-
initialValue: 'json',
|
|
53
|
-
});
|
|
54
|
-
if (typeof config_method === 'symbol')
|
|
55
|
-
return;
|
|
56
|
-
if (config_method === 'json') {
|
|
57
|
-
return await add_server_from_json(scope, cli_available);
|
|
58
|
-
}
|
|
59
|
-
const name = await text({
|
|
60
|
-
message: 'Server name:',
|
|
61
|
-
placeholder: 'e.g., mcp-sqlite-tools',
|
|
62
|
-
validate: (value) => {
|
|
63
|
-
if (!value || value.trim().length === 0) {
|
|
64
|
-
return 'Server name is required';
|
|
65
|
-
}
|
|
66
|
-
return undefined;
|
|
67
|
-
},
|
|
68
|
-
});
|
|
69
|
-
if (typeof name === 'symbol')
|
|
70
|
-
return;
|
|
71
|
-
const command = await text({
|
|
72
|
-
message: 'Command to run:',
|
|
73
|
-
placeholder: 'e.g., uvx, npx, node',
|
|
74
|
-
validate: (value) => {
|
|
75
|
-
if (!value || value.trim().length === 0) {
|
|
76
|
-
return 'Command is required';
|
|
77
|
-
}
|
|
78
|
-
return undefined;
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
if (typeof command === 'symbol')
|
|
82
|
-
return;
|
|
83
|
-
const args_input = await text({
|
|
84
|
-
message: 'Arguments (comma-separated):',
|
|
85
|
-
placeholder: 'e.g., mcp-sqlite-tools, --port, 3000',
|
|
86
|
-
defaultValue: '',
|
|
87
|
-
});
|
|
88
|
-
if (typeof args_input === 'symbol')
|
|
89
|
-
return;
|
|
90
|
-
const args = args_input
|
|
91
|
-
.split(',')
|
|
92
|
-
.map((arg) => arg.trim())
|
|
93
|
-
.filter((arg) => arg.length > 0);
|
|
94
|
-
const description = await text({
|
|
95
|
-
message: 'Description (optional):',
|
|
96
|
-
placeholder: 'Brief description of what this server provides',
|
|
97
|
-
});
|
|
98
|
-
if (typeof description === 'symbol')
|
|
99
|
-
return;
|
|
100
|
-
// Advanced configuration
|
|
101
|
-
const configure_advanced = await confirm({
|
|
102
|
-
message: 'Configure advanced settings (env variables, transport, etc.)?',
|
|
103
|
-
initialValue: false,
|
|
104
|
-
});
|
|
105
|
-
if (typeof configure_advanced === 'symbol')
|
|
106
|
-
return;
|
|
107
|
-
let server_data = {
|
|
108
|
-
name: name.trim(),
|
|
109
|
-
type: 'stdio',
|
|
110
|
-
command: command.trim(),
|
|
111
|
-
args,
|
|
112
|
-
...(description &&
|
|
113
|
-
description.trim() && { description: description.trim() }),
|
|
114
|
-
};
|
|
115
|
-
if (configure_advanced) {
|
|
116
|
-
// Transport type
|
|
117
|
-
const transport_type = await select({
|
|
118
|
-
message: 'Transport type:',
|
|
119
|
-
options: [
|
|
120
|
-
{
|
|
121
|
-
value: 'stdio',
|
|
122
|
-
label: 'stdio (default)',
|
|
123
|
-
hint: 'Standard input/output',
|
|
124
|
-
},
|
|
125
|
-
{ value: 'sse', label: 'sse', hint: 'Server-sent events' },
|
|
126
|
-
{ value: 'http', label: 'http', hint: 'HTTP transport' },
|
|
127
|
-
],
|
|
128
|
-
initialValue: 'stdio',
|
|
129
|
-
});
|
|
130
|
-
if (typeof transport_type === 'symbol')
|
|
131
|
-
return;
|
|
132
|
-
server_data.type = transport_type;
|
|
133
|
-
// URL for non-stdio transports
|
|
134
|
-
if (transport_type === 'sse' || transport_type === 'http') {
|
|
135
|
-
// Remove stdio-specific fields
|
|
136
|
-
delete server_data.command;
|
|
137
|
-
delete server_data.args;
|
|
138
|
-
const url = await text({
|
|
139
|
-
message: 'Server URL:',
|
|
140
|
-
placeholder: 'e.g., http://localhost:3000',
|
|
141
|
-
validate: (value) => {
|
|
142
|
-
if (!value || value.trim().length === 0) {
|
|
143
|
-
return 'URL is required for non-stdio transport';
|
|
144
|
-
}
|
|
145
|
-
return undefined;
|
|
146
|
-
},
|
|
147
|
-
});
|
|
148
|
-
if (typeof url === 'symbol')
|
|
149
|
-
return;
|
|
150
|
-
server_data.url = url.trim();
|
|
151
|
-
}
|
|
152
|
-
// Environment variables
|
|
153
|
-
const env_input = await text({
|
|
154
|
-
message: 'Environment variables (KEY=value, comma-separated):',
|
|
155
|
-
placeholder: 'e.g., API_KEY=abc123, TIMEOUT=30',
|
|
156
|
-
});
|
|
157
|
-
if (typeof env_input === 'symbol')
|
|
158
|
-
return;
|
|
159
|
-
if (env_input && env_input.trim()) {
|
|
160
|
-
const env = {};
|
|
161
|
-
env_input.split(',').forEach((pair) => {
|
|
162
|
-
const [key, ...valueParts] = pair.split('=');
|
|
163
|
-
if (key && valueParts.length > 0) {
|
|
164
|
-
env[key.trim()] = valueParts.join('=').trim();
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
if (Object.keys(env).length > 0) {
|
|
168
|
-
server_data.env = env;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
// Headers for HTTP transport
|
|
172
|
-
if (transport_type === 'http') {
|
|
173
|
-
const headers_input = await text({
|
|
174
|
-
message: 'HTTP headers (KEY=value, comma-separated):',
|
|
175
|
-
placeholder: 'e.g., Authorization=Bearer token, Content-Type=application/json',
|
|
176
|
-
});
|
|
177
|
-
if (typeof headers_input === 'symbol')
|
|
178
|
-
return;
|
|
179
|
-
if (headers_input && headers_input.trim()) {
|
|
180
|
-
const headers = {};
|
|
181
|
-
headers_input.split(',').forEach((pair) => {
|
|
182
|
-
const [key, ...valueParts] = pair.split('=');
|
|
183
|
-
if (key && valueParts.length > 0) {
|
|
184
|
-
headers[key.trim()] = valueParts.join('=').trim();
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
if (Object.keys(headers).length > 0) {
|
|
188
|
-
server_data.headers = headers;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
const validated_server = validate_mcp_server(server_data);
|
|
194
|
-
const details = format_server_details(validated_server);
|
|
195
|
-
details.push(`Scope: ${get_scope_description(scope)}`);
|
|
196
|
-
note(`Server to add:\n${details.join('\n')}`);
|
|
197
|
-
const should_add = await confirm({
|
|
198
|
-
message: 'Add this server?',
|
|
199
|
-
});
|
|
200
|
-
if (typeof should_add === 'symbol' || !should_add) {
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
// Always add to registry for profile/backup management
|
|
204
|
-
await add_server_to_registry(validated_server);
|
|
205
|
-
// Install via Claude CLI if available
|
|
206
|
-
if (cli_available) {
|
|
207
|
-
const result = await add_mcp_via_cli(validated_server, scope);
|
|
208
|
-
if (result.success) {
|
|
209
|
-
note(`Server "${validated_server.name}" installed successfully!\n` +
|
|
210
|
-
`Scope: ${get_scope_description(scope)}\n` +
|
|
211
|
-
`Also added to mcpick registry for profile management.`);
|
|
212
|
-
}
|
|
213
|
-
else {
|
|
214
|
-
log.warn(`CLI installation failed: ${result.error}\n` +
|
|
215
|
-
`Server added to registry only. Use 'claude mcp add' manually.`);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
log.warn(`Claude CLI not found. Server added to registry only.\n` +
|
|
220
|
-
`Install Claude Code CLI and run 'claude mcp add' to activate.`);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
catch (error) {
|
|
224
|
-
throw new Error(`Failed to add server: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
async function add_server_from_json(scope, cli_available) {
|
|
228
|
-
const json_input = await text({
|
|
229
|
-
message: 'Paste JSON configuration:',
|
|
230
|
-
placeholder: '{ "name": "mcp-sqlite-tools", "command": "npx", "args": ["-y", "mcp-sqlite-tools"] }',
|
|
231
|
-
validate: (value) => {
|
|
232
|
-
if (!value || value.trim().length === 0) {
|
|
233
|
-
return 'JSON configuration is required';
|
|
234
|
-
}
|
|
235
|
-
let jsonString = value.trim();
|
|
236
|
-
// If it doesn't start with {, wrap it in braces
|
|
237
|
-
if (!jsonString.startsWith('{')) {
|
|
238
|
-
jsonString = `{${jsonString}}`;
|
|
239
|
-
}
|
|
240
|
-
try {
|
|
241
|
-
const parsed = JSON.parse(jsonString);
|
|
242
|
-
if (typeof parsed !== 'object' || parsed === null) {
|
|
243
|
-
return 'JSON must be an object';
|
|
244
|
-
}
|
|
245
|
-
if (!parsed.command) {
|
|
246
|
-
return 'Server configuration must include a "command" field';
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
catch (error) {
|
|
250
|
-
return 'Invalid JSON format';
|
|
251
|
-
}
|
|
252
|
-
return undefined;
|
|
253
|
-
},
|
|
254
|
-
});
|
|
255
|
-
if (typeof json_input === 'symbol')
|
|
256
|
-
return;
|
|
257
|
-
try {
|
|
258
|
-
let jsonString = json_input.trim();
|
|
259
|
-
// If it doesn't start with {, wrap it in braces
|
|
260
|
-
if (!jsonString.startsWith('{')) {
|
|
261
|
-
jsonString = `{${jsonString}}`;
|
|
262
|
-
}
|
|
263
|
-
const parsed = JSON.parse(jsonString);
|
|
264
|
-
const server_data = parsed;
|
|
265
|
-
// Normalize the data to match schema expectations
|
|
266
|
-
if (!server_data.type && server_data.command) {
|
|
267
|
-
server_data.type = 'stdio';
|
|
268
|
-
}
|
|
269
|
-
if (server_data.type !== 'stdio') {
|
|
270
|
-
delete server_data.command;
|
|
271
|
-
delete server_data.args;
|
|
272
|
-
}
|
|
273
|
-
if (server_data.command && !server_data.args) {
|
|
274
|
-
server_data.args = [];
|
|
275
|
-
}
|
|
276
|
-
const validated_server = validate_mcp_server(server_data);
|
|
277
|
-
const details = format_server_details(validated_server);
|
|
278
|
-
details.push(`Scope: ${get_scope_description(scope)}`);
|
|
279
|
-
note(`Server to add:\n${details.join('\n')}`);
|
|
280
|
-
const should_add = await confirm({
|
|
281
|
-
message: 'Add this server?',
|
|
282
|
-
});
|
|
283
|
-
if (typeof should_add === 'symbol' || !should_add) {
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
// Always add to registry for profile/backup management
|
|
287
|
-
await add_server_to_registry(validated_server);
|
|
288
|
-
// Install via Claude CLI if available
|
|
289
|
-
if (cli_available) {
|
|
290
|
-
const result = await add_mcp_via_cli(validated_server, scope);
|
|
291
|
-
if (result.success) {
|
|
292
|
-
note(`Server "${validated_server.name}" installed successfully!\n` +
|
|
293
|
-
`Scope: ${get_scope_description(scope)}\n` +
|
|
294
|
-
`Also added to mcpick registry for profile management.`);
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
log.warn(`CLI installation failed: ${result.error}\n` +
|
|
298
|
-
`Server added to registry only. Use 'claude mcp add' manually.`);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
else {
|
|
302
|
-
log.warn(`Claude CLI not found. Server added to registry only.\n` +
|
|
303
|
-
`Install Claude Code CLI and run 'claude mcp add' to activate.`);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
catch (error) {
|
|
307
|
-
throw new Error(`Failed to parse or validate JSON: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
//# sourceMappingURL=add-server.js.map
|
package/dist/commands/backup.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { note } from '@clack/prompts';
|
|
2
|
-
import { readdir, unlink, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { read_claude_config } from '../core/config.js';
|
|
5
|
-
import { read_claude_settings } from '../core/settings.js';
|
|
6
|
-
import { ensure_directory_exists, get_backup_filename, get_backups_dir, get_plugin_backup_filename, } from '../utils/paths.js';
|
|
7
|
-
const MAX_BACKUPS = 10;
|
|
8
|
-
export async function backup_config() {
|
|
9
|
-
try {
|
|
10
|
-
const current_config = await read_claude_config();
|
|
11
|
-
const current_settings = await read_claude_settings();
|
|
12
|
-
const backups_dir = get_backups_dir();
|
|
13
|
-
await ensure_directory_exists(backups_dir);
|
|
14
|
-
// Backup MCP servers
|
|
15
|
-
const mcp_filename = get_backup_filename();
|
|
16
|
-
const mcp_path = join(backups_dir, mcp_filename);
|
|
17
|
-
const mcp_backup = {
|
|
18
|
-
mcpServers: current_config.mcpServers || {},
|
|
19
|
-
};
|
|
20
|
-
await writeFile(mcp_path, JSON.stringify(mcp_backup, null, 2), 'utf-8');
|
|
21
|
-
// Backup plugins
|
|
22
|
-
const plugins = current_settings.enabledPlugins || {};
|
|
23
|
-
const plugin_count = Object.keys(plugins).length;
|
|
24
|
-
let plugin_msg = '';
|
|
25
|
-
if (plugin_count > 0) {
|
|
26
|
-
const plugin_filename = get_plugin_backup_filename();
|
|
27
|
-
const plugin_path = join(backups_dir, plugin_filename);
|
|
28
|
-
const plugin_backup = { enabledPlugins: plugins };
|
|
29
|
-
await writeFile(plugin_path, JSON.stringify(plugin_backup, null, 2), 'utf-8');
|
|
30
|
-
plugin_msg = `\nPlugins backup: ${plugin_path}\n(${plugin_count} plugins backed up)`;
|
|
31
|
-
}
|
|
32
|
-
await cleanup_old_backups('mcp-servers-');
|
|
33
|
-
await cleanup_old_backups('plugins-');
|
|
34
|
-
const server_count = Object.keys(current_config.mcpServers || {}).length;
|
|
35
|
-
note(`MCP servers backup: ${mcp_path}\n(${server_count} servers backed up)${plugin_msg}`);
|
|
36
|
-
}
|
|
37
|
-
catch (error) {
|
|
38
|
-
throw new Error(`Failed to create backup: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
async function cleanup_old_backups(prefix) {
|
|
42
|
-
try {
|
|
43
|
-
const backups_dir = get_backups_dir();
|
|
44
|
-
const files = await readdir(backups_dir);
|
|
45
|
-
const backup_files = files
|
|
46
|
-
.filter((file) => file.startsWith(prefix) && file.endsWith('.json'))
|
|
47
|
-
.sort()
|
|
48
|
-
.reverse();
|
|
49
|
-
if (backup_files.length > MAX_BACKUPS) {
|
|
50
|
-
const files_to_delete = backup_files.slice(MAX_BACKUPS);
|
|
51
|
-
for (const file of files_to_delete) {
|
|
52
|
-
await unlink(join(backups_dir, file));
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
// Cleanup is best-effort
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
//# sourceMappingURL=backup.js.map
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { log, multiselect, note, select } from '@clack/prompts';
|
|
2
|
-
import { create_config_from_servers, get_enabled_servers, get_enabled_servers_for_scope, read_claude_config, write_claude_config, } from '../core/config.js';
|
|
3
|
-
import { get_all_available_servers, sync_servers_to_registry, } from '../core/registry.js';
|
|
4
|
-
import { add_mcp_via_cli, check_claude_cli, get_scope_description, get_scope_options, remove_mcp_via_cli, } from '../utils/claude-cli.js';
|
|
5
|
-
export async function edit_config() {
|
|
6
|
-
try {
|
|
7
|
-
// Check if Claude CLI is available
|
|
8
|
-
const cli_available = await check_claude_cli();
|
|
9
|
-
// Ask which scope to edit
|
|
10
|
-
const scope = await select({
|
|
11
|
-
message: 'Which configuration do you want to edit?',
|
|
12
|
-
options: get_scope_options(),
|
|
13
|
-
initialValue: 'local',
|
|
14
|
-
});
|
|
15
|
-
if (typeof scope === 'symbol')
|
|
16
|
-
return;
|
|
17
|
-
const current_config = await read_claude_config();
|
|
18
|
-
// If registry is empty but .claude.json has servers, populate registry from config
|
|
19
|
-
let all_servers = await get_all_available_servers();
|
|
20
|
-
if (all_servers.length === 0 && current_config.mcpServers) {
|
|
21
|
-
const current_servers = get_enabled_servers(current_config);
|
|
22
|
-
if (current_servers.length > 0) {
|
|
23
|
-
await sync_servers_to_registry(current_servers);
|
|
24
|
-
all_servers = current_servers;
|
|
25
|
-
note(`Imported ${current_servers.length} servers from your .claude.json file into registry.`);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
if (all_servers.length === 0) {
|
|
29
|
-
note('No MCP servers found in .claude.json or registry. Add servers first.');
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
// Get currently enabled servers for the selected scope
|
|
33
|
-
const currently_enabled = await get_enabled_servers_for_scope(scope);
|
|
34
|
-
const server_choices = all_servers.map((server) => ({
|
|
35
|
-
value: server.name,
|
|
36
|
-
label: server.name,
|
|
37
|
-
hint: server.description || '',
|
|
38
|
-
}));
|
|
39
|
-
const selected_server_names = await multiselect({
|
|
40
|
-
message: `Select MCP servers for ${get_scope_description(scope)}:`,
|
|
41
|
-
options: server_choices,
|
|
42
|
-
initialValues: currently_enabled,
|
|
43
|
-
required: false,
|
|
44
|
-
});
|
|
45
|
-
if (typeof selected_server_names === 'symbol') {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
const selected_servers = all_servers.filter((server) => selected_server_names.includes(server.name));
|
|
49
|
-
// Determine which servers to add and remove
|
|
50
|
-
const servers_to_add = selected_server_names.filter((name) => !currently_enabled.includes(name));
|
|
51
|
-
const servers_to_remove = currently_enabled.filter((name) => !selected_server_names.includes(name));
|
|
52
|
-
// If CLI is available, use it for add/remove operations
|
|
53
|
-
if (cli_available && (scope === 'local' || scope === 'project')) {
|
|
54
|
-
let success_count = 0;
|
|
55
|
-
let error_count = 0;
|
|
56
|
-
// Add new servers
|
|
57
|
-
for (const name of servers_to_add) {
|
|
58
|
-
const server = all_servers.find((s) => s.name === name);
|
|
59
|
-
if (server) {
|
|
60
|
-
const result = await add_mcp_via_cli(server, scope);
|
|
61
|
-
if (result.success) {
|
|
62
|
-
success_count++;
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
error_count++;
|
|
66
|
-
log.warn(`Failed to add ${name}: ${result.error}`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
// Remove servers
|
|
71
|
-
for (const name of servers_to_remove) {
|
|
72
|
-
const result = await remove_mcp_via_cli(name);
|
|
73
|
-
if (result.success) {
|
|
74
|
-
success_count++;
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
error_count++;
|
|
78
|
-
log.warn(`Failed to remove ${name}: ${result.error}`);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
await sync_servers_to_registry(selected_servers);
|
|
82
|
-
if (error_count > 0) {
|
|
83
|
-
note(`Configuration updated with ${error_count} errors.\n` +
|
|
84
|
-
`Scope: ${get_scope_description(scope)}\n` +
|
|
85
|
-
`Added: ${servers_to_add.length}, Removed: ${servers_to_remove.length}`);
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
note(`Configuration updated!\n` +
|
|
89
|
-
`Scope: ${get_scope_description(scope)}\n` +
|
|
90
|
-
`Enabled servers: ${selected_servers.length}`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
// Fallback to direct file writing (user scope or no CLI)
|
|
95
|
-
const new_config = create_config_from_servers(selected_servers);
|
|
96
|
-
await write_claude_config(new_config);
|
|
97
|
-
await sync_servers_to_registry(selected_servers);
|
|
98
|
-
if (!cli_available && scope !== 'user') {
|
|
99
|
-
log.warn(`Claude CLI not available. Changes written to ~/.claude.json (user scope) instead of ${scope} scope.`);
|
|
100
|
-
}
|
|
101
|
-
note(`Configuration updated!\n` +
|
|
102
|
-
`Enabled servers: ${selected_servers.length}`);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
throw new Error(`Failed to edit configuration: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
//# sourceMappingURL=edit-config.js.map
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import { confirm, isCancel, log, multiselect, note, select, text, } from '@clack/prompts';
|
|
2
|
-
import { build_enabled_plugins, get_all_plugins, read_claude_settings, write_claude_settings, } from '../core/settings.js';
|
|
3
|
-
import { install_plugin_via_cli, uninstall_plugin_via_cli, update_plugin_via_cli, } from '../utils/claude-cli.js';
|
|
4
|
-
async function handle_toggle() {
|
|
5
|
-
const settings = await read_claude_settings();
|
|
6
|
-
const plugins = get_all_plugins(settings);
|
|
7
|
-
if (plugins.length === 0) {
|
|
8
|
-
note('No plugins found in ~/.claude/settings.json.\n' +
|
|
9
|
-
'Install plugins via Claude Code: /plugin');
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
const plugin_choices = plugins.map((plugin) => ({
|
|
13
|
-
value: `${plugin.name}@${plugin.marketplace}`,
|
|
14
|
-
label: plugin.name,
|
|
15
|
-
hint: plugin.marketplace,
|
|
16
|
-
}));
|
|
17
|
-
const currently_enabled = plugins
|
|
18
|
-
.filter((p) => p.enabled)
|
|
19
|
-
.map((p) => `${p.name}@${p.marketplace}`);
|
|
20
|
-
const selected = await multiselect({
|
|
21
|
-
message: 'Select plugins to enable:',
|
|
22
|
-
options: plugin_choices,
|
|
23
|
-
initialValues: currently_enabled,
|
|
24
|
-
required: false,
|
|
25
|
-
});
|
|
26
|
-
if (typeof selected === 'symbol')
|
|
27
|
-
return;
|
|
28
|
-
const selected_set = new Set(selected);
|
|
29
|
-
const updated_plugins = plugins.map((plugin) => ({
|
|
30
|
-
...plugin,
|
|
31
|
-
enabled: selected_set.has(`${plugin.name}@${plugin.marketplace}`),
|
|
32
|
-
}));
|
|
33
|
-
const enabled_plugins = build_enabled_plugins(updated_plugins);
|
|
34
|
-
await write_claude_settings({ enabledPlugins: enabled_plugins });
|
|
35
|
-
const enabled_count = updated_plugins.filter((p) => p.enabled).length;
|
|
36
|
-
const disabled_count = updated_plugins.filter((p) => !p.enabled).length;
|
|
37
|
-
note(`Plugins updated!\n` +
|
|
38
|
-
`Enabled: ${enabled_count}, Disabled: ${disabled_count}`);
|
|
39
|
-
const newly_enabled = updated_plugins.filter((p) => p.enabled &&
|
|
40
|
-
!currently_enabled.includes(`${p.name}@${p.marketplace}`));
|
|
41
|
-
const newly_disabled = updated_plugins.filter((p) => !p.enabled &&
|
|
42
|
-
currently_enabled.includes(`${p.name}@${p.marketplace}`));
|
|
43
|
-
if (newly_enabled.length > 0) {
|
|
44
|
-
log.success(`Enabled: ${newly_enabled.map((p) => p.name).join(', ')}`);
|
|
45
|
-
}
|
|
46
|
-
if (newly_disabled.length > 0) {
|
|
47
|
-
log.warn(`Disabled: ${newly_disabled.map((p) => p.name).join(', ')}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
async function handle_install() {
|
|
51
|
-
const key = await text({
|
|
52
|
-
message: 'Plugin to install (name@marketplace):',
|
|
53
|
-
placeholder: 'e.g. commit-commands@claude-plugins-official',
|
|
54
|
-
validate: (value) => {
|
|
55
|
-
if (!value || value.trim().length === 0) {
|
|
56
|
-
return 'Plugin key is required';
|
|
57
|
-
}
|
|
58
|
-
if (!value.includes('@')) {
|
|
59
|
-
return 'Format: plugin-name@marketplace-name';
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
if (isCancel(key))
|
|
64
|
-
return;
|
|
65
|
-
const scope = await select({
|
|
66
|
-
message: 'Installation scope:',
|
|
67
|
-
options: [
|
|
68
|
-
{ value: 'user', label: 'User', hint: 'Global (default)' },
|
|
69
|
-
{
|
|
70
|
-
value: 'project',
|
|
71
|
-
label: 'Project',
|
|
72
|
-
hint: 'Shared with team',
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
value: 'local',
|
|
76
|
-
label: 'Local',
|
|
77
|
-
hint: 'This project only (gitignored)',
|
|
78
|
-
},
|
|
79
|
-
],
|
|
80
|
-
});
|
|
81
|
-
if (isCancel(scope))
|
|
82
|
-
return;
|
|
83
|
-
const result = await install_plugin_via_cli(key, scope);
|
|
84
|
-
if (result.success) {
|
|
85
|
-
log.success(`Installed '${key}' (scope: ${scope})`);
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
log.error(result.error ?? 'Unknown error');
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
async function handle_uninstall() {
|
|
92
|
-
const settings = await read_claude_settings();
|
|
93
|
-
const plugins = get_all_plugins(settings);
|
|
94
|
-
if (plugins.length === 0) {
|
|
95
|
-
log.info('No plugins installed.');
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
const selected = await select({
|
|
99
|
-
message: 'Select plugin to uninstall:',
|
|
100
|
-
options: plugins.map((p) => ({
|
|
101
|
-
value: `${p.name}@${p.marketplace}`,
|
|
102
|
-
label: p.name,
|
|
103
|
-
hint: p.marketplace,
|
|
104
|
-
})),
|
|
105
|
-
});
|
|
106
|
-
if (isCancel(selected))
|
|
107
|
-
return;
|
|
108
|
-
const should_uninstall = await confirm({
|
|
109
|
-
message: `Uninstall '${selected}'?`,
|
|
110
|
-
});
|
|
111
|
-
if (isCancel(should_uninstall) || !should_uninstall)
|
|
112
|
-
return;
|
|
113
|
-
const result = await uninstall_plugin_via_cli(selected);
|
|
114
|
-
if (result.success) {
|
|
115
|
-
log.success(`Uninstalled '${selected}'`);
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
log.error(result.error ?? 'Unknown error');
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
async function handle_update() {
|
|
122
|
-
const settings = await read_claude_settings();
|
|
123
|
-
const plugins = get_all_plugins(settings);
|
|
124
|
-
if (plugins.length === 0) {
|
|
125
|
-
log.info('No plugins installed.');
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
const selected = await select({
|
|
129
|
-
message: 'Select plugin to update:',
|
|
130
|
-
options: plugins.map((p) => ({
|
|
131
|
-
value: `${p.name}@${p.marketplace}`,
|
|
132
|
-
label: p.name,
|
|
133
|
-
hint: p.marketplace,
|
|
134
|
-
})),
|
|
135
|
-
});
|
|
136
|
-
if (isCancel(selected))
|
|
137
|
-
return;
|
|
138
|
-
const result = await update_plugin_via_cli(selected);
|
|
139
|
-
if (result.success) {
|
|
140
|
-
log.success(`Updated '${selected}'`);
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
log.error(result.error ?? 'Unknown error');
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
export async function edit_plugins() {
|
|
147
|
-
while (true) {
|
|
148
|
-
const action = await select({
|
|
149
|
-
message: 'Plugin management:',
|
|
150
|
-
options: [
|
|
151
|
-
{
|
|
152
|
-
value: 'toggle',
|
|
153
|
-
label: 'Enable / Disable plugins',
|
|
154
|
-
hint: 'Toggle plugins on/off',
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
value: 'install',
|
|
158
|
-
label: 'Install plugin',
|
|
159
|
-
hint: 'Install from a marketplace',
|
|
160
|
-
},
|
|
161
|
-
{
|
|
162
|
-
value: 'uninstall',
|
|
163
|
-
label: 'Uninstall plugin',
|
|
164
|
-
hint: 'Remove a plugin entirely',
|
|
165
|
-
},
|
|
166
|
-
{
|
|
167
|
-
value: 'update',
|
|
168
|
-
label: 'Update plugin',
|
|
169
|
-
hint: 'Update to latest version',
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
value: 'back',
|
|
173
|
-
label: 'Back',
|
|
174
|
-
hint: 'Return to main menu',
|
|
175
|
-
},
|
|
176
|
-
],
|
|
177
|
-
});
|
|
178
|
-
if (isCancel(action) || action === 'back')
|
|
179
|
-
return;
|
|
180
|
-
try {
|
|
181
|
-
switch (action) {
|
|
182
|
-
case 'toggle':
|
|
183
|
-
await handle_toggle();
|
|
184
|
-
break;
|
|
185
|
-
case 'install':
|
|
186
|
-
await handle_install();
|
|
187
|
-
break;
|
|
188
|
-
case 'uninstall':
|
|
189
|
-
await handle_uninstall();
|
|
190
|
-
break;
|
|
191
|
-
case 'update':
|
|
192
|
-
await handle_update();
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
catch (err) {
|
|
197
|
-
log.error(err instanceof Error ? err.message : 'Unknown error');
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
//# sourceMappingURL=edit-plugins.js.map
|