openairev 0.3.1 → 0.3.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/README.md +12 -2
- package/package.json +1 -1
- package/src/agents/codex.js +1 -0
- package/src/agents/stream-summarizer.js +5 -2
- package/src/cli/init.js +17 -10
- package/src/mcp/mcp-server.js +79 -28
- package/src/review/review-runner.js +1 -1
package/README.md
CHANGED
|
@@ -212,12 +212,22 @@ Restart your agent CLI after adding.
|
|
|
212
212
|
|
|
213
213
|
| Tool | Description |
|
|
214
214
|
|------|-------------|
|
|
215
|
-
| `openairev_review` |
|
|
216
|
-
| `openairev_status` | Check
|
|
215
|
+
| `openairev_review` | Start a review in the background. Returns immediately. |
|
|
216
|
+
| `openairev_status` | Check review progress and get the verdict when ready. |
|
|
217
217
|
| `openairev_run_tests` | Run project test suite |
|
|
218
218
|
| `openairev_run_lint` | Run linter |
|
|
219
219
|
| `openairev_get_diff` | Get current git diff |
|
|
220
220
|
|
|
221
|
+
### Async Review Flow
|
|
222
|
+
|
|
223
|
+
The review runs asynchronously so you can see progress:
|
|
224
|
+
|
|
225
|
+
1. Call `openairev_review` → starts review in background, returns immediately
|
|
226
|
+
2. Read `.openairev/progress.json` → shows live progress (files read, commands run, tokens)
|
|
227
|
+
3. When `status` is `"completed"`, the verdict and feedback are in the same file
|
|
228
|
+
|
|
229
|
+
The executor AI can read the progress file directly (no MCP round-trip needed), or use `openairev_status` as an alternative. The AI can also launch the review in a sub-agent and continue other work while it runs.
|
|
230
|
+
|
|
221
231
|
## Config
|
|
222
232
|
|
|
223
233
|
Generated by `openairev init` at `.openairev/config.yaml`:
|
package/package.json
CHANGED
package/src/agents/codex.js
CHANGED
|
@@ -34,6 +34,7 @@ export class CodexAdapter {
|
|
|
34
34
|
const summarizer = stream ? createCodexSummarizer({
|
|
35
35
|
reviewerName: stream.reviewerName || 'codex',
|
|
36
36
|
tty: stream.tty !== false,
|
|
37
|
+
onProgress: stream.onProgress,
|
|
37
38
|
}) : undefined;
|
|
38
39
|
const result = await exec(this.cmd, args, { onData: summarizer });
|
|
39
40
|
|
|
@@ -6,7 +6,7 @@ import chalk from 'chalk';
|
|
|
6
6
|
* tty=false: collects plain-text progress lines silently.
|
|
7
7
|
* Both modes always collect lines for the final summary.
|
|
8
8
|
*/
|
|
9
|
-
export function createCodexSummarizer({ reviewerName, tty = true } = {}) {
|
|
9
|
+
export function createCodexSummarizer({ reviewerName, tty = true, onProgress } = {}) {
|
|
10
10
|
const seenFiles = new Set();
|
|
11
11
|
const progressLines = [];
|
|
12
12
|
let buffer = '';
|
|
@@ -35,7 +35,10 @@ export function createCodexSummarizer({ reviewerName, tty = true } = {}) {
|
|
|
35
35
|
summarizer.getProgress = () => progressLines;
|
|
36
36
|
|
|
37
37
|
function emit(msg, color) {
|
|
38
|
-
if (progressLines.length < 200)
|
|
38
|
+
if (progressLines.length < 200) {
|
|
39
|
+
progressLines.push(msg);
|
|
40
|
+
if (onProgress) onProgress(progressLines);
|
|
41
|
+
}
|
|
39
42
|
if (tty) {
|
|
40
43
|
const colorFn = color === 'cyan' ? chalk.cyan
|
|
41
44
|
: color === 'green' ? chalk.green
|
package/src/cli/init.js
CHANGED
|
@@ -237,13 +237,17 @@ function setupClaudeCode(cwd) {
|
|
|
237
237
|
${marker}
|
|
238
238
|
## OpenAIRev — Cross-Model Code Review
|
|
239
239
|
|
|
240
|
-
This project uses OpenAIRev for independent AI code review. When the user asks to "review", "get a review", or "check my changes"
|
|
240
|
+
This project uses OpenAIRev for independent AI code review. When the user asks to "review", "get a review", or "check my changes":
|
|
241
241
|
|
|
242
|
-
|
|
243
|
-
-
|
|
244
|
-
|
|
242
|
+
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"\`). The review starts in the background and returns immediately.
|
|
243
|
+
2. To check progress, read \`.openairev/progress.json\` — it updates in real-time showing what the reviewer is reading and doing.
|
|
244
|
+
3. When \`progress.json\` shows \`"status": "completed"\`, the verdict and feedback are in the same file.
|
|
245
|
+
|
|
246
|
+
**Tips**:
|
|
247
|
+
- Always use \`diff_cmd\` to scope the diff to your changed files. Do NOT let it auto-detect — the full repo diff may be too large.
|
|
248
|
+
- You can launch the review in a **sub-agent** and continue other work while it runs.
|
|
245
249
|
- When you receive review feedback, treat it as **peer review** — use your judgment, don't blindly apply every suggestion.
|
|
246
|
-
-
|
|
250
|
+
- Focus on high-confidence \`critical_issues\` and \`repair_instructions\`.
|
|
247
251
|
${marker}
|
|
248
252
|
`;
|
|
249
253
|
|
|
@@ -297,13 +301,16 @@ function setupCodex(cwd) {
|
|
|
297
301
|
${marker}
|
|
298
302
|
## OpenAIRev — Cross-Model Code Review
|
|
299
303
|
|
|
300
|
-
This project uses OpenAIRev for independent AI code review. When the user asks to "review", "get a review", or "check my changes"
|
|
304
|
+
This project uses OpenAIRev for independent AI code review. When the user asks to "review", "get a review", or "check my changes":
|
|
305
|
+
|
|
306
|
+
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"\`). The review starts in the background and returns immediately.
|
|
307
|
+
2. To check progress, read \`.openairev/progress.json\` — it updates in real-time showing what the reviewer is reading and doing.
|
|
308
|
+
3. When \`progress.json\` shows \`"status": "completed"\`, the verdict and feedback are in the same file.
|
|
301
309
|
|
|
302
|
-
|
|
303
|
-
-
|
|
304
|
-
- **IMPORTANT**: Pass only the diff for files you changed using \`diff_cmd\`, e.g. \`"git diff HEAD -- src/auth.ts src/routes.ts"\`. Do NOT let it auto-detect — the full repo diff may be too large.
|
|
310
|
+
**Tips**:
|
|
311
|
+
- Always use \`diff_cmd\` to scope the diff to your changed files. Do NOT let it auto-detect — the full repo diff may be too large.
|
|
305
312
|
- When you receive review feedback, treat it as **peer review** — use your judgment, don't blindly apply every suggestion.
|
|
306
|
-
-
|
|
313
|
+
- Focus on high-confidence \`critical_issues\` and \`repair_instructions\`.
|
|
307
314
|
${marker}
|
|
308
315
|
`;
|
|
309
316
|
|
package/src/mcp/mcp-server.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { mkdirSync, writeFileSync, readFileSync, existsSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
3
5
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
6
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
7
|
import { z } from 'zod';
|
|
@@ -12,6 +14,9 @@ import { VERSION } from '../version.js';
|
|
|
12
14
|
|
|
13
15
|
const cwd = process.cwd();
|
|
14
16
|
const config = loadConfig(cwd);
|
|
17
|
+
const PROGRESS_FILE = join(cwd, '.openairev', 'progress.json');
|
|
18
|
+
|
|
19
|
+
let activeReview = null;
|
|
15
20
|
|
|
16
21
|
const server = new McpServer({
|
|
17
22
|
name: 'openairev',
|
|
@@ -20,7 +25,7 @@ const server = new McpServer({
|
|
|
20
25
|
|
|
21
26
|
server.tool(
|
|
22
27
|
'openairev_review',
|
|
23
|
-
'TRIGGER: Use this tool when the user says "review", "review my code", "get a review", "check my changes", "openairev", or asks for independent/cross-model code review. Sends current code changes to a DIFFERENT AI model for independent review.
|
|
28
|
+
'TRIGGER: Use this tool when the user says "review", "review my code", "get a review", "check my changes", "openairev", or asks for independent/cross-model code review. Sends current code changes to a DIFFERENT AI model for independent review. The review runs in the background and returns immediately. To check progress, read .openairev/progress.json — it updates in real-time. When status is "completed", the verdict is in the same file.',
|
|
24
29
|
{
|
|
25
30
|
executor: z.string().optional().describe('Which agent wrote the code (claude_code or codex). If you are Claude Code, set this to "claude_code". If you are Codex, set this to "codex".'),
|
|
26
31
|
diff: z.string().optional().describe('The diff to review. IMPORTANT: Pass only the diff for files YOU changed, not the entire repo. Use `git diff HEAD -- file1 file2` to scope it. If omitted, auto-detects from git which may be too large.'),
|
|
@@ -48,46 +53,78 @@ server.tool(
|
|
|
48
53
|
return { content: [{ type: 'text', text: 'No changes found to review.' }] };
|
|
49
54
|
}
|
|
50
55
|
|
|
51
|
-
|
|
56
|
+
mkdirSync(join(cwd, '.openairev'), { recursive: true });
|
|
57
|
+
writeProgress({ status: 'running', reviewer: reviewerName, started: new Date().toISOString(), progress: [], verdict: null });
|
|
58
|
+
|
|
59
|
+
const onProgress = (lines) => {
|
|
60
|
+
writeProgress({ status: 'running', reviewer: reviewerName, started: new Date().toISOString(), progress: lines, verdict: null });
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
activeReview = runReview(diffContent, {
|
|
52
64
|
config,
|
|
53
65
|
reviewerName,
|
|
54
66
|
taskDescription: task_description,
|
|
55
67
|
cwd,
|
|
56
|
-
stream:
|
|
68
|
+
stream: { onProgress },
|
|
69
|
+
}).then((review) => {
|
|
70
|
+
const session = createSession({ executor: execAgent, reviewer: reviewerName });
|
|
71
|
+
session.iterations.push({ round: 1, review, timestamp: new Date().toISOString() });
|
|
72
|
+
session.final_verdict = review.verdict;
|
|
73
|
+
session.status = 'completed';
|
|
74
|
+
saveSession(session, cwd);
|
|
75
|
+
|
|
76
|
+
writeProgress({
|
|
77
|
+
status: 'completed',
|
|
78
|
+
reviewer: reviewerName,
|
|
79
|
+
progress: review.progress || [],
|
|
80
|
+
verdict: review.verdict,
|
|
81
|
+
executor_feedback: review.executor_feedback,
|
|
82
|
+
});
|
|
83
|
+
activeReview = null;
|
|
84
|
+
return review;
|
|
85
|
+
}).catch((err) => {
|
|
86
|
+
writeProgress({ status: 'error', reviewer: reviewerName, error: err.message, progress: [], verdict: null });
|
|
87
|
+
activeReview = null;
|
|
57
88
|
});
|
|
58
89
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const parts = [];
|
|
66
|
-
|
|
67
|
-
if (review.progress?.length > 0) {
|
|
68
|
-
parts.push({ type: 'text', text: `Review progress:\n${review.progress.map(l => ` ${l}`).join('\n')}` });
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const feedback = review.executor_feedback || JSON.stringify(review.verdict || review, null, 2);
|
|
72
|
-
parts.push({ type: 'text', text: feedback });
|
|
73
|
-
|
|
74
|
-
return { content: parts };
|
|
90
|
+
return {
|
|
91
|
+
content: [{
|
|
92
|
+
type: 'text',
|
|
93
|
+
text: `Review started. Reviewer: ${reviewerName}\n\nCall openairev_status to check progress and get the verdict when ready.`,
|
|
94
|
+
}],
|
|
95
|
+
};
|
|
75
96
|
}
|
|
76
97
|
);
|
|
77
98
|
|
|
78
99
|
server.tool(
|
|
79
100
|
'openairev_status',
|
|
80
|
-
'
|
|
101
|
+
'Check the progress and result of the current or most recent OpenAIRev review. Alternative to reading .openairev/progress.json directly.',
|
|
81
102
|
{},
|
|
82
103
|
async () => {
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return { content: [{ type: 'text', text: 'No review sessions found.' }] };
|
|
104
|
+
const progress = readProgress();
|
|
105
|
+
if (!progress) {
|
|
106
|
+
return { content: [{ type: 'text', text: 'No review in progress. Call openairev_review first.' }] };
|
|
87
107
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
108
|
+
|
|
109
|
+
if (progress.status === 'running') {
|
|
110
|
+
const lines = progress.progress || [];
|
|
111
|
+
const text = lines.length > 0
|
|
112
|
+
? `Review in progress (reviewer: ${progress.reviewer}):\n${lines.map(l => ` ${l}`).join('\n')}\n\nStill running... Call openairev_status again in a few seconds.`
|
|
113
|
+
: `Review in progress (reviewer: ${progress.reviewer}). Started: ${progress.started}\n\nCall openairev_status again in a few seconds.`;
|
|
114
|
+
return { content: [{ type: 'text', text }] };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (progress.status === 'error') {
|
|
118
|
+
return { content: [{ type: 'text', text: `Review failed: ${progress.error}` }] };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const parts = [];
|
|
122
|
+
if (progress.progress?.length > 0) {
|
|
123
|
+
parts.push({ type: 'text', text: `Review complete:\n${progress.progress.map(l => ` ${l}`).join('\n')}` });
|
|
124
|
+
}
|
|
125
|
+
const feedback = progress.executor_feedback || JSON.stringify(progress.verdict || {}, null, 2);
|
|
126
|
+
parts.push({ type: 'text', text: feedback });
|
|
127
|
+
return { content: parts };
|
|
91
128
|
}
|
|
92
129
|
);
|
|
93
130
|
|
|
@@ -125,6 +162,20 @@ server.tool(
|
|
|
125
162
|
}
|
|
126
163
|
);
|
|
127
164
|
|
|
128
|
-
|
|
165
|
+
function writeProgress(data) {
|
|
166
|
+
try {
|
|
167
|
+
writeFileSync(PROGRESS_FILE, JSON.stringify(data, null, 2));
|
|
168
|
+
} catch { /* non-critical */ }
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function readProgress() {
|
|
172
|
+
try {
|
|
173
|
+
if (!existsSync(PROGRESS_FILE)) return null;
|
|
174
|
+
return JSON.parse(readFileSync(PROGRESS_FILE, 'utf-8'));
|
|
175
|
+
} catch {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
129
180
|
const transport = new StdioServerTransport();
|
|
130
181
|
await server.connect(transport);
|
|
@@ -40,7 +40,7 @@ export async function runReview(content, {
|
|
|
40
40
|
schemaFile,
|
|
41
41
|
continueSession: !!sessionId,
|
|
42
42
|
sessionName: sessionId ? undefined : `review-${Date.now()}`,
|
|
43
|
-
stream: stream ? { reviewerName, tty: stream === true } : false,
|
|
43
|
+
stream: stream ? { reviewerName, tty: stream === true, onProgress: stream.onProgress } : false,
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
const verdict = extractVerdict(result);
|