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.
@@ -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
- 🎯 RECOMMENDED USAGE (Agent-to-Agent):
33
+ 🚀 ZERO-CONFIG MODE - Just Call It!
34
34
 
35
- Pass the branch name and let us handle the diff collection:
35
+ // Review current branch in current workspace
36
+ await perform_code_review({});
36
37
 
37
- await perform_code_review({
38
- branchName: "feature/new-auth", // ⭐ RECOMMENDED: We collect the diff
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
- If you already have the diff, you can still pass it directly:
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
- 💡 KEY FEATURES:
62
- - Smart Detection: Automatically reviews committed OR uncommitted changes
63
- - Code-First: Reviews what code ACTUALLY does, not what docs say
64
- - Codebase-Aware: Auto-finds .cursor/rules, tsconfig, eslint config
65
- - Expert Prompts: GPT-5 Codex with project-specific context
66
- - Smart Validation: Every finding includes file, line, and code quote
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
- 📝 HOW IT HANDLES CHANGES:
69
- 1. First tries: Committed changes between branches
70
- 2. If none found and you're ON that branch: Reviews uncommitted changes
71
- 3. Works in ANY state: coding, staged, committed, or mixed!
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
- ⚠️ IMPORTANT:
74
- - Provide EITHER content OR branchName (not both)
75
- - workspaceDir is REQUIRED when using branchName
76
- - Reviews focus on actual code behavior over documentation
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
- 📋 ALL PARAMETERS:
79
- - branchName: Branch to review (RECOMMENDED - we handle git diff)
80
- - baseBranch: Compare against this (default: main/master)
81
- - content: Pre-collected diff/code (alternative to branchName)
82
- - contentType: "diff" or "code" (when using content)
83
- - workspaceDir: Repository root (REQUIRED for branchName, recommended always)
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
- // CONTENT OPTIONS: Provide either content OR branchName
91
- content: z.string().optional().describe('Code or diff content to review. Use this OR branchName (not both).'),
92
- // GIT BRANCH OPTIONS: Alternative to content
93
- branchName: z.string().optional().describe('Branch name to review (we\'ll run git diff). Faster for agent-to-agent! Requires workspaceDir.'),
94
- baseBranch: z.string().optional().describe('Branch to compare against (defaults to main/master). Only used with branchName.'),
95
- // CONTENT TYPE
96
- contentType: z.enum(['diff', 'code']).optional().describe('Type of content: "diff" for git diffs, "code" for static code review'),
97
- // CONTEXT OPTIONS
98
- workspaceDir: z.string().optional().describe('⭐ CRITICAL: Project root directory for finding .cursor/rules and config files. REQUIRED when using branchName!'),
99
- customContext: z.string().optional().describe('Optional: Provide manual context to skip auto-gathering (.cursor/rules, CODE_REVIEW.md, etc)'),
100
- skipContextGathering: z.boolean().optional().describe('Set to true to skip automatic context gathering (only if no context needed)'),
101
- // REVIEW OPTIONS
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
- // Run git diff for committed changes - use execFileSync to prevent command injection
49
- let diff = execFileSync('git', ['diff', `${base}...${branchName}`], {
50
- cwd: workspaceDir,
51
- encoding: 'utf8',
52
- maxBuffer: 10 * 1024 * 1024, // 10MB buffer for large diffs
53
- });
54
- // If no committed changes, check for uncommitted changes on the branch
55
- if (!diff.trim()) {
56
- await debugLog(`No committed changes found, checking for uncommitted changes`);
57
- // Check if we're currently on the target branch
58
- let currentBranch;
59
- try {
60
- currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {
61
- cwd: workspaceDir,
62
- encoding: 'utf8',
63
- stdio: ['ignore', 'pipe', 'ignore']
64
- }).trim();
65
- }
66
- catch {
67
- currentBranch = '';
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
- if (currentBranch === branchName) {
70
- // We're on the target branch - collect uncommitted changes
71
- await debugLog(`On target branch ${branchName}, collecting uncommitted changes`);
72
- // Get uncommitted changes (staged + unstaged)
73
- diff = execFileSync('git', ['diff', base], {
74
- cwd: workspaceDir,
75
- encoding: 'utf8',
76
- maxBuffer: 10 * 1024 * 1024,
77
- });
78
- if (diff.trim()) {
79
- await debugLog(`Found uncommitted changes: ${diff.length} chars`);
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
- `Tried:\n` +
86
- ` 1. Committed changes: git diff ${base}...${branchName}\n` +
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
- // Validate input: require either content OR branchName
50
- if (!input.content && !input.branchName) {
51
- throw new Error('Either content or branchName is required. ' +
52
- 'Provide content (diff/code) directly, or branchName to auto-collect the diff.');
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 branchName is provided, require workspaceDir
59
- if (input.branchName && !input.workspaceDir) {
60
- throw new Error('workspaceDir is required when using branchName. ' +
61
- 'We need to know where to run git commands.');
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
- // Auto-resolve relative paths to absolute paths for workspaceDir
64
- // This prevents common pitfalls where agents pass relative paths
65
- let workspaceDir = input.workspaceDir;
66
- if (workspaceDir && !isAbsolute(workspaceDir)) {
67
- workspaceDir = resolve(process.cwd(), workspaceDir);
68
- await debugLog(`Resolved relative workspaceDir to absolute: ${input.workspaceDir} → ${workspaceDir}`);
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-review-mcp",
3
- "version": "2.10.4",
3
+ "version": "2.10.6",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "build": "tsc",