mcpick 0.0.10 → 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.
@@ -2,53 +2,59 @@ import { note } from '@clack/prompts';
2
2
  import { readdir, unlink, writeFile } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
4
  import { read_claude_config } from '../core/config.js';
5
- import { ensure_directory_exists, get_backup_filename, get_backups_dir, } from '../utils/paths.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';
6
7
  const MAX_BACKUPS = 10;
7
8
  export async function backup_config() {
8
9
  try {
9
10
  const current_config = await read_claude_config();
11
+ const current_settings = await read_claude_settings();
10
12
  const backups_dir = get_backups_dir();
11
13
  await ensure_directory_exists(backups_dir);
12
- const backup_filename = get_backup_filename();
13
- const backup_path = join(backups_dir, backup_filename);
14
+ // Backup MCP servers
15
+ const mcp_filename = get_backup_filename();
16
+ const mcp_path = join(backups_dir, mcp_filename);
14
17
  const mcp_backup = {
15
18
  mcpServers: current_config.mcpServers || {},
16
19
  };
17
- const backup_content = JSON.stringify(mcp_backup, null, 2);
18
- await writeFile(backup_path, backup_content, 'utf-8');
19
- await cleanup_old_backups();
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-');
20
34
  const server_count = Object.keys(current_config.mcpServers || {}).length;
21
- note(`MCP servers backup created:\n${backup_path}\n(${server_count} servers backed up)`);
35
+ note(`MCP servers backup: ${mcp_path}\n(${server_count} servers backed up)${plugin_msg}`);
22
36
  }
23
37
  catch (error) {
24
38
  throw new Error(`Failed to create backup: ${error instanceof Error ? error.message : 'Unknown error'}`);
25
39
  }
26
40
  }
