codeflow-hook 1.0.0 โ 1.1.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/README.md +251 -248
- package/bin/codeflow-hook.js +409 -352
- package/package.json +37 -25
package/bin/codeflow-hook.js
CHANGED
|
@@ -1,352 +1,409 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { Command } from 'commander';
|
|
4
|
-
import chalk from 'chalk';
|
|
5
|
-
import ora from 'ora';
|
|
6
|
-
import axios from 'axios';
|
|
7
|
-
import fs from 'fs';
|
|
8
|
-
import path from 'path';
|
|
9
|
-
import { execSync } from 'child_process';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
.
|
|
16
|
-
.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
.
|
|
22
|
-
.
|
|
23
|
-
.option('-
|
|
24
|
-
.option('-
|
|
25
|
-
.option('-
|
|
26
|
-
.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
config.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
config.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import axios from 'axios';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { execSync } from 'child_process';
|
|
10
|
+
import readline from 'readline';
|
|
11
|
+
|
|
12
|
+
const program = new Command();
|
|
13
|
+
|
|
14
|
+
program
|
|
15
|
+
.name('codeflow-hook')
|
|
16
|
+
.description('Interactive CI/CD simulator and AI-powered code reviewer')
|
|
17
|
+
.version('1.0.0');
|
|
18
|
+
|
|
19
|
+
// Configure AI provider settings
|
|
20
|
+
program
|
|
21
|
+
.command('config')
|
|
22
|
+
.description('Configure AI provider settings')
|
|
23
|
+
.option('-p, --provider <provider>', 'AI provider (gemini, openai, claude)', 'gemini')
|
|
24
|
+
.option('-k, --key <key>', 'API key for the chosen provider')
|
|
25
|
+
.option('-u, --url <url>', 'Custom API URL (optional)')
|
|
26
|
+
.option('-m, --model <model>', 'AI model name (optional - uses provider default)')
|
|
27
|
+
.action(async (options) => {
|
|
28
|
+
const configDir = path.join(process.env.HOME, '.codeflow-hook');
|
|
29
|
+
if (!fs.existsSync(configDir)) {
|
|
30
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const configPath = path.join(configDir, 'config.json');
|
|
34
|
+
const existingConfig = fs.existsSync(configPath) ? JSON.parse(fs.readFileSync(configPath, 'utf8')) : {};
|
|
35
|
+
|
|
36
|
+
// Initialize config with existing values, then override with CLI options
|
|
37
|
+
const config = {
|
|
38
|
+
provider: options.provider || existingConfig.provider || 'gemini',
|
|
39
|
+
apiKey: options.key || existingConfig.apiKey,
|
|
40
|
+
apiUrl: options.url || existingConfig.apiUrl,
|
|
41
|
+
model: options.model || existingConfig.model
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Set default API URL and model if not provided by options or existing config
|
|
45
|
+
if (!config.apiUrl) {
|
|
46
|
+
switch (config.provider) {
|
|
47
|
+
case 'openai':
|
|
48
|
+
config.apiUrl = 'https://api.openai.com/v1/chat/completions';
|
|
49
|
+
config.model = config.model || 'gpt-4'; // Default model for OpenAI
|
|
50
|
+
break;
|
|
51
|
+
case 'claude':
|
|
52
|
+
config.apiUrl = 'https://api.anthropic.com/v1/messages';
|
|
53
|
+
config.model = config.model || 'claude-3-sonnet-20240229'; // Default model for Claude
|
|
54
|
+
break;
|
|
55
|
+
case 'gemini':
|
|
56
|
+
default:
|
|
57
|
+
// Updated Gemini API URL to v1
|
|
58
|
+
config.apiUrl = 'https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent';
|
|
59
|
+
// Default model for Gemini
|
|
60
|
+
config.model = config.model || 'gemini-pro'; // Using 'gemini-pro' as a common default
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Interactive model selection if model is not explicitly provided via CLI option
|
|
66
|
+
// AND if the model is not already set (either from existing config or default)
|
|
67
|
+
if (!options.model && !config.model) {
|
|
68
|
+
try {
|
|
69
|
+
switch (config.provider) {
|
|
70
|
+
case 'gemini':
|
|
71
|
+
const geminiModels = ['gemini-1.5-pro-latest', 'gemini-1.5-flash-latest', 'gemini-pro'];
|
|
72
|
+
const geminiRl = readline.createInterface({
|
|
73
|
+
input: process.stdin,
|
|
74
|
+
output: process.stdout
|
|
75
|
+
});
|
|
76
|
+
config.model = await new Promise(resolve => {
|
|
77
|
+
geminiRl.question(chalk.blue(`Select a Gemini model (${geminiModels.join(', ')}): `), (model) => {
|
|
78
|
+
geminiRl.close();
|
|
79
|
+
resolve(model || config.model); // Use input or current model if empty
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
break;
|
|
83
|
+
case 'openai':
|
|
84
|
+
const openaiModels = ['gpt-4o', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo'];
|
|
85
|
+
const openaiRl = readline.createInterface({
|
|
86
|
+
input: process.stdin,
|
|
87
|
+
output: process.stdout
|
|
88
|
+
});
|
|
89
|
+
config.model = await new Promise(resolve => {
|
|
90
|
+
openaiRl.question(chalk.blue(`Select an OpenAI model (${openaiModels.join(', ')}): `), (model) => {
|
|
91
|
+
openaiRl.close();
|
|
92
|
+
resolve(model || config.model);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
break;
|
|
96
|
+
case 'claude':
|
|
97
|
+
const claudeModels = ['claude-3-opus-20240229', 'claude-3-sonnet-20240229', 'claude-3-haiku-20240307'];
|
|
98
|
+
const claudeRl = readline.createInterface({
|
|
99
|
+
input: process.stdin,
|
|
100
|
+
output: process.stdout
|
|
101
|
+
});
|
|
102
|
+
config.model = await new Promise(resolve => {
|
|
103
|
+
claudeRl.question(chalk.blue(`Select a Claude model (${claudeModels.join(', ')}): `), (model) => {
|
|
104
|
+
claudeRl.close();
|
|
105
|
+
resolve(model || config.model);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(chalk.red(`Error selecting model: ${error.message}`));
|
|
112
|
+
process.exit(1); // Exit if model selection fails
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
117
|
+
console.log(chalk.green(`โ
Configuration saved for ${config.provider} provider`));
|
|
118
|
+
if (config.model) {
|
|
119
|
+
console.log(chalk.green(` Model: ${config.model}`));
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Install git hooks
|
|
124
|
+
program
|
|
125
|
+
.command('install')
|
|
126
|
+
.description('Install git hooks (pre-commit and pre-push)')
|
|
127
|
+
.option('--hooks-dir <dir>', 'Custom hooks directory', '.git/hooks')
|
|
128
|
+
.action(async (options) => {
|
|
129
|
+
const spinner = ora('Installing git hooks...').start();
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const hooksDir = path.resolve(options.hooksDir);
|
|
133
|
+
if (!fs.existsSync(hooksDir)) {
|
|
134
|
+
fs.mkdirSync(hooksDir, { recursive: true });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Create pre-commit hook
|
|
138
|
+
const preCommitHook = `#!/usr/bin/env bash
|
|
139
|
+
# Codeflow pre-commit hook
|
|
140
|
+
# Auto-generated by codeflow-hook CLI
|
|
141
|
+
|
|
142
|
+
set -e
|
|
143
|
+
|
|
144
|
+
echo "๐ฌ Running Codeflow AI Code Analysis..."
|
|
145
|
+
|
|
146
|
+
# Get staged changes
|
|
147
|
+
STAGED_DIFF=$(git diff --cached --no-color)
|
|
148
|
+
|
|
149
|
+
if [ -z "$STAGED_DIFF" ]; then
|
|
150
|
+
echo "โน๏ธ No staged changes to analyze"
|
|
151
|
+
exit 0
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
# Run AI analysis
|
|
155
|
+
npx codeflow-hook analyze-diff "$STAGED_DIFF"
|
|
156
|
+
`;
|
|
157
|
+
|
|
158
|
+
fs.writeFileSync(path.join(hooksDir, 'pre-commit'), preCommitHook, { mode: 0o755 });
|
|
159
|
+
|
|
160
|
+
// Create pre-push hook (enhanced version)
|
|
161
|
+
const prePushHook = `#!/usr/bin/env bash
|
|
162
|
+
# Codeflow pre-push hook
|
|
163
|
+
# Auto-generated by codeflow-hook CLI
|
|
164
|
+
|
|
165
|
+
set -e
|
|
166
|
+
|
|
167
|
+
echo "๐ Running Codeflow CI/CD simulation..."
|
|
168
|
+
|
|
169
|
+
# Run tests if available
|
|
170
|
+
if [ -f "package.json" ]; then
|
|
171
|
+
echo "๐งช Running tests..."
|
|
172
|
+
npm test || (echo "โ Tests failed" && exit 1)
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
# Get staged changes for AI analysis
|
|
176
|
+
STAGED_DIFF=$(git diff --cached --no-color)
|
|
177
|
+
|
|
178
|
+
if [ -n "$STAGED_DIFF" ]; then
|
|
179
|
+
echo "๐ฌ Running AI Code Review..."
|
|
180
|
+
npx codeflow-hook analyze-diff "$STAGED_DIFF" || exit 1
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
echo "โ
All checks passed!"
|
|
184
|
+
exit 0
|
|
185
|
+
`;
|
|
186
|
+
|
|
187
|
+
fs.writeFileSync(path.join(hooksDir, 'pre-push'), prePushHook, { mode: 0o755 });
|
|
188
|
+
|
|
189
|
+
spinner.succeed('Git hooks installed successfully');
|
|
190
|
+
console.log(chalk.blue('๐ Installed hooks:'));
|
|
191
|
+
console.log(chalk.gray(' - pre-commit: AI analysis on staged changes'));
|
|
192
|
+
console.log(chalk.gray(' - pre-push: CI/CD simulation with AI review + tests'));
|
|
193
|
+
|
|
194
|
+
} catch (error) {
|
|
195
|
+
spinner.fail('Failed to install hooks');
|
|
196
|
+
console.error(chalk.red(error.message));
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Analyze diff with configured AI provider
|
|
202
|
+
program
|
|
203
|
+
.command('analyze-diff')
|
|
204
|
+
.description('Analyze git diff with configured AI provider')
|
|
205
|
+
.argument('<diff>', 'Git diff content')
|
|
206
|
+
.action(async (diff) => {
|
|
207
|
+
try {
|
|
208
|
+
const configPath = path.join(process.env.HOME, '.codeflow-hook', 'config.json');
|
|
209
|
+
|
|
210
|
+
if (!fs.existsSync(configPath)) {
|
|
211
|
+
console.log(chalk.red('No configuration found. Run: codeflow-hook config -k <api-key>'));
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
216
|
+
|
|
217
|
+
if (diff.trim() === '') {
|
|
218
|
+
console.log(chalk.gray('โน๏ธ No changes to analyze'));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const spinner = ora(`Analyzing code with ${config.provider}...`).start();
|
|
223
|
+
const prompt = generateCodeReviewPrompt(diff);
|
|
224
|
+
|
|
225
|
+
let result;
|
|
226
|
+
try {
|
|
227
|
+
result = await callAIProvider(config, prompt);
|
|
228
|
+
} catch (error) {
|
|
229
|
+
spinner.fail('Analysis failed');
|
|
230
|
+
console.error(chalk.red(`AI API Error: ${error.message}`));
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
spinner.succeed('Analysis complete');
|
|
235
|
+
|
|
236
|
+
// Parse and display results
|
|
237
|
+
displayAnalysisResults(result);
|
|
238
|
+
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.log(chalk.red(`Configuration error: ${error.message}`));
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Show status
|
|
246
|
+
program
|
|
247
|
+
.command('status')
|
|
248
|
+
.description('Show installation and configuration status')
|
|
249
|
+
.action(() => {
|
|
250
|
+
console.log(chalk.blue('๐ Codeflow Hook Status'));
|
|
251
|
+
console.log();
|
|
252
|
+
|
|
253
|
+
// Check configuration
|
|
254
|
+
const configPath = path.join(process.env.HOME, '.codeflow-hook', 'config.json');
|
|
255
|
+
if (fs.existsSync(configPath)) {
|
|
256
|
+
console.log(chalk.green('โ
Configuration: Found'));
|
|
257
|
+
} else {
|
|
258
|
+
console.log(chalk.red('โ Configuration: Not found (run: codeflow-hook config)'));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Check git hooks
|
|
262
|
+
const hooksDir = '.git/hooks';
|
|
263
|
+
const preCommitHook = path.join(hooksDir, 'pre-commit');
|
|
264
|
+
const prePushHook = path.join(hooksDir, 'pre-push');
|
|
265
|
+
|
|
266
|
+
if (fs.existsSync(preCommitHook)) {
|
|
267
|
+
console.log(chalk.green('โ
Git Hook (pre-commit): Installed'));
|
|
268
|
+
} else {
|
|
269
|
+
console.log(chalk.red('โ Git Hook (pre-commit): Not installed'));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (fs.existsSync(prePushHook)) {
|
|
273
|
+
console.log(chalk.green('โ
Git Hook (pre-push): Installed'));
|
|
274
|
+
} else {
|
|
275
|
+
console.log(chalk.red('โ Git Hook (pre-push): Not installed'));
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
function generateCodeReviewPrompt(diff) {
|
|
280
|
+
return `You are "Codeflow", a world-class AI software engineering assistant acting as a Principal Engineer. Your mission is to perform a rigorous and constructive code review on the provided code changes.
|
|
281
|
+
|
|
282
|
+
**Guidelines:**
|
|
283
|
+
- Focus on code quality, security, performance, and best practices
|
|
284
|
+
- Be constructive and provide actionable suggestions
|
|
285
|
+
- Rate the changes on a scale of 1-10 (where 10 is excellent)
|
|
286
|
+
- If there are critical issues, suggest fixes
|
|
287
|
+
|
|
288
|
+
**Format your response as:**
|
|
289
|
+
**Rating:** [1-10]/10
|
|
290
|
+
**Summary:** [Brief summary]
|
|
291
|
+
|
|
292
|
+
**Issues:** (if any)
|
|
293
|
+
- [Issue description and suggestion]
|
|
294
|
+
|
|
295
|
+
**Recommendations:** (if any)
|
|
296
|
+
- [Recommendation]
|
|
297
|
+
|
|
298
|
+
**Code Changes to Review:**
|
|
299
|
+
\`\`\`
|
|
300
|
+
${diff}
|
|
301
|
+
\`\`\`
|
|
302
|
+
|
|
303
|
+
Provide your analysis:`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function callAIProvider(config, prompt) {
|
|
307
|
+
switch (config.provider) {
|
|
308
|
+
case 'openai':
|
|
309
|
+
return callOpenAI(config, prompt);
|
|
310
|
+
case 'claude':
|
|
311
|
+
return callClaude(config, prompt);
|
|
312
|
+
case 'gemini':
|
|
313
|
+
default:
|
|
314
|
+
return callGemini(config, prompt);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async function callGemini(config, prompt) {
|
|
319
|
+
const payload = {
|
|
320
|
+
contents: [{
|
|
321
|
+
parts: [{
|
|
322
|
+
text: prompt
|
|
323
|
+
}]
|
|
324
|
+
}],
|
|
325
|
+
generationConfig: {
|
|
326
|
+
temperature: 0.2,
|
|
327
|
+
topK: 40,
|
|
328
|
+
topP: 0.95,
|
|
329
|
+
maxOutputTokens: 2048,
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const response = await axios.post(`${config.apiUrl}?key=${config.apiKey}`, payload, {
|
|
334
|
+
headers: {
|
|
335
|
+
'Content-Type': 'application/json'
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
return response.data.candidates[0].content.parts[0].text;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async function callOpenAI(config, prompt) {
|
|
343
|
+
const payload = {
|
|
344
|
+
model: config.model,
|
|
345
|
+
messages: [{ role: 'user', content: prompt }],
|
|
346
|
+
temperature: 0.2,
|
|
347
|
+
max_tokens: 2048
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const response = await axios.post(config.apiUrl, payload, {
|
|
351
|
+
headers: {
|
|
352
|
+
'Content-Type': 'application/json',
|
|
353
|
+
'Authorization': `Bearer ${config.apiKey}`
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
return response.data.choices[0].message.content;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
async function callClaude(config, prompt) {
|
|
361
|
+
const payload = {
|
|
362
|
+
model: config.model,
|
|
363
|
+
max_tokens: 2048,
|
|
364
|
+
messages: [{ role: 'user', content: prompt }]
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
const response = await axios.post(config.apiUrl, payload, {
|
|
368
|
+
headers: {
|
|
369
|
+
'Content-Type': 'application/json',
|
|
370
|
+
'x-api-key': config.apiKey,
|
|
371
|
+
'anthropic-version': '2023-06-01'
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
return response.data.content[0].text;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function displayAnalysisResults(result) {
|
|
379
|
+
// Parse the AI response and format it nicely
|
|
380
|
+
const lines = result.split('\n');
|
|
381
|
+
|
|
382
|
+
for (const line of lines) {
|
|
383
|
+
if (line.startsWith('**Rating:**')) {
|
|
384
|
+
const rating = line.match(/\*\*Rating:\*\*\s*(\d+)/);
|
|
385
|
+
if (rating) {
|
|
386
|
+
const score = parseInt(rating[1]);
|
|
387
|
+
if (score >= 8) {
|
|
388
|
+
console.log(chalk.green(`โญ ${line}`));
|
|
389
|
+
} else if (score >= 5) {
|
|
390
|
+
console.log(chalk.yellow(`โ ๏ธ ${line}`));
|
|
391
|
+
} else {
|
|
392
|
+
console.log(chalk.red(`โ ${line}`));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
} else if (line.includes('**Issues:**') || line.includes('**Recommendations:**')) {
|
|
396
|
+
console.log(chalk.blue(line));
|
|
397
|
+
} else if (line.startsWith('- ')) {
|
|
398
|
+
console.log(chalk.gray(line));
|
|
399
|
+
} else if (line.includes('**Summary:**')) {
|
|
400
|
+
console.log(chalk.cyan(line));
|
|
401
|
+
} else {
|
|
402
|
+
console.log(line);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
console.log();
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
program.parse();
|