codex-review-mcp 2.10.4 → 2.10.6
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/dist/mcp-server.js +43 -64
- package/dist/review/collectDiff.js +42 -36
- package/dist/tools/performCodeReview.js +36 -14
- package/package.json +1 -1
package/dist/mcp-server.js
CHANGED
|
@@ -30,78 +30,57 @@ server.registerTool('perform_code_review', {
|
|
|
30
30
|
title: 'Perform Code Review',
|
|
31
31
|
description: `Review code/diffs using GPT-5 Codex with expert prompts and project-specific context.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
🚀 ZERO-CONFIG MODE - Just Call It!
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
// Review current branch in current workspace
|
|
36
|
+
await perform_code_review({});
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
workspaceDir: workspaceDir, // ⭐ REQUIRED: Where the repo is
|
|
40
|
-
baseBranch: "main", // Optional: defaults to main/master
|
|
41
|
-
focus: "security and performance" // Optional: specific areas
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
🔄 WHY USE BRANCH-BASED REVIEW?
|
|
45
|
-
✅ Faster: No need to run git diff yourself
|
|
46
|
-
✅ Simpler: Just pass branch name + workspace directory
|
|
47
|
-
✅ Smart: Automatically handles both committed AND uncommitted changes
|
|
48
|
-
✅ Reliable: We handle git operations correctly
|
|
49
|
-
✅ Focused: Reviews only the actual code changes, not docs
|
|
50
|
-
|
|
51
|
-
📋 ALTERNATIVE: CONTENT-BASED (Legacy)
|
|
38
|
+
// Review with focus
|
|
39
|
+
await perform_code_review({ focus: "security" });
|
|
52
40
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
await perform_code_review({
|
|
56
|
-
content: diff, // Your pre-collected diff
|
|
57
|
-
workspaceDir: workspaceDir, // For context gathering
|
|
58
|
-
contentType: "diff" // "diff" or "code"
|
|
59
|
-
});
|
|
41
|
+
// Review specific branch
|
|
42
|
+
await perform_code_review({ branchName: "feature/other-branch" });
|
|
60
43
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
44
|
+
✨ We Auto-Detect Everything:
|
|
45
|
+
✅ Current workspace (from WORKSPACE_ROOT env)
|
|
46
|
+
✅ Current branch (from git)
|
|
47
|
+
✅ Base branch (main/master/develop)
|
|
48
|
+
✅ All changes (committed + uncommitted)
|
|
49
|
+
✅ Project context (.cursor/rules, configs)
|
|
67
50
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
51
|
+
💡 OPTIONAL - Only When You Need Them:
|
|
52
|
+
• focus: "security", "performance", "React patterns"
|
|
53
|
+
• branchName: Review different branch
|
|
54
|
+
• workspaceDir: Review different repo
|
|
55
|
+
• force: Bypass chunking for large diffs (>2000 lines)
|
|
72
56
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
57
|
+
🔍 WHAT YOU GET:
|
|
58
|
+
Markdown report with:
|
|
59
|
+
• Quick summary + severity table
|
|
60
|
+
• Specific file:line references with code quotes
|
|
61
|
+
• Inline diff suggestions for fixes
|
|
62
|
+
• Clarifying questions (if context needed)
|
|
63
|
+
• Positive notes on good patterns
|
|
77
64
|
|
|
78
|
-
📋
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
- focus: Specific review focus (e.g., "security", "performance")
|
|
85
|
-
- customContext: Override auto-gathered context
|
|
86
|
-
- skipContextGathering: Skip context gathering (not recommended)
|
|
87
|
-
- force: Force review of large diffs (>2000 lines)
|
|
88
|
-
- maxTokens: Limit review length`,
|
|
65
|
+
📋 ADVANCED OPTIONS (Rarely Needed):
|
|
66
|
+
• baseBranch: Compare against non-default branch
|
|
67
|
+
• content: Pass diff directly instead of branchName (legacy)
|
|
68
|
+
• contentType: "diff" or "code" for static analysis
|
|
69
|
+
• customContext: Override auto-gathered project context
|
|
70
|
+
• maxTokens: Limit response length`,
|
|
89
71
|
inputSchema: {
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
customContext: z.string().optional().describe('
|
|
100
|
-
skipContextGathering: z.boolean().optional().describe('
|
|
101
|
-
|
|
102
|
-
focus: z.string().optional().describe('Specific areas to focus on (e.g., "security and performance")'),
|
|
103
|
-
maxTokens: z.number().optional().describe('Maximum tokens for review response'),
|
|
104
|
-
force: z.boolean().optional().describe('Force review even if diff is large (>2000 lines). Use this to bypass chunking suggestions.'),
|
|
72
|
+
// ZERO-CONFIG: All parameters are optional!
|
|
73
|
+
focus: z.string().optional().describe('Specialized review focus: "security", "performance", "React patterns", etc.'),
|
|
74
|
+
branchName: z.string().optional().describe('Branch to review (defaults to current branch)'),
|
|
75
|
+
workspaceDir: z.string().optional().describe('Repo path (defaults to WORKSPACE_ROOT env, auto-resolves relative paths)'),
|
|
76
|
+
force: z.boolean().optional().describe('Review large diffs (>2000 lines) without chunking suggestion'),
|
|
77
|
+
// ADVANCED - Rarely needed
|
|
78
|
+
baseBranch: z.string().optional().describe('Compare against specific branch (auto-detects main/master/develop)'),
|
|
79
|
+
content: z.string().optional().describe('LEGACY: Pass diff/code directly instead of branch-based review'),
|
|
80
|
+
contentType: z.enum(['diff', 'code']).optional().describe('LEGACY: "diff" or "code" when using content parameter'),
|
|
81
|
+
customContext: z.string().optional().describe('ADVANCED: Override auto-gathered project context'),
|
|
82
|
+
skipContextGathering: z.boolean().optional().describe('ADVANCED: Skip context gathering'),
|
|
83
|
+
maxTokens: z.number().optional().describe('ADVANCED: Limit response length'),
|
|
105
84
|
},
|
|
106
85
|
}, async (input, extra) => {
|
|
107
86
|
try {
|
|
@@ -45,47 +45,53 @@ export async function collectDiff(input) {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
await debugLog(`Collecting diff: ${base}...${branchName} in ${workspaceDir}`);
|
|
48
|
-
//
|
|
49
|
-
let
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
48
|
+
// Check if we're currently on the target branch
|
|
49
|
+
let currentBranch;
|
|
50
|
+
try {
|
|
51
|
+
currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {
|
|
52
|
+
cwd: workspaceDir,
|
|
53
|
+
encoding: 'utf8',
|
|
54
|
+
stdio: ['ignore', 'pipe', 'ignore']
|
|
55
|
+
}).trim();
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
currentBranch = '';
|
|
59
|
+
}
|
|
60
|
+
const onTargetBranch = currentBranch === branchName;
|
|
61
|
+
// If on target branch, get ALL changes (committed + uncommitted)
|
|
62
|
+
// This reviews work-in-progress, not just committed code
|
|
63
|
+
let diff;
|
|
64
|
+
if (onTargetBranch) {
|
|
65
|
+
await debugLog(`On target branch ${branchName}, reviewing ALL changes (committed + uncommitted)`);
|
|
66
|
+
// Get all changes from base branch (includes both committed and uncommitted)
|
|
67
|
+
diff = execFileSync('git', ['diff', base], {
|
|
68
|
+
cwd: workspaceDir,
|
|
69
|
+
encoding: 'utf8',
|
|
70
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
71
|
+
});
|
|
72
|
+
if (diff.trim()) {
|
|
73
|
+
await debugLog(`Found changes: ${diff.length} chars (committed + uncommitted)`);
|
|
68
74
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// Not on target branch, only get committed changes
|
|
78
|
+
await debugLog(`Not on target branch, reviewing only committed changes`);
|
|
79
|
+
diff = execFileSync('git', ['diff', `${base}...${branchName}`], {
|
|
80
|
+
cwd: workspaceDir,
|
|
81
|
+
encoding: 'utf8',
|
|
82
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
83
|
+
});
|
|
84
|
+
if (diff.trim()) {
|
|
85
|
+
await debugLog(`Found committed changes: ${diff.length} chars`);
|
|
81
86
|
}
|
|
82
87
|
}
|
|
83
88
|
if (!diff.trim()) {
|
|
89
|
+
const checkType = onTargetBranch
|
|
90
|
+
? `All changes (committed + uncommitted): git diff ${base}`
|
|
91
|
+
: `Committed changes: git diff ${base}...${branchName}`;
|
|
84
92
|
throw new Error(`No changes found on branch ${branchName}.\n` +
|
|
85
|
-
`
|
|
86
|
-
`
|
|
87
|
-
` 2. Uncommitted changes (if on branch): git diff ${base}\n` +
|
|
88
|
-
`Make sure the branch exists and has changes (committed or uncommitted).`);
|
|
93
|
+
`Checked: ${checkType}\n` +
|
|
94
|
+
`Make sure the branch has changes compared to ${base}.`);
|
|
89
95
|
}
|
|
90
96
|
await debugLog(`Collected diff: ${diff.length} chars, ${diff.split('\n').length} lines`);
|
|
91
97
|
return diff;
|
|
@@ -46,26 +46,48 @@ function parseFilesFromDiff(diff) {
|
|
|
46
46
|
* We handle: context gathering, expert prompt building, GPT-5 invocation, formatting.
|
|
47
47
|
*/
|
|
48
48
|
export async function performCodeReview(input, onProgress) {
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
// ZERO-CONFIG MODE: Default to current workspace and current branch!
|
|
50
|
+
// Perfect for "review this" or "review my changes"
|
|
51
|
+
// Auto-detect workspaceDir from Cursor's WORKSPACE_ROOT env var
|
|
52
|
+
let workspaceDir = input.workspaceDir || process.env.WORKSPACE_ROOT || process.cwd();
|
|
53
|
+
// Auto-resolve relative paths to absolute paths
|
|
54
|
+
if (!isAbsolute(workspaceDir)) {
|
|
55
|
+
workspaceDir = resolve(process.cwd(), workspaceDir);
|
|
56
|
+
await debugLog(`Resolved relative workspaceDir: ${input.workspaceDir} → ${workspaceDir}`);
|
|
53
57
|
}
|
|
58
|
+
await debugLog(`Using workspaceDir: ${workspaceDir}`);
|
|
59
|
+
// Validate: content mode is mutually exclusive with branch mode
|
|
54
60
|
if (input.content && input.branchName) {
|
|
55
61
|
throw new Error('Cannot provide both content and branchName. ' +
|
|
56
62
|
'Choose one: either provide the diff directly (content) or let us collect it (branchName).');
|
|
57
63
|
}
|
|
58
|
-
// If
|
|
59
|
-
if (input.
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
// If content provided, use that path (legacy mode)
|
|
65
|
+
if (input.content) {
|
|
66
|
+
// Continue with existing content-based flow
|
|
67
|
+
await debugLog(`Using provided content (${input.content.length} chars)`);
|
|
62
68
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
else {
|
|
70
|
+
// Branch mode: auto-detect current branch if not provided
|
|
71
|
+
let branchName = input.branchName;
|
|
72
|
+
if (!branchName) {
|
|
73
|
+
// Auto-detect current branch
|
|
74
|
+
try {
|
|
75
|
+
const { execSync } = await import('node:child_process');
|
|
76
|
+
branchName = execSync('git rev-parse --abbrev-ref HEAD', {
|
|
77
|
+
cwd: workspaceDir,
|
|
78
|
+
encoding: 'utf8',
|
|
79
|
+
stdio: ['ignore', 'pipe', 'ignore']
|
|
80
|
+
}).trim();
|
|
81
|
+
await debugLog(`Auto-detected current branch: ${branchName}`);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
throw new Error(`Could not auto-detect current branch. ` +
|
|
85
|
+
`Make sure you're in a git repository. ` +
|
|
86
|
+
`Error: ${error.message}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Store detected/provided branchName for later use
|
|
90
|
+
input.branchName = branchName;
|
|
69
91
|
}
|
|
70
92
|
// Collect diff if branchName is provided
|
|
71
93
|
let content = input.content;
|