@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.
- package/.vibecode/learning/fixes.json +1 -0
- package/.vibecode/learning/preferences.json +1 -0
- package/README.md +310 -49
- package/bin/vibecode.js +103 -2
- package/package.json +4 -2
- package/src/agent/decomposition.js +476 -0
- package/src/agent/index.js +325 -0
- package/src/agent/memory.js +542 -0
- package/src/agent/orchestrator.js +713 -0
- package/src/agent/self-healing.js +516 -0
- package/src/commands/agent.js +255 -0
- package/src/commands/assist.js +413 -0
- package/src/commands/build.js +13 -3
- package/src/commands/debug.js +457 -0
- package/src/commands/go.js +387 -0
- package/src/commands/learn.js +294 -0
- package/src/commands/undo.js +281 -0
- package/src/commands/wizard.js +322 -0
- package/src/core/backup.js +325 -0
- package/src/core/learning.js +295 -0
- package/src/core/test-runner.js +38 -5
- package/src/debug/analyzer.js +329 -0
- package/src/debug/evidence.js +228 -0
- package/src/debug/fixer.js +348 -0
- package/src/debug/index.js +378 -0
- package/src/debug/verifier.js +346 -0
- package/src/index.js +62 -0
- package/src/ui/__tests__/error-translator.test.js +390 -0
- package/src/ui/dashboard.js +364 -0
- package/src/ui/error-translator.js +775 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// VIBECODE CLI - Undo Command
|
|
3
|
+
// Phase H4: Revert last action with backup restore
|
|
4
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import inquirer from 'inquirer';
|
|
8
|
+
import { BackupManager } from '../core/backup.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Undo Command - Revert to previous state
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* vibecode undo - Interactive restore menu
|
|
15
|
+
* vibecode undo --list - List available backups
|
|
16
|
+
* vibecode undo --step 2 - Restore to 2 steps ago
|
|
17
|
+
* vibecode undo --clear - Clear all backups
|
|
18
|
+
*/
|
|
19
|
+
export async function undoCommand(options = {}) {
|
|
20
|
+
const backup = new BackupManager();
|
|
21
|
+
|
|
22
|
+
// Clear all backups
|
|
23
|
+
if (options.clear) {
|
|
24
|
+
await clearBackups(backup, options);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// List backups
|
|
29
|
+
if (options.list) {
|
|
30
|
+
await listBackups(backup);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Restore specific step
|
|
35
|
+
if (options.step) {
|
|
36
|
+
await restoreStep(backup, parseInt(options.step), options.force);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Interactive undo
|
|
41
|
+
await interactiveUndo(backup);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* List all available backups
|
|
46
|
+
*/
|
|
47
|
+
async function listBackups(backup) {
|
|
48
|
+
const backups = await backup.listBackups();
|
|
49
|
+
|
|
50
|
+
if (backups.length === 0) {
|
|
51
|
+
console.log(chalk.yellow('\n📭 Chưa có backup nào.\n'));
|
|
52
|
+
console.log(chalk.gray('Backup sẽ được tạo tự động khi chạy:'));
|
|
53
|
+
console.log(chalk.gray(' • vibecode build --auto'));
|
|
54
|
+
console.log(chalk.gray(' • vibecode agent'));
|
|
55
|
+
console.log(chalk.gray(' • vibecode go'));
|
|
56
|
+
console.log();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log(chalk.cyan(`
|
|
61
|
+
╭────────────────────────────────────────────────────────────────────╮
|
|
62
|
+
│ 📦 BACKUP HISTORY │
|
|
63
|
+
╰────────────────────────────────────────────────────────────────────╯
|
|
64
|
+
`));
|
|
65
|
+
|
|
66
|
+
backups.forEach((b, index) => {
|
|
67
|
+
const date = new Date(b.timestamp);
|
|
68
|
+
const timeAgo = getTimeAgo(date);
|
|
69
|
+
const filesCount = b.files?.length || 0;
|
|
70
|
+
|
|
71
|
+
console.log(chalk.white(` ${(index + 1).toString().padStart(2)}. ${chalk.bold(b.action)}`));
|
|
72
|
+
console.log(chalk.gray(` ${timeAgo} · ${filesCount} files`));
|
|
73
|
+
console.log('');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
console.log(chalk.gray(` Chạy ${chalk.cyan('vibecode undo')} để restore`));
|
|
77
|
+
console.log(chalk.gray(` Hoặc ${chalk.cyan('vibecode undo --step N')} để restore step cụ thể\n`));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Restore to specific step
|
|
82
|
+
*/
|
|
83
|
+
async function restoreStep(backup, step, force = false) {
|
|
84
|
+
const backups = await backup.listBackups();
|
|
85
|
+
|
|
86
|
+
if (backups.length === 0) {
|
|
87
|
+
console.log(chalk.yellow('\n📭 Chưa có backup nào để restore.\n'));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (step < 1 || step > backups.length) {
|
|
92
|
+
console.log(chalk.red(`\n❌ Step ${step} không tồn tại.`));
|
|
93
|
+
console.log(chalk.gray(` Có ${backups.length} backups (1-${backups.length}).\n`));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const targetBackup = backups[step - 1];
|
|
98
|
+
|
|
99
|
+
console.log(chalk.yellow(`\n⚠️ Sắp restore về: ${chalk.bold(targetBackup.action)}`));
|
|
100
|
+
console.log(chalk.gray(` Thời gian: ${new Date(targetBackup.timestamp).toLocaleString('vi-VN')}`));
|
|
101
|
+
console.log(chalk.gray(` Files: ${targetBackup.files?.length || 0}\n`));
|
|
102
|
+
|
|
103
|
+
if (!force) {
|
|
104
|
+
const { confirm } = await inquirer.prompt([
|
|
105
|
+
{
|
|
106
|
+
type: 'confirm',
|
|
107
|
+
name: 'confirm',
|
|
108
|
+
message: 'Xác nhận restore?',
|
|
109
|
+
default: false
|
|
110
|
+
}
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
if (!confirm) {
|
|
114
|
+
console.log(chalk.gray('\n👋 Đã huỷ.\n'));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const result = await backup.restore(targetBackup.id);
|
|
120
|
+
showRestoreResult(result);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Interactive undo menu
|
|
125
|
+
*/
|
|
126
|
+
async function interactiveUndo(backup) {
|
|
127
|
+
const backups = await backup.listBackups();
|
|
128
|
+
|
|
129
|
+
if (backups.length === 0) {
|
|
130
|
+
console.log(chalk.yellow('\n📭 Chưa có backup nào để undo.\n'));
|
|
131
|
+
console.log(chalk.gray('Backup sẽ được tạo tự động khi chạy các lệnh build.'));
|
|
132
|
+
console.log();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
console.log(chalk.cyan(`
|
|
137
|
+
╭────────────────────────────────────────────────────────────────────╮
|
|
138
|
+
│ ⏪ VIBECODE UNDO │
|
|
139
|
+
│ │
|
|
140
|
+
│ Chọn backup để restore files về trạng thái trước đó. │
|
|
141
|
+
│ │
|
|
142
|
+
╰────────────────────────────────────────────────────────────────────╯
|
|
143
|
+
`));
|
|
144
|
+
|
|
145
|
+
const choices = backups.map((b, index) => {
|
|
146
|
+
const timeAgo = getTimeAgo(new Date(b.timestamp));
|
|
147
|
+
const filesCount = b.files?.length || 0;
|
|
148
|
+
return {
|
|
149
|
+
name: `${b.action} (${timeAgo}, ${filesCount} files)`,
|
|
150
|
+
value: b.id,
|
|
151
|
+
short: b.action
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
choices.push(new inquirer.Separator());
|
|
156
|
+
choices.push({ name: '❌ Huỷ', value: 'cancel' });
|
|
157
|
+
|
|
158
|
+
const { backupId } = await inquirer.prompt([
|
|
159
|
+
{
|
|
160
|
+
type: 'list',
|
|
161
|
+
name: 'backupId',
|
|
162
|
+
message: 'Chọn backup để restore:',
|
|
163
|
+
choices,
|
|
164
|
+
pageSize: 10
|
|
165
|
+
}
|
|
166
|
+
]);
|
|
167
|
+
|
|
168
|
+
if (backupId === 'cancel') {
|
|
169
|
+
console.log(chalk.gray('\n👋 Đã huỷ.\n'));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const targetBackup = backups.find(b => b.id === backupId);
|
|
174
|
+
|
|
175
|
+
// Show files that will be restored
|
|
176
|
+
if (targetBackup.files && targetBackup.files.length > 0) {
|
|
177
|
+
console.log(chalk.gray('\n Files sẽ được restore:'));
|
|
178
|
+
const displayFiles = targetBackup.files.slice(0, 5);
|
|
179
|
+
for (const f of displayFiles) {
|
|
180
|
+
console.log(chalk.gray(` • ${f.path}`));
|
|
181
|
+
}
|
|
182
|
+
if (targetBackup.files.length > 5) {
|
|
183
|
+
console.log(chalk.gray(` ... và ${targetBackup.files.length - 5} files khác`));
|
|
184
|
+
}
|
|
185
|
+
console.log();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const { confirm } = await inquirer.prompt([
|
|
189
|
+
{
|
|
190
|
+
type: 'confirm',
|
|
191
|
+
name: 'confirm',
|
|
192
|
+
message: `Restore về "${targetBackup.action}"?`,
|
|
193
|
+
default: false
|
|
194
|
+
}
|
|
195
|
+
]);
|
|
196
|
+
|
|
197
|
+
if (!confirm) {
|
|
198
|
+
console.log(chalk.gray('\n👋 Đã huỷ.\n'));
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const result = await backup.restore(backupId);
|
|
203
|
+
showRestoreResult(result);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Clear all backups
|
|
208
|
+
*/
|
|
209
|
+
async function clearBackups(backup, options) {
|
|
210
|
+
const backups = await backup.listBackups();
|
|
211
|
+
|
|
212
|
+
if (backups.length === 0) {
|
|
213
|
+
console.log(chalk.yellow('\n📭 Không có backup nào để xoá.\n'));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (!options.force) {
|
|
218
|
+
console.log(chalk.yellow(`\n⚠️ Sắp xoá ${backups.length} backups.\n`));
|
|
219
|
+
|
|
220
|
+
const { confirm } = await inquirer.prompt([
|
|
221
|
+
{
|
|
222
|
+
type: 'confirm',
|
|
223
|
+
name: 'confirm',
|
|
224
|
+
message: 'Xác nhận xoá tất cả backups?',
|
|
225
|
+
default: false
|
|
226
|
+
}
|
|
227
|
+
]);
|
|
228
|
+
|
|
229
|
+
if (!confirm) {
|
|
230
|
+
console.log(chalk.gray('\n👋 Đã huỷ.\n'));
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
await backup.clearAllBackups();
|
|
236
|
+
console.log(chalk.green(`\n✅ Đã xoá ${backups.length} backups.\n`));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Show restore result
|
|
241
|
+
*/
|
|
242
|
+
function showRestoreResult(result) {
|
|
243
|
+
if (result.success) {
|
|
244
|
+
console.log(chalk.green(`
|
|
245
|
+
╭────────────────────────────────────────────────────────────────────╮
|
|
246
|
+
│ ✅ RESTORE THÀNH CÔNG │
|
|
247
|
+
│ │
|
|
248
|
+
│ Action: ${result.action.substring(0, 54).padEnd(54)}│
|
|
249
|
+
│ Files restored: ${String(result.filesRestored).padEnd(46)}│
|
|
250
|
+
│ │
|
|
251
|
+
╰────────────────────────────────────────────────────────────────────╯
|
|
252
|
+
`));
|
|
253
|
+
|
|
254
|
+
// Show some restored files
|
|
255
|
+
if (result.files && result.files.length > 0) {
|
|
256
|
+
console.log(chalk.gray(' Files đã restore:'));
|
|
257
|
+
for (const f of result.files.slice(0, 5)) {
|
|
258
|
+
console.log(chalk.gray(` ✓ ${f}`));
|
|
259
|
+
}
|
|
260
|
+
if (result.files.length > 5) {
|
|
261
|
+
console.log(chalk.gray(` ... và ${result.files.length - 5} files khác`));
|
|
262
|
+
}
|
|
263
|
+
console.log();
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
console.log(chalk.red(`\n❌ Restore thất bại: ${result.error}\n`));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Get human-readable time ago string
|
|
272
|
+
*/
|
|
273
|
+
function getTimeAgo(date) {
|
|
274
|
+
const seconds = Math.floor((new Date() - date) / 1000);
|
|
275
|
+
|
|
276
|
+
if (seconds < 60) return 'vừa xong';
|
|
277
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)} phút trước`;
|
|
278
|
+
if (seconds < 86400) return `${Math.floor(seconds / 3600)} giờ trước`;
|
|
279
|
+
if (seconds < 604800) return `${Math.floor(seconds / 86400)} ngày trước`;
|
|
280
|
+
return `${Math.floor(seconds / 604800)} tuần trước`;
|
|
281
|
+
}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// VIBECODE CLI - Interactive Wizard
|
|
3
|
+
// Phase H1: Smart Defaults - No args = Interactive menu
|
|
4
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
|
|
6
|
+
import inquirer from 'inquirer';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { VERSION } from '../config/constants.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Wizard Command - Interactive menu when no args provided
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* vibecode (no args) → Shows interactive menu
|
|
15
|
+
*/
|
|
16
|
+
export async function wizardCommand() {
|
|
17
|
+
// Show welcome banner
|
|
18
|
+
console.log(chalk.cyan(`
|
|
19
|
+
╭────────────────────────────────────────────────────────────────────╮
|
|
20
|
+
│ │
|
|
21
|
+
│ 🏗️ VIBECODE v${VERSION.padEnd(43)}│
|
|
22
|
+
│ Build Software with Discipline │
|
|
23
|
+
│ │
|
|
24
|
+
╰────────────────────────────────────────────────────────────────────╯
|
|
25
|
+
`));
|
|
26
|
+
|
|
27
|
+
const { action } = await inquirer.prompt([
|
|
28
|
+
{
|
|
29
|
+
type: 'list',
|
|
30
|
+
name: 'action',
|
|
31
|
+
message: 'Bạn muốn làm gì hôm nay?',
|
|
32
|
+
choices: [
|
|
33
|
+
{ name: '🚀 Tạo project mới nhanh', value: 'go' },
|
|
34
|
+
{ name: '🤖 Build project phức tạp (nhiều modules)', value: 'agent' },
|
|
35
|
+
{ name: '🔍 Debug/fix lỗi', value: 'debug' },
|
|
36
|
+
{ name: '🤝 Trợ giúp từ AI', value: 'assist' },
|
|
37
|
+
new inquirer.Separator(),
|
|
38
|
+
{ name: '📊 Xem trạng thái project hiện tại', value: 'status' },
|
|
39
|
+
{ name: '⚙️ Cài đặt', value: 'config' },
|
|
40
|
+
{ name: '📁 Khởi tạo workspace mới', value: 'init' },
|
|
41
|
+
new inquirer.Separator(),
|
|
42
|
+
{ name: '❓ Xem trợ giúp', value: 'help' },
|
|
43
|
+
{ name: '👋 Thoát', value: 'exit' }
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
switch (action) {
|
|
49
|
+
case 'go':
|
|
50
|
+
await handleGo();
|
|
51
|
+
break;
|
|
52
|
+
case 'agent':
|
|
53
|
+
await handleAgent();
|
|
54
|
+
break;
|
|
55
|
+
case 'debug':
|
|
56
|
+
await handleDebug();
|
|
57
|
+
break;
|
|
58
|
+
case 'assist':
|
|
59
|
+
await handleAssist();
|
|
60
|
+
break;
|
|
61
|
+
case 'status':
|
|
62
|
+
await handleStatus();
|
|
63
|
+
break;
|
|
64
|
+
case 'config':
|
|
65
|
+
await handleConfig();
|
|
66
|
+
break;
|
|
67
|
+
case 'init':
|
|
68
|
+
await handleInit();
|
|
69
|
+
break;
|
|
70
|
+
case 'help':
|
|
71
|
+
showHelp();
|
|
72
|
+
break;
|
|
73
|
+
case 'exit':
|
|
74
|
+
console.log(chalk.cyan('\n👋 Hẹn gặp lại!\n'));
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Handle Go command with prompts
|
|
81
|
+
*/
|
|
82
|
+
async function handleGo() {
|
|
83
|
+
const { goCommand } = await import('./go.js');
|
|
84
|
+
|
|
85
|
+
const { description } = await inquirer.prompt([
|
|
86
|
+
{
|
|
87
|
+
type: 'input',
|
|
88
|
+
name: 'description',
|
|
89
|
+
message: 'Mô tả project bạn muốn tạo:',
|
|
90
|
+
validate: (input) => input.length > 5 || 'Vui lòng mô tả chi tiết hơn (ít nhất 6 ký tự)'
|
|
91
|
+
}
|
|
92
|
+
]);
|
|
93
|
+
|
|
94
|
+
const { template } = await inquirer.prompt([
|
|
95
|
+
{
|
|
96
|
+
type: 'list',
|
|
97
|
+
name: 'template',
|
|
98
|
+
message: 'Chọn template (hoặc để trống):',
|
|
99
|
+
choices: [
|
|
100
|
+
{ name: '🎨 Không dùng template (tự do)', value: null },
|
|
101
|
+
{ name: '🌐 Landing page', value: 'landing' },
|
|
102
|
+
{ name: '💼 SaaS application', value: 'saas' },
|
|
103
|
+
{ name: '⌨️ CLI tool', value: 'cli' },
|
|
104
|
+
{ name: '🔌 REST API', value: 'api' }
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
const { confirm } = await inquirer.prompt([
|
|
110
|
+
{
|
|
111
|
+
type: 'confirm',
|
|
112
|
+
name: 'confirm',
|
|
113
|
+
message: `Tạo project: "${description}"${template ? ` (template: ${template})` : ''}?`,
|
|
114
|
+
default: true
|
|
115
|
+
}
|
|
116
|
+
]);
|
|
117
|
+
|
|
118
|
+
if (confirm) {
|
|
119
|
+
const options = {};
|
|
120
|
+
if (template) options.template = template;
|
|
121
|
+
await goCommand(description, options);
|
|
122
|
+
} else {
|
|
123
|
+
console.log(chalk.gray('\n✗ Đã huỷ.\n'));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Handle Agent command with prompts
|
|
129
|
+
*/
|
|
130
|
+
async function handleAgent() {
|
|
131
|
+
const { agentCommand } = await import('./agent.js');
|
|
132
|
+
|
|
133
|
+
const { description } = await inquirer.prompt([
|
|
134
|
+
{
|
|
135
|
+
type: 'input',
|
|
136
|
+
name: 'description',
|
|
137
|
+
message: 'Mô tả project phức tạp (VD: SaaS với auth, billing, dashboard):',
|
|
138
|
+
validate: (input) => input.length > 10 || 'Vui lòng mô tả chi tiết hơn (ít nhất 11 ký tự)'
|
|
139
|
+
}
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
const { createNew } = await inquirer.prompt([
|
|
143
|
+
{
|
|
144
|
+
type: 'confirm',
|
|
145
|
+
name: 'createNew',
|
|
146
|
+
message: 'Tạo thư mục project mới?',
|
|
147
|
+
default: true
|
|
148
|
+
}
|
|
149
|
+
]);
|
|
150
|
+
|
|
151
|
+
const { verbose } = await inquirer.prompt([
|
|
152
|
+
{
|
|
153
|
+
type: 'confirm',
|
|
154
|
+
name: 'verbose',
|
|
155
|
+
message: 'Hiển thị chi tiết quá trình build?',
|
|
156
|
+
default: false
|
|
157
|
+
}
|
|
158
|
+
]);
|
|
159
|
+
|
|
160
|
+
console.log(chalk.blue('\n🤖 Bắt đầu Agent Mode...\n'));
|
|
161
|
+
await agentCommand(description, { new: createNew, verbose });
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Handle Debug command
|
|
166
|
+
*/
|
|
167
|
+
async function handleDebug() {
|
|
168
|
+
const { debugCommand } = await import('./debug.js');
|
|
169
|
+
|
|
170
|
+
const { mode } = await inquirer.prompt([
|
|
171
|
+
{
|
|
172
|
+
type: 'list',
|
|
173
|
+
name: 'mode',
|
|
174
|
+
message: 'Chọn chế độ debug:',
|
|
175
|
+
choices: [
|
|
176
|
+
{ name: '🔄 Interactive - Debug trực tiếp', value: 'interactive' },
|
|
177
|
+
{ name: '🔍 Auto-scan - Tự động tìm và fix lỗi', value: 'auto' },
|
|
178
|
+
{ name: '📝 Mô tả lỗi', value: 'describe' }
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
]);
|
|
182
|
+
|
|
183
|
+
if (mode === 'interactive') {
|
|
184
|
+
await debugCommand([], { interactive: true });
|
|
185
|
+
} else if (mode === 'auto') {
|
|
186
|
+
await debugCommand([], { auto: true });
|
|
187
|
+
} else {
|
|
188
|
+
const { description } = await inquirer.prompt([
|
|
189
|
+
{
|
|
190
|
+
type: 'input',
|
|
191
|
+
name: 'description',
|
|
192
|
+
message: 'Mô tả lỗi bạn gặp phải:',
|
|
193
|
+
validate: (input) => input.length > 5 || 'Vui lòng mô tả chi tiết hơn'
|
|
194
|
+
}
|
|
195
|
+
]);
|
|
196
|
+
await debugCommand([description], {});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Handle Assist command
|
|
202
|
+
*/
|
|
203
|
+
async function handleAssist() {
|
|
204
|
+
const { assistCommand } = await import('./assist.js');
|
|
205
|
+
|
|
206
|
+
const { hasPrompt } = await inquirer.prompt([
|
|
207
|
+
{
|
|
208
|
+
type: 'confirm',
|
|
209
|
+
name: 'hasPrompt',
|
|
210
|
+
message: 'Bạn có câu hỏi cụ thể không?',
|
|
211
|
+
default: false
|
|
212
|
+
}
|
|
213
|
+
]);
|
|
214
|
+
|
|
215
|
+
if (hasPrompt) {
|
|
216
|
+
const { prompt } = await inquirer.prompt([
|
|
217
|
+
{
|
|
218
|
+
type: 'input',
|
|
219
|
+
name: 'prompt',
|
|
220
|
+
message: 'Nhập câu hỏi của bạn:',
|
|
221
|
+
validate: (input) => input.length > 3 || 'Vui lòng nhập câu hỏi'
|
|
222
|
+
}
|
|
223
|
+
]);
|
|
224
|
+
await assistCommand([prompt], {});
|
|
225
|
+
} else {
|
|
226
|
+
await assistCommand([], {});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Handle Status command
|
|
232
|
+
*/
|
|
233
|
+
async function handleStatus() {
|
|
234
|
+
const { statusCommand } = await import('./status.js');
|
|
235
|
+
await statusCommand({});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Handle Config command
|
|
240
|
+
*/
|
|
241
|
+
async function handleConfig() {
|
|
242
|
+
const { configCommand } = await import('./config.js');
|
|
243
|
+
|
|
244
|
+
const { action } = await inquirer.prompt([
|
|
245
|
+
{
|
|
246
|
+
type: 'list',
|
|
247
|
+
name: 'action',
|
|
248
|
+
message: 'Cài đặt:',
|
|
249
|
+
choices: [
|
|
250
|
+
{ name: '👀 Xem cài đặt hiện tại', value: 'show' },
|
|
251
|
+
{ name: '🔧 Thay đổi provider', value: 'provider' }
|
|
252
|
+
]
|
|
253
|
+
}
|
|
254
|
+
]);
|
|
255
|
+
|
|
256
|
+
if (action === 'show') {
|
|
257
|
+
await configCommand({ show: true });
|
|
258
|
+
} else {
|
|
259
|
+
const { provider } = await inquirer.prompt([
|
|
260
|
+
{
|
|
261
|
+
type: 'list',
|
|
262
|
+
name: 'provider',
|
|
263
|
+
message: 'Chọn AI provider:',
|
|
264
|
+
choices: [
|
|
265
|
+
{ name: 'Claude Code (mặc định)', value: 'claude-code' }
|
|
266
|
+
]
|
|
267
|
+
}
|
|
268
|
+
]);
|
|
269
|
+
await configCommand({ provider });
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Handle Init command
|
|
275
|
+
*/
|
|
276
|
+
async function handleInit() {
|
|
277
|
+
const { initCommand } = await import('./init.js');
|
|
278
|
+
|
|
279
|
+
const { confirm } = await inquirer.prompt([
|
|
280
|
+
{
|
|
281
|
+
type: 'confirm',
|
|
282
|
+
name: 'confirm',
|
|
283
|
+
message: `Khởi tạo Vibecode workspace tại ${process.cwd()}?`,
|
|
284
|
+
default: true
|
|
285
|
+
}
|
|
286
|
+
]);
|
|
287
|
+
|
|
288
|
+
if (confirm) {
|
|
289
|
+
await initCommand({});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Show help text
|
|
295
|
+
*/
|
|
296
|
+
function showHelp() {
|
|
297
|
+
console.log(chalk.white(`
|
|
298
|
+
📚 VIBECODE COMMANDS
|
|
299
|
+
|
|
300
|
+
${chalk.cyan('Workflow cơ bản:')}
|
|
301
|
+
vibecode init Khởi tạo workspace
|
|
302
|
+
vibecode start Bắt đầu intake → blueprint → contract
|
|
303
|
+
vibecode lock Khoá contract
|
|
304
|
+
vibecode plan Tạo execution plan
|
|
305
|
+
vibecode build Build với AI
|
|
306
|
+
vibecode review Kiểm tra kết quả
|
|
307
|
+
vibecode snapshot Tạo release
|
|
308
|
+
|
|
309
|
+
${chalk.cyan('Power commands:')}
|
|
310
|
+
vibecode go "..." Một lệnh, tạo cả project
|
|
311
|
+
vibecode agent "..." Build project phức tạp tự động
|
|
312
|
+
vibecode debug Debug thông minh 9 bước
|
|
313
|
+
vibecode assist Trợ giúp trực tiếp từ AI
|
|
314
|
+
|
|
315
|
+
${chalk.cyan('Khác:')}
|
|
316
|
+
vibecode status Xem trạng thái
|
|
317
|
+
vibecode config Cài đặt
|
|
318
|
+
vibecode doctor Kiểm tra health
|
|
319
|
+
|
|
320
|
+
📖 Chi tiết: ${chalk.yellow('vibecode <command> --help')}
|
|
321
|
+
`));
|
|
322
|
+
}
|