cc-reviewer 1.5.3 → 1.8.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/README.md +36 -8
- package/commands/ask-codex.md +35 -0
- package/commands/ask-gemini.md +35 -0
- package/commands/ask-multi.md +38 -0
- package/dist/adapters/base.d.ts +36 -2
- package/dist/adapters/codex.d.ts +3 -1
- package/dist/adapters/codex.js +96 -5
- package/dist/adapters/gemini.d.ts +3 -1
- package/dist/adapters/gemini.js +88 -2
- package/dist/handoff.d.ts +15 -0
- package/dist/handoff.js +122 -0
- package/dist/index.js +17 -0
- package/dist/schema.d.ts +222 -33
- package/dist/schema.js +203 -14
- package/dist/tools/feedback.d.ts +2 -2
- package/dist/tools/feedback.js +1 -1
- package/dist/tools/peer.d.ts +183 -0
- package/dist/tools/peer.js +336 -0
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ 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`) are automatically installed.
|
|
14
|
+
The MCP tools and slash commands (`/codex`, `/gemini`, `/multi`, `/ask-codex`, `/ask-gemini`, `/ask-multi`) are automatically installed.
|
|
15
15
|
|
|
16
16
|
**Manual command install** (if needed):
|
|
17
17
|
```bash
|
|
@@ -51,11 +51,16 @@ gemini # follow auth prompts
|
|
|
51
51
|
|
|
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
55
|
- `/codex` or "review with codex" - Get external Codex review
|
|
56
56
|
- `/gemini` or "review with gemini" - Get external Gemini review
|
|
57
57
|
- `/multi` - Get parallel reviews from both CLIs
|
|
58
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
|
|
63
|
+
|
|
59
64
|
**For regular reviews:** Just say "review" and Claude Code will use its native capabilities. These external tools are only invoked when explicitly requested.
|
|
60
65
|
|
|
61
66
|
## Slash Commands
|
|
@@ -63,14 +68,18 @@ These tools provide **external second-opinion reviews** from Codex and Gemini CL
|
|
|
63
68
|
These commands are available after restart:
|
|
64
69
|
|
|
65
70
|
```bash
|
|
71
|
+
# Review tools
|
|
66
72
|
/codex # Review with Codex
|
|
67
73
|
/codex security # Focus on security
|
|
68
74
|
/codex-xhigh # Codex with xhigh reasoning effort
|
|
69
|
-
|
|
70
75
|
/gemini # Review with Gemini
|
|
71
76
|
/gemini architecture # Focus on architecture
|
|
72
|
-
|
|
73
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
|
|
74
83
|
```
|
|
75
84
|
|
|
76
85
|
## How It Works
|
|
@@ -99,23 +108,33 @@ CC does work → User: /codex → External CLI reviews → CC synthesizes → Up
|
|
|
99
108
|
|
|
100
109
|
## MCP Tools
|
|
101
110
|
|
|
102
|
-
The plugin exposes
|
|
111
|
+
The plugin exposes six MCP tools:
|
|
103
112
|
|
|
104
113
|
| Tool | Description |
|
|
105
114
|
|------|-------------|
|
|
106
115
|
| `codex_review` | Get Codex review (correctness, edge cases, performance) |
|
|
107
116
|
| `gemini_review` | Get Gemini review (design patterns, scalability, tech debt) |
|
|
108
117
|
| `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 |
|
|
109
121
|
|
|
110
122
|
## Output Format
|
|
111
123
|
|
|
112
|
-
|
|
124
|
+
**Review tools** return structured JSON feedback with:
|
|
113
125
|
- **Findings**: Issues with severity, confidence, location, and suggestions
|
|
114
126
|
- **Agreements**: Validations of CC's correct assessments
|
|
115
127
|
- **Disagreements**: Challenges to CC's claims with corrections
|
|
116
128
|
- **Alternatives**: Different approaches with tradeoffs
|
|
117
129
|
- **Risk Assessment**: Overall risk level with top concerns
|
|
118
130
|
|
|
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
|
+
|
|
119
138
|
## Development
|
|
120
139
|
|
|
121
140
|
```bash
|
|
@@ -130,9 +149,18 @@ npm start # Run server
|
|
|
130
149
|
|
|
131
150
|
## Publishing
|
|
132
151
|
|
|
133
|
-
|
|
152
|
+
Release-based publish via npm Trusted Publishing (OIDC, no tokens needed).
|
|
153
|
+
CI triggers on GitHub Release, validates the tag matches `package.json`.
|
|
154
|
+
|
|
134
155
|
```bash
|
|
135
|
-
|
|
156
|
+
# 1. Bump version in package.json
|
|
157
|
+
# 2. Rebuild and test
|
|
158
|
+
npm run build && npm test
|
|
159
|
+
# 3. Commit, tag, push, release
|
|
160
|
+
git add -A && git commit -m "v1.x.x"
|
|
161
|
+
git tag v1.x.x
|
|
162
|
+
git push && git push --tags
|
|
163
|
+
gh release create v1.x.x --title "v1.x.x" --generate-notes
|
|
136
164
|
```
|
|
137
165
|
|
|
138
166
|
## License
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Ask Codex
|
|
2
|
+
|
|
3
|
+
Ask OpenAI Codex CLI for help as a peer engineer.
|
|
4
|
+
|
|
5
|
+
## Arguments
|
|
6
|
+
- `$ARGUMENTS` - Your question or request
|
|
7
|
+
|
|
8
|
+
## Codex Strengths
|
|
9
|
+
- **Correctness**: Logic errors, edge cases, bugs
|
|
10
|
+
- **Performance**: Efficiency, complexity analysis
|
|
11
|
+
- **Security**: Vulnerability detection
|
|
12
|
+
|
|
13
|
+
## Tool Invocation
|
|
14
|
+
|
|
15
|
+
Call `ask_codex` with:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"workingDir": "<current directory>",
|
|
20
|
+
"prompt": "<your question or request from $ARGUMENTS>",
|
|
21
|
+
"taskType": "<infer from request: plan|debug|explain|question|fix|explore|general>",
|
|
22
|
+
"relevantFiles": ["<files related to the question>"],
|
|
23
|
+
"context": "<any error messages or prior analysis>"
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## After Receiving Response
|
|
28
|
+
|
|
29
|
+
1. **Read the answer** and key points
|
|
30
|
+
2. **Check file references** — verify they exist
|
|
31
|
+
3. **Evaluate suggested actions** — do they make sense?
|
|
32
|
+
4. **Apply your judgment** — you may disagree
|
|
33
|
+
5. **Act on the suggestions** or ask follow-up questions
|
|
34
|
+
|
|
35
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Ask Gemini
|
|
2
|
+
|
|
3
|
+
Ask Google Gemini CLI for help as a peer engineer.
|
|
4
|
+
|
|
5
|
+
## Arguments
|
|
6
|
+
- `$ARGUMENTS` - Your question or request
|
|
7
|
+
|
|
8
|
+
## Gemini Strengths
|
|
9
|
+
- **Architecture**: Design patterns, structure
|
|
10
|
+
- **Scalability**: Load handling, bottlenecks
|
|
11
|
+
- **Maintainability**: Code clarity, tech debt
|
|
12
|
+
|
|
13
|
+
## Tool Invocation
|
|
14
|
+
|
|
15
|
+
Call `ask_gemini` with:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"workingDir": "<current directory>",
|
|
20
|
+
"prompt": "<your question or request from $ARGUMENTS>",
|
|
21
|
+
"taskType": "<infer from request: plan|debug|explain|question|fix|explore|general>",
|
|
22
|
+
"relevantFiles": ["<files related to the question>"],
|
|
23
|
+
"context": "<any error messages or prior analysis>"
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## After Receiving Response
|
|
28
|
+
|
|
29
|
+
1. **Read the answer** and key points
|
|
30
|
+
2. **Check file references** — verify they exist
|
|
31
|
+
3. **Evaluate suggested actions** — do they make sense?
|
|
32
|
+
4. **Apply your judgment** — you may disagree
|
|
33
|
+
5. **Act on the suggestions** or ask follow-up questions
|
|
34
|
+
|
|
35
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Ask Multi
|
|
2
|
+
|
|
3
|
+
Ask both Codex and Gemini for help in parallel — get multiple perspectives.
|
|
4
|
+
|
|
5
|
+
## Arguments
|
|
6
|
+
- `$ARGUMENTS` - Your question or request
|
|
7
|
+
|
|
8
|
+
## When to Use
|
|
9
|
+
|
|
10
|
+
Use `/ask-multi` when you want perspectives from both models on the same question.
|
|
11
|
+
|
|
12
|
+
## Tool Invocation
|
|
13
|
+
|
|
14
|
+
Call `ask_multi` with:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"workingDir": "<current directory>",
|
|
19
|
+
"prompt": "<your question or request from $ARGUMENTS>",
|
|
20
|
+
"taskType": "<infer from request: plan|debug|explain|question|fix|explore|general>",
|
|
21
|
+
"relevantFiles": ["<files related to the question>"],
|
|
22
|
+
"context": "<any error messages or prior analysis>"
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## After Receiving Responses
|
|
27
|
+
|
|
28
|
+
You will receive separate responses from each model.
|
|
29
|
+
|
|
30
|
+
### Synthesize
|
|
31
|
+
|
|
32
|
+
1. **Find agreements** — both models say the same thing (higher confidence)
|
|
33
|
+
2. **Identify conflicts** — they disagree (YOU decide who's right)
|
|
34
|
+
3. **Note unique insights** — findings only one model provided
|
|
35
|
+
4. **Verify file references** — check they exist
|
|
36
|
+
5. **Make YOUR recommendation** — don't just relay, apply judgment
|
|
37
|
+
|
|
38
|
+
$ARGUMENTS
|
package/dist/adapters/base.d.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* Makes it easy to add new models (Ollama, Azure, etc.) without
|
|
6
6
|
* changing the core orchestration logic.
|
|
7
7
|
*/
|
|
8
|
-
import { ReviewOutput } from '../schema.js';
|
|
9
|
-
import { FocusArea, OutputType, ReasoningEffort } from '../types.js';
|
|
8
|
+
import { ReviewOutput, PeerOutput } from '../schema.js';
|
|
9
|
+
import { FocusArea, OutputType, ReasoningEffort, TaskType } from '../types.js';
|
|
10
10
|
export interface ReviewerCapabilities {
|
|
11
11
|
/** Display name for this reviewer */
|
|
12
12
|
name: string;
|
|
@@ -43,6 +43,24 @@ export interface ReviewRequest {
|
|
|
43
43
|
/** Expert role configuration (optional override) */
|
|
44
44
|
expertRole?: ExpertRole;
|
|
45
45
|
}
|
|
46
|
+
export interface PeerRequest {
|
|
47
|
+
/** Working directory containing the code */
|
|
48
|
+
workingDir: string;
|
|
49
|
+
/** The question or request from CC */
|
|
50
|
+
prompt: string;
|
|
51
|
+
/** Hint about the type of task */
|
|
52
|
+
taskType?: TaskType;
|
|
53
|
+
/** Files the peer should focus on */
|
|
54
|
+
relevantFiles?: string[];
|
|
55
|
+
/** Additional context (error messages, prior analysis) */
|
|
56
|
+
context?: string;
|
|
57
|
+
/** Areas to focus on */
|
|
58
|
+
focusAreas?: FocusArea[];
|
|
59
|
+
/** Custom instructions from the user */
|
|
60
|
+
customPrompt?: string;
|
|
61
|
+
/** Reasoning effort level (for models that support it) */
|
|
62
|
+
reasoningEffort?: ReasoningEffort;
|
|
63
|
+
}
|
|
46
64
|
export interface ExpertRole {
|
|
47
65
|
name: string;
|
|
48
66
|
description: string;
|
|
@@ -74,6 +92,20 @@ export interface ReviewError {
|
|
|
74
92
|
message: string;
|
|
75
93
|
details?: Record<string, unknown>;
|
|
76
94
|
}
|
|
95
|
+
export interface PeerSuccess {
|
|
96
|
+
success: true;
|
|
97
|
+
output: PeerOutput;
|
|
98
|
+
rawOutput?: string;
|
|
99
|
+
executionTimeMs: number;
|
|
100
|
+
}
|
|
101
|
+
export interface PeerFailure {
|
|
102
|
+
success: false;
|
|
103
|
+
error: ReviewError;
|
|
104
|
+
suggestion?: string;
|
|
105
|
+
rawOutput?: string;
|
|
106
|
+
executionTimeMs: number;
|
|
107
|
+
}
|
|
108
|
+
export type PeerResult = PeerSuccess | PeerFailure;
|
|
77
109
|
/**
|
|
78
110
|
* Base interface that all reviewer adapters must implement.
|
|
79
111
|
* This allows easy addition of new AI CLIs without changing orchestration logic.
|
|
@@ -87,6 +119,8 @@ export interface ReviewerAdapter {
|
|
|
87
119
|
isAvailable(): Promise<boolean>;
|
|
88
120
|
/** Run a review and return structured output */
|
|
89
121
|
runReview(request: ReviewRequest): Promise<ReviewResult>;
|
|
122
|
+
/** Run a general-purpose peer request and return structured output */
|
|
123
|
+
runPeerRequest(request: PeerRequest): Promise<PeerResult>;
|
|
90
124
|
/**
|
|
91
125
|
* Optional: Run peer review of another model's output
|
|
92
126
|
* Future capability - not currently implemented by any adapter
|
package/dist/adapters/codex.d.ts
CHANGED
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
* Implements the ReviewerAdapter interface for OpenAI's Codex CLI.
|
|
5
5
|
* Specializes in correctness, edge cases, and performance analysis.
|
|
6
6
|
*/
|
|
7
|
-
import { ReviewerAdapter, ReviewerCapabilities, ReviewRequest, ReviewResult } from './base.js';
|
|
7
|
+
import { ReviewerAdapter, ReviewerCapabilities, ReviewRequest, ReviewResult, PeerRequest, PeerResult } from './base.js';
|
|
8
8
|
export declare class CodexAdapter implements ReviewerAdapter {
|
|
9
9
|
readonly id = "codex";
|
|
10
10
|
getCapabilities(): ReviewerCapabilities;
|
|
11
11
|
isAvailable(): Promise<boolean>;
|
|
12
12
|
runReview(request: ReviewRequest): Promise<ReviewResult>;
|
|
13
13
|
private runWithRetry;
|
|
14
|
+
runPeerRequest(request: PeerRequest): Promise<PeerResult>;
|
|
15
|
+
private runPeerWithRetry;
|
|
14
16
|
private runCli;
|
|
15
17
|
private categorizeError;
|
|
16
18
|
private getSuggestion;
|
package/dist/adapters/codex.js
CHANGED
|
@@ -9,8 +9,8 @@ import { existsSync, writeFileSync, unlinkSync, mkdtempSync } from 'fs';
|
|
|
9
9
|
import { tmpdir } from 'os';
|
|
10
10
|
import { join } from 'path';
|
|
11
11
|
import { registerAdapter, } from './base.js';
|
|
12
|
-
import { parseReviewOutput, parseLegacyMarkdownOutput, getReviewOutputJsonSchema } from '../schema.js';
|
|
13
|
-
import { buildSimpleHandoff, buildHandoffPrompt, selectRole, } from '../handoff.js';
|
|
12
|
+
import { parseReviewOutput, parseLegacyMarkdownOutput, getReviewOutputJsonSchema, getPeerOutputJsonSchema, parsePeerOutput } from '../schema.js';
|
|
13
|
+
import { buildSimpleHandoff, buildHandoffPrompt, buildPeerPrompt, selectRole, } from '../handoff.js';
|
|
14
14
|
// =============================================================================
|
|
15
15
|
// CONFIGURATION
|
|
16
16
|
// =============================================================================
|
|
@@ -89,7 +89,7 @@ export class CodexAdapter {
|
|
|
89
89
|
(previousOutput ? `\nPrevious output (for reference):\n${previousOutput.slice(0, 500)}...` : '');
|
|
90
90
|
}
|
|
91
91
|
// Run the CLI
|
|
92
|
-
const result = await this.runCli(prompt, request.workingDir, request.reasoningEffort || 'high');
|
|
92
|
+
const result = await this.runCli(prompt, request.workingDir, request.reasoningEffort || 'high', getReviewOutputJsonSchema);
|
|
93
93
|
// Handle CLI errors
|
|
94
94
|
if (result.exitCode !== 0) {
|
|
95
95
|
const error = this.categorizeError(result.stderr);
|
|
@@ -214,14 +214,105 @@ export class CodexAdapter {
|
|
|
214
214
|
};
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
|
-
|
|
217
|
+
async runPeerRequest(request) {
|
|
218
|
+
const startTime = Date.now();
|
|
219
|
+
if (!existsSync(request.workingDir)) {
|
|
220
|
+
return {
|
|
221
|
+
success: false,
|
|
222
|
+
error: {
|
|
223
|
+
type: 'cli_error',
|
|
224
|
+
message: `Working directory does not exist: ${request.workingDir}`,
|
|
225
|
+
},
|
|
226
|
+
suggestion: 'Check that the working directory path is correct',
|
|
227
|
+
executionTimeMs: Date.now() - startTime,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
return this.runPeerWithRetry(request, 0, startTime);
|
|
231
|
+
}
|
|
232
|
+
async runPeerWithRetry(request, attempt, startTime, previousError, previousOutput) {
|
|
233
|
+
try {
|
|
234
|
+
let prompt = buildPeerPrompt({
|
|
235
|
+
workingDir: request.workingDir,
|
|
236
|
+
prompt: request.prompt,
|
|
237
|
+
taskType: request.taskType,
|
|
238
|
+
relevantFiles: request.relevantFiles,
|
|
239
|
+
context: request.context,
|
|
240
|
+
focusAreas: request.focusAreas,
|
|
241
|
+
customInstructions: request.customPrompt,
|
|
242
|
+
outputFormat: 'json',
|
|
243
|
+
});
|
|
244
|
+
if (attempt > 0) {
|
|
245
|
+
prompt += `\n\n---\n\n# RETRY ATTEMPT ${attempt + 1}\n\n` +
|
|
246
|
+
`Previous output had issues: ${previousError}\n` +
|
|
247
|
+
`Please fix these issues and provide valid JSON output.\n` +
|
|
248
|
+
(previousOutput ? `\nPrevious output (for reference):\n${previousOutput.slice(0, 500)}...` : '');
|
|
249
|
+
}
|
|
250
|
+
const result = await this.runCli(prompt, request.workingDir, request.reasoningEffort || 'high', getPeerOutputJsonSchema);
|
|
251
|
+
if (result.exitCode !== 0) {
|
|
252
|
+
const error = this.categorizeError(result.stderr);
|
|
253
|
+
return {
|
|
254
|
+
success: false,
|
|
255
|
+
error,
|
|
256
|
+
suggestion: this.getSuggestion(error),
|
|
257
|
+
rawOutput: result.stderr,
|
|
258
|
+
executionTimeMs: Date.now() - startTime,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
if (result.truncated) {
|
|
262
|
+
return {
|
|
263
|
+
success: false,
|
|
264
|
+
error: { type: 'cli_error', message: 'Output exceeded maximum buffer size (1MB)' },
|
|
265
|
+
suggestion: 'Try a more focused request',
|
|
266
|
+
executionTimeMs: Date.now() - startTime,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
const output = parsePeerOutput(result.stdout);
|
|
270
|
+
if (!output) {
|
|
271
|
+
if (attempt < MAX_RETRIES) {
|
|
272
|
+
return this.runPeerWithRetry(request, attempt + 1, startTime, 'Output did not match expected JSON schema', result.stdout);
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
success: false,
|
|
276
|
+
error: { type: 'parse_error', message: 'Failed to parse peer output after retries',
|
|
277
|
+
details: { rawOutput: result.stdout.slice(0, 1000) } },
|
|
278
|
+
suggestion: 'The model may not be following the output format.',
|
|
279
|
+
rawOutput: result.stdout,
|
|
280
|
+
executionTimeMs: Date.now() - startTime,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
success: true,
|
|
285
|
+
output,
|
|
286
|
+
rawOutput: result.stdout,
|
|
287
|
+
executionTimeMs: Date.now() - startTime,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
const err = error;
|
|
292
|
+
if (err.code === 'ENOENT') {
|
|
293
|
+
return { success: false, error: { type: 'cli_not_found', message: 'Codex CLI not found' },
|
|
294
|
+
suggestion: 'Install with: npm install -g @openai/codex', executionTimeMs: Date.now() - startTime };
|
|
295
|
+
}
|
|
296
|
+
if (err.message === 'TIMEOUT') {
|
|
297
|
+
return { success: false, error: { type: 'timeout', message: 'No output for 2 minutes' },
|
|
298
|
+
suggestion: 'Try a simpler request', executionTimeMs: Date.now() - startTime };
|
|
299
|
+
}
|
|
300
|
+
if (err.message === 'MAX_TIMEOUT') {
|
|
301
|
+
return { success: false, error: { type: 'timeout', message: 'Task exceeded 60 minute maximum' },
|
|
302
|
+
suggestion: 'Try a smaller scope', executionTimeMs: Date.now() - startTime };
|
|
303
|
+
}
|
|
304
|
+
return { success: false, error: { type: 'cli_error', message: err.message },
|
|
305
|
+
executionTimeMs: Date.now() - startTime };
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
runCli(prompt, workingDir, reasoningEffort, schemaGetter) {
|
|
218
309
|
return new Promise((resolve, reject) => {
|
|
219
310
|
// Create temp schema file for structured output
|
|
220
311
|
let schemaFile = null;
|
|
221
312
|
try {
|
|
222
313
|
const tempDir = mkdtempSync(join(tmpdir(), 'codex-schema-'));
|
|
223
314
|
schemaFile = join(tempDir, 'schema.json');
|
|
224
|
-
const schema =
|
|
315
|
+
const schema = schemaGetter();
|
|
225
316
|
writeFileSync(schemaFile, JSON.stringify(schema, null, 2), 'utf-8');
|
|
226
317
|
}
|
|
227
318
|
catch (err) {
|
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
* Implements the ReviewerAdapter interface for Google's Gemini CLI.
|
|
5
5
|
* Specializes in architecture, design patterns, and large-context analysis.
|
|
6
6
|
*/
|
|
7
|
-
import { ReviewerAdapter, ReviewerCapabilities, ReviewRequest, ReviewResult } from './base.js';
|
|
7
|
+
import { ReviewerAdapter, ReviewerCapabilities, ReviewRequest, ReviewResult, PeerRequest, PeerResult } from './base.js';
|
|
8
8
|
export declare class GeminiAdapter implements ReviewerAdapter {
|
|
9
9
|
readonly id = "gemini";
|
|
10
10
|
getCapabilities(): ReviewerCapabilities;
|
|
11
11
|
isAvailable(): Promise<boolean>;
|
|
12
12
|
runReview(request: ReviewRequest): Promise<ReviewResult>;
|
|
13
13
|
private runWithRetry;
|
|
14
|
+
runPeerRequest(request: PeerRequest): Promise<PeerResult>;
|
|
15
|
+
private runPeerWithRetry;
|
|
14
16
|
private runCli;
|
|
15
17
|
private categorizeError;
|
|
16
18
|
private getSuggestion;
|
package/dist/adapters/gemini.js
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
import { spawn } from 'child_process';
|
|
8
8
|
import { existsSync } from 'fs';
|
|
9
9
|
import { registerAdapter, } from './base.js';
|
|
10
|
-
import { parseReviewOutput, parseLegacyMarkdownOutput } from '../schema.js';
|
|
11
|
-
import { buildSimpleHandoff, buildHandoffPrompt, selectRole, } from '../handoff.js';
|
|
10
|
+
import { parseReviewOutput, parseLegacyMarkdownOutput, parsePeerOutput } from '../schema.js';
|
|
11
|
+
import { buildSimpleHandoff, buildHandoffPrompt, buildPeerPrompt, selectRole, } from '../handoff.js';
|
|
12
12
|
// =============================================================================
|
|
13
13
|
// CONFIGURATION
|
|
14
14
|
// =============================================================================
|
|
@@ -212,6 +212,92 @@ export class GeminiAdapter {
|
|
|
212
212
|
};
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
|
+
async runPeerRequest(request) {
|
|
216
|
+
const startTime = Date.now();
|
|
217
|
+
if (!existsSync(request.workingDir)) {
|
|
218
|
+
return {
|
|
219
|
+
success: false,
|
|
220
|
+
error: { type: 'cli_error', message: `Working directory does not exist: ${request.workingDir}` },
|
|
221
|
+
suggestion: 'Check that the working directory path is correct',
|
|
222
|
+
executionTimeMs: Date.now() - startTime,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
return this.runPeerWithRetry(request, 0, startTime);
|
|
226
|
+
}
|
|
227
|
+
async runPeerWithRetry(request, attempt, startTime, previousError, previousOutput) {
|
|
228
|
+
try {
|
|
229
|
+
let prompt = buildPeerPrompt({
|
|
230
|
+
workingDir: request.workingDir,
|
|
231
|
+
prompt: request.prompt,
|
|
232
|
+
taskType: request.taskType,
|
|
233
|
+
relevantFiles: request.relevantFiles,
|
|
234
|
+
context: request.context,
|
|
235
|
+
focusAreas: request.focusAreas,
|
|
236
|
+
customInstructions: request.customPrompt,
|
|
237
|
+
outputFormat: 'json',
|
|
238
|
+
});
|
|
239
|
+
if (attempt > 0) {
|
|
240
|
+
prompt += `\n\n---\n\n# RETRY ATTEMPT ${attempt + 1}\n\n` +
|
|
241
|
+
`Previous output had issues: ${previousError}\n` +
|
|
242
|
+
`Please fix these issues and provide valid JSON output.\n` +
|
|
243
|
+
(previousOutput ? `\nPrevious output (for reference):\n${previousOutput.slice(0, 500)}...` : '');
|
|
244
|
+
}
|
|
245
|
+
const result = await this.runCli(prompt, request.workingDir);
|
|
246
|
+
if (result.exitCode !== 0) {
|
|
247
|
+
const error = this.categorizeError(result.stderr);
|
|
248
|
+
return {
|
|
249
|
+
success: false, error,
|
|
250
|
+
suggestion: this.getSuggestion(error),
|
|
251
|
+
rawOutput: result.stderr,
|
|
252
|
+
executionTimeMs: Date.now() - startTime,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
if (result.truncated) {
|
|
256
|
+
return {
|
|
257
|
+
success: false,
|
|
258
|
+
error: { type: 'cli_error', message: 'Output exceeded maximum buffer size (1MB)' },
|
|
259
|
+
suggestion: 'Try a more focused request',
|
|
260
|
+
executionTimeMs: Date.now() - startTime,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
const output = parsePeerOutput(result.stdout);
|
|
264
|
+
if (!output) {
|
|
265
|
+
if (attempt < MAX_RETRIES) {
|
|
266
|
+
return this.runPeerWithRetry(request, attempt + 1, startTime, 'Output did not match expected JSON schema', result.stdout);
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
success: false,
|
|
270
|
+
error: { type: 'parse_error', message: 'Failed to parse peer output after retries',
|
|
271
|
+
details: { rawOutput: result.stdout.slice(0, 1000) } },
|
|
272
|
+
suggestion: 'The model may not be following the output format.',
|
|
273
|
+
rawOutput: result.stdout,
|
|
274
|
+
executionTimeMs: Date.now() - startTime,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
success: true, output,
|
|
279
|
+
rawOutput: result.stdout,
|
|
280
|
+
executionTimeMs: Date.now() - startTime,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
const err = error;
|
|
285
|
+
if (err.code === 'ENOENT') {
|
|
286
|
+
return { success: false, error: { type: 'cli_not_found', message: 'Gemini CLI not found' },
|
|
287
|
+
suggestion: 'Install with: npm install -g @google/gemini-cli', executionTimeMs: Date.now() - startTime };
|
|
288
|
+
}
|
|
289
|
+
if (err.message === 'TIMEOUT') {
|
|
290
|
+
return { success: false, error: { type: 'timeout', message: 'No output for 10 minutes' },
|
|
291
|
+
suggestion: 'Try a simpler request', executionTimeMs: Date.now() - startTime };
|
|
292
|
+
}
|
|
293
|
+
if (err.message === 'MAX_TIMEOUT') {
|
|
294
|
+
return { success: false, error: { type: 'timeout', message: 'Task exceeded 60 minute maximum' },
|
|
295
|
+
suggestion: 'Try a smaller scope', executionTimeMs: Date.now() - startTime };
|
|
296
|
+
}
|
|
297
|
+
return { success: false, error: { type: 'cli_error', message: err.message },
|
|
298
|
+
executionTimeMs: Date.now() - startTime };
|
|
299
|
+
}
|
|
300
|
+
}
|
|
215
301
|
runCli(prompt, workingDir) {
|
|
216
302
|
return new Promise((resolve, reject) => {
|
|
217
303
|
// Gemini CLI uses --yolo for auto-approval, prompt passed via stdin
|
package/dist/handoff.d.ts
CHANGED
|
@@ -230,3 +230,18 @@ export declare function buildSimpleHandoff(workingDir: string, ccOutput: string,
|
|
|
230
230
|
* CC should call this to add its specific concerns
|
|
231
231
|
*/
|
|
232
232
|
export declare function enhanceHandoff(handoff: Handoff, uncertainties?: Uncertainty[], questions?: Question[], decisions?: Decision[]): Handoff;
|
|
233
|
+
export interface PeerPromptOptions {
|
|
234
|
+
workingDir: string;
|
|
235
|
+
prompt: string;
|
|
236
|
+
taskType?: string;
|
|
237
|
+
relevantFiles?: string[];
|
|
238
|
+
context?: string;
|
|
239
|
+
focusAreas?: FocusArea[];
|
|
240
|
+
customInstructions?: string;
|
|
241
|
+
outputFormat: 'json';
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Build a prompt for general-purpose peer assistance (not review).
|
|
245
|
+
* The peer acts as a collaborative coworker, not a critic.
|
|
246
|
+
*/
|
|
247
|
+
export declare function buildPeerPrompt(options: PeerPromptOptions): string;
|