cc-reviewer 3.0.0 → 4.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,92 @@
1
+ # Claude Review
2
+
3
+ Get a review from a fresh Claude (Opus) instance with clean context — zero memory of this session.
4
+
5
+ ## Arguments
6
+ - `$ARGUMENTS` - Optional: focus area or custom instructions
7
+
8
+ ## Claude Strengths
9
+ - **Deep Analysis**: Thorough reasoning across all dimensions
10
+ - **Correctness**: Logic errors, edge cases, subtle bugs
11
+ - **Security**: Vulnerability detection, auth analysis
12
+ - **Architecture**: Design patterns, coupling, abstractions
13
+ - **Clean Context**: No confirmation bias from the current session
14
+
15
+ ## Before Calling - PREPARE THE HANDOFF
16
+
17
+ ### 1. Summarize What You Did (Brief!)
18
+ ```
19
+ "Implemented WebSocket reconnection with exponential backoff
20
+ and added session persistence across reconnects."
21
+ ```
22
+
23
+ ### 2. List Your Uncertainties
24
+ What should Claude verify?
25
+
26
+ ```
27
+ UNCERTAINTIES:
28
+ - "Is the backoff strategy correct for this use case?"
29
+ - "Could there be a race condition between reconnect and message send?"
30
+ ```
31
+
32
+ ### 3. Ask Specific Questions
33
+ ```
34
+ QUESTIONS:
35
+ - "Is the session token handling secure during reconnect?"
36
+ - "Should I handle partial message delivery differently?"
37
+ ```
38
+
39
+ ## Tool Invocation
40
+
41
+ Call `claude_review` with:
42
+
43
+ ```json
44
+ {
45
+ "workingDir": "<current directory>",
46
+ "ccOutput": "<structured handoff - see below>",
47
+ "outputType": "analysis",
48
+ "focusAreas": ["<from $ARGUMENTS>"]
49
+ }
50
+ ```
51
+
52
+ ### Structure your ccOutput:
53
+
54
+ ```
55
+ SUMMARY:
56
+ <what you did, 1-3 sentences>
57
+
58
+ UNCERTAINTIES (verify these):
59
+ 1. <your uncertainty>
60
+ 2. <another uncertainty>
61
+
62
+ QUESTIONS:
63
+ 1. <specific question>
64
+
65
+ KEY DECISIONS:
66
+ - <decision>: <rationale>
67
+
68
+ PRIORITY FILES:
69
+ - <file to focus on>
70
+ ```
71
+
72
+ ## After Receiving Review
73
+
74
+ 1. **Verify file references exist**
75
+ - Check mentioned file:line locations
76
+ - Flag any that don't exist
77
+
78
+ 2. **Cross-check findings**
79
+ - Read the actual code
80
+ - Confirm the issue exists
81
+
82
+ 3. **Mark confidence:**
83
+ - ✓✓ Verified by you
84
+ - ✓ Plausible, not verified
85
+ - ? Needs investigation
86
+ - ✗ Rejected
87
+
88
+ 4. **Apply judgment**
89
+ - You may disagree with findings
90
+ - Make YOUR recommendation
91
+
92
+ $ARGUMENTS
@@ -1,13 +1,13 @@
1
1
  # Multi Review
2
2
 
3
- Get parallel reviews from both Codex and Gemini, raw output for manual synthesis.
3
+ Get parallel reviews from Codex, Gemini, and a fresh Claude (Opus) instance, raw output for manual synthesis.
4
4
 
5
5
  ## Arguments
6
6
  - `$ARGUMENTS` - Optional: focus area or custom instructions
7
7
 
8
8
  ## When to Use
9
9
 
10
- Use `/multi-review` when you want parallel reviews from both Codex and Gemini.
10
+ Use `/multi-review` when you want parallel reviews from Codex, Gemini, and a fresh Claude (Opus) instance.
11
11
 
12
12
  ## Before Calling - PREPARE THE HANDOFF
