@contextrail/code-review-agent 0.1.2-alpha.3 → 0.1.2-alpha.6
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 +21 -9
- package/dist/cli/help.js +33 -26
- package/dist/cli/parser.js +16 -0
- package/dist/cli/types.d.ts +4 -0
- package/dist/config/defaults.d.ts +1 -1
- package/dist/config/defaults.js +1 -1
- package/dist/config/index.js +8 -6
- package/dist/reviewers/executor.js +25 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,14 +34,17 @@ npm i -g @contextrail/code-review-agent
|
|
|
34
34
|
code-review-agent review --help
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
##
|
|
37
|
+
## Configuration
|
|
38
|
+
|
|
39
|
+
### Required (sensitive — use environment variables)
|
|
38
40
|
|
|
39
41
|
```bash
|
|
40
|
-
export CONTEXTRAIL_MCP_SERVER_URL="https://<your-mcp-host>"
|
|
41
42
|
export CONTEXTRAIL_MCP_JWT_TOKEN="<your-contextrail-jwt-token>"
|
|
42
43
|
export OPENROUTER_API_KEY="<your-openrouter-key>"
|
|
43
44
|
```
|
|
44
45
|
|
|
46
|
+
These contain credentials and should not be passed as CLI flags to avoid exposure in shell history and process listings.
|
|
47
|
+
|
|
45
48
|
### Getting Your ContextRail MCP Token
|
|
46
49
|
|
|
47
50
|
To get your `CONTEXTRAIL_MCP_JWT_TOKEN`:
|
|
@@ -51,14 +54,22 @@ To get your `CONTEXTRAIL_MCP_JWT_TOKEN`:
|
|
|
51
54
|
|
|
52
55
|
The token is used to authenticate with the ContextRail MCP server to retrieve your organization's review standards and contexts.
|
|
53
56
|
|
|
54
|
-
|
|
57
|
+
### Required (env var or CLI flag)
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
export CONTEXTRAIL_MCP_SERVER_URL="https://<your-mcp-host>"
|
|
61
|
+
# or pass inline:
|
|
62
|
+
# --mcp-server-url https://<your-mcp-host>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Optional
|
|
55
66
|
|
|
56
67
|
```bash
|
|
57
|
-
export LLM_MODEL_ORCHESTRATOR="google/gemini-3-flash-preview"
|
|
58
|
-
export LLM_MODEL_REVIEWER="qwen/qwen3-coder-next"
|
|
59
|
-
export LLM_MODEL_CRITIC="qwen/qwen3-coder-next"
|
|
60
|
-
export REVIEW_DOMAINS="security,architecture"
|
|
61
|
-
export PR_DESCRIPTION="Optional PR context"
|
|
68
|
+
export LLM_MODEL_ORCHESTRATOR="google/gemini-3-flash-preview" # or --orchestrator-model
|
|
69
|
+
export LLM_MODEL_REVIEWER="qwen/qwen3-coder-next" # or --reviewer-model
|
|
70
|
+
export LLM_MODEL_CRITIC="qwen/qwen3-coder-next" # or --critic-model
|
|
71
|
+
export REVIEW_DOMAINS="security,architecture" # or --domains
|
|
72
|
+
export PR_DESCRIPTION="Optional PR context" # or --pr-description
|
|
62
73
|
```
|
|
63
74
|
|
|
64
75
|
## Run Against a PR Diff
|
|
@@ -153,7 +164,8 @@ Exit codes:
|
|
|
153
164
|
- **`Command "code-review-agent" not found`**
|
|
154
165
|
- use `npx -y @contextrail/code-review-agent ...` or install globally
|
|
155
166
|
- **Missing required env vars**
|
|
156
|
-
- set `
|
|
167
|
+
- set `CONTEXTRAIL_MCP_JWT_TOKEN` and `OPENROUTER_API_KEY` as environment variables
|
|
168
|
+
- set `CONTEXTRAIL_MCP_SERVER_URL` as an environment variable or pass `--mcp-server-url`
|
|
157
169
|
- **One reviewer failed with structured-output/schema errors**
|
|
158
170
|
- inspect `./.review/reviewers/<reviewer>/failures.md`
|
|
159
171
|
- inspect `./.review/reviewers/<reviewer>/progress.json`
|
package/dist/cli/help.js
CHANGED
|
@@ -10,34 +10,41 @@ Commands:
|
|
|
10
10
|
review Run code review on git diff
|
|
11
11
|
|
|
12
12
|
Options:
|
|
13
|
-
--repo <path>
|
|
14
|
-
--from <sha>
|
|
15
|
-
--to <sha>
|
|
16
|
-
--files <list>
|
|
17
|
-
--file <path>
|
|
18
|
-
--output <dir>
|
|
19
|
-
--
|
|
20
|
-
--
|
|
21
|
-
--
|
|
22
|
-
--
|
|
23
|
-
--
|
|
24
|
-
--
|
|
25
|
-
--
|
|
26
|
-
--
|
|
27
|
-
-
|
|
13
|
+
--repo <path> Repository path (default: current directory)
|
|
14
|
+
--from <sha> Base commit SHA (required)
|
|
15
|
+
--to <sha> Head commit SHA (required)
|
|
16
|
+
--files <list> Comma-separated list of files to review
|
|
17
|
+
--file <path> File to review (repeatable)
|
|
18
|
+
--output <dir> Output directory (default: ./review)
|
|
19
|
+
--mcp-server-url <url> ContextRail MCP server URL (overrides CONTEXTRAIL_MCP_SERVER_URL)
|
|
20
|
+
--orchestrator-model <model> Model for orchestrator (overrides LLM_MODEL_ORCHESTRATOR)
|
|
21
|
+
--reviewer-model <model> Model for reviewers (overrides LLM_MODEL_REVIEWER)
|
|
22
|
+
--critic-model <model> Model for critic pass (overrides LLM_MODEL_CRITIC)
|
|
23
|
+
--log-level <lv> Log level: debug|info|warn|error|silent (overrides DEBUG)
|
|
24
|
+
--max-steps <n> Maximum LLM steps per call (default: 10)
|
|
25
|
+
--max-iterations <n> Maximum reviewer validation iterations (default: 2)
|
|
26
|
+
--aggregation-max-steps <n> Maximum steps for aggregation (default: 5)
|
|
27
|
+
--max-tokens-per-file <n> Token budget for surrounding context per file (default: 20000)
|
|
28
|
+
--context-lines <n> Lines of context around changes (default: 10)
|
|
29
|
+
--pr-description <text> PR description to include as context (optional)
|
|
30
|
+
--domains <list> Comma-separated review focus domains (optional)
|
|
31
|
+
-h, --help Show this help message
|
|
28
32
|
|
|
29
|
-
Environment Variables:
|
|
30
|
-
CONTEXTRAIL_MCP_SERVER_URL ContextRail MCP server URL (required)
|
|
33
|
+
Environment Variables (sensitive — use env vars, not CLI flags):
|
|
31
34
|
CONTEXTRAIL_MCP_JWT_TOKEN ContextRail MCP authentication token (required)
|
|
32
|
-
OPENROUTER_API_KEY
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
OPENROUTER_API_KEY OpenRouter API key (required)
|
|
36
|
+
|
|
37
|
+
Environment Variables (can also be set via CLI flags above):
|
|
38
|
+
CONTEXTRAIL_MCP_SERVER_URL ContextRail MCP server URL (required)
|
|
39
|
+
LLM_MODEL_ORCHESTRATOR Model for orchestrator
|
|
40
|
+
LLM_MODEL_REVIEWER Model for reviewers
|
|
41
|
+
LLM_MODEL_CRITIC Model for critic pass
|
|
42
|
+
MAX_STEPS Maximum LLM steps per call (default: 10)
|
|
43
|
+
MAX_ITERATIONS Maximum reviewer validation iterations (default: 2)
|
|
44
|
+
AGGREGATION_MAX_STEPS Maximum steps for aggregation (default: 5)
|
|
45
|
+
MAX_TOKENS_PER_FILE Token budget for surrounding context per file (default: 20000)
|
|
46
|
+
CONTEXT_LINES Lines of context around changes (default: 10)
|
|
47
|
+
REVIEW_DOMAINS Comma-separated review focus domains (optional)
|
|
41
48
|
|
|
42
49
|
Examples:
|
|
43
50
|
code-review-agent review --repo . --from HEAD^ --to HEAD
|
package/dist/cli/parser.js
CHANGED
|
@@ -56,6 +56,22 @@ export const parseArgs = (args) => {
|
|
|
56
56
|
parsed.logLevel = args[i + 1];
|
|
57
57
|
i += 2;
|
|
58
58
|
}
|
|
59
|
+
else if (arg === '--mcp-server-url' && i + 1 < args.length) {
|
|
60
|
+
parsed.mcpServerUrl = args[i + 1];
|
|
61
|
+
i += 2;
|
|
62
|
+
}
|
|
63
|
+
else if (arg === '--orchestrator-model' && i + 1 < args.length) {
|
|
64
|
+
parsed.orchestratorModel = args[i + 1];
|
|
65
|
+
i += 2;
|
|
66
|
+
}
|
|
67
|
+
else if (arg === '--reviewer-model' && i + 1 < args.length) {
|
|
68
|
+
parsed.reviewerModel = args[i + 1];
|
|
69
|
+
i += 2;
|
|
70
|
+
}
|
|
71
|
+
else if (arg === '--critic-model' && i + 1 < args.length) {
|
|
72
|
+
parsed.criticModel = args[i + 1];
|
|
73
|
+
i += 2;
|
|
74
|
+
}
|
|
59
75
|
else if (arg === '--pr-description' && i + 1 < args.length) {
|
|
60
76
|
parsed.prDescription = args[i + 1];
|
|
61
77
|
i += 2;
|
package/dist/cli/types.d.ts
CHANGED
|
@@ -7,6 +7,10 @@ export interface CliArgs {
|
|
|
7
7
|
logLevel?: string;
|
|
8
8
|
files?: string[];
|
|
9
9
|
help?: boolean;
|
|
10
|
+
mcpServerUrl?: string;
|
|
11
|
+
orchestratorModel?: string;
|
|
12
|
+
reviewerModel?: string;
|
|
13
|
+
criticModel?: string;
|
|
10
14
|
maxSteps?: number;
|
|
11
15
|
maxIterations?: number;
|
|
12
16
|
aggregationMaxSteps?: number;
|
|
@@ -51,7 +51,7 @@ export declare const getDefaultCriticModel: (reviewerModel: string | undefined)
|
|
|
51
51
|
*/
|
|
52
52
|
export declare const DEFAULT_SURROUNDING_CONTEXT_CONFIG: {
|
|
53
53
|
readonly enabled: true;
|
|
54
|
-
readonly maxTokensPerFile:
|
|
54
|
+
readonly maxTokensPerFile: 10000;
|
|
55
55
|
readonly contextLines: 10;
|
|
56
56
|
};
|
|
57
57
|
/**
|
package/dist/config/defaults.js
CHANGED
package/dist/config/index.js
CHANGED
|
@@ -55,15 +55,17 @@ export const loadConfig = () => {
|
|
|
55
55
|
prDescription: normalizeOptionalString(process.env.PR_DESCRIPTION),
|
|
56
56
|
reviewDomains,
|
|
57
57
|
};
|
|
58
|
-
// Fail fast when required environment variables are missing.
|
|
59
|
-
// Note: CLI --help bypasses loadConfig in src/index.ts so help remains accessible.
|
|
60
|
-
const missingRequired = getMissingRequiredEnvVars(config);
|
|
61
|
-
if (missingRequired.length > 0) {
|
|
62
|
-
throw new Error(`Missing required environment variables: ${missingRequired.join(', ')}`);
|
|
63
|
-
}
|
|
64
58
|
return config;
|
|
65
59
|
};
|
|
66
60
|
export const applyCliArgs = (config, args) => {
|
|
61
|
+
if (args.mcpServerUrl !== undefined)
|
|
62
|
+
config.mcpServerUrl = args.mcpServerUrl;
|
|
63
|
+
if (args.orchestratorModel !== undefined)
|
|
64
|
+
config.orchestratorModel = args.orchestratorModel;
|
|
65
|
+
if (args.reviewerModel !== undefined)
|
|
66
|
+
config.reviewerModel = args.reviewerModel;
|
|
67
|
+
if (args.criticModel !== undefined)
|
|
68
|
+
config.criticModel = args.criticModel;
|
|
67
69
|
if (args.maxSteps !== undefined)
|
|
68
70
|
config.maxSteps = args.maxSteps;
|
|
69
71
|
if (args.maxIterations !== undefined)
|
|
@@ -218,6 +218,7 @@ export const runReviewerLoop = async (reviewer, inputs, understanding, outputDir
|
|
|
218
218
|
const chunkToolCalls = [];
|
|
219
219
|
const chunkContextIds = [];
|
|
220
220
|
const chunkUsages = [];
|
|
221
|
+
let iterationChunkError = null;
|
|
221
222
|
for (let chunkIdx = 0; chunkIdx < chunks.length; chunkIdx++) {
|
|
222
223
|
const chunk = chunks[chunkIdx];
|
|
223
224
|
if (!chunk) {
|
|
@@ -246,10 +247,26 @@ export const runReviewerLoop = async (reviewer, inputs, understanding, outputDir
|
|
|
246
247
|
? error.message
|
|
247
248
|
: `[REVIEWER] Failed to run reviewer iteration for chunk ${chunkIdx + 1}`;
|
|
248
249
|
const details = buildFailureDetails(error, `Chunk ${chunkIdx + 1} of ${chunks.length} failed`);
|
|
249
|
-
|
|
250
|
-
|
|
250
|
+
iterationChunkError = { msg: errorMsg, details, err: error };
|
|
251
|
+
break;
|
|
251
252
|
}
|
|
252
253
|
}
|
|
254
|
+
if (iterationChunkError !== null) {
|
|
255
|
+
if (iteration < maxIterations) {
|
|
256
|
+
await appendFailure(reviewersDir, reviewer, {
|
|
257
|
+
gate: 'validation',
|
|
258
|
+
message: iterationChunkError.msg,
|
|
259
|
+
details: iterationChunkError.details,
|
|
260
|
+
});
|
|
261
|
+
logger?.debug(`[${reviewer}] Chunk error at iteration ${iteration}; retrying (${iteration + 1}/${maxIterations})`);
|
|
262
|
+
await logContinuation(reviewersDir, reviewer, iteration + 1);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
await recordFailureAndBlock(iterationChunkError.msg, iterationChunkError.details);
|
|
266
|
+
throw iterationChunkError.err instanceof Error
|
|
267
|
+
? iterationChunkError.err
|
|
268
|
+
: new Error(iterationChunkError.msg);
|
|
269
|
+
}
|
|
253
270
|
// Merge findings from all chunks
|
|
254
271
|
const mergedFindings = mergeChunkFindings(chunkFindings);
|
|
255
272
|
findings = mergedFindings;
|
|
@@ -287,6 +304,12 @@ export const runReviewerLoop = async (reviewer, inputs, understanding, outputDir
|
|
|
287
304
|
catch (error) {
|
|
288
305
|
const errorMsg = error instanceof Error ? error.message : '[REVIEWER] Failed to run reviewer iteration';
|
|
289
306
|
const details = buildFailureDetails(error, 'generateText returned no object output');
|
|
307
|
+
if (iteration < maxIterations) {
|
|
308
|
+
await appendFailure(reviewersDir, reviewer, { gate: 'validation', message: errorMsg, details });
|
|
309
|
+
logger?.debug(`[${reviewer}] Iteration ${iteration} threw an error; retrying (${iteration + 1}/${maxIterations})`);
|
|
310
|
+
await logContinuation(reviewersDir, reviewer, iteration + 1);
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
290
313
|
await recordFailureAndBlock(errorMsg, details);
|
|
291
314
|
throw error;
|
|
292
315
|
}
|
package/package.json
CHANGED