cc-reviewer 1.0.3 → 1.0.5
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/cli/codex.js +36 -15
- package/dist/cli/gemini.js +32 -11
- package/dist/tools/feedback.d.ts +3 -0
- package/dist/tools/feedback.js +4 -2
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
package/dist/cli/codex.js
CHANGED
|
@@ -8,7 +8,9 @@ import { spawn } from 'child_process';
|
|
|
8
8
|
import { existsSync } from 'fs';
|
|
9
9
|
import { build7SectionPrompt, buildDeveloperInstructions, buildRetryPrompt, isValidFeedbackOutput } from '../prompt.js';
|
|
10
10
|
import { createTimeoutError, createCliNotFoundError, getSuggestion } from '../errors.js';
|
|
11
|
-
|
|
11
|
+
// Activity-based timeout: reset on output, kill on silence
|
|
12
|
+
const INACTIVITY_TIMEOUT_MS = 120000; // 2 min of no output = timeout
|
|
13
|
+
const MAX_TIMEOUT_MS = 3600000; // 60 min absolute max (edge case safety)
|
|
12
14
|
const MAX_RETRIES = 2;
|
|
13
15
|
const MAX_BUFFER_SIZE = 1024 * 1024; // 1MB max buffer to prevent memory issues
|
|
14
16
|
/**
|
|
@@ -45,7 +47,7 @@ async function runWithRetry(request, attempt, previousError, previousOutput) {
|
|
|
45
47
|
// Codex exec doesn't have a separate system instruction flag
|
|
46
48
|
const fullPrompt = `${developerInstructions}\n\n---\n\n${basePrompt}`;
|
|
47
49
|
// Run the CLI
|
|
48
|
-
const result = await runCodexCli(fullPrompt, request.workingDir);
|
|
50
|
+
const result = await runCodexCli(fullPrompt, request.workingDir, request.reasoningEffort || 'high');
|
|
49
51
|
// Check for CLI errors
|
|
50
52
|
if (result.exitCode !== 0) {
|
|
51
53
|
// Check for specific error patterns in stderr
|
|
@@ -136,11 +138,15 @@ async function runWithRetry(request, attempt, previousError, previousOutput) {
|
|
|
136
138
|
model: 'codex'
|
|
137
139
|
};
|
|
138
140
|
}
|
|
139
|
-
if (err.message === 'TIMEOUT') {
|
|
141
|
+
if (err.message === 'TIMEOUT' || err.message === 'MAX_TIMEOUT') {
|
|
142
|
+
const isMaxTimeout = err.message === 'MAX_TIMEOUT';
|
|
143
|
+
const timeoutMs = isMaxTimeout ? MAX_TIMEOUT_MS : INACTIVITY_TIMEOUT_MS;
|
|
140
144
|
return {
|
|
141
145
|
success: false,
|
|
142
|
-
error: createTimeoutError('codex',
|
|
143
|
-
suggestion:
|
|
146
|
+
error: createTimeoutError('codex', timeoutMs),
|
|
147
|
+
suggestion: isMaxTimeout
|
|
148
|
+
? 'Task exceeded 60 minute maximum. Try a smaller scope.'
|
|
149
|
+
: 'No output for 2 minutes. Process may be hung. Try a smaller scope or use --focus.',
|
|
144
150
|
model: 'codex'
|
|
145
151
|
};
|
|
146
152
|
}
|
|
@@ -160,17 +166,17 @@ async function runWithRetry(request, attempt, previousError, previousOutput) {
|
|
|
160
166
|
/**
|
|
161
167
|
* Execute the Codex CLI in non-interactive mode
|
|
162
168
|
*
|
|
163
|
-
* Uses: codex exec -m gpt-5.2-codex -c model_reasoning_effort="xhigh" \
|
|
169
|
+
* Uses: codex exec -m gpt-5.2-codex -c model_reasoning_effort="high|xhigh" \
|
|
164
170
|
* -c model_reasoning_summary_format=experimental \
|
|
165
171
|
* --dangerously-bypass-approvals-and-sandbox "<prompt>"
|
|
166
172
|
*/
|
|
167
|
-
function runCodexCli(prompt, workingDir) {
|
|
173
|
+
function runCodexCli(prompt, workingDir, reasoningEffort = 'high') {
|
|
168
174
|
return new Promise((resolve, reject) => {
|
|
169
175
|
// Build CLI arguments for non-interactive execution
|
|
170
176
|
const args = [
|
|
171
177
|
'exec',
|
|
172
178
|
'-m', 'gpt-5.2-codex',
|
|
173
|
-
'-c',
|
|
179
|
+
'-c', `model_reasoning_effort=${reasoningEffort}`,
|
|
174
180
|
'-c', 'model_reasoning_summary_format=experimental',
|
|
175
181
|
'--dangerously-bypass-approvals-and-sandbox',
|
|
176
182
|
'--skip-git-repo-check',
|
|
@@ -185,7 +191,24 @@ function runCodexCli(prompt, workingDir) {
|
|
|
185
191
|
let stdout = '';
|
|
186
192
|
let stderr = '';
|
|
187
193
|
let truncated = false;
|
|
194
|
+
let inactivityTimer;
|
|
195
|
+
// Max timeout - absolute cap (60 min)
|
|
196
|
+
const maxTimer = setTimeout(() => {
|
|
197
|
+
proc.kill('SIGTERM');
|
|
198
|
+
reject(new Error('MAX_TIMEOUT'));
|
|
199
|
+
}, MAX_TIMEOUT_MS);
|
|
200
|
+
// Activity-based timeout - reset on any output
|
|
201
|
+
const resetInactivityTimer = () => {
|
|
202
|
+
clearTimeout(inactivityTimer);
|
|
203
|
+
inactivityTimer = setTimeout(() => {
|
|
204
|
+
proc.kill('SIGTERM');
|
|
205
|
+
reject(new Error('TIMEOUT'));
|
|
206
|
+
}, INACTIVITY_TIMEOUT_MS);
|
|
207
|
+
};
|
|
208
|
+
// Start inactivity timer
|
|
209
|
+
resetInactivityTimer();
|
|
188
210
|
proc.stdout.on('data', (data) => {
|
|
211
|
+
resetInactivityTimer(); // Still streaming = reset timer
|
|
189
212
|
if (stdout.length < MAX_BUFFER_SIZE) {
|
|
190
213
|
stdout += data.toString();
|
|
191
214
|
if (stdout.length > MAX_BUFFER_SIZE) {
|
|
@@ -195,6 +218,7 @@ function runCodexCli(prompt, workingDir) {
|
|
|
195
218
|
}
|
|
196
219
|
});
|
|
197
220
|
proc.stderr.on('data', (data) => {
|
|
221
|
+
resetInactivityTimer(); // Still streaming = reset timer
|
|
198
222
|
if (stderr.length < MAX_BUFFER_SIZE) {
|
|
199
223
|
stderr += data.toString();
|
|
200
224
|
if (stderr.length > MAX_BUFFER_SIZE) {
|
|
@@ -202,13 +226,9 @@ function runCodexCli(prompt, workingDir) {
|
|
|
202
226
|
}
|
|
203
227
|
}
|
|
204
228
|
});
|
|
205
|
-
// Timeout handling
|
|
206
|
-
const timeout = setTimeout(() => {
|
|
207
|
-
proc.kill('SIGTERM');
|
|
208
|
-
reject(new Error('TIMEOUT'));
|
|
209
|
-
}, TIMEOUT_MS);
|
|
210
229
|
proc.on('close', (code) => {
|
|
211
|
-
clearTimeout(
|
|
230
|
+
clearTimeout(inactivityTimer);
|
|
231
|
+
clearTimeout(maxTimer);
|
|
212
232
|
resolve({
|
|
213
233
|
stdout,
|
|
214
234
|
stderr,
|
|
@@ -217,7 +237,8 @@ function runCodexCli(prompt, workingDir) {
|
|
|
217
237
|
});
|
|
218
238
|
});
|
|
219
239
|
proc.on('error', (err) => {
|
|
220
|
-
clearTimeout(
|
|
240
|
+
clearTimeout(inactivityTimer);
|
|
241
|
+
clearTimeout(maxTimer);
|
|
221
242
|
reject(err);
|
|
222
243
|
});
|
|
223
244
|
});
|
package/dist/cli/gemini.js
CHANGED
|
@@ -9,7 +9,9 @@ import { spawn } from 'child_process';
|
|
|
9
9
|
import { existsSync } from 'fs';
|
|
10
10
|
import { build7SectionPrompt, buildDeveloperInstructions, buildRetryPrompt, isValidFeedbackOutput } from '../prompt.js';
|
|
11
11
|
import { createTimeoutError, createCliNotFoundError, getSuggestion } from '../errors.js';
|
|
12
|
-
|
|
12
|
+
// Activity-based timeout: reset on output, kill on silence
|
|
13
|
+
const INACTIVITY_TIMEOUT_MS = 120000; // 2 min of no output = timeout
|
|
14
|
+
const MAX_TIMEOUT_MS = 3600000; // 60 min absolute max (edge case safety)
|
|
13
15
|
const MAX_RETRIES = 2;
|
|
14
16
|
const MAX_BUFFER_SIZE = 1024 * 1024; // 1MB max buffer to prevent memory issues
|
|
15
17
|
/**
|
|
@@ -139,11 +141,15 @@ async function runWithRetry(request, attempt, previousError, previousOutput) {
|
|
|
139
141
|
model: 'gemini'
|
|
140
142
|
};
|
|
141
143
|
}
|
|
142
|
-
if (err.message === 'TIMEOUT') {
|
|
144
|
+
if (err.message === 'TIMEOUT' || err.message === 'MAX_TIMEOUT') {
|
|
145
|
+
const isMaxTimeout = err.message === 'MAX_TIMEOUT';
|
|
146
|
+
const timeoutMs = isMaxTimeout ? MAX_TIMEOUT_MS : INACTIVITY_TIMEOUT_MS;
|
|
143
147
|
return {
|
|
144
148
|
success: false,
|
|
145
|
-
error: createTimeoutError('gemini',
|
|
146
|
-
suggestion:
|
|
149
|
+
error: createTimeoutError('gemini', timeoutMs),
|
|
150
|
+
suggestion: isMaxTimeout
|
|
151
|
+
? 'Task exceeded 60 minute maximum. Try a smaller scope.'
|
|
152
|
+
: 'No output for 2 minutes. Process may be hung. Try a smaller scope or use --focus.',
|
|
147
153
|
model: 'gemini'
|
|
148
154
|
};
|
|
149
155
|
}
|
|
@@ -182,7 +188,24 @@ function runGeminiCli(prompt, workingDir) {
|
|
|
182
188
|
let stdout = '';
|
|
183
189
|
let stderr = '';
|
|
184
190
|
let truncated = false;
|
|
191
|
+
let inactivityTimer;
|
|
192
|
+
// Max timeout - absolute cap (60 min)
|
|
193
|
+
const maxTimer = setTimeout(() => {
|
|
194
|
+
proc.kill('SIGTERM');
|
|
195
|
+
reject(new Error('MAX_TIMEOUT'));
|
|
196
|
+
}, MAX_TIMEOUT_MS);
|
|
197
|
+
// Activity-based timeout - reset on any output
|
|
198
|
+
const resetInactivityTimer = () => {
|
|
199
|
+
clearTimeout(inactivityTimer);
|
|
200
|
+
inactivityTimer = setTimeout(() => {
|
|
201
|
+
proc.kill('SIGTERM');
|
|
202
|
+
reject(new Error('TIMEOUT'));
|
|
203
|
+
}, INACTIVITY_TIMEOUT_MS);
|
|
204
|
+
};
|
|
205
|
+
// Start inactivity timer
|
|
206
|
+
resetInactivityTimer();
|
|
185
207
|
proc.stdout.on('data', (data) => {
|
|
208
|
+
resetInactivityTimer(); // Still streaming = reset timer
|
|
186
209
|
if (stdout.length < MAX_BUFFER_SIZE) {
|
|
187
210
|
stdout += data.toString();
|
|
188
211
|
if (stdout.length > MAX_BUFFER_SIZE) {
|
|
@@ -192,6 +215,7 @@ function runGeminiCli(prompt, workingDir) {
|
|
|
192
215
|
}
|
|
193
216
|
});
|
|
194
217
|
proc.stderr.on('data', (data) => {
|
|
218
|
+
resetInactivityTimer(); // Still streaming = reset timer
|
|
195
219
|
if (stderr.length < MAX_BUFFER_SIZE) {
|
|
196
220
|
stderr += data.toString();
|
|
197
221
|
if (stderr.length > MAX_BUFFER_SIZE) {
|
|
@@ -199,13 +223,9 @@ function runGeminiCli(prompt, workingDir) {
|
|
|
199
223
|
}
|
|
200
224
|
}
|
|
201
225
|
});
|
|
202
|
-
// Timeout handling
|
|
203
|
-
const timeout = setTimeout(() => {
|
|
204
|
-
proc.kill('SIGTERM');
|
|
205
|
-
reject(new Error('TIMEOUT'));
|
|
206
|
-
}, TIMEOUT_MS);
|
|
207
226
|
proc.on('close', (code) => {
|
|
208
|
-
clearTimeout(
|
|
227
|
+
clearTimeout(inactivityTimer);
|
|
228
|
+
clearTimeout(maxTimer);
|
|
209
229
|
resolve({
|
|
210
230
|
stdout,
|
|
211
231
|
stderr,
|
|
@@ -214,7 +234,8 @@ function runGeminiCli(prompt, workingDir) {
|
|
|
214
234
|
});
|
|
215
235
|
});
|
|
216
236
|
proc.on('error', (err) => {
|
|
217
|
-
clearTimeout(
|
|
237
|
+
clearTimeout(inactivityTimer);
|
|
238
|
+
clearTimeout(maxTimer);
|
|
218
239
|
reject(err);
|
|
219
240
|
});
|
|
220
241
|
});
|
package/dist/tools/feedback.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export declare const FeedbackInputSchema: z.ZodObject<{
|
|
|
9
9
|
analyzedFiles: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
10
10
|
focusAreas: z.ZodOptional<z.ZodArray<z.ZodEnum<["security", "performance", "architecture", "correctness", "maintainability", "scalability", "testing", "documentation"]>, "many">>;
|
|
11
11
|
customPrompt: z.ZodOptional<z.ZodString>;
|
|
12
|
+
reasoningEffort: z.ZodOptional<z.ZodEnum<["high", "xhigh"]>>;
|
|
12
13
|
}, "strip", z.ZodTypeAny, {
|
|
13
14
|
workingDir: string;
|
|
14
15
|
ccOutput: string;
|
|
@@ -16,6 +17,7 @@ export declare const FeedbackInputSchema: z.ZodObject<{
|
|
|
16
17
|
analyzedFiles?: string[] | undefined;
|
|
17
18
|
focusAreas?: ("security" | "performance" | "architecture" | "correctness" | "maintainability" | "scalability" | "testing" | "documentation")[] | undefined;
|
|
18
19
|
customPrompt?: string | undefined;
|
|
20
|
+
reasoningEffort?: "high" | "xhigh" | undefined;
|
|
19
21
|
}, {
|
|
20
22
|
workingDir: string;
|
|
21
23
|
ccOutput: string;
|
|
@@ -23,6 +25,7 @@ export declare const FeedbackInputSchema: z.ZodObject<{
|
|
|
23
25
|
analyzedFiles?: string[] | undefined;
|
|
24
26
|
focusAreas?: ("security" | "performance" | "architecture" | "correctness" | "maintainability" | "scalability" | "testing" | "documentation")[] | undefined;
|
|
25
27
|
customPrompt?: string | undefined;
|
|
28
|
+
reasoningEffort?: "high" | "xhigh" | undefined;
|
|
26
29
|
}>;
|
|
27
30
|
export type FeedbackInput = z.infer<typeof FeedbackInputSchema>;
|
|
28
31
|
/**
|
package/dist/tools/feedback.js
CHANGED
|
@@ -16,7 +16,8 @@ export const FeedbackInputSchema = z.object({
|
|
|
16
16
|
'security', 'performance', 'architecture', 'correctness',
|
|
17
17
|
'maintainability', 'scalability', 'testing', 'documentation'
|
|
18
18
|
])).optional().describe('Areas to focus the review on'),
|
|
19
|
-
customPrompt: z.string().optional().describe('Custom instructions for the reviewer')
|
|
19
|
+
customPrompt: z.string().optional().describe('Custom instructions for the reviewer'),
|
|
20
|
+
reasoningEffort: z.enum(['high', 'xhigh']).optional().describe('Codex reasoning effort level (default: high, use xhigh for deeper analysis)')
|
|
20
21
|
});
|
|
21
22
|
/**
|
|
22
23
|
* Convert tool input to FeedbackRequest
|
|
@@ -28,7 +29,8 @@ function toFeedbackRequest(input) {
|
|
|
28
29
|
outputType: input.outputType,
|
|
29
30
|
analyzedFiles: input.analyzedFiles,
|
|
30
31
|
focusAreas: input.focusAreas,
|
|
31
|
-
customPrompt: input.customPrompt
|
|
32
|
+
customPrompt: input.customPrompt,
|
|
33
|
+
reasoningEffort: input.reasoningEffort
|
|
32
34
|
};
|
|
33
35
|
}
|
|
34
36
|
/**
|
package/dist/types.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
export type OutputType = 'plan' | 'findings' | 'analysis' | 'proposal';
|
|
5
5
|
export type FocusArea = 'security' | 'performance' | 'architecture' | 'correctness' | 'maintainability' | 'scalability' | 'testing' | 'documentation';
|
|
6
6
|
export type CliType = 'codex' | 'gemini';
|
|
7
|
+
export type ReasoningEffort = 'high' | 'xhigh';
|
|
7
8
|
export interface FeedbackRequest {
|
|
8
9
|
workingDir: string;
|
|
9
10
|
ccOutput: string;
|
|
@@ -11,6 +12,7 @@ export interface FeedbackRequest {
|
|
|
11
12
|
analyzedFiles?: string[];
|
|
12
13
|
focusAreas?: FocusArea[];
|
|
13
14
|
customPrompt?: string;
|
|
15
|
+
reasoningEffort?: ReasoningEffort;
|
|
14
16
|
}
|
|
15
17
|
export interface FeedbackSuccess {
|
|
16
18
|
success: true;
|