mr-sliy 1.0.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.
Files changed (50) hide show
  1. package/.env.example +145 -0
  2. package/database/schema.sql +187 -0
  3. package/package.json +74 -0
  4. package/scripts/download-tree-sitter.js +171 -0
  5. package/scripts/postinstall.js +134 -0
  6. package/src/agent/agent.js +563 -0
  7. package/src/agent.js +87 -0
  8. package/src/cli/index.js +1643 -0
  9. package/src/config/index.js +232 -0
  10. package/src/engine/dualModeEngine.js +486 -0
  11. package/src/index.js +165 -0
  12. package/src/middlewares/errorHandler.js +166 -0
  13. package/src/middlewares/index.js +23 -0
  14. package/src/routes/aiRoutes.js +117 -0
  15. package/src/routes/configRoutes.js +31 -0
  16. package/src/routes/index.js +75 -0
  17. package/src/routes/issueRoutes.js +195 -0
  18. package/src/routes/projectRoutes.js +46 -0
  19. package/src/routes/reportRoutes.js +40 -0
  20. package/src/routes/scanRoutes.js +245 -0
  21. package/src/routes/userRoutes.js +47 -0
  22. package/src/services/ast/parser.js +503 -0
  23. package/src/services/detection/detector.js +934 -0
  24. package/src/services/llm/providers.js +1107 -0
  25. package/src/services/rag/agent.js +375 -0
  26. package/src/services/vector/knowledgeBase.js +863 -0
  27. package/src/skills/Skill.js +38 -0
  28. package/src/skills/code-analysis/index.js +272 -0
  29. package/src/skills/code-detection/index.js +166 -0
  30. package/src/skills/code-detection/rules/console-log.js +45 -0
  31. package/src/skills/code-detection/rules/deep-nesting.js +76 -0
  32. package/src/skills/code-detection/rules/duplicate-code.js +57 -0
  33. package/src/skills/code-detection/rules/high-complexity.js +109 -0
  34. package/src/skills/code-detection/rules/index.js +59 -0
  35. package/src/skills/code-detection/rules/long-functions.js +54 -0
  36. package/src/skills/code-detection/rules/magic-numbers.js +48 -0
  37. package/src/skills/code-detection/rules/missing-comment.js +64 -0
  38. package/src/skills/code-detection/rules/null-check.js +71 -0
  39. package/src/skills/code-detection/rules/unnecessary-else.js +46 -0
  40. package/src/skills/code-detection/rules/unused-functions.js +57 -0
  41. package/src/skills/code-detection/rules/unused-imports.js +57 -0
  42. package/src/skills/code-detection/rules/unused-variables.js +54 -0
  43. package/src/skills/code-optimization/index.js +319 -0
  44. package/src/skills/index.js +152 -0
  45. package/src/utils/crypto.js +212 -0
  46. package/src/utils/database.js +125 -0
  47. package/src/utils/helpers.js +226 -0
  48. package/src/utils/logger.js +202 -0
  49. package/src/utils/mysql.js +198 -0
  50. package/src/utils/response.js +124 -0
