@adversity/coding-tool-x 2.5.0 → 2.6.0

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.
@@ -5,12 +5,12 @@
5
5
  <link rel="icon" href="/favicon.ico">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>CC-TOOL - ClaudeCode增强工作助手</title>
8
- <script type="module" crossorigin src="/assets/index-k9b43kTe.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-Ej0MPDUI.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/vue-vendor-CEeI-Azr.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/vendors-CzcvkTIS.js">
11
- <link rel="modulepreload" crossorigin href="/assets/icons-BALJo7bE.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/icons-CNM9_Fh0.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/naive-ui-sh0u_0bf.js">
13
- <link rel="stylesheet" crossorigin href="/assets/index-CcYz-Mcz.css">
13
+ <link rel="stylesheet" crossorigin href="/assets/index-BcmuQT-z.css">
14
14
  </head>
15
15
  <body>
16
16
  <div id="app"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adversity/coding-tool-x",
3
- "version": "2.5.0",
3
+ "version": "2.6.0",
4
4
  "description": "Vibe Coding 增强工作助手 - 智能会话管理、动态渠道切换、全局搜索、实时监控",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -49,6 +49,7 @@
49
49
  "@lydell/node-pty": "^1.1.0",
50
50
  "@xterm/headless": "^6.0.0",
51
51
  "adm-zip": "^0.5.16",
52
+ "ajv": "^8.12.0",
52
53
  "cc-tool-web": "file:src/web",
53
54
  "chalk": "^4.1.2",
54
55
  "express": "^4.18.2",
@@ -58,6 +59,7 @@
58
59
  "open": "^8.4.2",
59
60
  "ora": "^5.4.1",
60
61
  "pm2": "^5.4.3",
62
+ "semver": "^7.6.0",
61
63
  "toml": "^3.0.0",
62
64
  "ws": "^8.18.3"
63
65
  },
@@ -72,4 +74,4 @@
72
74
  "url": "https://github.com/ZeaoZhang/coding-tool/issues"
73
75
  },
74
76
  "homepage": "https://github.com/ZeaoZhang/coding-tool#readme"
