cc-reviewer 4.0.0 → 5.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.
- package/commands/multi-review.md +28 -16
- package/dist/adapters/base.d.ts +2 -0
- package/dist/adapters/claude.js +18 -10
- package/dist/adapters/codex.js +18 -11
- package/dist/adapters/gemini.js +11 -7
- package/dist/commands.js +1 -0
- package/dist/handoff.d.ts +10 -2
- package/dist/handoff.js +127 -5
- package/dist/prompt.js +1 -0
- package/dist/tools/feedback.js +23 -9
- package/package.json +1 -1
package/commands/multi-review.md
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
# Multi Review
|
|
2
2
|
|
|
3
|
-
Get parallel reviews from Codex, Gemini,
|
|
3
|
+
Get parallel standard AND adversarial reviews from all available models (Codex, Gemini, Claude Opus).
|
|
4
|
+
|
|
5
|
+
Each model runs twice: once as a standard reviewer (finding bugs, issues, improvements) and once as an adversarial challenger (breaking confidence in the change, questioning assumptions, targeting hidden failure paths). Results are presented in two sections.
|
|
6
|
+
|
|
7
|
+
Use `$ARGUMENTS` to steer the adversarial focus (e.g., "focus the challenge on race conditions and rollback safety").
|
|
4
8
|
|
|
5
9
|
## Arguments
|
|
6
|
-
- `$ARGUMENTS` - Optional: focus area or
|
|
10
|
+
- `$ARGUMENTS` - Optional: focus area, custom instructions, or adversarial steering
|
|
7
11
|
|
|
8
12
|
## When to Use
|
|
9
13
|
|
|
10
|
-
Use `/multi-review` when you want parallel reviews from
|
|
14
|
+
Use `/multi-review` when you want thorough parallel reviews from all available models. Every invocation includes both standard and adversarial passes.
|
|
15
|
+
|
|
16
|
+
## Examples
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
/multi-review
|
|
20
|
+
/multi-review focus the challenge on race conditions and rollback safety
|
|
21
|
+
/multi-review challenge whether this was the right caching and retry design
|
|
22
|
+
```
|
|
11
23
|
|
|
12
24
|
## Before Calling - PREPARE THE HANDOFF
|
|
13
25
|
|
|
@@ -22,7 +34,6 @@ Added cache invalidation on product updates."
|
|
|
22
34
|
UNCERTAINTIES:
|
|
23
35
|
- "Is the cache TTL appropriate for this data?"
|
|
24
36
|
- "Does the invalidation handle all update scenarios?"
|
|
25
|
-
- "Is the Redis connection pooling configured correctly?"
|
|
26
37
|
```
|
|
27
38
|
|
|
28
39
|
### 3. Ask Specific Questions
|
|
@@ -42,7 +53,7 @@ Call `multi_review` with:
|
|
|
42
53
|
"ccOutput": "<structured handoff>",
|
|
43
54
|
"outputType": "analysis",
|
|
44
55
|
"focusAreas": ["<from $ARGUMENTS>"],
|
|
45
|
-
"
|
|
56
|
+
"customPrompt": "<steering text from $ARGUMENTS for adversarial focus>"
|
|
46
57
|
}
|
|
47
58
|
```
|
|
48
59
|
|
|
@@ -70,21 +81,22 @@ PRIORITY FILES:
|
|
|
70
81
|
|
|
71
82
|
## After Receiving Review
|
|
72
83
|
|
|
73
|
-
You will receive
|
|
84
|
+
You will receive two sections: **Standard Review Findings** and **Challenge Review Findings**.
|
|
74
85
|
|
|
75
|
-
### Synthesize
|
|
86
|
+
### Synthesize
|
|
76
87
|
|
|
77
|
-
1. **
|
|
78
|
-
-
|
|
79
|
-
-
|
|
88
|
+
1. **Standard findings** — bugs, issues, improvements from each model
|
|
89
|
+
- Find agreements across models (higher confidence)
|
|
90
|
+
- Identify conflicts (YOU decide who's right)
|
|
80
91
|
|
|
81
|
-
2. **
|
|
82
|
-
-
|
|
83
|
-
-
|
|
92
|
+
2. **Challenge findings** — adversarial challenges from each model
|
|
93
|
+
- These target assumptions and design decisions, not just bugs
|
|
94
|
+
- Evaluate on merit — some challenges are speculative by design
|
|
95
|
+
- Strong challenges with evidence deserve serious consideration
|
|
84
96
|
|
|
85
|
-
3. **
|
|
86
|
-
-
|
|
87
|
-
-
|
|
97
|
+
3. **Cross-reference** standard vs challenge findings
|
|
98
|
+
- Standard + challenge agreement = high confidence issue
|
|
99
|
+
- Challenge-only finding = investigate further before acting
|
|
88
100
|
|
|
89
101
|
4. **Verify all findings**
|
|
90
102
|
- Check file/line references exist
|
package/dist/adapters/base.d.ts
CHANGED
|
@@ -41,6 +41,8 @@ export interface ReviewRequest {
|
|
|
41
41
|
reasoningEffort?: ReasoningEffort;
|
|
42
42
|
/** Service tier (for models that support it: priority = fast, flex = cheap) */
|
|
43
43
|
serviceTier?: ServiceTier;
|
|
44
|
+
/** Review mode: standard finds bugs, adversarial challenges assumptions */
|
|
45
|
+
reviewMode?: 'standard' | 'adversarial';
|
|
44
46
|
}
|
|
45
47
|
/** @deprecated Use handoff.ts roles instead */
|
|
46
48
|
export interface ExpertRole {
|
package/dist/adapters/claude.js
CHANGED
|
@@ -15,7 +15,7 @@ import { existsSync } from 'fs';
|
|
|
15
15
|
import { registerAdapter, } from './base.js';
|
|
16
16
|
import { CliExecutor } from '../executor.js';
|
|
17
17
|
import { ClaudeEventDecoder } from '../decoders/index.js';
|
|
18
|
-
import { buildSimpleHandoff, buildHandoffPrompt, selectRole, } from '../handoff.js';
|
|
18
|
+
import { buildSimpleHandoff, buildHandoffPrompt, buildAdversarialHandoffPrompt, selectRole, } from '../handoff.js';
|
|
19
19
|
// =============================================================================
|
|
20
20
|
// CONFIGURATION
|
|
21
21
|
// =============================================================================
|
|
@@ -63,8 +63,9 @@ export class ClaudeAdapter {
|
|
|
63
63
|
}
|
|
64
64
|
try {
|
|
65
65
|
const handoff = buildSimpleHandoff(request.workingDir, request.ccOutput, request.analyzedFiles, request.focusAreas, request.customPrompt);
|
|
66
|
-
const
|
|
67
|
-
|
|
66
|
+
const prompt = request.reviewMode === 'adversarial'
|
|
67
|
+
? buildAdversarialHandoffPrompt({ handoff })
|
|
68
|
+
: buildHandoffPrompt({ handoff, role: selectRole(request.focusAreas) });
|
|
68
69
|
const result = await this.runCli(prompt, request.workingDir);
|
|
69
70
|
if (result.exitCode !== 0) {
|
|
70
71
|
const error = this.categorizeError(result.stderr);
|
|
@@ -88,6 +89,7 @@ export class ClaudeAdapter {
|
|
|
88
89
|
const args = [
|
|
89
90
|
'-p', // Non-interactive, print and exit
|
|
90
91
|
'--model', 'opus', // Use Opus
|
|
92
|
+
'--bare', // Skip hooks, plugins, CLAUDE.md, auto-memory
|
|
91
93
|
'--permission-mode', 'plan', // Read-only enforcement (layer 1)
|
|
92
94
|
'--verbose', // Required for stream-json
|
|
93
95
|
'--output-format', 'stream-json', // Structured streaming events
|
|
@@ -122,14 +124,20 @@ export class ClaudeAdapter {
|
|
|
122
124
|
// Check for errors captured from stream events
|
|
123
125
|
const decoderError = decoder.getError();
|
|
124
126
|
if (decoderError) {
|
|
125
|
-
|
|
127
|
+
const combined = result.stderr ? `${decoderError}\n\nCLI stderr: ${result.stderr}` : decoderError;
|
|
128
|
+
return { stdout: '', stderr: combined, exitCode: 1, truncated: false };
|
|
126
129
|
}
|
|
127
130
|
const finalResponse = decoder.getFinalResponse();
|
|
128
131
|
if (!finalResponse && decoder.hasNoOutput()) {
|
|
129
|
-
|
|
132
|
+
const combined = result.stderr ? `No output from Claude\n\nCLI stderr: ${result.stderr}` : 'No output from Claude';
|
|
133
|
+
return { stdout: '', stderr: combined, exitCode: 1, truncated: false };
|
|
134
|
+
}
|
|
135
|
+
if (!finalResponse) {
|
|
136
|
+
const combined = result.stderr ? `No result event from Claude\n\nCLI stderr: ${result.stderr}` : 'No result event from Claude';
|
|
137
|
+
return { stdout: '', stderr: combined, exitCode: 1, truncated: false };
|
|
130
138
|
}
|
|
131
139
|
return {
|
|
132
|
-
stdout: finalResponse
|
|
140
|
+
stdout: finalResponse,
|
|
133
141
|
stderr: result.stderr,
|
|
134
142
|
exitCode: result.exitCode,
|
|
135
143
|
truncated: result.truncated,
|
|
@@ -153,13 +161,13 @@ export class ClaudeAdapter {
|
|
|
153
161
|
}
|
|
154
162
|
categorizeError(stderr) {
|
|
155
163
|
const lower = stderr.toLowerCase();
|
|
156
|
-
if (lower.includes('rate limit') || lower.includes('
|
|
157
|
-
return { type: 'rate_limit', message:
|
|
164
|
+
if (lower.includes('rate limit') || lower.includes('rate_limit') || lower.includes('quota')) {
|
|
165
|
+
return { type: 'rate_limit', message: `Claude rate limit: ${stderr.slice(0, 500)}` };
|
|
158
166
|
}
|
|
159
167
|
if (lower.includes('unauthorized') || lower.includes('authentication') || lower.includes('api key') || stderr.includes('401') || stderr.includes('403')) {
|
|
160
|
-
return { type: 'auth_error', message:
|
|
168
|
+
return { type: 'auth_error', message: `Authentication failed: ${stderr.slice(0, 500)}`, details: { stderr } };
|
|
161
169
|
}
|
|
162
|
-
return { type: 'cli_error', message: stderr || 'Unknown error' };
|
|
170
|
+
return { type: 'cli_error', message: stderr.slice(0, 500) || 'Unknown error' };
|
|
163
171
|
}
|
|
164
172
|
getSuggestion(error) {
|
|
165
173
|
switch (error.type) {
|
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, selectRole, } from '../handoff.js';
|
|
13
|
+
import { buildSimpleHandoff, buildHandoffPrompt, buildAdversarialHandoffPrompt, selectRole, } from '../handoff.js';
|
|
14
14
|
// =============================================================================
|
|
15
15
|
// CONFIGURATION
|
|
16
16
|
// =============================================================================
|
|
@@ -59,8 +59,9 @@ export class CodexAdapter {
|
|
|
59
59
|
}
|
|
60
60
|
try {
|
|
61
61
|
const handoff = buildSimpleHandoff(request.workingDir, request.ccOutput, request.analyzedFiles, request.focusAreas, request.customPrompt);
|
|
62
|
-
const
|
|
63
|
-
|
|
62
|
+
const prompt = request.reviewMode === 'adversarial'
|
|
63
|
+
? buildAdversarialHandoffPrompt({ handoff })
|
|
64
|
+
: buildHandoffPrompt({ handoff, role: selectRole(request.focusAreas) });
|
|
64
65
|
const result = await this.runCli(prompt, request.workingDir, request.reasoningEffort || 'high', request.serviceTier);
|
|
65
66
|
if (result.exitCode !== 0) {
|
|
66
67
|
const error = this.categorizeError(result.stderr);
|
|
@@ -123,14 +124,20 @@ export class CodexAdapter {
|
|
|
123
124
|
// Check for errors captured from JSONL events
|
|
124
125
|
const decoderError = decoder.getError();
|
|
125
126
|
if (decoderError) {
|
|
126
|
-
|
|
127
|
+
const combined = result.stderr ? `${decoderError}\n\nCLI stderr: ${result.stderr}` : decoderError;
|
|
128
|
+
return { stdout: '', stderr: combined, exitCode: 1, truncated: false };
|
|
127
129
|
}
|
|
128
130
|
const finalResponse = decoder.getFinalResponse();
|
|
129
131
|
if (!finalResponse && decoder.hasNoOutput()) {
|
|
130
|
-
|
|
132
|
+
const combined = result.stderr ? `No output from Codex\n\nCLI stderr: ${result.stderr}` : 'No output from Codex';
|
|
133
|
+
return { stdout: '', stderr: combined, exitCode: 1, truncated: false };
|
|
134
|
+
}
|
|
135
|
+
if (!finalResponse) {
|
|
136
|
+
const combined = result.stderr ? `No result event from Codex\n\nCLI stderr: ${result.stderr}` : 'No result event from Codex';
|
|
137
|
+
return { stdout: '', stderr: combined, exitCode: 1, truncated: false };
|
|
131
138
|
}
|
|
132
139
|
return {
|
|
133
|
-
stdout: finalResponse
|
|
140
|
+
stdout: finalResponse,
|
|
134
141
|
stderr: result.stderr,
|
|
135
142
|
exitCode: result.exitCode,
|
|
136
143
|
truncated: result.truncated,
|
|
@@ -154,16 +161,16 @@ export class CodexAdapter {
|
|
|
154
161
|
}
|
|
155
162
|
categorizeError(stderr) {
|
|
156
163
|
const lower = stderr.toLowerCase();
|
|
157
|
-
if (lower.includes('rate limit') || lower.includes('
|
|
158
|
-
return { type: 'rate_limit', message:
|
|
164
|
+
if (lower.includes('rate limit') || lower.includes('rate_limit')) {
|
|
165
|
+
return { type: 'rate_limit', message: `Codex rate limit: ${stderr.slice(0, 500)}` };
|
|
159
166
|
}
|
|
160
167
|
if (lower.includes('unauthorized') || lower.includes('authentication') || stderr.includes('401') || stderr.includes('403')) {
|
|
161
|
-
return { type: 'auth_error', message:
|
|
168
|
+
return { type: 'auth_error', message: `Authentication failed: ${stderr.slice(0, 500)}`, details: { stderr } };
|
|
162
169
|
}
|
|
163
170
|
if (lower.includes('invalid_json_schema') || lower.includes('invalid_request_error')) {
|
|
164
|
-
return { type: 'cli_error', message: `API error: ${stderr.slice(0,
|
|
171
|
+
return { type: 'cli_error', message: `API error: ${stderr.slice(0, 500)}` };
|
|
165
172
|
}
|
|
166
|
-
return { type: 'cli_error', message: stderr || 'Unknown error' };
|
|
173
|
+
return { type: 'cli_error', message: stderr.slice(0, 500) || 'Unknown error' };
|
|
167
174
|
}
|
|
168
175
|
getSuggestion(error) {
|
|
169
176
|
switch (error.type) {
|
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, selectRole, } from '../handoff.js';
|
|
13
|
+
import { buildSimpleHandoff, buildHandoffPrompt, buildAdversarialHandoffPrompt, selectRole, } from '../handoff.js';
|
|
14
14
|
// =============================================================================
|
|
15
15
|
// CONFIGURATION
|
|
16
16
|
// =============================================================================
|
|
@@ -56,8 +56,9 @@ export class GeminiAdapter {
|
|
|
56
56
|
}
|
|
57
57
|
try {
|
|
58
58
|
const handoff = buildSimpleHandoff(request.workingDir, request.ccOutput, request.analyzedFiles, request.focusAreas, request.customPrompt);
|
|
59
|
-
const
|
|
60
|
-
|
|
59
|
+
const prompt = request.reviewMode === 'adversarial'
|
|
60
|
+
? buildAdversarialHandoffPrompt({ handoff })
|
|
61
|
+
: buildHandoffPrompt({ handoff, role: selectRole(request.focusAreas) });
|
|
61
62
|
const result = await this.runCli(prompt, request.workingDir);
|
|
62
63
|
if (result.exitCode !== 0) {
|
|
63
64
|
const error = this.categorizeError(result.stderr);
|
|
@@ -109,8 +110,11 @@ export class GeminiAdapter {
|
|
|
109
110
|
const elapsed = Math.round((Date.now() - cliStartTime) / 1000);
|
|
110
111
|
console.error(`[gemini] ✓ complete (${elapsed}s)`);
|
|
111
112
|
const finalResponse = decoder.getFinalResponse();
|
|
113
|
+
if (!finalResponse && result.exitCode === 0) {
|
|
114
|
+
return { stdout: '', stderr: 'Gemini produced no output — review may have failed silently', exitCode: 1, truncated: false };
|
|
115
|
+
}
|
|
112
116
|
return {
|
|
113
|
-
stdout: finalResponse ||
|
|
117
|
+
stdout: finalResponse || '',
|
|
114
118
|
stderr: result.stderr,
|
|
115
119
|
exitCode: result.exitCode,
|
|
116
120
|
truncated: result.truncated,
|
|
@@ -135,12 +139,12 @@ export class GeminiAdapter {
|
|
|
135
139
|
categorizeError(stderr) {
|
|
136
140
|
const lower = stderr.toLowerCase();
|
|
137
141
|
if (lower.includes('rate limit') || lower.includes('quota')) {
|
|
138
|
-
return { type: 'rate_limit', message:
|
|
142
|
+
return { type: 'rate_limit', message: `Rate limit or quota exceeded: ${stderr.slice(0, 500)}` };
|
|
139
143
|
}
|
|
140
144
|
if (lower.includes('unauthorized') || lower.includes('authentication') || lower.includes('api key') || stderr.includes('401') || stderr.includes('403')) {
|
|
141
|
-
return { type: 'auth_error', message:
|
|
145
|
+
return { type: 'auth_error', message: `Authentication failed: ${stderr.slice(0, 500)}`, details: { stderr } };
|
|
142
146
|
}
|
|
143
|
-
return { type: 'cli_error', message: stderr || 'Unknown error' };
|
|
147
|
+
return { type: 'cli_error', message: stderr.slice(0, 500) || 'Unknown error' };
|
|
144
148
|
}
|
|
145
149
|
getSuggestion(error) {
|
|
146
150
|
switch (error.type) {
|
package/dist/commands.js
CHANGED
package/dist/handoff.d.ts
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* Defines the minimal, targeted information that should flow from CC to reviewers.
|
|
5
5
|
*
|
|
6
6
|
* Philosophy:
|
|
7
|
-
* - Reviewers have filesystem
|
|
7
|
+
* - Reviewers have filesystem access - don't duplicate what they can discover
|
|
8
8
|
* - Pass ONLY what CC uniquely knows: uncertainties, decisions, questions
|
|
9
|
-
* - Let reviewer use their tools (
|
|
9
|
+
* - Let reviewer use their tools (file reading) for actual code
|
|
10
|
+
* - Do NOT assume git — working directory may not be a git repo
|
|
10
11
|
*/
|
|
11
12
|
import { z } from 'zod';
|
|
12
13
|
import { FocusArea } from './types.js';
|
|
@@ -211,6 +212,13 @@ export declare const ROLES: Record<string, ReviewerRole>;
|
|
|
211
212
|
* Select the best role based on focus areas
|
|
212
213
|
*/
|
|
213
214
|
export declare function selectRole(focusAreas?: FocusArea[]): ReviewerRole;
|
|
215
|
+
export declare const ADVERSARIAL_REVIEWER: ReviewerRole;
|
|
216
|
+
/**
|
|
217
|
+
* Build an adversarial handoff prompt with challenge-mode stance sections.
|
|
218
|
+
* Same structure as buildHandoffPrompt but adds adversarial XML sections
|
|
219
|
+
* and uses the ADVERSARIAL_REVIEWER role.
|
|
220
|
+
*/
|
|
221
|
+
export declare function buildAdversarialHandoffPrompt(options: PromptOptions): string;
|
|
214
222
|
export interface PromptOptions {
|
|
215
223
|
handoff: Handoff;
|
|
216
224
|
role?: ReviewerRole;
|
package/dist/handoff.js
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* Defines the minimal, targeted information that should flow from CC to reviewers.
|
|
5
5
|
*
|
|
6
6
|
* Philosophy:
|
|
7
|
-
* - Reviewers have filesystem
|
|
7
|
+
* - Reviewers have filesystem access - don't duplicate what they can discover
|
|
8
8
|
* - Pass ONLY what CC uniquely knows: uncertainties, decisions, questions
|
|
9
|
-
* - Let reviewer use their tools (
|
|
9
|
+
* - Let reviewer use their tools (file reading) for actual code
|
|
10
|
+
* - Do NOT assume git — working directory may not be a git repo
|
|
10
11
|
*/
|
|
11
12
|
import { z } from 'zod';
|
|
12
13
|
// =============================================================================
|
|
@@ -87,7 +88,7 @@ export const CHANGE_FOCUSED_REVIEWER = {
|
|
|
87
88
|
isGeneric: true,
|
|
88
89
|
applicableFocusAreas: [],
|
|
89
90
|
systemPrompt: `Change reviewer. Focus on: goal achievement, regressions, edge cases, side effects.
|
|
90
|
-
Reference specific lines in the
|
|
91
|
+
Reference specific lines in the source files.`,
|
|
91
92
|
};
|
|
92
93
|
/**
|
|
93
94
|
* Specialized roles - when specific focus is requested
|
|
@@ -153,6 +154,121 @@ export function selectRole(focusAreas) {
|
|
|
153
154
|
}
|
|
154
155
|
return CHANGE_FOCUSED_REVIEWER;
|
|
155
156
|
}
|
|
157
|
+
// =============================================================================
|
|
158
|
+
// ADVERSARIAL REVIEWER — Challenge mode for multi_review
|
|
159
|
+
// =============================================================================
|
|
160
|
+
export const ADVERSARIAL_REVIEWER = {
|
|
161
|
+
id: 'adversarial',
|
|
162
|
+
name: 'Adversarial Reviewer',
|
|
163
|
+
description: 'Actively tries to break confidence in the change — challenges assumptions, not just bugs',
|
|
164
|
+
isGeneric: false,
|
|
165
|
+
applicableFocusAreas: [],
|
|
166
|
+
systemPrompt: `Senior staff engineer performing an adversarial review. Your job is to break confidence in the change, not to validate it.`,
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Build an adversarial handoff prompt with challenge-mode stance sections.
|
|
170
|
+
* Same structure as buildHandoffPrompt but adds adversarial XML sections
|
|
171
|
+
* and uses the ADVERSARIAL_REVIEWER role.
|
|
172
|
+
*/
|
|
173
|
+
export function buildAdversarialHandoffPrompt(options) {
|
|
174
|
+
const { handoff } = options;
|
|
175
|
+
const role = ADVERSARIAL_REVIEWER;
|
|
176
|
+
const sections = [];
|
|
177
|
+
// SECTION 1: ROLE
|
|
178
|
+
sections.push(`# ROLE: ${role.name}\n\n${role.systemPrompt}`);
|
|
179
|
+
// SECTION 2: ADVERSARIAL STANCE
|
|
180
|
+
sections.push(`## ADVERSARIAL STANCE
|
|
181
|
+
|
|
182
|
+
<operating_stance>
|
|
183
|
+
Default to skepticism. Assume the change can fail in subtle, high-cost,
|
|
184
|
+
or user-visible ways until the evidence says otherwise. Do not give credit
|
|
185
|
+
for good intent, partial fixes, or likely follow-up work.
|
|
186
|
+
</operating_stance>
|
|
187
|
+
|
|
188
|
+
<attack_surface>
|
|
189
|
+
Prioritized failure categories:
|
|
190
|
+
1. Auth/permissions bypass
|
|
191
|
+
2. Data loss or corruption
|
|
192
|
+
3. Rollback safety
|
|
193
|
+
4. Race conditions / concurrency
|
|
194
|
+
5. Empty-state / null / timeout handling
|
|
195
|
+
6. Version skew / backwards compatibility
|
|
196
|
+
7. Observability gaps (missing logs, metrics, alerts)
|
|
197
|
+
</attack_surface>
|
|
198
|
+
|
|
199
|
+
<review_method>
|
|
200
|
+
Actively try to disprove the change. Look for violated invariants,
|
|
201
|
+
missing guards, unhandled failure paths. If the user supplied a focus area,
|
|
202
|
+
weight it heavily, but still report any other material issue you can defend.
|
|
203
|
+
</review_method>
|
|
204
|
+
|
|
205
|
+
<finding_bar>
|
|
206
|
+
Material findings only. Each must answer:
|
|
207
|
+
1. What can go wrong?
|
|
208
|
+
2. Why is this code path vulnerable?
|
|
209
|
+
3. What is the likely impact?
|
|
210
|
+
4. What concrete change would reduce the risk?
|
|
211
|
+
</finding_bar>
|
|
212
|
+
|
|
213
|
+
<calibration_rules>
|
|
214
|
+
Prefer one strong finding over several weak ones. If you cannot defend
|
|
215
|
+
a finding from the provided code, drop it.
|
|
216
|
+
</calibration_rules>
|
|
217
|
+
|
|
218
|
+
<grounding_rules>
|
|
219
|
+
Be aggressive, but stay grounded. Every finding must be defensible from
|
|
220
|
+
the repository context. No speculative findings. No "might be an issue"
|
|
221
|
+
without concrete evidence from the code.
|
|
222
|
+
</grounding_rules>`);
|
|
223
|
+
// SECTION 3: TASK (same as standard)
|
|
224
|
+
sections.push(`## YOUR TASK
|
|
225
|
+
|
|
226
|
+
Review code in \`${handoff.workingDir}\`.
|
|
227
|
+
|
|
228
|
+
**Summary:** ${handoff.summary}${handoff.confidence !== undefined && handoff.confidence < 0.9 ? `\n**CC Confidence:** ${Math.round(handoff.confidence * 100)}% — verify weak areas` : ''}
|
|
229
|
+
|
|
230
|
+
**IMPORTANT:**
|
|
231
|
+
- This is a READ-ONLY review. Do NOT create, modify, or delete any files. Only read files to verify claims.
|
|
232
|
+
- Do NOT assume a git repository exists. Do NOT run git commands. Read files directly from the filesystem.`);
|
|
233
|
+
// SECTION 4: CC'S UNCERTAINTIES
|
|
234
|
+
if (handoff.uncertainties && handoff.uncertainties.length > 0) {
|
|
235
|
+
sections.push(`## CC'S UNCERTAINTIES
|
|
236
|
+
|
|
237
|
+
${handoff.uncertainties.map((u, i) => `### ${i + 1}. ${u.topic} ${u.severity === 'critical' ? '⚠️' : ''}
|
|
238
|
+
- **Question:** ${u.question}
|
|
239
|
+
${u.ccAssumption ? `- **CC assumed:** ${u.ccAssumption}` : ''}
|
|
240
|
+
${u.relevantFiles ? `- **Files:** ${u.relevantFiles.join(', ')}` : ''}`).join('\n\n')}`);
|
|
241
|
+
}
|
|
242
|
+
// SECTION 5: SPECIFIC QUESTIONS
|
|
243
|
+
if (handoff.questions && handoff.questions.length > 0) {
|
|
244
|
+
sections.push(`## QUESTIONS FROM CC
|
|
245
|
+
|
|
246
|
+
${handoff.questions.map((q, i) => `${i + 1}. **${q.question}**
|
|
247
|
+
${q.context ? `Context: ${q.context}` : ''}
|
|
248
|
+
${q.ccGuess ? `CC Guess: ${q.ccGuess}` : ''}`).join('\n')}`);
|
|
249
|
+
}
|
|
250
|
+
// SECTION 6: DECISIONS TO EVALUATE
|
|
251
|
+
if (handoff.decisions && handoff.decisions.length > 0) {
|
|
252
|
+
sections.push(`## DECISIONS TO EVALUATE
|
|
253
|
+
|
|
254
|
+
${handoff.decisions.map((d, i) => `${i + 1}. **${d.decision}**
|
|
255
|
+
Rationale: ${d.rationale}
|
|
256
|
+
${d.alternatives ? `Alternatives: ${d.alternatives.join(', ')}` : ''}`).join('\n')}`);
|
|
257
|
+
}
|
|
258
|
+
// SECTION 7: FOCUS AREAS
|
|
259
|
+
if (handoff.focusAreas && handoff.focusAreas.length > 0) {
|
|
260
|
+
sections.push(`## FOCUS AREAS\n\nWeight these areas heavily in your adversarial analysis:\n${handoff.focusAreas.map(f => `- **${f}**`).join('\n')}`);
|
|
261
|
+
}
|
|
262
|
+
// SECTION 8: PRIORITY FILES
|
|
263
|
+
if (handoff.priorityFiles && handoff.priorityFiles.length > 0) {
|
|
264
|
+
sections.push(`## PRIORITY FILES\n\n${handoff.priorityFiles.map(f => `- \`${f}\``).join('\n')}`);
|
|
265
|
+
}
|
|
266
|
+
// SECTION 9: ADVERSARIAL FOCUS (customInstructions steers the challenge)
|
|
267
|
+
if (handoff.customInstructions) {
|
|
268
|
+
sections.push(`## ADVERSARIAL FOCUS\n\n${handoff.customInstructions}`);
|
|
269
|
+
}
|
|
270
|
+
return sections.join('\n\n');
|
|
271
|
+
}
|
|
156
272
|
/**
|
|
157
273
|
* Build the review prompt using minimal, targeted context.
|
|
158
274
|
* No output format constraints — reviewer responds naturally, CC interprets.
|
|
@@ -166,11 +282,13 @@ export function buildHandoffPrompt(options) {
|
|
|
166
282
|
// SECTION 2: TASK
|
|
167
283
|
sections.push(`## YOUR TASK
|
|
168
284
|
|
|
169
|
-
Review
|
|
285
|
+
Review code in \`${handoff.workingDir}\`.
|
|
170
286
|
|
|
171
287
|
**Summary:** ${handoff.summary}${handoff.confidence !== undefined && handoff.confidence < 0.9 ? `\n**CC Confidence:** ${Math.round(handoff.confidence * 100)}% — verify weak areas` : ''}
|
|
172
288
|
|
|
173
|
-
**IMPORTANT
|
|
289
|
+
**IMPORTANT:**
|
|
290
|
+
- This is a READ-ONLY review. Do NOT create, modify, or delete any files. Only read files to verify claims.
|
|
291
|
+
- Do NOT assume a git repository exists. Do NOT run git commands. Read files directly from the filesystem.`);
|
|
174
292
|
// SECTION 3: CC'S UNCERTAINTIES
|
|
175
293
|
if (handoff.uncertainties && handoff.uncertainties.length > 0) {
|
|
176
294
|
sections.push(`## CC'S UNCERTAINTIES
|
|
@@ -200,6 +318,10 @@ ${handoff.decisions.map((d, i) => `${i + 1}. **${d.decision}**
|
|
|
200
318
|
if (handoff.priorityFiles && handoff.priorityFiles.length > 0) {
|
|
201
319
|
sections.push(`## PRIORITY FILES\n\n${handoff.priorityFiles.map(f => `- \`${f}\``).join('\n')}`);
|
|
202
320
|
}
|
|
321
|
+
// SECTION 7: CUSTOM INSTRUCTIONS
|
|
322
|
+
if (handoff.customInstructions) {
|
|
323
|
+
sections.push(`## ADDITIONAL INSTRUCTIONS\n\n${handoff.customInstructions}`);
|
|
324
|
+
}
|
|
203
325
|
return sections.join('\n\n');
|
|
204
326
|
}
|
|
205
327
|
// =============================================================================
|
package/dist/prompt.js
CHANGED
|
@@ -101,6 +101,7 @@ ${request.customPrompt}`);
|
|
|
101
101
|
CONSTRAINTS:
|
|
102
102
|
• You have filesystem access - READ files to verify claims
|
|
103
103
|
• Do NOT modify any files (advisory mode only)
|
|
104
|
+
• Do NOT assume a git repository exists - do NOT run git commands
|
|
104
105
|
• Reference specific file:line when making claims
|
|
105
106
|
• Do NOT hallucinate file paths - verify they exist
|
|
106
107
|
• Be skeptical - verify before agreeing with CC's findings`);
|
package/dist/tools/feedback.js
CHANGED
|
@@ -92,24 +92,38 @@ export async function handleMultiReview(input) {
|
|
|
92
92
|
if (availableAdapters.length === 0) {
|
|
93
93
|
return { content: [{ type: 'text', text: '❌ No AI CLIs found.\n\nInstall at least one:\n - Codex: npm install -g @openai/codex-cli\n - Gemini: npm install -g @google/gemini-cli' }] };
|
|
94
94
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
// Spawn 2 reviews per adapter: standard + adversarial (all in parallel)
|
|
96
|
+
// customPrompt steers the adversarial focus only — strip it from standard pass to avoid bias
|
|
97
|
+
const { customPrompt, ...standardRequest } = request;
|
|
98
|
+
const reviewPromises = availableAdapters.flatMap((adapter) => [
|
|
99
|
+
adapter.runReview({ ...standardRequest }).then(result => ({ adapter, result, mode: 'standard' })),
|
|
100
|
+
adapter.runReview({ ...request, reviewMode: 'adversarial' }).then(result => ({ adapter, result, mode: 'adversarial' })),
|
|
101
|
+
]);
|
|
102
|
+
const results = await Promise.all(reviewPromises);
|
|
103
|
+
const standardResults = results.filter(r => r.mode === 'standard');
|
|
104
|
+
const adversarialResults = results.filter(r => r.mode === 'adversarial');
|
|
100
105
|
const allFailed = results.every(r => !r.result.success);
|
|
101
106
|
const someFailed = results.some(r => !r.result.success);
|
|
107
|
+
const lines = [];
|
|
102
108
|
if (allFailed)
|
|
103
109
|
lines.push('## Multi-Model Review ❌ All Failed\n');
|
|
104
110
|
else if (someFailed)
|
|
105
111
|
lines.push('## Multi-Model Review ⚠️ Partial Success\n');
|
|
106
112
|
else
|
|
107
113
|
lines.push('## Multi-Model Review ✓\n');
|
|
108
|
-
lines.push(`**Models:** ${availableAdapters.map(a => a.id).join(', ')}\n`);
|
|
109
|
-
|
|
114
|
+
lines.push(`**Models:** ${availableAdapters.map(a => a.id).join(', ')} (standard + adversarial)\n`);
|
|
115
|
+
// Standard section
|
|
116
|
+
lines.push('## Standard Review Findings\n');
|
|
117
|
+
for (const { adapter, result } of standardResults) {
|
|
110
118
|
lines.push(formatResult(result, adapter.getCapabilities().name));
|
|
111
119
|
lines.push('');
|
|
112
120
|
}
|
|
121
|
+
// Adversarial section
|
|
122
|
+
lines.push('## Challenge Review Findings\n');
|
|
123
|
+
for (const { adapter, result } of adversarialResults) {
|
|
124
|
+
lines.push(formatResult(result, `${adapter.getCapabilities().name} (Adversarial)`));
|
|
125
|
+
lines.push('');
|
|
126
|
+
}
|
|
113
127
|
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
114
128
|
}
|
|
115
129
|
// =============================================================================
|
|
@@ -168,7 +182,7 @@ export const TOOL_DEFINITIONS = {
|
|
|
168
182
|
},
|
|
169
183
|
multi_review: {
|
|
170
184
|
name: 'multi_review',
|
|
171
|
-
description: "ONLY use when user explicitly requests '/multi-review' or 'review with all models'.
|
|
185
|
+
description: "ONLY use when user explicitly requests '/multi-review' or 'review with all models'. Runs parallel standard AND adversarial reviews from all available models. Each model reviews twice: standard (bugs/issues) + adversarial (challenge assumptions/design decisions). Use customPrompt to steer the adversarial focus. DO NOT use for general 'review' requests.",
|
|
172
186
|
inputSchema: {
|
|
173
187
|
type: 'object',
|
|
174
188
|
properties: {
|
|
@@ -177,7 +191,7 @@ export const TOOL_DEFINITIONS = {
|
|
|
177
191
|
outputType: { type: 'string', enum: ['plan', 'findings', 'analysis', 'proposal'], description: 'Type of output being reviewed' },
|
|
178
192
|
analyzedFiles: { type: 'array', items: { type: 'string' }, description: 'File paths that CC analyzed' },
|
|
179
193
|
focusAreas: { type: 'array', items: { type: 'string', enum: ['security', 'performance', 'architecture', 'correctness', 'maintainability', 'scalability', 'testing', 'documentation'] }, description: 'Areas to focus the review on' },
|
|
180
|
-
customPrompt: { type: 'string', description: 'Custom instructions for
|
|
194
|
+
customPrompt: { type: 'string', description: 'Custom instructions for standard review + adversarial focus steering' },
|
|
181
195
|
serviceTier: { type: 'string', enum: ['default', 'fast', 'flex'], description: 'Codex service tier (fast = priority processing, flex = cheaper/slower). Only applies to Codex.' }
|
|
182
196
|
},
|
|
183
197
|
required: ['workingDir', 'ccOutput', 'outputType']
|