@@ -0,0 +1,1643 @@
1
+ /**
2
+ * Code Optimizer Agent CLI 交互入口
3
+ * 交互式菜单面板 + 命令行智能体
4
+ * 支持:上下键选择、Enter确认、模糊搜索、美化界面
5
+ */
6
+
7
+ const readline = require('readline');
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const { agent } = require('../agent/agent');
11
+ const { logger } = require('../utils/logger');
12
+
13
+ const colors = {
14
+ reset: '\x1b[0m',
15
+ bright: '\x1b[1m',
16
+ dim: '\x1b[2m',
17
+ black: '\x1b[30m',
18
+ red: '\x1b[31m',
19
+ green: '\x1b[32m',
20
+ yellow: '\x1b[33m',
21
+ blue: '\x1b[34m',
22
+ magenta: '\x1b[35m',
23
+ cyan: '\x1b[36m',
24
+ white: '\x1b[37m',
25
+ gray: '\x1b[90m',
26
+ bgGreen: '\x1b[42m',
27
+ bgYellow: '\x1b[43m'
28
+ };
29
+
30
+ function c(text, color) {
31
+ return (colors[color] || colors.white) + text + colors.reset;
32
+ }
33
+
34
+ const MENU_ITEMS = [
35
+ { key: 'analyze', command: '/analyze', label: '/analyze', desc: '分析单个文件', shortcut: 'a', keywords: 'analyze file analysis 分析' },
36
+ { key: 'scan', command: '/scan', label: '/scan', desc: '扫描项目目录', shortcut: 's', keywords: 'scan project directory 扫描' },
37
+ { key: 'optimize', command: '/optimize', label: '/optimize', desc: '交互式代码优化', shortcut: 'o', keywords: 'optimize code improvement optimization 优化' },
38
+ { key: 'provider', command: '/provider', label: '/provider', desc: '大模型提供商管理', shortcut: 'p', keywords: 'provider llm model api 提供商 模型' },
39
+ { key: 'knowledge', command: '/knowledge', label: '/knowledge', desc: '知识库管理', shortcut: 'k', keywords: 'knowledge kb database rag 知识库' },
40
+ { key: 'mode', command: '/mode', label: '/mode', desc: '切换工作模式', shortcut: 'm', keywords: 'mode online offline auto 模式 离线 在线' },
41
+ { key: 'status', command: '/status', label: '/status', desc: '查看系统状态', shortcut: 'i', keywords: 'status info state stat 状态 信息' },
42
+ { key: 'help', command: '/help', label: '/help', desc: '帮助文档', shortcut: 'h', keywords: 'help manual usage guide 帮助 说明' },
43
+ { key: 'clear', command: '/clear', label: '/clear', desc: '清空屏幕', shortcut: 'c', keywords: 'clear cls screen clean 清空 清理' },
44
+ { key: 'exit', command: '/exit', label: '/exit', desc: '退出程序', shortcut: 'e', keywords: 'exit quit bye 离开 退出' }
45
+ ];
46
+
47
+ const inputState = {
48
+ mode: 'idle',
49
+ selectedIndex: 0,
50
+ filter: '',
51
+ inputBuffer: '',
52
+ inputPrompt: '',
53
+ inputResolve: null,
54
+ inputSubmitOnEnter: true,
55
+ showCursor: true
56
+ };
57
+
58
+ function initInput() {
59
+ if (process.stdin.isTTY) {
60
+ readline.emitKeypressEvents(process.stdin);
61
+ process.stdin.setRawMode(true);
62
+ }
63
+ process.stdin.on('keypress', handleGlobalKeypress);
64
+ }
65
+
66
+ function handleGlobalKeypress(chunk, key) {
67
+ if (inputState.mode === 'menu') {
68
+ handleMenuKeypress(key);
69
+ } else if (inputState.mode === 'search') {
70
+ handleSearchKeypress(key);
71
+ } else if (inputState.mode === 'input') {
72
+ handleInputKeypress(key);
73
+ } else {
74
+ return;
75
+ }
76
+ }
77
+
78
+ function getFilteredMenu() {
79
+ if (!inputState.filter) return MENU_ITEMS;
80
+ const f = inputState.filter.toLowerCase();
81
+ return MENU_ITEMS.filter(item =>
82
+ item.key.toLowerCase().includes(f) ||
83
+ item.label.toLowerCase().includes(f) ||
84
+ item.shortcut.toLowerCase().includes(f) ||
85
+ item.desc.toLowerCase().includes(f) ||
86
+ (item.keywords && item.keywords.toLowerCase().includes(f))
87
+ );
88
+ }
89
+
90
+ function handleMenuKeypress(key) {
91
+ const filtered = getFilteredMenu();
92
+
93
+ if (key.name === 'up' || (key.name === 'p' && key.ctrl)) {
94
+ if (filtered.length > 0) {
95
+ inputState.selectedIndex = Math.max(0, inputState.selectedIndex - 1);
96
+ reprintMenu();
97
+ printChatPrompt();
98
+ }
99
+ } else if (key.name === 'down' || (key.name === 'n' && key.ctrl)) {
100
+ if (filtered.length > 0) {
101
+ inputState.selectedIndex = Math.min(filtered.length - 1, inputState.selectedIndex + 1);
102
+ reprintMenu();
103
+ printChatPrompt();
104
+ }
105
+ } else if (key.name === 'return' || key.name === 'enter') {
106
+ if (filtered.length > 0 && filtered[inputState.selectedIndex]) {
107
+ const choice = filtered[inputState.selectedIndex].key;
108
+ inputState.mode = 'idle';
109
+ if (inputState.inputResolve) {
110
+ inputState.inputResolve(choice);
111
+ inputState.inputResolve = null;
112
+ }
113
+ }
114
+ } else if (key.name === '/') {
115
+ inputState.mode = 'search';
116
+ inputState.filter = '';
117
+ inputState.selectedIndex = 0;
118
+ reprintMenu();
119
+ printChatPrompt();
120
+ process.stdout.write('\n 🔎 搜索: ');
121
+ } else if (key.name === 'escape') {
122
+ if (inputState.filter) {
123
+ inputState.filter = '';
124
+ inputState.selectedIndex = 0;
125
+ reprintMenu();
126
+ printChatPrompt();
127
+ }
128
+ } else if (key.name === 'backspace') {
129
+ } else if (key.name === 'q') {
130
+ inputState.mode = 'idle';
131
+ if (inputState.inputResolve) {
132
+ inputState.inputResolve('__BACK__');
133
+ inputState.inputResolve = null;
134
+ }
135
+ } else if (key.name === 'c' && key.ctrl) {
136
+ process.exit(0);
137
+ } else if (!key.ctrl && !key.meta && key.name && key.name.length === 1) {
138
+ const shortcutItem = filtered.find(item => item.shortcut === key.name);
139
+ if (shortcutItem) {
140
+ inputState.mode = 'idle';
141
+ if (inputState.inputResolve) {
142
+ inputState.inputResolve(shortcutItem.key);
143
+ inputState.inputResolve = null;
144
+ }
145
+ }
146
+ }
147
+ }
148
+
149
+ function handleInputKeypress(key) {
150
+ if (key.name === 'return' || key.name === 'enter') {
151
+ const value = inputState.inputBuffer;
152
+ inputState.mode = 'idle';
153
+ inputState.inputBuffer = '';
154
+ inputState.promptText = '';
155
+ process.stdout.write('\n');
156
+ if (inputState.inputResolve) {
157
+ inputState.inputResolve(value);
158
+ inputState.inputResolve = null;
159
+ }
160
+ } else if (key.name === 'backspace') {
161
+ if (inputState.inputBuffer.length > 0) {
162
+ inputState.inputBuffer = inputState.inputBuffer.slice(0, -1);
163
+ process.stdout.write('\r');
164
+ process.stdout.write(c(inputState.promptText, 'white'));
165
+ process.stdout.write(inputState.inputBuffer);
166
+ process.stdout.write('\x1b[K');
167
+ }
168
+ } else if (key.name === 'tab') {
169
+ autocompleteInput();
170
+ } else if (key.name === 'escape') {
171
+ inputState.mode = 'idle';
172
+ inputState.inputBuffer = '';
173
+ inputState.promptText = '';
174
+ process.stdout.write('\n');
175
+ if (inputState.inputResolve) {
176
+ inputState.inputResolve('__CANCEL__');
177
+ inputState.inputResolve = null;
178
+ }
179
+ } else if (key.name === 'c' && key.ctrl) {
180
+ process.exit(0);
181
+ } else if (key.name === 'up' || key.name === 'down' || key.name === 'left' || key.name === 'right' || key.name === 'home' || key.name === 'end' || key.name === 'pageup' || key.name === 'pagedown') {
182
+ return;
183
+ } else if (!key.ctrl && !key.meta && key.sequence) {
184
+ inputState.inputBuffer += key.sequence;
185
+ process.stdout.write(key.sequence);
186
+ }
187
+ }
188
+
189
+ function autocompleteInput() {
190
+ const input = inputState.inputBuffer;
191
+
192
+ if (!input.startsWith('/')) {
193
+ return;
194
+ }
195
+
196
+ const matches = MENU_ITEMS.filter(item =>
197
+ item.command.startsWith(input) ||
198
+ item.key.startsWith(input.substring(1))
199
+ );
200
+
201
+ if (matches.length === 1) {
202
+ inputState.inputBuffer = matches[0].command;
203
+ process.stdout.write('\r');
204
+ process.stdout.write(c(inputState.promptText, 'white'));
205
+ process.stdout.write(inputState.inputBuffer);
206
+ process.stdout.write('\x1b[K');
207
+ } else if (matches.length > 1) {
208
+ const commonPrefix = findCommonPrefix(matches.map(m => m.command));
209
+ if (commonPrefix.length > input.length) {
210
+ inputState.inputBuffer = commonPrefix;
211
+ process.stdout.write('\r');
212
+ process.stdout.write(c(inputState.promptText, 'white'));
213
+ process.stdout.write(inputState.inputBuffer);
214
+ process.stdout.write('\x1b[K');
215
+ } else {
216
+ console.log();
217
+ matches.forEach(item => {
218
+ console.log(' ' + c(item.command, 'cyan') + ' - ' + c(item.desc, 'gray'));
219
+ });
220
+ process.stdout.write(c(inputState.promptText, 'white') + inputState.inputBuffer);
221
+ }
222
+ }
223
+ }
224
+
225
+ function findCommonPrefix(strings) {
226
+ if (strings.length === 0) return '';
227
+ let prefix = strings[0];
228
+ for (let i = 1; i < strings.length; i++) {
229
+ while (strings[i].indexOf(prefix) !== 0) {
230
+ prefix = prefix.substring(0, prefix.length - 1);
231
+ if (prefix === '') return '';
232
+ }
233
+ }
234
+ return prefix;
235
+ }
236
+
237
+ function handleSearchKeypress(key) {
238
+ if (key.name === 'return' || key.name === 'enter') {
239
+ inputState.mode = 'menu';
240
+ process.stdout.write('\n');
241
+ reprintMenu();
242
+ } else if (key.name === 'escape') {
243
+ inputState.filter = '';
244
+ inputState.selectedIndex = 0;
245
+ inputState.mode = 'menu';
246
+ process.stdout.write('\n');
247
+ reprintMenu();
248
+ } else if (key.name === 'backspace') {
249
+ if (inputState.filter.length > 0) {
250
+ inputState.filter = inputState.filter.slice(0, -1);
251
+ inputState.selectedIndex = 0;
252
+ process.stdout.write('\r');
253
+ process.stdout.write(' 🔎 搜索: ' + inputState.filter);
254
+ process.stdout.write('\x1b[K');
255
+ }
256
+ } else if (key.name === 'up') {
257
+ const filtered = getFilteredMenu();
258
+ if (filtered.length > 0) {
259
+ inputState.selectedIndex = Math.max(0, inputState.selectedIndex - 1);
260
+ reprintMenu();
261
+ process.stdout.write(' 🔎 搜索: ' + inputState.filter);
262
+ }
263
+ } else if (key.name === 'down') {
264
+ const filtered = getFilteredMenu();
265
+ if (filtered.length > 0) {
266
+ inputState.selectedIndex = Math.min(filtered.length - 1, inputState.selectedIndex + 1);
267
+ reprintMenu();
268
+ process.stdout.write(' 🔎 搜索: ' + inputState.filter);
269
+ }
270
+ } else if (!key.ctrl && !key.meta && key.sequence) {
271
+ inputState.filter += key.sequence;
272
+ inputState.selectedIndex = 0;
273
+ process.stdout.write(key.sequence);
274
+ }
275
+ }
276
+
277
+ async function showMenu() {
278
+ return new Promise((resolve) => {
279
+ const menuState = {
280
+ input: '',
281
+ selectedIndex: 0,
282
+ resolve,
283
+ isCommandMode: false
284
+ };
285
+
286
+ function render() {
287
+ console.clear();
288
+ printBanner();
289
+ printStatusBar();
290
+ printMenu();
291
+ console.log();
292
+ console.log(c('─'.repeat(70), 'dim'));
293
+
294
+ const filtered = getFilteredCommands(menuState.input);
295
+
296
+ if (menuState.input.startsWith('/') && filtered.length > 0) {
297
+ console.log(c(' 📋 匹配命令:', 'cyan'));
298
+
299
+ const maxDisplay = 5;
300
+ const start = Math.max(0, Math.min(menuState.selectedIndex - Math.floor(maxDisplay / 2), filtered.length - maxDisplay));
301
+ const end = Math.min(start + maxDisplay, filtered.length);
302
+
303
+ for (let i = start; i < end; i++) {
304
+ const item = filtered[i];
305
+ const isSel = i === menuState.selectedIndex;
306
+ const prefix = isSel ? c(' ▶ ', 'green') : ' ';
307
+ const cmd = isSel ? c(item.command, 'bright white') : c(item.command, 'cyan');
308
+ const desc = isSel ? c(' ' + item.desc, 'white') : c(' ' + item.desc, 'gray');
309
+ console.log(prefix + cmd + desc);
310
+ }
311
+
312
+ if (filtered.length > maxDisplay) {
313
+ console.log(c(' ... 共 ' + filtered.length + ' 条匹配', 'dim'));
314
+ }
315
+ } else if (menuState.input && !menuState.input.startsWith('/')) {
316
+ console.log(c(' 💭 按 Enter 发送消息与AI聊天', 'gray'));
317
+ } else {
318
+ console.log(c(' 输入 /command 执行功能,直接输入文字与AI聊天', 'gray'));
319
+ console.log(c(' ↑↓ 选择命令 Enter 确认 Esc 取消', 'gray'));
320
+ }
321
+
322
+ console.log(c('─'.repeat(70), 'dim'));
323
+ console.log();
324
+ process.stdout.write(c(' 💬 ', 'green') + c('输入命令或与AI聊天: ', 'white') + menuState.input);
325
+ }
326
+
327
+ function handleKey(chunk, key) {
328
+ if (key.name === 'return' || key.name === 'enter') {
329
+ process.stdin.removeListener('keypress', handleKey);
330
+ inputState.mode = 'idle';
331
+ process.stdout.write('\n');
332
+
333
+ if (!menuState.input.trim()) {
334
+ resolve(null);
335
+ return;
336
+ }
337
+
338
+ const filtered = getFilteredCommands(menuState.input);
339
+ if (menuState.input.startsWith('/') && filtered.length > 0 && menuState.selectedIndex < filtered.length) {
340
+ resolve(filtered[menuState.selectedIndex].key);
341
+ } else if (menuState.input.startsWith('/')) {
342
+ console.log(c(' ✗ 未知命令: ' + menuState.input, 'red'));
343
+ setTimeout(() => {
344
+ waitEnter().then(() => resolve(null));
345
+ }, 100);
346
+ } else {
347
+ handleAIChat(menuState.input.trim()).then(() => resolve(null));
348
+ }
349
+ return;
350
+ }
351
+
352
+ if (key.name === 'escape') {
353
+ process.stdin.removeListener('keypress', handleKey);
354
+ inputState.mode = 'idle';
355
+ process.stdout.write('\n');
356
+ resolve('__BACK__');
357
+ return;
358
+ }
359
+
360
+ if (key.name === 'up') {
361
+ const filtered = getFilteredCommands(menuState.input);
362
+ if (filtered.length > 0) {
363
+ menuState.selectedIndex = Math.max(0, menuState.selectedIndex - 1);
364
+ render();
365
+ }
366
+ return;
367
+ }
368
+
369
+ if (key.name === 'down') {
370
+ const filtered = getFilteredCommands(menuState.input);
371
+ if (filtered.length > 0) {
372
+ menuState.selectedIndex = Math.min(filtered.length - 1, menuState.selectedIndex + 1);
373
+ render();
374
+ }
375
+ return;
376
+ }
377
+
378
+ if (key.name === 'tab') {
379
+ const filtered = getFilteredCommands(menuState.input);
380
+ if (filtered.length > 0) {
381
+ menuState.input = filtered[menuState.selectedIndex].command;
382
+ menuState.selectedIndex = 0;
383
+ render();
384
+ }
385
+ return;
386
+ }
387
+
388
+ if (key.name === 'backspace') {
389
+ if (menuState.input.length > 0) {
390
+ menuState.input = menuState.input.slice(0, -1);
391
+ menuState.selectedIndex = 0;
392
+ render();
393
+ }
394
+ return;
395
+ }
396
+
397
+ if (key.name === 'c' && key.ctrl) {
398
+ process.exit(0);
399
+ return;
400
+ }
401
+
402
+ if (!key.ctrl && !key.meta && key.sequence) {
403
+ menuState.input += key.sequence;
404
+ menuState.selectedIndex = 0;
405
+ render();
406
+ }
407
+ }
408
+
409
+ inputState.mode = 'input';
410
+ process.stdin.on('keypress', handleKey);
411
+ render();
412
+ });
413
+ }
414
+
415
+ function getFilteredCommands(input) {
416
+ if (!input.startsWith('/')) {
417
+ return [];
418
+ }
419
+
420
+ const query = input.substring(1).toLowerCase();
421
+
422
+ return MENU_ITEMS.filter(item => {
423
+ const cmdName = item.key.toLowerCase();
424
+ if (cmdName.startsWith(query)) return true;
425
+ if (cmdName.includes(query)) return true;
426
+
427
+ if (item.desc.includes(query)) return true;
428
+
429
+ return false;
430
+ });
431
+ }
432
+
433
+ function printChatPrompt() {
434
+ }
435
+
436
+ async function chatWithAI() {
437
+ printBanner();
438
+ console.log(c(' 💬 AI代码助手', 'bright cyan'));
439
+ console.log(c(' 输入 q 返回主菜单,空行发送消息', 'dim'));
440
+ console.log(c(' 仅限代码相关内容,AI可帮您调用智能体功能', 'dim'));
441
+ console.log(c('─'.repeat(70), 'dim'));
442
+ console.log();
443
+
444
+ const firstMessage = await ask(c(' 💭 您: ', 'white'));
445
+ if (firstMessage === '__CANCEL__' || firstMessage.toLowerCase() === 'q' || firstMessage.toLowerCase() === 'quit') {
446
+ return;
447
+ }
448
+ if (!firstMessage.trim()) {
449
+ return;
450
+ }
451
+
452
+ await handleAIChat(firstMessage);
453
+ }
454
+
455
+ function ask(prompt) {
456
+ return new Promise((resolve) => {
457
+ inputState.mode = 'input';
458
+ inputState.inputBuffer = '';
459
+ inputState.promptText = prompt;
460
+ inputState.inputResolve = resolve;
461
+ process.stdout.write(c(prompt, 'white'));
462
+ });
463
+ }
464
+
465
+ function printBanner() {
466
+ console.clear();
467
+ const lines = [
468
+ '',
469
+ c('╔══════════════════════════════════════════════════════════════════════╗', 'cyan'),
470
+ c('║', 'cyan') + c(' ╭──────────────────────────────────────────────────────────────╮ ', 'cyan') + c('║', 'cyan'),
471
+ c('║', 'cyan') + c(' │ ', 'cyan') + c('Mr.Sliy', 'bright cyan') + c(' - 多语言代码优化智能体 ', 'white') + c('│ ', 'cyan') + c('║', 'cyan'),
472
+ c('║', 'cyan') + c(' │ ', 'cyan') + c('v1.0.0', 'dim') + c(' │ 基于 Tree-sitter + RAG 的智能检测优化 ', 'gray') + c('│ ', 'cyan') + c('║', 'cyan'),
473
+ c('║', 'cyan') + c(' ╰──────────────────────────────────────────────────────────────╯ ', 'cyan') + c('║', 'cyan'),
474
+ c('╠══════════════════════════════════════════════════════════════════════╣', 'cyan'),
475
+ c('║', 'cyan') + c(' 🟢 离线模式: ', 'green') + c('AST检测 + 本地RAG知识库', 'white') + c(' ', 'cyan') + c('║', 'cyan'),
476
+ c('║', 'cyan') + c(' 🔵 在线模式: ', 'blue') + c('AST检测 + 云端大模型 + RAG增强', 'white') + c(' ', 'cyan') + c('║', 'cyan'),
477
+ c('║', 'cyan') + c(' ⚡ 自动模式: ', 'yellow') + c('智能判断,自动切换最优模式', 'white') + c(' ', 'cyan') + c('║', 'cyan'),
478
+ c('╚══════════════════════════════════════════════════════════════════════╝', 'cyan'),
479
+ ''
480
+ ];
481
+ lines.forEach(l => console.log(l));
482
+ }
483
+
484
+ function printStatusBar() {
485
+ const status = agent.getStatus();
486
+ const mode = status.engine.actualMode;
487
+ const modeLabel = mode === 'online' ? 'ONLINE' : mode === 'offline' ? 'OFFLINE' : 'AUTO';
488
+ const providers = status.engine.providers.filter(p => p.available).length;
489
+ const kb = status.engine.knowledgeBase;
490
+
491
+ let t = '';
492
+ t += c(' 状态', 'dim') + ': ' + c(status.state, 'green') + ' ';
493
+ t += c('模式', 'dim') + ': ' + c(modeLabel, 'white') + ' ';
494
+ t += c('提供商', 'dim') + ': ' + c(providers, 'cyan') + ' ';
495
+ t += c('知识库', 'dim') + ': ' + c(kb.totalEntries + '条', 'magenta');
496
+
497
+ console.log(t);
498
+ console.log(c('─'.repeat(70), 'dim'));
499
+ }
500
+
501
+ function printMenu() {
502
+ console.log();
503
+ console.log(c(' 📋 可用命令', 'bright cyan'));
504
+ console.log(c('─'.repeat(70), 'dim'));
505
+
506
+ MENU_ITEMS.forEach(item => {
507
+ console.log(' ' + c(item.command, 'cyan') + c(' - ', 'white') + c(item.desc, 'gray'));
508
+ });
509
+
510
+ console.log(c('─'.repeat(70), 'dim'));
511
+ }
512
+
513
+ async function handleAIChat(message) {
514
+ console.log();
515
+ agent.clearChatHistory();
516
+
517
+ try {
518
+ while (true) {
519
+ console.log(c(' 🤖 AI思考中...', 'cyan'));
520
+
521
+ const result = await agent.chat(message);
522
+ process.stdout.write('\x1b[1A\x1b[2K\r');
523
+
524
+ console.log(c(' 🤖 AI:', 'green'));
525
+ console.log(c(' ' + result.content.replace(/\n/g, '\n '), 'white'));
526
+ console.log();
527
+
528
+ const functionCall = parseFunctionCall(result.content);
529
+ if (functionCall) {
530
+ console.log(c(' 🔧 AI请求调用功能: ' + functionCall.function, 'cyan'));
531
+ await executeFunctionCall(functionCall);
532
+ console.log();
533
+ }
534
+
535
+ console.log(c('─'.repeat(70), 'dim'));
536
+ const nextInput = await ask(c(' 💬 ', 'green') + c('继续对话或输入 q 返回: ', 'white'));
537
+
538
+ if (nextInput === '__CANCEL__' || nextInput.toLowerCase() === 'q' || nextInput.toLowerCase() === 'quit' || !nextInput.trim()) {
539
+ agent.clearChatHistory();
540
+ return;
541
+ }
542
+
543
+ message = nextInput;
544
+ console.log();
545
+ }
546
+ } catch (error) {
547
+ console.log(c(' ✗ 聊天失败: ' + error.message, 'red'));
548
+ console.log(c(' 请先在提供商管理中配置并启用LLM提供商', 'yellow'));
549
+ console.log();
550
+ agent.clearChatHistory();
551
+ await waitEnter();
552
+ }
553
+ }
554
+
555
+ function parseFunctionCall(content) {
556
+ const match = content.match(/<function_call>\s*([\s\S]*?)\s*<\/function_call>/i);
557
+ if (match) {
558
+ try {
559
+ return JSON.parse(match[1]);
560
+ } catch (e) {
561
+ return null;
562
+ }
563
+ }
564
+ return null;
565
+ }
566
+
567
+ async function executeFunctionCall(call) {
568
+ switch (call.function) {
569
+ case 'analyze_file':
570
+ if (call.params?.filePath) {
571
+ console.log(c(' 正在分析文件: ' + call.params.filePath, 'white'));
572
+ await analyzeFile(call.params.filePath);
573
+ }
574
+ break;
575
+ case 'scan_project':
576
+ if (call.params?.path) {
577
+ console.log(c(' 正在扫描项目: ' + call.params.path, 'white'));
578
+ await scanProject(call.params.path);
579
+ }
580
+ break;
581
+ case 'optimize_code':
582
+ if (call.params?.code) {
583
+ console.log(c(' 正在优化代码...', 'white'));
584
+ await optimizeCodeWithCode(call.params.code);
585
+ }
586
+ break;
587
+ case 'search_knowledge':
588
+ if (call.params?.query) {
589
+ console.log(c(' 正在搜索知识库: ' + call.params.query, 'white'));
590
+ await searchKnowledge(call.params.query);
591
+ }
592
+ break;
593
+ case 'get_status':
594
+ await showStatus();
595
+ break;
596
+ case 'switch_provider':
597
+ if (call.params?.name) {
598
+ console.log(c(' 正在切换提供商: ' + call.params.name, 'white'));
599
+ try {
600
+ await agent.switchProvider(call.params.name);
601
+ console.log(c(' ✓ 切换成功', 'green'));
602
+ } catch (error) {
603
+ console.log(c(' ✗ 切换失败: ' + error.message, 'red'));
604
+ }
605
+ }
606
+ break;
607
+ case 'switch_mode':
608
+ if (call.params?.mode) {
609
+ console.log(c(' 正在切换模式: ' + call.params.mode, 'white'));
610
+ try {
611
+ await agent.configure({ mode: call.params.mode });
612
+ console.log(c(' ✓ 切换成功', 'green'));
613
+ } catch (error) {
614
+ console.log(c(' ✗ 切换失败: ' + error.message, 'red'));
615
+ }
616
+ }
617
+ break;
618
+ default:
619
+ console.log(c(' ⚠️ 未知功能: ' + call.function, 'yellow'));
620
+ }
621
+ }
622
+
623
+ async function optimizeCodeWithCode(code) {
624
+ console.log(c(' ✨ 代码优化结果', 'bright cyan'));
625
+ console.log(c('─'.repeat(70), 'dim'));
626
+ console.log();
627
+
628
+ try {
629
+ const result = await agent.analyzeSnippet(code, 'javascript', { generalOptimize: true });
630
+
631
+ if (!result.success) {
632
+ console.log(c(' ✗ 优化失败: ' + result.message, 'red'));
633
+ return;
634
+ }
635
+
636
+ console.log(c(' ✓ 分析完成 [' + result.mode + ']', 'green'));
637
+ console.log();
638
+
639
+ result.issues.forEach((issue, i) => {
640
+ console.log(c(' ' + (i + 1) + '. ' + issue.message, 'yellow'));
641
+
642
+ if (issue.optimization) {
643
+ const expl = (issue.optimization.explanation || '').replace(/参考知识[\s\S]*$/, '').trim();
644
+ if (expl) console.log(c(' 说明: ' + expl, 'white'));
645
+
646
+ if (issue.optimization.optimizedCode && issue.optimization.optimizedCode !== issue.codeSnippet) {
647
+ console.log(c(' 优化后:', 'green'));
648
+ issue.optimization.optimizedCode.split('\n').forEach(l => {
649
+ console.log(c(' ' + l, 'cyan'));
650
+ });
651
+ }
652
+
653
+ if (issue.optimization.suggestions && issue.optimization.suggestions.length > 0) {
654
+ console.log(c(' 建议:', 'green'));
655
+ issue.optimization.suggestions.slice(0, 3).forEach(s => {
656
+ console.log(c(' • ' + s, 'white'));
657
+ });
658
+ }
659
+ }
660
+ console.log();
661
+ });
662
+ } catch (error) {
663
+ console.log(c(' ✗ 优化失败: ' + error.message, 'red'));
664
+ }
665
+ }
666
+
667
+ async function searchKnowledge(query) {
668
+ console.log(c(' 📚 知识库搜索结果', 'bright cyan'));
669
+ console.log(c('─'.repeat(70), 'dim'));
670
+ console.log();
671
+
672
+ try {
673
+ const results = await agent.searchKnowledge(query, { limit: 5 });
674
+
675
+ if (!results || results.length === 0) {
676
+ console.log(c(' 未找到相关知识', 'yellow'));
677
+ return;
678
+ }
679
+
680
+ results.forEach((item, i) => {
681
+ console.log(c(' ' + (i + 1) + '. ' + (item.title || '知识条目'), 'green'));
682
+ console.log(c(' ' + (item.content || item.description || '').substring(0, 100) + '...', 'white'));
683
+ if (item.type) console.log(c(' 类型: ' + item.type, 'dim'));
684
+ console.log();
685
+ });
686
+ } catch (error) {
687
+ console.log(c(' ✗ 搜索失败: ' + error.message, 'red'));
688
+ }
689
+ }
690
+
691
+ function reprintMenu() {
692
+ console.clear();
693
+ printBanner();
694
+ printStatusBar();
695
+ printMenu();
696
+ }
697
+
698
+ async function analyzeFile() {
699
+ printBanner();
700
+ console.log(c(' 🔍 文件分析', 'bright cyan'));
701
+ console.log(c(' 输入 q 返回主菜单,Esc 取消', 'dim'));
702
+ console.log(c('─'.repeat(70), 'dim'));
703
+
704
+ const filePath = await ask(' 请输入文件路径: ');
705
+
706
+ if (filePath === '__CANCEL__') return;
707
+ if (filePath.toLowerCase() === 'q' || filePath.toLowerCase() === 'quit') return;
708
+ if (!filePath) {
709
+ console.log(c(' 已取消', 'yellow'));
710
+ await waitEnter();
711
+ return;
712
+ }
713
+
714
+ if (!fs.existsSync(filePath)) {
715
+ console.log(c(' ✗ 文件不存在: ' + filePath, 'red'));
716
+ await waitEnter();
717
+ return;
718
+ }
719
+
720
+ console.log(c(' 正在分析...', 'cyan'));
721
+
722
+ try {
723
+ const result = await agent.analyzeFile(filePath);
724
+
725
+ process.stdout.write('\x1b[1A\x1b[2K\r');
726
+
727
+ if (!result.success) {
728
+ console.log(c(' ✗ 分析失败: ' + result.message, 'red'));
729
+ await waitEnter();
730
+ return;
731
+ }
732
+
733
+ console.log(c(' ✓ 分析完成 [' + result.mode + '] (' + result.durationMs + 'ms)', 'green'));
734
+ console.log(c(' 语言: ' + result.language, 'white'));
735
+ console.log(c(' 发现问题: ' + result.totalIssues + ' 个', result.totalIssues > 0 ? 'yellow' : 'green'));
736
+
737
+ if (result.totalIssues > 0) {
738
+ const counts = result.issueCounts;
739
+ console.log(c(' 严重: ' + counts.critical + ' 高: ' + counts.high + ' 中: ' + counts.medium + ' 低: ' + counts.low, 'white'));
740
+ console.log();
741
+
742
+ const displayIssues = result.issues.slice(0, 10);
743
+ displayIssues.forEach((issue, i) => {
744
+ const sevColor = issue.severity === 'critical' || issue.severity === 'high' ? 'red' : 'yellow';
745
+ console.log(c(' ' + (i + 1) + '. [' + issue.severity.toUpperCase() + '] ' + issue.message, sevColor));
746
+ console.log(c(' 位置: 第' + issue.lineStart + '行', 'dim'));
747
+
748
+ if (issue.optimization && issue.optimization.success) {
749
+ const expl = (issue.optimization.explanation || '').substring(0, 80).replace(/参考知识[\s\S]*$/, '').trim();
750
+ if (expl) console.log(c(' 💡 ' + expl, 'green'));
751
+ }
752
+ });
753
+
754
+ if (result.issues.length > 10) {
755
+ console.log(c(' ... 还有 ' + (result.issues.length - 10) + ' 个问题', 'dim'));
756
+ }
757
+ }
758
+
759
+ console.log();
760
+ await waitEnter();
761
+ } catch (error) {
762
+ console.log(c(' ✗ 分析失败: ' + error.message, 'red'));
763
+ await waitEnter();
764
+ }
765
+ }
766
+
767
+ async function scanProject() {
768
+ printBanner();
769
+ console.log(c(' 📁 项目扫描', 'bright cyan'));
770
+ console.log(c(' 输入 q 返回主菜单,Esc 取消', 'dim'));
771
+ console.log(c('─'.repeat(70), 'dim'));
772
+
773
+ const projectPath = await ask(' 请输入项目路径: ');
774
+
775
+ if (projectPath === '__CANCEL__') return;
776
+ if (projectPath.toLowerCase() === 'q' || projectPath.toLowerCase() === 'quit') return;
777
+ if (!projectPath) {
778
+ console.log(c(' 已取消', 'yellow'));
779
+ await waitEnter();
780
+ return;
781
+ }
782
+
783
+ if (!fs.existsSync(projectPath)) {
784
+ console.log(c(' ✗ 路径不存在: ' + projectPath, 'red'));
785
+ await waitEnter();
786
+ return;
787
+ }
788
+
789
+ console.log(c(' 正在扫描项目...', 'cyan'));
790
+ console.log(c(' 这可能需要一些时间...', 'dim'));
791
+
792
+ try {
793
+ const result = await agent.analyzeProject(projectPath);
794
+
795
+ console.log();
796
+ process.stdout.write('\x1b[3A\x1b[0J');
797
+
798
+ if (!result.success) {
799
+ console.log(c(' ✗ 扫描失败: ' + result.message, 'red'));
800
+ await waitEnter();
801
+ return;
802
+ }
803
+
804
+ console.log(c(' ✓ 扫描完成 [' + result.mode + '] (' + result.durationMs + 'ms)', 'green'));
805
+ console.log(c(' 扫描文件: ' + result.scannedFiles + '/' + result.totalFiles, 'white'));
806
+ console.log(c(' 失败文件: ' + result.failedFiles, result.failedFiles > 0 ? 'yellow' : 'white'));
807
+ console.log(c(' 总问题数: ' + result.totalIssues, result.totalIssues > 0 ? 'yellow' : 'green'));
808
+
809
+ const fileIssues = result.results
810
+ .filter(r => r.success && r.totalIssues > 0)
811
+ .map(r => ({ file: path.basename(r.filePath), count: r.totalIssues }))
812
+ .sort((a, b) => b.count - a.count)
813
+ .slice(0, 8);
814
+
815
+ if (fileIssues.length > 0) {
816
+ console.log();
817
+ console.log(c(' 问题最多的文件:', 'cyan'));
818
+ fileIssues.forEach(f => {
819
+ console.log(c(' ' + f.file + ': ' + f.count + ' 个问题', 'white'));
820
+ });
821
+ }
822
+
823
+ console.log();
824
+ await waitEnter();
825
+ } catch (error) {
826
+ console.log(c(' ✗ 扫描失败: ' + error.message, 'red'));
827
+ await waitEnter();
828
+ }
829
+ }
830
+
831
+ async function optimizeCode() {
832
+ printBanner();
833
+ console.log(c(' ✨ 交互式代码优化', 'bright cyan'));
834
+ console.log(c(' 输入 q 返回主菜单,空行结束输入', 'dim'));
835
+ console.log(c('─'.repeat(70), 'dim'));
836
+ console.log(c(' 请输入代码片段(空行结束输入):', 'white'));
837
+ console.log();
838
+
839
+ let codeLines = [];
840
+ while (true) {
841
+ const line = await ask(' > ');
842
+ if (line === '__CANCEL__') return;
843
+ if (line.toLowerCase() === 'q' || line.toLowerCase() === 'quit') return;
844
+ if (!line.trim()) break;
845
+ codeLines.push(line);
846
+ }
847
+
848
+ const code = codeLines.join('\n');
849
+ if (!code.trim()) {
850
+ console.log(c(' 未输入代码', 'yellow'));
851
+ await waitEnter();
852
+ return;
853
+ }
854
+
855
+ console.log(c(' 正在分析并优化...', 'cyan'));
856
+
857
+ try {
858
+ const result = await agent.analyzeSnippet(code, 'javascript', { generalOptimize: true });
859
+
860
+ process.stdout.write('\x1b[1A\x1b[2K\r');
861
+
862
+ if (!result.success) {
863
+ console.log(c(' ✗ 优化失败: ' + result.message, 'red'));
864
+ await waitEnter();
865
+ return;
866
+ }
867
+
868
+ console.log(c(' ✓ 分析完成 [' + result.mode + ']', 'green'));
869
+ console.log();
870
+
871
+ result.issues.forEach((issue, i) => {
872
+ console.log(c(' ' + (i + 1) + '. ' + issue.message, 'yellow'));
873
+
874
+ if (issue.optimization) {
875
+ const expl = (issue.optimization.explanation || '').replace(/参考知识[\s\S]*$/, '').trim();
876
+ if (expl) console.log(c(' 说明: ' + expl, 'white'));
877
+
878
+ if (issue.optimization.optimizedCode && issue.optimization.optimizedCode !== issue.codeSnippet) {
879
+ console.log(c(' 优化后:', 'green'));
880
+ issue.optimization.optimizedCode.split('\n').forEach(l => {
881
+ console.log(c(' ' + l, 'cyan'));
882
+ });
883
+ }
884
+
885
+ if (issue.optimization.suggestions && issue.optimization.suggestions.length > 0) {
886
+ console.log(c(' 建议:', 'green'));
887
+ issue.optimization.suggestions.slice(0, 3).forEach(s => {
888
+ console.log(c(' • ' + s, 'white'));
889
+ });
890
+ }
891
+ }
892
+ console.log();
893
+ });
894
+
895
+ await waitEnter();
896
+ } catch (error) {
897
+ console.log(c(' ✗ 优化失败: ' + error.message, 'red'));
898
+ await waitEnter();
899
+ }
900
+ }
901
+
902
+ async function providerMenu() {
903
+ while (true) {
904
+ printBanner();
905
+ console.log(c(' 🌐 大模型提供商管理', 'bright cyan'));
906
+ console.log(c(' 输入 q 返回主菜单,Esc 返回', 'dim'));
907
+ console.log(c('─'.repeat(70), 'dim'));
908
+
909
+ const providers = agent.getProviders();
910
+
911
+ console.log(c(' 已注册提供商:', 'cyan'));
912
+ providers.forEach((p, i) => {
913
+ const marker = p.available ? '✓' : '✗';
914
+ const color = p.available ? 'green' : 'red';
915
+ console.log(c(' ' + (i + 1) + '. [' + marker + '] ' + p.name + ' (' + p.model + ')', color));
916
+ });
917
+ console.log();
918
+ console.log(c(' 操作:', 'cyan'));
919
+ console.log(c(' 1) 切换活跃提供商 (switch)', 'white'));
920
+ console.log(c(' 2) 注册新提供商 (register)', 'white'));
921
+ console.log(c(' 3) 配置API Key (config)', 'white'));
922
+ console.log(c(' 4) 查看可用列表 (list)', 'white'));
923
+ console.log(c(' 0) 返回主菜单 (back)', 'dim'));
924
+ console.log();
925
+
926
+ const choice = await ask(' 请选择操作: ');
927
+
928
+ if (choice === '__CANCEL__') return;
929
+ if (choice.toLowerCase() === 'q' || choice.toLowerCase() === 'quit' || choice === '0' || choice.toLowerCase() === 'back') {
930
+ return;
931
+ }
932
+
933
+ switch (choice) {
934
+ case '1':
935
+ case 'switch':
936
+ await switchProvider();
937
+ break;
938
+ case '2':
939
+ case 'register':
940
+ await registerProvider();
941
+ break;
942
+ case '3':
943
+ case 'config':
944
+ await configProvider();
945
+ break;
946
+ case '4':
947
+ case 'list':
948
+ console.log();
949
+ console.log(c(' 支持的提供商:', 'cyan'));
950
+ console.log(c(' openai, claude, azure, gemini, tongyi, doubao, wenxin, ollama', 'white'));
951
+ await waitEnter();
952
+ break;
953
+ default:
954
+ console.log(c(' 无效的选择,请重新输入', 'yellow'));
955
+ await waitEnter();
956
+ }
957
+ }
958
+ }
959
+
960
+ async function switchProvider() {
961
+ const name = await ask(' 请输入提供商名称: ');
962
+ if (name === '__CANCEL__') return;
963
+ if (name.toLowerCase() === 'q' || name.toLowerCase() === 'quit') return;
964
+ if (!name) return;
965
+
966
+ const result = agent.switchProvider(name);
967
+ console.log(c(result.success ? ' ✓ ' + result.message : ' ✗ ' + result.message, result.success ? 'green' : 'red'));
968
+ await waitEnter();
969
+ }
970
+
971
+ async function registerProvider() {
972
+ const name = await ask(' 请输入提供商名称: ');
973
+ if (name === '__CANCEL__') return;
974
+ if (name.toLowerCase() === 'q' || name.toLowerCase() === 'quit') return;
975
+ if (!name) return;
976
+
977
+ const apiKey = await ask(' 请输入API Key (可选): ');
978
+ if (apiKey === '__CANCEL__') return;
979
+ if (apiKey.toLowerCase() === 'q') return;
980
+
981
+ const result = agent.registerProvider(name, { apiKey: apiKey || undefined });
982
+ console.log(c(result.success ? ' ✓ ' + result.message : ' ✗ ' + result.message, result.success ? 'green' : 'red'));
983
+ await waitEnter();
984
+ }
985
+
986
+ async function configProvider() {
987
+ const name = await ask(' 请输入提供商名称: ');
988
+ if (name === '__CANCEL__') return;
989
+ if (name.toLowerCase() === 'q' || name.toLowerCase() === 'quit') return;
990
+ if (!name) return;
991
+
992
+ const key = await ask(' 请输入配置项 (apiKey/model/baseURL): ');
993
+ if (key === '__CANCEL__') return;
994
+ if (key.toLowerCase() === 'q' || key.toLowerCase() === 'quit') return;
995
+ if (!key) return;
996
+
997
+ const value = await ask(' 请输入值: ');
998
+ if (value === '__CANCEL__') return;
999
+ if (value.toLowerCase() === 'q' || value.toLowerCase() === 'quit') return;
1000
+
1001
+ const config = {};
1002
+ config[key] = value;
1003
+ const result = agent.updateProviderConfig(name, config);
1004
+
1005
+ console.log(c(result.success ? ' ✓ ' + result.message : ' ✗ ' + result.message, result.success ? 'green' : 'red'));
1006
+ await waitEnter();
1007
+ }
1008
+
1009
+ async function knowledgeMenu() {
1010
+ while (true) {
1011
+ printBanner();
1012
+ console.log(c(' 📚 知识库管理', 'bright cyan'));
1013
+ console.log(c(' 输入 q 返回主菜单,Esc 返回', 'dim'));
1014
+ console.log(c('─'.repeat(70), 'dim'));
1015
+
1016
+ const stats = agent.getStatus().engine.knowledgeBase;
1017
+ console.log(c(' 总条目: ' + stats.totalEntries + ' | 总案例: ' + stats.totalCases, 'white'));
1018
+ console.log();
1019
+
1020
+ console.log(c(' 操作:', 'cyan'));
1021
+ console.log(c(' 1) 搜索知识库 (search)', 'white'));
1022
+ console.log(c(' 2) 导入知识库 (import)', 'white'));
1023
+ console.log(c(' 3) 导出知识库 (export)', 'white'));
1024
+ console.log(c(' 4) 添加知识条目 (add)', 'white'));
1025
+ console.log(c(' 5) 查看统计 (stats)', 'white'));
1026
+ console.log(c(' 6) 云端同步设置 (cloud)', 'white'));
1027
+ console.log(c(' 0) 返回主菜单 (back)', 'dim'));
1028
+ console.log();
1029
+
1030
+ const choice = await ask(' 请选择操作: ');
1031
+
1032
+ if (choice === '__CANCEL__') return;
1033
+ if (choice.toLowerCase() === 'q' || choice.toLowerCase() === 'quit' || choice === '0' || choice.toLowerCase() === 'back') {
1034
+ return;
1035
+ }
1036
+
1037
+ switch (choice) {
1038
+ case '1':
1039
+ case 'search':
1040
+ await searchKnowledge();
1041
+ break;
1042
+ case '2':
1043
+ case 'import':
1044
+ await importKnowledge();
1045
+ break;
1046
+ case '3':
1047
+ case 'export':
1048
+ await exportKnowledge();
1049
+ break;
1050
+ case '4':
1051
+ case 'add':
1052
+ await addKnowledge();
1053
+ break;
1054
+ case '5':
1055
+ case 'stats':
1056
+ await showKnowledgeStats();
1057
+ break;
1058
+ case '6':
1059
+ case 'cloud':
1060
+ await cloudSyncMenu();
1061
+ break;
1062
+ default:
1063
+ console.log(c(' 无效的选择,请重新输入', 'yellow'));
1064
+ await waitEnter();
1065
+ }
1066
+ }
1067
+ }
1068
+
1069
+ async function exportKnowledge() {
1070
+ console.log();
1071
+ const filePath = await ask(' 请输入导出文件路径 (默认 data/knowledge-export.json): ');
1072
+
1073
+ if (filePath === '__CANCEL__') return;
1074
+ if (filePath.toLowerCase() === 'q' || filePath.toLowerCase() === 'quit') return;
1075
+
1076
+ try {
1077
+ console.log(c(' 正在导出知识库...', 'cyan'));
1078
+ const result = agent.exportKnowledge(filePath || undefined);
1079
+ console.log(c(' ✅ 导出成功!', 'green'));
1080
+ console.log(c(' 知识条目: ' + result.entryCount + ' 条', 'white'));
1081
+ console.log(c(' 优化案例: ' + result.caseCount + ' 个', 'white'));
1082
+ } catch (error) {
1083
+ console.log(c(' ✗ 导出失败: ' + error.message, 'red'));
1084
+ }
1085
+
1086
+ await waitEnter();
1087
+ }
1088
+
1089
+ async function cloudSyncMenu() {
1090
+ while (true) {
1091
+ console.log();
1092
+ console.log(c(' ☁️ 云端同步', 'bright cyan'));
1093
+ console.log(c(' 输入 q 返回上一级', 'dim'));
1094
+ console.log(c('─'.repeat(70), 'dim'));
1095
+
1096
+ const { config } = require('../config');
1097
+ const cloudEnabled = config.mysql.enabled;
1098
+
1099
+ console.log(c(' 云端同步状态: ' + (cloudEnabled ? c('已启用', 'green') : c('未启用', 'yellow')), 'white'));
1100
+ console.log(c(' 服务器: ' + config.mysql.host + ':' + config.mysql.port, 'dim'));
1101
+ console.log(c(' 数据库: ' + config.mysql.database, 'dim'));
1102
+ console.log();
1103
+
1104
+ console.log(c(' 操作:', 'cyan'));
1105
+ console.log(c(' 1) 测试连接 (test)', 'white'));
1106
+ console.log(c(' 2) 上传到云端 (upload)', 'white'));
1107
+ console.log(c(' 3) 从云端下载 (download)', 'white'));
1108
+ console.log(c(' 0) 返回 (back)', 'dim'));
1109
+ console.log();
1110
+
1111
+ const choice = await ask(' 请选择操作: ');
1112
+
1113
+ if (choice === '__CANCEL__') return;
1114
+ if (choice.toLowerCase() === 'q' || choice.toLowerCase() === 'quit' || choice === '0' || choice.toLowerCase() === 'back') {
1115
+ return;
1116
+ }
1117
+
1118
+ switch (choice) {
1119
+ case '1':
1120
+ case 'test':
1121
+ await testCloudConnection();
1122
+ break;
1123
+ case '2':
1124
+ case 'upload':
1125
+ await uploadToCloud();
1126
+ break;
1127
+ case '3':
1128
+ case 'download':
1129
+ await downloadFromCloud();
1130
+ break;
1131
+ default:
1132
+ console.log(c(' 无效的选择,请重新输入', 'yellow'));
1133
+ }
1134
+ }
1135
+ }
1136
+
1137
+ async function testCloudConnection() {
1138
+ console.log();
1139
+ console.log(c(' 正在测试云端连接...', 'cyan'));
1140
+
1141
+ try {
1142
+ const result = await agent.testCloudConnection();
1143
+ if (result.success) {
1144
+ console.log(c(' ✅ ' + result.message, 'green'));
1145
+ } else {
1146
+ console.log(c(' ✗ 连接失败: ' + result.message, 'red'));
1147
+ console.log(c(' 提示: 请确保 MySQL 服务器已启动并开放 3306 端口', 'yellow'));
1148
+ }
1149
+ } catch (error) {
1150
+ console.log(c(' ✗ 测试失败: ' + error.message, 'red'));
1151
+ }
1152
+
1153
+ await waitEnter();
1154
+ }
1155
+
1156
+ async function uploadToCloud() {
1157
+ console.log();
1158
+ const confirm = await ask(' 确认将本地知识库同步到云端?(y/N): ');
1159
+
1160
+ if (confirm === '__CANCEL__') return;
1161
+ if (confirm.toLowerCase() !== 'y' && confirm.toLowerCase() !== 'yes') {
1162
+ console.log(c(' 已取消', 'yellow'));
1163
+ await waitEnter();
1164
+ return;
1165
+ }
1166
+
1167
+ console.log(c(' 正在同步到云端...', 'cyan'));
1168
+
1169
+ try {
1170
+ const result = await agent.syncKnowledgeToCloud();
1171
+ if (result.success) {
1172
+ console.log(c(' ✅ ' + result.message, 'green'));
1173
+ } else {
1174
+ console.log(c(' ✗ 同步失败: ' + result.message, 'red'));
1175
+ }
1176
+ } catch (error) {
1177
+ console.log(c(' ✗ 同步失败: ' + error.message, 'red'));
1178
+ }
1179
+
1180
+ await waitEnter();
1181
+ }
1182
+
1183
+ async function downloadFromCloud() {
1184
+ console.log();
1185
+ const confirm = await ask(' 确认从云端同步到本地?(本地已有数据将被覆盖) (y/N): ');
1186
+
1187
+ if (confirm === '__CANCEL__') return;
1188
+ if (confirm.toLowerCase() !== 'y' && confirm.toLowerCase() !== 'yes') {
1189
+ console.log(c(' 已取消', 'yellow'));
1190
+ await waitEnter();
1191
+ return;
1192
+ }
1193
+
1194
+ console.log(c(' 正在从云端同步...', 'cyan'));
1195
+
1196
+ try {
1197
+ const result = await agent.syncKnowledgeFromCloud();
1198
+ if (result.success) {
1199
+ console.log(c(' ✅ ' + result.message, 'green'));
1200
+ } else {
1201
+ console.log(c(' ✗ 同步失败: ' + result.message, 'red'));
1202
+ }
1203
+ } catch (error) {
1204
+ console.log(c(' ✗ 同步失败: ' + error.message, 'red'));
1205
+ }
1206
+
1207
+ await waitEnter();
1208
+ }
1209
+
1210
+ async function searchKnowledge() {
1211
+ const query = await ask(' 请输入搜索词: ');
1212
+ if (query === '__CANCEL__') return;
1213
+ if (query.toLowerCase() === 'q' || query.toLowerCase() === 'quit') return;
1214
+ if (!query) return;
1215
+
1216
+ const results = agent.queryKnowledge(query);
1217
+ console.log();
1218
+ console.log(c(' 找到 ' + results.total + ' 条结果', 'white'));
1219
+ console.log();
1220
+
1221
+ if (results.entries.length > 0) {
1222
+ console.log(c(' 知识条目:', 'cyan'));
1223
+ results.entries.slice(0, 5).forEach((e, i) => {
1224
+ console.log(c(' ' + (i + 1) + '. [' + (e.similarity * 100).toFixed(0) + '%] ' + e.content.substring(0, 60), 'white'));
1225
+ });
1226
+ }
1227
+
1228
+ if (results.cases.length > 0) {
1229
+ console.log(c(' 优化案例:', 'cyan'));
1230
+ results.cases.slice(0, 5).forEach((cc, i) => {
1231
+ console.log(c(' ' + (i + 1) + '. [' + (cc.similarity * 100).toFixed(0) + '%] ' + (cc.explanation || '').substring(0, 60), 'white'));
1232
+ });
1233
+ }
1234
+
1235
+ console.log();
1236
+ await waitEnter();
1237
+ }
1238
+
1239
+ async function importKnowledge() {
1240
+ while (true) {
1241
+ console.log();
1242
+ console.log(c(' 导入来源:', 'cyan'));
1243
+ console.log(c(' 1) GitHub 仓库 (github)', 'white'));
1244
+ console.log(c(' 2) 单个 URL (url)', 'white'));
1245
+ console.log(c(' 3) 本地文件 (file)', 'white'));
1246
+ console.log(c(' 0) 返回 (back)', 'dim'));
1247
+ console.log();
1248
+
1249
+ const choice = await ask(' 请选择来源: ');
1250
+
1251
+ if (choice === '__CANCEL__') return;
1252
+ if (choice.toLowerCase() === 'q' || choice.toLowerCase() === 'quit' || choice === '0' || choice.toLowerCase() === 'back') {
1253
+ return;
1254
+ }
1255
+
1256
+ switch (choice) {
1257
+ case '1':
1258
+ case 'github':
1259
+ await importFromGithub();
1260
+ break;
1261
+ case '2':
1262
+ case 'url':
1263
+ await importFromUrl();
1264
+ break;
1265
+ case '3':
1266
+ case 'file':
1267
+ await importFromFile();
1268
+ break;
1269
+ default:
1270
+ console.log(c(' 无效的选择,请重新输入', 'yellow'));
1271
+ }
1272
+ }
1273
+ }
1274
+
1275
+ async function importFromGithub() {
1276
+ const repo = await ask(' 请输入GitHub仓库地址 (user/repo): ');
1277
+ if (repo === '__CANCEL__') return;
1278
+ if (repo.toLowerCase() === 'q' || repo.toLowerCase() === 'quit') return;
1279
+ if (!repo) return;
1280
+
1281
+ console.log(c(' 正在从GitHub获取知识库内容...', 'cyan'));
1282
+
1283
+ try {
1284
+ const url = 'https://api.github.com/repos/' + repo + '/readme';
1285
+ const response = await fetch(url, { headers: { 'Accept': 'application/vnd.github.v3+json' } });
1286
+
1287
+ if (!response.ok) {
1288
+ console.log(c(' ✗ 获取失败: ' + response.statusText, 'red'));
1289
+ await waitEnter();
1290
+ return;
1291
+ }
1292
+
1293
+ const data = await response.json();
1294
+ const content = Buffer.from(data.content, 'base64').toString('utf-8');
1295
+
1296
+ const lines = content.split('\n').filter(l => l.trim().length > 20);
1297
+ let added = 0;
1298
+
1299
+ for (let i = 0; i < Math.min(20, lines.length); i++) {
1300
+ const line = lines[i].trim();
1301
+ if (line.length > 10 && line.length < 200) {
1302
+ const r = await agent.addKnowledge(line, {
1303
+ type: 'best_practice',
1304
+ language: 'general',
1305
+ tags: ['github', repo.split('/')[1]],
1306
+ source: 'github:' + repo
1307
+ });
1308
+ if (r.success) added++;
1309
+ }
1310
+ }
1311
+
1312
+ console.log(c(' ✓ 成功导入 ' + added + ' 条知识', 'green'));
1313
+ await waitEnter();
1314
+ } catch (error) {
1315
+ console.log(c(' ✗ 导入失败: ' + error.message, 'red'));
1316
+ await waitEnter();
1317
+ }
1318
+ }
1319
+
1320
+ async function importFromUrl() {
1321
+ const url = await ask(' 请输入URL地址: ');
1322
+ if (url === '__CANCEL__') return;
1323
+ if (url.toLowerCase() === 'q' || url.toLowerCase() === 'quit') return;
1324
+ if (!url) return;
1325
+
1326
+ console.log(c(' 正在从URL获取内容...', 'cyan'));
1327
+
1328
+ try {
1329
+ const response = await fetch(url);
1330
+ if (!response.ok) {
1331
+ console.log(c(' ✗ 获取失败: ' + response.statusText, 'red'));
1332
+ await waitEnter();
1333
+ return;
1334
+ }
1335
+
1336
+ const content = await response.text();
1337
+ const lines = content.split('\n').filter(l => l.trim().length > 20 && !l.trim().startsWith('<'));
1338
+ let added = 0;
1339
+
1340
+ for (let i = 0; i < Math.min(15, lines.length); i++) {
1341
+ const line = lines[i].trim();
1342
+ if (line.length > 10 && line.length < 200) {
1343
+ const r = await agent.addKnowledge(line, {
1344
+ type: 'best_practice',
1345
+ language: 'general',
1346
+ tags: ['url-import'],
1347
+ source: url
1348
+ });
1349
+ if (r.success) added++;
1350
+ }
1351
+ }
1352
+
1353
+ console.log(c(' ✓ 成功导入 ' + added + ' 条知识', 'green'));
1354
+ await waitEnter();
1355
+ } catch (error) {
1356
+ console.log(c(' ✗ 导入失败: ' + error.message, 'red'));
1357
+ await waitEnter();
1358
+ }
1359
+ }
1360
+
1361
+ async function importFromFile() {
1362
+ const filePath = await ask(' 请输入文件路径: ');
1363
+ if (filePath === '__CANCEL__') return;
1364
+ if (filePath.toLowerCase() === 'q' || filePath.toLowerCase() === 'quit') return;
1365
+ if (!filePath) return;
1366
+
1367
+ if (!fs.existsSync(filePath)) {
1368
+ console.log(c(' ✗ 文件不存在: ' + filePath, 'red'));
1369
+ await waitEnter();
1370
+ return;
1371
+ }
1372
+
1373
+ console.log(c(' 正在读取并导入...', 'cyan'));
1374
+
1375
+ try {
1376
+ const content = fs.readFileSync(filePath, 'utf-8');
1377
+ const lines = content.split('\n').filter(l => l.trim().length > 20);
1378
+ let added = 0;
1379
+
1380
+ for (let i = 0; i < lines.length; i++) {
1381
+ const line = lines[i].trim();
1382
+ if (line.length > 10 && line.length < 300) {
1383
+ const r = await agent.addKnowledge(line, {
1384
+ type: 'best_practice',
1385
+ language: 'general',
1386
+ tags: ['file-import'],
1387
+ source: filePath
1388
+ });
1389
+ if (r.success) added++;
1390
+ }
1391
+ }
1392
+
1393
+ console.log(c(' ✓ 成功导入 ' + added + ' 条知识', 'green'));
1394
+ await waitEnter();
1395
+ } catch (error) {
1396
+ console.log(c(' ✗ 导入失败: ' + error.message, 'red'));
1397
+ await waitEnter();
1398
+ }
1399
+ }
1400
+
1401
+ async function addKnowledge() {
1402
+ const content = await ask(' 请输入知识内容: ');
1403
+ if (content === '__CANCEL__') return;
1404
+ if (content.toLowerCase() === 'q' || content.toLowerCase() === 'quit') return;
1405
+ if (!content) return;
1406
+
1407
+ const type = await ask(' 类型 (best_practice/case/pattern) [best_practice]: ') || 'best_practice';
1408
+ if (type === '__CANCEL__') return;
1409
+ if (type.toLowerCase() === 'q' || type.toLowerCase() === 'quit') return;
1410
+
1411
+ const language = await ask(' 语言 [general]: ') || 'general';
1412
+ if (language === '__CANCEL__') return;
1413
+ if (language.toLowerCase() === 'q' || language.toLowerCase() === 'quit') return;
1414
+
1415
+ const result = await agent.addKnowledge(content, {
1416
+ type,
1417
+ language,
1418
+ tags: ['manual'],
1419
+ source: 'manual'
1420
+ });
1421
+
1422
+ console.log(c(result.success ? ' ✓ ' + result.message : ' ✗ ' + result.message, result.success ? 'green' : 'red'));
1423
+ await waitEnter();
1424
+ }
1425
+
1426
+ async function showKnowledgeStats() {
1427
+ const stats = agent.getStatus().engine.knowledgeBase;
1428
+ console.log();
1429
+ console.log(c(' 📊 知识库统计', 'cyan'));
1430
+ console.log(c(' 总条目: ' + stats.totalEntries, 'white'));
1431
+ console.log(c(' 总案例: ' + stats.totalCases, 'white'));
1432
+
1433
+ if (stats.typeStats && stats.typeStats.length > 0) {
1434
+ console.log(c(' 按类型:', 'blue'));
1435
+ stats.typeStats.forEach(t => {
1436
+ console.log(c(' ' + t.content_type + ': ' + t.count, 'white'));
1437
+ });
1438
+ }
1439
+
1440
+ if (stats.languageStats && stats.languageStats.length > 0) {
1441
+ console.log(c(' 按语言:', 'blue'));
1442
+ stats.languageStats.forEach(l => {
1443
+ console.log(c(' ' + l.language + ': ' + l.count, 'white'));
1444
+ });
1445
+ }
1446
+
1447
+ console.log();
1448
+ await waitEnter();
1449
+ }
1450
+
1451
+ async function modeMenu() {
1452
+ printBanner();
1453
+ console.log(c(' 🔄 切换工作模式', 'bright cyan'));
1454
+ console.log(c(' 输入 q 返回主菜单,Esc 返回', 'dim'));
1455
+ console.log(c('─'.repeat(70), 'dim'));
1456
+
1457
+ const status = agent.getStatus();
1458
+ console.log(c(' 当前配置模式: ' + status.config.mode, 'white'));
1459
+ console.log(c(' 实际运行模式: ' + status.engine.actualMode, status.engine.actualMode === 'online' ? 'green' : 'yellow'));
1460
+ console.log();
1461
+
1462
+ console.log(c(' 可选模式:', 'cyan'));
1463
+ console.log(c(' 1) 离线模式 (offline) - 仅使用本地AST检测+RAG知识库', 'white'));
1464
+ console.log(c(' 2) 在线模式 (online) - AST检测+云端大模型+RAG增强', 'white'));
1465
+ console.log(c(' 3) 自动模式 (auto) - 智能判断,自动切换最优模式', 'white'));
1466
+ console.log(c(' 0) 返回主菜单 (back)', 'dim'));
1467
+ console.log();
1468
+
1469
+ const choice = await ask(' 请选择模式: ');
1470
+
1471
+ if (choice === '__CANCEL__') return;
1472
+ if (choice.toLowerCase() === 'q' || choice.toLowerCase() === 'quit' || choice === '0' || choice.toLowerCase() === 'back') {
1473
+ return;
1474
+ }
1475
+
1476
+ let mode = null;
1477
+ if (choice === '1' || choice === 'offline') mode = 'offline';
1478
+ else if (choice === '2' || choice === 'online') mode = 'online';
1479
+ else if (choice === '3' || choice === 'auto') mode = 'auto';
1480
+
1481
+ if (mode) {
1482
+ const result = agent.setMode(mode);
1483
+ console.log(c(' ✓ 模式已切换: ' + result.mode + ' (实际: ' + result.actualMode + ')', 'green'));
1484
+ await waitEnter();
1485
+ } else {
1486
+ console.log(c(' 无效的选择,请重新输入', 'yellow'));
1487
+ await waitEnter();
1488
+ }
1489
+ }
1490
+
1491
+ async function showStatus() {
1492
+ printBanner();
1493
+ console.log(c(' 📊 系统状态', 'bright cyan'));
1494
+ console.log(c(' 输入 q 返回主菜单,Esc 返回', 'dim'));
1495
+ console.log(c('─'.repeat(70), 'dim'));
1496
+
1497
+ const status = agent.getStatus();
1498
+
1499
+ console.log(c(' Agent 状态:', 'cyan'));
1500
+ console.log(c(' 状态: ' + status.state, 'white'));
1501
+ console.log(c(' 配置模式: ' + status.config.mode, 'white'));
1502
+ console.log(c(' 实际模式: ' + status.engine.actualMode, status.engine.actualMode === 'online' ? 'green' : 'yellow'));
1503
+ console.log(c(' 引擎初始化: ' + (status.engine.initialized ? '是' : '否'), 'white'));
1504
+ console.log(c(' 任务历史: ' + status.historyCount + ' 条', 'white'));
1505
+ console.log();
1506
+
1507
+ console.log(c(' LLM提供商:', 'cyan'));
1508
+ status.engine.providers.forEach(p => {
1509
+ const color = p.available ? 'green' : 'red';
1510
+ const marker = p.available ? '✓' : '✗';
1511
+ console.log(c(' [' + marker + '] ' + p.name + ' (' + p.model + ')', color));
1512
+ });
1513
+ console.log();
1514
+
1515
+ console.log(c(' 知识库:', 'cyan'));
1516
+ const kb = status.engine.knowledgeBase;
1517
+ console.log(c(' 条目数: ' + kb.totalEntries, 'white'));
1518
+ console.log(c(' 案例数: ' + kb.totalCases, 'white'));
1519
+ console.log();
1520
+
1521
+ console.log(c(' 配置:', 'cyan'));
1522
+ console.log(c(' 自动保存: ' + (status.config.autoSave ? '是' : '否'), 'white'));
1523
+ console.log(c(' 最大问题数/文件: ' + status.config.maxIssuesPerFile, 'white'));
1524
+ console.log();
1525
+
1526
+ await waitEnter();
1527
+ }
1528
+
1529
+ async function showHelp() {
1530
+ printBanner();
1531
+ console.log(c(' ❓ 帮助文档', 'bright cyan'));
1532
+ console.log(c(' 输入 q 返回主菜单,Esc 返回', 'dim'));
1533
+ console.log(c('─'.repeat(70), 'dim'));
1534
+
1535
+ console.log(c(' 快捷键:', 'cyan'));
1536
+ console.log(c(' ↑↓ 上下选择菜单项', 'white'));
1537
+ console.log(c(' Enter 执行选中的命令', 'white'));
1538
+ console.log(c(' / 搜索/过滤命令', 'white'));
1539
+ console.log(c(' Esc 取消搜索/返回', 'white'));
1540
+ console.log(c(' q 返回上级菜单', 'white'));
1541
+ console.log(c(' 字母键 快捷键快速执行', 'white'));
1542
+ console.log();
1543
+
1544
+ console.log(c(' 功能说明:', 'cyan'));
1545
+ console.log(c(' 🔍 文件分析 分析单个代码文件,检测缺陷并给出优化建议', 'white'));
1546
+ console.log(c(' 📁 项目扫描 扫描整个项目,批量分析所有代码文件', 'white'));
1547
+ console.log(c(' ✨ 代码优化 交互式输入代码,获取优化建议', 'white'));
1548
+ console.log(c(' 🌐 提供商管理 配置云端大模型,支持多种API', 'white'));
1549
+ console.log(c(' 📚 知识库管理 搜索、导入、扩充本地RAG知识库', 'white'));
1550
+ console.log(c(' 🔄 模式切换 离线/在线/自动 三种工作模式', 'white'));
1551
+ console.log(c(' 📊 系统状态 查看系统运行状态和配置信息', 'white'));
1552
+ console.log();
1553
+
1554
+ console.log(c(' 工作模式:', 'cyan'));
1555
+ console.log(c(' 离线模式 使用本地AST检测 + RAG知识库优化,无需联网', 'white'));
1556
+ console.log(c(' 在线模式 AST检测 + 云端大模型 + RAG增强,效果更好', 'white'));
1557
+ console.log(c(' 自动模式 自动判断可用资源,选择最优模式', 'white'));
1558
+ console.log();
1559
+
1560
+ await waitEnter();
1561
+ }
1562
+
1563
+ function waitEnter() {
1564
+ return ask(c(' 按 Enter 或 q 返回...', 'dim'));
1565
+ }
1566
+
1567
+ async function startCLI() {
1568
+ agent.init();
1569
+
1570
+ if (process.env.OPENAI_API_KEY) {
1571
+ agent.registerProvider('openai', { apiKey: process.env.OPENAI_API_KEY, model: process.env.OPENAI_MODEL || 'gpt-4' });
1572
+ }
1573
+ if (process.env.CLAUDE_API_KEY) {
1574
+ agent.registerProvider('claude', { apiKey: process.env.CLAUDE_API_KEY, model: process.env.CLAUDE_MODEL || 'claude-3-sonnet-20240229' });
1575
+ }
1576
+ if (process.env.GEMINI_API_KEY) {
1577
+ agent.registerProvider('gemini', { apiKey: process.env.GEMINI_API_KEY, model: process.env.GEMINI_MODEL || 'gemini-1.5-pro' });
1578
+ }
1579
+ if (process.env.TONGYI_API_KEY) {
1580
+ agent.registerProvider('tongyi', { apiKey: process.env.TONGYI_API_KEY, model: process.env.TONGYI_MODEL || 'qwen-plus' });
1581
+ }
1582
+ if (process.env.DOUBAO_API_KEY) {
1583
+ agent.registerProvider('doubao', { apiKey: process.env.DOUBAO_API_KEY, model: process.env.DOUBAO_MODEL || 'Doubao-7B' });
1584
+ }
1585
+ if (process.env.WENXIN_API_KEY && process.env.WENXIN_SECRET_KEY) {
1586
+ agent.registerProvider('wenxin', { apiKey: process.env.WENXIN_API_KEY, secretKey: process.env.WENXIN_SECRET_KEY, model: process.env.WENXIN_MODEL || 'ernie-3.5' });
1587
+ }
1588
+
1589
+ agent.registerProvider('ollama', { baseURL: process.env.OLLAMA_URL || 'http://localhost:11434', model: process.env.OLLAMA_MODEL || 'codellama' });
1590
+
1591
+ initInput();
1592
+
1593
+ while (true) {
1594
+ const choice = await showMenu();
1595
+
1596
+ if (choice === '__BACK__') continue;
1597
+
1598
+ switch (choice) {
1599
+ case 'analyze':
1600
+ await analyzeFile();
1601
+ break;
1602
+ case 'scan':
1603
+ await scanProject();
1604
+ break;
1605
+ case 'optimize':
1606
+ await optimizeCode();
1607
+ break;
1608
+ case 'provider':
1609
+ await providerMenu();
1610
+ break;
1611
+ case 'knowledge':
1612
+ await knowledgeMenu();
1613
+ break;
1614
+ case 'mode':
1615
+ await modeMenu();
1616
+ break;
1617
+ case 'status':
1618
+ await showStatus();
1619
+ break;
1620
+ case 'help':
1621
+ await showHelp();
1622
+ break;
1623
+ case 'clear':
1624
+ console.clear();
1625
+ break;
1626
+ case 'exit':
1627
+ console.clear();
1628
+ printBanner();
1629
+ console.log(c(' 👋 感谢使用 Code Optimizer Agent!', 'green'));
1630
+ console.log(c(' 再见!', 'green'));
1631
+ console.log();
1632
+ process.exit(0);
1633
+ default:
1634
+ break;
1635
+ }
1636
+ }
1637
+ }
1638
+
1639
+ module.exports = { startCLI };
1640
+
1641
+ if (require.main === module) {
1642
+ startCLI();
1643
+ }