@nclamvn/vibecode-cli 1.7.0 → 1.8.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/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/index.js +8 -0
|
@@ -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
|
+
}
|
package/src/commands/review.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
2
|
// VIBECODE CLI - Review Command
|
|
3
|
+
// Phase K1: AI Code Review
|
|
3
4
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
4
5
|
|
|
5
6
|
import chalk from 'chalk';
|
|
6
7
|
import ora from 'ora';
|
|
7
8
|
import path from 'path';
|
|
8
|
-
import
|
|
9
|
+
import fs from 'fs/promises';
|
|
10
|
+
import { exec, spawn } from 'child_process';
|
|
9
11
|
import { promisify } from 'util';
|
|
10
12
|
import inquirer from 'inquirer';
|
|
11
13
|
import { workspaceExists, getProjectName } from '../core/workspace.js';
|
|
@@ -26,6 +28,10 @@ import { printBox, printError, printSuccess, printWarning, printNextStep } from
|
|
|
26
28
|
const execAsync = promisify(exec);
|
|
27
29
|
|
|
28
30
|
export async function reviewCommand(options = {}) {
|
|
31
|
+
// K1: AI-powered code review
|
|
32
|
+
if (options.ai) {
|
|
33
|
+
return aiCodeReview(options);
|
|
34
|
+
}
|
|
29
35
|
const spinner = ora('Starting review...').start();
|
|
30
36
|
|
|
31
37
|
try {
|
|
@@ -288,3 +294,122 @@ async function checkGitStatus() {
|
|
|
288
294
|
return { clean: true, message: 'Git not available' };
|
|
289
295
|
}
|
|
290
296
|
}
|
|
297
|
+
|
|
298
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
299
|
+
// K1: AI CODE REVIEW
|
|
300
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
301
|
+
|
|
302
|
+
async function aiCodeReview(options) {
|
|
303
|
+
const cwd = process.cwd();
|
|
304
|
+
|
|
305
|
+
console.log(chalk.cyan(`
|
|
306
|
+
╭────────────────────────────────────────────────────────────────────╮
|
|
307
|
+
│ 🔍 AI CODE REVIEW │
|
|
308
|
+
│ │
|
|
309
|
+
│ Analyzing codebase for: │
|
|
310
|
+
│ • Code quality & best practices │
|
|
311
|
+
│ • Potential bugs & edge cases │
|
|
312
|
+
│ • Performance concerns │
|
|
313
|
+
│ • Security vulnerabilities │
|
|
314
|
+
│ • Maintainability issues │
|
|
315
|
+
│ │
|
|
316
|
+
╰────────────────────────────────────────────────────────────────────╯
|
|
317
|
+
`));
|
|
318
|
+
|
|
319
|
+
// Gather codebase context
|
|
320
|
+
const files = await gatherSourceFiles(cwd, options.path);
|
|
321
|
+
|
|
322
|
+
console.log(chalk.gray(` Found ${files.length} files to review\n`));
|
|
323
|
+
|
|
324
|
+
const prompt = `
|
|
325
|
+
# AI Code Review Request
|
|
326
|
+
|
|
327
|
+
## Project: ${path.basename(cwd)}
|
|
328
|
+
|
|
329
|
+
## Files to Review:
|
|
330
|
+
${files.map(f => `- ${f}`).join('\n')}
|
|
331
|
+
|
|
332
|
+
## Review Criteria:
|
|
333
|
+
1. **Code Quality**: Clean code, readability, naming conventions
|
|
334
|
+
2. **Best Practices**: Design patterns, DRY, SOLID principles
|
|
335
|
+
3. **Potential Bugs**: Edge cases, null checks, error handling
|
|
336
|
+
4. **Performance**: Inefficient loops, memory leaks, N+1 queries
|
|
337
|
+
5. **Security**: Input validation, XSS, SQL injection, auth issues
|
|
338
|
+
6. **Maintainability**: Modularity, testability, documentation
|
|
339
|
+
|
|
340
|
+
## Output Format:
|
|
341
|
+
For each issue found, provide:
|
|
342
|
+
- **Severity**: Critical / High / Medium / Low
|
|
343
|
+
- **Category**: Bug / Performance / Security / Quality / Maintainability
|
|
344
|
+
- **File**: path/to/file.ts
|
|
345
|
+
- **Line**: (if applicable)
|
|
346
|
+
- **Issue**: Description
|
|
347
|
+
- **Suggestion**: How to fix
|
|
348
|
+
|
|
349
|
+
## Summary:
|
|
350
|
+
End with overall score (A-F) and top 3 priorities.
|
|
351
|
+
|
|
352
|
+
Review the codebase now.
|
|
353
|
+
`;
|
|
354
|
+
|
|
355
|
+
// Write prompt
|
|
356
|
+
const promptFile = path.join(cwd, '.vibecode', 'review-prompt.md');
|
|
357
|
+
await fs.mkdir(path.dirname(promptFile), { recursive: true });
|
|
358
|
+
await fs.writeFile(promptFile, prompt);
|
|
359
|
+
|
|
360
|
+
// Run Claude Code
|
|
361
|
+
console.log(chalk.gray(' Analyzing with Claude Code...\n'));
|
|
362
|
+
|
|
363
|
+
await runClaudeCode(prompt, cwd);
|
|
364
|
+
|
|
365
|
+
// Save report
|
|
366
|
+
const reportDir = path.join(cwd, '.vibecode', 'reports');
|
|
367
|
+
await fs.mkdir(reportDir, { recursive: true });
|
|
368
|
+
const reportFile = path.join(reportDir, `review-${Date.now()}.md`);
|
|
369
|
+
|
|
370
|
+
console.log(chalk.green(`\n✅ Review complete!`));
|
|
371
|
+
console.log(chalk.gray(` Report saved to: .vibecode/reports/\n`));
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async function gatherSourceFiles(cwd, targetPath) {
|
|
375
|
+
const files = [];
|
|
376
|
+
const extensions = ['.js', '.ts', '.tsx', '.jsx', '.py', '.go', '.rs', '.vue', '.svelte'];
|
|
377
|
+
const ignoreDirs = ['node_modules', '.git', 'dist', 'build', '.next', 'coverage', '.vibecode', '__pycache__'];
|
|
378
|
+
|
|
379
|
+
const scanDir = targetPath ? path.join(cwd, targetPath) : cwd;
|
|
380
|
+
|
|
381
|
+
async function scan(dir) {
|
|
382
|
+
try {
|
|
383
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
384
|
+
|
|
385
|
+
for (const entry of entries) {
|
|
386
|
+
if (entry.name.startsWith('.')) continue;
|
|
387
|
+
if (ignoreDirs.includes(entry.name)) continue;
|
|
388
|
+
|
|
389
|
+
const fullPath = path.join(dir, entry.name);
|
|
390
|
+
const relativePath = path.relative(cwd, fullPath);
|
|
391
|
+
|
|
392
|
+
if (entry.isDirectory()) {
|
|
393
|
+
await scan(fullPath);
|
|
394
|
+
} else if (extensions.some(ext => entry.name.endsWith(ext))) {
|
|
395
|
+
files.push(relativePath);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
} catch {}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
await scan(scanDir);
|
|
402
|
+
return files.slice(0, 50); // Limit files
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
async function runClaudeCode(prompt, cwd) {
|
|
406
|
+
return new Promise((resolve) => {
|
|
407
|
+
const child = spawn('claude', ['-p', prompt, '--dangerously-skip-permissions'], {
|
|
408
|
+
cwd,
|
|
409
|
+
stdio: 'inherit'
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
child.on('close', resolve);
|
|
413
|
+
child.on('error', () => resolve());
|
|
414
|
+
});
|
|
415
|
+
}
|