@nclamvn/vibecode-cli 1.7.0 → 1.8.1
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/SESSION_NOTES.md +119 -0
- package/bin/vibecode.js +53 -0
- package/package.json +1 -1
- package/src/commands/ask.js +230 -0
- package/src/commands/docs.js +167 -0
- package/src/commands/git.js +103 -2
- package/src/commands/migrate.js +341 -0
- package/src/commands/refactor.js +205 -0
- package/src/commands/review.js +126 -1
- package/src/commands/security.js +229 -0
- package/src/commands/test.js +194 -0
- package/src/config/constants.js +5 -1
- package/src/index.js +8 -0
- package/docs-site/README.md +0 -41
- package/docs-site/blog/2019-05-28-first-blog-post.md +0 -12
- package/docs-site/blog/2019-05-29-long-blog-post.md +0 -44
- package/docs-site/blog/2021-08-01-mdx-blog-post.mdx +0 -24
- package/docs-site/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
- package/docs-site/blog/2021-08-26-welcome/index.md +0 -29
- package/docs-site/blog/authors.yml +0 -25
- package/docs-site/blog/tags.yml +0 -19
- package/docs-site/docs/commands/agent.md +0 -162
- package/docs-site/docs/commands/assist.md +0 -71
- package/docs-site/docs/commands/build.md +0 -53
- package/docs-site/docs/commands/config.md +0 -30
- package/docs-site/docs/commands/debug.md +0 -173
- package/docs-site/docs/commands/doctor.md +0 -34
- package/docs-site/docs/commands/go.md +0 -128
- package/docs-site/docs/commands/index.md +0 -79
- package/docs-site/docs/commands/init.md +0 -42
- package/docs-site/docs/commands/learn.md +0 -82
- package/docs-site/docs/commands/lock.md +0 -33
- package/docs-site/docs/commands/plan.md +0 -29
- package/docs-site/docs/commands/review.md +0 -31
- package/docs-site/docs/commands/snapshot.md +0 -34
- package/docs-site/docs/commands/start.md +0 -32
- package/docs-site/docs/commands/status.md +0 -37
- package/docs-site/docs/commands/undo.md +0 -83
- package/docs-site/docs/configuration.md +0 -72
- package/docs-site/docs/faq.md +0 -83
- package/docs-site/docs/getting-started.md +0 -119
- package/docs-site/docs/guides/agent-mode.md +0 -94
- package/docs-site/docs/guides/debug-mode.md +0 -83
- package/docs-site/docs/guides/magic-mode.md +0 -107
- package/docs-site/docs/installation.md +0 -98
- package/docs-site/docs/intro.md +0 -67
- package/docs-site/docusaurus.config.ts +0 -141
- package/docs-site/package-lock.json +0 -18039
- package/docs-site/package.json +0 -48
- package/docs-site/sidebars.ts +0 -70
- package/docs-site/src/components/HomepageFeatures/index.tsx +0 -72
- package/docs-site/src/components/HomepageFeatures/styles.module.css +0 -16
- package/docs-site/src/css/custom.css +0 -30
- package/docs-site/src/pages/index.module.css +0 -23
- package/docs-site/src/pages/index.tsx +0 -44
- package/docs-site/src/pages/markdown-page.md +0 -7
- package/docs-site/src/theme/Footer/index.tsx +0 -127
- package/docs-site/src/theme/Footer/styles.module.css +0 -285
- package/docs-site/static/.nojekyll +0 -0
- package/docs-site/static/img/docusaurus-social-card.jpg +0 -0
- package/docs-site/static/img/docusaurus.png +0 -0
- package/docs-site/static/img/favicon.ico +0 -0
- package/docs-site/static/img/logo.svg +0 -1
- package/docs-site/static/img/undraw_docusaurus_mountain.svg +0 -171
- package/docs-site/static/img/undraw_docusaurus_react.svg +0 -170
- package/docs-site/static/img/undraw_docusaurus_tree.svg +0 -40
- package/docs-site/tsconfig.json +0 -8
package/src/commands/git.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Git Integration for Vibecode CLI
|
|
3
3
|
* Native git commands with enhanced UI and AI-powered commit messages
|
|
4
|
+
* Phase K7: AI Diff Review
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
|
-
import { exec } from 'child_process';
|
|
7
|
+
import { exec, spawn } from 'child_process';
|
|
7
8
|
import { promisify } from 'util';
|
|
9
|
+
import fs from 'fs/promises';
|
|
10
|
+
import path from 'path';
|
|
8
11
|
import chalk from 'chalk';
|
|
9
12
|
import inquirer from 'inquirer';
|
|
10
13
|
|
|
@@ -435,11 +438,16 @@ async function generateCommitMessage(files) {
|
|
|
435
438
|
* Show diff with syntax highlighting
|
|
436
439
|
*/
|
|
437
440
|
async function gitDiff(args, options) {
|
|
441
|
+
// K7: AI diff review
|
|
442
|
+
if (options.review || args.includes('--review')) {
|
|
443
|
+
return aiDiffReview();
|
|
444
|
+
}
|
|
445
|
+
|
|
438
446
|
try {
|
|
439
447
|
const file = args[0];
|
|
440
448
|
let cmd = 'git diff';
|
|
441
449
|
|
|
442
|
-
if (file) {
|
|
450
|
+
if (file && file !== '--review') {
|
|
443
451
|
cmd = `git diff -- "${file}"`;
|
|
444
452
|
} else if (options.staged) {
|
|
445
453
|
cmd = 'git diff --cached';
|
|
@@ -480,6 +488,99 @@ async function gitDiff(args, options) {
|
|
|
480
488
|
}
|
|
481
489
|
}
|
|
482
490
|
|
|
491
|
+
/**
|
|
492
|
+
* K7: AI Diff Review
|
|
493
|
+
*/
|
|
494
|
+
async function aiDiffReview() {
|
|
495
|
+
const cwd = process.cwd();
|
|
496
|
+
|
|
497
|
+
console.log(chalk.cyan(`
|
|
498
|
+
╭────────────────────────────────────────────────────────────────────╮
|
|
499
|
+
│ 🔍 AI DIFF REVIEW │
|
|
500
|
+
│ │
|
|
501
|
+
│ Reviewing staged changes... │
|
|
502
|
+
│ │
|
|
503
|
+
╰────────────────────────────────────────────────────────────────────╯
|
|
504
|
+
`));
|
|
505
|
+
|
|
506
|
+
// Get diff
|
|
507
|
+
let diff = '';
|
|
508
|
+
try {
|
|
509
|
+
const { stdout: stagedDiff } = await execAsync('git diff --staged', { cwd });
|
|
510
|
+
diff = stagedDiff;
|
|
511
|
+
} catch {}
|
|
512
|
+
|
|
513
|
+
if (!diff.trim()) {
|
|
514
|
+
// Try unstaged
|
|
515
|
+
try {
|
|
516
|
+
const { stdout: unstagedDiff } = await execAsync('git diff', { cwd });
|
|
517
|
+
diff = unstagedDiff;
|
|
518
|
+
} catch {}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (!diff.trim()) {
|
|
522
|
+
console.log(chalk.yellow('\n No changes to review.\n'));
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Truncate diff if too long
|
|
527
|
+
const maxDiffLength = 8000;
|
|
528
|
+
const truncatedDiff = diff.length > maxDiffLength
|
|
529
|
+
? diff.substring(0, maxDiffLength) + '\n... (truncated)'
|
|
530
|
+
: diff;
|
|
531
|
+
|
|
532
|
+
const prompt = `
|
|
533
|
+
# Git Diff Review
|
|
534
|
+
|
|
535
|
+
Review these code changes:
|
|
536
|
+
|
|
537
|
+
\`\`\`diff
|
|
538
|
+
${truncatedDiff}
|
|
539
|
+
\`\`\`
|
|
540
|
+
|
|
541
|
+
## Review Criteria:
|
|
542
|
+
1. **Correctness**: Are there any bugs or logic errors?
|
|
543
|
+
2. **Best Practices**: Does it follow conventions and patterns?
|
|
544
|
+
3. **Security**: Any security concerns introduced?
|
|
545
|
+
4. **Performance**: Any performance issues?
|
|
546
|
+
5. **Tests**: Should tests be added/updated?
|
|
547
|
+
6. **Edge Cases**: Any edge cases not handled?
|
|
548
|
+
|
|
549
|
+
## Output Format:
|
|
550
|
+
1. **Overall Assessment**: (Good / Concerns / Issues)
|
|
551
|
+
2. **Specific Feedback**: Per file/change feedback
|
|
552
|
+
3. **Suggested Improvements**: What could be better
|
|
553
|
+
4. **Recommended Commit Message**: A conventional commit message for these changes
|
|
554
|
+
|
|
555
|
+
Review the diff now.
|
|
556
|
+
`;
|
|
557
|
+
|
|
558
|
+
const promptFile = path.join(cwd, '.vibecode', 'diff-review-prompt.md');
|
|
559
|
+
await fs.mkdir(path.dirname(promptFile), { recursive: true });
|
|
560
|
+
await fs.writeFile(promptFile, prompt);
|
|
561
|
+
|
|
562
|
+
console.log(chalk.gray(' Reviewing with Claude Code...\n'));
|
|
563
|
+
|
|
564
|
+
await runClaudeCode(prompt, cwd);
|
|
565
|
+
|
|
566
|
+
console.log(chalk.green('\n✅ Diff review complete!\n'));
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Run Claude Code with prompt
|
|
571
|
+
*/
|
|
572
|
+
async function runClaudeCode(prompt, cwd) {
|
|
573
|
+
return new Promise((resolve) => {
|
|
574
|
+
const child = spawn('claude', ['-p', prompt, '--dangerously-skip-permissions'], {
|
|
575
|
+
cwd,
|
|
576
|
+
stdio: 'inherit'
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
child.on('close', resolve);
|
|
580
|
+
child.on('error', () => resolve());
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
|
|
483
584
|
/**
|
|
484
585
|
* Branch management
|
|
485
586
|
*/
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// VIBECODE CLI - Refactor Command
|
|
3
|
+
// Phase K4: AI-Powered Refactoring
|
|
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 refactorCommand(targetPath, options = {}) {
|
|
14
|
+
const cwd = process.cwd();
|
|
15
|
+
|
|
16
|
+
// Interactive mode
|
|
17
|
+
if (!targetPath && !options.type) {
|
|
18
|
+
return interactiveRefactor(cwd);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return performRefactor(cwd, targetPath || 'src/', options);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function interactiveRefactor(cwd) {
|
|
25
|
+
console.log(chalk.cyan(`
|
|
26
|
+
╭────────────────────────────────────────────────────────────────────╮
|
|
27
|
+
│ 🔄 AI REFACTORING │
|
|
28
|
+
│ │
|
|
29
|
+
│ Improve code quality with AI-powered refactoring │
|
|
30
|
+
│ │
|
|
31
|
+
╰────────────────────────────────────────────────────────────────────╯
|
|
32
|
+
`));
|
|
33
|
+
|
|
34
|
+
const { refactorType } = await inquirer.prompt([{
|
|
35
|
+
type: 'list',
|
|
36
|
+
name: 'refactorType',
|
|
37
|
+
message: 'What type of refactoring?',
|
|
38
|
+
choices: [
|
|
39
|
+
{ name: '🧹 Clean Code - Improve readability', value: 'clean' },
|
|
40
|
+
{ name: '🔄 DRY - Remove duplication', value: 'dry' },
|
|
41
|
+
{ name: '⚡ Performance - Optimize slow code', value: 'performance' },
|
|
42
|
+
{ name: '🏗️ Architecture - Better structure', value: 'architecture' },
|
|
43
|
+
{ name: '📦 Modularize - Split large files', value: 'modularize' },
|
|
44
|
+
{ name: '🆕 Modernize - Update to modern syntax', value: 'modernize' },
|
|
45
|
+
{ name: '🎯 Custom - Describe your own', value: 'custom' },
|
|
46
|
+
{ name: '👋 Exit', value: 'exit' }
|
|
47
|
+
]
|
|
48
|
+
}]);
|
|
49
|
+
|
|
50
|
+
if (refactorType === 'exit') return;
|
|
51
|
+
|
|
52
|
+
const { targetPath } = await inquirer.prompt([{
|
|
53
|
+
type: 'input',
|
|
54
|
+
name: 'targetPath',
|
|
55
|
+
message: 'Path to refactor:',
|
|
56
|
+
default: 'src/'
|
|
57
|
+
}]);
|
|
58
|
+
|
|
59
|
+
let customDescription = '';
|
|
60
|
+
if (refactorType === 'custom') {
|
|
61
|
+
const { desc } = await inquirer.prompt([{
|
|
62
|
+
type: 'input',
|
|
63
|
+
name: 'desc',
|
|
64
|
+
message: 'Describe the refactoring:'
|
|
65
|
+
}]);
|
|
66
|
+
customDescription = desc;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return performRefactor(cwd, targetPath, { type: refactorType, description: customDescription });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function performRefactor(cwd, targetPath, options) {
|
|
73
|
+
const refactorType = options.type || 'clean';
|
|
74
|
+
|
|
75
|
+
// Create backup first
|
|
76
|
+
console.log(chalk.gray('\n Creating backup before refactoring...\n'));
|
|
77
|
+
const backup = new BackupManager(cwd);
|
|
78
|
+
await backup.createBackup(`refactor-${refactorType}`);
|
|
79
|
+
|
|
80
|
+
const prompts = {
|
|
81
|
+
clean: `
|
|
82
|
+
# Clean Code Refactoring
|
|
83
|
+
|
|
84
|
+
Improve code readability:
|
|
85
|
+
- Better variable/function names (descriptive, consistent)
|
|
86
|
+
- Simplify complex conditions (extract to named variables/functions)
|
|
87
|
+
- Extract magic numbers to named constants
|
|
88
|
+
- Add meaningful comments where logic is complex
|
|
89
|
+
- Improve code formatting and consistency
|
|
90
|
+
- Follow language conventions and style guides
|
|
91
|
+
- Remove dead code and unused imports
|
|
92
|
+
`,
|
|
93
|
+
dry: `
|
|
94
|
+
# DRY Refactoring (Don't Repeat Yourself)
|
|
95
|
+
|
|
96
|
+
Remove code duplication:
|
|
97
|
+
- Identify repeated code patterns (3+ occurrences)
|
|
98
|
+
- Extract common logic to reusable functions
|
|
99
|
+
- Create utility modules for shared functionality
|
|
100
|
+
- Use generics/templates where appropriate
|
|
101
|
+
- Consolidate similar functions with parameters
|
|
102
|
+
- Create higher-order functions for common patterns
|
|
103
|
+
`,
|
|
104
|
+
performance: `
|
|
105
|
+
# Performance Optimization
|
|
106
|
+
|
|
107
|
+
Optimize for speed and efficiency:
|
|
108
|
+
- Replace O(n²) with O(n) where possible
|
|
109
|
+
- Add memoization for expensive computations
|
|
110
|
+
- Optimize database queries (N+1 problem)
|
|
111
|
+
- Implement lazy loading for large data
|
|
112
|
+
- Use efficient data structures (Map/Set vs Array)
|
|
113
|
+
- Remove unnecessary re-renders (React)
|
|
114
|
+
- Add proper caching strategies
|
|
115
|
+
- Optimize loops and iterations
|
|
116
|
+
`,
|
|
117
|
+
architecture: `
|
|
118
|
+
# Architecture Refactoring
|
|
119
|
+
|
|
120
|
+
Improve code structure:
|
|
121
|
+
- Apply SOLID principles
|
|
122
|
+
- Implement proper separation of concerns
|
|
123
|
+
- Use dependency injection where appropriate
|
|
124
|
+
- Apply appropriate design patterns
|
|
125
|
+
- Improve module boundaries
|
|
126
|
+
- Better error handling structure
|
|
127
|
+
- Create clear interfaces between modules
|
|
128
|
+
- Reduce coupling, increase cohesion
|
|
129
|
+
`,
|
|
130
|
+
modularize: `
|
|
131
|
+
# Modularization
|
|
132
|
+
|
|
133
|
+
Split large files and improve organization:
|
|
134
|
+
- Break large files (>300 lines) into smaller modules
|
|
135
|
+
- One component/class per file
|
|
136
|
+
- Group related functionality in folders
|
|
137
|
+
- Create proper index files for exports
|
|
138
|
+
- Improve import structure
|
|
139
|
+
- Separate concerns (UI, logic, data)
|
|
140
|
+
- Create feature-based folder structure
|
|
141
|
+
`,
|
|
142
|
+
modernize: `
|
|
143
|
+
# Modernize Code
|
|
144
|
+
|
|
145
|
+
Update to modern syntax and practices:
|
|
146
|
+
- ES6+ features (arrow functions, destructuring, spread)
|
|
147
|
+
- async/await instead of callbacks/Promise chains
|
|
148
|
+
- Optional chaining (?.) and nullish coalescing (??)
|
|
149
|
+
- Modern array methods (map, filter, reduce)
|
|
150
|
+
- Template literals instead of string concatenation
|
|
151
|
+
- const/let instead of var
|
|
152
|
+
- TypeScript improvements (if TS project)
|
|
153
|
+
- Modern React patterns (hooks, functional components)
|
|
154
|
+
`,
|
|
155
|
+
custom: `
|
|
156
|
+
# Custom Refactoring
|
|
157
|
+
|
|
158
|
+
${options.description || 'Improve the code based on best practices.'}
|
|
159
|
+
`
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const prompt = `
|
|
163
|
+
${prompts[refactorType]}
|
|
164
|
+
|
|
165
|
+
## Target Path: ${targetPath}
|
|
166
|
+
|
|
167
|
+
## Instructions:
|
|
168
|
+
1. Analyze the code in the target path
|
|
169
|
+
2. Apply the refactoring described above
|
|
170
|
+
3. Preserve all functionality (don't break anything!)
|
|
171
|
+
4. Make minimal changes to achieve the goal
|
|
172
|
+
5. Add comments explaining significant changes
|
|
173
|
+
|
|
174
|
+
## Safety:
|
|
175
|
+
- Run tests after refactoring if available
|
|
176
|
+
- Keep backward compatibility
|
|
177
|
+
- Don't change public APIs without good reason
|
|
178
|
+
|
|
179
|
+
Perform the refactoring now.
|
|
180
|
+
`;
|
|
181
|
+
|
|
182
|
+
const promptFile = path.join(cwd, '.vibecode', 'refactor-prompt.md');
|
|
183
|
+
await fs.mkdir(path.dirname(promptFile), { recursive: true });
|
|
184
|
+
await fs.writeFile(promptFile, prompt);
|
|
185
|
+
|
|
186
|
+
console.log(chalk.yellow(` Refactoring (${refactorType})...\n`));
|
|
187
|
+
|
|
188
|
+
await runClaudeCode(prompt, cwd);
|
|
189
|
+
|
|
190
|
+
console.log(chalk.green('\n✅ Refactoring complete!'));
|
|
191
|
+
console.log(chalk.gray(' • Run tests to verify changes'));
|
|
192
|
+
console.log(chalk.gray(' • Use `vibecode undo` to revert if needed\n'));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function runClaudeCode(prompt, cwd) {
|
|
196
|
+
return new Promise((resolve) => {
|
|
197
|
+
const child = spawn('claude', ['-p', prompt, '--dangerously-skip-permissions'], {
|
|
198
|
+
cwd,
|
|
199
|
+
stdio: 'inherit'
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
child.on('close', resolve);
|
|
203
|
+
child.on('error', () => resolve());
|
|
204
|
+
});
|
|
205
|
+
}
|