@nclamvn/vibecode-cli 2.0.0 → 2.2.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/SESSION_NOTES.md +154 -0
- package/bin/vibecode.js +235 -2
- package/package.json +5 -2
- package/src/agent/decomposition.js +476 -0
- package/src/agent/index.js +391 -0
- package/src/agent/memory.js +542 -0
- package/src/agent/orchestrator.js +917 -0
- package/src/agent/self-healing.js +516 -0
- package/src/commands/agent.js +349 -0
- package/src/commands/ask.js +230 -0
- package/src/commands/assist.js +413 -0
- package/src/commands/build.js +345 -4
- package/src/commands/debug.js +565 -0
- package/src/commands/docs.js +167 -0
- package/src/commands/git.js +1024 -0
- package/src/commands/go.js +635 -0
- package/src/commands/learn.js +294 -0
- package/src/commands/migrate.js +341 -0
- package/src/commands/plan.js +8 -2
- package/src/commands/refactor.js +205 -0
- package/src/commands/review.js +126 -1
- package/src/commands/security.js +229 -0
- package/src/commands/shell.js +486 -0
- package/src/commands/templates.js +397 -0
- package/src/commands/test.js +194 -0
- package/src/commands/undo.js +281 -0
- package/src/commands/watch.js +556 -0
- package/src/commands/wizard.js +322 -0
- package/src/config/constants.js +5 -1
- package/src/config/templates.js +146 -15
- package/src/core/backup.js +325 -0
- package/src/core/error-analyzer.js +237 -0
- package/src/core/fix-generator.js +195 -0
- package/src/core/iteration.js +226 -0
- package/src/core/learning.js +295 -0
- package/src/core/session.js +18 -2
- package/src/core/test-runner.js +281 -0
- package/src/debug/analyzer.js +329 -0
- package/src/debug/evidence.js +228 -0
- package/src/debug/fixer.js +348 -0
- package/src/debug/image-analyzer.js +304 -0
- package/src/debug/index.js +378 -0
- package/src/debug/verifier.js +346 -0
- package/src/index.js +102 -0
- package/src/providers/claude-code.js +12 -7
- package/src/templates/index.js +724 -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
- package/src/utils/image.js +222 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// VIBECODE CLI - Learn Command
|
|
3
|
+
// Phase H5: View and manage AI learnings
|
|
4
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import inquirer from 'inquirer';
|
|
8
|
+
import fs from 'fs/promises';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { LearningEngine } from '../core/learning.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Learn Command - View and manage AI learnings
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* vibecode learn - Interactive menu
|
|
17
|
+
* vibecode learn --stats - Show learning statistics
|
|
18
|
+
* vibecode learn --clear - Clear all learnings
|
|
19
|
+
* vibecode learn --export - Export learnings to file
|
|
20
|
+
*/
|
|
21
|
+
export async function learnCommand(options = {}) {
|
|
22
|
+
const learning = new LearningEngine();
|
|
23
|
+
|
|
24
|
+
if (options.stats) {
|
|
25
|
+
await showStats(learning);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (options.clear) {
|
|
30
|
+
await clearLearnings(learning, options.force);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (options.export) {
|
|
35
|
+
await exportLearnings(learning);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Default: show interactive menu
|
|
40
|
+
await interactiveLearn(learning);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Show learning statistics
|
|
45
|
+
*/
|
|
46
|
+
async function showStats(learning) {
|
|
47
|
+
const stats = await learning.getStats();
|
|
48
|
+
|
|
49
|
+
console.log(chalk.cyan(`
|
|
50
|
+
╭────────────────────────────────────────────────────────────────────╮
|
|
51
|
+
│ 📊 LEARNING STATISTICS │
|
|
52
|
+
╰────────────────────────────────────────────────────────────────────╯
|
|
53
|
+
`));
|
|
54
|
+
|
|
55
|
+
console.log(chalk.white(` 📁 Project Learnings`));
|
|
56
|
+
console.log(chalk.gray(` Tổng fixes: ${stats.local.total}`));
|
|
57
|
+
console.log(chalk.gray(` Thành công: ${stats.local.success} (${stats.local.rate}%)`));
|
|
58
|
+
console.log('');
|
|
59
|
+
|
|
60
|
+
console.log(chalk.white(` 🌍 Global Learnings`));
|
|
61
|
+
console.log(chalk.gray(` Tổng fixes: ${stats.global.total}`));
|
|
62
|
+
console.log(chalk.gray(` Thành công: ${stats.global.success} (${stats.global.rate}%)`));
|
|
63
|
+
console.log('');
|
|
64
|
+
|
|
65
|
+
if (Object.keys(stats.byCategory).length > 0) {
|
|
66
|
+
console.log(chalk.white(` 📂 Theo Error Category`));
|
|
67
|
+
for (const [cat, data] of Object.entries(stats.byCategory)) {
|
|
68
|
+
const rate = data.total > 0 ? (data.success / data.total * 100).toFixed(0) : 0;
|
|
69
|
+
const bar = renderMiniBar(data.success, data.total);
|
|
70
|
+
console.log(chalk.gray(` ${cat.padEnd(12)} ${bar} ${data.success}/${data.total} (${rate}%)`));
|
|
71
|
+
}
|
|
72
|
+
console.log('');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log(chalk.white(` ⚙️ Preferences đã lưu: ${stats.preferences}`));
|
|
76
|
+
|
|
77
|
+
if (stats.lastLearning) {
|
|
78
|
+
const lastDate = new Date(stats.lastLearning).toLocaleString('vi-VN');
|
|
79
|
+
console.log(chalk.gray(` 📅 Learning gần nhất: ${lastDate}`));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log('');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Render a mini progress bar
|
|
87
|
+
*/
|
|
88
|
+
function renderMiniBar(value, total) {
|
|
89
|
+
const width = 10;
|
|
90
|
+
const filled = total > 0 ? Math.round(width * value / total) : 0;
|
|
91
|
+
const empty = width - filled;
|
|
92
|
+
return chalk.green('█'.repeat(filled)) + chalk.gray('░'.repeat(empty));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Clear all local learnings
|
|
97
|
+
*/
|
|
98
|
+
async function clearLearnings(learning, force) {
|
|
99
|
+
const stats = await learning.getStats();
|
|
100
|
+
|
|
101
|
+
if (stats.local.total === 0) {
|
|
102
|
+
console.log(chalk.yellow('\n📭 Không có learnings nào để xoá.\n'));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!force) {
|
|
107
|
+
console.log(chalk.yellow(`\n⚠️ Sắp xoá ${stats.local.total} learnings của project này.\n`));
|
|
108
|
+
|
|
109
|
+
const { confirm } = await inquirer.prompt([
|
|
110
|
+
{
|
|
111
|
+
type: 'confirm',
|
|
112
|
+
name: 'confirm',
|
|
113
|
+
message: 'Xác nhận xoá tất cả learnings?',
|
|
114
|
+
default: false
|
|
115
|
+
}
|
|
116
|
+
]);
|
|
117
|
+
|
|
118
|
+
if (!confirm) {
|
|
119
|
+
console.log(chalk.gray('\n👋 Đã huỷ.\n'));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
await learning.clearLocal();
|
|
125
|
+
console.log(chalk.green(`\n✅ Đã xoá ${stats.local.total} learnings.\n`));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Export learnings to file
|
|
130
|
+
*/
|
|
131
|
+
async function exportLearnings(learning) {
|
|
132
|
+
const stats = await learning.getStats();
|
|
133
|
+
const fixes = await learning.loadJson(
|
|
134
|
+
path.join(learning.localPath, 'fixes.json'),
|
|
135
|
+
[]
|
|
136
|
+
);
|
|
137
|
+
const prefs = await learning.loadJson(
|
|
138
|
+
path.join(learning.localPath, 'preferences.json'),
|
|
139
|
+
{}
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
if (fixes.length === 0 && Object.keys(prefs).length === 0) {
|
|
143
|
+
console.log(chalk.yellow('\n📭 Không có learnings nào để export.\n'));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const exportData = {
|
|
148
|
+
exportedAt: new Date().toISOString(),
|
|
149
|
+
projectPath: learning.projectPath,
|
|
150
|
+
stats,
|
|
151
|
+
fixes: fixes.map(f => ({
|
|
152
|
+
id: f.id,
|
|
153
|
+
errorType: f.errorType,
|
|
154
|
+
errorCategory: f.errorCategory,
|
|
155
|
+
success: f.success,
|
|
156
|
+
userFeedback: f.userFeedback,
|
|
157
|
+
projectType: f.projectType,
|
|
158
|
+
timestamp: f.timestamp
|
|
159
|
+
})),
|
|
160
|
+
preferences: prefs
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const exportPath = `vibecode-learnings-${Date.now()}.json`;
|
|
164
|
+
await fs.writeFile(exportPath, JSON.stringify(exportData, null, 2));
|
|
165
|
+
|
|
166
|
+
console.log(chalk.green(`\n✅ Đã export ${fixes.length} learnings → ${exportPath}\n`));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Interactive learning menu
|
|
171
|
+
*/
|
|
172
|
+
async function interactiveLearn(learning) {
|
|
173
|
+
const stats = await learning.getStats();
|
|
174
|
+
|
|
175
|
+
const successBar = renderMiniBar(stats.local.success, stats.local.total);
|
|
176
|
+
|
|
177
|
+
console.log(chalk.cyan(`
|
|
178
|
+
╭────────────────────────────────────────────────────────────────────╮
|
|
179
|
+
│ 🧠 VIBECODE LEARNING │
|
|
180
|
+
│ │
|
|
181
|
+
│ AI học từ feedback của bạn để cải thiện suggestions. │
|
|
182
|
+
│ │
|
|
183
|
+
│ Success rate: ${successBar} ${String(stats.local.rate + '%').padEnd(30)}│
|
|
184
|
+
│ Total learnings: ${String(stats.local.total).padEnd(44)}│
|
|
185
|
+
│ │
|
|
186
|
+
╰────────────────────────────────────────────────────────────────────╯
|
|
187
|
+
`));
|
|
188
|
+
|
|
189
|
+
const { action } = await inquirer.prompt([
|
|
190
|
+
{
|
|
191
|
+
type: 'list',
|
|
192
|
+
name: 'action',
|
|
193
|
+
message: 'Bạn muốn làm gì?',
|
|
194
|
+
choices: [
|
|
195
|
+
{ name: '📊 Xem thống kê chi tiết', value: 'stats' },
|
|
196
|
+
{ name: '📤 Export learnings', value: 'export' },
|
|
197
|
+
{ name: '🗑️ Xoá learnings', value: 'clear' },
|
|
198
|
+
new inquirer.Separator(),
|
|
199
|
+
{ name: '👋 Thoát', value: 'exit' }
|
|
200
|
+
]
|
|
201
|
+
}
|
|
202
|
+
]);
|
|
203
|
+
|
|
204
|
+
switch (action) {
|
|
205
|
+
case 'stats':
|
|
206
|
+
await showStats(learning);
|
|
207
|
+
break;
|
|
208
|
+
case 'export':
|
|
209
|
+
await exportLearnings(learning);
|
|
210
|
+
break;
|
|
211
|
+
case 'clear':
|
|
212
|
+
await clearLearnings(learning, false);
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Ask for feedback after a fix
|
|
219
|
+
* Called from debug/fix commands
|
|
220
|
+
*/
|
|
221
|
+
export async function askFeedback(fixInfo) {
|
|
222
|
+
console.log('');
|
|
223
|
+
const { feedback } = await inquirer.prompt([
|
|
224
|
+
{
|
|
225
|
+
type: 'list',
|
|
226
|
+
name: 'feedback',
|
|
227
|
+
message: 'Fix này có đúng không?',
|
|
228
|
+
choices: [
|
|
229
|
+
{ name: '✅ Đúng, hoạt động tốt', value: 'success' },
|
|
230
|
+
{ name: '❌ Không đúng', value: 'failed' },
|
|
231
|
+
{ name: '🔄 Đúng một phần', value: 'partial' },
|
|
232
|
+
{ name: '⏭️ Bỏ qua', value: 'skip' }
|
|
233
|
+
]
|
|
234
|
+
}
|
|
235
|
+
]);
|
|
236
|
+
|
|
237
|
+
if (feedback === 'skip') {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const learning = new LearningEngine();
|
|
242
|
+
let userCorrection = null;
|
|
243
|
+
|
|
244
|
+
if (feedback === 'failed' || feedback === 'partial') {
|
|
245
|
+
const { correction } = await inquirer.prompt([
|
|
246
|
+
{
|
|
247
|
+
type: 'input',
|
|
248
|
+
name: 'correction',
|
|
249
|
+
message: 'Mô tả ngắn vấn đề hoặc cách fix đúng (Enter để bỏ qua):',
|
|
250
|
+
}
|
|
251
|
+
]);
|
|
252
|
+
userCorrection = correction || null;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
await learning.recordFix({
|
|
256
|
+
errorType: fixInfo.errorType,
|
|
257
|
+
errorMessage: fixInfo.errorMessage,
|
|
258
|
+
errorCategory: fixInfo.errorCategory,
|
|
259
|
+
fixApplied: fixInfo.fixApplied,
|
|
260
|
+
success: feedback === 'success',
|
|
261
|
+
userFeedback: feedback,
|
|
262
|
+
userCorrection
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
if (feedback === 'success') {
|
|
266
|
+
console.log(chalk.green(' ✅ Đã ghi nhận. Cảm ơn!\n'));
|
|
267
|
+
} else {
|
|
268
|
+
console.log(chalk.yellow(' 📝 Đã ghi nhận feedback.\n'));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return feedback;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Show learning-based suggestion (if available)
|
|
276
|
+
*/
|
|
277
|
+
export async function showLearningSuggestion(errorType, errorCategory) {
|
|
278
|
+
const learning = new LearningEngine();
|
|
279
|
+
const suggestion = await learning.getSuggestion(errorType, errorCategory);
|
|
280
|
+
|
|
281
|
+
if (suggestion && suggestion.confidence > 0.6) {
|
|
282
|
+
const confidencePercent = (suggestion.confidence * 100).toFixed(0);
|
|
283
|
+
console.log(chalk.cyan(` 💡 Dựa trên ${suggestion.basedOn} fixes trước (độ tin cậy: ${confidencePercent}%)`));
|
|
284
|
+
|
|
285
|
+
if (suggestion.suggestion) {
|
|
286
|
+
const shortSuggestion = suggestion.suggestion.substring(0, 100);
|
|
287
|
+
console.log(chalk.gray(` Gợi ý: ${shortSuggestion}${suggestion.suggestion.length > 100 ? '...' : ''}`));
|
|
288
|
+
}
|
|
289
|
+
console.log('');
|
|
290
|
+
return suggestion;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// VIBECODE CLI - Migrate Command
|
|
3
|
+
// Phase K8: AI-Powered Code Migration
|
|
4
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
|
|
6
|
+
import { spawn } from 'child_process';
|
|
7
|
+
import fs from 'fs/promises';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import inquirer from 'inquirer';
|
|
11
|
+
import { BackupManager } from '../core/backup.js';
|
|
12
|
+
|
|
13
|
+
export async function migrateCommand(description, options = {}) {
|
|
14
|
+
const cwd = process.cwd();
|
|
15
|
+
|
|
16
|
+
// Interactive mode
|
|
17
|
+
if (!description || description.length === 0) {
|
|
18
|
+
return interactiveMigrate(cwd);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const migrationDesc = Array.isArray(description) ? description.join(' ') : description;
|
|
22
|
+
return performMigration(cwd, migrationDesc, options);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function interactiveMigrate(cwd) {
|
|
26
|
+
console.log(chalk.cyan(`
|
|
27
|
+
╭────────────────────────────────────────────────────────────────────╮
|
|
28
|
+
│ 🔄 CODE MIGRATION │
|
|
29
|
+
│ │
|
|
30
|
+
│ AI-powered code migration and transformation │
|
|
31
|
+
│ │
|
|
32
|
+
╰────────────────────────────────────────────────────────────────────╯
|
|
33
|
+
`));
|
|
34
|
+
|
|
35
|
+
const { migrationType } = await inquirer.prompt([{
|
|
36
|
+
type: 'list',
|
|
37
|
+
name: 'migrationType',
|
|
38
|
+
message: 'What migration do you need?',
|
|
39
|
+
choices: [
|
|
40
|
+
{ name: '📘 JavaScript → TypeScript', value: 'js-to-ts' },
|
|
41
|
+
{ name: '⚛️ Class Components → Hooks', value: 'class-to-hooks' },
|
|
42
|
+
{ name: '🔄 CommonJS → ESM', value: 'cjs-to-esm' },
|
|
43
|
+
{ name: '📦 REST → GraphQL', value: 'rest-to-graphql' },
|
|
44
|
+
{ name: '🎨 CSS → Tailwind', value: 'css-to-tailwind' },
|
|
45
|
+
{ name: '🗄️ Mongoose → Prisma', value: 'mongoose-to-prisma' },
|
|
46
|
+
{ name: '⚡ Express → Fastify', value: 'express-to-fastify' },
|
|
47
|
+
{ name: '📱 Pages Router → App Router', value: 'pages-to-app' },
|
|
48
|
+
{ name: '🧪 Jest → Vitest', value: 'jest-to-vitest' },
|
|
49
|
+
{ name: '✏️ Custom migration', value: 'custom' },
|
|
50
|
+
{ name: '👋 Exit', value: 'exit' }
|
|
51
|
+
]
|
|
52
|
+
}]);
|
|
53
|
+
|
|
54
|
+
if (migrationType === 'exit') return;
|
|
55
|
+
|
|
56
|
+
let migrationDesc = migrationType;
|
|
57
|
+
|
|
58
|
+
if (migrationType === 'custom') {
|
|
59
|
+
const { desc } = await inquirer.prompt([{
|
|
60
|
+
type: 'input',
|
|
61
|
+
name: 'desc',
|
|
62
|
+
message: 'Describe your migration:'
|
|
63
|
+
}]);
|
|
64
|
+
migrationDesc = desc;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const { targetPath } = await inquirer.prompt([{
|
|
68
|
+
type: 'input',
|
|
69
|
+
name: 'targetPath',
|
|
70
|
+
message: 'Path to migrate (leave empty for entire project):',
|
|
71
|
+
default: ''
|
|
72
|
+
}]);
|
|
73
|
+
|
|
74
|
+
return performMigration(cwd, migrationDesc, { path: targetPath });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function performMigration(cwd, migrationDesc, options) {
|
|
78
|
+
// Create backup
|
|
79
|
+
console.log(chalk.gray('\n Creating backup before migration...\n'));
|
|
80
|
+
const backup = new BackupManager(cwd);
|
|
81
|
+
await backup.createBackup(`migrate-${Date.now()}`);
|
|
82
|
+
|
|
83
|
+
const migrations = {
|
|
84
|
+
'js-to-ts': `
|
|
85
|
+
# JavaScript to TypeScript Migration
|
|
86
|
+
|
|
87
|
+
Convert JavaScript files to TypeScript:
|
|
88
|
+
|
|
89
|
+
1. **Setup**
|
|
90
|
+
- Create/update tsconfig.json with appropriate settings
|
|
91
|
+
- Install TypeScript and @types packages
|
|
92
|
+
|
|
93
|
+
2. **File Conversion**
|
|
94
|
+
- Rename .js files to .ts/.tsx (for React components)
|
|
95
|
+
- Add type annotations to variables, parameters, return values
|
|
96
|
+
- Define interfaces/types for objects and props
|
|
97
|
+
- Fix any TypeScript errors
|
|
98
|
+
|
|
99
|
+
3. **Best Practices**
|
|
100
|
+
- Use strict mode
|
|
101
|
+
- Avoid 'any' where possible
|
|
102
|
+
- Use enums for constants
|
|
103
|
+
- Add proper generics
|
|
104
|
+
|
|
105
|
+
4. **Cleanup**
|
|
106
|
+
- Update imports
|
|
107
|
+
- Remove JSDoc type annotations (now in code)
|
|
108
|
+
`,
|
|
109
|
+
'class-to-hooks': `
|
|
110
|
+
# React Class to Hooks Migration
|
|
111
|
+
|
|
112
|
+
Convert React class components to functional components with hooks:
|
|
113
|
+
|
|
114
|
+
1. **Component Conversion**
|
|
115
|
+
- Convert class to function component
|
|
116
|
+
- Replace this.state with useState
|
|
117
|
+
- Replace lifecycle methods:
|
|
118
|
+
- componentDidMount → useEffect(..., [])
|
|
119
|
+
- componentDidUpdate → useEffect
|
|
120
|
+
- componentWillUnmount → useEffect cleanup
|
|
121
|
+
|
|
122
|
+
2. **Methods & Bindings**
|
|
123
|
+
- Convert class methods to functions
|
|
124
|
+
- Remove .bind(this) calls
|
|
125
|
+
- Use useCallback for memoized callbacks
|
|
126
|
+
|
|
127
|
+
3. **Refs & Context**
|
|
128
|
+
- Replace createRef with useRef
|
|
129
|
+
- Replace this.context with useContext
|
|
130
|
+
|
|
131
|
+
4. **State Management**
|
|
132
|
+
- Use useReducer for complex state
|
|
133
|
+
- Lift state or use context where appropriate
|
|
134
|
+
`,
|
|
135
|
+
'cjs-to-esm': `
|
|
136
|
+
# CommonJS to ES Modules Migration
|
|
137
|
+
|
|
138
|
+
Convert CommonJS to ESM:
|
|
139
|
+
|
|
140
|
+
1. **Syntax Changes**
|
|
141
|
+
- Replace require() with import
|
|
142
|
+
- Replace module.exports with export
|
|
143
|
+
- Replace exports.x with export const x
|
|
144
|
+
|
|
145
|
+
2. **Package.json**
|
|
146
|
+
- Add "type": "module"
|
|
147
|
+
- Update main/exports fields
|
|
148
|
+
|
|
149
|
+
3. **File Extensions**
|
|
150
|
+
- Ensure .js extensions in imports (or configure bundler)
|
|
151
|
+
|
|
152
|
+
4. **Dynamic Imports**
|
|
153
|
+
- Replace require() in conditionals with import()
|
|
154
|
+
- Handle __dirname, __filename with import.meta.url
|
|
155
|
+
`,
|
|
156
|
+
'rest-to-graphql': `
|
|
157
|
+
# REST to GraphQL Migration
|
|
158
|
+
|
|
159
|
+
Convert REST API to GraphQL:
|
|
160
|
+
|
|
161
|
+
1. **Schema Design**
|
|
162
|
+
- Create GraphQL types from REST response shapes
|
|
163
|
+
- Design queries for GET endpoints
|
|
164
|
+
- Design mutations for POST/PUT/DELETE
|
|
165
|
+
|
|
166
|
+
2. **Server Setup**
|
|
167
|
+
- Set up Apollo Server or similar
|
|
168
|
+
- Create resolvers for each query/mutation
|
|
169
|
+
- Connect to existing data sources
|
|
170
|
+
|
|
171
|
+
3. **Client Migration**
|
|
172
|
+
- Replace fetch/axios calls with GraphQL queries
|
|
173
|
+
- Set up Apollo Client or urql
|
|
174
|
+
- Use generated types if TypeScript
|
|
175
|
+
`,
|
|
176
|
+
'css-to-tailwind': `
|
|
177
|
+
# CSS to Tailwind Migration
|
|
178
|
+
|
|
179
|
+
Convert CSS/SCSS to Tailwind classes:
|
|
180
|
+
|
|
181
|
+
1. **Setup**
|
|
182
|
+
- Install and configure Tailwind CSS
|
|
183
|
+
- Set up tailwind.config.js
|
|
184
|
+
|
|
185
|
+
2. **Conversion**
|
|
186
|
+
- Replace CSS properties with Tailwind utilities
|
|
187
|
+
- Convert custom values to Tailwind config
|
|
188
|
+
- Handle responsive designs with breakpoint prefixes
|
|
189
|
+
|
|
190
|
+
3. **Components**
|
|
191
|
+
- Use @apply for repeated patterns
|
|
192
|
+
- Create component classes where needed
|
|
193
|
+
- Remove unused CSS
|
|
194
|
+
|
|
195
|
+
4. **Cleanup**
|
|
196
|
+
- Remove old CSS files
|
|
197
|
+
- Update build process
|
|
198
|
+
`,
|
|
199
|
+
'mongoose-to-prisma': `
|
|
200
|
+
# Mongoose to Prisma Migration
|
|
201
|
+
|
|
202
|
+
Convert Mongoose ODM to Prisma ORM:
|
|
203
|
+
|
|
204
|
+
1. **Schema Migration**
|
|
205
|
+
- Create Prisma schema from Mongoose models
|
|
206
|
+
- Convert types (ObjectId → String with @id)
|
|
207
|
+
- Define relations explicitly
|
|
208
|
+
|
|
209
|
+
2. **Query Migration**
|
|
210
|
+
- Replace Model.find() with prisma.model.findMany()
|
|
211
|
+
- Replace Model.findById() with prisma.model.findUnique()
|
|
212
|
+
- Update create/update/delete operations
|
|
213
|
+
|
|
214
|
+
3. **Setup**
|
|
215
|
+
- Configure Prisma client
|
|
216
|
+
- Set up migrations
|
|
217
|
+
- Handle connection pooling
|
|
218
|
+
`,
|
|
219
|
+
'express-to-fastify': `
|
|
220
|
+
# Express to Fastify Migration
|
|
221
|
+
|
|
222
|
+
Convert Express.js to Fastify:
|
|
223
|
+
|
|
224
|
+
1. **App Setup**
|
|
225
|
+
- Replace express() with fastify()
|
|
226
|
+
- Update middleware to plugins
|
|
227
|
+
- Configure async handlers (native in Fastify)
|
|
228
|
+
|
|
229
|
+
2. **Routes**
|
|
230
|
+
- Update route syntax
|
|
231
|
+
- Replace req/res patterns
|
|
232
|
+
- Use schema validation
|
|
233
|
+
|
|
234
|
+
3. **Middleware**
|
|
235
|
+
- Convert Express middleware to Fastify plugins
|
|
236
|
+
- Update error handling
|
|
237
|
+
- Replace body-parser (built into Fastify)
|
|
238
|
+
`,
|
|
239
|
+
'pages-to-app': `
|
|
240
|
+
# Next.js Pages to App Router Migration
|
|
241
|
+
|
|
242
|
+
Convert Pages Router to App Router:
|
|
243
|
+
|
|
244
|
+
1. **Directory Structure**
|
|
245
|
+
- Move pages/ to app/
|
|
246
|
+
- Create layout.tsx files
|
|
247
|
+
- Convert _app.tsx to root layout
|
|
248
|
+
|
|
249
|
+
2. **Data Fetching**
|
|
250
|
+
- Replace getServerSideProps with server components
|
|
251
|
+
- Replace getStaticProps with fetch + cache
|
|
252
|
+
- Use generateStaticParams for dynamic routes
|
|
253
|
+
|
|
254
|
+
3. **Components**
|
|
255
|
+
- Mark client components with 'use client'
|
|
256
|
+
- Keep server components as default
|
|
257
|
+
- Update metadata handling
|
|
258
|
+
|
|
259
|
+
4. **API Routes**
|
|
260
|
+
- Move to app/api/[route]/route.ts
|
|
261
|
+
- Update to new Route Handlers format
|
|
262
|
+
`,
|
|
263
|
+
'jest-to-vitest': `
|
|
264
|
+
# Jest to Vitest Migration
|
|
265
|
+
|
|
266
|
+
Convert Jest tests to Vitest:
|
|
267
|
+
|
|
268
|
+
1. **Configuration**
|
|
269
|
+
- Create vitest.config.ts
|
|
270
|
+
- Remove jest.config.js
|
|
271
|
+
- Update package.json scripts
|
|
272
|
+
|
|
273
|
+
2. **Test Files**
|
|
274
|
+
- Update imports from 'vitest'
|
|
275
|
+
- Replace jest.fn() with vi.fn()
|
|
276
|
+
- Replace jest.mock() with vi.mock()
|
|
277
|
+
- Replace jest.spyOn() with vi.spyOn()
|
|
278
|
+
|
|
279
|
+
3. **Assertions**
|
|
280
|
+
- Most expect() syntax is compatible
|
|
281
|
+
- Update any Jest-specific matchers
|
|
282
|
+
`
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const migrationGuide = migrations[migrationDesc] || `
|
|
286
|
+
# Custom Migration: ${migrationDesc}
|
|
287
|
+
|
|
288
|
+
Perform this migration following best practices:
|
|
289
|
+
1. Analyze current code structure
|
|
290
|
+
2. Plan the migration steps
|
|
291
|
+
3. Apply changes incrementally
|
|
292
|
+
4. Verify each step works
|
|
293
|
+
5. Run tests after migration
|
|
294
|
+
`;
|
|
295
|
+
|
|
296
|
+
const targetInfo = options.path ? `\n## Target Path: ${options.path}` : '\n## Target: Entire project';
|
|
297
|
+
|
|
298
|
+
const prompt = `
|
|
299
|
+
${migrationGuide}
|
|
300
|
+
${targetInfo}
|
|
301
|
+
|
|
302
|
+
## Safety Requirements:
|
|
303
|
+
1. Preserve all existing functionality
|
|
304
|
+
2. Don't lose any data or configuration
|
|
305
|
+
3. Make incremental changes when possible
|
|
306
|
+
4. Add comments for complex changes
|
|
307
|
+
5. Run tests if available
|
|
308
|
+
|
|
309
|
+
## Instructions:
|
|
310
|
+
1. Analyze the codebase
|
|
311
|
+
2. Create a migration plan
|
|
312
|
+
3. Execute the migration step by step
|
|
313
|
+
4. Verify the changes work
|
|
314
|
+
|
|
315
|
+
Perform the migration now.
|
|
316
|
+
`;
|
|
317
|
+
|
|
318
|
+
const promptFile = path.join(cwd, '.vibecode', 'migrate-prompt.md');
|
|
319
|
+
await fs.mkdir(path.dirname(promptFile), { recursive: true });
|
|
320
|
+
await fs.writeFile(promptFile, prompt);
|
|
321
|
+
|
|
322
|
+
console.log(chalk.yellow(` Performing migration: ${migrationDesc}\n`));
|
|
323
|
+
|
|
324
|
+
await runClaudeCode(prompt, cwd);
|
|
325
|
+
|
|
326
|
+
console.log(chalk.green('\n✅ Migration complete!'));
|
|
327
|
+
console.log(chalk.gray(' • Run tests to verify'));
|
|
328
|
+
console.log(chalk.gray(' • Use `vibecode undo` to revert if needed\n'));
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async function runClaudeCode(prompt, cwd) {
|
|
332
|
+
return new Promise((resolve) => {
|
|
333
|
+
const child = spawn('claude', ['-p', prompt, '--dangerously-skip-permissions'], {
|
|
334
|
+
cwd,
|
|
335
|
+
stdio: 'inherit'
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
child.on('close', resolve);
|
|
339
|
+
child.on('error', () => resolve());
|
|
340
|
+
});
|
|
341
|
+
}
|
package/src/commands/plan.js
CHANGED
|
@@ -45,7 +45,7 @@ export async function planCommand(options = {}) {
|
|
|
45
45
|
const sessionPath = await getCurrentSessionPath();
|
|
46
46
|
const specHash = await getSpecHash();
|
|
47
47
|
|
|
48
|
-
// Read contract and
|
|
48
|
+
// Read contract, blueprint, and intake
|
|
49
49
|
spinner.text = 'Reading contract...';
|
|
50
50
|
const contractContent = await readSessionFile('contract.md');
|
|
51
51
|
|
|
@@ -54,6 +54,11 @@ export async function planCommand(options = {}) {
|
|
|
54
54
|
blueprintContent = await readSessionFile('blueprint.md');
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
let intakeContent = '';
|
|
58
|
+
if (await sessionFileExists('intake.md')) {
|
|
59
|
+
intakeContent = await readSessionFile('intake.md');
|
|
60
|
+
}
|
|
61
|
+
|
|
57
62
|
// Generate plan
|
|
58
63
|
spinner.text = 'Generating plan...';
|
|
59
64
|
const planContent = getPlanTemplate(projectName, sessionId, specHash, contractContent);
|
|
@@ -66,7 +71,8 @@ export async function planCommand(options = {}) {
|
|
|
66
71
|
sessionId,
|
|
67
72
|
specHash,
|
|
68
73
|
contractContent,
|
|
69
|
-
blueprintContent
|
|
74
|
+
blueprintContent,
|
|
75
|
+
intakeContent
|
|
70
76
|
);
|
|
71
77
|
await writeSessionFile('coder_pack.md', coderPackContent);
|
|
72
78
|
|