cc-reviewer 2.0.0 → 2.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.
package/dist/schema.js CHANGED
@@ -154,7 +154,7 @@ export function getReviewOutputJsonSchema() {
154
154
  return {
155
155
  type: 'object',
156
156
  additionalProperties: false,
157
- required: ['reviewer', 'findings', 'risk_assessment'],
157
+ required: ['reviewer', 'findings', 'agreements', 'disagreements', 'alternatives', 'risk_assessment', 'uncertainty_responses', 'question_answers'],
158
158
  properties: {
159
159
  reviewer: { type: 'string' },
160
160
  findings: {
@@ -1,9 +1,8 @@
1
1
  /**
2
- * MCP Tool Implementations
2
+ * MCP Tool Implementations — Review Tools
3
3
  *
4
- * Provides two levels of review:
5
- * 1. Single model review (codex_review, gemini_review)
6
- * 2. Multi-model parallel review (multi_review)
4
+ * Returns raw reviewer text to CC. No JSON parsing, no reformatting.
5
+ * CC handles interpretation and synthesis.
7
6
  */
8
7
  import { z } from 'zod';
9
8
  export declare const ReviewInputSchema: z.ZodObject<{
@@ -20,8 +19,8 @@ export declare const ReviewInputSchema: z.ZodObject<{
20
19
  ccOutput: string;
21
20
  outputType: "findings" | "analysis" | "plan" | "proposal";
22
21
  focusAreas?: ("performance" | "security" | "testing" | "architecture" | "correctness" | "maintainability" | "scalability" | "documentation")[] | undefined;
23
- customPrompt?: string | undefined;
24
22
  analyzedFiles?: string[] | undefined;
23
+ customPrompt?: string | undefined;
25
24
  reasoningEffort?: "high" | "xhigh" | undefined;
26
25
  serviceTier?: "default" | "fast" | "flex" | undefined;
27
26
  }, {
@@ -29,8 +28,8 @@ export declare const ReviewInputSchema: z.ZodObject<{
29
28
  ccOutput: string;
30
29
  outputType: "findings" | "analysis" | "plan" | "proposal";
31
30
  focusAreas?: ("performance" | "security" | "testing" | "architecture" | "correctness" | "maintainability" | "scalability" | "documentation")[] | undefined;
32
- customPrompt?: string | undefined;
33
31
  analyzedFiles?: string[] | undefined;
32
+ customPrompt?: string | undefined;
34
33
  reasoningEffort?: "high" | "xhigh" | undefined;
35
34
  serviceTier?: "default" | "fast" | "flex" | undefined;
36
35
  }>;
@@ -1,9 +1,8 @@
1
1
  /**
2
- * MCP Tool Implementations
2
+ * MCP Tool Implementations — Review Tools
3
3
  *
4
- * Provides two levels of review:
5
- * 1. Single model review (codex_review, gemini_review)
6
- * 2. Multi-model parallel review (multi_review)
4
+ * Returns raw reviewer text to CC. No JSON parsing, no reformatting.
5
+ * CC handles interpretation and synthesis.
7
6
  */
8
7
  import { z } from 'zod';
9
8
  import { getAdapter, getAvailableAdapters, } from '../adapters/index.js';
@@ -24,7 +23,7 @@ export const ReviewInputSchema = z.object({
24
23
  serviceTier: z.enum(['default', 'fast', 'flex']).optional().describe('Codex service tier (default: default, fast = priority processing, flex = cheaper/slower)')
25
24
  });
26
25
  // =============================================================================
27
- // HELPER FUNCTIONS
26
+ // HELPERS
28
27
  // =============================================================================
29
28
  function toReviewRequest(input) {
30
29
  return {
@@ -38,260 +37,70 @@ function toReviewRequest(input) {
38
37
  serviceTier: input.serviceTier,
39
38
  };
40
39
  }
41
- function formatSingleReviewResponse(result, modelName) {
40
+ function formatResult(result, modelName) {
42
41
  if (!result.success) {
43
- return formatErrorResponse(result.error, result.suggestion);
44
- }
45
- const output = result.output;
46
- const lines = [];
47
- lines.push(`## ${modelName} Review\n`);
48
- lines.push(`**Execution Time:** ${(result.executionTimeMs / 1000).toFixed(1)}s\n`);
49
- // Risk Assessment
50
- const riskEmoji = {
51
- critical: '🔴',
52
- high: '🟠',
53
- medium: '🟡',
54
- low: '🟢',
55
- minimal: '✅',
56
- };
57
- lines.push(`### Risk Assessment ${riskEmoji[output.risk_assessment.overall_level]}`);
58
- lines.push(`**${output.risk_assessment.overall_level.toUpperCase()}** (Score: ${output.risk_assessment.score}/100)`);
59
- lines.push(`${output.risk_assessment.summary}\n`);
60
- // Findings
61
- if (output.findings.length > 0) {
62
- lines.push(`### New Findings (${output.findings.length})\n`);
63
- for (const finding of output.findings) {
64
- const severityEmoji = {
65
- critical: '🔴', high: '🟠', medium: '🟡', low: '🟢', info: 'ℹ️'
66
- };
67
- const confidence = Math.round(finding.confidence * 100);
68
- lines.push(`${severityEmoji[finding.severity]} **${finding.title}** [${confidence}% confidence]`);
69
- if (finding.location) {
70
- const loc = finding.location.line_start
71
- ? `${finding.location.file}:${finding.location.line_start}`
72
- : finding.location.file;
73
- lines.push(` 📍 ${loc}`);
74
- }
75
- lines.push(` ${finding.description}`);
76
- if (finding.suggestion) {
77
- lines.push(` 💡 ${finding.suggestion}`);
78
- }
79
- lines.push('');
80
- }
81
- }
82
- // Uncertainty Responses
83
- if (output.uncertainty_responses && output.uncertainty_responses.length > 0) {
84
- lines.push(`### Uncertainty Responses (${output.uncertainty_responses.length})\n`);
85
- for (const ur of output.uncertainty_responses) {
86
- const icon = ur.verified ? '✓' : '✗';
87
- lines.push(`${icon} **Uncertainty #${ur.uncertainty_index}**: ${ur.finding}`);
88
- if (ur.recommendation) {
89
- lines.push(` → ${ur.recommendation}`);
90
- }
91
- lines.push('');
92
- }
93
- }
94
- // Question Answers
95
- if (output.question_answers && output.question_answers.length > 0) {
96
- lines.push(`### Question Answers (${output.question_answers.length})\n`);
97
- for (const qa of output.question_answers) {
98
- const confidence = qa.confidence != null ? ` [${Math.round(qa.confidence * 100)}%]` : '';
99
- lines.push(`**Q${qa.question_index}**${confidence}: ${qa.answer}`);
100
- lines.push('');
101
- }
102
- }
103
- // Agreements
104
- if (output.agreements.length > 0) {
105
- lines.push(`### Agreements (${output.agreements.length})\n`);
106
- for (const agreement of output.agreements) {
107
- const confidence = Math.round(agreement.confidence * 100);
108
- lines.push(`✓ **${agreement.original_claim}** [${confidence}%]`);
109
- if (agreement.supporting_evidence) {
110
- lines.push(` ${agreement.supporting_evidence}`);
111
- }
112
- lines.push('');
113
- }
114
- }
115
- // Disagreements
116
- if (output.disagreements.length > 0) {
117
- lines.push(`### Disagreements (${output.disagreements.length})\n`);
118
- for (const disagreement of output.disagreements) {
119
- const confidence = Math.round(disagreement.confidence * 100);
120
- lines.push(`✗ **${disagreement.original_claim}** [${disagreement.issue}] [${confidence}%]`);
121
- lines.push(` ${disagreement.reason}`);
122
- if (disagreement.correction) {
123
- lines.push(` → Correction: ${disagreement.correction}`);
124
- }
125
- lines.push('');
126
- }
127
- }
128
- // Alternatives
129
- if (output.alternatives.length > 0) {
130
- lines.push(`### Alternatives (${output.alternatives.length})\n`);
131
- for (const alt of output.alternatives) {
132
- lines.push(`**${alt.topic}**`);
133
- lines.push(` Current: ${alt.current_approach}`);
134
- lines.push(` Alternative: ${alt.alternative}`);
135
- lines.push(` Recommendation: ${alt.recommendation}`);
136
- lines.push('');
137
- }
138
- }
139
- return lines.join('\n');
140
- }
141
- function formatErrorResponse(error, suggestion) {
142
- const emoji = {
143
- cli_not_found: '❌',
144
- timeout: '⏱️',
145
- rate_limit: '🚫',
146
- auth_error: '🔐',
147
- parse_error: '⚠️',
148
- cli_error: '❌',
149
- };
150
- let response = `${emoji[error.type] || '❌'} **${error.type}**: ${error.message}`;
151
- if (suggestion) {
152
- response += `\n\n💡 ${suggestion}`;
42
+ const emoji = {
43
+ cli_not_found: '❌', timeout: '⏱️', rate_limit: '🚫',
44
+ auth_error: '🔐', cli_error: '❌',
45
+ };
46
+ let msg = `${emoji[result.error.type] || '❌'} **${result.error.type}**: ${result.error.message}`;
47
+ if (result.suggestion)
48
+ msg += `\n\n💡 ${result.suggestion}`;
49
+ return msg;
153
50
  }
154
- return response;
51
+ return `## ${modelName} Review\n\n**Execution Time:** ${(result.executionTimeMs / 1000).toFixed(1)}s\n\n${result.output}`;
155
52
  }
156
53
  // =============================================================================
157
54
  // SINGLE MODEL HANDLERS
158
55
  // =============================================================================
159
56
  export async function handleCodexReview(input) {
160
57
  const adapter = getAdapter('codex');
161
- if (!adapter) {
162
- return {
163
- content: [{
164
- type: 'text',
165
- text: '❌ Codex adapter not registered'
166
- }]
167
- };
168
- }
58
+ if (!adapter)
59
+ return { content: [{ type: 'text', text: '❌ Codex adapter not registered' }] };
169
60
  const available = await adapter.isAvailable();
170
- if (!available) {
171
- return {
172
- content: [{
173
- type: 'text',
174
- text: '❌ Codex CLI not found.\n\nInstall with: npm install -g @openai/codex\n\nAlternative: Use gemini_review instead'
175
- }]
176
- };
177
- }
178
- const request = toReviewRequest(input);
179
- const result = await adapter.runReview(request);
180
- return {
181
- content: [{
182
- type: 'text',
183
- text: formatSingleReviewResponse(result, 'Codex')
184
- }]
185
- };
61
+ if (!available)
62
+ return { content: [{ type: 'text', text: '❌ Codex CLI not found.\n\nInstall with: npm install -g @openai/codex\n\nAlternative: Use gemini_review instead' }] };
63
+ const result = await adapter.runReview(toReviewRequest(input));
64
+ return { content: [{ type: 'text', text: formatResult(result, 'Codex') }] };
186
65
  }
187
66
  export async function handleGeminiReview(input) {
188
67
  const adapter = getAdapter('gemini');
189
- if (!adapter) {
190
- return {
191
- content: [{
192
- type: 'text',
193
- text: '❌ Gemini adapter not registered'
194
- }]
195
- };
196
- }
68
+ if (!adapter)
69
+ return { content: [{ type: 'text', text: '❌ Gemini adapter not registered' }] };
197
70
  const available = await adapter.isAvailable();
198
- if (!available) {
199
- return {
200
- content: [{
201
- type: 'text',
202
- text: '❌ Gemini CLI not found.\n\nInstall with: npm install -g @google/gemini-cli\n\nAlternative: Use codex_review instead'
203
- }]
204
- };
205
- }
206
- const request = toReviewRequest(input);
207
- const result = await adapter.runReview(request);
208
- return {
209
- content: [{
210
- type: 'text',
211
- text: formatSingleReviewResponse(result, 'Gemini')
212
- }]
213
- };
71
+ if (!available)
72
+ return { content: [{ type: 'text', text: '❌ Gemini CLI not found.\n\nInstall with: npm install -g @google/gemini-cli\n\nAlternative: Use codex_review instead' }] };
73
+ const result = await adapter.runReview(toReviewRequest(input));
74
+ return { content: [{ type: 'text', text: formatResult(result, 'Gemini') }] };
214
75
  }
215
76
  // =============================================================================
216
77
  // MULTI-MODEL HANDLER
217
78
  // =============================================================================
218
79
  export async function handleMultiReview(input) {
219
80
  const request = toReviewRequest(input);
220
- // Get all available adapters
221
81
  const availableAdapters = await getAvailableAdapters();
222
82
  if (availableAdapters.length === 0) {
223
- return {
224
- content: [{
225
- type: 'text',
226
- text: `❌ No AI CLIs found.
227
-
228
- Install at least one:
229
- - Codex: npm install -g @openai/codex
230
- - Gemini: npm install -g @google/gemini-cli`
231
- }]
232
- };
83
+ return { content: [{ type: 'text', text: '❌ No AI CLIs found.\n\nInstall at least one:\n - Codex: npm install -g @openai/codex\n - Gemini: npm install -g @google/gemini-cli' }] };
233
84
  }
234
- // Run all available adapters in parallel
235
- const promises = availableAdapters.map(async (adapter) => {
236
- const adapterRequest = { ...request };
237
- const result = await adapter.runReview(adapterRequest);
85
+ const results = await Promise.all(availableAdapters.map(async (adapter) => {
86
+ const result = await adapter.runReview({ ...request });
238
87
  return { adapter, result };
239
- });
240
- const results = await Promise.all(promises);
241
- // Collect successful and failed results
242
- const successful = [];
243
- const failed = [];
244
- for (const { adapter, result } of results) {
245
- if (result.success) {
246
- successful.push({ model: adapter.id, output: result.output });
247
- }
248
- else {
249
- failed.push({ model: adapter.id, error: result.error.message });
250
- }
251
- }
252
- // Build response
88
+ }));
253
89
  const lines = [];
254
- // Header
255
- if (failed.length === results.length) {
90
+ const allFailed = results.every(r => !r.result.success);
91
+ const someFailed = results.some(r => !r.result.success);
92
+ if (allFailed)
256
93
  lines.push('## Multi-Model Review ❌ All Failed\n');
257
- }
258
- else if (failed.length > 0) {
94
+ else if (someFailed)
259
95
  lines.push('## Multi-Model Review ⚠️ Partial Success\n');
260
- }
261
- else {
96
+ else
262
97
  lines.push('## Multi-Model Review ✓\n');
263
- }
264
- lines.push(`**Models:** ${availableAdapters.map(a => a.id).join(', ')}`);
265
- lines.push('');
266
- // Successful reviews
267
- for (const { model, output } of successful) {
268
- lines.push(`### ${model.charAt(0).toUpperCase() + model.slice(1)} Review\n`);
269
- lines.push(formatSingleReviewResponse({ success: true, output, executionTimeMs: 0 }, model));
270
- lines.push('');
271
- }
272
- // Failed reviews
273
- if (failed.length > 0) {
274
- lines.push('### Failures\n');
275
- for (const { model, error } of failed) {
276
- lines.push(`**${model}:** ${error}`);
277
- }
98
+ lines.push(`**Models:** ${availableAdapters.map(a => a.id).join(', ')}\n`);
99
+ for (const { adapter, result } of results) {
100
+ lines.push(formatResult(result, adapter.getCapabilities().name));
278
101
  lines.push('');
279
102
  }
280
- // Synthesis instructions (only if multiple successful)
281
- if (successful.length > 1) {
282
- lines.push(`---
283
-
284
- **Synthesis Instructions:**
285
- - ✓✓ Mark agreements where both models concur
286
- - Resolve conflicts with your own judgment
287
- - Note unique insights from each model`);
288
- }
289
- return {
290
- content: [{
291
- type: 'text',
292
- text: lines.join('\n')
293
- }]
294
- };
103
+ return { content: [{ type: 'text', text: lines.join('\n') }] };
295
104
  }
296
105
  // =============================================================================
297
106
  // TOOL DEFINITIONS
@@ -303,46 +112,14 @@ export const TOOL_DEFINITIONS = {
303
112
  inputSchema: {
304
113
  type: 'object',
305
114
  properties: {
306
- workingDir: {
307
- type: 'string',
308
- description: 'Working directory for the CLI to operate in'
309
- },
310
- ccOutput: {
311
- type: 'string',
312
- description: "Claude Code's output to review (findings, plan, analysis)"
313
- },
314
- outputType: {
315
- type: 'string',
316
- enum: ['plan', 'findings', 'analysis', 'proposal'],
317
- description: 'Type of output being reviewed'
318
- },
319
- analyzedFiles: {
320
- type: 'array',
321
- items: { type: 'string' },
322
- description: 'File paths that CC analyzed'
323
- },
324
- focusAreas: {
325
- type: 'array',
326
- items: {
327
- type: 'string',
328
- enum: ['security', 'performance', 'architecture', 'correctness', 'maintainability', 'scalability', 'testing', 'documentation']
329
- },
330
- description: 'Areas to focus the review on'
331
- },
332
- customPrompt: {
333
- type: 'string',
334
- description: 'Custom instructions for the reviewer'
335
- },
336
- reasoningEffort: {
337
- type: 'string',
338
- enum: ['high', 'xhigh'],
339
- description: 'Codex reasoning effort (default: high, use xhigh for deeper analysis)'
340
- },
341
- serviceTier: {
342
- type: 'string',
343
- enum: ['default', 'fast', 'flex'],
344
- description: 'Codex service tier (fast = priority processing, flex = cheaper/slower)'
345
- }
115
+ workingDir: { type: 'string', description: 'Working directory for the CLI to operate in' },
116
+ ccOutput: { type: 'string', description: "Claude Code's output to review (findings, plan, analysis)" },
117
+ outputType: { type: 'string', enum: ['plan', 'findings', 'analysis', 'proposal'], description: 'Type of output being reviewed' },
118
+ analyzedFiles: { type: 'array', items: { type: 'string' }, description: 'File paths that CC analyzed' },
119
+ focusAreas: { type: 'array', items: { type: 'string', enum: ['security', 'performance', 'architecture', 'correctness', 'maintainability', 'scalability', 'testing', 'documentation'] }, description: 'Areas to focus the review on' },
120
+ customPrompt: { type: 'string', description: 'Custom instructions for the reviewer' },
121
+ reasoningEffort: { type: 'string', enum: ['high', 'xhigh'], description: 'Codex reasoning effort (default: high, use xhigh for deeper analysis)' },
122
+ serviceTier: { type: 'string', enum: ['default', 'fast', 'flex'], description: 'Codex service tier (fast = priority processing, flex = cheaper/slower)' }
346
123
  },
347
124
  required: ['workingDir', 'ccOutput', 'outputType']
348
125
  }
@@ -353,36 +130,12 @@ export const TOOL_DEFINITIONS = {
353
130
  inputSchema: {
354
131
  type: 'object',
355
132
  properties: {
356
- workingDir: {
357
- type: 'string',
358
- description: 'Working directory for the CLI to operate in'
359
- },
360
- ccOutput: {
361
- type: 'string',
362
- description: "Claude Code's output to review (findings, plan, analysis)"
363
- },
364
- outputType: {
365
- type: 'string',
366
- enum: ['plan', 'findings', 'analysis', 'proposal'],
367
- description: 'Type of output being reviewed'
368
- },
369
- analyzedFiles: {
370
- type: 'array',
371
- items: { type: 'string' },
372
- description: 'File paths that CC analyzed'
373
- },
374
- focusAreas: {
375
- type: 'array',
376
- items: {
377
- type: 'string',
378
- enum: ['security', 'performance', 'architecture', 'correctness', 'maintainability', 'scalability', 'testing', 'documentation']
379
- },
380
- description: 'Areas to focus the review on'
381
- },
382
- customPrompt: {
383
- type: 'string',
384
- description: 'Custom instructions for the reviewer'
385
- }
133
+ workingDir: { type: 'string', description: 'Working directory for the CLI to operate in' },
134
+ ccOutput: { type: 'string', description: "Claude Code's output to review (findings, plan, analysis)" },
135
+ outputType: { type: 'string', enum: ['plan', 'findings', 'analysis', 'proposal'], description: 'Type of output being reviewed' },
136
+ analyzedFiles: { type: 'array', items: { type: 'string' }, description: 'File paths that CC analyzed' },
137
+ focusAreas: { type: 'array', items: { type: 'string', enum: ['security', 'performance', 'architecture', 'correctness', 'maintainability', 'scalability', 'testing', 'documentation'] }, description: 'Areas to focus the review on' },
138
+ customPrompt: { type: 'string', description: 'Custom instructions for the reviewer' },
386
139
  },
387
140
  required: ['workingDir', 'ccOutput', 'outputType']
388
141
  }
@@ -393,41 +146,13 @@ export const TOOL_DEFINITIONS = {
393
146
  inputSchema: {
394
147
  type: 'object',
395
148
  properties: {
396
- workingDir: {
397
- type: 'string',
398
- description: 'Working directory for the CLI to operate in'
399
- },
400
- ccOutput: {
401
- type: 'string',
402
- description: "Claude Code's output to review (findings, plan, analysis)"
403
- },
404
- outputType: {
405
- type: 'string',
406
- enum: ['plan', 'findings', 'analysis', 'proposal'],
407
- description: 'Type of output being reviewed'
408
- },
409
- analyzedFiles: {
410
- type: 'array',
411
- items: { type: 'string' },
412
- description: 'File paths that CC analyzed'
413
- },
414
- focusAreas: {
415
- type: 'array',
416
- items: {
417
- type: 'string',
418
- enum: ['security', 'performance', 'architecture', 'correctness', 'maintainability', 'scalability', 'testing', 'documentation']
419
- },
420
- description: 'Areas to focus the review on'
421
- },
422
- customPrompt: {
423
- type: 'string',
424
- description: 'Custom instructions for the reviewer'
425
- },
426
- serviceTier: {
427
- type: 'string',
428
- enum: ['default', 'fast', 'flex'],
429
- description: 'Codex service tier (fast = priority processing, flex = cheaper/slower). Only applies to Codex.'
430
- }
149
+ workingDir: { type: 'string', description: 'Working directory for the CLI to operate in' },
150
+ ccOutput: { type: 'string', description: "Claude Code's output to review (findings, plan, analysis)" },
151
+ outputType: { type: 'string', enum: ['plan', 'findings', 'analysis', 'proposal'], description: 'Type of output being reviewed' },
152
+ analyzedFiles: { type: 'array', items: { type: 'string' }, description: 'File paths that CC analyzed' },
153
+ focusAreas: { type: 'array', items: { type: 'string', enum: ['security', 'performance', 'architecture', 'correctness', 'maintainability', 'scalability', 'testing', 'documentation'] }, description: 'Areas to focus the review on' },
154
+ customPrompt: { type: 'string', description: 'Custom instructions for the reviewer' },
155
+ serviceTier: { type: 'string', enum: ['default', 'fast', 'flex'], description: 'Codex service tier (fast = priority processing, flex = cheaper/slower). Only applies to Codex.' }
431
156
  },
432
157
  required: ['workingDir', 'ccOutput', 'outputType']
433
158
  }
@@ -6,7 +6,6 @@
6
6
  * 2. ask_gemini - Ask Gemini for help
7
7
  * 3. ask_multi - Ask both in parallel
8
8
  */
9
- import { PeerResult } from '../adapters/index.js';
10
9
  export type PeerInput = {
11
10
  workingDir: string;
12
11
  prompt: string;
@@ -18,7 +17,6 @@ export type PeerInput = {
18
17
  reasoningEffort?: 'high' | 'xhigh';
19
18
  serviceTier?: 'default' | 'fast' | 'flex';
20
19
  };
21
- export declare function formatPeerResponse(result: PeerResult, modelName: string): string;
22
20
  export declare function handleAskCodex(input: PeerInput): Promise<{
23
21
  content: Array<{
24
22
  type: 'text';