27
- async function cleanup_old_backups() {
41
+ async function cleanup_old_backups(prefix) {
28
42
  try {
29
43
  const backups_dir = get_backups_dir();
30
44
  const files = await readdir(backups_dir);
31
45
  const backup_files = files
32
- .filter((file) => file.startsWith('mcp-servers-') && file.endsWith('.json'))
33
- .map((file) => {
34
- const timestamp_match = file.match(/mcp-servers-(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})(\d{2})\.json/);
35
- if (!timestamp_match)
36
- return null;
37
- const [, year, month, day, hour, minute, second] = timestamp_match;
38
- const timestamp = new Date(parseInt(year), parseInt(month) - 1, parseInt(day), parseInt(hour), parseInt(minute), parseInt(second));
39
- return { file, timestamp };
40
- })
41
- .filter((backup) => backup !== null)
42
- .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
46
+ .filter((file) => file.startsWith(prefix) && file.endsWith('.json'))
47
+ .sort()
48
+ .reverse();
43
49
  if (backup_files.length > MAX_BACKUPS) {
44
50
  const files_to_delete = backup_files.slice(MAX_BACKUPS);
45
- for (const { file } of files_to_delete) {
51
+ for (const file of files_to_delete) {
46
52
  await unlink(join(backups_dir, file));
47
53
  }
48
54
  }
49
55
  }
50
- catch (error) {
51
- console.warn('Warning: Failed to cleanup old backups:', error);
56
+ catch {
57
+ // Cleanup is best-effort
52
58
  }
53
59
  }
54
60
  //# sourceMappingURL=backup.js.map
@@ -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
- try {
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,
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 (typeof selected === 'symbol') {
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
- const selected_set = new Set(selected);
30
- const updated_plugins = plugins.map((plugin) => ({
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
@@ -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 backups = await list_backups();
8
- if (backups.length === 0) {
9
- note('No backups found.');
10
- return;
11
- }
12
- const backup_choices = backups.map((backup) => ({
13
- value: backup.path,
14
- label: `${backup.filename} (${backup.timestamp.toLocaleString()})`,
15
- hint: format_time_ago(backup.timestamp),
16
- }));
17
- const selected_backup_path = await select({
18
- message: 'Select backup to restore:',
19
- options: backup_choices,
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 (typeof selected_backup_path === 'symbol') {
23
+ if (isCancel(restore_type))
22
24
  return;
25
+ if (restore_type === 'mcp') {
26
+ await restore_mcp();
23
27
  }
24
- const should_restore = await confirm({
25
- message: 'This will replace your current MCP servers configuration. Continue?',
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();
@@ -1,6 +1,7 @@
1
1
  import { access, readFile, readdir, writeFile, } from 'node:fs/promises';
2
2
  import { ensure_directory_exists, get_profile_path, get_profiles_dir, } from '../utils/paths.js';
3
3
  import { read_claude_config } from './config.js';
4
+ import { read_claude_settings } from './settings.js';
4
5
  import { validate_claude_config } from './validation.js';
5
6
  export async function load_profile(name) {
6
7
  const profile_path = get_profile_path(name);
@@ -8,12 +9,24 @@ export async function load_profile(name) {
8
9
  await access(profile_path);
9
10
  const content = await readFile(profile_path, 'utf-8');
10
11
  const parsed = JSON.parse(content);
11
- // Profile can be either full ClaudeConfig format or just mcpServers object
12
+ // Profile can be either full format or just mcpServers object
13
+ let config;
12
14
  if (parsed.mcpServers) {
13
- return validate_claude_config(parsed);
15
+ config = validate_claude_config(parsed);
14
16
  }
15
- // If it's just a servers object, wrap it
16
- return validate_claude_config({ mcpServers: parsed });
17
+ else if (!parsed.enabledPlugins) {
18
+ // Bare servers object (legacy)
19
+ config = validate_claude_config({ mcpServers: parsed });
20
+ }
21
+ else {
22
+ config = validate_claude_config({
23
+ mcpServers: parsed.mcpServers || {},
24
+ });
25
+ }
26
+ return {
27
+ config,
28
+ enabledPlugins: parsed.enabledPlugins,
29
+ };
17
30
  }
18
31
  catch (error) {
19
32
  if (error instanceof Error &&
@@ -37,10 +50,12 @@ export async function list_profiles() {
37
50
  const content = await readFile(path, 'utf-8');
38
51
  const parsed = JSON.parse(content);
39
52
  const servers = parsed.mcpServers || parsed;
53
+ const plugins = parsed.enabledPlugins || {};
40
54
  profiles.push({
41
55
  name: file.replace('.json', ''),
42
56
  path,
43
57
  serverCount: Object.keys(servers).length,
58
+ pluginCount: Object.keys(plugins).length,
44
59
  });
45
60
  }
46
61
  catch {
@@ -55,16 +70,25 @@ export async function list_profiles() {
55
70
  }
56
71
  export async function save_profile(name) {
57
72
  const config = await read_claude_config();
73
+ const settings = await read_claude_settings();
58
74
  const servers = config.mcpServers || {};
75
+ const plugins = settings.enabledPlugins || {};
59
76
  const server_count = Object.keys(servers).length;
60
- if (server_count === 0) {
61
- throw new Error('No MCP servers configured to save');
77
+ const plugin_count = Object.keys(plugins).length;
78
+ if (server_count === 0 && plugin_count === 0) {
79
+ throw new Error('No MCP servers or plugins configured to save');
62
80
  }
63
81
  const profiles_dir = get_profiles_dir();
64
82
  await ensure_directory_exists(profiles_dir);
83
+ const profile_data = {
84
+ mcpServers: servers,
85
+ };
86
+ if (plugin_count > 0) {
87
+ profile_data.enabledPlugins = plugins;
88
+ }
65
89
  const profile_path = get_profile_path(name);
66
- const content = JSON.stringify({ mcpServers: servers }, null, 2);
90
+ const content = JSON.stringify(profile_data, null, 2);
67
91
  await writeFile(profile_path, content, 'utf-8');
68
- return server_count;
92
+ return { serverCount: server_count, pluginCount: plugin_count };
69
93
  }
70
94
  //# sourceMappingURL=profile.js.map
@@ -56,36 +56,40 @@ export async function sync_servers_to_registry(servers) {
56
56
  });
57
57
  await write_server_registry(registry);
58
58
  }
59
- export async function list_backups() {
60
- const backups_dir = get_backups_dir();
61
- try {
62
- await access(backups_dir);
63
- const files = await readdir(backups_dir);
64
- const backup_files = files
65
- .filter((file) => file.startsWith('mcp-servers-') && file.endsWith('.json'))
66
- .map((file) => {
67
- const timestamp_match = file.match(/mcp-servers-(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})(\d{2})\.json/);
68
- if (!timestamp_match)
69
- return null;
70
- const [, year, month, day, hour, minute, second] = timestamp_match;
71
- const timestamp = new Date(parseInt(year), parseInt(month) - 1, parseInt(day), parseInt(hour), parseInt(minute), parseInt(second));
72
- return {
73
- filename: file,
74
- timestamp,
75
- path: join(backups_dir, file),
76
- };
77
- })
78
- .filter((backup) => backup !== null)
79
- .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
80
- return backup_files;
81
- }
82
- catch (error) {
83
- if (error instanceof Error &&
84
- 'code' in error &&
85
- error.code === 'ENOENT') {
86
- return [];
59
+ function parse_backups(prefix, pattern) {
60
+ return async () => {
61
+ const backups_dir = get_backups_dir();
62
+ try {
63
+ await access(backups_dir);
64
+ const files = await readdir(backups_dir);
65
+ const backup_files = files
66
+ .filter((file) => file.startsWith(prefix) && file.endsWith('.json'))
67
+ .map((file) => {
68
+ const timestamp_match = file.match(pattern);
69
+ if (!timestamp_match)
70
+ return null;
71
+ const [, year, month, day, hour, minute, second] = timestamp_match;
72
+ const timestamp = new Date(parseInt(year), parseInt(month) - 1, parseInt(day), parseInt(hour), parseInt(minute), parseInt(second));
73
+ return {
74
+ filename: file,
75
+ timestamp,
76
+ path: join(backups_dir, file),
77
+ };
78
+ })
79
+ .filter((backup) => backup !== null)
80
+ .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
81
+ return backup_files;
87
82
  }
88
- throw error;
89
- }
83
+ catch (error) {
84
+ if (error instanceof Error &&
85
+ 'code' in error &&
86
+ error.code === 'ENOENT') {
87
+ return [];
88
+ }
89
+ throw error;
90
+ }
91
+ };
90
92
  }
93
+ export const list_backups = parse_backups('mcp-servers-', /mcp-servers-(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})(\d{2})\.json/);
94
+ export const list_plugin_backups = parse_backups('plugins-', /plugins-(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})(\d{2})\.json/);
91
95
  //# sourceMappingURL=registry.js.map