winter-super-cli 2026.6.5 → 2026.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/cli/repl.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * ?? WINTER REPL ??
2
+ * WINTER REPL
3
3
  * Claude Code / Codex style interactive REPL
4
4
  */
5
5
 
@@ -30,7 +30,7 @@ import {
30
30
  buildPromptToolResultWithTokenJuice,
31
31
  } from './tool-runtime.js';
32
32
  import { TokenJuice } from '../context/token-juice.js';
33
- import { classifyModelTier, isSmallModel } from '../ai/model-capabilities.js';
33
+ import { classifyModelTier } from '../ai/model-capabilities.js';
34
34
  import {
35
35
  addUsage as mergeUsage,
36
36
  buildToolCallSignature as buildToolCallSignatureText,
@@ -77,8 +77,8 @@ export class WinterREPL {
77
77
  this.ai = new AIProviderManager(this.config);
78
78
  this.tools = new ToolExecutor(this);
79
79
  this.projectPath = options.projectPath || process.cwd();
80
- this.sessionId = options.sessionId || null; // Nh?n sessionId t? bin
81
- this.version = options.version || '1.0.0'; // Nh?n version t? bin
80
+ this.sessionId = options.sessionId || null; // Nhận sessionId từ bin
81
+ this.version = options.version || '1.0.0'; // Nhận version từ bin
82
82
  this.running = true;
83
83
  this.history = [];
84
84
  this.maxHistory = 500;
@@ -118,7 +118,7 @@ export class WinterREPL {
118
118
 
119
119
  async initCodebaseSearch() {
120
120
  if (this.codebaseSearcher) return;
121
- this.codebaseSearcher = new CodebaseSearch({ projectPath: this.projectPath });
121
+ this.codebaseSearcher = new CodebaseSearch({ projectPath: this.projectPath, enableCodeGraph: true });
122
122
  await this.codebaseSearcher.init();
123
123
  this.atContext = new AtContextResolver({
124
124
  projectPath: this.projectPath,
@@ -166,7 +166,7 @@ export class WinterREPL {
166
166
  await this.codebaseSearcher.clear();
167
167
  }
168
168
  const stats = await this.codebaseSearcher.reindex();
169
- console.log(`${colors.green}? Codebase indexed:${colors.reset}`);
169
+ console.log(`${colors.green} Codebase indexed:${colors.reset}`);
170
170
  console.log(` ${colors.dim}Files: ${stats.totalFiles}, Chunks: ${stats.totalChunks}, Indexed: ${stats.indexedFiles}, Skipped: ${stats.skipped}${colors.reset}`);
171
171
  }
172
172
 
@@ -229,11 +229,11 @@ export class WinterREPL {
229
229
  if (before.totalChunks > 0) return before;
230
230
 
231
231
  if (verbose) {
232
- console.log(`${colors.dim}? Indexing codebase for semantic search...${colors.reset}`);
232
+ console.log(`${colors.dim}Indexing codebase for semantic search...${colors.reset}`);
233
233
  }
234
234
  const indexedStats = await this.codebaseSearcher.reindex();
235
235
  if (verbose) {
236
- console.log(`${colors.green}? Codebase indexed: ${indexedStats.totalFiles} files, ${indexedStats.totalChunks} chunks${colors.reset}`);
236
+ console.log(`${colors.green} Codebase indexed: ${indexedStats.totalFiles} files, ${indexedStats.totalChunks} chunks${colors.reset}`);
237
237
  }
238
238
  return this.codebaseSearcher.indexer.getStats();
239
239
  }
@@ -278,7 +278,7 @@ export class WinterREPL {
278
278
  }
279
279
  }
280
280
 
281
- return this.compactText(lines.join('\n'), this.shouldUseCompactPrompt() ? 1800 : 3200, 'codebase context');
281
+ return this.compactText(lines.join('\n'), 4200, 'codebase context');
282
282
  } catch (error) {
283
283
  return `[Codebase Index]\nUnavailable: ${error.message}`;
284
284
  }
@@ -312,7 +312,7 @@ export class WinterREPL {
312
312
  if (ans.trim().toLowerCase() === 'y') {
313
313
  const ok = await this.diffView.restoreFromBackup(latest.backup, latest.original);
314
314
  if (ok) {
315
- console.log(`${colors.green}? Restored ${latest.original} from backup${colors.reset}`);
315
+ console.log(`${colors.green} Restored ${latest.original} from backup${colors.reset}`);
316
316
  } else {
317
317
  console.log(`${colors.red}Failed to restore${colors.reset}`);
318
318
  }
@@ -456,7 +456,7 @@ export class WinterREPL {
456
456
  });
457
457
  await this.session.replaceMemory('[Project Anchor]', `Current project is ${this.projectPath}. Treat this path as the canonical working directory for the session.`, 'info');
458
458
 
459
- // T? ??ng ??c v? ghi nh? m?t s? t?i nguy?n c?c b? (an to?n): ch? n?p file ho?c README trong th? m?c
459
+ // Tự động đọc ghi nhớ một số tài nguyên cục bộ an toàn.
460
460
  const fsPromises = await import('fs/promises');
461
461
  const resourcePaths = this.getResourcePaths();
462
462
  const autoLoadTargets = [resourcePaths.agents, resourcePaths.designs, resourcePaths.karpathy, resourcePaths.pageAgent];
@@ -469,9 +469,9 @@ export class WinterREPL {
469
469
  if (stat.isFile()) {
470
470
  const content = await fsPromises.readFile(targetPath, 'utf8');
471
471
  const fileName = path.basename(targetPath);
472
- const memoryKey = `[T? ??ng ghi nh? file ${fileName}]`;
472
+ const memoryKey = `[Tự động ghi nhớ file ${fileName}]`;
473
473
  await this.session.replaceMemory(memoryKey, content);
474
- console.log(`${colors.dim}? ?? t? ??ng n?p v? ghi nh? file ${fileName}${colors.reset}`);
474
+ console.log(`${colors.dim} Đã tự động nạp ghi nhớ file ${fileName}${colors.reset}`);
475
475
  continue;
476
476
  }
477
477
 
@@ -485,9 +485,9 @@ export class WinterREPL {
485
485
  const cstat = await fsPromises.stat(p).catch(() => null);
486
486
  if (cstat && cstat.isFile()) {
487
487
  const content = await fsPromises.readFile(p, 'utf8');
488
- const memoryKey = `[T? ??ng ghi nh? file ${path.basename(targetPath)}/${c}]`;
488
+ const memoryKey = `[Tự động ghi nhớ file ${path.basename(targetPath)}/${c}]`;
489
489
  await this.session.replaceMemory(memoryKey, content);
490
- console.log(`${colors.dim}? ?? t? ??ng n?p v? ghi nh? ${path.basename(targetPath)}/${c}${colors.reset}`);
490
+ console.log(`${colors.dim} Đã tự động nạp ghi nhớ ${path.basename(targetPath)}/${c}${colors.reset}`);
491
491
  loaded = true;
492
492
  break;
493
493
  }
@@ -505,63 +505,63 @@ export class WinterREPL {
505
505
  }
506
506
  }
507
507
 
508
- // N?p c?c file quy t?c d? ?n theo th? t? ?u ti?n.
508
+ // Nạp các file quy tắc dự án theo thứ tự ưu tiên.
509
509
  const projectInstructionFiles = await this.readProjectInstructionFiles();
510
510
  try {
511
511
  if (projectInstructionFiles.length > 0) {
512
512
  for (const file of projectInstructionFiles) {
513
- const memoryKey = `[Quy t?c d? ?n t? ${file.relativePath}]`;
513
+ const memoryKey = `[Quy tắc dự án từ ${file.relativePath}]`;
514
514
  await this.session.replaceMemory(memoryKey, file.content);
515
- console.log(`${colors.dim}? ?? n?p quy t?c d? ?n t? ${file.relativePath}${colors.reset}`);
515
+ console.log(`${colors.dim} Đã nạp quy tắc dự án từ ${file.relativePath}${colors.reset}`);
516
516
  }
517
517
  }
518
518
  } catch (e) {
519
- // N?u kh?ng c?, t? ??ng t?o file m?u!
519
+ // Nếu không có, tự động tạo file mẫu.
520
520
  const template = `# Winter Project Rules
521
521
 
522
- ## ?? Project Overview
523
- - **Name**: [T?n d? ?n]
524
- - **Description**: [M? t? ng?n v? d? ?n]
522
+ ## Project Overview
523
+ - **Name**: [Tên dự án]
524
+ - **Description**: [ tả ngắn về dự án]
525
525
 
526
- ## ?? Tech Stack
526
+ ## Tech Stack
527
527
  - **Languages**: JavaScript / TypeScript
528
528
  - **Runtime**: Node.js
529
- - **Frameworks**: [T? ?i?n n?u c?, VD: Express, React...]
529
+ - **Frameworks**: [Tự điền nếu có, VD: Express, React...]
530
530
 
531
- ## ?? AI Behavior & Coding Guidelines
531
+ ## AI Behavior & Coding Guidelines
532
532
 
533
- ### 1. Nguy?n T?c Code (Coding Standards)
534
- - Lu?n ?u ti?n vi?t code s?ch (Clean Code), d? ??c v? d? b?o tr?.
535
- - S? d?ng ES Modules (\`import/export\`) thay v? CommonJS (\`require\`) tr? khi c? l? do ??c bi?t.
536
- - Gi? nguy?n c?c comment v? JSDoc hi?n c? trong file tr? khi ???c y?u c?u s?a.
533
+ ### 1. Nguyên tắc Code (Coding Standards)
534
+ - Luôn ưu tiên viết code sạch (Clean Code), dễ đọc dễ bảo trì.
535
+ - Sử dụng ES Modules (\`import/export\`) thay CommonJS (\`require\`) trừ khi do đặc biệt.
536
+ - Giữ nguyên các comment JSDoc hiện trong file trừ khi được yêu cầu sửa.
537
537
 
538
- ### 2. T??ng T?c V?i Ng??i D?ng (User Interaction)
539
- - Lu?n gi?i th?ch NG?N G?N l? do th?c hi?n thay ??i tr??c khi s?a file.
540
- - Khi g?p l?i, h?y ?? xu?t gi?i ph?p thay v? ch? b?o l?i.
541
- - KH?NG t? ti?n x?a code c? c?a user tr? khi ch?c ch?n n? kh?ng c?n d?ng ho?c ???c y?u c?u.
538
+ ### 2. Tương tác với người dùng (User Interaction)
539
+ - Luôn giải thích ngắn gọn do thực hiện thay đổi trước khi sửa file.
540
+ - Khi gặp lỗi, hãy đề xuất giải pháp thay chỉ báo lỗi.
541
+ - Không tự tiện xóa code của user trừ khi chắc chắn không còn dùng hoặc được yêu cầu.
542
542
 
543
543
  ### 3. Git & Commits
544
- - Vi?t commit message theo chu?n Conventional Commits (VD: \`feat:\`, \`fix:\`, \`docs:\`).
545
- - Lu?n ki?m tra \`git status\` tr??c khi th?c hi?n thay ??i l?n.
544
+ - Viết commit message theo chuẩn Conventional Commits (VD: \`feat:\`, \`fix:\`, \`docs:\`).
545
+ - Luôn kiểm tra \`git status\` trước khi thực hiện thay đổi lớn.
546
546
 
547
- ### 4. X? L? File (File Operations)
548
- - Ch? s?a nh?ng d?ng c?n thi?t, tr?nh vi?t l?i to?n b? file n?u kh?ng c?n.
549
- - Lu?n ??m b?o file kh?ng b? l?i c? ph?p sau khi s?a.
547
+ ### 4. Xử File (File Operations)
548
+ - Chỉ sửa những dòng cần thiết, tránh viết lại toàn bộ file nếu không cần.
549
+ - Luôn đảm bảo file không bị lỗi pháp sau khi sửa.
550
550
  `;
551
551
  try {
552
552
  const projectWinterMd = path.join(this.projectPath, 'winter.md');
553
553
  await fsPromises.writeFile(projectWinterMd, template, 'utf8');
554
- console.log(`\n${colors.green}? ?? t? ??ng t?o file winter.md m?u cho d? ?n m?i!${colors.reset}`);
555
- console.log(`${colors.dim}B?n c? th? ch?nh s?a file n?y ?? d?y AI c?c quy t?c ri?ng c?a d? ?n.${colors.reset}\n`);
554
+ console.log(`\n${colors.green} Đã tự động tạo file winter.md mẫu cho dự án mới!${colors.reset}`);
555
+ console.log(`${colors.dim}Bạn thể chỉnh sửa file này để dạy AI các quy tắc riêng của dự án.${colors.reset}\n`);
556
556
 
557
- // N?p lu?n v?o memory
558
- await this.session.replaceMemory(`[Quy t?c d? ?n t? winter.md]`, template);
557
+ // Nạp luôn vào memory.
558
+ await this.session.replaceMemory(`[Quy tắc dự án từ winter.md]`, template);
559
559
  } catch (err) {
560
- // B? qua n?u kh?ng ghi ???c file
560
+ // Bỏ qua nếu không ghi được file.
561
561
  }
562
562
  }
563
563
 
564
- // ?? T? ??ng t?o design.md, skill.md, rule.md n?u ch?a c? ??????????????
564
+ // Tự động tạo design.md, skill.md, rule.md nếu chưa có.
565
565
  const autoCreateDocs = await buildProjectDocs({
566
566
  projectPath: this.projectPath,
567
567
  resourcePaths: this.getResourcePaths(),
@@ -577,17 +577,17 @@ export class WinterREPL {
577
577
  if (!isWinterGeneratedProjectDoc(existing)) continue;
578
578
 
579
579
  await fsPromises.writeFile(filePath, doc.content, 'utf8');
580
- console.log(`${colors.green}? ?? n?ng c?p file ${doc.filename} t? local resources.${colors.reset}`);
581
- const memoryKey = `[Quy t?c d? ?n t? ${doc.filename}]`;
580
+ console.log(`${colors.green} Đã nâng cấp file ${doc.filename} từ local resources.${colors.reset}`);
581
+ const memoryKey = `[Quy tắc dự án từ ${doc.filename}]`;
582
582
  await this.session.replaceMemory(memoryKey, doc.content);
583
583
  } catch {
584
584
  try {
585
585
  await fsPromises.writeFile(filePath, doc.content, 'utf8');
586
- console.log(`${colors.green}? ?? t? ??ng t?o file ${doc.filename} t? local resources!${colors.reset}`);
587
- const memoryKey = `[Quy t?c d? ?n t? ${doc.filename}]`;
586
+ console.log(`${colors.green} Đã tự động tạo file ${doc.filename} từ local resources!${colors.reset}`);
587
+ const memoryKey = `[Quy tắc dự án từ ${doc.filename}]`;
588
588
  await this.session.replaceMemory(memoryKey, doc.content);
589
589
  } catch (err) {
590
- // B? qua n?u kh?ng t?o ???c
590
+ // Bỏ qua nếu không tạo được.
591
591
  }
592
592
  }
593
593
  }
@@ -619,13 +619,13 @@ export class WinterREPL {
619
619
  this.showStatus();
620
620
  }
621
621
 
622
- // Hi?n th? l?ch s? chat n?u ?ang load l?i session c?, nh?ng ch? replay b?n t?m t?t.
622
+ // Hiển thị lịch sử chat nếu đang load lại session cũ, nhưng chỉ replay bản tóm tắt.
623
623
  const sessionHistory = this.session.getHistory(4);
624
624
  if (sessionHistory.length > 0) {
625
625
  const columns = process.stdout.columns || 80;
626
626
  const W = Math.max(60, Math.min(Math.floor(columns * 0.95), 100));
627
- const titleStr = ' L?ch s? phi?n l?m vi?c ';
628
- const ruleChar = this.useUnicodeUi ? '?' : '-';
627
+ const titleStr = ' Lịch sử phiên làm việc ';
628
+ const ruleChar = this.useUnicodeUi ? '' : '-';
629
629
  const sideLine = ruleChar.repeat(Math.max(0, Math.floor((W - titleStr.length) / 2)));
630
630
  const bottomLine = ruleChar.repeat(W);
631
631
 
@@ -633,7 +633,7 @@ export class WinterREPL {
633
633
  for (const msg of sessionHistory) {
634
634
  const text = this.formatStartupHistoryEntry(msg.content);
635
635
  if (msg.role === 'user') {
636
- console.log(`\n${colors.cyan}B?n:${colors.reset} ${text}`);
636
+ console.log(`\n${colors.cyan}Bạn:${colors.reset} ${text}`);
637
637
  } else if (msg.role === 'assistant') {
638
638
  console.log(`\n${colors.bright}${colors.magenta}Winter:${colors.reset} ${text}`);
639
639
  }
@@ -645,12 +645,12 @@ export class WinterREPL {
645
645
  this.rl = readline.createInterface({
646
646
  input: process.stdin,
647
647
  output: process.stdout,
648
- prompt: `${colors.bright}${colors.cyan}${this.useUnicodeUi ? 'winter??:' : 'winter>'} ${colors.reset}`,
648
+ prompt: `${colors.bright}${colors.cyan}winter > ${colors.reset}`,
649
649
  completer: this.completer.bind(this),
650
650
  });
651
651
  this.inputController.installSlashSuggestions();
652
652
 
653
- // B?t s? ki?n Ctrl+C ?? in ra l?nh ti?p t?c session
653
+ // Bắt sự kiện Ctrl+C để in ra lệnh tiếp tục session.
654
654
  this.rl.on('SIGINT', () => {
655
655
  console.log(`\n\n${colors.cyan}Cảm ơn đã sử dụng Winter!${colors.reset}`);
656
656
  console.log(`${colors.yellow}Tiếp tục phiên làm việc:${colors.reset}`);
@@ -658,7 +658,7 @@ export class WinterREPL {
658
658
  process.exit(0);
659
659
  });
660
660
 
661
- // Hi?n th? prompt l?n ??u ti?n ngay khi kh?i ??ng xong
661
+ // Hiển thị prompt lần đầu tiên ngay khi khởi động xong.
662
662
  this.showInputPrompt();
663
663
 
664
664
  this.rl.on('line', (line) => {
@@ -695,7 +695,7 @@ export class WinterREPL {
695
695
  if (!this.useUnicodeUi) {
696
696
  text = text
697
697
  .replace(/\p{Extended_Pictographic}/gu, '')
698
- .replace(/[????????]/g, '-')
698
+ .replace(/[─━—–]/g, '-')
699
699
  .replace(/\s+/g, ' ')
700
700
  .trim();
701
701
  }
@@ -917,7 +917,7 @@ export class WinterREPL {
917
917
  async handleInput(input) {
918
918
  if (this.isProcessing) {
919
919
  const pos = this.taskQueue.length + 1;
920
- console.log(`${colors.magenta}?${colors.reset} ${colors.dim}?? x?p h?ng ch? (v? tr? #${pos})${colors.reset}`);
920
+ console.log(`${colors.magenta}•${colors.reset} ${colors.dim}Đã xếp hàng chờ (vị trí #${pos})${colors.reset}`);
921
921
  this.taskQueue.push(input);
922
922
  return;
923
923
  }
@@ -998,9 +998,9 @@ export class WinterREPL {
998
998
  }
999
999
  } catch (error) {
1000
1000
  if (error.message === 'AbortError') {
1001
- console.log(colors.red + '\n?? h?y c?ng vi?c hi?n t?i.' + colors.reset);
1001
+ console.log(colors.red + '\nĐã hủy công việc hiện tại.' + colors.reset);
1002
1002
  } else {
1003
- console.log(colors.red + '\nL?i: ' + error.message + colors.reset);
1003
+ console.log(colors.red + '\nLỗi: ' + error.message + colors.reset);
1004
1004
  }
1005
1005
  } finally {
1006
1006
  this.isProcessing = false;
@@ -1063,13 +1063,13 @@ export class WinterREPL {
1063
1063
 
1064
1064
  let printedLines = 0;
1065
1065
  const render = () => {
1066
- // X?a nh?ng d?ng ?? in tr??c ??
1066
+ // Xóa những dòng đã in trước đó.
1067
1067
  if (printedLines > 0) {
1068
1068
  process.stdout.write('\x1b[' + printedLines + 'A\x1b[J');
1069
1069
  }
1070
1070
 
1071
1071
  let out = `\n${title}\n`;
1072
- out += `D?ng m?i t?n (?/?) ?? di chuy?n, [Space] ?? ch?n/b? ch?n, [Enter] ?? x?c nh?n\n\n`;
1072
+ out += `Dùng mũi tên (↑/↓) để di chuyển, [Space] để chọn/bỏ chọn, [Enter] để xác nhận\n\n`;
1073
1073
 
1074
1074
  for (let i = 0; i < items.length; i++) {
1075
1075
  const isHover = i === cursor;
@@ -1119,11 +1119,11 @@ export class WinterREPL {
1119
1119
  }
1120
1120
 
1121
1121
  async generateInteractivePlan(task) {
1122
- this.spinner = new Spinner('?ang ph?n t?ch v? chia nh? y?u c?u...');
1122
+ this.spinner = new Spinner('Đang phân tích chia nhỏ yêu cầu...');
1123
1123
  this.spinner.start();
1124
1124
 
1125
1125
  const messages = [
1126
- { role: 'system', content: 'B?n l? chuy?n gia l?p k? ho?ch. H?y chia nh? y?u c?u c?a ng??i d?ng th?nh c?c b??c c? th?, h?nh ??ng ???c, r?t ng?n g?n (d??i 15 ch? m?i b??c). CH? TR? V? M?T M?NG JSON C?C CHU?I, KH?NG GI?I TH?CH G? TH?M. V? d?: ["T?o file index.html", "Th?m CSS styling", "Vi?t script.js"]' },
1126
+ { role: 'system', content: 'Bạn chuyên gia lập kế hoạch. Hãy chia nhỏ yêu cầu của người dùng thành các bước cụ thể, hành động được, rất ngắn gọn (dưới 15 chữ mỗi bước). Chỉ trả về một mảng JSON các chuỗi, không giải thích thêm. dụ: ["Tạo file index.html", "Thêm CSS styling", "Viết script.js"]' },
1127
1127
  { role: 'user', content: task }
1128
1128
  ];
1129
1129
 
@@ -1146,40 +1146,40 @@ export class WinterREPL {
1146
1146
  }
1147
1147
 
1148
1148
  if (!Array.isArray(items) || items.length === 0) {
1149
- console.log(`\x1b[33mKh?ng th? parse k? ho?ch. AI ph?n h?i: ${text}\x1b[0m`);
1149
+ console.log(`\x1b[33mKhông thể parse kế hoạch. AI phản hồi: ${text}\x1b[0m`);
1150
1150
  return;
1151
1151
  }
1152
1152
 
1153
- const selectedSteps = await this.showInteractiveChecklist('K? HO?CH TH?C HI?N:', items);
1153
+ const selectedSteps = await this.showInteractiveChecklist('KẾ HOẠCH THỰC HIỆN:', items);
1154
1154
 
1155
1155
  if (selectedSteps.length > 0) {
1156
1156
  for (const step of selectedSteps) {
1157
1157
  await this.session.createPlan(step, task);
1158
1158
  }
1159
- console.log(`\x1b[32m? ?? th?m ${selectedSteps.length} c?ng vi?c v?o Memory (g? /plans ?? xem).\x1b[0m`);
1159
+ console.log(`\x1b[32m Đã thêm ${selectedSteps.length} công việc vào Memory ( /plans để xem).\x1b[0m`);
1160
1160
 
1161
- this.rl.question(`\n\x1b[36mB?n c? mu?n AI b?t tay l?m c?ng vi?c ??U TI?N ngay b?y gi? kh?ng? [y/N]: \x1b[0m`, async (answer) => {
1161
+ this.rl.question(`\n\x1b[36mBạn muốn AI bắt tay làm công việc ĐẦU TIÊN ngay bây giờ không? [y/N]: \x1b[0m`, async (answer) => {
1162
1162
  if (answer.toLowerCase() === 'y') {
1163
- await this.chat(`H?y b?t ??u th?c hi?n b??c ??u ti?n: ${selectedSteps[0]}`);
1163
+ await this.chat(`Hãy bắt đầu thực hiện bước đầu tiên: ${selectedSteps[0]}`);
1164
1164
  }
1165
1165
  });
1166
1166
  } else {
1167
- console.log(`\n\x1b[2m?? hu? k? ho?ch.\x1b[0m`);
1167
+ console.log(`\n\x1b[2mĐã huỷ kế hoạch.\x1b[0m`);
1168
1168
  }
1169
1169
  } catch (e) {
1170
1170
  if (this.spinner) this.spinner.stop();
1171
- console.log(`\x1b[31mL?i: ${e.message}\x1b[0m`);
1171
+ console.log(`\x1b[31mLỗi: ${e.message}\x1b[0m`);
1172
1172
  }
1173
1173
  }
1174
1174
 
1175
1175
 
1176
1176
  async runAutoHealing(task) {
1177
- console.log(`\n\x1b[35m[ TDD AUTO-HEALING MODE ]\x1b[0m K?ch ho?t v?ng l?p ch?a l?nh l?i t? ??ng.`);
1177
+ console.log(`\n\x1b[35m[ TDD AUTO-HEALING MODE ]\x1b[0m Kích hoạt vòng lặp tự sửa lỗi.`);
1178
1178
 
1179
- // T?ng gi?i h?n loop l?n 15 ?? cho ph?p AI t? s?a l?i nhi?u l?n
1179
+ // Tăng giới hạn loop lên 15 để cho phép AI tự sửa lỗi nhiều lần.
1180
1180
  const originalRequestAssistantTurn = this.requestAssistantTurn;
1181
1181
 
1182
- // Ch?n prompt ??c bi?t ?p AI ph?i verify
1182
+ // Chèn prompt đặc biệt ép AI phải verify.
1183
1183
  const verifyCommands = await this.inferVerificationCommands(task);
1184
1184
  const autoPrompt = `TASK: ${task}
1185
1185
 
@@ -1195,7 +1195,7 @@ CRITICAL DEBUG/AGENT RULES:
1195
1195
  }
1196
1196
 
1197
1197
  async runAutoCommit(context = '') {
1198
- console.log(`\n${colors.cyan}? T?o commit message t? ??ng...${colors.reset}`);
1198
+ console.log(`\n${colors.cyan} Tạo commit message tự động...${colors.reset}`);
1199
1199
  const diffResult = await this.tools.execute('Bash', { command: 'git diff --cached' }, { cwd: this.projectPath });
1200
1200
 
1201
1201
  let diff = diffResult.stdout;
@@ -1207,14 +1207,14 @@ CRITICAL DEBUG/AGENT RULES:
1207
1207
  }
1208
1208
 
1209
1209
  if (!diff || diff.trim() === '') {
1210
- console.log(`${colors.yellow}? Kh?ng c? thay ??i n?o ?? commit.${colors.reset}`);
1210
+ console.log(`${colors.yellow}Không thay đổi nào để commit.${colors.reset}`);
1211
1211
  if (this.running && !this.readlineClosed) this.rl.prompt();
1212
1212
  return;
1213
1213
  }
1214
1214
 
1215
- const prompt = `B?n l? chuy?n gia Git. H?y vi?t M?T commit message duy nh?t (kh?ng gi?i th?ch th?m) cho nh?ng thay ??i sau ??y. D?ng format chu?n Conventional Commits (feat/fix/chore/refactor: ...). ${context ? `\nNg? c?nh: ${context}` : ''}\n\nDiff:\n${diff.slice(0, 6000)}`;
1215
+ const prompt = `Bạn chuyên gia Git. Hãy viết MỘT commit message duy nhất (không giải thích thêm) cho những thay đổi sau đây. Dùng format chuẩn Conventional Commits (feat/fix/chore/refactor: ...). ${context ? `\nNgữ cảnh: ${context}` : ''}\n\nDiff:\n${diff.slice(0, 6000)}`;
1216
1216
 
1217
- this.spinner = new Spinner('?ang sinh commit message...');
1217
+ this.spinner = new Spinner('Đang sinh commit message...');
1218
1218
  this.spinner.start();
1219
1219
  try {
1220
1220
  const response = await this.ai.sendRequest([{ role: 'user', content: prompt }], {
@@ -1222,19 +1222,19 @@ CRITICAL DEBUG/AGENT RULES:
1222
1222
  });
1223
1223
  if (this.spinner) this.spinner.stop();
1224
1224
 
1225
- const message = response.choices?.[0]?.message?.content?.trim().replace(/^["']|["']$/g, '') || 'C?p nh?t m? ngu?n';
1225
+ const message = response.choices?.[0]?.message?.content?.trim().replace(/^["']|["']$/g, '') || 'Cập nhật nguồn';
1226
1226
 
1227
- console.log(`\n${colors.green}?? xu?t commit message:${colors.reset}`);
1227
+ console.log(`\n${colors.green}Đề xuất commit message:${colors.reset}`);
1228
1228
  console.log(`${colors.bright}${message}${colors.reset}\n`);
1229
1229
 
1230
- this.rl.question(`${colors.yellow}B?n c? mu?n commit v?i message n?y kh?ng? [y/N/e (t? s?a)]: ${colors.reset}`, async (ans) => {
1230
+ this.rl.question(`${colors.yellow}Bạn muốn commit với message này không? [y/N/e (tự sửa)]: ${colors.reset}`, async (ans) => {
1231
1231
  const choice = ans.trim().toLowerCase();
1232
1232
  if (choice === 'y') {
1233
1233
  if (!isStaged) await this.tools.execute('Bash', { command: 'git add .' }, { cwd: this.projectPath });
1234
1234
  const res = await this.tools.execute('Bash', { command: `git commit -m "${message.replace(/"/g, '\\"')}"` }, { cwd: this.projectPath });
1235
1235
  console.log(res.stdout);
1236
1236
  } else if (choice === 'e') {
1237
- this.rl.question(`${colors.cyan}Nh?p commit message: ${colors.reset}`, async (customMsg) => {
1237
+ this.rl.question(`${colors.cyan}Nhập commit message: ${colors.reset}`, async (customMsg) => {
1238
1238
  if (customMsg.trim()) {
1239
1239
  if (!isStaged) await this.tools.execute('Bash', { command: 'git add .' }, { cwd: this.projectPath });
1240
1240
  const res = await this.tools.execute('Bash', { command: `git commit -m "${customMsg.replace(/"/g, '\\"')}"` }, { cwd: this.projectPath });
@@ -1244,28 +1244,28 @@ CRITICAL DEBUG/AGENT RULES:
1244
1244
  });
1245
1245
  return;
1246
1246
  } else {
1247
- console.log(`${colors.dim}?? hu? commit.${colors.reset}`);
1247
+ console.log(`${colors.dim}Đã huỷ commit.${colors.reset}`);
1248
1248
  }
1249
1249
  if (this.running && !this.readlineClosed) this.rl.prompt();
1250
1250
  });
1251
1251
  } catch (e) {
1252
1252
  if (this.spinner) this.spinner.stop();
1253
- console.log(`${colors.red}L?i: ${e.message}${colors.reset}`);
1253
+ console.log(`${colors.red}Lỗi: ${e.message}${colors.reset}`);
1254
1254
  if (this.running && !this.readlineClosed) this.rl.prompt();
1255
1255
  }
1256
1256
  }
1257
1257
 
1258
1258
  async runCodeReview(context = '') {
1259
- console.log(`\n${colors.cyan}? AI ?ang soi code c?a b?n...${colors.reset}`);
1259
+ console.log(`\n${colors.cyan}AI đang soi code của bạn...${colors.reset}`);
1260
1260
  const diffResult = await this.tools.execute('Bash', { command: 'git diff HEAD' }, { cwd: this.projectPath });
1261
1261
  const diff = diffResult.stdout;
1262
1262
 
1263
1263
  if (!diff || diff.trim() === '') {
1264
- console.log(`${colors.yellow}? Kh?ng c? thay ??i n?o ?? review.${colors.reset}`);
1264
+ console.log(`${colors.yellow}Không thay đổi nào để review.${colors.reset}`);
1265
1265
  return;
1266
1266
  }
1267
1267
 
1268
- const prompt = `H?y ??ng vai m?t Senior Developer kh? t?nh. Review nhanh c?c thay ??i sau ??y. Ch? ra bug (n?u c?), v?n ?? b?o m?t, ho?c nh?ng ch? c?n clean code. Kh?ng c?n khen ng?i, h?y n?i th?ng v?o v?n ??. Tr?nh b?y d?ng Markdown ??p m?t. \n\n${context ? `Ng? c?nh: ${context}\n` : ''}Diff:\n${diff.slice(0, 8000)}`;
1268
+ const prompt = `Hãy đóng vai một Senior Developer khó tính. Review nhanh các thay đổi sau đây. Chỉ ra bug nếu có, vấn đề bảo mật, hoặc những chỗ cần clean code. Không cần khen ngợi, hãy nói thẳng vào vấn đề. Trình bày dạng Markdown đẹp mắt.\n\n${context ? `Ngữ cảnh: ${context}\n` : ''}Diff:\n${diff.slice(0, 8000)}`;
1269
1269
 
1270
1270
  await this.chat(prompt);
1271
1271
  }
@@ -1286,39 +1286,39 @@ CRITICAL DEBUG/AGENT RULES:
1286
1286
  };
1287
1287
 
1288
1288
  const body = [
1289
- `${c.bright}${c.cyan}${this.useUnicodeUi ? '? ' : ''}WINTER COMMANDS${c.reset}`,
1289
+ `${c.bright}${c.cyan}${this.useUnicodeUi ? ' ' : ''}WINTER COMMANDS${c.reset}`,
1290
1290
  `${c.dim}@file context | @Agent task | !cmd bash | /theme:toggle${c.reset}`,
1291
1291
  '',
1292
- `${c.bright}D? ?n & Phi?n l?m vi?c${c.reset}`,
1293
- row(`${c.yellow}/pwd${c.reset} Th? m?c hi?n t?i`, `${c.yellow}/session${c.reset} Phi?n l?m vi?c`),
1294
- row(`${c.yellow}/cd${c.reset} ??i th? m?c`, `${c.yellow}/clear${c.reset} X?a m?n h?nh`),
1295
- row(`${c.yellow}/config${c.reset} Xem c?u h?nh`, `${c.yellow}/exit${c.reset} Tho?t`),
1292
+ `${c.bright}Dự án & Phiên làm việc${c.reset}`,
1293
+ row(`${c.yellow}/pwd${c.reset} Thư mục hiện tại`, `${c.yellow}/session${c.reset} Phiên làm việc`),
1294
+ row(`${c.yellow}/cd${c.reset} Đổi thư mục`, `${c.yellow}/clear${c.reset} Xóa màn hình`),
1295
+ row(`${c.yellow}/config${c.reset} Xem cấu hình`, `${c.yellow}/exit${c.reset} Thoát`),
1296
1296
  '',
1297
- `${c.bright}AI & C?ng c?${c.reset}`,
1298
- row(`${c.yellow}/auto${c.reset} TDD t? s?a l?i`, `${c.yellow}/debug${c.reset} Auto debug l?i`),
1299
- row(`${c.yellow}/doctor${c.reset} Ki?m tra tool-call`, `${c.yellow}/agent${c.reset} Ch?y sub-agent`),
1300
- row(`${c.yellow}/swe${c.reset} SWE workflow`, `${c.yellow}/plan${c.reset} L?p k? ho?ch`),
1301
- row(`${c.yellow}/read${c.reset} ??c file`, `${c.yellow}/write${c.reset} Ghi file`),
1302
- row(`${c.yellow}/bash${c.reset} Ch?y l?nh terminal`, `${c.yellow}/grep${c.reset} T?m trong file`),
1303
- row(`${c.yellow}/glob${c.reset} T?m file theo pattern`, `${c.yellow}/image${c.reset} ?nh/file/clipboard`),
1304
- row(`${c.yellow}/paste${c.reset} D?n text/?nh clipboard`, `${c.yellow}/composer${c.reset} Multi-file edit`),
1305
- row(`${c.yellow}/complete${c.reset} G?i ? code`, `${c.yellow}/search${c.reset} T?m ki?m code`),
1306
- row(`${c.yellow}/browse${c.reset} M? URL trong tr?nh duy?t`, `${c.yellow}/page-agent${c.reset} GUI Agent resources`),
1307
- row(`${c.yellow}/ensemble${c.reset} Ch?y nhi?u AI`, `${c.yellow}/vote${c.reset} B?nh ch?n hay nh?t`),
1308
- row(`${c.yellow}/orchestrate${c.reset} Pipeline ?a model`, `${c.yellow}/undo${c.reset} Undo backup`),
1297
+ `${c.bright}AI & Công cụ${c.reset}`,
1298
+ row(`${c.yellow}/auto${c.reset} TDD tự sửa lỗi`, `${c.yellow}/debug${c.reset} Auto debug lỗi`),
1299
+ row(`${c.yellow}/doctor${c.reset} Kiểm tra tool-call`, `${c.yellow}/agent${c.reset} Chạy sub-agent`),
1300
+ row(`${c.yellow}/swe${c.reset} SWE workflow`, `${c.yellow}/plan${c.reset} Lập kế hoạch`),
1301
+ row(`${c.yellow}/read${c.reset} Đọc file`, `${c.yellow}/write${c.reset} Ghi file`),
1302
+ row(`${c.yellow}/bash${c.reset} Chạy lệnh terminal`, `${c.yellow}/grep${c.reset} Tìm trong file`),
1303
+ row(`${c.yellow}/glob${c.reset} Tìm file theo pattern`, `${c.yellow}/image${c.reset} Ảnh/file/clipboard`),
1304
+ row(`${c.yellow}/paste${c.reset} Dán text/ảnh clipboard`, `${c.yellow}/composer${c.reset} Multi-file edit`),
1305
+ row(`${c.yellow}/complete${c.reset} Gợi ý code`, `${c.yellow}/search${c.reset} Tìm kiếm code`),
1306
+ row(`${c.yellow}/browse${c.reset} Mở URL trong trình duyệt`, `${c.yellow}/page-agent${c.reset} GUI Agent resources`),
1307
+ row(`${c.yellow}/ensemble${c.reset} Chạy nhiều AI`, `${c.yellow}/vote${c.reset} Bình chọn hay nhất`),
1308
+ row(`${c.yellow}/orchestrate${c.reset} Pipeline đa model`, `${c.yellow}/undo${c.reset} Undo backup`),
1309
1309
  row(`${c.yellow}/ecc${c.reset} ECC resource browser`, `${c.yellow}/codex${c.reset} Codex resources`),
1310
1310
  '',
1311
1311
  `${c.bright}Git Auto-Pilot${c.reset}`,
1312
- row(`${c.yellow}/commit${c.reset} AI t? vi?t commit`, `${c.yellow}/review${c.reset} AI review code thay ??i`),
1312
+ row(`${c.yellow}/commit${c.reset} AI tự viết commit`, `${c.yellow}/review${c.reset} AI review code thay đổi`),
1313
1313
  '',
1314
- `${c.bright}C?u h?nh Model${c.reset}`,
1315
- row(`${c.yellow}/provider${c.reset} ??i provider AI`, `${c.yellow}/model${c.reset} ??i model`),
1316
- row(`${c.yellow}/providers${c.reset} Danh s?ch provider`, `${c.yellow}/models${c.reset} Danh s?ch model`),
1317
- row(`${c.yellow}/mcp${c.reset} MCP server mgmt`, `${c.yellow}/permissions${c.reset} Quy?n/allowlist`),
1314
+ `${c.bright}Cấu hình Model${c.reset}`,
1315
+ row(`${c.yellow}/provider${c.reset} Đổi provider AI`, `${c.yellow}/model${c.reset} Đổi model`),
1316
+ row(`${c.yellow}/providers${c.reset} Danh sách provider`, `${c.yellow}/models${c.reset} Danh sách model`),
1317
+ row(`${c.yellow}/mcp${c.reset} MCP server mgmt`, `${c.yellow}/permissions${c.reset} Quyền/allowlist`),
1318
1318
  '',
1319
- `${c.bright}B? nh? & K? n?ng${c.reset}`,
1320
- row(`${c.yellow}/remember${c.reset} L?u v?o b? nh?`, `${c.yellow}/memories${c.reset} Xem b? nh?`),
1321
- row(`${c.yellow}/skills${c.reset} Danh s?ch k? n?ng`, `${c.yellow}/designs${c.reset} H? th?ng thi?t k?`),
1319
+ `${c.bright}Bộ nhớ & Kỹ năng${c.reset}`,
1320
+ row(`${c.yellow}/remember${c.reset} Lưu vào bộ nhớ`, `${c.yellow}/memories${c.reset} Xem bộ nhớ`),
1321
+ row(`${c.yellow}/skills${c.reset} Danh sách kỹ năng`, `${c.yellow}/designs${c.reset} Hệ thống thiết kế`),
1322
1322
  ];
1323
1323
 
1324
1324
  console.log(`
@@ -1329,14 +1329,14 @@ ${renderBox({
1329
1329
  titleColor: c.cyan,
1330
1330
  body,
1331
1331
  })}
1332
- ${c.dim}G? tin nh?n tr?c ti?p ?? chat ? ESC ?? h?y ? Prompt t? x?p h?ng ch?${c.reset}
1332
+ ${c.dim}Gửi tin nhắn trực tiếp để chat, ESC để hủy${c.reset}
1333
1333
  `);
1334
1334
  }
1335
1335
 
1336
1336
  showHelp() {
1337
1337
  console.log(`
1338
- ${colors.cyan}? WINTER COMMANDS${colors.reset}
1339
- ${colors.dim}${''.padEnd(50, '?')}${colors.reset}
1338
+ ${colors.cyan}${this.useUnicodeUi ? '❄ ' : ''}WINTER COMMANDS${colors.reset}
1339
+ ${colors.dim}${''.padEnd(50, this.useUnicodeUi ? '─' : '-')}${colors.reset}
1340
1340
 
1341
1341
  ${colors.white}Project:${colors.reset}
1342
1342
  /project, /pwd Show current project
@@ -1417,7 +1417,7 @@ ${colors.reset}
1417
1417
  }
1418
1418
  console.log(`${colors.cyan}Design Systems:${colors.reset}`);
1419
1419
  filtered.forEach(e => {
1420
- const icon = e.isDirectory ? '??' : '??';
1420
+ const icon = e.isDirectory ? '[dir]' : '[file]';
1421
1421
  console.log(` ${icon} ${e.name}`);
1422
1422
  });
1423
1423
  } catch (error) {
@@ -1437,7 +1437,7 @@ ${colors.reset}
1437
1437
  const entries = await this.listPathEntries(section.path, 50);
1438
1438
  if (entries.length > 0) {
1439
1439
  console.log(`${colors.cyan}${section.label}:${colors.reset}`);
1440
- entries.forEach(e => console.log(` ${e.isDirectory ? '??' : '??'} ${e.name}`));
1440
+ entries.forEach(e => console.log(` ${e.isDirectory ? '[dir]' : '[file]'} ${e.name}`));
1441
1441
  }
1442
1442
  } catch { }
1443
1443
  }
@@ -1451,8 +1451,8 @@ ${colors.reset}
1451
1451
  const providers = this.ai.listProviders();
1452
1452
  console.log(`${colors.cyan}Configured Models:${colors.reset}`);
1453
1453
  providers.forEach(p => {
1454
- const active = p.name === this.ai.getActiveProvider() ? ` ${colors.green}? active${colors.reset}` : '';
1455
- const status = p.ready ? `${colors.green}?${colors.reset}` : `${colors.red}?${colors.reset}`;
1454
+ const active = p.name === this.ai.getActiveProvider() ? ` ${colors.green}< active${colors.reset}` : '';
1455
+ const status = p.ready ? `${colors.green}ok${colors.reset}` : `${colors.red}off${colors.reset}`;
1456
1456
  console.log(` ${status} ${colors.bright}${p.name}${colors.reset}: ${p.model}${active}`);
1457
1457
  });
1458
1458
 
@@ -1506,7 +1506,7 @@ ${colors.reset}
1506
1506
  }
1507
1507
 
1508
1508
  shouldUseCompactPrompt() {
1509
- return isSmallModel(this.getActiveModelTier());
1509
+ return false;
1510
1510
  }
1511
1511
 
1512
1512
  selectExecutionProfile(messages = [], options = {}) {
@@ -1573,9 +1573,9 @@ ${colors.reset}
1573
1573
  const text = this.getLatestUserText(messages).toLowerCase();
1574
1574
  if (!text.trim()) return false;
1575
1575
 
1576
- const actionPattern = /\b(fix|repair|bug|debug|implement|create|write|edit|modify|update|delete|remove|refactor|run|test|build|commit|push|publish|install|check|inspect|read|scan|grep|search|change|apply|patch|sua|lam|tao|ghi|doc|xoa|chay|kiem tra|cai|them|doi|review|tim)\b/i;
1577
- const targetPattern = /\b(file|repo|project|code|src|test|build|git|npm|node|folder|directory|cli|tool|provider|model|config|readme|package\.json|du an|thu muc|tap tin|loi|chuc nang)\b|[A-Za-z]:[\\/]|\.js\b|\.ts\b|\.tsx\b|\.json\b|\.md\b/i;
1578
- const pureQuestionPattern = /^(what|why|how|when|where|is|are|can|could|should|would|tai sao|vi sao|la gi|co nen|co phai)\b/i;
1576
+ const actionPattern = /\b(fix|repair|bug|debug|implement|create|write|edit|modify|update|delete|remove|refactor|run|test|build|commit|push|publish|install|check|inspect|read|scan|grep|search|change|apply|patch|sua|lam|tao|ghi|doc|xoa|chay|kiem tra|cai|them|doi|review|tim|sửa|làm|tạo|đọc|xóa|xoá|chạy|kiểm tra|cài|thêm|đổi|tìm)\b/i;
1577
+ const targetPattern = /\b(file|repo|project|code|src|test|build|git|npm|node|folder|directory|cli|tool|provider|model|config|readme|package\.json|du an|thu muc|tap tin|loi|chuc nang|dự án|thư mục|tập tin|lỗi|chức năng)\b|[A-Za-z]:[\\/]|\.js\b|\.ts\b|\.tsx\b|\.json\b|\.md\b/i;
1578
+ const pureQuestionPattern = /^(what|why|how|when|where|is|are|can|could|should|would|tai sao|vi sao|la gi|co nen|co phai|tại sao|vì sao|là gì|có nên|có phải)\b/i;
1579
1579
 
1580
1580
  if (pureQuestionPattern.test(text) && !actionPattern.test(text)) return false;
1581
1581
  return actionPattern.test(text) && targetPattern.test(text);
@@ -1585,17 +1585,17 @@ ${colors.reset}
1585
1585
  if (!usedMutatingTools) return false;
1586
1586
  const text = String(originalMessage || '').toLowerCase();
1587
1587
  if (!text.trim()) return false;
1588
- if (/\b(skip tests?|no verify|don't verify|khong test|khong verify|bo qua test)\b/i.test(text)) {
1588
+ if (/\b(skip tests?|no verify|don't verify|khong test|khong verify|bo qua test|không test|không verify|bỏ qua test)\b/i.test(text)) {
1589
1589
  return false;
1590
1590
  }
1591
- return /\b(fix|bug|error|test|build|lint|typecheck|compile|refactor|implement|edit|write|change|patch|debug|sua|loi|kiem tra|bien dich|trien khai|lam|doi|viet)\b/i.test(text);
1591
+ return /\b(fix|bug|error|test|build|lint|typecheck|compile|refactor|implement|edit|write|change|patch|debug|sua|loi|kiem tra|bien dich|trien khai|lam|doi|viet|sửa|lỗi|kiểm tra|biên dịch|triển khai|làm|đổi|viết)\b/i.test(text);
1592
1592
  }
1593
1593
 
1594
1594
  responseNeedsToolEvidence(content = '') {
1595
1595
  const text = String(content || '').toLowerCase();
1596
1596
  if (!text.trim()) return false;
1597
1597
 
1598
- const clarification = /(?:c?n th?m|cho m?nh|vui l?ng|please provide|which file|what file|need more|clarify|kh?ng r?|ch?a r?|file n?o|th? m?c n?o)/i;
1598
+ const clarification = /(?:cần thêm|cho mình|vui lòng|please provide|which file|what file|need more|clarify|không rõ|chưa rõ|file nào|thư mục nào|c?n th?m|cho m?nh|vui l?ng|kh?ng r?|ch?a r?|file n?o|th? m?c n?o)/i;
1599
1599
  if (clarification.test(text)) return false;
1600
1600
  return true;
1601
1601
  }
@@ -1806,7 +1806,7 @@ ${colors.reset}
1806
1806
  if (key.name === 'escape' && this.isProcessing) {
1807
1807
  this.isCancelled = true;
1808
1808
  if (this.spinner) this.spinner.stop();
1809
- console.log(`\n\x1b[31m[ ?? nh?n l?nh H?Y... AI s? k?t th?c ? thao t?c ti?p theo ]\x1b[0m`);
1809
+ console.log(`\n\x1b[31m[ Đã nhận lệnh HỦY... AI sẽ kết thúc thao tác tiếp theo ]\x1b[0m`);
1810
1810
  return;
1811
1811
  }
1812
1812
 
@@ -1842,7 +1842,7 @@ ${colors.reset}
1842
1842
  })
1843
1843
  .catch((error) => {
1844
1844
  this.closeInputBox();
1845
- console.log(`\n${colors.red}? Paste image error: ${error.message}${colors.reset}\n`);
1845
+ console.log(`\n${colors.red} Paste image error: ${error.message}${colors.reset}\n`);
1846
1846
  if (this.running && !this.readlineClosed) this.showInputPrompt();
1847
1847
  });
1848
1848
  return true;
@@ -1956,13 +1956,13 @@ ${colors.reset}
1956
1956
 
1957
1957
  if (matches.length > maxDisplay) {
1958
1958
  readline.clearLine(process.stdout, 1);
1959
- process.stdout.write(` ${colors.dim}... v? ${matches.length - maxDisplay} l?nh kh?c (g? ti?p ?? l?c)${colors.reset}\n`);
1959
+ process.stdout.write(` ${colors.dim}... ${matches.length - maxDisplay} lệnh khác ( tiếp để lọc)${colors.reset}\n`);
1960
1960
  }
1961
1961
 
1962
1962
  readline.clearLine(process.stdout, 1);
1963
- process.stdout.write(`${colors.dim}?/? ch?n ? Enter/Tab d?ng ? Esc ??ng${colors.reset}\n`);
1963
+ process.stdout.write(`${colors.dim}↑/↓ chọn · Enter/Tab dùng · Esc đóng${colors.reset}\n`);
1964
1964
 
1965
- // X?a c?c d?ng th?a n?u s? l??ng d?ng m?i ?t h?n s? l??ng d?ng c?
1965
+ // Xóa các dòng thừa nếu số lượng dòng mới ít hơn số lượng dòng cũ.
1966
1966
  const currentLines = Math.min(matches.length, maxDisplay) + 3 + (matches.length > maxDisplay ? 1 : 0);
1967
1967
  if (this.slashMenu.printedLines > currentLines) {
1968
1968
  for (let i = 0; i < this.slashMenu.printedLines - currentLines; i++) {
@@ -2174,7 +2174,7 @@ ${colors.reset}
2174
2174
  body: [...body, '', `${colors.dim}${footer}${colors.reset}`],
2175
2175
  })}\n`);
2176
2176
  return;
2177
- console.log(`${colors.dim}${'?'.repeat(50)}${colors.reset}`);
2177
+ console.log(`${colors.dim}${(this.useUnicodeUi ? '─' : '-').repeat(50)}${colors.reset}`);
2178
2178
  console.log(`${colors.dim}${this.formatAnswerFooter(startedAt, usage)}${colors.reset}\n`);
2179
2179
  }
2180
2180
 
@@ -2223,16 +2223,16 @@ ${colors.reset}
2223
2223
  }
2224
2224
 
2225
2225
  async promptToolPermission(commandText) {
2226
- const side = this.useUnicodeUi ? '?' : '|';
2227
- const warn = this.useUnicodeUi ? '?' : '!';
2228
- process.stdout.write(`${colors.magenta}${side}${colors.reset} ${colors.yellow}${warn} AI mu?n ch?y: ${colors.bright}${commandText}${colors.reset}\n`);
2229
- process.stdout.write(`${colors.magenta}${side}${colors.reset} ${colors.cyan}1.${colors.reset} Cho ph?p\n`);
2230
- process.stdout.write(`${colors.magenta}${side}${colors.reset} ${colors.cyan}2.${colors.reset} Cho ph?p trong phi?n\n`);
2231
- process.stdout.write(`${colors.magenta}${side}${colors.reset} ${colors.cyan}3.${colors.reset} Kh?ng cho ph?p\n`);
2226
+ const side = this.useUnicodeUi ? '' : '|';
2227
+ const warn = this.useUnicodeUi ? '!' : '!';
2228
+ process.stdout.write(`${colors.magenta}${side}${colors.reset} ${colors.yellow}${warn} AI muốn chạy: ${colors.bright}${commandText}${colors.reset}\n`);
2229
+ process.stdout.write(`${colors.magenta}${side}${colors.reset} ${colors.cyan}1.${colors.reset} Cho phép\n`);
2230
+ process.stdout.write(`${colors.magenta}${side}${colors.reset} ${colors.cyan}2.${colors.reset} Cho phép trong phiên\n`);
2231
+ process.stdout.write(`${colors.magenta}${side}${colors.reset} ${colors.cyan}3.${colors.reset} Không cho phép\n`);
2232
2232
 
2233
2233
  while (true) {
2234
2234
  const answer = await new Promise(resolve => {
2235
- this.rl.question(`${colors.magenta}${side}${colors.reset} ${colors.yellow}Ch?n [1/2/3]: ${colors.reset}`, resolve);
2235
+ this.rl.question(`${colors.magenta}${side}${colors.reset} ${colors.yellow}Chọn [1/2/3]: ${colors.reset}`, resolve);
2236
2236
  });
2237
2237
 
2238
2238
  const choice = String(answer || '').trim().toLowerCase();
@@ -2246,7 +2246,7 @@ ${colors.reset}
2246
2246
  return false;
2247
2247
  }
2248
2248
 
2249
- process.stdout.write(`${colors.magenta}${side}${colors.reset} ${colors.dim}Vui l?ng ch?n 1, 2 ho?c 3.${colors.reset}\n`);
2249
+ process.stdout.write(`${colors.magenta}${side}${colors.reset} ${colors.dim}Vui lòng chọn 1, 2 hoặc 3.${colors.reset}\n`);
2250
2250
  }
2251
2251
  }
2252
2252
 
@@ -2297,7 +2297,7 @@ ${colors.reset}
2297
2297
  await this.session.replaceMemory('[Conversation Summary]', compressed.summary, 'summary');
2298
2298
 
2299
2299
  if (verbose) {
2300
- console.log(`${colors.green}? Compressed ${compressed.omittedCount} old message(s) into session summary.${colors.reset}`);
2300
+ console.log(`${colors.green} Compressed ${compressed.omittedCount} old message(s) into session summary.${colors.reset}`);
2301
2301
  }
2302
2302
  return compressed;
2303
2303
  }
@@ -2371,7 +2371,7 @@ ${colors.reset}
2371
2371
  this.rl.question(`${colors.yellow}Apply is not automatic here. Continue? [y/N]: ${colors.reset}`, resolve);
2372
2372
  });
2373
2373
  console.log(/^y(es)?$/i.test(String(answer || '').trim())
2374
- ? `${colors.green}? Confirmed${colors.reset}`
2374
+ ? `${colors.green} Confirmed${colors.reset}`
2375
2375
  : `${colors.dim}Cancelled${colors.reset}`);
2376
2376
  }
2377
2377
  }
@@ -2380,7 +2380,7 @@ ${colors.reset}
2380
2380
  const action = args[0];
2381
2381
  if (action === 'stop') {
2382
2382
  this.stopWatchers();
2383
- console.log(`${colors.green}? Watcher stopped${colors.reset}`);
2383
+ console.log(`${colors.green} Watcher stopped${colors.reset}`);
2384
2384
  return;
2385
2385
  }
2386
2386
 
@@ -2405,7 +2405,7 @@ ${colors.reset}
2405
2405
  run(name);
2406
2406
  });
2407
2407
  this.watchers.push(watcher);
2408
- console.log(`${colors.green}? Watching ${this.projectPath}${colors.reset}`);
2408
+ console.log(`${colors.green} Watching ${this.projectPath}${colors.reset}`);
2409
2409
  console.log(`${colors.dim}Command: ${command}. Use /watch stop to stop.${colors.reset}`);
2410
2410
  }
2411
2411
 
@@ -2438,7 +2438,7 @@ ${colors.reset}
2438
2438
  const promptHistory = this.getCompressedPromptHistory({
2439
2439
  limit: 20,
2440
2440
  keepRecent: 14,
2441
- maxTotalChars: this.shouldUseCompactPrompt() ? 5000 : 12000,
2441
+ maxTotalChars: 16000,
2442
2442
  });
2443
2443
  if (promptHistory.summary) {
2444
2444
  messages.push({ role: 'system', content: `Compressed prior conversation:\n${promptHistory.summary}` });
@@ -2468,7 +2468,7 @@ ${colors.reset}
2468
2468
  await this.session.addToHistory({ role: 'user', content: message });
2469
2469
  await this.session.addToHistory({ role: 'assistant', content: finalContent });
2470
2470
 
2471
- // T? ??ng verify: n?u AI ?? d?ng tools (s?a code), ch?y test/build
2471
+ // Tự động verify: nếu AI đã dùng tools (sửa code), chạy test/build.
2472
2472
  if (finalContent && this.shouldAutoVerifyAfterTools(message, usedMutatingTools)) {
2473
2473
  const sessionContext = this.session?.getContext?.() || {};
2474
2474
  const profile = String(sessionContext.workflowProfile || 'general');
@@ -2479,7 +2479,7 @@ ${colors.reset}
2479
2479
  }
2480
2480
 
2481
2481
  } catch (error) {
2482
- console.log(`\n${colors.red}? Error: ${error.message}${colors.reset}\n`);
2482
+ console.log(`\n${colors.red} Error: ${error.message}${colors.reset}\n`);
2483
2483
  }
2484
2484
  }
2485
2485
 
@@ -2541,7 +2541,7 @@ ${colors.reset}
2541
2541
  }
2542
2542
 
2543
2543
  /**
2544
- * Ch?y verification commands (test, build) v? tr? v? k?t qu?
2544
+ * Chạy verification commands (test, build) trả về kết quả.
2545
2545
  */
2546
2546
  async inferVerificationCommands(task = '') {
2547
2547
  const fs = await import('fs/promises');
@@ -2551,7 +2551,7 @@ ${colors.reset}
2551
2551
  const pkg = JSON.parse(await fs.readFile(packagePath, 'utf8'));
2552
2552
  const scripts = pkg.scripts || {};
2553
2553
  if (scripts.test) candidates.push('npm test');
2554
- if (scripts.build && /\b(build|compile|type|typescript|tsc|frontend|ui|design|next|vite|react|debug|fix|bug|error|l?i)\b/i.test(task)) {
2554
+ if (scripts.build && /\b(build|compile|type|typescript|tsc|frontend|ui|design|next|vite|react|debug|fix|bug|error|loi|lỗi)\b/i.test(task)) {
2555
2555
  candidates.push('npm run build');
2556
2556
  }
2557
2557
  if (scripts.lint && /\b(lint|style|eslint|quality|review)\b/i.test(task)) candidates.push('npm run lint');
@@ -2593,10 +2593,10 @@ ${colors.reset}
2593
2593
  }
2594
2594
 
2595
2595
  /**
2596
- * V?ng l?p t? ??ng verify + s?a l?i:
2597
- * - Ch?y test/build
2598
- * - N?u fail, g?i l?i cho AI fix
2599
- * - L?p ??n khi pass h?t ho?c h?t s? l?n th?
2596
+ * Vòng lặp tự động verify + sửa lỗi:
2597
+ * - Chạy test/build
2598
+ * - Nếu fail, gửi lại cho AI fix
2599
+ * - Lặp đến khi pass hết hoặc hết số lần thử
2600
2600
  */
2601
2601
  async verifyAndHeal(messages, tools, maxAttempts = 5) {
2602
2602
  const verifCommands = await this.inferVerificationCommands(this.getLatestUserText(messages));
@@ -2607,7 +2607,7 @@ ${colors.reset}
2607
2607
  const result = await this.runVerification(verifCommands);
2608
2608
 
2609
2609
  if (result.passed) {
2610
- console.log(`\n${colors.green}? All verifications passed!${colors.reset}\n`);
2610
+ console.log(`\n${colors.green} All verifications passed!${colors.reset}\n`);
2611
2611
  return;
2612
2612
  }
2613
2613
 
@@ -2617,7 +2617,7 @@ ${colors.reset}
2617
2617
  .map(r => `Command: ${r.cmd}\n${r.output}`)
2618
2618
  .join('\n\n---\n\n');
2619
2619
 
2620
- console.log(`\n${colors.yellow}? Verification failed. Sending errors back to AI for fix...${colors.reset}\n`);
2620
+ console.log(`\n${colors.yellow}Verification failed. Sending errors back to AI for fix...${colors.reset}\n`);
2621
2621
 
2622
2622
  // Push error output as user message for AI to fix
2623
2623
  const fixPrompt = `VERIFICATION FAILED (attempt ${attempt}/${maxAttempts}):
@@ -2635,12 +2635,12 @@ Do NOT stop until all errors are resolved.`;
2635
2635
  const { usedTools: fixUsedTools } = await this.runConversation(messages, 'Fixing', tools);
2636
2636
 
2637
2637
  if (!fixUsedTools) {
2638
- console.log(`\n${colors.red}? AI did not attempt to fix the errors. Stopping.${colors.reset}\n`);
2638
+ console.log(`\n${colors.red}AI did not attempt to fix the errors. Stopping.${colors.reset}\n`);
2639
2639
  break;
2640
2640
  }
2641
2641
  }
2642
2642
 
2643
- console.log(`\n${colors.red}? Max verification attempts (${maxAttempts}) reached. Some issues may remain.${colors.reset}\n`);
2643
+ console.log(`\n${colors.red}Max verification attempts (${maxAttempts}) reached. Some issues may remain.${colors.reset}\n`);
2644
2644
  }
2645
2645
 
2646
2646
  shouldUseTools(message = '', imageAttachments = []) {
@@ -2676,9 +2676,9 @@ Do NOT stop until all errors are resolved.`;
2676
2676
  ];
2677
2677
 
2678
2678
  const promptHistory = this.getCompressedPromptHistory({
2679
- limit: this.shouldUseCompactPrompt() ? 14 : 30,
2680
- keepRecent: this.shouldUseCompactPrompt() ? 8 : 14,
2681
- maxTotalChars: this.shouldUseCompactPrompt() ? 5000 : 12000,
2679
+ limit: 40,
2680
+ keepRecent: 16,
2681
+ maxTotalChars: 16000,
2682
2682
  });
2683
2683
  if (promptHistory.summary) {
2684
2684
  messages.push({ role: 'system', content: `Compressed prior conversation:\n${promptHistory.summary}` });
@@ -2770,14 +2770,14 @@ Do NOT stop until all errors are resolved.`;
2770
2770
  const passed = result.usedTools && Boolean(readEvent || /readme\.md/i.test(result.finalContent || ''));
2771
2771
 
2772
2772
  if (passed) {
2773
- console.log(`${colors.green}? Tool calling works for ${provider}/${model}.${colors.reset}`);
2773
+ console.log(`${colors.green} Tool calling works for ${provider}/${model}.${colors.reset}`);
2774
2774
  if (readEvent) {
2775
2775
  console.log(`${colors.dim} Last Read result: ${readEvent.result?.path || probePath}${colors.reset}`);
2776
2776
  }
2777
2777
  return { success: true, provider, model, usedTools: result.usedTools, beforeEvents };
2778
2778
  }
2779
2779
 
2780
- console.log(`${colors.red}? Tool calling did not execute for ${provider}/${model}.${colors.reset}`);
2780
+ console.log(`${colors.red} Tool calling did not execute for ${provider}/${model}.${colors.reset}`);
2781
2781
  console.log(`${colors.yellow} Try a stronger model or use a provider that supports OpenAI-compatible tools/fallback text output.${colors.reset}`);
2782
2782
  return { success: false, provider, model, usedTools: result.usedTools, beforeEvents };
2783
2783
  }
@@ -2864,11 +2864,17 @@ Do NOT stop until all errors are resolved.`;
2864
2864
  context.push(requiredLocalResources);
2865
2865
  }
2866
2866
 
2867
+ const shouldIncludeResources = /\b(resource|resources|skill|skills|plugin|plugins|claude|codex|agent|agents|design|ui|figma|brand|mcp)\b/i.test(String(task || ''));
2868
+ const localResources = shouldIncludeResources ? await this.getLocalResourceContext() : '';
2869
+ if (localResources) {
2870
+ context.push(localResources);
2871
+ }
2872
+
2867
2873
  const projectInstructionFiles = await this.readProjectInstructionFiles();
2868
2874
 
2869
2875
  for (const file of projectInstructionFiles) {
2870
2876
  try {
2871
- const preview = this.compactText(file.content, this.shouldUseCompactPrompt() ? 450 : 900, 'project instruction');
2877
+ const preview = this.compactText(file.content, 1200, 'project instruction');
2872
2878
  context.push(`[${file.relativePath}]\n${preview}`);
2873
2879
  } catch { }
2874
2880
  }
@@ -2878,21 +2884,24 @@ Do NOT stop until all errors are resolved.`;
2878
2884
  const stat = await fs.stat(packageJsonPath);
2879
2885
  if (stat.isFile()) {
2880
2886
  const content = await fs.readFile(packageJsonPath, 'utf-8');
2881
- context.push(`[package.json]\n${this.compactText(content, this.shouldUseCompactPrompt() ? 650 : 1200, 'package.json')}`);
2887
+ context.push(`[package.json]\n${this.compactText(content, 1600, 'package.json')}`);
2882
2888
  }
2883
2889
  } catch { }
2884
2890
 
2885
- const shouldIncludeResources = /\b(resource|resources|skill|skills|plugin|plugins|claude|codex|agent|agents|design|ui|figma|brand|mcp)\b/i.test(String(task || ''));
2886
- const localResources = shouldIncludeResources ? await this.getLocalResourceContext() : '';
2887
- if (localResources) {
2888
- context.push(localResources);
2889
- }
2890
-
2891
2891
  const codebaseContext = await this.buildCodebaseContext(task);
2892
2892
  if (codebaseContext) {
2893
2893
  context.push(codebaseContext);
2894
2894
  }
2895
2895
 
2896
+ const graphContext = await this.codebaseSearcher?.buildGraphContext?.(task, {
2897
+ maxNodes: 24,
2898
+ maxCodeBlocks: 8,
2899
+ maxCodeBlockSize: 1800,
2900
+ });
2901
+ if (graphContext) {
2902
+ context.push(`[CodeGraph Context]\n${this.compactText(graphContext, 5200, 'codegraph context')}`);
2903
+ }
2904
+
2896
2905
  // Git Context
2897
2906
  try {
2898
2907
  const { execSync } = await import('child_process');
@@ -2902,20 +2911,20 @@ Do NOT stop until all errors are resolved.`;
2902
2911
 
2903
2912
  const gitSummary = execSync('git diff --stat --summary', { cwd: this.projectPath, encoding: 'utf8', stdio: 'pipe', maxBuffer: 1024 * 50 }).trim();
2904
2913
  if (gitSummary) {
2905
- context.push(`[Git Summary]\n${this.compactText(gitSummary, this.shouldUseCompactPrompt() ? 650 : 1200, 'git summary')}`);
2914
+ context.push(`[Git Summary]\n${this.compactText(gitSummary, 1200, 'git summary')}`);
2906
2915
  }
2907
2916
 
2908
2917
  // Get brief git diff for context
2909
2918
  const gitDiff = execSync('git diff', { cwd: this.projectPath, encoding: 'utf8', stdio: 'pipe', maxBuffer: 1024 * 50 }).trim().split('\n').slice(0, 30).join('\n');
2910
2919
  if (gitDiff) {
2911
- context.push(`[Git Diff]\n${this.compactText(gitDiff, this.shouldUseCompactPrompt() ? 900 : 1800, 'git diff')}`);
2920
+ context.push(`[Git Diff]\n${this.compactText(gitDiff, 2200, 'git diff')}`);
2912
2921
  }
2913
2922
  }
2914
2923
  } catch (e) {
2915
2924
  // Not a git repo or git not installed
2916
2925
  }
2917
2926
 
2918
- return this.compactText(context.join('\n\n') || 'No project context found.', this.shouldUseCompactPrompt() ? 4200 : 9000, 'project context');
2927
+ return this.compactText(context.join('\n\n') || 'No project context found.', 14000, 'project context');
2919
2928
  }
2920
2929
 
2921
2930
  async getLocalResourceContext() {
@@ -3017,7 +3026,7 @@ Do NOT stop until all errors are resolved.`;
3017
3026
  this.promptBuilder.tools = this.tools;
3018
3027
  this.promptBuilder.sessionPermissionGrants = this.sessionPermissionGrants;
3019
3028
  return this.promptBuilder.buildSystemPrompt(context, {
3020
- projectContextBudget: this.shouldUseCompactPrompt() ? 2200 : 3200,
3029
+ projectContextBudget: 5200,
3021
3030
  });
3022
3031
  }
3023
3032
 
@@ -3064,7 +3073,7 @@ Do NOT stop until all errors are resolved.`;
3064
3073
  }
3065
3074
  config.mcp.servers.forEach(server => {
3066
3075
  const enabled = server.enabled === false ? `${colors.red}disabled${colors.reset}` : `${colors.green}enabled${colors.reset}`;
3067
- console.log(` ? ${server.name} (${enabled}) -> ${server.command}${server.args?.length ? ` ${server.args.join(' ')}` : ''}`);
3076
+ console.log(` - ${server.name} (${enabled}) -> ${server.command}${server.args?.length ? ` ${server.args.join(' ')}` : ''}`);
3068
3077
  });
3069
3078
  break;
3070
3079
  case 'add': {
@@ -3086,7 +3095,7 @@ Do NOT stop until all errors are resolved.`;
3086
3095
  config.mcp.servers = (config.mcp.servers || []).filter(server => server.name !== name);
3087
3096
  config.mcp.servers.push({ name, command, args: parsedArgs, enabled: true });
3088
3097
  await this.config.save(config);
3089
- console.log(`${colors.green}? Added MCP server: ${name}${colors.reset}`);
3098
+ console.log(`${colors.green} Added MCP server: ${name}${colors.reset}`);
3090
3099
  break;
3091
3100
  }
3092
3101
  case 'remove': {
@@ -3097,7 +3106,7 @@ Do NOT stop until all errors are resolved.`;
3097
3106
  }
3098
3107
  config.mcp.servers = (config.mcp.servers || []).filter(server => server.name !== name);
3099
3108
  await this.config.save(config);
3100
- console.log(`${colors.green}? Removed MCP server: ${name}${colors.reset}`);
3109
+ console.log(`${colors.green} Removed MCP server: ${name}${colors.reset}`);
3101
3110
  break;
3102
3111
  }
3103
3112
  case 'allow': {
@@ -3107,7 +3116,7 @@ Do NOT stop until all errors are resolved.`;
3107
3116
  break;
3108
3117
  }
3109
3118
  await this.config.setPermissionAllowlist({ mcpServers: [name] });
3110
- console.log(`${colors.green}? MCP server allowed: ${name}${colors.reset}`);
3119
+ console.log(`${colors.green} MCP server allowed: ${name}${colors.reset}`);
3111
3120
  break;
3112
3121
  }
3113
3122
  default:
@@ -3142,13 +3151,13 @@ Do NOT stop until all errors are resolved.`;
3142
3151
  break;
3143
3152
  }
3144
3153
  await this.config.setPermissionAllowlist({ [field]: [value] });
3145
- console.log(`${colors.green}? Allowed ${kind}: ${value}${colors.reset}`);
3154
+ console.log(`${colors.green} Allowed ${kind}: ${value}${colors.reset}`);
3146
3155
  break;
3147
3156
  }
3148
3157
  case 'prompt': {
3149
3158
  const value = String(rest[0] || '').toLowerCase();
3150
3159
  await this.config.setPermissionAllowlist({ promptByDefault: !(value === 'off' || value === 'false' || value === '0' || value === 'no') });
3151
- console.log(`${colors.green}? Updated prompt policy${colors.reset}`);
3160
+ console.log(`${colors.green} Updated prompt policy${colors.reset}`);
3152
3161
  break;
3153
3162
  }
3154
3163
  default: