@nclamvn/vibecode-cli 1.3.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,775 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════════
2
+ // VIBECODE CLI - Error Translator
3
+ // Phase H3: Human-Friendly Error Messages
4
+ // Technical errors → Vietnamese explanations + fix suggestions
5
+ // ═══════════════════════════════════════════════════════════════════════════════
6
+
7
+ import chalk from 'chalk';
8
+
9
+ /**
10
+ * Error patterns and their Vietnamese translations
11
+ * Each pattern has:
12
+ * - pattern: RegExp to match error
13
+ * - translate: Function that returns translated error info
14
+ *
15
+ * IMPORTANT: Patterns should handle variations:
16
+ * - Quote types: single ('), double ("), escaped (\"), or none
17
+ * - Case variations
18
+ * - Prefix variations (TypeError:, Error:, etc.)
19
+ */
20
+ const ERROR_PATTERNS = [
21
+ // ─────────────────────────────────────────────────────────────────────────────
22
+ // JavaScript/Node.js Runtime Errors
23
+ // ─────────────────────────────────────────────────────────────────────────────
24
+
25
+ // Cannot read properties of undefined/null - handles both quote types (new style)
26
+ {
27
+ pattern: /Cannot read propert(?:y|ies) of (undefined|null)(?: \(reading ['"]?(\w+)['"]?\))?/i,
28
+ translate: (match) => ({
29
+ title: match[1] === 'null' ? 'Biến là null' : 'Biến chưa được định nghĩa',
30
+ description: `Code đang cố truy cập thuộc tính '${match[2] || 'unknown'}' của một biến ${match[1]}`,
31
+ suggestions: [
32
+ 'Kiểm tra biến có được khởi tạo trước khi sử dụng',
33
+ 'Thêm optional chaining (?.) khi truy cập thuộc tính',
34
+ 'Kiểm tra null/undefined với if statement'
35
+ ],
36
+ category: 'RUNTIME'
37
+ })
38
+ },
39
+
40
+ // Cannot read property X of undefined (old style - Node < 16)
41
+ {
42
+ pattern: /Cannot read property ['"]?(\w+)['"]? of (undefined|null)/i,
43
+ translate: (match) => ({
44
+ title: match[2] === 'null' ? 'Biến là null' : 'Biến chưa được định nghĩa',
45
+ description: `Code đang cố truy cập thuộc tính '${match[1]}' của một biến ${match[2]}`,
46
+ suggestions: [
47
+ 'Kiểm tra biến có được khởi tạo trước khi sử dụng',
48
+ 'Thêm optional chaining (?.) khi truy cập thuộc tính',
49
+ 'Kiểm tra null/undefined với if statement'
50
+ ],
51
+ category: 'RUNTIME'
52
+ })
53
+ },
54
+
55
+ // Module not found: Can't resolve (webpack/bundler - BUILD error)
56
+ // MUST come before generic "Cannot find module" pattern
57
+ {
58
+ pattern: /Module not found:?\s*Can'?t resolve ['"]?([^'">\s]+)['"]?/i,
59
+ translate: (match) => ({
60
+ title: 'Module không tìm thấy khi build',
61
+ description: `Bundler không thể resolve: '${match[1]}'`,
62
+ suggestions: [
63
+ 'Kiểm tra path import có đúng không',
64
+ 'Chạy npm install nếu thiếu package',
65
+ 'Kiểm tra tsconfig paths nếu dùng alias'
66
+ ],
67
+ category: 'BUILD'
68
+ })
69
+ },
70
+
71
+ // Cannot find module - handles quotes and various formats (runtime error)
72
+ {
73
+ pattern: /(?:Cannot find module|Module not found)[:\s]+['"]?([^'">\s]+)['"]?/i,
74
+ translate: (match) => ({
75
+ title: 'Không tìm thấy module',
76
+ description: `Module '${match[1]}' không tồn tại hoặc chưa được cài đặt`,
77
+ suggestions: [
78
+ match[1].startsWith('.') || match[1].startsWith('@/')
79
+ ? 'Kiểm tra đường dẫn import có chính xác'
80
+ : `Chạy: npm install ${match[1]}`,
81
+ 'Kiểm tra file có tồn tại không',
82
+ 'Kiểm tra tsconfig paths nếu dùng alias'
83
+ ],
84
+ category: 'MODULE'
85
+ })
86
+ },
87
+
88
+ // SyntaxError: Unexpected token - handles various formats
89
+ {
90
+ pattern: /SyntaxError:?\s*Unexpected token\s*['"]?(.{1,10})['"]?/i,
91
+ translate: (match) => ({
92
+ title: 'Lỗi cú pháp',
93
+ description: `Có lỗi cú pháp trong code - token không mong đợi: '${match[1] || 'unknown'}'`,
94
+ suggestions: [
95
+ 'Kiểm tra dấu ngoặc đóng/mở có đủ không',
96
+ 'Kiểm tra dấu phẩy, chấm phẩy',
97
+ 'Kiểm tra cú pháp arrow function, object'
98
+ ],
99
+ category: 'SYNTAX'
100
+ })
101
+ },
102
+
103
+ // SyntaxError: Unexpected end of input
104
+ {
105
+ pattern: /SyntaxError:?\s*Unexpected end of/i,
106
+ translate: () => ({
107
+ title: 'Lỗi cú pháp - Thiếu đóng ngoặc',
108
+ description: 'Code kết thúc đột ngột, có thể thiếu dấu ngoặc hoặc dấu quote',
109
+ suggestions: [
110
+ 'Kiểm tra tất cả dấu { } ( ) [ ] có đủ cặp',
111
+ 'Kiểm tra string có đóng quote đúng không',
112
+ 'Sử dụng editor có highlight để tìm lỗi'
113
+ ],
114
+ category: 'SYNTAX'
115
+ })
116
+ },
117
+
118
+ // ReferenceError: X is not defined
119
+ {
120
+ pattern: /ReferenceError:?\s*(\w+) is not defined/i,
121
+ translate: (match) => ({
122
+ title: 'Biến chưa được khai báo',
123
+ description: `Biến '${match[1]}' được sử dụng nhưng chưa được khai báo`,
124
+ suggestions: [
125
+ `Khai báo biến: const ${match[1]} = ...`,
126
+ 'Kiểm tra import có đúng không',
127
+ 'Kiểm tra scope của biến'
128
+ ],
129
+ category: 'REFERENCE'
130
+ })
131
+ },
132
+
133
+ // TypeError: X is not a function
134
+ {
135
+ pattern: /TypeError:?\s*['"]?(\w+)['"]? is not a function/i,
136
+ translate: (match) => ({
137
+ title: 'Không phải hàm',
138
+ description: `'${match[1]}' không phải là một function, không thể gọi được`,
139
+ suggestions: [
140
+ 'Kiểm tra import có đúng không',
141
+ 'Kiểm tra tên function có chính xác',
142
+ 'Kiểm tra object có method này không'
143
+ ],
144
+ category: 'TYPE'
145
+ })
146
+ },
147
+
148
+ // TypeError: X is not iterable
149
+ {
150
+ pattern: /TypeError:?\s*(.+?) is not iterable/i,
151
+ translate: (match) => ({
152
+ title: 'Không thể lặp qua dữ liệu',
153
+ description: `'${match[1]}' không phải là array hoặc iterable object`,
154
+ suggestions: [
155
+ 'Kiểm tra data có phải array không',
156
+ 'Thêm Array.isArray() check trước khi loop',
157
+ 'Kiểm tra API response format'
158
+ ],
159
+ category: 'TYPE'
160
+ })
161
+ },
162
+
163
+ // Maximum call stack size exceeded
164
+ {
165
+ pattern: /Maximum call stack size exceeded/i,
166
+ translate: () => ({
167
+ title: 'Stack Overflow',
168
+ description: 'Có recursion vô hạn hoặc quá sâu trong code',
169
+ suggestions: [
170
+ 'Kiểm tra recursive function có base case',
171
+ 'Kiểm tra circular dependency',
172
+ 'Sử dụng iteration thay vì recursion'
173
+ ],
174
+ category: 'RUNTIME'
175
+ })
176
+ },
177
+
178
+ // ─────────────────────────────────────────────────────────────────────────────
179
+ // File System Errors
180
+ // ─────────────────────────────────────────────────────────────────────────────
181
+
182
+ // ENOENT - handles with or without quotes
183
+ {
184
+ pattern: /ENOENT:?\s*no such file or directory[,:]?\s*(?:open\s+)?['"]?([^'">\s]+)['"]?/i,
185
+ translate: (match) => ({
186
+ title: 'File không tồn tại',
187
+ description: `Không tìm thấy file hoặc thư mục: '${match[1] || 'unknown'}'`,
188
+ suggestions: [
189
+ 'Kiểm tra đường dẫn file có chính xác',
190
+ 'Tạo file/thư mục nếu cần',
191
+ 'Kiểm tra quyền truy cập'
192
+ ],
193
+ category: 'FILE'
194
+ })
195
+ },
196
+
197
+ // ENOENT - simple format fallback
198
+ {
199
+ pattern: /ENOENT/i,
200
+ translate: () => ({
201
+ title: 'File không tồn tại',
202
+ description: 'Không tìm thấy file hoặc thư mục được yêu cầu',
203
+ suggestions: [
204
+ 'Kiểm tra đường dẫn file có chính xác',
205
+ 'Tạo file/thư mục nếu cần',
206
+ 'Kiểm tra quyền truy cập'
207
+ ],
208
+ category: 'FILE'
209
+ })
210
+ },
211
+
212
+ // EACCES
213
+ {
214
+ pattern: /EACCES:?\s*permission denied/i,
215
+ translate: () => ({
216
+ title: 'Không có quyền truy cập',
217
+ description: 'Không có quyền để thực hiện thao tác này',
218
+ suggestions: [
219
+ 'Chạy với sudo (cẩn thận!)',
220
+ 'Kiểm tra quyền của file/thư mục',
221
+ 'Đổi ownership: chown -R $USER:$USER .'
222
+ ],
223
+ category: 'PERMISSION'
224
+ })
225
+ },
226
+
227
+ // EADDRINUSE
228
+ {
229
+ pattern: /EADDRINUSE.*?:?(\d+)/i,
230
+ translate: (match) => ({
231
+ title: 'Port đang được sử dụng',
232
+ description: `Port ${match[1]} đã có process khác đang chạy`,
233
+ suggestions: [
234
+ `Kill process: lsof -ti:${match[1]} | xargs kill`,
235
+ 'Dùng port khác trong config',
236
+ 'Tìm và tắt ứng dụng đang dùng port'
237
+ ],
238
+ category: 'NETWORK'
239
+ })
240
+ },
241
+
242
+ // Database connection refused (common ports) - MUST come before generic ECONNREFUSED
243
+ {
244
+ pattern: /ECONNREFUSED.*?(?:3306|5432|27017|6379)/i,
245
+ translate: () => ({
246
+ title: 'Database không chạy',
247
+ description: 'Không thể kết nối database - service có thể chưa start',
248
+ suggestions: [
249
+ 'Start database service',
250
+ 'Kiểm tra Docker container nếu dùng Docker',
251
+ 'Kiểm tra connection string trong .env'
252
+ ],
253
+ category: 'DATABASE'
254
+ })
255
+ },
256
+
257
+ // ECONNREFUSED (generic - for non-database ports)
258
+ {
259
+ pattern: /ECONNREFUSED.*?(?::(\d+)|(\d+\.\d+\.\d+\.\d+))/i,
260
+ translate: (match) => ({
261
+ title: 'Không thể kết nối',
262
+ description: `Không thể kết nối đến ${match[1] ? 'port ' + match[1] : 'server'} - service có thể chưa chạy`,
263
+ suggestions: [
264
+ 'Kiểm tra service đã start chưa',
265
+ 'Kiểm tra firewall settings',
266
+ 'Kiểm tra URL/port trong config'
267
+ ],
268
+ category: 'NETWORK'
269
+ })
270
+ },
271
+
272
+ // ─────────────────────────────────────────────────────────────────────────────
273
+ // NPM Errors
274
+ // ─────────────────────────────────────────────────────────────────────────────
275
+
276
+ // npm ERR! code ERESOLVE
277
+ {
278
+ pattern: /npm ERR!?\s*(?:code\s+)?ERESOLVE/i,
279
+ translate: () => ({
280
+ title: 'Xung đột phiên bản dependency',
281
+ description: 'Các package yêu cầu phiên bản dependency khác nhau',
282
+ suggestions: [
283
+ 'Chạy: npm install --legacy-peer-deps',
284
+ 'Chạy: npm install --force',
285
+ 'Kiểm tra và update các package xung đột'
286
+ ],
287
+ category: 'NPM'
288
+ })
289
+ },
290
+
291
+ // npm ERR! code ENOENT
292
+ {
293
+ pattern: /npm ERR!?\s*(?:code\s+)?ENOENT/i,
294
+ translate: () => ({
295
+ title: 'npm không tìm thấy file',
296
+ description: 'npm không tìm thấy package.json hoặc file cần thiết',
297
+ suggestions: [
298
+ 'Kiểm tra đang ở đúng thư mục project',
299
+ 'Chạy: npm init nếu chưa có package.json',
300
+ 'Kiểm tra file/thư mục có tồn tại'
301
+ ],
302
+ category: 'NPM'
303
+ })
304
+ },
305
+
306
+ // npm ERR! code E4XX/E5XX
307
+ {
308
+ pattern: /npm ERR!?\s*(?:code\s+)?E(\d{3})/i,
309
+ translate: (match) => ({
310
+ title: 'Lỗi npm registry',
311
+ description: `npm gặp lỗi HTTP ${match[1]} khi tải package`,
312
+ suggestions: [
313
+ 'Kiểm tra kết nối internet',
314
+ 'Thử lại sau vài phút',
315
+ 'Kiểm tra npm registry có hoạt động không'
316
+ ],
317
+ category: 'NPM'
318
+ })
319
+ },
320
+
321
+ // npm peer dep missing
322
+ {
323
+ pattern: /npm ERR!?\s*peer dep(?:endency)? missing/i,
324
+ translate: () => ({
325
+ title: 'Thiếu peer dependency',
326
+ description: 'Một package yêu cầu dependency khác mà bạn chưa cài',
327
+ suggestions: [
328
+ 'Chạy: npm install --legacy-peer-deps',
329
+ 'Cài thủ công package được yêu cầu',
330
+ 'Kiểm tra version compatibility'
331
+ ],
332
+ category: 'NPM'
333
+ })
334
+ },
335
+
336
+ // ─────────────────────────────────────────────────────────────────────────────
337
+ // Next.js / React Errors
338
+ // ─────────────────────────────────────────────────────────────────────────────
339
+
340
+ // Functions cannot be passed to Client Components
341
+ {
342
+ pattern: /Functions cannot be passed directly to Client Components/i,
343
+ translate: () => ({
344
+ title: 'Lỗi Server/Client Component (Next.js)',
345
+ description: 'Đang truyền function vào Client Component - không được phép trong App Router',
346
+ suggestions: [
347
+ 'Chuyển function thành serializable data',
348
+ 'Thêm "use client" vào component con',
349
+ 'Di chuyển logic vào Client Component'
350
+ ],
351
+ category: 'NEXTJS'
352
+ })
353
+ },
354
+
355
+ // Hydration failed
356
+ {
357
+ pattern: /Hydration failed|Text content does not match/i,
358
+ translate: () => ({
359
+ title: 'Lỗi Hydration (React/Next.js)',
360
+ description: 'HTML từ server không khớp với client - thường do render khác nhau',
361
+ suggestions: [
362
+ 'Tránh dùng Date, random trong initial render',
363
+ 'Sử dụng useEffect cho browser-only code',
364
+ 'Kiểm tra conditional rendering logic'
365
+ ],
366
+ category: 'REACT'
367
+ })
368
+ },
369
+
370
+ // Invalid hook call
371
+ {
372
+ pattern: /Invalid hook call/i,
373
+ translate: () => ({
374
+ title: 'Lỗi React Hook',
375
+ description: 'Hook được gọi sai cách - có thể ngoài component hoặc trong condition',
376
+ suggestions: [
377
+ 'Chỉ gọi hook trong function component',
378
+ 'Không gọi hook trong if/loop',
379
+ 'Kiểm tra có duplicate React không'
380
+ ],
381
+ category: 'REACT'
382
+ })
383
+ },
384
+
385
+ // ─────────────────────────────────────────────────────────────────────────────
386
+ // TypeScript Errors
387
+ // ─────────────────────────────────────────────────────────────────────────────
388
+
389
+ // TS Error codes (TSxxxx)
390
+ {
391
+ pattern: /(?:error\s+)?TS(\d{4,5}):\s*(.{10,80})/i,
392
+ translate: (match) => ({
393
+ title: `Lỗi TypeScript (TS${match[1]})`,
394
+ description: match[2].substring(0, 60) + (match[2].length > 60 ? '...' : ''),
395
+ suggestions: [
396
+ 'Kiểm tra kiểu dữ liệu của biến',
397
+ 'Thêm type annotation nếu cần',
398
+ `Tìm hiểu thêm: typescript.tv/errors/#TS${match[1]}`
399
+ ],
400
+ category: 'TYPESCRIPT'
401
+ })
402
+ },
403
+
404
+ // Type X is not assignable to type Y
405
+ {
406
+ pattern: /Type ['"]?([^'"]+?)['"]? is not assignable to type ['"]?([^'"]+?)['"]?/i,
407
+ translate: (match) => ({
408
+ title: 'Lỗi TypeScript - Type không khớp',
409
+ description: `Type '${match[1].substring(0, 20)}' không thể gán cho '${match[2].substring(0, 20)}'`,
410
+ suggestions: [
411
+ 'Kiểm tra kiểu dữ liệu của biến',
412
+ 'Thêm type assertion nếu chắc chắn',
413
+ 'Sửa type definition'
414
+ ],
415
+ category: 'TYPESCRIPT'
416
+ })
417
+ },
418
+
419
+ // Property X does not exist on type
420
+ {
421
+ pattern: /Property ['"]?(\w+)['"]? does not exist on type/i,
422
+ translate: (match) => ({
423
+ title: 'Lỗi TypeScript - Property không tồn tại',
424
+ description: `Property '${match[1]}' không có trong type definition`,
425
+ suggestions: [
426
+ 'Thêm property vào interface/type',
427
+ 'Kiểm tra tên property có đúng không',
428
+ 'Sử dụng type assertion hoặc any (tạm thời)'
429
+ ],
430
+ category: 'TYPESCRIPT'
431
+ })
432
+ },
433
+
434
+ // Argument of type X is not assignable
435
+ {
436
+ pattern: /Argument of type ['"]?([^'"]+?)['"]? is not assignable/i,
437
+ translate: (match) => ({
438
+ title: 'Lỗi TypeScript - Argument không khớp',
439
+ description: `Argument '${match[1].substring(0, 30)}' không đúng type`,
440
+ suggestions: [
441
+ 'Kiểm tra function signature',
442
+ 'Cast type nếu cần',
443
+ 'Sửa data truyền vào cho đúng type'
444
+ ],
445
+ category: 'TYPESCRIPT'
446
+ })
447
+ },
448
+
449
+ // ─────────────────────────────────────────────────────────────────────────────
450
+ // Database Errors
451
+ // ─────────────────────────────────────────────────────────────────────────────
452
+
453
+ // Prisma errors
454
+ {
455
+ pattern: /Prisma(?:Client)?.*?P(\d+)/i,
456
+ translate: (match) => ({
457
+ title: 'Lỗi Prisma Database',
458
+ description: `Prisma error code P${match[1]}`,
459
+ suggestions: [
460
+ 'Chạy: npx prisma db push',
461
+ 'Chạy: npx prisma generate',
462
+ 'Kiểm tra DATABASE_URL trong .env'
463
+ ],
464
+ category: 'DATABASE'
465
+ })
466
+ },
467
+
468
+ // Prisma invalid invocation
469
+ {
470
+ pattern: /Invalid [`']?prisma\.(\w+)\.(\w+)\(\)/i,
471
+ translate: (match) => ({
472
+ title: 'Lỗi Prisma Query',
473
+ description: `Lỗi khi gọi prisma.${match[1]}.${match[2]}()`,
474
+ suggestions: [
475
+ 'Kiểm tra model tồn tại trong schema.prisma',
476
+ 'Chạy: npx prisma generate',
477
+ 'Kiểm tra syntax của query'
478
+ ],
479
+ category: 'DATABASE'
480
+ })
481
+ },
482
+
483
+ // MySQL/PostgreSQL access denied
484
+ {
485
+ pattern: /ER_ACCESS_DENIED_ERROR|FATAL:\s*password authentication failed/i,
486
+ translate: () => ({
487
+ title: 'Lỗi truy cập Database',
488
+ description: 'Sai username/password hoặc không có quyền truy cập database',
489
+ suggestions: [
490
+ 'Kiểm tra DATABASE_URL trong .env',
491
+ 'Kiểm tra user có quyền access database',
492
+ 'Reset password nếu cần'
493
+ ],
494
+ category: 'DATABASE'
495
+ })
496
+ },
497
+
498
+ // SQLITE_ERROR
499
+ {
500
+ pattern: /SQLITE_ERROR:?\s*(.+)/i,
501
+ translate: (match) => ({
502
+ title: 'Lỗi SQLite',
503
+ description: match[1].substring(0, 60),
504
+ suggestions: [
505
+ 'Kiểm tra file database có tồn tại',
506
+ 'Kiểm tra table/column name có đúng',
507
+ 'Chạy migration nếu schema thay đổi'
508
+ ],
509
+ category: 'DATABASE'
510
+ })
511
+ },
512
+
513
+ // ─────────────────────────────────────────────────────────────────────────────
514
+ // Git Errors
515
+ // ─────────────────────────────────────────────────────────────────────────────
516
+
517
+ // Not a git repository
518
+ {
519
+ pattern: /fatal:?\s*not a git repository/i,
520
+ translate: () => ({
521
+ title: 'Không phải Git repository',
522
+ description: 'Thư mục hiện tại chưa được khởi tạo git',
523
+ suggestions: [
524
+ 'Chạy: git init',
525
+ 'Đảm bảo đang ở đúng thư mục project',
526
+ 'Kiểm tra thư mục .git có tồn tại'
527
+ ],
528
+ category: 'GIT'
529
+ })
530
+ },
531
+
532
+ // Failed to push
533
+ {
534
+ pattern: /error:?\s*failed to push some refs/i,
535
+ translate: () => ({
536
+ title: 'Không thể push lên remote',
537
+ description: 'Remote có commits mới mà local chưa có',
538
+ suggestions: [
539
+ 'Chạy: git pull --rebase',
540
+ 'Resolve conflicts nếu có',
541
+ 'Force push (cẩn thận!): git push -f'
542
+ ],
543
+ category: 'GIT'
544
+ })
545
+ },
546
+
547
+ // Merge conflict
548
+ {
549
+ pattern: /CONFLICT.*?Merge conflict in (.+)/i,
550
+ translate: (match) => ({
551
+ title: 'Git Merge Conflict',
552
+ description: `Có conflict trong file: ${match[1]}`,
553
+ suggestions: [
554
+ 'Mở file và resolve conflicts thủ công',
555
+ 'Sử dụng git mergetool',
556
+ 'Sau khi resolve: git add . && git commit'
557
+ ],
558
+ category: 'GIT'
559
+ })
560
+ },
561
+
562
+ // ─────────────────────────────────────────────────────────────────────────────
563
+ // Build/Webpack Errors
564
+ // ─────────────────────────────────────────────────────────────────────────────
565
+
566
+ // Module build failed
567
+ {
568
+ pattern: /Module build failed/i,
569
+ translate: () => ({
570
+ title: 'Build module thất bại',
571
+ description: 'Webpack/bundler không thể build một module',
572
+ suggestions: [
573
+ 'Kiểm tra syntax của file được báo lỗi',
574
+ 'Kiểm tra loader config nếu dùng custom',
575
+ 'Clear cache: rm -rf .next node_modules/.cache'
576
+ ],
577
+ category: 'BUILD'
578
+ })
579
+ },
580
+
581
+ // ─────────────────────────────────────────────────────────────────────────────
582
+ // Memory/Performance Errors
583
+ // ─────────────────────────────────────────────────────────────────────────────
584
+
585
+ // JavaScript heap out of memory
586
+ {
587
+ pattern: /JavaScript heap out of memory|FATAL ERROR:?\s*(?:Ineffective mark-compacts|CALL_AND_RETRY_LAST)/i,
588
+ translate: () => ({
589
+ title: 'Hết bộ nhớ',
590
+ description: 'Node.js hết RAM khi chạy - thường do data quá lớn hoặc memory leak',
591
+ suggestions: [
592
+ 'Tăng memory: NODE_OPTIONS="--max-old-space-size=4096"',
593
+ 'Kiểm tra có memory leak không',
594
+ 'Xử lý data theo batch thay vì load all'
595
+ ],
596
+ category: 'MEMORY'
597
+ })
598
+ }
599
+ ];
600
+
601
+ /**
602
+ * Fallback for unknown errors
603
+ */
604
+ const UNKNOWN_ERROR = {
605
+ title: 'Lỗi không xác định',
606
+ description: 'Đã xảy ra lỗi trong quá trình thực thi',
607
+ suggestions: [
608
+ 'Chạy vibecode debug --auto để AI phân tích',
609
+ 'Kiểm tra console log chi tiết',
610
+ 'Chạy vibecode assist để được hỗ trợ'
611
+ ],
612
+ category: 'UNKNOWN'
613
+ };
614
+
615
+ /**
616
+ * Translate an error to human-friendly Vietnamese
617
+ * @param {Error|string} error - The error to translate
618
+ * @param {Object} options - Options
619
+ * @param {boolean} options.debug - Enable debug mode
620
+ * @returns {Object} Translated error info
621
+ */
622
+ export function translateError(error, options = {}) {
623
+ const errorMessage = typeof error === 'string' ? error : error.message || String(error);
624
+
625
+ if (options.debug) {
626
+ console.log('[DEBUG] translateError input:', errorMessage.substring(0, 100));
627
+ }
628
+
629
+ for (let i = 0; i < ERROR_PATTERNS.length; i++) {
630
+ const { pattern, translate } = ERROR_PATTERNS[i];
631
+
632
+ if (options.debug) {
633
+ console.log(`[DEBUG] Testing pattern ${i}:`, pattern.toString().substring(0, 50));
634
+ }
635
+
636
+ const match = errorMessage.match(pattern);
637
+
638
+ if (match) {
639
+ if (options.debug) {
640
+ console.log('[DEBUG] Matched! Groups:', match.slice(0, 3));
641
+ }
642
+ const result = translate(match);
643
+ return result;
644
+ }
645
+ }
646
+
647
+ if (options.debug) {
648
+ console.log('[DEBUG] No pattern matched');
649
+ }
650
+
651
+ return {
652
+ ...UNKNOWN_ERROR,
653
+ originalMessage: errorMessage.substring(0, 100)
654
+ };
655
+ }
656
+
657
+ /**
658
+ * Format translated error as a pretty box
659
+ * @param {Error|string} error - The error to format
660
+ * @param {Object} options - Formatting options
661
+ * @returns {string} Formatted error string
662
+ */
663
+ export function formatTranslatedError(error, options = {}) {
664
+ const translated = translateError(error);
665
+ const errorMessage = typeof error === 'string' ? error : error.message || String(error);
666
+
667
+ // Extract location if available
668
+ const locationMatch = errorMessage.match(/at\s+(?:\w+\s+)?\(?([^:]+):(\d+)(?::\d+)?\)?/);
669
+ const location = locationMatch ? `${locationMatch[1]} dòng ${locationMatch[2]}` : null;
670
+
671
+ const lines = [];
672
+
673
+ // Header
674
+ lines.push(chalk.red('╭────────────────────────────────────────────────────────────────────╮'));
675
+ lines.push(chalk.red('│') + ` ❌ ${chalk.bold.red('LỖI:')} ${chalk.white(translated.title)}`.padEnd(76) + chalk.red('│'));
676
+ lines.push(chalk.red('│') + ''.padEnd(68) + chalk.red('│'));
677
+
678
+ // Description (wrap if too long)
679
+ const desc = translated.description;
680
+ if (desc.length <= 55) {
681
+ lines.push(chalk.red('│') + ` ${chalk.gray('Vấn đề:')} ${chalk.white(desc)}`.padEnd(76) + chalk.red('│'));
682
+ } else {
683
+ lines.push(chalk.red('│') + ` ${chalk.gray('Vấn đề:')} ${chalk.white(desc.substring(0, 55))}`.padEnd(76) + chalk.red('│'));
684
+ lines.push(chalk.red('│') + ` ${chalk.white(desc.substring(55, 110))}`.padEnd(76) + chalk.red('│'));
685
+ }
686
+
687
+ // Location
688
+ if (location) {
689
+ lines.push(chalk.red('│') + ''.padEnd(68) + chalk.red('│'));
690
+ lines.push(chalk.red('│') + ` 📍 ${chalk.gray('Vị trí:')} ${chalk.yellow(location)}`.padEnd(76) + chalk.red('│'));
691
+ }
692
+
693
+ // Suggestions
694
+ lines.push(chalk.red('│') + ''.padEnd(68) + chalk.red('│'));
695
+ lines.push(chalk.red('│') + ` 💡 ${chalk.gray('Gợi ý:')}`.padEnd(76) + chalk.red('│'));
696
+
697
+ for (const suggestion of translated.suggestions.slice(0, 3)) {
698
+ const truncated = suggestion.length > 58 ? suggestion.substring(0, 55) + '...' : suggestion;
699
+ lines.push(chalk.red('│') + ` ${chalk.cyan('•')} ${chalk.white(truncated)}`.padEnd(76) + chalk.red('│'));
700
+ }
701
+
702
+ // Debug hint
703
+ lines.push(chalk.red('│') + ''.padEnd(68) + chalk.red('│'));
704
+ lines.push(chalk.red('│') + ` ${chalk.gray('Chạy')} ${chalk.cyan('vibecode debug --auto')} ${chalk.gray('để AI phân tích chi tiết')}`.padEnd(76) + chalk.red('│'));
705
+ lines.push(chalk.red('│') + ''.padEnd(68) + chalk.red('│'));
706
+ lines.push(chalk.red('╰────────────────────────────────────────────────────────────────────╯'));
707
+
708
+ // Original error (verbose mode)
709
+ if (options.verbose && options.showOriginal !== false) {
710
+ lines.push('');
711
+ lines.push(chalk.gray('Original error:'));
712
+ lines.push(chalk.gray(errorMessage.substring(0, 300)));
713
+ }
714
+
715
+ return lines.join('\n');
716
+ }
717
+
718
+ /**
719
+ * Show error in formatted box (console.log wrapper)
720
+ * @param {Error|string} error - The error to show
721
+ * @param {Object} options - Display options
722
+ */
723
+ export function showError(error, options = {}) {
724
+ console.log(formatTranslatedError(error, options));
725
+ }
726
+
727
+ /**
728
+ * Get inline error message (short, single line)
729
+ * @param {Error|string} error - The error to translate
730
+ * @returns {string} Short error message
731
+ */
732
+ export function inlineError(error) {
733
+ const translated = translateError(error);
734
+ return chalk.red(`❌ ${translated.title}: ${translated.description.substring(0, 50)}${translated.description.length > 50 ? '...' : ''}`);
735
+ }
736
+
737
+ /**
738
+ * Get error category for filtering/grouping
739
+ * @param {Error|string} error - The error
740
+ * @returns {string} Error category
741
+ */
742
+ export function getErrorCategory(error) {
743
+ const translated = translateError(error);
744
+ return translated.category || 'UNKNOWN';
745
+ }
746
+
747
+ /**
748
+ * Check if error is of a specific category
749
+ * @param {Error|string} error - The error
750
+ * @param {string} category - Category to check
751
+ * @returns {boolean}
752
+ */
753
+ export function isErrorCategory(error, category) {
754
+ return getErrorCategory(error) === category;
755
+ }
756
+
757
+ /**
758
+ * Get all pattern categories for documentation
759
+ * @returns {string[]} List of categories
760
+ */
761
+ export function getCategories() {
762
+ const categories = new Set();
763
+ for (const { translate } of ERROR_PATTERNS) {
764
+ // Call translate with empty match to get category
765
+ try {
766
+ const result = translate(['', '', '']);
767
+ if (result.category) {
768
+ categories.add(result.category);
769
+ }
770
+ } catch {
771
+ // Some translates may fail with empty match, ignore
772
+ }
773
+ }
774
+ return [...categories];
775
+ }