cc-reviewer 1.5.1 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/codex.js +27 -9
- package/dist/adapters/gemini.js +24 -6
- package/dist/schema.d.ts +82 -0
- package/dist/schema.js +51 -0
- package/dist/tools/feedback.js +21 -0
- package/package.json +1 -1
package/dist/adapters/codex.js
CHANGED
|
@@ -138,16 +138,29 @@ export class CodexAdapter {
|
|
|
138
138
|
executionTimeMs: Date.now() - startTime,
|
|
139
139
|
};
|
|
140
140
|
}
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
141
|
+
// Check for empty/minimal data on any parse path
|
|
142
|
+
const hasMinimalData = output.findings.length === 0 &&
|
|
143
|
+
output.agreements.length === 0 &&
|
|
144
|
+
output.disagreements.length === 0;
|
|
145
|
+
if (hasMinimalData) {
|
|
146
|
+
if (attempt < MAX_RETRIES) {
|
|
147
|
+
console.error(`[codex] Received empty output, retrying...`);
|
|
148
|
+
return this.runWithRetry(request, attempt + 1, startTime, usedFallback
|
|
149
|
+
? 'Received markdown output instead of JSON. Please provide valid JSON output.'
|
|
150
|
+
: 'Output contained no findings, agreements, or disagreements. Please provide substantive review.', result.stdout);
|
|
150
151
|
}
|
|
152
|
+
// Final attempt with no data — report failure
|
|
153
|
+
return {
|
|
154
|
+
success: false,
|
|
155
|
+
error: {
|
|
156
|
+
type: 'parse_error',
|
|
157
|
+
message: 'Reviewer returned empty output after retries',
|
|
158
|
+
details: { rawOutput: result.stdout.slice(0, 1000) },
|
|
159
|
+
},
|
|
160
|
+
suggestion: 'The model returned no substantive review. Try a different focus area.',
|
|
161
|
+
rawOutput: result.stdout,
|
|
162
|
+
executionTimeMs: Date.now() - startTime,
|
|
163
|
+
};
|
|
151
164
|
}
|
|
152
165
|
return {
|
|
153
166
|
success: true,
|
|
@@ -236,6 +249,11 @@ export class CodexAdapter {
|
|
|
236
249
|
stdio: ['pipe', 'pipe', 'pipe'], // stdin is pipe for prompt delivery
|
|
237
250
|
env: { ...process.env }
|
|
238
251
|
});
|
|
252
|
+
// Guard against EPIPE if the child exits before consuming stdin.
|
|
253
|
+
// Log but don't reject — let the `close` handler capture the real exit code.
|
|
254
|
+
proc.stdin.on('error', (err) => {
|
|
255
|
+
console.error(`[codex] stdin error (likely EPIPE): ${err.message}`);
|
|
256
|
+
});
|
|
239
257
|
// Deliver prompt via stdin
|
|
240
258
|
proc.stdin.write(prompt);
|
|
241
259
|
proc.stdin.end();
|
package/dist/adapters/gemini.js
CHANGED
|
@@ -136,17 +136,29 @@ export class GeminiAdapter {
|
|
|
136
136
|
executionTimeMs: Date.now() - startTime,
|
|
137
137
|
};
|
|
138
138
|
}
|
|
139
|
-
// If output has no substantive data, retry
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (
|
|
139
|
+
// If output has no substantive data, retry or fail
|
|
140
|
+
const hasMinimalData = output.findings.length === 0 &&
|
|
141
|
+
output.agreements.length === 0 &&
|
|
142
|
+
output.disagreements.length === 0;
|
|
143
|
+
if (hasMinimalData) {
|
|
144
|
+
if (attempt < MAX_RETRIES) {
|
|
145
145
|
console.error(`[gemini] Received empty output, retrying...`);
|
|
146
146
|
return this.runWithRetry(request, attempt + 1, startTime, usedFallback
|
|
147
147
|
? 'Received markdown output instead of JSON. Please provide valid JSON output.'
|
|
148
148
|
: 'Output contained no findings, agreements, or disagreements. Please provide substantive review.', result.stdout);
|
|
149
149
|
}
|
|
150
|
+
// Final attempt with no data — report failure
|
|
151
|
+
return {
|
|
152
|
+
success: false,
|
|
153
|
+
error: {
|
|
154
|
+
type: 'parse_error',
|
|
155
|
+
message: 'Reviewer returned empty output after retries',
|
|
156
|
+
details: { rawOutput: result.stdout.slice(0, 1000) },
|
|
157
|
+
},
|
|
158
|
+
suggestion: 'The model returned no substantive review. Try a different focus area.',
|
|
159
|
+
rawOutput: result.stdout,
|
|
160
|
+
executionTimeMs: Date.now() - startTime,
|
|
161
|
+
};
|
|
150
162
|
}
|
|
151
163
|
return {
|
|
152
164
|
success: true,
|
|
@@ -209,12 +221,18 @@ export class GeminiAdapter {
|
|
|
209
221
|
'--yolo',
|
|
210
222
|
'--output-format', 'json', // Force JSON output
|
|
211
223
|
'--include-directories', workingDir,
|
|
224
|
+
'-p', '', // Force headless mode; actual prompt delivered via stdin
|
|
212
225
|
];
|
|
213
226
|
const proc = spawn('gemini', args, {
|
|
214
227
|
cwd: workingDir,
|
|
215
228
|
stdio: ['pipe', 'pipe', 'pipe'], // stdin is pipe for prompt delivery
|
|
216
229
|
env: { ...process.env }
|
|
217
230
|
});
|
|
231
|
+
// Guard against EPIPE if the child exits before consuming stdin.
|
|
232
|
+
// Log but don't reject — let the `close` handler capture the real exit code.
|
|
233
|
+
proc.stdin.on('error', (err) => {
|
|
234
|
+
console.error(`[gemini] stdin error (likely EPIPE): ${err.message}`);
|
|
235
|
+
});
|
|
218
236
|
// Deliver prompt via stdin — more stable than args for complex content
|
|
219
237
|
proc.stdin.write(prompt);
|
|
220
238
|
proc.stdin.end();
|
package/dist/schema.d.ts
CHANGED
|
@@ -200,6 +200,37 @@ export declare const RiskAssessment: z.ZodObject<{
|
|
|
200
200
|
mitigations?: string[] | undefined;
|
|
201
201
|
}>;
|
|
202
202
|
export type RiskAssessment = z.infer<typeof RiskAssessment>;
|
|
203
|
+
export declare const UncertaintyResponse: z.ZodObject<{
|
|
204
|
+
uncertainty_index: z.ZodNumber;
|
|
205
|
+
verified: z.ZodBoolean;
|
|
206
|
+
finding: z.ZodString;
|
|
207
|
+
recommendation: z.ZodOptional<z.ZodString>;
|
|
208
|
+
}, "strip", z.ZodTypeAny, {
|
|
209
|
+
verified: boolean;
|
|
210
|
+
uncertainty_index: number;
|
|
211
|
+
finding: string;
|
|
212
|
+
recommendation?: string | undefined;
|
|
213
|
+
}, {
|
|
214
|
+
verified: boolean;
|
|
215
|
+
uncertainty_index: number;
|
|
216
|
+
finding: string;
|
|
217
|
+
recommendation?: string | undefined;
|
|
218
|
+
}>;
|
|
219
|
+
export type UncertaintyResponse = z.infer<typeof UncertaintyResponse>;
|
|
220
|
+
export declare const QuestionAnswer: z.ZodObject<{
|
|
221
|
+
question_index: z.ZodNumber;
|
|
222
|
+
answer: z.ZodString;
|
|
223
|
+
confidence: z.ZodOptional<z.ZodNumber>;
|
|
224
|
+
}, "strip", z.ZodTypeAny, {
|
|
225
|
+
question_index: number;
|
|
226
|
+
answer: string;
|
|
227
|
+
confidence?: number | undefined;
|
|
228
|
+
}, {
|
|
229
|
+
question_index: number;
|
|
230
|
+
answer: string;
|
|
231
|
+
confidence?: number | undefined;
|
|
232
|
+
}>;
|
|
233
|
+
export type QuestionAnswer = z.infer<typeof QuestionAnswer>;
|
|
203
234
|
export declare const ReviewOutput: z.ZodObject<{
|
|
204
235
|
reviewer: z.ZodString;
|
|
205
236
|
timestamp: z.ZodOptional<z.ZodString>;
|
|
@@ -348,6 +379,35 @@ export declare const ReviewOutput: z.ZodObject<{
|
|
|
348
379
|
alternative: string;
|
|
349
380
|
recommendation: "strongly_prefer" | "consider" | "situational" | "informational";
|
|
350
381
|
}>, "many">;
|
|
382
|
+
uncertainty_responses: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
383
|
+
uncertainty_index: z.ZodNumber;
|
|
384
|
+
verified: z.ZodBoolean;
|
|
385
|
+
finding: z.ZodString;
|
|
386
|
+
recommendation: z.ZodOptional<z.ZodString>;
|
|
387
|
+
}, "strip", z.ZodTypeAny, {
|
|
388
|
+
verified: boolean;
|
|
389
|
+
uncertainty_index: number;
|
|
390
|
+
finding: string;
|
|
391
|
+
recommendation?: string | undefined;
|
|
392
|
+
}, {
|
|
393
|
+
verified: boolean;
|
|
394
|
+
uncertainty_index: number;
|
|
395
|
+
finding: string;
|
|
396
|
+
recommendation?: string | undefined;
|
|
397
|
+
}>, "many">>;
|
|
398
|
+
question_answers: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
399
|
+
question_index: z.ZodNumber;
|
|
400
|
+
answer: z.ZodString;
|
|
401
|
+
confidence: z.ZodOptional<z.ZodNumber>;
|
|
402
|
+
}, "strip", z.ZodTypeAny, {
|
|
403
|
+
question_index: number;
|
|
404
|
+
answer: string;
|
|
405
|
+
confidence?: number | undefined;
|
|
406
|
+
}, {
|
|
407
|
+
question_index: number;
|
|
408
|
+
answer: string;
|
|
409
|
+
confidence?: number | undefined;
|
|
410
|
+
}>, "many">>;
|
|
351
411
|
risk_assessment: z.ZodObject<{
|
|
352
412
|
overall_level: z.ZodEnum<["critical", "high", "medium", "low", "minimal"]>;
|
|
353
413
|
score: z.ZodNumber;
|
|
@@ -424,6 +484,17 @@ export declare const ReviewOutput: z.ZodObject<{
|
|
|
424
484
|
mitigations?: string[] | undefined;
|
|
425
485
|
};
|
|
426
486
|
timestamp?: string | undefined;
|
|
487
|
+
uncertainty_responses?: {
|
|
488
|
+
verified: boolean;
|
|
489
|
+
uncertainty_index: number;
|
|
490
|
+
finding: string;
|
|
491
|
+
recommendation?: string | undefined;
|
|
492
|
+
}[] | undefined;
|
|
493
|
+
question_answers?: {
|
|
494
|
+
question_index: number;
|
|
495
|
+
answer: string;
|
|
496
|
+
confidence?: number | undefined;
|
|
497
|
+
}[] | undefined;
|
|
427
498
|
files_examined?: string[] | undefined;
|
|
428
499
|
execution_notes?: string | undefined;
|
|
429
500
|
}, {
|
|
@@ -481,6 +552,17 @@ export declare const ReviewOutput: z.ZodObject<{
|
|
|
481
552
|
mitigations?: string[] | undefined;
|
|
482
553
|
};
|
|
483
554
|
timestamp?: string | undefined;
|
|
555
|
+
uncertainty_responses?: {
|
|
556
|
+
verified: boolean;
|
|
557
|
+
uncertainty_index: number;
|
|
558
|
+
finding: string;
|
|
559
|
+
recommendation?: string | undefined;
|
|
560
|
+
}[] | undefined;
|
|
561
|
+
question_answers?: {
|
|
562
|
+
question_index: number;
|
|
563
|
+
answer: string;
|
|
564
|
+
confidence?: number | undefined;
|
|
565
|
+
}[] | undefined;
|
|
484
566
|
files_examined?: string[] | undefined;
|
|
485
567
|
execution_notes?: string | undefined;
|
|
486
568
|
}>;
|
package/dist/schema.js
CHANGED
|
@@ -94,6 +94,20 @@ export const RiskAssessment = z.object({
|
|
|
94
94
|
mitigations: z.array(z.string()).optional().describe('Suggested mitigations'),
|
|
95
95
|
});
|
|
96
96
|
// =============================================================================
|
|
97
|
+
// UNCERTAINTY & QUESTION RESPONSES
|
|
98
|
+
// =============================================================================
|
|
99
|
+
export const UncertaintyResponse = z.object({
|
|
100
|
+
uncertainty_index: z.number().int().positive().describe('1-based index of the uncertainty being addressed'),
|
|
101
|
+
verified: z.boolean().describe('Whether the uncertainty was verified'),
|
|
102
|
+
finding: z.string().describe('What the reviewer found'),
|
|
103
|
+
recommendation: z.string().optional().describe('What CC should do'),
|
|
104
|
+
});
|
|
105
|
+
export const QuestionAnswer = z.object({
|
|
106
|
+
question_index: z.number().int().positive().describe('1-based index of the question being answered'),
|
|
107
|
+
answer: z.string().describe('The reviewer answer'),
|
|
108
|
+
confidence: ConfidenceScore.optional().describe('Confidence in the answer (0-1)'),
|
|
109
|
+
});
|
|
110
|
+
// =============================================================================
|
|
97
111
|
// COMPLETE REVIEW OUTPUT (Single Reviewer)
|
|
98
112
|
// =============================================================================
|
|
99
113
|
export const ReviewOutput = z.object({
|
|
@@ -104,6 +118,9 @@ export const ReviewOutput = z.object({
|
|
|
104
118
|
agreements: z.array(Agreement).describe("Validation of CC's correct assessments"),
|
|
105
119
|
disagreements: z.array(Disagreement).describe("Challenges to CC's claims"),
|
|
106
120
|
alternatives: z.array(Alternative).describe('Alternative approaches to consider'),
|
|
121
|
+
// Responses to CC's uncertainties and questions
|
|
122
|
+
uncertainty_responses: z.array(UncertaintyResponse).optional().describe('Responses to CC uncertainties'),
|
|
123
|
+
question_answers: z.array(QuestionAnswer).optional().describe('Answers to CC questions'),
|
|
107
124
|
// Summary
|
|
108
125
|
risk_assessment: RiskAssessment,
|
|
109
126
|
// Metadata
|
|
@@ -211,6 +228,33 @@ export function getReviewOutputJsonSchema() {
|
|
|
211
228
|
}
|
|
212
229
|
}
|
|
213
230
|
},
|
|
231
|
+
uncertainty_responses: {
|
|
232
|
+
type: 'array',
|
|
233
|
+
items: {
|
|
234
|
+
type: 'object',
|
|
235
|
+
additionalProperties: false,
|
|
236
|
+
required: ['uncertainty_index', 'verified', 'finding'],
|
|
237
|
+
properties: {
|
|
238
|
+
uncertainty_index: { type: 'integer', minimum: 1 },
|
|
239
|
+
verified: { type: 'boolean' },
|
|
240
|
+
finding: { type: 'string' },
|
|
241
|
+
recommendation: { type: 'string' }
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
question_answers: {
|
|
246
|
+
type: 'array',
|
|
247
|
+
items: {
|
|
248
|
+
type: 'object',
|
|
249
|
+
additionalProperties: false,
|
|
250
|
+
required: ['question_index', 'answer'],
|
|
251
|
+
properties: {
|
|
252
|
+
question_index: { type: 'integer', minimum: 1 },
|
|
253
|
+
answer: { type: 'string' },
|
|
254
|
+
confidence: { type: 'number', minimum: 0, maximum: 1 }
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
},
|
|
214
258
|
risk_assessment: {
|
|
215
259
|
type: 'object',
|
|
216
260
|
additionalProperties: false,
|
|
@@ -252,6 +296,13 @@ function normalizeReviewOutput(parsed) {
|
|
|
252
296
|
normalized.disagreements = normalized.disagreements ?? [];
|
|
253
297
|
normalized.alternatives = normalized.alternatives ?? [];
|
|
254
298
|
normalized.findings = normalized.findings ?? [];
|
|
299
|
+
// Normalize optional response arrays — drop non-array values
|
|
300
|
+
if (normalized.uncertainty_responses !== undefined && !Array.isArray(normalized.uncertainty_responses)) {
|
|
301
|
+
delete normalized.uncertainty_responses;
|
|
302
|
+
}
|
|
303
|
+
if (normalized.question_answers !== undefined && !Array.isArray(normalized.question_answers)) {
|
|
304
|
+
delete normalized.question_answers;
|
|
305
|
+
}
|
|
255
306
|
// Normalize risk_assessment from simplified formats
|
|
256
307
|
if (!normalized.risk_assessment) {
|
|
257
308
|
const ra = normalized.risk_assessment;
|
package/dist/tools/feedback.js
CHANGED
|
@@ -77,6 +77,27 @@ function formatSingleReviewResponse(result, modelName) {
|
|
|
77
77
|
lines.push('');
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
+
// Uncertainty Responses
|
|
81
|
+
if (output.uncertainty_responses && output.uncertainty_responses.length > 0) {
|
|
82
|
+
lines.push(`### Uncertainty Responses (${output.uncertainty_responses.length})\n`);
|
|
83
|
+
for (const ur of output.uncertainty_responses) {
|
|
84
|
+
const icon = ur.verified ? '✓' : '✗';
|
|
85
|
+
lines.push(`${icon} **Uncertainty #${ur.uncertainty_index}**: ${ur.finding}`);
|
|
86
|
+
if (ur.recommendation) {
|
|
87
|
+
lines.push(` → ${ur.recommendation}`);
|
|
88
|
+
}
|
|
89
|
+
lines.push('');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Question Answers
|
|
93
|
+
if (output.question_answers && output.question_answers.length > 0) {
|
|
94
|
+
lines.push(`### Question Answers (${output.question_answers.length})\n`);
|
|
95
|
+
for (const qa of output.question_answers) {
|
|
96
|
+
const confidence = qa.confidence !== undefined ? ` [${Math.round(qa.confidence * 100)}%]` : '';
|
|
97
|
+
lines.push(`**Q${qa.question_index}**${confidence}: ${qa.answer}`);
|
|
98
|
+
lines.push('');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
80
101
|
// Agreements
|
|
81
102
|
if (output.agreements.length > 0) {
|
|
82
103
|
lines.push(`### Agreements (${output.agreements.length})\n`);
|