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