openairev 0.3.6 → 0.3.8
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 +11 -0
- package/package.json +1 -1
- package/src/agents/claude-code.js +2 -1
- package/src/agents/codex.js +2 -2
- package/src/agents/exec-helper.js +15 -2
- package/src/cli/init.js +18 -14
- package/src/cli/wait.js +5 -1
- package/src/mcp/mcp-server.js +20 -1
- package/src/review/review-runner.js +2 -0
package/README.md
CHANGED
|
@@ -94,6 +94,15 @@ Options:
|
|
|
94
94
|
--dry-run Show what would happen without executing
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
+
### `openairev wait`
|
|
98
|
+
|
|
99
|
+
Stream review progress and block until the verdict is ready.
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
Options:
|
|
103
|
+
--file <path> Path to progress.json (auto-detected from .openairev/)
|
|
104
|
+
```
|
|
105
|
+
|
|
97
106
|
### `openairev resume`
|
|
98
107
|
|
|
99
108
|
Resume an active or blocked workflow. If blocked on `awaiting_user`, prompts for answers to pending questions.
|
|
@@ -213,6 +222,7 @@ Restart your agent CLI after adding.
|
|
|
213
222
|
| Tool | Description |
|
|
214
223
|
|------|-------------|
|
|
215
224
|
| `openairev_review` | Start a review in the background. Returns immediately. |
|
|
225
|
+
| `openairev_cancel` | Cancel the currently running review. |
|
|
216
226
|
| `openairev_status` | Check review progress (prefer `openairev wait` instead). |
|
|
217
227
|
| `openairev_run_tests` | Run project test suite |
|
|
218
228
|
| `openairev_run_lint` | Run linter |
|
|
@@ -224,6 +234,7 @@ The review runs asynchronously so you can see progress:
|
|
|
224
234
|
|
|
225
235
|
1. Call `openairev_review` → starts review in background, returns immediately
|
|
226
236
|
2. Run `openairev wait` via Bash → streams progress (files read, commands run, tokens) and outputs the verdict when done
|
|
237
|
+
3. If stuck, call `openairev_cancel` to kill the running review and retry with a smaller diff
|
|
227
238
|
|
|
228
239
|
One MCP call + one Bash call. No polling, no sleep loops. The AI can also launch the review in a sub-agent and continue other work while it runs.
|
|
229
240
|
|
package/package.json
CHANGED
|
@@ -23,6 +23,7 @@ export class ClaudeCodeAdapter {
|
|
|
23
23
|
continueSession = false,
|
|
24
24
|
sessionName = null,
|
|
25
25
|
stream = false,
|
|
26
|
+
signal,
|
|
26
27
|
} = {}) {
|
|
27
28
|
const args = ['-p', prompt];
|
|
28
29
|
|
|
@@ -51,7 +52,7 @@ export class ClaudeCodeAdapter {
|
|
|
51
52
|
onProgress: stream.onProgress,
|
|
52
53
|
}) : undefined;
|
|
53
54
|
|
|
54
|
-
const result = await exec(this.cmd, args, { onData: summarizer, cwd: this.cwd });
|
|
55
|
+
const result = await exec(this.cmd, args, { onData: summarizer, cwd: this.cwd, signal });
|
|
55
56
|
|
|
56
57
|
if (stream) {
|
|
57
58
|
return parseClaudeStreamOutput(result.stdout, {
|
package/src/agents/codex.js
CHANGED
|
@@ -16,7 +16,7 @@ export class CodexAdapter {
|
|
|
16
16
|
this.sessionId = id;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
async run(prompt, { useSchema = false, schemaFile = 'verdict-schema.json', continueSession = false, sessionName = null, stream = false } = {}) {
|
|
19
|
+
async run(prompt, { useSchema = false, schemaFile = 'verdict-schema.json', continueSession = false, sessionName = null, stream = false, signal } = {}) {
|
|
20
20
|
const args = ['exec'];
|
|
21
21
|
|
|
22
22
|
if (continueSession && this.sessionId) {
|
|
@@ -36,7 +36,7 @@ export class CodexAdapter {
|
|
|
36
36
|
tty: stream.tty !== false,
|
|
37
37
|
onProgress: stream.onProgress,
|
|
38
38
|
}) : undefined;
|
|
39
|
-
const result = await exec(this.cmd, args, { onData: summarizer });
|
|
39
|
+
const result = await exec(this.cmd, args, { onData: summarizer, signal });
|
|
40
40
|
|
|
41
41
|
try {
|
|
42
42
|
const lines = result.stdout.trim().split('\n');
|
|
@@ -2,13 +2,24 @@ import { spawn } from 'child_process';
|
|
|
2
2
|
|
|
3
3
|
const MAX_BUFFER = 10 * 1024 * 1024;
|
|
4
4
|
|
|
5
|
-
export function exec(cmd, args, { onData, cwd } = {}) {
|
|
5
|
+
export function exec(cmd, args, { onData, cwd, signal } = {}) {
|
|
6
6
|
return new Promise((resolve, reject) => {
|
|
7
7
|
const child = spawn(cmd, args, {
|
|
8
8
|
timeout: 300_000,
|
|
9
9
|
cwd,
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
+
if (signal) {
|
|
13
|
+
if (signal.aborted) {
|
|
14
|
+
child.kill();
|
|
15
|
+
return reject(new Error(`${cmd} aborted`));
|
|
16
|
+
}
|
|
17
|
+
signal.addEventListener('abort', () => {
|
|
18
|
+
killed = true;
|
|
19
|
+
child.kill();
|
|
20
|
+
}, { once: true });
|
|
21
|
+
}
|
|
22
|
+
|
|
12
23
|
const stdoutChunks = [];
|
|
13
24
|
const stderrChunks = [];
|
|
14
25
|
let stdoutLen = 0;
|
|
@@ -43,7 +54,9 @@ export function exec(cmd, args, { onData, cwd } = {}) {
|
|
|
43
54
|
child.on('close', (code) => {
|
|
44
55
|
const stdout = Buffer.concat(stdoutChunks).toString();
|
|
45
56
|
const stderr = Buffer.concat(stderrChunks).toString();
|
|
46
|
-
if (
|
|
57
|
+
if (signal?.aborted) {
|
|
58
|
+
reject(new Error(`${cmd} aborted`));
|
|
59
|
+
} else if (killed) {
|
|
47
60
|
reject(new Error(`${cmd} output exceeded ${MAX_BUFFER} bytes`));
|
|
48
61
|
} else if (code !== 0 && !stdout) {
|
|
49
62
|
reject(new Error(`${cmd} failed (exit ${code}): ${stderr}`));
|
package/src/cli/init.js
CHANGED
|
@@ -257,14 +257,16 @@ ${marker}
|
|
|
257
257
|
|
|
258
258
|
This project uses OpenAIRev for independent AI code review. When the user asks to "review", "get a review", or "check my changes":
|
|
259
259
|
|
|
260
|
-
1. Call \`openairev_review\` with \`executor: "claude_code"\` and \`diff_cmd\` scoped to the files you changed (e.g. \`"git diff HEAD -- src/auth.ts src/routes.ts"\`).
|
|
261
|
-
2. Run \`openairev wait\` via Bash
|
|
262
|
-
|
|
263
|
-
**
|
|
264
|
-
-
|
|
265
|
-
-
|
|
266
|
-
-
|
|
267
|
-
-
|
|
260
|
+
1. Call \`openairev_review\` (MCP tool) with \`executor: "claude_code"\` and \`diff_cmd\` scoped to the files you changed (e.g. \`"git diff HEAD -- src/auth.ts src/routes.ts"\`). It returns immediately.
|
|
261
|
+
2. Run \`openairev wait\` via Bash. It streams live progress and blocks until the verdict is ready — one command, that's it.
|
|
262
|
+
|
|
263
|
+
**Rules — you MUST follow these:**
|
|
264
|
+
- ALWAYS scope \`diff_cmd\` to your changed files. Never send the full repo diff — the reviewer will run out of budget.
|
|
265
|
+
- NEVER use \`sleep\`, polling loops, or manually read progress.json. \`openairev wait\` handles everything.
|
|
266
|
+
- NEVER call \`openairev_status\`. Use \`openairev wait\` instead.
|
|
267
|
+
- If a review is stuck or you want to retry, call \`openairev_cancel\` to kill the running review, then start a new one.
|
|
268
|
+
- You MAY launch the review + wait in a sub-agent and continue other work in parallel.
|
|
269
|
+
- Treat review feedback as peer review — use your judgment, focus on \`critical_issues\` and \`repair_instructions\`.
|
|
268
270
|
${marker}
|
|
269
271
|
`;
|
|
270
272
|
|
|
@@ -311,13 +313,15 @@ ${marker}
|
|
|
311
313
|
|
|
312
314
|
This project uses OpenAIRev for independent AI code review. When the user asks to "review", "get a review", or "check my changes":
|
|
313
315
|
|
|
314
|
-
1. Call \`openairev_review\` with \`executor: "codex"\` and \`diff_cmd\` scoped to the files you changed (e.g. \`"git diff HEAD -- src/auth.ts src/routes.ts"\`).
|
|
315
|
-
2. Run \`openairev wait\`
|
|
316
|
+
1. Call \`openairev_review\` (MCP tool) with \`executor: "codex"\` and \`diff_cmd\` scoped to the files you changed (e.g. \`"git diff HEAD -- src/auth.ts src/routes.ts"\`). It returns immediately.
|
|
317
|
+
2. Run \`openairev wait\` via Bash. It streams live progress and blocks until the verdict is ready — one command, that's it.
|
|
316
318
|
|
|
317
|
-
**
|
|
318
|
-
-
|
|
319
|
-
-
|
|
320
|
-
-
|
|
319
|
+
**Rules — you MUST follow these:**
|
|
320
|
+
- ALWAYS scope \`diff_cmd\` to your changed files. Never send the full repo diff — the reviewer will run out of budget.
|
|
321
|
+
- NEVER use \`sleep\`, polling loops, or manually read progress.json. \`openairev wait\` handles everything.
|
|
322
|
+
- NEVER call \`openairev_status\`. Use \`openairev wait\` instead.
|
|
323
|
+
- If a review is stuck or you want to retry, call \`openairev_cancel\` to kill the running review, then start a new one.
|
|
324
|
+
- Treat review feedback as peer review — use your judgment, focus on \`critical_issues\` and \`repair_instructions\`.
|
|
321
325
|
${marker}
|
|
322
326
|
`;
|
|
323
327
|
|
package/src/cli/wait.js
CHANGED
|
@@ -23,7 +23,7 @@ export async function waitCommand(options) {
|
|
|
23
23
|
}
|
|
24
24
|
lastLen = lines.length;
|
|
25
25
|
|
|
26
|
-
if (data.status === 'completed' || data.status === 'error') {
|
|
26
|
+
if (data.status === 'completed' || data.status === 'error' || data.status === 'cancelled') {
|
|
27
27
|
clearInterval(timer);
|
|
28
28
|
printResult(data);
|
|
29
29
|
resolve();
|
|
@@ -41,6 +41,10 @@ function readProgress(path) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
function printResult(data) {
|
|
44
|
+
if (data.status === 'cancelled') {
|
|
45
|
+
console.log('\nReview cancelled.');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
44
48
|
if (data.status === 'error') {
|
|
45
49
|
console.log(`\nReview failed: ${data.error}`);
|
|
46
50
|
process.exit(1);
|
package/src/mcp/mcp-server.js
CHANGED
|
@@ -17,6 +17,7 @@ const config = loadConfig(cwd);
|
|
|
17
17
|
const PROGRESS_FILE = join(cwd, '.openairev', 'progress.json');
|
|
18
18
|
|
|
19
19
|
let activeReview = null;
|
|
20
|
+
let activeAbort = null;
|
|
20
21
|
|
|
21
22
|
const server = new McpServer({
|
|
22
23
|
name: 'openairev',
|
|
@@ -60,12 +61,14 @@ server.tool(
|
|
|
60
61
|
writeProgress({ status: 'running', reviewer: reviewerName, started: new Date().toISOString(), progress: lines, verdict: null });
|
|
61
62
|
};
|
|
62
63
|
|
|
64
|
+
activeAbort = new AbortController();
|
|
63
65
|
activeReview = runReview(diffContent, {
|
|
64
66
|
config,
|
|
65
67
|
reviewerName,
|
|
66
68
|
taskDescription: task_description,
|
|
67
69
|
cwd,
|
|
68
70
|
stream: { onProgress },
|
|
71
|
+
signal: activeAbort.signal,
|
|
69
72
|
}).then((review) => {
|
|
70
73
|
const session = createSession({ executor: execAgent, reviewer: reviewerName });
|
|
71
74
|
session.iterations.push({ round: 1, review, timestamp: new Date().toISOString() });
|
|
@@ -81,10 +84,13 @@ server.tool(
|
|
|
81
84
|
executor_feedback: review.executor_feedback,
|
|
82
85
|
});
|
|
83
86
|
activeReview = null;
|
|
87
|
+
activeAbort = null;
|
|
84
88
|
return review;
|
|
85
89
|
}).catch((err) => {
|
|
86
|
-
|
|
90
|
+
const status = activeAbort?.signal?.aborted ? 'cancelled' : 'error';
|
|
91
|
+
writeProgress({ status, reviewer: reviewerName, error: err.message, progress: [], verdict: null });
|
|
87
92
|
activeReview = null;
|
|
93
|
+
activeAbort = null;
|
|
88
94
|
});
|
|
89
95
|
|
|
90
96
|
return {
|
|
@@ -128,6 +134,19 @@ server.tool(
|
|
|
128
134
|
}
|
|
129
135
|
);
|
|
130
136
|
|
|
137
|
+
server.tool(
|
|
138
|
+
'openairev_cancel',
|
|
139
|
+
'Cancel the currently running review. Use this when the review is taking too long, the diff was too large, or you want to retry with different parameters.',
|
|
140
|
+
{},
|
|
141
|
+
async () => {
|
|
142
|
+
if (!activeReview || !activeAbort) {
|
|
143
|
+
return { content: [{ type: 'text', text: 'No review is currently running.' }] };
|
|
144
|
+
}
|
|
145
|
+
activeAbort.abort();
|
|
146
|
+
return { content: [{ type: 'text', text: 'Review cancelled.' }] };
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
|
|
131
150
|
server.tool(
|
|
132
151
|
'openairev_run_tests',
|
|
133
152
|
'Run the project test suite and return pass/fail results.',
|
|
@@ -13,6 +13,7 @@ export async function runReview(content, {
|
|
|
13
13
|
cwd = process.cwd(),
|
|
14
14
|
sessionId = null,
|
|
15
15
|
stream = false,
|
|
16
|
+
signal,
|
|
16
17
|
}) {
|
|
17
18
|
const adapter = createAdapter(reviewerName, config, { cwd });
|
|
18
19
|
|
|
@@ -41,6 +42,7 @@ export async function runReview(content, {
|
|
|
41
42
|
continueSession: !!sessionId,
|
|
42
43
|
sessionName: sessionId ? undefined : `review-${Date.now()}`,
|
|
43
44
|
stream: stream ? { reviewerName, tty: stream === true, onProgress: stream.onProgress } : false,
|
|
45
|
+
signal,
|
|
44
46
|
});
|
|
45
47
|
|
|
46
48
|
const verdict = extractVerdict(result);
|