13
13
 
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Claude CLI Adapter
3
+ *
4
+ * Implements the ReviewerAdapter interface for Anthropic's Claude CLI.
5
+ * Spawns a FRESH Claude Code instance with zero session context.
6
+ * Returns raw text — CC handles interpretation.
7
+ *
8
+ * Read-only enforcement (defense-in-depth):
9
+ * 1. --permission-mode plan (CLI-level read-only)
10
+ * 2. --disallowed-tools (write tools explicitly blocked)
11
+ * 3. Handoff prompt (explicit READ-ONLY instruction)
12
+ */
13
+ import { ReviewerAdapter, ReviewerCapabilities, ReviewRequest, ReviewResult } from './base.js';
14
+ export declare class ClaudeAdapter implements ReviewerAdapter {
15
+ readonly id = "claude";
16
+ getCapabilities(): ReviewerCapabilities;
17
+ isAvailable(): Promise<boolean>;
18
+ runReview(request: ReviewRequest): Promise<ReviewResult>;
19
+ private runCli;
20
+ private handleException;
21
+ private categorizeError;
22
+ private getSuggestion;
23
+ }
24
+ export declare const claudeAdapter: ClaudeAdapter;
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Claude CLI Adapter
3
+ *
4
+ * Implements the ReviewerAdapter interface for Anthropic's Claude CLI.
5
+ * Spawns a FRESH Claude Code instance with zero session context.
6
+ * Returns raw text — CC handles interpretation.
7
+ *
8
+ * Read-only enforcement (defense-in-depth):
9
+ * 1. --permission-mode plan (CLI-level read-only)
10
+ * 2. --disallowed-tools (write tools explicitly blocked)
11
+ * 3. Handoff prompt (explicit READ-ONLY instruction)
12
+ */
13
+ import { spawn } from 'child_process';
14
+ import { existsSync } from 'fs';
15
+ import { registerAdapter, } from './base.js';
16
+ import { CliExecutor } from '../executor.js';
17
+ import { ClaudeEventDecoder } from '../decoders/index.js';
18
+ import { buildSimpleHandoff, buildHandoffPrompt, selectRole, } from '../handoff.js';
19
+ // =============================================================================
20
+ // CONFIGURATION
21
+ // =============================================================================
22
+ const INACTIVITY_TIMEOUT_MS = 300_000; // 5 min — Opus has long thinking phases
23
+ const MAX_TIMEOUT_MS = 3_600_000; // 60 min absolute max
24
+ const MAX_BUFFER_SIZE = 1024 * 1024; // 1MB max buffer
25
+ // Write tools explicitly blocked as defense-in-depth
26
+ const DISALLOWED_TOOLS = 'Edit Write NotebookEdit';
27
+ // =============================================================================
28
+ // CLAUDE ADAPTER
29
+ // =============================================================================
30
+ export class ClaudeAdapter {
31
+ id = 'claude';
32
+ getCapabilities() {
33
+ return {
34
+ name: 'Claude',
35
+ description: 'Anthropic Claude (Opus) - fresh instance with clean context, excels at deep analysis across all dimensions',
36
+ strengths: ['correctness', 'security', 'architecture', 'maintainability'],
37
+ weaknesses: [],
38
+ hasFilesystemAccess: true,
39
+ supportsStructuredOutput: false,
40
+ maxContextTokens: 200000,
41
+ reasoningLevels: undefined,
42
+ };
43
+ }
44
+ async isAvailable() {
45
+ return new Promise((resolve) => {
46
+ const proc = spawn('claude', ['--version'], {
47
+ stdio: ['ignore', 'pipe', 'pipe'],
48
+ });
49
+ proc.on('close', (code) => resolve(code === 0));
50
+ proc.on('error', () => resolve(false));
51
+ setTimeout(() => { proc.kill(); resolve(false); }, 5000);
52
+ });
53
+ }
54
+ async runReview(request) {
55
+ const startTime = Date.now();
56
+ if (!existsSync(request.workingDir)) {
57
+ return {
58
+ success: false,
59
+ error: { type: 'cli_error', message: `Working directory does not exist: ${request.workingDir}` },
60
+ suggestion: 'Check that the working directory path is correct',
61
+ executionTimeMs: Date.now() - startTime,
62
+ };
63
+ }
64
+ try {
65
+ const handoff = buildSimpleHandoff(request.workingDir, request.ccOutput, request.analyzedFiles, request.focusAreas, request.customPrompt);
66
+ const role = selectRole(request.focusAreas);
67
+ const prompt = buildHandoffPrompt({ handoff, role });
68
+ const result = await this.runCli(prompt, request.workingDir);
69
+ if (result.exitCode !== 0) {
70
+ const error = this.categorizeError(result.stderr);
71
+ return { success: false, error, suggestion: this.getSuggestion(error), executionTimeMs: Date.now() - startTime };
72
+ }
73
+ if (!result.stdout.trim()) {
74
+ return {
75
+ success: false,
76
+ error: { type: 'cli_error', message: 'Claude returned empty response' },
77
+ suggestion: 'Try again or use /codex-review instead',
78
+ executionTimeMs: Date.now() - startTime,
79
+ };
80
+ }
81
+ return { success: true, output: result.stdout, executionTimeMs: Date.now() - startTime };
82
+ }
83
+ catch (error) {
84
+ return this.handleException(error, startTime);
85
+ }
86
+ }
87
+ async runCli(prompt, workingDir) {
88
+ const args = [
89
+ '-p', // Non-interactive, print and exit
90
+ '--model', 'opus', // Use Opus
91
+ '--permission-mode', 'plan', // Read-only enforcement (layer 1)
92
+ '--verbose', // Required for stream-json
93
+ '--output-format', 'stream-json', // Structured streaming events
94
+ '--no-session-persistence', // Ephemeral — no trace
95
+ '--disable-slash-commands', // No skills — minimal startup
96
+ '--disallowed-tools', DISALLOWED_TOOLS, // Block write tools (layer 2)
97
+ '-', // Read prompt from stdin
98
+ ];
99
+ const decoder = new ClaudeEventDecoder();
100
+ const cliStartTime = Date.now();
101
+ console.error('[claude] Running Opus review...');
102
+ decoder.onProgress = (eventType, detail) => {
103
+ const elapsed = Math.round((Date.now() - cliStartTime) / 1000);
104
+ const detailStr = detail ? ` — ${detail}` : '';
105
+ console.error(`[claude] ${eventType}${detailStr} (${elapsed}s)`);
106
+ };
107
+ const executor = new CliExecutor({
108
+ command: 'claude',
109
+ args,
110
+ cwd: workingDir,
111
+ stdin: prompt,
112
+ inactivityTimeoutMs: INACTIVITY_TIMEOUT_MS,
113
+ maxTimeoutMs: MAX_TIMEOUT_MS,
114
+ maxBufferSize: MAX_BUFFER_SIZE,
115
+ onLine: (line) => {
116
+ decoder.processLine(line);
117
+ },
118
+ });
119
+ const result = await executor.run();
120
+ const elapsed = Math.round((Date.now() - cliStartTime) / 1000);
121
+ console.error(`[claude] ✓ complete (${elapsed}s)`);
122
+ // Check for errors captured from stream events
123
+ const decoderError = decoder.getError();
124
+ if (decoderError) {
125
+ return { stdout: '', stderr: decoderError, exitCode: 1, truncated: false };
126
+ }
127
+ const finalResponse = decoder.getFinalResponse();
128
+ if (!finalResponse && decoder.hasNoOutput()) {
129
+ return { stdout: '', stderr: 'No response from Claude — possible rate limit or auth issue', exitCode: 1, truncated: false };
130
+ }
131
+ return {
132
+ stdout: finalResponse || result.rawStdout,
133
+ stderr: result.stderr,
134
+ exitCode: result.exitCode,
135
+ truncated: result.truncated,
136
+ };
137
+ }
138
+ handleException(error, startTime) {
139
+ const err = error;
140
+ if (err.code === 'ENOENT') {
141
+ return { success: false, error: { type: 'cli_not_found', message: 'Claude CLI not found' },
142
+ suggestion: 'Install Claude Code: https://docs.anthropic.com/en/docs/claude-code', executionTimeMs: Date.now() - startTime };
143
+ }
144
+ if (err.message === 'TIMEOUT') {
145
+ return { success: false, error: { type: 'timeout', message: 'Claude timed out — no events received' },
146
+ suggestion: 'Try a smaller scope or use /codex-review', executionTimeMs: Date.now() - startTime };
147
+ }
148
+ if (err.message === 'MAX_TIMEOUT') {
149
+ return { success: false, error: { type: 'timeout', message: 'Task exceeded 60 minute maximum' },
150
+ suggestion: 'Try a smaller scope', executionTimeMs: Date.now() - startTime };
151
+ }
152
+ return { success: false, error: { type: 'cli_error', message: err.message }, executionTimeMs: Date.now() - startTime };
153
+ }
154
+ categorizeError(stderr) {
155
+ const lower = stderr.toLowerCase();
156
+ if (lower.includes('rate limit') || lower.includes('quota') || lower.includes('no response from claude')) {
157
+ return { type: 'rate_limit', message: 'Claude rate limit — try again later' };
158
+ }
159
+ if (lower.includes('unauthorized') || lower.includes('authentication') || lower.includes('api key') || stderr.includes('401') || stderr.includes('403')) {
160
+ return { type: 'auth_error', message: 'Authentication failed', details: { stderr } };
161
+ }
162
+ return { type: 'cli_error', message: stderr || 'Unknown error' };
163
+ }
164
+ getSuggestion(error) {
165
+ switch (error.type) {
166
+ case 'rate_limit': return 'Wait and retry, or use /codex-review or /gemini-review instead';
167
+ case 'auth_error': return 'Run `claude auth` to authenticate';
168
+ case 'cli_not_found': return 'Install Claude Code: https://docs.anthropic.com/en/docs/claude-code';
169
+ default: return 'Check the error message and try again';
170
+ }
171
+ }
172
+ }
173
+ // Register the adapter
174
+ registerAdapter(new ClaudeAdapter());
175
+ export const claudeAdapter = new ClaudeAdapter();
@@ -87,7 +87,8 @@ export class CodexAdapter {
87
87
  '-m', 'gpt-5.4',
88
88
  '-c', `model_reasoning_effort=${reasoningEffort}`,
89
89
  '-c', 'model_reasoning_summary_format=experimental',
90
- '--dangerously-bypass-approvals-and-sandbox',
90
+ '--full-auto',
91
+ '--sandbox', 'read-only',
91
92
  '--skip-git-repo-check',
92
93
  '-C', workingDir,
93
94
  '-', // Read prompt from stdin
@@ -79,7 +79,8 @@ export class GeminiAdapter {
79
79
  }
80
80
  async runCli(prompt, workingDir) {
81
81
  const args = [
82
- '--yolo',
82
+ '--sandbox',
83
+ '--approval-mode', 'plan',
83
84
  '--output-format', 'stream-json',
84
85
  '--include-directories', workingDir,
85
86
  '-p', '',
@@ -5,6 +5,8 @@
5
5
  */
6
6
  import './codex.js';
7
7
  import './gemini.js';
8
+ import './claude.js';
8
9
  export * from './base.js';
9
10
  export { codexAdapter } from './codex.js';
10
11
  export { geminiAdapter } from './gemini.js';
12
+ export { claudeAdapter } from './claude.js';
@@ -6,8 +6,10 @@
6
6
  // Import adapters to register them
7
7
  import './codex.js';
8
8
  import './gemini.js';
9
+ import './claude.js';
9
10
  // Re-export everything from base
10
11
  export * from './base.js';
11
12
  // Export specific adapters
12
13
  export { codexAdapter } from './codex.js';
13
14
  export { geminiAdapter } from './gemini.js';
15
+ export { claudeAdapter } from './claude.js';
package/dist/cli/check.js CHANGED
@@ -22,11 +22,12 @@ async function commandExists(command) {
22
22
  * Check availability of all supported CLIs
23
23
  */
24
24
  export async function checkCliAvailability() {
25
- const [codex, gemini] = await Promise.all([
25
+ const [codex, gemini, claude] = await Promise.all([
26
26
  commandExists('codex'),
27
- commandExists('gemini')
27
+ commandExists('gemini'),
28
+ commandExists('claude')
28
29
  ]);
29
- return { codex, gemini };
30
+ return { codex, gemini, claude };
30
31
  }
31
32
  /**
32
33
  * Check if a specific CLI is available
@@ -67,9 +68,11 @@ export async function logCliStatus() {
67
68
  console.error('AI Reviewer CLI Status:');
68
69
  console.error(` - Codex: ${status.codex ? '✓ Available' : '✗ Not found'}`);
69
70
  console.error(` - Gemini: ${status.gemini ? '✓ Available' : '✗ Not found'}`);
70
- if (!status.codex && !status.gemini) {
71
- console.error('\nWarning: No AI CLIs found. Install with:');
71
+ console.error(` - Claude: ${status.claude ? '✓ Available' : '✗ Not found'}`);
72
+ if (!status.codex && !status.gemini && !status.claude) {
73
+ console.error('\nWarning: No AI CLIs found. Install at least one:');
72
74
  console.error(' npm install -g @openai/codex-cli');
73
75
  console.error(' npm install -g @google/gemini-cli');
76
+ console.error(' Claude Code: https://docs.anthropic.com/en/docs/claude-code');
74
77
  }
75
78
  }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * ClaudeEventDecoder — Parses Claude CLI stream-json JSONL events.
3
+ *
4
+ * Event stream format (with --output-format stream-json --verbose):
5
+ * {"type":"system","subtype":"init",...}
6
+ * {"type":"assistant","message":{"content":[{"type":"text","text":"..."}],...},...}
7
+ * {"type":"result","subtype":"success","result":"...","duration_ms":...,"usage":{...}}
8
+ */
9
+ export interface ClaudeEvent {
10
+ type: string;
11
+ subtype?: string;
12
+ session_id?: string;
13
+ model?: string;
14
+ result?: string;
15
+ is_error?: boolean;
16
+ duration_ms?: number;
17
+ message?: {
18
+ content?: Array<{
19
+ type: string;
20
+ text?: string;
21
+ }>;
22
+ usage?: {
23
+ input_tokens: number;
24
+ output_tokens: number;
25
+ cache_read_input_tokens?: number;
26
+ cache_creation_input_tokens?: number;
27
+ };
28
+ };
29
+ usage?: {
30
+ input_tokens: number;
31
+ output_tokens: number;
32
+ cache_read_input_tokens?: number;
33
+ cache_creation_input_tokens?: number;
34
+ };
35
+ tool_use_id?: string;
36
+ tool_name?: string;
37
+ }
38
+ export declare class ClaudeEventDecoder {
39
+ onProgress?: (eventType: string, detail?: string) => void;
40
+ private _finalResponse;
41
+ private _usage;
42
+ private _error;
43
+ private _eventCount;
44
+ private _durationMs;
45
+ processLine(line: string): void;
46
+ getFinalResponse(): string | null;
47
+ getUsage(): ClaudeEvent['usage'] | null;
48
+ getError(): string | null;
49
+ getDurationMs(): number | null;
50
+ hasNoOutput(): boolean;
51
+ private _handleEvent;
52
+ private _describeEvent;
53
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * ClaudeEventDecoder — Parses Claude CLI stream-json JSONL events.
3
+ *
4
+ * Event stream format (with --output-format stream-json --verbose):
5
+ * {"type":"system","subtype":"init",...}
6
+ * {"type":"assistant","message":{"content":[{"type":"text","text":"..."}],...},...}
7
+ * {"type":"result","subtype":"success","result":"...","duration_ms":...,"usage":{...}}
8
+ */
9
+ // =============================================================================
10
+ // DECODER
11
+ // =============================================================================
12
+ export class ClaudeEventDecoder {
13
+ onProgress;
14
+ _finalResponse = null;
15
+ _usage = null;
16
+ _error = null;
17
+ _eventCount = 0;
18
+ _durationMs = null;
19
+ // =============================================================================
20
+ // PUBLIC API
21
+ // =============================================================================
22
+ processLine(line) {
23
+ const trimmed = line.trim();
24
+ if (trimmed.length === 0)
25
+ return;
26
+ let event;
27
+ try {
28
+ const parsed = JSON.parse(trimmed);
29
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
30
+ return;
31
+ event = parsed;
32
+ }
33
+ catch {
34
+ return;
35
+ }
36
+ if (!event.type)
37
+ return;
38
+ this._handleEvent(event);
39
+ }
40
+ getFinalResponse() {
41
+ return this._finalResponse;
42
+ }
43
+ getUsage() {
44
+ return this._usage;
45
+ }
46
+ getError() {
47
+ return this._error;
48
+ }
49
+ getDurationMs() {
50
+ return this._durationMs;
51
+ }
52
+ hasNoOutput() {
53
+ return this._eventCount > 0 && this._finalResponse === null;
54
+ }
55
+ // =============================================================================
56
+ // PRIVATE HELPERS
57
+ // =============================================================================
58
+ _handleEvent(event) {
59
+ this._eventCount++;
60
+ switch (event.type) {
61
+ case 'result':
62
+ // The result event contains the final text response
63
+ if (event.subtype === 'success' && typeof event.result === 'string') {
64
+ this._finalResponse = event.result;
65
+ }
66
+ if (event.is_error) {
67
+ this._error = event.result || 'Claude review failed';
68
+ }
69
+ if (event.usage) {
70
+ this._usage = event.usage;
71
+ }
72
+ if (event.duration_ms != null) {
73
+ this._durationMs = event.duration_ms;
74
+ }
75
+ break;
76
+ case 'assistant':
77
+ // Track usage from assistant messages
78
+ if (event.message?.usage) {
79
+ this._usage = event.message.usage;
80
+ }
81
+ break;
82
+ case 'error':
83
+ this._error = event.result || 'Unknown error from Claude CLI';
84
+ break;
85
+ }
86
+ this.onProgress?.(event.type, this._describeEvent(event));
87
+ }
88
+ _describeEvent(event) {
89
+ switch (event.type) {
90
+ case 'system':
91
+ if (event.subtype === 'init')
92
+ return `model: ${event.model || 'opus'}`;
93
+ if (event.subtype)
94
+ return event.subtype;
95
+ return undefined;
96
+ case 'assistant':
97
+ return 'assistant message';
98
+ case 'tool_use':
99
+ return event.tool_name ? `tool: ${event.tool_name}` : 'tool use';
100
+ case 'result':
101
+ return `status: ${event.subtype || 'unknown'}`;
102
+ default:
103
+ return undefined;
104
+ }
105
+ }
106
+ }
@@ -2,3 +2,5 @@ export { CodexEventDecoder } from './codex.js';
2
2
  export type { CodexEvent } from './codex.js';
3
3
  export { GeminiEventDecoder } from './gemini.js';
4
4
  export type { GeminiEvent } from './gemini.js';
5
+ export { ClaudeEventDecoder } from './claude.js';
6
+ export type { ClaudeEvent } from './claude.js';
@@ -1,2 +1,3 @@
1
1
  export { CodexEventDecoder } from './codex.js';
2
2
  export { GeminiEventDecoder } from './gemini.js';
3
+ export { ClaudeEventDecoder } from './claude.js';
package/dist/errors.js CHANGED
@@ -6,17 +6,20 @@
6
6
  // Gemini: https://github.com/google-gemini/gemini-cli
7
7
  const INSTALL_COMMANDS = {
8
8
  codex: 'npm install -g @openai/codex-cli',
9
- gemini: 'npm install -g @google/gemini-cli'
9
+ gemini: 'npm install -g @google/gemini-cli',
10
+ claude: 'https://docs.anthropic.com/en/docs/claude-code'
10
11
  };
11
12
  // Environment variables for API keys
12
13
  const ENV_VARS = {
13
14
  codex: 'OPENAI_API_KEY',
14
- gemini: 'GEMINI_API_KEY'
15
+ gemini: 'GEMINI_API_KEY',
16
+ claude: 'ANTHROPIC_API_KEY'
15
17
  };
16
18
  // Authentication commands
17
19
  const AUTH_COMMANDS = {
18
20
  codex: 'codex login',
19
- gemini: 'gemini (follow prompts)'
21
+ gemini: 'gemini (follow prompts)',
22
+ claude: 'claude auth'
20
23
  };
21
24
  /**
22
25
  * Create a CLI not found error
@@ -83,7 +86,8 @@ export function createCliError(cli, exitCode, stderr) {
83
86
  * Format an error for user display
84
87
  */
85
88
  export function formatErrorForUser(error) {
86
- const otherCli = error.cli === 'codex' ? 'gemini' : 'codex';
89
+ const others = ['codex', 'gemini', 'claude'].filter(c => c !== error.cli);
90
+ const otherCli = others[0];
87
91
  switch (error.type) {
88
92
  case 'cli_not_found':
89
93
  return `❌ ${error.cli} CLI not found.
package/dist/handoff.js CHANGED
@@ -168,7 +168,9 @@ export function buildHandoffPrompt(options) {
168
168
 
169
169
  Review recent work in \`${handoff.workingDir}\`.
170
170
 
171
- **Summary:** ${handoff.summary}${handoff.confidence !== undefined && handoff.confidence < 0.9 ? `\n**CC Confidence:** ${Math.round(handoff.confidence * 100)}% — verify weak areas` : ''}`);
171
+ **Summary:** ${handoff.summary}${handoff.confidence !== undefined && handoff.confidence < 0.9 ? `\n**CC Confidence:** ${Math.round(handoff.confidence * 100)}% — verify weak areas` : ''}
172
+
173
+ **IMPORTANT: This is a READ-ONLY review. Do NOT create, modify, or delete any files. Only read files to verify claims.**`);
172
174
  // SECTION 3: CC'S UNCERTAINTIES
173
175
  if (handoff.uncertainties && handoff.uncertainties.length > 0) {
174
176
  sections.push(`## CC'S UNCERTAINTIES
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@
18
18
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
19
19
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
20
20
  import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
21
- import { handleCodexReview, handleGeminiReview, handleMultiReview, ReviewInputSchema, TOOL_DEFINITIONS } from './tools/feedback.js';
21
+ import { handleCodexReview, handleGeminiReview, handleClaudeReview, handleMultiReview, ReviewInputSchema, TOOL_DEFINITIONS } from './tools/feedback.js';
22
22
  import { logCliStatus } from './cli/check.js';
23
23
  import { installCommands } from './commands.js';
24
24
  // Read version from package.json
@@ -62,6 +62,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
62
62
  tools: [
63
63
  TOOL_DEFINITIONS.codex_review,
64
64
  TOOL_DEFINITIONS.gemini_review,
65
+ TOOL_DEFINITIONS.claude_review,
65
66
  TOOL_DEFINITIONS.multi_review,
66
67
  ],
67
68
  };
@@ -79,6 +80,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
79
80
  const input = ReviewInputSchema.parse(args);
80
81
  return await handleGeminiReview(input);
81
82
  }
83
+ case 'claude_review': {
84
+ const input = ReviewInputSchema.parse(args);
85
+ return await handleClaudeReview(input);
86
+ }
82
87
  case 'multi_review': {
83
88
  const input = ReviewInputSchema.parse(args);
84
89
  return await handleMultiReview(input);
@@ -46,6 +46,12 @@ export declare function handleGeminiReview(input: ReviewInput): Promise<{
46
46
  text: string;
47
47
  }>;
48
48
  }>;
49
+ export declare function handleClaudeReview(input: ReviewInput): Promise<{
50
+ content: Array<{
51
+ type: 'text';
52
+ text: string;
53
+ }>;
54
+ }>;
49
55
  export declare function handleMultiReview(input: ReviewInput): Promise<{
50
56
  content: Array<{
51
57
  type: 'text';
@@ -147,6 +153,48 @@ export declare const TOOL_DEFINITIONS: {
147
153
  required: string[];
148
154
  };
149
155
  };
156
+ claude_review: {
157
+ name: string;
158
+ description: string;
159
+ inputSchema: {
160
+ type: string;
161
+ properties: {
162
+ workingDir: {
163
+ type: string;
164
+ description: string;
165
+ };
166
+ ccOutput: {
167
+ type: string;
168
+ description: string;
169
+ };
170
+ outputType: {
171
+ type: string;
172
+ enum: string[];
173
+ description: string;
174
+ };
175
+ analyzedFiles: {
176
+ type: string;
177
+ items: {
178
+ type: string;
179
+ };
180
+ description: string;
181
+ };
182
+ focusAreas: {
183
+ type: string;
184
+ items: {
185
+ type: string;
186
+ enum: string[];
187
+ };
188
+ description: string;
189
+ };
190
+ customPrompt: {
191
+ type: string;
192
+ description: string;
193
+ };
194
+ };
195
+ required: string[];
196
+ };
197
+ };
150
198
  multi_review: {
151
199
  name: string;
152
200
  description: string;
@@ -73,6 +73,16 @@ export async function handleGeminiReview(input) {
73
73
  const result = await adapter.runReview(toReviewRequest(input));
74
74
  return { content: [{ type: 'text', text: formatResult(result, 'Gemini') }] };
75
75
  }
76
+ export async function handleClaudeReview(input) {
77
+ const adapter = getAdapter('claude');
78
+ if (!adapter)
79
+ return { content: [{ type: 'text', text: '❌ Claude adapter not registered' }] };
80
+ const available = await adapter.isAvailable();
81
+ if (!available)
82
+ return { content: [{ type: 'text', text: '❌ Claude CLI not found.\n\nInstall Claude Code: https://docs.anthropic.com/en/docs/claude-code\n\nAlternative: Use codex_review or gemini_review instead' }] };
83
+ const result = await adapter.runReview(toReviewRequest(input));
84
+ return { content: [{ type: 'text', text: formatResult(result, 'Claude (Opus)') }] };
85
+ }
76
86
  // =============================================================================
77
87
  // MULTI-MODEL HANDLER
78
88
  // =============================================================================
@@ -140,9 +150,25 @@ export const TOOL_DEFINITIONS = {
140
150
  required: ['workingDir', 'ccOutput', 'outputType']
141
151
  }
142
152
  },
153
+ claude_review: {
154
+ name: 'claude_review',
155
+ description: "ONLY use when user explicitly requests '/claude-review' or 'review with claude'. Get second-opinion from a fresh Claude (Opus) instance with clean context — no memory of this session. Excels at deep analysis across all dimensions. DO NOT use for general 'review' requests.",
156
+ inputSchema: {
157
+ type: 'object',
158
+ properties: {
159
+ workingDir: { type: 'string', description: 'Working directory for the CLI to operate in' },
160
+ ccOutput: { type: 'string', description: "Claude Code's output to review (findings, plan, analysis)" },
161
+ outputType: { type: 'string', enum: ['plan', 'findings', 'analysis', 'proposal'], description: 'Type of output being reviewed' },
162
+ analyzedFiles: { type: 'array', items: { type: 'string' }, description: 'File paths that CC analyzed' },
163
+ focusAreas: { type: 'array', items: { type: 'string', enum: ['security', 'performance', 'architecture', 'correctness', 'maintainability', 'scalability', 'testing', 'documentation'] }, description: 'Areas to focus the review on' },
164
+ customPrompt: { type: 'string', description: 'Custom instructions for the reviewer' },
165
+ },
166
+ required: ['workingDir', 'ccOutput', 'outputType']
167
+ }
168
+ },
143
169
  multi_review: {
144
170
  name: 'multi_review',
145
- description: "ONLY use when user explicitly requests '/multi-review' or 'review with both codex and gemini'. Get parallel second-opinions from both external CLIs (Codex and Gemini). Returns combined reviews for synthesis. DO NOT use for general 'review' requests.",
171
+ description: "ONLY use when user explicitly requests '/multi-review' or 'review with all models'. Get parallel second-opinions from Codex, Gemini, and a fresh Claude (Opus) instance. Returns combined reviews for synthesis. DO NOT use for general 'review' requests.",
146
172
  inputSchema: {
147
173
  type: 'object',
148
174
  properties: {
package/dist/types.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export type OutputType = 'plan' | 'findings' | 'analysis' | 'proposal';
5
5
  export type FocusArea = 'security' | 'performance' | 'architecture' | 'correctness' | 'maintainability' | 'scalability' | 'testing' | 'documentation';
6
- export type CliType = 'codex' | 'gemini';
6
+ export type CliType = 'codex' | 'gemini' | 'claude';
7
7
  export type ReasoningEffort = 'high' | 'xhigh';
8
8
  export type ServiceTier = 'default' | 'fast' | 'flex';
9
9
  export interface FeedbackRequest {
@@ -69,6 +69,7 @@ export interface MultiFeedbackResult {
69
69
  export interface CliStatus {
70
70
  codex: boolean;
71
71
  gemini: boolean;
72
+ claude: boolean;
72
73
  }
73
74
  export interface StructuredFeedback {
74
75
  agreements: Array<{
package/dist/types.js CHANGED
@@ -11,6 +11,11 @@ export const REVIEWER_PERSONAS = {
11
11
  name: 'Gemini',
12
12
  focus: 'design patterns, scalability, tech debt',
13
13
  style: 'Think holistically - consider broader context.'
14
+ },
15
+ claude: {
16
+ name: 'Claude',
17
+ focus: 'deep analysis, correctness, security, architecture',
18
+ style: 'Fresh perspective with clean context - challenge assumptions.'
14
19
  }
15
20
  };
16
21
  // Focus area descriptions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-reviewer",
3
- "version": "3.0.0",
3
+ "version": "4.0.0",
4
4
  "description": "MCP server for Claude Code - Get second-opinion feedback from Codex/Gemini CLIs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",