ai-pr-review-cli 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AI PR Review CLI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,212 @@
1
+ # šŸ¤– AI PR Review CLI
2
+
3
+ An intelligent command-line tool that uses AI to review your code changes and provide instant feedback on pull requests, issues, typos, and improvements.
4
+
5
+ ## ✨ Features
6
+
7
+ - šŸ” **Intelligent Code Analysis** - Uses GPT to analyze your code changes
8
+ - šŸŽÆ **Smart Branch Detection** - Automatically detects common base branches
9
+ - šŸŽØ **Beautiful CLI Interface** - Clean, colorful output with emojis
10
+ - ⚔ **Fast & Easy** - One command to get comprehensive feedback
11
+ - šŸ”§ **Flexible Configuration** - Multiple ways to configure base branch and AI model
12
+ - šŸ“Š **Categorized Feedback** - Issues, typos, and improvements clearly separated
13
+
14
+ ## šŸš€ Quick Start
15
+
16
+ ### Installation
17
+
18
+ ```bash
19
+ # Install globally for use anywhere
20
+ npm install -g ai-pr-review-cli
21
+
22
+ # Or use with npx (no installation needed)
23
+ npx ai-pr-review-cli review
24
+ ```
25
+
26
+ ### Setup
27
+
28
+ 1. **Get an OpenAI API key** from [OpenAI](https://platform.openai.com/api-keys)
29
+
30
+ 2. **Set your API key**:
31
+ ```bash
32
+ # Option 1: Environment variable (recommended)
33
+ export OPENAI_API_KEY="your-api-key-here"
34
+
35
+ # Option 2: Create .env file in your project
36
+ echo "OPENAI_API_KEY=your-api-key-here" > .env
37
+ ```
38
+
39
+ 3. **Run a review**:
40
+ ```bash
41
+ # In your git repository
42
+ ai-pr-review review
43
+
44
+ # Or use the short alias
45
+ aipr review
46
+ ```
47
+
48
+ ## šŸ“– Usage
49
+
50
+ ### Basic Usage
51
+
52
+ ```bash
53
+ # Review current branch against main
54
+ ai-pr-review review
55
+
56
+ # Review against specific branch
57
+ ai-pr-review review --base origin/develop
58
+
59
+ # Use different AI model
60
+ ai-pr-review review --model gpt-4o
61
+
62
+ # Show detailed output
63
+ ai-pr-review review --verbose
64
+ ```
65
+
66
+ ### Command Options
67
+
68
+ | Option | Short | Description | Example |
69
+ |--------|-------|-------------|---------|
70
+ | `--base <branch>` | `-b` | Base branch to compare against | `-b origin/main` |
71
+ | `--model <model>` | `-m` | AI model to use | `-m gpt-4o` |
72
+ | `--provider <provider>` | | AI provider (currently only openai) | `--provider openai` |
73
+ | `--verbose` | `-v` | Show detailed output | `-v` |
74
+
75
+ ### Examples
76
+
77
+ ```bash
78
+ # Review feature branch against main
79
+ git checkout feature/new-feature
80
+ ai-pr-review review --base main
81
+
82
+ # Review with GPT-4 and verbose output
83
+ ai-pr-review review --model gpt-4o --verbose
84
+
85
+ # Review staged changes only
86
+ git add .
87
+ ai-pr-review review
88
+
89
+ # Review uncommitted changes
90
+ ai-pr-review review # Automatically includes working directory changes
91
+ ```
92
+
93
+ ## āš™ļø Configuration
94
+
95
+ ### Method 1: Command Line Arguments (Recommended)
96
+ ```bash
97
+ ai-pr-review review --base origin/main --model gpt-4o-mini
98
+ ```
99
+
100
+ ### Method 2: Project Configuration File
101
+ Create `.aiprconfig.json` in your project root:
102
+
103
+ ```json
104
+ {
105
+ "provider": "openai",
106
+ "model": "gpt-4o-mini",
107
+ "baseBranch": "origin/main"
108
+ }
109
+ ```
110
+
111
+ ### Method 3: Auto-Detection
112
+ The tool automatically detects common base branches in this order:
113
+ 1. `origin/main`
114
+ 2. `origin/master`
115
+ 3. `origin/develop`
116
+ 4. `main`
117
+ 5. `master`
118
+ 6. `develop`
119
+ 7. `HEAD~1` (fallback)
120
+
121
+ ## šŸŽÆ What Gets Reviewed
122
+
123
+ The tool analyzes:
124
+ - āœ… **Committed changes** between branches
125
+ - āœ… **Staged changes** (files added with `git add`)
126
+ - āœ… **Working directory changes** (uncommitted modifications)
127
+ - āœ… **File contents** for context
128
+
129
+ ## šŸ“Š Output Categories
130
+
131
+ ### 🚨 Issues
132
+ - Critical bugs and logic errors
133
+ - Security vulnerabilities
134
+ - Performance problems
135
+ - Breaking changes
136
+
137
+ ### āœļø Typos
138
+ - Spelling mistakes in comments
139
+ - Grammar errors in strings
140
+ - Documentation typos
141
+
142
+ ### šŸ’” Improvements
143
+ - Code quality suggestions
144
+ - Best practice recommendations
145
+ - Performance optimizations
146
+ - Refactoring opportunities
147
+
148
+ ## šŸ”§ Supported AI Models
149
+
150
+ | Model | Speed | Quality | Cost |
151
+ |-------|-------|---------|------|
152
+ | `gpt-4o-mini` | ⚔⚔⚔ | ⭐⭐⭐ | šŸ’° |
153
+ | `gpt-4o` | ⚔⚔ | ⭐⭐⭐⭐⭐ | šŸ’°šŸ’°šŸ’° |
154
+ | `gpt-3.5-turbo` | ⚔⚔⚔ | ⭐⭐ | šŸ’° |
155
+
156
+ ## šŸ› ļø Troubleshooting
157
+
158
+ ### "No changes found to review"
159
+ - Make sure you're on a feature branch (not main/master)
160
+ - Check if you have uncommitted changes: `git status`
161
+ - Try specifying a different base branch: `--base main`
162
+ - Update remote branches: `git fetch origin`
163
+
164
+ ### "API key not found"
165
+ ```bash
166
+ # Set your OpenAI API key
167
+ export OPENAI_API_KEY="your-key-here"
168
+
169
+ # Or create .env file
170
+ echo "OPENAI_API_KEY=your-key-here" > .env
171
+ ```
172
+
173
+ ### "Branch not found"
174
+ ```bash
175
+ # List all available branches
176
+ git branch -a
177
+
178
+ # Use a branch that exists
179
+ ai-pr-review review --base origin/master
180
+ ```
181
+
182
+ ## šŸ” Security & Privacy
183
+
184
+ - Your code is sent to OpenAI's API for analysis
185
+ - API keys are read from environment variables or .env files
186
+ - No code is stored permanently by the tool
187
+ - Consider using this on non-sensitive codebases
188
+ - Review OpenAI's [data usage policies](https://openai.com/policies/api-data-usage-policies)
189
+
190
+ ## šŸ¤ Contributing
191
+
192
+ Contributions are welcome! Please feel free to submit a Pull Request.
193
+
194
+ ## šŸ“„ License
195
+
196
+ MIT License - see [LICENSE](LICENSE) file for details.
197
+
198
+ ## šŸ’” Tips
199
+
200
+ - **Use on feature branches** for best results
201
+ - **Commit changes first** for more accurate reviews
202
+ - **Use `--verbose`** to see what's being analyzed
203
+ - **Try different models** for varying levels of detail
204
+ - **Set up aliases** in your shell for quick access:
205
+ ```bash
206
+ alias review="ai-pr-review review"
207
+ alias aipr="ai-pr-review review"
208
+ ```
209
+
210
+ ---
211
+
212
+ Made with ā¤ļø for developers who want better code reviews!
package/bin/cli.js ADDED
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { Command } = require("commander");
4
+ const review = require("../src/reviewer");
5
+ const { createGlobalConfig } = require("../src/config");
6
+
7
+ const program = new Command();
8
+
9
+ program
10
+ .name("ai-pr-review")
11
+ .description("šŸ¤– AI-powered Pull Request reviewer")
12
+ .version("1.0.0");
13
+
14
+ program
15
+ .command("review")
16
+ .description("šŸ” Review current branch changes with AI")
17
+ .option("-b, --base <branch>", "Base branch to compare against (e.g., main, origin/main, develop)")
18
+ .option("-m, --model <model>", "AI model to use (default: gpt-4o-mini)")
19
+ .option("--provider <provider>", "AI provider (default: openai)")
20
+ .option("-v, --verbose", "Show detailed output")
21
+ .action(async (options) => {
22
+ // Show a nice header
23
+ const { default: chalk } = await import("chalk");
24
+ console.log(chalk.blue.bold("\nšŸ¤– AI PR Review"));
25
+ console.log(chalk.gray("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"));
26
+
27
+ await review(options);
28
+ });
29
+
30
+ program
31
+ .command("config")
32
+ .description("āš™ļø Set up global configuration")
33
+ .option("-m, --model <model>", "Default AI model to use")
34
+ .option("-b, --base <branch>", "Default base branch")
35
+ .option("--provider <provider>", "Default AI provider")
36
+ .action(async (options) => {
37
+ const { default: chalk } = await import("chalk");
38
+
39
+ if (Object.keys(options).length === 0) {
40
+ console.log(chalk.yellow("Please specify configuration options:"));
41
+ console.log(chalk.gray(" ai-pr-review config --model gpt-4o-mini --base main"));
42
+ console.log(chalk.gray(" ai-pr-review config --provider openai"));
43
+ return;
44
+ }
45
+
46
+ try {
47
+ const configPath = createGlobalConfig(options);
48
+ console.log(chalk.green("āœ… Global configuration saved!"));
49
+ console.log(chalk.gray(` Config file: ${configPath}`));
50
+
51
+ if (options.model) console.log(chalk.blue(` Default model: ${options.model}`));
52
+ if (options.provider) console.log(chalk.blue(` Default provider: ${options.provider}`));
53
+ if (options.base) console.log(chalk.blue(` Default base branch: ${options.base}`));
54
+ } catch (error) {
55
+ console.error(chalk.red(`āŒ Failed to save configuration: ${error.message}`));
56
+ }
57
+ });
58
+
59
+ // Also support direct usage without subcommand
60
+ program
61
+ .option("-b, --base <branch>", "Base branch to compare against")
62
+ .option("-m, --model <model>", "AI model to use")
63
+ .option("--provider <provider>", "AI provider")
64
+ .option("-v, --verbose", "Show detailed output")
65
+ .action(async (options) => {
66
+ // If no specific command was run, default to review
67
+ if (process.argv.length > 2 && !process.argv.includes('review')) {
68
+ const { default: chalk } = await import("chalk");
69
+ console.log(chalk.blue.bold("\nšŸ¤– AI PR Review"));
70
+ console.log(chalk.gray("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"));
71
+ await review(options);
72
+ }
73
+ });
74
+
75
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "ai-pr-review-cli",
3
+ "version": "1.0.0",
4
+ "description": "šŸ¤– AI-powered Pull Request reviewer - Get instant feedback on your code changes using GPT",
5
+ "main": "src/reviewer.js",
6
+ "bin": {
7
+ "ai-pr-review": "./bin/cli.js",
8
+ "aipr": "./bin/cli.js"
9
+ },
10
+ "type": "commonjs",
11
+ "engines": {
12
+ "node": ">=16.0.0"
13
+ },
14
+ "scripts": {
15
+ "start": "node bin/cli.js",
16
+ "test": "echo \"Error: no test specified\" && exit 1",
17
+ "prepublishOnly": "echo 'Ready to publish!'"
18
+ },
19
+ "keywords": [
20
+ "ai",
21
+ "artificial-intelligence",
22
+ "pr-review",
23
+ "pull-request",
24
+ "code-review",
25
+ "cli",
26
+ "git",
27
+ "github",
28
+ "openai",
29
+ "gpt",
30
+ "developer-tools",
31
+ "automation"
32
+ ],
33
+ "author": {
34
+ "name": "Deepanshu Chauhan",
35
+ "email": "chauhandeepanshu336@gmail.com"
36
+ },
37
+ "license": "MIT",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://github.com/yourusername/ai-pr-review-cli.git"
41
+ },
42
+ "bugs": {
43
+ "url": "https://github.com/yourusername/ai-pr-review-cli/issues"
44
+ },
45
+ "homepage": "https://github.com/yourusername/ai-pr-review-cli#readme",
46
+ "files": [
47
+ "bin/",
48
+ "src/",
49
+ "README.md",
50
+ "LICENSE"
51
+ ],
52
+ "preferGlobal": true,
53
+ "dependencies": {
54
+ "axios": "^1.6.0",
55
+ "chalk": "^5.3.0",
56
+ "commander": "^11.0.0",
57
+ "dotenv": "^17.3.1",
58
+ "ora": "^6.3.1",
59
+ "simple-git": "^3.19.1"
60
+ },
61
+ "devDependencies": {},
62
+ "funding": {
63
+ "type": "individual",
64
+ "url": "https://github.com/blockDeepanshu"
65
+ }
66
+ }
package/src/ai.js ADDED
@@ -0,0 +1,25 @@
1
+ const axios = require("axios");
2
+ require("dotenv").config();
3
+ async function openai(prompt, model) {
4
+ const res = await axios.post(
5
+ "https://api.openai.com/v1/chat/completions",
6
+ {
7
+ model,
8
+ messages: [{ role: "user", content: prompt }],
9
+ temperature: 0.2,
10
+ },
11
+ {
12
+ headers: {
13
+ Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
14
+ },
15
+ },
16
+ );
17
+
18
+ return res.data.choices[0].message.content;
19
+ }
20
+
21
+ async function runAI(prompt, config) {
22
+ return openai(prompt, config.model);
23
+ }
24
+
25
+ module.exports = { runAI };
package/src/config.js ADDED
@@ -0,0 +1,52 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const os = require("os");
4
+
5
+ function getConfig() {
6
+ // Default configuration
7
+ const defaultConfig = {
8
+ provider: "openai",
9
+ model: "gpt-4o-mini",
10
+ baseBranch: null, // Will be auto-detected
11
+ };
12
+
13
+ // Try to load global config from user's home directory
14
+ const globalConfigPath = path.join(os.homedir(), ".aiprconfig.json");
15
+ let config = { ...defaultConfig };
16
+
17
+ if (fs.existsSync(globalConfigPath)) {
18
+ try {
19
+ const globalConfig = JSON.parse(fs.readFileSync(globalConfigPath, "utf-8"));
20
+ config = { ...config, ...globalConfig };
21
+ } catch (error) {
22
+ console.warn("Warning: Could not parse global config file:", globalConfigPath);
23
+ }
24
+ }
25
+
26
+ // Try to load local project config (overrides global)
27
+ const localConfigPath = path.join(process.cwd(), ".aiprconfig.json");
28
+ if (fs.existsSync(localConfigPath)) {
29
+ try {
30
+ const localConfig = JSON.parse(fs.readFileSync(localConfigPath, "utf-8"));
31
+ config = { ...config, ...localConfig };
32
+ } catch (error) {
33
+ console.warn("Warning: Could not parse local config file:", localConfigPath);
34
+ }
35
+ }
36
+
37
+ return config;
38
+ }
39
+
40
+ function createGlobalConfig(options) {
41
+ const globalConfigPath = path.join(os.homedir(), ".aiprconfig.json");
42
+ const config = {
43
+ provider: options.provider || "openai",
44
+ model: options.model || "gpt-4o-mini",
45
+ ...(options.baseBranch && { baseBranch: options.baseBranch })
46
+ };
47
+
48
+ fs.writeFileSync(globalConfigPath, JSON.stringify(config, null, 2));
49
+ return globalConfigPath;
50
+ }
51
+
52
+ module.exports = { getConfig, createGlobalConfig };
package/src/git.js ADDED
@@ -0,0 +1,148 @@
1
+ const simpleGit = require("simple-git");
2
+ const fs = require("fs");
3
+
4
+ const git = simpleGit();
5
+
6
+ async function getCurrentBranch() {
7
+ try {
8
+ const status = await git.status();
9
+ return status.current;
10
+ } catch (error) {
11
+ return 'unknown';
12
+ }
13
+ }
14
+
15
+ async function checkBranchExists(branchName) {
16
+ try {
17
+ await git.raw(['rev-parse', '--verify', branchName]);
18
+ return true;
19
+ } catch (error) {
20
+ return false;
21
+ }
22
+ }
23
+
24
+ async function detectBaseBranch() {
25
+ // List of common base branch names in order of preference
26
+ const commonBranches = [
27
+ 'origin/main',
28
+ 'origin/master',
29
+ 'origin/develop',
30
+ 'main',
31
+ 'master',
32
+ 'develop'
33
+ ];
34
+
35
+ for (const branch of commonBranches) {
36
+ if (await checkBranchExists(branch)) {
37
+ return branch;
38
+ }
39
+ }
40
+
41
+ // Fallback to HEAD~1 if no common branch found
42
+ return 'HEAD~1';
43
+ }
44
+
45
+ async function getAllBranches() {
46
+ try {
47
+ const result = await git.branch(['-a']);
48
+ return result.all;
49
+ } catch (error) {
50
+ return [];
51
+ }
52
+ }
53
+
54
+ async function getDiff(baseBranch) {
55
+ try {
56
+ // First try the original approach
57
+ const committedDiff = await git.diff([`${baseBranch}...HEAD`]);
58
+
59
+ // Also check for working directory changes
60
+ const workingDiff = await git.diff();
61
+ const stagedDiff = await git.diff(['--cached']);
62
+
63
+ // Combine all diffs
64
+ const totalDiff = [committedDiff, workingDiff, stagedDiff].filter(d => d.length > 0).join('\n\n--- NEXT DIFF SECTION ---\n\n');
65
+
66
+ return totalDiff;
67
+ } catch (error) {
68
+ // Silently try alternative approaches
69
+
70
+ try {
71
+ // Try diffing against the previous commit
72
+ return await git.diff(['HEAD~1']);
73
+ } catch (error2) {
74
+ try {
75
+ // Fallback: show changes in working directory and staged changes
76
+ const workingDiff = await git.diff();
77
+ const stagedDiff = await git.diff(['--cached']);
78
+ return workingDiff + '\n' + stagedDiff;
79
+ } catch (error3) {
80
+ return '';
81
+ }
82
+ }
83
+ }
84
+ }
85
+
86
+ async function getChangedFiles(baseBranch) {
87
+ try {
88
+ // Get committed changes
89
+ const committedSummary = await git.diffSummary([`${baseBranch}...HEAD`]);
90
+ const committedFiles = committedSummary.files.map((f) => f.file);
91
+
92
+ // Also check for working directory changes
93
+ const workingSummary = await git.diffSummary();
94
+ const workingFiles = workingSummary.files.map((f) => f.file);
95
+
96
+ // Also check for staged changes
97
+ const stagedSummary = await git.diffSummary(['--cached']);
98
+ const stagedFiles = stagedSummary.files.map((f) => f.file);
99
+
100
+ // Combine all changed files (remove duplicates)
101
+ const allFiles = [...new Set([...committedFiles, ...workingFiles, ...stagedFiles])];
102
+
103
+ return allFiles;
104
+ } catch (error) {
105
+ // Try alternatives silently
106
+
107
+ try {
108
+ // Try diffing against the previous commit
109
+ const summary = await git.diffSummary(['HEAD~1']);
110
+ return summary.files.map((f) => f.file);
111
+ } catch (error2) {
112
+ try {
113
+ // Fallback: show changes in working directory
114
+ const summary = await git.diffSummary();
115
+ return summary.files.map((f) => f.file);
116
+ } catch (error3) {
117
+ // Return all JavaScript/TypeScript files in src directory
118
+ const files = [];
119
+ if (fs.existsSync('src')) {
120
+ const srcFiles = fs.readdirSync('src', { recursive: true });
121
+ files.push(...srcFiles
122
+ .filter(f => typeof f === 'string' && (f.endsWith('.js') || f.endsWith('.ts') || f.endsWith('.jsx') || f.endsWith('.tsx')))
123
+ .map(f => `src/${f}`)
124
+ );
125
+ }
126
+ // Also check root directory for common files
127
+ const rootFiles = ['package.json', 'README.md', 'index.js', 'app.js', 'server.js'];
128
+ rootFiles.forEach(file => {
129
+ if (fs.existsSync(file)) {
130
+ files.push(file);
131
+ }
132
+ });
133
+ return files;
134
+ }
135
+ }
136
+ }
137
+ }
138
+
139
+ function readFiles(files) {
140
+ return files
141
+ .filter((f) => fs.existsSync(f))
142
+ .map((file) => ({
143
+ name: file,
144
+ content: fs.readFileSync(file, "utf-8").slice(0, 5000),
145
+ }));
146
+ }
147
+
148
+ module.exports = { getDiff, getChangedFiles, readFiles, getCurrentBranch, checkBranchExists, detectBaseBranch, getAllBranches };
package/src/prompt.js ADDED
@@ -0,0 +1,22 @@
1
+ function buildPrompt(diff, files) {
2
+ return `You are a strict code reviewer. Find ALL problems in these code changes. Check for:
3
+
4
+ ISSUES: Syntax errors, logic bugs, invalid JSON/config, security flaws, broken imports, undefined variables, type errors
5
+ TYPOS: Spelling/grammar mistakes in comments, strings, documentation
6
+ IMPROVEMENTS: Code quality, performance, best practices, refactoring opportunities
7
+
8
+ DIFF:
9
+ ${diff}
10
+
11
+ FILES:
12
+ ${files.map((f) => `${f.name}:\n${f.content}`).join("\n\n")}
13
+
14
+ Be thorough and critical. Return ONLY this JSON (no other text):
15
+ {
16
+ "issues": ["list specific problems found"],
17
+ "typos": ["list spelling/grammar errors"],
18
+ "improvements": ["list enhancement suggestions"]
19
+ }`;
20
+ }
21
+
22
+ module.exports = { buildPrompt };
@@ -0,0 +1,151 @@
1
+ const { getDiff, getChangedFiles, readFiles, getCurrentBranch, checkBranchExists, detectBaseBranch } = require("./git");
2
+ const { buildPrompt } = require("./prompt");
3
+ const { runAI } = require("./ai");
4
+ const { getConfig } = require("./config");
5
+
6
+ async function review(options = {}) {
7
+ const { default: ora } = await import("ora");
8
+ const { default: chalk } = await import("chalk");
9
+
10
+ const spinner = ora("šŸ¤– AI is analyzing your code changes...").start();
11
+
12
+ try {
13
+ // Merge config with CLI options
14
+ const baseConfig = getConfig();
15
+ const config = {
16
+ ...baseConfig,
17
+ ...(options.provider && { provider: options.provider }),
18
+ ...(options.model && { model: options.model })
19
+ };
20
+
21
+ // Determine base branch: CLI option > config file > auto-detection
22
+ let baseBranch = options.base || config.baseBranch;
23
+
24
+ if (!baseBranch) {
25
+ if (options.verbose) console.log(chalk.gray("šŸ” Auto-detecting base branch..."));
26
+ baseBranch = await detectBaseBranch();
27
+ }
28
+
29
+ // Get current git status
30
+ const currentBranch = await getCurrentBranch();
31
+ const baseBranchExists = await checkBranchExists(baseBranch);
32
+
33
+ console.log(chalk.blue(`\nšŸ“Š Analyzing changes on branch: ${chalk.bold(currentBranch)}`));
34
+ if (options.verbose || !baseBranchExists) {
35
+ console.log(chalk.gray(` Comparing against: ${baseBranch} ${baseBranchExists ? 'āœ“' : 'āŒ'}`));
36
+ }
37
+
38
+ const diff = await getDiff(baseBranch);
39
+ const changedFiles = await getChangedFiles(baseBranch);
40
+ const files = readFiles(changedFiles);
41
+
42
+ if (files.length === 0 && diff.length === 0) {
43
+ spinner.info(chalk.yellow('No changes found to review'));
44
+
45
+ console.log(chalk.yellow('\nšŸ’” Possible reasons:'));
46
+ if (currentBranch === baseBranch.replace('origin/', '')) {
47
+ console.log(chalk.gray(' • You are currently on the base branch. Create a feature branch first.'));
48
+ }
49
+ if (!baseBranchExists) {
50
+ console.log(chalk.gray(' • The base branch does not exist. Try "main", "master", or "origin/master".'));
51
+ }
52
+ console.log(chalk.gray(' • No commits made yet on this branch.'));
53
+ console.log(chalk.gray(' • Try: git fetch origin (to update remote branches)'));
54
+ console.log(chalk.gray(' • Try: git status (to see uncommitted changes)'));
55
+
56
+ return;
57
+ }
58
+
59
+ console.log(chalk.green(`šŸ“ Found ${changedFiles.length} changed file${changedFiles.length === 1 ? '' : 's'}: ${chalk.bold(changedFiles.join(', '))}`));
60
+
61
+ const prompt = buildPrompt(diff, files);
62
+ const result = await runAI(prompt, config);
63
+
64
+ spinner.succeed(chalk.green('Review completed!'));
65
+
66
+ let parsed;
67
+ try {
68
+ parsed = JSON.parse(result);
69
+ } catch (jsonError) {
70
+ // Try multiple strategies to extract JSON
71
+ let jsonText = null;
72
+
73
+ // Strategy 1: Find JSON object with proper structure
74
+ const jsonMatch = result.match(/\{[\s\S]*?"issues"[\s\S]*?"typos"[\s\S]*?"improvements"[\s\S]*?\}/);
75
+ if (jsonMatch) {
76
+ jsonText = jsonMatch[0];
77
+ } else {
78
+ // Strategy 2: Find any JSON object
79
+ const anyJsonMatch = result.match(/\{[\s\S]*?\}/);
80
+ if (anyJsonMatch) {
81
+ jsonText = anyJsonMatch[0];
82
+ }
83
+ }
84
+
85
+ if (jsonText) {
86
+ try {
87
+ parsed = JSON.parse(jsonText);
88
+ // Ensure required structure
89
+ if (!parsed.issues) parsed.issues = [];
90
+ if (!parsed.typos) parsed.typos = [];
91
+ if (!parsed.improvements) parsed.improvements = [];
92
+ } catch (extractError) {
93
+ // Create fallback response
94
+ parsed = {
95
+ issues: [`JSON parsing failed. Raw AI response: ${result.substring(0, 200)}...`],
96
+ typos: [],
97
+ improvements: []
98
+ };
99
+ }
100
+ } else {
101
+ // No JSON found at all - create fallback response
102
+ parsed = {
103
+ issues: [`AI response format error. Response: ${result.substring(0, 200)}...`],
104
+ typos: [],
105
+ improvements: []
106
+ };
107
+ }
108
+ }
109
+
110
+ // Display results with better formatting
111
+ console.log(chalk.red.bold("\n🚨 Issues"));
112
+ if (parsed.issues.length === 0) {
113
+ console.log(chalk.gray(" No critical issues found"));
114
+ } else {
115
+ parsed.issues.forEach((i, index) =>
116
+ console.log(chalk.red(` ${index + 1}. ${i}`))
117
+ );
118
+ }
119
+
120
+ console.log(chalk.yellow.bold("\nāœļø Typos"));
121
+ if (parsed.typos.length === 0) {
122
+ console.log(chalk.gray(" No typos found"));
123
+ } else {
124
+ parsed.typos.forEach((t, index) =>
125
+ console.log(chalk.yellow(` ${index + 1}. ${t}`))
126
+ );
127
+ }
128
+
129
+ console.log(chalk.cyan.bold("\nšŸ’” Improvements"));
130
+ if (parsed.improvements.length === 0) {
131
+ console.log(chalk.gray(" No improvement suggestions"));
132
+ } else {
133
+ parsed.improvements.forEach((i, index) =>
134
+ console.log(chalk.cyan(` ${index + 1}. ${i}`))
135
+ );
136
+ }
137
+
138
+ // Summary
139
+ const totalCount = parsed.issues.length + parsed.typos.length + parsed.improvements.length;
140
+ if (totalCount === 0) {
141
+ console.log(chalk.green.bold("\n✨ Great job! Your code looks clean and well-written."));
142
+ } else {
143
+ console.log(chalk.blue(`\nšŸ“‹ Review Summary: ${parsed.issues.length} issues, ${parsed.typos.length} typos, ${parsed.improvements.length} improvements`));
144
+ }
145
+ } catch (e) {
146
+ spinner.fail(chalk.red("Review failed"));
147
+ console.error(chalk.red(`āŒ Error: ${e.message}`));
148
+ }
149
+ }
150
+
151
+ module.exports = review;