mcpick 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +114 -4
- package/dist/cli/commands/add.js +135 -0
- package/dist/cli/commands/backup.js +83 -0
- package/dist/cli/commands/cache.js +181 -0
- package/dist/cli/commands/disable.js +33 -0
- package/dist/cli/commands/enable.js +39 -0
- package/dist/cli/commands/list.js +63 -0
- package/dist/cli/commands/plugins.js +202 -0
- package/dist/cli/commands/profile.js +134 -0
- package/dist/cli/commands/remove.js +31 -0
- package/dist/cli/commands/restore.js +105 -0
- package/dist/cli/index.js +21 -0
- package/dist/cli/output.js +21 -0
- package/dist/commands/backup.js +28 -22
- package/dist/commands/edit-plugins.js +192 -47
- package/dist/commands/manage-cache.js +155 -0
- package/dist/commands/restore.js +85 -34
- package/dist/core/plugin-cache.js +259 -0
- package/dist/core/profile.js +32 -8
- package/dist/core/registry.js +34 -30
- package/dist/index.js +81 -23
- package/dist/utils/claude-cli.js +69 -0
- package/dist/utils/paths.js +26 -2
- package/package.json +7 -6
|
@@ -1,56 +1,201 @@
|
|
|
1
|
-
import { log, multiselect, note } from '@clack/prompts';
|
|
1
|
+
import { confirm, isCancel, log, multiselect, note, select, text, } from '@clack/prompts';
|
|
2
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
|
+
}
|
|
3
146
|
export async function edit_plugins() {
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
+
],
|
|
25
177
|
});
|
|
26
|
-
if (
|
|
178
|
+
if (isCancel(action) || action === 'back')
|
|
27
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
|
+
}
|
|
28
195
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
...plugin,
|
|
32
|
-
enabled: selected_set.has(`${plugin.name}@${plugin.marketplace}`),
|
|
33
|
-
}));
|
|
34
|
-
const enabled_plugins = build_enabled_plugins(updated_plugins);
|
|
35
|
-
await write_claude_settings({ enabledPlugins: enabled_plugins });
|
|
36
|
-
const enabled_count = updated_plugins.filter((p) => p.enabled).length;
|
|
37
|
-
const disabled_count = updated_plugins.filter((p) => !p.enabled).length;
|
|
38
|
-
note(`Plugins updated!\n` +
|
|
39
|
-
`Enabled: ${enabled_count}, Disabled: ${disabled_count}`);
|
|
40
|
-
// Show what changed
|
|
41
|
-
const newly_enabled = updated_plugins.filter((p) => p.enabled &&
|
|
42
|
-
!currently_enabled.includes(`${p.name}@${p.marketplace}`));
|
|
43
|
-
const newly_disabled = updated_plugins.filter((p) => !p.enabled &&
|
|
44
|
-
currently_enabled.includes(`${p.name}@${p.marketplace}`));
|
|
45
|
-
if (newly_enabled.length > 0) {
|
|
46
|
-
log.success(`Enabled: ${newly_enabled.map((p) => p.name).join(', ')}`);
|
|
196
|
+
catch (err) {
|
|
197
|
+
log.error(err instanceof Error ? err.message : 'Unknown error');
|
|
47
198
|
}
|
|
48
|
-
if (newly_disabled.length > 0) {
|
|
49
|
-
log.warn(`Disabled: ${newly_disabled.map((p) => p.name).join(', ')}`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
catch (error) {
|
|
53
|
-
throw new Error(`Failed to edit plugins: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
54
199
|
}
|
|
55
200
|
}
|
|
56
201
|
//# sourceMappingURL=edit-plugins.js.map
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { confirm, isCancel, log, multiselect, note, select, } from '@clack/prompts';
|
|
2
|
+
import { clean_orphaned_versions, clear_plugin_caches, get_cached_plugins_info, refresh_all_marketplaces, } from '../core/plugin-cache.js';
|
|
3
|
+
function format_status_line(p) {
|
|
4
|
+
const markers = [];
|
|
5
|
+
if (p.isVersionStale) {
|
|
6
|
+
markers.push(`version: ${p.installedVersion} → ${p.latestVersion}`);
|
|
7
|
+
}
|
|
8
|
+
if (p.isShaStale) {
|
|
9
|
+
markers.push('commits behind');
|
|
10
|
+
}
|
|
11
|
+
if (p.orphanedVersions.length > 0) {
|
|
12
|
+
markers.push(`${p.orphanedVersions.length} orphaned`);
|
|
13
|
+
}
|
|
14
|
+
const status = markers.length > 0
|
|
15
|
+
? `[stale: ${markers.join(', ')}]`
|
|
16
|
+
: '[up to date]';
|
|
17
|
+
return `${p.name}@${p.marketplace} v${p.installedVersion} ${status}`;
|
|
18
|
+
}
|
|
19
|
+
async function handle_status() {
|
|
20
|
+
const plugins = await get_cached_plugins_info();
|
|
21
|
+
if (plugins.length === 0) {
|
|
22
|
+
log.info('No cached plugins found.');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const lines = plugins.map(format_status_line).join('\n');
|
|
26
|
+
note(lines, 'Plugin Cache Status');
|
|
27
|
+
}
|
|
28
|
+
async function handle_clear() {
|
|
29
|
+
const plugins = await get_cached_plugins_info();
|
|
30
|
+
if (plugins.length === 0) {
|
|
31
|
+
log.info('No cached plugins to clear.');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const selected = await multiselect({
|
|
35
|
+
message: 'Select plugins to clear cache for:',
|
|
36
|
+
options: plugins.map((p) => {
|
|
37
|
+
const stale = p.isVersionStale || p.isShaStale;
|
|
38
|
+
return {
|
|
39
|
+
value: p.key,
|
|
40
|
+
label: `${p.name}@${p.marketplace}`,
|
|
41
|
+
hint: stale
|
|
42
|
+
? `v${p.installedVersion} → ${p.latestVersion ?? 'unknown'} (stale)`
|
|
43
|
+
: `v${p.installedVersion}`,
|
|
44
|
+
};
|
|
45
|
+
}),
|
|
46
|
+
initialValues: plugins
|
|
47
|
+
.filter((p) => p.isVersionStale || p.isShaStale)
|
|
48
|
+
.map((p) => p.key),
|
|
49
|
+
});
|
|
50
|
+
if (isCancel(selected) || selected.length === 0)
|
|
51
|
+
return;
|
|
52
|
+
const should_clear = await confirm({
|
|
53
|
+
message: `Clear cache for ${selected.length} plugin(s)? This will also refresh the marketplace.`,
|
|
54
|
+
});
|
|
55
|
+
if (isCancel(should_clear) || !should_clear)
|
|
56
|
+
return;
|
|
57
|
+
const result = await clear_plugin_caches(selected);
|
|
58
|
+
for (const key of result.cleared) {
|
|
59
|
+
log.success(`Cleared: ${key}`);
|
|
60
|
+
}
|
|
61
|
+
for (const err of result.errors) {
|
|
62
|
+
log.error(`Error: ${err}`);
|
|
63
|
+
}
|
|
64
|
+
if (result.cleared.length > 0) {
|
|
65
|
+
note('Run /reload-plugins in Claude Code or restart your session to apply changes.', 'Next Steps');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function handle_clean_orphaned() {
|
|
69
|
+
const should_clean = await confirm({
|
|
70
|
+
message: 'Remove all orphaned plugin version directories?',
|
|
71
|
+
});
|
|
72
|
+
if (isCancel(should_clean) || !should_clean)
|
|
73
|
+
return;
|
|
74
|
+
const result = await clean_orphaned_versions();
|
|
75
|
+
if (result.cleaned === 0) {
|
|
76
|
+
log.info('No orphaned versions found.');
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
for (const p of result.paths) {
|
|
80
|
+
log.success(`Removed: ${p}`);
|
|
81
|
+
}
|
|
82
|
+
log.info(`Cleaned ${result.cleaned} orphaned version(s).`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function handle_refresh() {
|
|
86
|
+
const should_refresh = await confirm({
|
|
87
|
+
message: 'Refresh all marketplace clones (git pull)?',
|
|
88
|
+
});
|
|
89
|
+
if (isCancel(should_refresh) || !should_refresh)
|
|
90
|
+
return;
|
|
91
|
+
const results = await refresh_all_marketplaces();
|
|
92
|
+
if (results.size === 0) {
|
|
93
|
+
log.info('No marketplaces configured.');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
for (const [name, result] of results) {
|
|
97
|
+
if (result.success) {
|
|
98
|
+
log.success(`${name}: refreshed`);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
log.error(`${name}: ${result.error}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export async function manage_cache() {
|
|
106
|
+
while (true) {
|
|
107
|
+
const action = await select({
|
|
108
|
+
message: 'Plugin cache management:',
|
|
109
|
+
options: [
|
|
110
|
+
{
|
|
111
|
+
value: 'status',
|
|
112
|
+
label: 'View cache status',
|
|
113
|
+
hint: 'Show plugins with staleness info',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
value: 'clear',
|
|
117
|
+
label: 'Clear plugin caches',
|
|
118
|
+
hint: 'Refresh marketplace + clear selected caches',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
value: 'clean-orphaned',
|
|
122
|
+
label: 'Clean orphaned versions',
|
|
123
|
+
hint: 'Remove old version directories',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
value: 'refresh',
|
|
127
|
+
label: 'Refresh marketplaces',
|
|
128
|
+
hint: 'Git pull all marketplace clones',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
value: 'back',
|
|
132
|
+
label: 'Back',
|
|
133
|
+
hint: 'Return to main menu',
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
});
|
|
137
|
+
if (isCancel(action) || action === 'back')
|
|
138
|
+
return;
|
|
139
|
+
switch (action) {
|
|
140
|
+
case 'status':
|
|
141
|
+
await handle_status();
|
|
142
|
+
break;
|
|
143
|
+
case 'clear':
|
|
144
|
+
await handle_clear();
|
|
145
|
+
break;
|
|
146
|
+
case 'clean-orphaned':
|
|
147
|
+
await handle_clean_orphaned();
|
|
148
|
+
break;
|
|
149
|
+
case 'refresh':
|
|
150
|
+
await handle_refresh();
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=manage-cache.js.map
|
package/dist/commands/restore.js
CHANGED
|
@@ -1,50 +1,101 @@
|
|
|
1
|
-
import { confirm, note, select } from '@clack/prompts';
|
|
1
|
+
import { confirm, isCancel, log, note, select } from '@clack/prompts';
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { read_claude_config, write_claude_config, } from '../core/config.js';
|
|
4
|
-
import { list_backups } from '../core/registry.js';
|
|
4
|
+
import { list_backups, list_plugin_backups, } from '../core/registry.js';
|
|
5
|
+
import { write_claude_settings } from '../core/settings.js';
|
|
5
6
|
export async function restore_config() {
|
|
6
7
|
try {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
const restore_type = await select({
|
|
9
|
+
message: 'What would you like to restore?',
|
|
10
|
+
options: [
|
|
11
|
+
{
|
|
12
|
+
value: 'mcp',
|
|
13
|
+
label: 'MCP servers',
|
|
14
|
+
hint: 'Restore server configuration',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
value: 'plugins',
|
|
18
|
+
label: 'Plugins',
|
|
19
|
+
hint: 'Restore plugin enabled/disabled state',
|
|
20
|
+
},
|
|
21
|
+
],
|
|
20
22
|
});
|
|
21
|
-
if (
|
|
23
|
+
if (isCancel(restore_type))
|
|
22
24
|
return;
|
|
25
|
+
if (restore_type === 'mcp') {
|
|
26
|
+
await restore_mcp();
|
|
23
27
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
});
|
|
27
|
-
if (typeof should_restore === 'symbol' || !should_restore) {
|
|
28
|
-
return;
|
|
28
|
+
else {
|
|
29
|
+
await restore_plugins();
|
|
29
30
|
}
|
|
30
|
-
// Read the backup file
|
|
31
|
-
const backup_content = await readFile(selected_backup_path, 'utf-8');
|
|
32
|
-
const backup_data = JSON.parse(backup_content);
|
|
33
|
-
// Read current config and merge
|
|
34
|
-
const current_config = await read_claude_config();
|
|
35
|
-
const updated_config = {
|
|
36
|
-
...current_config,
|
|
37
|
-
mcpServers: backup_data.mcpServers || {},
|
|
38
|
-
};
|
|
39
|
-
// Write back only the updated config
|
|
40
|
-
await write_claude_config(updated_config);
|
|
41
|
-
const server_count = Object.keys(backup_data.mcpServers || {}).length;
|
|
42
|
-
note(`MCP servers configuration restored successfully!\n(${server_count} servers restored)`);
|
|
43
31
|
}
|
|
44
32
|
catch (error) {
|
|
45
33
|
throw new Error(`Failed to restore configuration: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
46
34
|
}
|
|
47
35
|
}
|
|
36
|
+
async function restore_mcp() {
|
|
37
|
+
const backups = await list_backups();
|
|
38
|
+
if (backups.length === 0) {
|
|
39
|
+
note('No MCP server backups found.');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const backup_choices = backups.map((backup) => ({
|
|
43
|
+
value: backup.path,
|
|
44
|
+
label: `${backup.filename} (${backup.timestamp.toLocaleString()})`,
|
|
45
|
+
hint: format_time_ago(backup.timestamp),
|
|
46
|
+
}));
|
|
47
|
+
const selected_backup_path = await select({
|
|
48
|
+
message: 'Select backup to restore:',
|
|
49
|
+
options: backup_choices,
|
|
50
|
+
});
|
|
51
|
+
if (isCancel(selected_backup_path))
|
|
52
|
+
return;
|
|
53
|
+
const should_restore = await confirm({
|
|
54
|
+
message: 'This will replace your current MCP servers configuration. Continue?',
|
|
55
|
+
});
|
|
56
|
+
if (isCancel(should_restore) || !should_restore)
|
|
57
|
+
return;
|
|
58
|
+
const backup_content = await readFile(selected_backup_path, 'utf-8');
|
|
59
|
+
const backup_data = JSON.parse(backup_content);
|
|
60
|
+
const current_config = await read_claude_config();
|
|
61
|
+
const updated_config = {
|
|
62
|
+
...current_config,
|
|
63
|
+
mcpServers: backup_data.mcpServers || {},
|
|
64
|
+
};
|
|
65
|
+
await write_claude_config(updated_config);
|
|
66
|
+
const server_count = Object.keys(backup_data.mcpServers || {}).length;
|
|
67
|
+
log.success(`MCP servers restored (${server_count} servers)`);
|
|
68
|
+
}
|
|
69
|
+
async function restore_plugins() {
|
|
70
|
+
const backups = await list_plugin_backups();
|
|
71
|
+
if (backups.length === 0) {
|
|
72
|
+
note('No plugin backups found.');
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const backup_choices = backups.map((backup) => ({
|
|
76
|
+
value: backup.path,
|
|
77
|
+
label: `${backup.filename} (${backup.timestamp.toLocaleString()})`,
|
|
78
|
+
hint: format_time_ago(backup.timestamp),
|
|
79
|
+
}));
|
|
80
|
+
const selected_backup_path = await select({
|
|
81
|
+
message: 'Select plugin backup to restore:',
|
|
82
|
+
options: backup_choices,
|
|
83
|
+
});
|
|
84
|
+
if (isCancel(selected_backup_path))
|
|
85
|
+
return;
|
|
86
|
+
const should_restore = await confirm({
|
|
87
|
+
message: 'This will replace your current plugin configuration. Continue?',
|
|
88
|
+
});
|
|
89
|
+
if (isCancel(should_restore) || !should_restore)
|
|
90
|
+
return;
|
|
91
|
+
const backup_content = await readFile(selected_backup_path, 'utf-8');
|
|
92
|
+
const backup_data = JSON.parse(backup_content);
|
|
93
|
+
await write_claude_settings({
|
|
94
|
+
enabledPlugins: backup_data.enabledPlugins || {},
|
|
95
|
+
});
|
|
96
|
+
const plugin_count = Object.keys(backup_data.enabledPlugins || {}).length;
|
|
97
|
+
log.success(`Plugins restored (${plugin_count} plugins)`);
|
|
98
|
+
}
|
|
48
99
|
function format_time_ago(date) {
|
|
49
100
|
const now = new Date();
|
|
50
101
|
const diff = now.getTime() - date.getTime();
|