@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 CHANGED
@@ -34,14 +34,17 @@ npm i -g @contextrail/code-review-agent
34
34
  code-review-agent review --help
35
35
  ```
36
36
 
37
- ## Required Environment Variables
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
- Optional:
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 `CONTEXTRAIL_MCP_SERVER_URL`, `CONTEXTRAIL_MCP_JWT_TOKEN`, and `OPENROUTER_API_KEY`
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> 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
- --log-level <lv> Log level: debug|info|warn|error|silent (overrides DEBUG)
20
- --max-steps <n> Maximum LLM steps per call (default: 10)
21
- --max-iterations <n> Maximum reviewer validation iterations (default: 2)
22
- --aggregation-max-steps <n> Maximum steps for aggregation (default: 5)
23
- --max-tokens-per-file <n> Token budget for surrounding context per file (default: 20000)
24
- --context-lines <n> Lines of context around changes (default: 10)
25
- --pr-description <text> PR description to include as context (optional)
26
- --domains <list> Comma-separated review focus domains (optional)
27
- -h, --help Show this help message
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 OpenRouter API key (required)
33
- LLM_MODEL_ORCHESTRATOR Model for orchestrator (default: anthropic/claude-haiku-4.5)
34
- LLM_MODEL_REVIEWER Model for reviewers (default: anthropic/claude-haiku-4.5)
35
- MAX_STEPS Maximum LLM steps per call (default: 10)
36
- MAX_ITERATIONS Maximum reviewer validation iterations (default: 2)
37
- AGGREGATION_MAX_STEPS Maximum steps for aggregation (default: 5)
38
- MAX_TOKENS_PER_FILE Token budget for surrounding context per file (default: 20000)
39
- CONTEXT_LINES Lines of context around changes (default: 10)
40
- REVIEW_DOMAINS Comma-separated review focus domains (optional)
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
@@ -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;
@@ -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: 20000;
54
+ readonly maxTokensPerFile: 10000;
55
55
  readonly contextLines: 10;
56
56
  };
57
57
  /**
@@ -65,7 +65,7 @@ export const getDefaultCriticModel = (reviewerModel) => {
65
65
  */
66
66
  export const DEFAULT_SURROUNDING_CONTEXT_CONFIG = {
67
67
  enabled: true,
68
- maxTokensPerFile: 20_000,
68
+ maxTokensPerFile: 10_000,
69
69
  contextLines: 10,
70
70
  };
71
71
  /**
@@ -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
- await recordFailureAndBlock(errorMsg, details);
250
- throw error;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contextrail/code-review-agent",
3
- "version": "0.1.2-alpha.3",
3
+ "version": "0.1.2-alpha.6",
4
4
  "description": "CLI tool for orchestrating ContextRail-powered code reviews",
5
5
  "homepage": "https://contextrail.app",
6
6
  "repository": {