cc-reviewer 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.
@@ -0,0 +1,360 @@
1
+ /**
2
+ * MCP Tool Implementations for AI Reviewer
3
+ */
4
+ import { z } from 'zod';
5
+ import { runCodexReview } from '../cli/codex.js';
6
+ import { runGeminiReview } from '../cli/gemini.js';
7
+ import { isCliAvailable } from '../cli/check.js';
8
+ import { formatErrorForUser } from '../errors.js';
9
+ // Input schema for feedback tools
10
+ export const FeedbackInputSchema = z.object({
11
+ workingDir: z.string().describe('Working directory for the CLI to operate in'),
12
+ ccOutput: z.string().describe("Claude Code's output to review (findings, plan, analysis)"),
13
+ outputType: z.enum(['plan', 'findings', 'analysis', 'proposal']).describe('Type of output being reviewed'),
14
+ analyzedFiles: z.array(z.string()).optional().describe('File paths that CC analyzed'),
15
+ focusAreas: z.array(z.enum([
16
+ 'security', 'performance', 'architecture', 'correctness',
17
+ 'maintainability', 'scalability', 'testing', 'documentation'
18
+ ])).optional().describe('Areas to focus the review on'),
19
+ customPrompt: z.string().optional().describe('Custom instructions for the reviewer')
20
+ });
21
+ /**
22
+ * Convert tool input to FeedbackRequest
23
+ */
24
+ function toFeedbackRequest(input) {
25
+ return {
26
+ workingDir: input.workingDir,
27
+ ccOutput: input.ccOutput,
28
+ outputType: input.outputType,
29
+ analyzedFiles: input.analyzedFiles,
30
+ focusAreas: input.focusAreas,
31
+ customPrompt: input.customPrompt
32
+ };
33
+ }
34
+ /**
35
+ * Format successful feedback for display
36
+ */
37
+ function formatSuccessResponse(result) {
38
+ if (!result.success) {
39
+ return formatErrorResponse(result);
40
+ }
41
+ return `## ${result.model.charAt(0).toUpperCase() + result.model.slice(1)} Review
42
+
43
+ ${result.feedback}`;
44
+ }
45
+ /**
46
+ * Format error response for display
47
+ */
48
+ function formatErrorResponse(result) {
49
+ if (result.success) {
50
+ return result.feedback;
51
+ }
52
+ let response = formatErrorForUser(result.error);
53
+ if (result.suggestion) {
54
+ response += `\n\nšŸ’” Suggestion: ${result.suggestion}`;
55
+ }
56
+ return response;
57
+ }
58
+ /**
59
+ * Codex feedback tool handler
60
+ */
61
+ export async function handleCodexFeedback(input) {
62
+ // Check if Codex CLI is available
63
+ const available = await isCliAvailable('codex');
64
+ if (!available) {
65
+ return {
66
+ content: [{
67
+ type: 'text',
68
+ text: 'āŒ Codex CLI not found.\n\nInstall with: npm install -g @openai/codex\n\nAlternative: Use gemini_feedback instead'
69
+ }]
70
+ };
71
+ }
72
+ const request = toFeedbackRequest(input);
73
+ const result = await runCodexReview(request);
74
+ return {
75
+ content: [{
76
+ type: 'text',
77
+ text: formatSuccessResponse(result)
78
+ }]
79
+ };
80
+ }
81
+ /**
82
+ * Gemini feedback tool handler
83
+ */
84
+ export async function handleGeminiFeedback(input) {
85
+ // Check if Gemini CLI is available
86
+ const available = await isCliAvailable('gemini');
87
+ if (!available) {
88
+ return {
89
+ content: [{
90
+ type: 'text',
91
+ text: 'āŒ Gemini CLI not found.\n\nInstall with: npm install -g @google/generative-ai-cli\n\nAlternative: Use codex_feedback instead'
92
+ }]
93
+ };
94
+ }
95
+ const request = toFeedbackRequest(input);
96
+ const result = await runGeminiReview(request);
97
+ return {
98
+ content: [{
99
+ type: 'text',
100
+ text: formatSuccessResponse(result)
101
+ }]
102
+ };
103
+ }
104
+ /**
105
+ * Multi-model feedback tool handler (parallel execution)
106
+ */
107
+ export async function handleMultiFeedback(input) {
108
+ const request = toFeedbackRequest(input);
109
+ // Check availability of both CLIs
110
+ const [codexAvailable, geminiAvailable] = await Promise.all([
111
+ isCliAvailable('codex'),
112
+ isCliAvailable('gemini')
113
+ ]);
114
+ if (!codexAvailable && !geminiAvailable) {
115
+ return {
116
+ content: [{
117
+ type: 'text',
118
+ text: `āŒ No AI CLIs found.
119
+
120
+ Install at least one:
121
+ - Codex: npm install -g @openai/codex
122
+ - Gemini: npm install -g @google/generative-ai-cli`
123
+ }]
124
+ };
125
+ }
126
+ // Run available CLIs in parallel
127
+ const promises = [];
128
+ const cliNames = [];
129
+ if (codexAvailable) {
130
+ promises.push(runCodexReview(request));
131
+ cliNames.push('codex');
132
+ }
133
+ if (geminiAvailable) {
134
+ promises.push(runGeminiReview(request));
135
+ cliNames.push('gemini');
136
+ }
137
+ const results = await Promise.allSettled(promises);
138
+ // Process results
139
+ const multiResult = {
140
+ successful: [],
141
+ failed: [],
142
+ partialSuccess: false,
143
+ allFailed: false
144
+ };
145
+ results.forEach((result, index) => {
146
+ if (result.status === 'fulfilled') {
147
+ const feedbackResult = result.value;
148
+ if (feedbackResult.success) {
149
+ multiResult.successful.push({
150
+ model: feedbackResult.model,
151
+ feedback: feedbackResult.feedback
152
+ });
153
+ }
154
+ else {
155
+ multiResult.failed.push({
156
+ model: feedbackResult.model,
157
+ error: feedbackResult.error
158
+ });
159
+ }
160
+ }
161
+ else {
162
+ // Promise rejected (unexpected)
163
+ multiResult.failed.push({
164
+ model: cliNames[index],
165
+ error: {
166
+ type: 'cli_error',
167
+ cli: cliNames[index],
168
+ exitCode: -1,
169
+ stderr: result.reason?.message || 'Unknown error'
170
+ }
171
+ });
172
+ }
173
+ });
174
+ multiResult.partialSuccess = multiResult.successful.length > 0 && multiResult.failed.length > 0;
175
+ multiResult.allFailed = multiResult.successful.length === 0;
176
+ // Format response
177
+ return {
178
+ content: [{
179
+ type: 'text',
180
+ text: formatMultiResponse(multiResult, codexAvailable, geminiAvailable)
181
+ }]
182
+ };
183
+ }
184
+ /**
185
+ * Format multi-model response
186
+ */
187
+ function formatMultiResponse(result, codexAvailable, geminiAvailable) {
188
+ const parts = [];
189
+ // Header with status
190
+ if (result.allFailed) {
191
+ parts.push('## Multi-Model Review - All Failed āŒ\n');
192
+ }
193
+ else if (result.partialSuccess) {
194
+ parts.push('## Multi-Model Review - Partial Success āš ļø\n');
195
+ }
196
+ else {
197
+ parts.push('## Multi-Model Review āœ“\n');
198
+ }
199
+ // Show availability status
200
+ const statusLines = [];
201
+ if (!codexAvailable)
202
+ statusLines.push('- Codex: Not available');
203
+ if (!geminiAvailable)
204
+ statusLines.push('- Gemini: Not available');
205
+ if (statusLines.length > 0) {
206
+ parts.push('**CLI Status:**');
207
+ parts.push(statusLines.join('\n'));
208
+ parts.push('');
209
+ }
210
+ // Successful reviews
211
+ if (result.successful.length > 0) {
212
+ for (const success of result.successful) {
213
+ parts.push(`### ${success.model.charAt(0).toUpperCase() + success.model.slice(1)} Review\n`);
214
+ parts.push(success.feedback);
215
+ parts.push('');
216
+ }
217
+ }
218
+ // Failed reviews
219
+ if (result.failed.length > 0) {
220
+ parts.push('### Failures\n');
221
+ for (const failure of result.failed) {
222
+ parts.push(`**${failure.model}:** ${formatErrorForUser(failure.error)}\n`);
223
+ }
224
+ }
225
+ // Synthesis instruction (only if we have successful results)
226
+ if (result.successful.length > 1) {
227
+ parts.push(`---
228
+
229
+ **Note:** Both models provided feedback above. Please synthesize their perspectives:
230
+ - Mark agreements with āœ“āœ“
231
+ - Resolve conflicts with your recommendation
232
+ - Highlight unique insights from each model`);
233
+ }
234
+ return parts.join('\n');
235
+ }
236
+ /**
237
+ * Tool definitions for MCP registration
238
+ */
239
+ export const TOOL_DEFINITIONS = {
240
+ codex_feedback: {
241
+ name: 'codex_feedback',
242
+ description: "Get Codex's review of Claude Code's work. Codex focuses on correctness, edge cases, and performance.",
243
+ inputSchema: {
244
+ type: 'object',
245
+ properties: {
246
+ workingDir: {
247
+ type: 'string',
248
+ description: 'Working directory for the CLI to operate in'
249
+ },
250
+ ccOutput: {
251
+ type: 'string',
252
+ description: "Claude Code's output to review (findings, plan, analysis)"
253
+ },
254
+ outputType: {
255
+ type: 'string',
256
+ enum: ['plan', 'findings', 'analysis', 'proposal'],
257
+ description: 'Type of output being reviewed'
258
+ },
259
+ analyzedFiles: {
260
+ type: 'array',
261
+ items: { type: 'string' },
262
+ description: 'File paths that CC analyzed'
263
+ },
264
+ focusAreas: {
265
+ type: 'array',
266
+ items: {
267
+ type: 'string',
268
+ enum: ['security', 'performance', 'architecture', 'correctness', 'maintainability', 'scalability', 'testing', 'documentation']
269
+ },
270
+ description: 'Areas to focus the review on'
271
+ },
272
+ customPrompt: {
273
+ type: 'string',
274
+ description: 'Custom instructions for the reviewer'
275
+ }
276
+ },
277
+ required: ['workingDir', 'ccOutput', 'outputType']
278
+ }
279
+ },
280
+ gemini_feedback: {
281
+ name: 'gemini_feedback',
282
+ description: "Get Gemini's review of Claude Code's work. Gemini focuses on design patterns, scalability, and tech debt.",
283
+ inputSchema: {
284
+ type: 'object',
285
+ properties: {
286
+ workingDir: {
287
+ type: 'string',
288
+ description: 'Working directory for the CLI to operate in'
289
+ },
290
+ ccOutput: {
291
+ type: 'string',
292
+ description: "Claude Code's output to review (findings, plan, analysis)"
293
+ },
294
+ outputType: {
295
+ type: 'string',
296
+ enum: ['plan', 'findings', 'analysis', 'proposal'],
297
+ description: 'Type of output being reviewed'
298
+ },
299
+ analyzedFiles: {
300
+ type: 'array',
301
+ items: { type: 'string' },
302
+ description: 'File paths that CC analyzed'
303
+ },
304
+ focusAreas: {
305
+ type: 'array',
306
+ items: {
307
+ type: 'string',
308
+ enum: ['security', 'performance', 'architecture', 'correctness', 'maintainability', 'scalability', 'testing', 'documentation']
309
+ },
310
+ description: 'Areas to focus the review on'
311
+ },
312
+ customPrompt: {
313
+ type: 'string',
314
+ description: 'Custom instructions for the reviewer'
315
+ }
316
+ },
317
+ required: ['workingDir', 'ccOutput', 'outputType']
318
+ }
319
+ },
320
+ multi_feedback: {
321
+ name: 'multi_feedback',
322
+ description: "Get parallel reviews from all available AI CLIs (Codex and Gemini). Returns combined feedback for synthesis.",
323
+ inputSchema: {
324
+ type: 'object',
325
+ properties: {
326
+ workingDir: {
327
+ type: 'string',
328
+ description: 'Working directory for the CLI to operate in'
329
+ },
330
+ ccOutput: {
331
+ type: 'string',
332
+ description: "Claude Code's output to review (findings, plan, analysis)"
333
+ },
334
+ outputType: {
335
+ type: 'string',
336
+ enum: ['plan', 'findings', 'analysis', 'proposal'],
337
+ description: 'Type of output being reviewed'
338
+ },
339
+ analyzedFiles: {
340
+ type: 'array',
341
+ items: { type: 'string' },
342
+ description: 'File paths that CC analyzed'
343
+ },
344
+ focusAreas: {
345
+ type: 'array',
346
+ items: {
347
+ type: 'string',
348
+ enum: ['security', 'performance', 'architecture', 'correctness', 'maintainability', 'scalability', 'testing', 'documentation']
349
+ },
350
+ description: 'Areas to focus the review on'
351
+ },
352
+ customPrompt: {
353
+ type: 'string',
354
+ description: 'Custom instructions for the reviewer'
355
+ }
356
+ },
357
+ required: ['workingDir', 'ccOutput', 'outputType']
358
+ }
359
+ }
360
+ };
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Types for AI Reviewer MCP Server
3
+ */
4
+ export type OutputType = 'plan' | 'findings' | 'analysis' | 'proposal';
5
+ export type FocusArea = 'security' | 'performance' | 'architecture' | 'correctness' | 'maintainability' | 'scalability' | 'testing' | 'documentation';
6
+ export type CliType = 'codex' | 'gemini';
7
+ export interface FeedbackRequest {
8
+ workingDir: string;
9
+ ccOutput: string;
10
+ outputType: OutputType;
11
+ analyzedFiles?: string[];
12
+ focusAreas?: FocusArea[];
13
+ customPrompt?: string;
14
+ }
15
+ export interface FeedbackSuccess {
16
+ success: true;
17
+ feedback: string;
18
+ model: CliType;
19
+ }
20
+ export interface FeedbackFailure {
21
+ success: false;
22
+ error: FeedbackError;
23
+ suggestion?: string;
24
+ model: CliType;
25
+ }
26
+ export type FeedbackResult = FeedbackSuccess | FeedbackFailure;
27
+ export type FeedbackError = {
28
+ type: 'cli_not_found';
29
+ cli: CliType;
30
+ installCmd: string;
31
+ } | {
32
+ type: 'timeout';
33
+ cli: CliType;
34
+ durationMs: number;
35
+ } | {
36
+ type: 'rate_limit';
37
+ cli: CliType;
38
+ retryAfterMs?: number;
39
+ } | {
40
+ type: 'auth_error';
41
+ cli: CliType;
42
+ message: string;
43
+ } | {
44
+ type: 'invalid_response';
45
+ cli: CliType;
46
+ rawOutput: string;
47
+ } | {
48
+ type: 'cli_error';
49
+ cli: CliType;
50
+ exitCode: number;
51
+ stderr: string;
52
+ };
53
+ export interface MultiFeedbackResult {
54
+ successful: Array<{
55
+ model: CliType;
56
+ feedback: string;
57
+ }>;
58
+ failed: Array<{
59
+ model: CliType;
60
+ error: FeedbackError;
61
+ }>;
62
+ partialSuccess: boolean;
63
+ allFailed: boolean;
64
+ }
65
+ export interface CliStatus {
66
+ codex: boolean;
67
+ gemini: boolean;
68
+ }
69
+ export interface StructuredFeedback {
70
+ agreements: Array<{
71
+ finding: string;
72
+ reason: string;
73
+ }>;
74
+ disagreements: Array<{
75
+ finding: string;
76
+ reason: string;
77
+ correction: string;
78
+ }>;
79
+ additions: Array<{
80
+ finding: string;
81
+ location: string;
82
+ impact: string;
83
+ }>;
84
+ alternatives: Array<{
85
+ topic: string;
86
+ alternative: string;
87
+ tradeoffs: string;
88
+ }>;
89
+ riskAssessment: {
90
+ level: 'Low' | 'Medium' | 'High';
91
+ reason: string;
92
+ };
93
+ }
94
+ export interface ReviewerPersona {
95
+ name: string;
96
+ focus: string;
97
+ style: string;
98
+ }
99
+ export declare const REVIEWER_PERSONAS: Record<CliType, ReviewerPersona>;
100
+ export declare const FOCUS_AREA_DESCRIPTIONS: Record<FocusArea, string>;
package/dist/types.js ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Types for AI Reviewer MCP Server
3
+ */
4
+ export const REVIEWER_PERSONAS = {
5
+ codex: {
6
+ name: 'Codex',
7
+ focus: 'correctness, edge cases, performance',
8
+ style: 'Apply pragmatic skepticism - verify before agreeing.'
9
+ },
10
+ gemini: {
11
+ name: 'Gemini',
12
+ focus: 'design patterns, scalability, tech debt',
13
+ style: 'Think holistically - consider broader context.'
14
+ }
15
+ };
16
+ // Focus area descriptions
17
+ export const FOCUS_AREA_DESCRIPTIONS = {
18
+ security: 'Vulnerabilities, auth, input validation',
19
+ performance: 'Speed, memory, efficiency',
20
+ architecture: 'Design patterns, structure, coupling',
21
+ correctness: 'Logic errors, edge cases, bugs',
22
+ maintainability: 'Code clarity, documentation, complexity',
23
+ scalability: 'Load handling, bottlenecks',
24
+ testing: 'Test coverage, test quality',
25
+ documentation: 'Comments, docs, API docs'
26
+ };
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "cc-reviewer",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for Claude Code - Get second-opinion feedback from Codex/Gemini CLIs",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "cc-reviewer": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist/**/*"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "start": "node dist/index.js",
16
+ "dev": "tsc --watch",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "mcp",
21
+ "claude",
22
+ "claude-code",
23
+ "codex",
24
+ "gemini",
25
+ "ai-review",
26
+ "code-review"
27
+ ],
28
+ "author": "SimonRen",
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/SimonRen/cc-reviewer.git"
33
+ },
34
+ "homepage": "https://github.com/SimonRen/cc-reviewer#readme",
35
+ "dependencies": {
36
+ "@modelcontextprotocol/sdk": "^1.0.0",
37
+ "zod": "^3.22.0"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^20.0.0",
41
+ "typescript": "^5.0.0"
42
+ }
43
+ }