75
- }
77
+ }
@@ -0,0 +1,585 @@
1
+ const chalk = require('chalk');
2
+ const inquirer = require('inquirer');
3
+ const { installPlugin, uninstallPlugin, updatePlugin, updateAllPlugins } = require('../plugins/plugin-installer');
4
+ const { listPlugins, getPlugin, updatePlugin: updatePluginRegistry } = require('../plugins/registry');
5
+ const { createPluginContext } = require('../plugins/plugin-api');
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const { INSTALLED_DIR } = require('../plugins/constants');
9
+
10
+ /**
11
+ * Main plugin command handler
12
+ * @param {Array} args - Command line arguments
13
+ */
14
+ async function handlePluginCommand(args) {
15
+ const [subcommand, ...subArgs] = args;
16
+
17
+ if (!subcommand) {
18
+ showHelp();
19
+ return;
20
+ }
21
+
22
+ switch (subcommand) {
23
+ case 'install':
24
+ await handleInstall(subArgs);
25
+ break;
26
+ case 'remove':
27
+ case 'uninstall':
28
+ await handleRemove(subArgs);
29
+ break;
30
+ case 'list':
31
+ handleList(subArgs);
32
+ break;
33
+ case 'enable':
34
+ handleEnable(subArgs);
35
+ break;
36
+ case 'disable':
37
+ handleDisable(subArgs);
38
+ break;
39
+ case 'info':
40
+ handleInfo(subArgs);
41
+ break;
42
+ case 'config':
43
+ handleConfig(subArgs);
44
+ break;
45
+ case 'update':
46
+ await handleUpdate(subArgs);
47
+ break;
48
+ case 'help':
49
+ showHelp();
50
+ break;
51
+ default:
52
+ console.error(chalk.red(`\n❌ Unknown subcommand: ${subcommand}\n`));
53
+ showHelp();
54
+ process.exit(1);
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Handle plugin install command
60
+ * @param {Array} args - Subcommand arguments
61
+ */
62
+ async function handleInstall(args) {
63
+ const url = args[0];
64
+
65
+ if (!url) {
66
+ console.error(chalk.red('\n❌ Git URL is required\n'));
67
+ console.log(chalk.gray('Usage: ctx plugin install <git-url>\n'));
68
+ console.log(chalk.gray('Example: ctx plugin install https://github.com/user/ctx-plugin.git\n'));
69
+ process.exit(1);
70
+ }
71
+
72
+ console.log(chalk.cyan(`\n📦 Installing plugin from ${url}...\n`));
73
+
74
+ const result = await installPlugin(url);
75
+
76
+ if (result.success) {
77
+ console.log(chalk.green('\n✅ Plugin installed successfully!\n'));
78
+ console.log(chalk.gray('─'.repeat(60)));
79
+ console.log(chalk.cyan('Name: ') + chalk.white(result.plugin.name));
80
+ console.log(chalk.cyan('Version: ') + chalk.white(result.plugin.version));
81
+ console.log(chalk.cyan('Author: ') + chalk.white(result.plugin.author || 'N/A'));
82
+ console.log(chalk.cyan('Commands: ') + chalk.white(result.plugin.commands || 0));
83
+ console.log(chalk.cyan('Hooks: ') + chalk.white(result.plugin.hooks || 0));
84
+ console.log(chalk.gray('─'.repeat(60)));
85
+ console.log(chalk.gray(`\n💡 Run ${chalk.cyan(`ctx plugin info ${result.plugin.name}`)} for more details\n`));
86
+ } else {
87
+ console.error(chalk.red('\n❌ Installation failed:\n'));
88
+ console.error(chalk.red(result.error));
89
+ console.log();
90
+ process.exit(1);
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Handle plugin remove command
96
+ * @param {Array} args - Subcommand arguments
97
+ */
98
+ async function handleRemove(args) {
99
+ const name = args[0];
100
+
101
+ if (!name) {
102
+ console.error(chalk.red('\n❌ Plugin name is required\n'));
103
+ console.log(chalk.gray('Usage: ctx plugin remove <name>\n'));
104
+ process.exit(1);
105
+ }
106
+
107
+ // Check if plugin exists
108
+ const plugin = getPlugin(name);
109
+ if (!plugin) {
110
+ console.error(chalk.red(`\n❌ Plugin "${name}" is not installed\n`));
111
+ process.exit(1);
112
+ }
113
+
114
+ // Confirm uninstall
115
+ const { confirmed } = await inquirer.prompt([
116
+ {
117
+ type: 'confirm',
118
+ name: 'confirmed',
119
+ message: `Are you sure you want to uninstall "${name}"?`,
120
+ default: false
121
+ }
122
+ ]);
123
+
124
+ if (!confirmed) {
125
+ console.log(chalk.yellow('\n⚠️ Uninstall cancelled\n'));
126
+ return;
127
+ }
128
+
129
+ console.log(chalk.cyan(`\n🗑️ Uninstalling plugin "${name}"...\n`));
130
+
131
+ const result = uninstallPlugin(name);
132
+
133
+ if (result.success) {
134
+ console.log(chalk.green(`\n✅ ${result.message}\n`));
135
+ } else {
136
+ console.error(chalk.red('\n❌ Uninstall failed:\n'));
137
+ console.error(chalk.red(result.error));
138
+ console.log();
139
+ process.exit(1);
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Handle plugin list command
145
+ * @param {Array} args - Subcommand arguments
146
+ */
147
+ function handleList(args) {
148
+ const plugins = listPlugins();
149
+
150
+ if (plugins.length === 0) {
151
+ console.log(chalk.yellow('\n⚠️ No plugins installed\n'));
152
+ console.log(chalk.gray('💡 Install a plugin with: ') + chalk.cyan('ctx plugin install <git-url>\n'));
153
+ return;
154
+ }
155
+
156
+ console.log(chalk.cyan(`\n📦 Installed Plugins (${plugins.length})\n`));
157
+ console.log(chalk.gray('═'.repeat(80)));
158
+
159
+ // Sort by name
160
+ plugins.sort((a, b) => a.name.localeCompare(b.name));
161
+
162
+ for (const plugin of plugins) {
163
+ const status = plugin.enabled
164
+ ? chalk.green('✓ enabled ')
165
+ : chalk.gray('✗ disabled');
166
+
167
+ console.log('\n' + chalk.white.bold(plugin.name) + ' ' + chalk.gray(`v${plugin.version}`) + ' ' + status);
168
+
169
+ // Try to read manifest for description
170
+ const manifestPath = path.join(INSTALLED_DIR, plugin.name, 'plugin.json');
171
+ if (fs.existsSync(manifestPath)) {
172
+ try {
173
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
174
+ if (manifest.description) {
175
+ console.log(chalk.gray(' ' + manifest.description));
176
+ }
177
+ if (manifest.author) {
178
+ console.log(chalk.gray(` Author: ${manifest.author}`));
179
+ }
180
+ } catch (err) {
181
+ // Ignore read errors
182
+ }
183
+ }
184
+
185
+ if (plugin.source) {
186
+ console.log(chalk.gray(` Source: ${plugin.source}`));
187
+ }
188
+
189
+ console.log(chalk.gray(` Installed: ${new Date(plugin.installedAt).toLocaleDateString()}`));
190
+ }
191
+
192
+ console.log('\n' + chalk.gray('═'.repeat(80)));
193
+ console.log(chalk.gray(`\n💡 Use ${chalk.cyan('ctx plugin info <name>')} for detailed information\n`));
194
+ }
195
+
196
+ /**
197
+ * Handle plugin enable command
198
+ * @param {Array} args - Subcommand arguments
199
+ */
200
+ function handleEnable(args) {
201
+ const name = args[0];
202
+
203
+ if (!name) {
204
+ console.error(chalk.red('\n❌ Plugin name is required\n'));
205
+ console.log(chalk.gray('Usage: ctx plugin enable <name>\n'));
206
+ process.exit(1);
207
+ }
208
+
209
+ const plugin = getPlugin(name);
210
+ if (!plugin) {
211
+ console.error(chalk.red(`\n❌ Plugin "${name}" is not installed\n`));
212
+ process.exit(1);
213
+ }
214
+
215
+ if (plugin.enabled) {
216
+ console.log(chalk.yellow(`\n⚠️ Plugin "${name}" is already enabled\n`));
217
+ return;
218
+ }
219
+
220
+ try {
221
+ updatePluginRegistry(name, { enabled: true });
222
+ console.log(chalk.green(`\n✅ Plugin "${name}" has been enabled\n`));
223
+ console.log(chalk.gray('💡 Restart ctx for changes to take effect\n'));
224
+ } catch (error) {
225
+ console.error(chalk.red(`\n❌ Failed to enable plugin: ${error.message}\n`));
226
+ process.exit(1);
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Handle plugin disable command
232
+ * @param {Array} args - Subcommand arguments
233
+ */
234
+ function handleDisable(args) {
235
+ const name = args[0];
236
+
237
+ if (!name) {
238
+ console.error(chalk.red('\n❌ Plugin name is required\n'));
239
+ console.log(chalk.gray('Usage: ctx plugin disable <name>\n'));
240
+ process.exit(1);
241
+ }
242
+
243
+ const plugin = getPlugin(name);
244
+ if (!plugin) {
245
+ console.error(chalk.red(`\n❌ Plugin "${name}" is not installed\n`));
246
+ process.exit(1);
247
+ }
248
+
249
+ if (!plugin.enabled) {
250
+ console.log(chalk.yellow(`\n⚠️ Plugin "${name}" is already disabled\n`));
251
+ return;
252
+ }
253
+
254
+ try {
255
+ updatePluginRegistry(name, { enabled: false });
256
+ console.log(chalk.green(`\n✅ Plugin "${name}" has been disabled\n`));
257
+ console.log(chalk.gray('💡 Restart ctx for changes to take effect\n'));
258
+ } catch (error) {
259
+ console.error(chalk.red(`\n❌ Failed to disable plugin: ${error.message}\n`));
260
+ process.exit(1);
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Handle plugin info command
266
+ * @param {Array} args - Subcommand arguments
267
+ */
268
+ function handleInfo(args) {
269
+ const name = args[0];
270
+
271
+ if (!name) {
272
+ console.error(chalk.red('\n❌ Plugin name is required\n'));
273
+ console.log(chalk.gray('Usage: ctx plugin info <name>\n'));
274
+ process.exit(1);
275
+ }
276
+
277
+ const plugin = getPlugin(name);
278
+ if (!plugin) {
279
+ console.error(chalk.red(`\n❌ Plugin "${name}" is not installed\n`));
280
+ process.exit(1);
281
+ }
282
+
283
+ // Read manifest
284
+ const manifestPath = path.join(INSTALLED_DIR, name, 'plugin.json');
285
+ if (!fs.existsSync(manifestPath)) {
286
+ console.error(chalk.red(`\n❌ Plugin manifest not found: ${manifestPath}\n`));
287
+ process.exit(1);
288
+ }
289
+
290
+ let manifest;
291
+ try {
292
+ manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
293
+ } catch (error) {
294
+ console.error(chalk.red(`\n❌ Failed to read plugin manifest: ${error.message}\n`));
295
+ process.exit(1);
296
+ }
297
+
298
+ // Display plugin information
299
+ console.log(chalk.cyan(`\n📦 Plugin Information: ${name}\n`));
300
+ console.log(chalk.gray('═'.repeat(80)));
301
+
302
+ console.log(chalk.white('\nBasic Info:'));
303
+ console.log(chalk.cyan(' Name: ') + chalk.white(manifest.name));
304
+ console.log(chalk.cyan(' Version: ') + chalk.white(manifest.version));
305
+ console.log(chalk.cyan(' Description: ') + chalk.white(manifest.description || 'N/A'));
306
+ console.log(chalk.cyan(' Author: ') + chalk.white(manifest.author || 'N/A'));
307
+ console.log(chalk.cyan(' License: ') + chalk.white(manifest.license || 'N/A'));
308
+
309
+ console.log(chalk.white('\nStatus:'));
310
+ console.log(chalk.cyan(' Enabled: ') + (plugin.enabled ? chalk.green('Yes') : chalk.gray('No')));
311
+ console.log(chalk.cyan(' Load Order: ') + chalk.white(plugin.loadOrder || 10));
312
+ console.log(chalk.cyan(' Installed: ') + chalk.white(new Date(plugin.installedAt).toLocaleString()));
313
+
314
+ if (plugin.updatedAt) {
315
+ console.log(chalk.cyan(' Updated: ') + chalk.white(new Date(plugin.updatedAt).toLocaleString()));
316
+ }
317
+
318
+ if (plugin.source) {
319
+ console.log(chalk.cyan(' Source: ') + chalk.white(plugin.source));
320
+ }
321
+
322
+ if (manifest.minVersion) {
323
+ console.log(chalk.cyan(' Min Version: ') + chalk.white(manifest.minVersion));
324
+ }
325
+
326
+ // Commands
327
+ if (manifest.commands && manifest.commands.length > 0) {
328
+ console.log(chalk.white('\nCommands:'));
329
+ manifest.commands.forEach(cmd => {
330
+ const cmdName = typeof cmd === 'string' ? cmd : cmd.name;
331
+ const cmdDesc = typeof cmd === 'object' ? cmd.description : '';
332
+ console.log(chalk.cyan(' • ') + chalk.white(cmdName) + (cmdDesc ? chalk.gray(` - ${cmdDesc}`) : ''));
333
+ });
334
+ }
335
+
336
+ // Hooks
337
+ if (manifest.hooks && manifest.hooks.length > 0) {
338
+ console.log(chalk.white('\nHooks:'));
339
+ manifest.hooks.forEach(hook => {
340
+ console.log(chalk.cyan(' • ') + chalk.white(hook));
341
+ });
342
+ }
343
+
344
+ // Configuration
345
+ const configFile = path.join(require('../plugins/constants').CONFIG_DIR, `${name}.json`);
346
+ if (fs.existsSync(configFile)) {
347
+ try {
348
+ const config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
349
+ const keys = Object.keys(config);
350
+ if (keys.length > 0) {
351
+ console.log(chalk.white('\nConfiguration:'));
352
+ keys.forEach(key => {
353
+ const value = config[key];
354
+ const displayValue = typeof value === 'object' ? JSON.stringify(value) : value;
355
+ console.log(chalk.cyan(` ${key}: `) + chalk.white(displayValue));
356
+ });
357
+ }
358
+ } catch (err) {
359
+ // Ignore read errors
360
+ }
361
+ }
362
+
363
+ console.log('\n' + chalk.gray('═'.repeat(80)) + '\n');
364
+ }
365
+
366
+ /**
367
+ * Handle plugin config command
368
+ * @param {Array} args - Subcommand arguments
369
+ */
370
+ function handleConfig(args) {
371
+ const [name, key, ...valueParts] = args;
372
+
373
+ if (!name) {
374
+ console.error(chalk.red('\n❌ Plugin name is required\n'));
375
+ console.log(chalk.gray('Usage: ctx plugin config <name> [key] [value]\n'));
376
+ console.log(chalk.gray('Examples:\n'));
377
+ console.log(chalk.gray(' ctx plugin config my-plugin # View all config'));
378
+ console.log(chalk.gray(' ctx plugin config my-plugin apiKey # View specific key'));
379
+ console.log(chalk.gray(' ctx plugin config my-plugin apiKey xyz # Set key\n'));
380
+ process.exit(1);
381
+ }
382
+
383
+ const plugin = getPlugin(name);
384
+ if (!plugin) {
385
+ console.error(chalk.red(`\n❌ Plugin "${name}" is not installed\n`));
386
+ process.exit(1);
387
+ }
388
+
389
+ const configFile = path.join(require('../plugins/constants').CONFIG_DIR, `${name}.json`);
390
+
391
+ // Ensure config directory exists
392
+ const configDir = path.dirname(configFile);
393
+ if (!fs.existsSync(configDir)) {
394
+ fs.mkdirSync(configDir, { recursive: true });
395
+ }
396
+
397
+ // Load existing config
398
+ let config = {};
399
+ if (fs.existsSync(configFile)) {
400
+ try {
401
+ config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
402
+ } catch (error) {
403
+ console.error(chalk.red(`\n❌ Failed to read config: ${error.message}\n`));
404
+ process.exit(1);
405
+ }
406
+ }
407
+
408
+ // No key provided - view all config
409
+ if (!key) {
410
+ console.log(chalk.cyan(`\n⚙️ Configuration for "${name}"\n`));
411
+ console.log(chalk.gray('═'.repeat(80)));
412
+
413
+ const keys = Object.keys(config);
414
+ if (keys.length === 0) {
415
+ console.log(chalk.gray('\n No configuration set\n'));
416
+ } else {
417
+ console.log();
418
+ keys.forEach(k => {
419
+ const value = config[k];
420
+ const displayValue = typeof value === 'object'
421
+ ? JSON.stringify(value, null, 2).split('\n').map((line, i) => i === 0 ? line : ' ' + line).join('\n')
422
+ : value;
423
+ console.log(chalk.cyan(` ${k}: `) + chalk.white(displayValue));
424
+ });
425
+ console.log();
426
+ }
427
+
428
+ console.log(chalk.gray('═'.repeat(80)) + '\n');
429
+ return;
430
+ }
431
+
432
+ // No value provided - view specific key
433
+ if (valueParts.length === 0) {
434
+ if (config[key] === undefined) {
435
+ console.log(chalk.yellow(`\n⚠️ Key "${key}" is not set\n`));
436
+ } else {
437
+ console.log(chalk.cyan(`\n⚙️ ${key}: `) + chalk.white(JSON.stringify(config[key], null, 2)) + '\n');
438
+ }
439
+ return;
440
+ }
441
+
442
+ // Set value
443
+ const value = valueParts.join(' ');
444
+
445
+ // Try to parse as JSON if it looks like JSON
446
+ let parsedValue = value;
447
+ if (value.startsWith('{') || value.startsWith('[') || value === 'true' || value === 'false' || !isNaN(value)) {
448
+ try {
449
+ parsedValue = JSON.parse(value);
450
+ } catch (err) {
451
+ // Keep as string if not valid JSON
452
+ }
453
+ }
454
+
455
+ config[key] = parsedValue;
456
+
457
+ try {
458
+ fs.writeFileSync(configFile, JSON.stringify(config, null, 2), 'utf8');
459
+ console.log(chalk.green(`\n✅ Configuration updated: ${key} = ${JSON.stringify(parsedValue)}\n`));
460
+ } catch (error) {
461
+ console.error(chalk.red(`\n❌ Failed to save config: ${error.message}\n`));
462
+ process.exit(1);
463
+ }
464
+ }
465
+
466
+ /**
467
+ * Handle plugin update command
468
+ * @param {Array} args - Subcommand arguments
469
+ */
470
+ async function handleUpdate(args) {
471
+ const name = args[0];
472
+ const isUpdateAll = name === '--all' || name === '-a';
473
+
474
+ if (!name) {
475
+ console.error(chalk.red('\n❌ Plugin name is required\n'));
476
+ console.log(chalk.gray('Usage: ctx plugin update <name>\n'));
477
+ console.log(chalk.gray(' ctx plugin update --all\n'));
478
+ process.exit(1);
479
+ }
480
+
481
+ if (isUpdateAll) {
482
+ console.log(chalk.cyan('\n🔄 Updating all plugins...\n'));
483
+
484
+ const result = await updateAllPlugins();
485
+
486
+ console.log(chalk.gray('═'.repeat(80)));
487
+
488
+ for (const pluginResult of result.results) {
489
+ if (pluginResult.success) {
490
+ if (pluginResult.plugin && pluginResult.plugin.updated) {
491
+ console.log(chalk.green(`✓ ${pluginResult.name}: `) +
492
+ chalk.gray(`v${pluginResult.plugin.oldVersion}`) +
493
+ chalk.white(' → ') +
494
+ chalk.green(`v${pluginResult.plugin.newVersion}`));
495
+ } else {
496
+ console.log(chalk.gray(`○ ${pluginResult.name}: up to date`));
497
+ }
498
+ } else {
499
+ console.log(chalk.red(`✗ ${pluginResult.name}: ${pluginResult.error}`));
500
+ }
501
+ }
502
+
503
+ console.log(chalk.gray('═'.repeat(80)));
504
+ console.log(chalk.white('\nSummary:'));
505
+ console.log(chalk.green(` Updated: ${result.summary.updated}`));
506
+ console.log(chalk.gray(` Unchanged: ${result.summary.unchanged}`));
507
+ if (result.summary.failed > 0) {
508
+ console.log(chalk.red(` Failed: ${result.summary.failed}`));
509
+ }
510
+ console.log();
511
+
512
+ if (!result.success) {
513
+ process.exit(1);
514
+ }
515
+ } else {
516
+ // Update single plugin
517
+ const plugin = getPlugin(name);
518
+ if (!plugin) {
519
+ console.error(chalk.red(`\n❌ Plugin "${name}" is not installed\n`));
520
+ process.exit(1);
521
+ }
522
+
523
+ console.log(chalk.cyan(`\n🔄 Updating plugin "${name}"...\n`));
524
+
525
+ const result = await updatePlugin(name);
526
+
527
+ if (result.success) {
528
+ console.log(chalk.green(`\n✅ ${result.message}\n`));
529
+ if (result.plugin && result.plugin.updated) {
530
+ console.log(chalk.gray(` ${result.plugin.oldVersion} → ${result.plugin.newVersion}\n`));
531
+ }
532
+ } else {
533
+ console.error(chalk.red('\n❌ Update failed:\n'));
534
+ console.error(chalk.red(result.error));
535
+ console.log();
536
+ process.exit(1);
537
+ }
538
+ }
539
+ }
540
+
541
+ /**
542
+ * Show plugin command help
543
+ */
544
+ function showHelp() {
545
+ console.log(chalk.cyan('\n📦 Plugin Management Commands\n'));
546
+ console.log(chalk.gray('═'.repeat(80)));
547
+
548
+ console.log(chalk.white('\nUsage: ') + chalk.cyan('ctx plugin <subcommand> [options]\n'));
549
+
550
+ console.log(chalk.white('Subcommands:\n'));
551
+
552
+ const commands = [
553
+ ['install <url>', 'Install plugin from Git URL'],
554
+ ['remove <name>', 'Uninstall plugin'],
555
+ ['list', 'List installed plugins with status'],
556
+ ['enable <name>', 'Enable disabled plugin'],
557
+ ['disable <name>', 'Disable plugin without removing'],
558
+ ['info <name>', 'Show plugin manifest, config, and hooks'],
559
+ ['config <name> [key] [value]', 'View or set plugin configuration'],
560
+ ['update <name>', 'Update plugin to latest version'],
561
+ ['update --all', 'Update all installed plugins'],
562
+ ['help', 'Show this help message']
563
+ ];
564
+
565
+ commands.forEach(([cmd, desc]) => {
566
+ console.log(' ' + chalk.cyan(cmd.padEnd(30)) + chalk.gray(desc));
567
+ });
568
+
569
+ console.log('\n' + chalk.white('Examples:\n'));
570
+
571
+ console.log(chalk.gray(' ctx plugin install https://github.com/user/ctx-plugin.git'));
572
+ console.log(chalk.gray(' ctx plugin list'));
573
+ console.log(chalk.gray(' ctx plugin info my-plugin'));
574
+ console.log(chalk.gray(' ctx plugin config my-plugin apiKey abc123'));
575
+ console.log(chalk.gray(' ctx plugin update my-plugin'));
576
+ console.log(chalk.gray(' ctx plugin update --all'));
577
+ console.log(chalk.gray(' ctx plugin disable my-plugin'));
578
+ console.log(chalk.gray(' ctx plugin remove my-plugin\n'));
579
+
580
+ console.log(chalk.gray('═'.repeat(80)) + '\n');
581
+ }
582
+
583
+ module.exports = {
584
+ handlePluginCommand
585
+ };
@@ -22,17 +22,36 @@ const DEFAULT_CONFIG = {
22
22
  input: 3,
23
23
  output: 15,
24
24
  cacheCreation: 3.75,
25
- cacheRead: 0.30
25
+ cacheRead: 0.30,
26
+ models: {
27
+ 'claude-sonnet-4-20250514': { mode: 'auto' },
28
+ 'claude-haiku-3-5-20241022': {
29
+ mode: 'custom',
30
+ input: 0.8,
31
+ output: 4,
32
+ cacheCreation: 1,
33
+ cacheRead: 0.08
34
+ },
35
+ 'claude-opus-4-20250514': { mode: 'auto' }
36
+ }
26
37
  },
27
38
  codex: {
28
39
  mode: 'auto',
29
40
  input: 2.5,
30
- output: 10
41
+ output: 10,
42
+ models: {
43
+ 'gpt-5-codex': { mode: 'auto' },
44
+ 'gpt-4o-mini': { mode: 'auto' }
45
+ }
31
46
  },
32
47
  gemini: {
33
48
  mode: 'auto',
34
49
  input: 1.25,
35
- output: 5
50
+ output: 5,
51
+ models: {
52
+ 'gemini-2.5-pro': { mode: 'auto' },
53
+ 'gemini-2.5-flash': { mode: 'auto' }
54
+ }
36
55
  }
37
56
  }
38
57
  };
@@ -3,6 +3,7 @@ const fs = require('fs');
3
3
  const path = require('path');
4
4
  const os = require('os');
5
5
  const DEFAULT_CONFIG = require('./default');
6
+ const eventBus = require('../plugins/event-bus');
6
7
 
7
8
  const CONFIG_FILE = path.join(__dirname, '../../config.json');
8
9
 
@@ -49,12 +50,15 @@ function loadConfig() {
49
50
  config.currentProject = config.defaultProject;
50
51
  }
51
52
 
53
+ eventBus.emitSync('config:loaded', { config });
52
54
  return config;
53
55
  }
54
56
  } catch (error) {
55
57
  console.error('加载配置文件失败,使用默认配置');
56
58
  }
57
- return { ...DEFAULT_CONFIG, currentProject: DEFAULT_CONFIG.defaultProject };
59
+ const defaultConfig = { ...DEFAULT_CONFIG, currentProject: DEFAULT_CONFIG.defaultProject };
60
+ eventBus.emitSync('config:loaded', { config: defaultConfig });
61
+ return defaultConfig;
58
62
  }
59
63
 
60
64
  /**
@@ -63,6 +67,7 @@ function loadConfig() {
63
67
  function saveConfig(config) {
64
68
  try {
65
69
  fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
70
+ eventBus.emitSync('config:saved', { config });
66
71
  } catch (error) {
67
72
  console.error('保存配置失败:', error.message);
68
73
  }