cc-reviewer 2.1.0 → 3.0.1
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/README.md +15 -35
- package/commands/{codex-xhigh.md → codex-xhigh-review.md} +3 -3
- package/commands/{multi.md → multi-review.md} +1 -1
- package/dist/adapters/base.d.ts +1 -36
- package/dist/adapters/codex.d.ts +1 -2
- package/dist/adapters/codex.js +8 -46
- package/dist/adapters/gemini.d.ts +1 -2
- package/dist/adapters/gemini.js +6 -44
- package/dist/commands.d.ts +2 -3
- package/dist/commands.js +33 -11
- package/dist/handoff.d.ts +0 -14
- package/dist/handoff.js +3 -33
- package/dist/index.d.ts +1 -1
- package/dist/index.js +17 -22
- package/dist/schema.d.ts +0 -189
- package/dist/schema.js +0 -189
- package/dist/tools/feedback.js +5 -5
- package/dist/types.d.ts +0 -1
- package/package.json +2 -3
- package/commands/ask-codex.md +0 -36
- package/commands/ask-gemini.md +0 -35
- package/commands/ask-multi.md +0 -39
- package/dist/setup.d.ts +0 -7
- package/dist/setup.js +0 -27
- package/dist/tools/peer.d.ts +0 -192
- package/dist/tools/peer.js +0 -264
- /package/commands/{codex.md → codex-review.md} +0 -0
- /package/commands/{gemini.md → gemini-review.md} +0 -0
package/README.md
CHANGED
|
@@ -11,11 +11,11 @@ claude mcp add -s user cc-reviewer -- npx -y cc-reviewer
|
|
|
11
11
|
|
|
12
12
|
**Step 2: Restart Claude Code**
|
|
13
13
|
|
|
14
|
-
The MCP tools and slash commands (`/codex`, `/gemini`, `/multi
|
|
14
|
+
The MCP tools and slash commands (`/codex-review`, `/gemini-review`, `/multi-review`) are automatically installed.
|
|
15
15
|
|
|
16
16
|
**Manual command install** (if needed):
|
|
17
17
|
```bash
|
|
18
|
-
npx cc-reviewer
|
|
18
|
+
npx cc-reviewer update
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
Verify with:
|
|
@@ -52,14 +52,10 @@ gemini # follow auth prompts
|
|
|
52
52
|
These tools provide **external second-opinion reviews** from Codex and Gemini CLIs. They are designed to complement Claude Code's native review capabilities, not replace them.
|
|
53
53
|
|
|
54
54
|
**Review tools** (external second-opinion on your work):
|
|
55
|
-
- `/codex` or "review with codex" - Get external Codex review
|
|
56
|
-
- `/
|
|
57
|
-
- `/
|
|
58
|
-
|
|
59
|
-
**Ask tools** (get help from a peer engineer):
|
|
60
|
-
- `/ask-codex` - Ask Codex for help (planning, debugging, explaining, fixing)
|
|
61
|
-
- `/ask-gemini` - Ask Gemini for help (architecture, patterns, scalability)
|
|
62
|
-
- `/ask-multi` - Ask both models in parallel
|
|
55
|
+
- `/codex-review` or "review with codex" - Get external Codex review
|
|
56
|
+
- `/codex-xhigh-review` - Deep-thinking Codex review with xhigh reasoning
|
|
57
|
+
- `/gemini-review` or "review with gemini" - Get external Gemini review
|
|
58
|
+
- `/multi-review` - Get parallel reviews from both CLIs
|
|
63
59
|
|
|
64
60
|
**For regular reviews:** Just say "review" and Claude Code will use its native capabilities. These external tools are only invoked when explicitly requested.
|
|
65
61
|
|
|
@@ -68,24 +64,18 @@ These tools provide **external second-opinion reviews** from Codex and Gemini CL
|
|
|
68
64
|
These commands are available after restart:
|
|
69
65
|
|
|
70
66
|
```bash
|
|
71
|
-
# Review
|
|
72
|
-
/codex
|
|
73
|
-
/codex
|
|
74
|
-
/
|
|
75
|
-
/gemini
|
|
76
|
-
/
|
|
77
|
-
/multi # Both models in parallel
|
|
78
|
-
|
|
79
|
-
# Ask tools (peer engineer)
|
|
80
|
-
/ask-codex # Ask Codex for help
|
|
81
|
-
/ask-gemini # Ask Gemini for help
|
|
82
|
-
/ask-multi # Ask both in parallel
|
|
67
|
+
/codex-review # Review with Codex
|
|
68
|
+
/codex-review security # Focus on security
|
|
69
|
+
/codex-xhigh-review # Codex with xhigh reasoning effort
|
|
70
|
+
/gemini-review # Review with Gemini
|
|
71
|
+
/gemini-review architecture # Focus on architecture
|
|
72
|
+
/multi-review # Both models in parallel
|
|
83
73
|
```
|
|
84
74
|
|
|
85
75
|
## How It Works
|
|
86
76
|
|
|
87
77
|
```
|
|
88
|
-
CC does work → User: /codex → External CLI reviews → CC synthesizes → Updated output
|
|
78
|
+
CC does work → User: /codex-review → External CLI reviews → CC synthesizes → Updated output
|
|
89
79
|
```
|
|
90
80
|
|
|
91
81
|
**Key Principles:**
|
|
@@ -108,33 +98,23 @@ CC does work → User: /codex → External CLI reviews → CC synthesizes → Up
|
|
|
108
98
|
|
|
109
99
|
## MCP Tools
|
|
110
100
|
|
|
111
|
-
The plugin exposes
|
|
101
|
+
The plugin exposes three MCP tools:
|
|
112
102
|
|
|
113
103
|
| Tool | Description |
|
|
114
104
|
|------|-------------|
|
|
115
105
|
| `codex_review` | Get Codex review (correctness, edge cases, performance) |
|
|
116
106
|
| `gemini_review` | Get Gemini review (design patterns, scalability, tech debt) |
|
|
117
107
|
| `multi_review` | Parallel review from both models |
|
|
118
|
-
| `ask_codex` | Ask Codex for help (planning, debugging, explaining, fixing) |
|
|
119
|
-
| `ask_gemini` | Ask Gemini for help (architecture, patterns, scalability) |
|
|
120
|
-
| `ask_multi` | Ask both models in parallel |
|
|
121
108
|
|
|
122
109
|
## Output Format
|
|
123
110
|
|
|
124
|
-
**Review tools** return structured
|
|
111
|
+
**Review tools** return structured feedback from the external CLIs. Claude Code parses this feedback to identify:
|
|
125
112
|
- **Findings**: Issues with severity, confidence, location, and suggestions
|
|
126
113
|
- **Agreements**: Validations of CC's correct assessments
|
|
127
114
|
- **Disagreements**: Challenges to CC's claims with corrections
|
|
128
115
|
- **Alternatives**: Different approaches with tradeoffs
|
|
129
116
|
- **Risk Assessment**: Overall risk level with top concerns
|
|
130
117
|
|
|
131
|
-
**Ask tools** return structured JSON responses with:
|
|
132
|
-
- **Answer**: Main response text (markdown)
|
|
133
|
-
- **Key Points**: Bullet summary of main points
|
|
134
|
-
- **Suggested Actions**: Recommended actions with priority and rationale
|
|
135
|
-
- **File References**: Files examined with line ranges and relevance
|
|
136
|
-
- **Alternatives**: Alternative approaches considered
|
|
137
|
-
|
|
138
118
|
## Development
|
|
139
119
|
|
|
140
120
|
```bash
|
|
@@ -17,10 +17,10 @@ Use the `codex_review` MCP tool with `reasoningEffort: "xhigh"` for deeper analy
|
|
|
17
17
|
2. Call the `codex_review` tool with:
|
|
18
18
|
- `workingDir`: current working directory
|
|
19
19
|
- `ccOutput`: brief summary of recent changes or context
|
|
20
|
-
- `reasoningEffort`: "xhigh" (this is the key difference from /codex)
|
|
20
|
+
- `reasoningEffort`: "xhigh" (this is the key difference from /codex-review)
|
|
21
21
|
- `serviceTier`: if user says "fast mode"/"fast"/"priority" → "fast"; if "flex"/"cheap"/"budget" → "flex"; otherwise omit
|
|
22
|
-
- `
|
|
23
|
-
- `
|
|
22
|
+
- `focusAreas`: extracted from $ARGUMENTS if it's a known focus area
|
|
23
|
+
- `customPrompt`: $ARGUMENTS if it's custom text
|
|
24
24
|
|
|
25
25
|
3. After receiving feedback - VALIDATE before accepting:
|
|
26
26
|
|
|
@@ -7,7 +7,7 @@ Get parallel reviews from both Codex and Gemini, raw output for manual synthesis
|
|
|
7
7
|
|
|
8
8
|
## When to Use
|
|
9
9
|
|
|
10
|
-
Use `/multi` when you want parallel reviews from both Codex and Gemini.
|
|
10
|
+
Use `/multi-review` when you want parallel reviews from both Codex and Gemini.
|
|
11
11
|
|
|
12
12
|
## Before Calling - PREPARE THE HANDOFF
|
|
13
13
|
|
package/dist/adapters/base.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Makes it easy to add new models (Ollama, Azure, etc.) without
|
|
6
6
|
* changing the core orchestration logic.
|
|
7
7
|
*/
|
|
8
|
-
import { FocusArea, OutputType, ReasoningEffort, ServiceTier
|
|
8
|
+
import { FocusArea, OutputType, ReasoningEffort, ServiceTier } from '../types.js';
|
|
9
9
|
export interface ReviewerCapabilities {
|
|
10
10
|
/** Display name for this reviewer */
|
|
11
11
|
name: string;
|
|
@@ -54,26 +54,6 @@ export interface ExpertRole {
|
|
|
54
54
|
export declare const EXPERT_ROLES: Record<string, ExpertRole>;
|
|
55
55
|
/** @deprecated Use handoff.ts selectRole() instead */
|
|
56
56
|
export declare function selectExpertRole(focusAreas?: FocusArea[]): ExpertRole;
|
|
57
|
-
export interface PeerRequest {
|
|
58
|
-
/** Working directory containing the code */
|
|
59
|
-
workingDir: string;
|
|
60
|
-
/** The question or request from CC */
|
|
61
|
-
prompt: string;
|
|
62
|
-
/** Hint about the type of task */
|
|
63
|
-
taskType?: TaskType;
|
|
64
|
-
/** Files the peer should focus on */
|
|
65
|
-
relevantFiles?: string[];
|
|
66
|
-
/** Additional context (error messages, prior analysis) */
|
|
67
|
-
context?: string;
|
|
68
|
-
/** Areas to focus on */
|
|
69
|
-
focusAreas?: FocusArea[];
|
|
70
|
-
/** Custom instructions from the user */
|
|
71
|
-
customPrompt?: string;
|
|
72
|
-
/** Reasoning effort level (for models that support it) */
|
|
73
|
-
reasoningEffort?: ReasoningEffort;
|
|
74
|
-
/** Service tier (for models that support it: priority = fast, flex = cheap) */
|
|
75
|
-
serviceTier?: ServiceTier;
|
|
76
|
-
}
|
|
77
57
|
export interface ReviewSuccess {
|
|
78
58
|
success: true;
|
|
79
59
|
output: string;
|
|
@@ -92,19 +72,6 @@ export interface ReviewError {
|
|
|
92
72
|
message: string;
|
|
93
73
|
details?: Record<string, unknown>;
|
|
94
74
|
}
|
|
95
|
-
export interface PeerSuccess {
|
|
96
|
-
success: true;
|
|
97
|
-
output: string;
|
|
98
|
-
executionTimeMs: number;
|
|
99
|
-
}
|
|
100
|
-
export interface PeerFailure {
|
|
101
|
-
success: false;
|
|
102
|
-
error: ReviewError;
|
|
103
|
-
suggestion?: string;
|
|
104
|
-
rawOutput?: string;
|
|
105
|
-
executionTimeMs: number;
|
|
106
|
-
}
|
|
107
|
-
export type PeerResult = PeerSuccess | PeerFailure;
|
|
108
75
|
/**
|
|
109
76
|
* Base interface that all reviewer adapters must implement.
|
|
110
77
|
* This allows easy addition of new AI CLIs without changing orchestration logic.
|
|
@@ -118,8 +85,6 @@ export interface ReviewerAdapter {
|
|
|
118
85
|
isAvailable(): Promise<boolean>;
|
|
119
86
|
/** Run a review and return structured output */
|
|
120
87
|
runReview(request: ReviewRequest): Promise<ReviewResult>;
|
|
121
|
-
/** Run a general-purpose peer request and return structured output */
|
|
122
|
-
runPeerRequest(request: PeerRequest): Promise<PeerResult>;
|
|
123
88
|
/**
|
|
124
89
|
* Optional: Run peer review of another model's output
|
|
125
90
|
* Future capability - not currently implemented by any adapter
|
package/dist/adapters/codex.d.ts
CHANGED
|
@@ -5,13 +5,12 @@
|
|
|
5
5
|
* Returns raw text — no JSON parsing or schema enforcement.
|
|
6
6
|
* CC handles interpretation of the reviewer's response.
|
|
7
7
|
*/
|
|
8
|
-
import { ReviewerAdapter, ReviewerCapabilities, ReviewRequest, ReviewResult
|
|
8
|
+
import { ReviewerAdapter, ReviewerCapabilities, ReviewRequest, ReviewResult } from './base.js';
|
|
9
9
|
export declare class CodexAdapter implements ReviewerAdapter {
|
|
10
10
|
readonly id = "codex";
|
|
11
11
|
getCapabilities(): ReviewerCapabilities;
|
|
12
12
|
isAvailable(): Promise<boolean>;
|
|
13
13
|
runReview(request: ReviewRequest): Promise<ReviewResult>;
|
|
14
|
-
runPeerRequest(request: PeerRequest): Promise<PeerResult>;
|
|
15
14
|
private runCli;
|
|
16
15
|
private handleException;
|
|
17
16
|
private categorizeError;
|
package/dist/adapters/codex.js
CHANGED
|
@@ -10,7 +10,7 @@ import { existsSync } from 'fs';
|
|
|
10
10
|
import { registerAdapter, } from './base.js';
|
|
11
11
|
import { CliExecutor } from '../executor.js';
|
|
12
12
|
import { CodexEventDecoder } from '../decoders/index.js';
|
|
13
|
-
import { buildSimpleHandoff, buildHandoffPrompt,
|
|
13
|
+
import { buildSimpleHandoff, buildHandoffPrompt, selectRole, } from '../handoff.js';
|
|
14
14
|
// =============================================================================
|
|
15
15
|
// CONFIGURATION
|
|
16
16
|
// =============================================================================
|
|
@@ -70,46 +70,7 @@ export class CodexAdapter {
|
|
|
70
70
|
return {
|
|
71
71
|
success: false,
|
|
72
72
|
error: { type: 'cli_error', message: 'Codex returned empty response' },
|
|
73
|
-
suggestion: 'Try again or use /gemini instead',
|
|
74
|
-
executionTimeMs: Date.now() - startTime,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
return { success: true, output: result.stdout, executionTimeMs: Date.now() - startTime };
|
|
78
|
-
}
|
|
79
|
-
catch (error) {
|
|
80
|
-
return this.handleException(error, startTime);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
async runPeerRequest(request) {
|
|
84
|
-
const startTime = Date.now();
|
|
85
|
-
if (!existsSync(request.workingDir)) {
|
|
86
|
-
return {
|
|
87
|
-
success: false,
|
|
88
|
-
error: { type: 'cli_error', message: `Working directory does not exist: ${request.workingDir}` },
|
|
89
|
-
suggestion: 'Check that the working directory path is correct',
|
|
90
|
-
executionTimeMs: Date.now() - startTime,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
try {
|
|
94
|
-
const prompt = buildPeerPrompt({
|
|
95
|
-
workingDir: request.workingDir,
|
|
96
|
-
prompt: request.prompt,
|
|
97
|
-
taskType: request.taskType,
|
|
98
|
-
relevantFiles: request.relevantFiles,
|
|
99
|
-
context: request.context,
|
|
100
|
-
focusAreas: request.focusAreas,
|
|
101
|
-
customInstructions: request.customPrompt,
|
|
102
|
-
});
|
|
103
|
-
const result = await this.runCli(prompt, request.workingDir, request.reasoningEffort || 'high', request.serviceTier);
|
|
104
|
-
if (result.exitCode !== 0) {
|
|
105
|
-
const error = this.categorizeError(result.stderr);
|
|
106
|
-
return { success: false, error, suggestion: this.getSuggestion(error), executionTimeMs: Date.now() - startTime };
|
|
107
|
-
}
|
|
108
|
-
if (!result.stdout.trim()) {
|
|
109
|
-
return {
|
|
110
|
-
success: false,
|
|
111
|
-
error: { type: 'cli_error', message: 'Codex returned empty response' },
|
|
112
|
-
suggestion: 'Try again or use /ask-gemini instead',
|
|
73
|
+
suggestion: 'Try again or use /gemini-review instead',
|
|
113
74
|
executionTimeMs: Date.now() - startTime,
|
|
114
75
|
};
|
|
115
76
|
}
|
|
@@ -126,7 +87,8 @@ export class CodexAdapter {
|
|
|
126
87
|
'-m', 'gpt-5.4',
|
|
127
88
|
'-c', `model_reasoning_effort=${reasoningEffort}`,
|
|
128
89
|
'-c', 'model_reasoning_summary_format=experimental',
|
|
129
|
-
'--
|
|
90
|
+
'--full-auto',
|
|
91
|
+
'--sandbox', 'read-only',
|
|
130
92
|
'--skip-git-repo-check',
|
|
131
93
|
'-C', workingDir,
|
|
132
94
|
'-', // Read prompt from stdin
|
|
@@ -178,11 +140,11 @@ export class CodexAdapter {
|
|
|
178
140
|
const err = error;
|
|
179
141
|
if (err.code === 'ENOENT') {
|
|
180
142
|
return { success: false, error: { type: 'cli_not_found', message: 'Codex CLI not found' },
|
|
181
|
-
suggestion: 'Install with: npm install -g @openai/codex', executionTimeMs: Date.now() - startTime };
|
|
143
|
+
suggestion: 'Install with: npm install -g @openai/codex-cli', executionTimeMs: Date.now() - startTime };
|
|
182
144
|
}
|
|
183
145
|
if (err.message === 'TIMEOUT') {
|
|
184
146
|
return { success: false, error: { type: 'timeout', message: 'Codex timed out — no events received' },
|
|
185
|
-
suggestion: 'Try a smaller scope or use /gemini', executionTimeMs: Date.now() - startTime };
|
|
147
|
+
suggestion: 'Try a smaller scope or use /gemini-review', executionTimeMs: Date.now() - startTime };
|
|
186
148
|
}
|
|
187
149
|
if (err.message === 'MAX_TIMEOUT') {
|
|
188
150
|
return { success: false, error: { type: 'timeout', message: 'Task exceeded 60 minute maximum' },
|
|
@@ -205,9 +167,9 @@ export class CodexAdapter {
|
|
|
205
167
|
}
|
|
206
168
|
getSuggestion(error) {
|
|
207
169
|
switch (error.type) {
|
|
208
|
-
case 'rate_limit': return 'Wait and retry, or use /gemini instead';
|
|
170
|
+
case 'rate_limit': return 'Wait and retry, or use /gemini-review instead';
|
|
209
171
|
case 'auth_error': return 'Run `codex login` to authenticate';
|
|
210
|
-
case 'cli_not_found': return 'Install with: npm install -g @openai/codex';
|
|
172
|
+
case 'cli_not_found': return 'Install with: npm install -g @openai/codex-cli';
|
|
211
173
|
default: return 'Check the error message and try again';
|
|
212
174
|
}
|
|
213
175
|
}
|
|
@@ -5,13 +5,12 @@
|
|
|
5
5
|
* Returns raw text — no JSON parsing or schema enforcement.
|
|
6
6
|
* CC handles interpretation of the reviewer's response.
|
|
7
7
|
*/
|
|
8
|
-
import { ReviewerAdapter, ReviewerCapabilities, ReviewRequest, ReviewResult
|
|
8
|
+
import { ReviewerAdapter, ReviewerCapabilities, ReviewRequest, ReviewResult } from './base.js';
|
|
9
9
|
export declare class GeminiAdapter implements ReviewerAdapter {
|
|
10
10
|
readonly id = "gemini";
|
|
11
11
|
getCapabilities(): ReviewerCapabilities;
|
|
12
12
|
isAvailable(): Promise<boolean>;
|
|
13
13
|
runReview(request: ReviewRequest): Promise<ReviewResult>;
|
|
14
|
-
runPeerRequest(request: PeerRequest): Promise<PeerResult>;
|
|
15
14
|
private runCli;
|
|
16
15
|
private handleException;
|
|
17
16
|
private categorizeError;
|
package/dist/adapters/gemini.js
CHANGED
|
@@ -10,7 +10,7 @@ import { existsSync } from 'fs';
|
|
|
10
10
|
import { registerAdapter, } from './base.js';
|
|
11
11
|
import { CliExecutor } from '../executor.js';
|
|
12
12
|
import { GeminiEventDecoder } from '../decoders/index.js';
|
|
13
|
-
import { buildSimpleHandoff, buildHandoffPrompt,
|
|
13
|
+
import { buildSimpleHandoff, buildHandoffPrompt, selectRole, } from '../handoff.js';
|
|
14
14
|
// =============================================================================
|
|
15
15
|
// CONFIGURATION
|
|
16
16
|
// =============================================================================
|
|
@@ -67,46 +67,7 @@ export class GeminiAdapter {
|
|
|
67
67
|
return {
|
|
68
68
|
success: false,
|
|
69
69
|
error: { type: 'cli_error', message: 'Gemini returned empty response' },
|
|
70
|
-
suggestion: 'Try again or use /codex instead',
|
|
71
|
-
executionTimeMs: Date.now() - startTime,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
return { success: true, output: result.stdout, executionTimeMs: Date.now() - startTime };
|
|
75
|
-
}
|
|
76
|
-
catch (error) {
|
|
77
|
-
return this.handleException(error, startTime);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
async runPeerRequest(request) {
|
|
81
|
-
const startTime = Date.now();
|
|
82
|
-
if (!existsSync(request.workingDir)) {
|
|
83
|
-
return {
|
|
84
|
-
success: false,
|
|
85
|
-
error: { type: 'cli_error', message: `Working directory does not exist: ${request.workingDir}` },
|
|
86
|
-
suggestion: 'Check that the working directory path is correct',
|
|
87
|
-
executionTimeMs: Date.now() - startTime,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
try {
|
|
91
|
-
const prompt = buildPeerPrompt({
|
|
92
|
-
workingDir: request.workingDir,
|
|
93
|
-
prompt: request.prompt,
|
|
94
|
-
taskType: request.taskType,
|
|
95
|
-
relevantFiles: request.relevantFiles,
|
|
96
|
-
context: request.context,
|
|
97
|
-
focusAreas: request.focusAreas,
|
|
98
|
-
customInstructions: request.customPrompt,
|
|
99
|
-
});
|
|
100
|
-
const result = await this.runCli(prompt, request.workingDir);
|
|
101
|
-
if (result.exitCode !== 0) {
|
|
102
|
-
const error = this.categorizeError(result.stderr);
|
|
103
|
-
return { success: false, error, suggestion: this.getSuggestion(error), executionTimeMs: Date.now() - startTime };
|
|
104
|
-
}
|
|
105
|
-
if (!result.stdout.trim()) {
|
|
106
|
-
return {
|
|
107
|
-
success: false,
|
|
108
|
-
error: { type: 'cli_error', message: 'Gemini returned empty response' },
|
|
109
|
-
suggestion: 'Try again or use /ask-codex instead',
|
|
70
|
+
suggestion: 'Try again or use /codex-review instead',
|
|
110
71
|
executionTimeMs: Date.now() - startTime,
|
|
111
72
|
};
|
|
112
73
|
}
|
|
@@ -118,7 +79,8 @@ export class GeminiAdapter {
|
|
|
118
79
|
}
|
|
119
80
|
async runCli(prompt, workingDir) {
|
|
120
81
|
const args = [
|
|
121
|
-
'--
|
|
82
|
+
'--sandbox',
|
|
83
|
+
'--approval-mode', 'plan',
|
|
122
84
|
'--output-format', 'stream-json',
|
|
123
85
|
'--include-directories', workingDir,
|
|
124
86
|
'-p', '',
|
|
@@ -162,7 +124,7 @@ export class GeminiAdapter {
|
|
|
162
124
|
}
|
|
163
125
|
if (err.message === 'TIMEOUT') {
|
|
164
126
|
return { success: false, error: { type: 'timeout', message: 'Gemini timed out — no events received' },
|
|
165
|
-
suggestion: 'Try a smaller scope or use /codex', executionTimeMs: Date.now() - startTime };
|
|
127
|
+
suggestion: 'Try a smaller scope or use /codex-review', executionTimeMs: Date.now() - startTime };
|
|
166
128
|
}
|
|
167
129
|
if (err.message === 'MAX_TIMEOUT') {
|
|
168
130
|
return { success: false, error: { type: 'timeout', message: 'Task exceeded 60 minute maximum' },
|
|
@@ -182,7 +144,7 @@ export class GeminiAdapter {
|
|
|
182
144
|
}
|
|
183
145
|
getSuggestion(error) {
|
|
184
146
|
switch (error.type) {
|
|
185
|
-
case 'rate_limit': return 'Wait and retry, or use /codex instead';
|
|
147
|
+
case 'rate_limit': return 'Wait and retry, or use /codex-review instead';
|
|
186
148
|
case 'auth_error': return 'Run `gemini` and follow auth prompts, or set GEMINI_API_KEY';
|
|
187
149
|
case 'cli_not_found': return 'Install with: npm install -g @google/gemini-cli';
|
|
188
150
|
default: return 'Check the error message and try again';
|
package/dist/commands.d.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared module for slash command installation
|
|
3
3
|
*
|
|
4
|
-
* Used by
|
|
5
|
-
* - setup.ts (manual CLI tool: npx cc-reviewer-setup)
|
|
6
|
-
* - index.ts (auto-install on MCP server startup)
|
|
4
|
+
* Used by index.ts (auto-install on MCP server startup and `update` subcommand)
|
|
7
5
|
*/
|
|
8
6
|
export interface InstallResult {
|
|
9
7
|
success: boolean;
|
|
10
8
|
installed: string[];
|
|
9
|
+
removed: string[];
|
|
11
10
|
error?: string;
|
|
12
11
|
}
|
|
13
12
|
/**
|
package/dist/commands.js
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared module for slash command installation
|
|
3
3
|
*
|
|
4
|
-
* Used by
|
|
5
|
-
* - setup.ts (manual CLI tool: npx cc-reviewer-setup)
|
|
6
|
-
* - index.ts (auto-install on MCP server startup)
|
|
4
|
+
* Used by index.ts (auto-install on MCP server startup and `update` subcommand)
|
|
7
5
|
*/
|
|
8
|
-
import { existsSync, mkdirSync, copyFileSync, readdirSync, statSync } from 'fs';
|
|
6
|
+
import { existsSync, mkdirSync, copyFileSync, readdirSync, statSync, unlinkSync } from 'fs';
|
|
9
7
|
import { join, dirname } from 'path';
|
|
10
8
|
import { homedir } from 'os';
|
|
11
9
|
import { fileURLToPath } from 'url';
|
|
12
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
13
11
|
const __dirname = dirname(__filename);
|
|
12
|
+
/** Old command filenames that should be pruned on upgrade */
|
|
13
|
+
const DEPRECATED_COMMANDS = [
|
|
14
|
+
'codex.md',
|
|
15
|
+
'gemini.md',
|
|
16
|
+
'multi.md',
|
|
17
|
+
'codex-xhigh.md',
|
|
18
|
+
'ask-codex.md',
|
|
19
|
+
'ask-gemini.md',
|
|
20
|
+
'ask-multi.md',
|
|
21
|
+
];
|
|
14
22
|
/**
|
|
15
23
|
* Get source and target paths for command files
|
|
16
24
|
*/
|
|
@@ -29,13 +37,13 @@ export function installCommands() {
|
|
|
29
37
|
const { source, target } = getCommandPaths();
|
|
30
38
|
// Check source exists
|
|
31
39
|
if (!existsSync(source)) {
|
|
32
|
-
return { success: false, installed: [], error: 'Commands directory not found' };
|
|
40
|
+
return { success: false, installed: [], removed: [], error: 'Commands directory not found' };
|
|
33
41
|
}
|
|
34
42
|
// Create target directory, handle errors (not a dir, permission denied)
|
|
35
43
|
try {
|
|
36
44
|
if (existsSync(target)) {
|
|
37
45
|
if (!statSync(target).isDirectory()) {
|
|
38
|
-
return { success: false, installed: [], error: `${target} exists but is not a directory` };
|
|
46
|
+
return { success: false, installed: [], removed: [], error: `${target} exists but is not a directory` };
|
|
39
47
|
}
|
|
40
48
|
}
|
|
41
49
|
else {
|
|
@@ -44,13 +52,27 @@ export function installCommands() {
|
|
|
44
52
|
}
|
|
45
53
|
catch (err) {
|
|
46
54
|
const msg = err instanceof Error ? err.message : String(err);
|
|
47
|
-
return { success: false, installed: [], error: `Cannot create target directory: ${msg}` };
|
|
55
|
+
return { success: false, installed: [], removed: [], error: `Cannot create target directory: ${msg}` };
|
|
48
56
|
}
|
|
49
57
|
const files = readdirSync(source).filter(f => f.endsWith('.md'));
|
|
50
58
|
if (files.length === 0) {
|
|
51
|
-
return { success: false, installed: [], error: 'No command files found' };
|
|
59
|
+
return { success: false, installed: [], removed: [], error: 'No command files found' };
|
|
52
60
|
}
|
|
53
|
-
//
|
|
61
|
+
// Prune deprecated commands from target
|
|
62
|
+
const removed = [];
|
|
63
|
+
for (const oldFile of DEPRECATED_COMMANDS) {
|
|
64
|
+
const oldPath = join(target, oldFile);
|
|
65
|
+
if (existsSync(oldPath)) {
|
|
66
|
+
try {
|
|
67
|
+
unlinkSync(oldPath);
|
|
68
|
+
removed.push(oldFile.replace('.md', ''));
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Best-effort removal — don't fail the install
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Copy current files
|
|
54
76
|
const installed = [];
|
|
55
77
|
try {
|
|
56
78
|
for (const file of files) {
|
|
@@ -60,7 +82,7 @@ export function installCommands() {
|
|
|
60
82
|
}
|
|
61
83
|
catch (err) {
|
|
62
84
|
const msg = err instanceof Error ? err.message : String(err);
|
|
63
|
-
return { success: false, installed, error: `Copy failed: ${msg}` };
|
|
85
|
+
return { success: false, installed, removed, error: `Copy failed: ${msg}` };
|
|
64
86
|
}
|
|
65
|
-
return { success: true, installed };
|
|
87
|
+
return { success: true, installed, removed };
|
|
66
88
|
}
|
package/dist/handoff.d.ts
CHANGED
|
@@ -229,17 +229,3 @@ export declare function buildSimpleHandoff(workingDir: string, ccOutput: string,
|
|
|
229
229
|
* CC should call this to add its specific concerns
|
|
230
230
|
*/
|
|
231
231
|
export declare function enhanceHandoff(handoff: Handoff, uncertainties?: Uncertainty[], questions?: Question[], decisions?: Decision[]): Handoff;
|
|
232
|
-
export interface PeerPromptOptions {
|
|
233
|
-
workingDir: string;
|
|
234
|
-
prompt: string;
|
|
235
|
-
taskType?: string;
|
|
236
|
-
relevantFiles?: string[];
|
|
237
|
-
context?: string;
|
|
238
|
-
focusAreas?: FocusArea[];
|
|
239
|
-
customInstructions?: string;
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Build a prompt for general-purpose peer assistance (not review).
|
|
243
|
-
* No output format constraints — peer responds naturally, CC interprets.
|
|
244
|
-
*/
|
|
245
|
-
export declare function buildPeerPrompt(options: PeerPromptOptions): string;
|
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
|
|
@@ -227,35 +229,3 @@ export function enhanceHandoff(handoff, uncertainties, questions, decisions) {
|
|
|
227
229
|
decisions: decisions || handoff.decisions,
|
|
228
230
|
};
|
|
229
231
|
}
|
|
230
|
-
/**
|
|
231
|
-
* Build a prompt for general-purpose peer assistance (not review).
|
|
232
|
-
* No output format constraints — peer responds naturally, CC interprets.
|
|
233
|
-
*/
|
|
234
|
-
export function buildPeerPrompt(options) {
|
|
235
|
-
const { workingDir, prompt, taskType, relevantFiles, context, focusAreas, customInstructions } = options;
|
|
236
|
-
const role = selectRole(focusAreas);
|
|
237
|
-
const sections = [];
|
|
238
|
-
// SECTION 1: ROLE
|
|
239
|
-
sections.push(`# ROLE: ${role.name} — Peer Engineer
|
|
240
|
-
|
|
241
|
-
${role.systemPrompt}
|
|
242
|
-
Be direct and actionable.`);
|
|
243
|
-
// SECTION 2: TASK
|
|
244
|
-
const taskLabel = taskType ? ` [${taskType.toUpperCase()}]` : '';
|
|
245
|
-
sections.push(`## YOUR TASK${taskLabel}
|
|
246
|
-
|
|
247
|
-
**Request:** ${prompt}${context ? `\n**Context:** ${context}` : ''}`);
|
|
248
|
-
// SECTION 3: RELEVANT FILES
|
|
249
|
-
if (relevantFiles && relevantFiles.length > 0) {
|
|
250
|
-
sections.push(`## RELEVANT FILES\n${relevantFiles.map(f => `- \`${f}\``).join('\n')}`);
|
|
251
|
-
}
|
|
252
|
-
// SECTION 4: FOCUS AREAS
|
|
253
|
-
if (focusAreas && focusAreas.length > 0) {
|
|
254
|
-
sections.push(`## FOCUS AREAS\n\n${focusAreas.join(', ')}`);
|
|
255
|
-
}
|
|
256
|
-
// SECTION 5: CUSTOM INSTRUCTIONS
|
|
257
|
-
if (customInstructions) {
|
|
258
|
-
sections.push(`## ADDITIONAL INSTRUCTIONS\n\n${customInstructions}`);
|
|
259
|
-
}
|
|
260
|
-
return sections.join('\n\n');
|
|
261
|
-
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -13,21 +13,31 @@
|
|
|
13
13
|
*
|
|
14
14
|
* Usage:
|
|
15
15
|
* - npx cc-reviewer # Run MCP server (normal usage)
|
|
16
|
-
* - npx cc-reviewer
|
|
16
|
+
* - npx cc-reviewer update # Install/update slash commands
|
|
17
17
|
*/
|
|
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
21
|
import { handleCodexReview, handleGeminiReview, handleMultiReview, ReviewInputSchema, TOOL_DEFINITIONS } from './tools/feedback.js';
|
|
22
|
-
import { handleAskCodex, handleAskGemini, handleAskMulti, PEER_TOOL_DEFINITIONS, } from './tools/peer.js';
|
|
23
|
-
import { PeerInputSchema } from './schema.js';
|
|
24
22
|
import { logCliStatus } from './cli/check.js';
|
|
25
23
|
import { installCommands } from './commands.js';
|
|
26
|
-
//
|
|
27
|
-
|
|
24
|
+
// Read version from package.json
|
|
25
|
+
import { readFileSync } from 'fs';
|
|
26
|
+
import { join, dirname } from 'path';
|
|
27
|
+
import { fileURLToPath } from 'url';
|
|
28
|
+
const __pkgPath = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
|
|
29
|
+
const __pkg = JSON.parse(readFileSync(__pkgPath, 'utf-8'));
|
|
30
|
+
const VERSION = __pkg.version;
|
|
31
|
+
// Handle subcommands
|
|
32
|
+
const subcommand = process.argv[2];
|
|
33
|
+
if (subcommand === 'update' || subcommand === '--setup' || subcommand === '--commands') {
|
|
28
34
|
const result = installCommands();
|
|
29
35
|
if (result.success) {
|
|
30
|
-
console.log(
|
|
36
|
+
console.log(`cc-reviewer v${VERSION}`);
|
|
37
|
+
if (result.removed.length > 0) {
|
|
38
|
+
console.log(`✓ Removed ${result.removed.length} deprecated commands: ${result.removed.map(c => `/${c}`).join(', ')}`);
|
|
39
|
+
}
|
|
40
|
+
console.log(`✓ Installed ${result.installed.length} slash commands: ${result.installed.map(c => `/${c}`).join(', ')}`);
|
|
31
41
|
process.exit(0);
|
|
32
42
|
}
|
|
33
43
|
else {
|
|
@@ -40,7 +50,7 @@ import './adapters/index.js';
|
|
|
40
50
|
// Create the MCP server
|
|
41
51
|
const server = new Server({
|
|
42
52
|
name: 'ai-reviewer',
|
|
43
|
-
version:
|
|
53
|
+
version: VERSION,
|
|
44
54
|
}, {
|
|
45
55
|
capabilities: {
|
|
46
56
|
tools: {},
|
|
@@ -53,9 +63,6 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
53
63
|
TOOL_DEFINITIONS.codex_review,
|
|
54
64
|
TOOL_DEFINITIONS.gemini_review,
|
|
55
65
|
TOOL_DEFINITIONS.multi_review,
|
|
56
|
-
PEER_TOOL_DEFINITIONS.ask_codex,
|
|
57
|
-
PEER_TOOL_DEFINITIONS.ask_gemini,
|
|
58
|
-
PEER_TOOL_DEFINITIONS.ask_multi,
|
|
59
66
|
],
|
|
60
67
|
};
|
|
61
68
|
});
|
|
@@ -76,18 +83,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
76
83
|
const input = ReviewInputSchema.parse(args);
|
|
77
84
|
return await handleMultiReview(input);
|
|
78
85
|
}
|
|
79
|
-
case 'ask_codex': {
|
|
80
|
-
const input = PeerInputSchema.parse(args);
|
|
81
|
-
return await handleAskCodex(input);
|
|
82
|
-
}
|
|
83
|
-
case 'ask_gemini': {
|
|
84
|
-
const input = PeerInputSchema.parse(args);
|
|
85
|
-
return await handleAskGemini(input);
|
|
86
|
-
}
|
|
87
|
-
case 'ask_multi': {
|
|
88
|
-
const input = PeerInputSchema.parse(args);
|
|
89
|
-
return await handleAskMulti(input);
|
|
90
|
-
}
|
|
91
86
|
default:
|
|
92
87
|
return {
|
|
93
88
|
content: [{
|