@contextrail/code-review-agent 0.1.1-alpha.0
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/LICENSE +26 -0
- package/MODEL_RECOMMENDATIONS.md +178 -0
- package/README.md +177 -0
- package/dist/config/defaults.d.ts +72 -0
- package/dist/config/defaults.js +113 -0
- package/dist/config/index.d.ts +34 -0
- package/dist/config/index.js +89 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +603 -0
- package/dist/llm/factory.d.ts +21 -0
- package/dist/llm/factory.js +50 -0
- package/dist/llm/index.d.ts +3 -0
- package/dist/llm/index.js +2 -0
- package/dist/llm/service.d.ts +38 -0
- package/dist/llm/service.js +191 -0
- package/dist/llm/types.d.ts +119 -0
- package/dist/llm/types.js +1 -0
- package/dist/logging/logger.d.ts +9 -0
- package/dist/logging/logger.js +52 -0
- package/dist/mcp/client.d.ts +429 -0
- package/dist/mcp/client.js +173 -0
- package/dist/mcp/mcp-tools.d.ts +292 -0
- package/dist/mcp/mcp-tools.js +40 -0
- package/dist/mcp/token-validation.d.ts +31 -0
- package/dist/mcp/token-validation.js +57 -0
- package/dist/mcp/tools-provider.d.ts +18 -0
- package/dist/mcp/tools-provider.js +24 -0
- package/dist/observability/index.d.ts +2 -0
- package/dist/observability/index.js +1 -0
- package/dist/observability/metrics.d.ts +48 -0
- package/dist/observability/metrics.js +86 -0
- package/dist/orchestrator/agentic-orchestrator.d.ts +29 -0
- package/dist/orchestrator/agentic-orchestrator.js +136 -0
- package/dist/orchestrator/prompts.d.ts +25 -0
- package/dist/orchestrator/prompts.js +98 -0
- package/dist/orchestrator/validation.d.ts +2 -0
- package/dist/orchestrator/validation.js +7 -0
- package/dist/orchestrator/writer.d.ts +4 -0
- package/dist/orchestrator/writer.js +17 -0
- package/dist/output/aggregator.d.ts +30 -0
- package/dist/output/aggregator.js +132 -0
- package/dist/output/prompts.d.ts +32 -0
- package/dist/output/prompts.js +153 -0
- package/dist/output/schema.d.ts +1515 -0
- package/dist/output/schema.js +224 -0
- package/dist/output/writer.d.ts +31 -0
- package/dist/output/writer.js +120 -0
- package/dist/review-inputs/chunking.d.ts +29 -0
- package/dist/review-inputs/chunking.js +113 -0
- package/dist/review-inputs/diff-summary.d.ts +52 -0
- package/dist/review-inputs/diff-summary.js +83 -0
- package/dist/review-inputs/file-patterns.d.ts +40 -0
- package/dist/review-inputs/file-patterns.js +182 -0
- package/dist/review-inputs/filtering.d.ts +31 -0
- package/dist/review-inputs/filtering.js +53 -0
- package/dist/review-inputs/git-diff-provider.d.ts +2 -0
- package/dist/review-inputs/git-diff-provider.js +42 -0
- package/dist/review-inputs/index.d.ts +46 -0
- package/dist/review-inputs/index.js +122 -0
- package/dist/review-inputs/path-validation.d.ts +10 -0
- package/dist/review-inputs/path-validation.js +37 -0
- package/dist/review-inputs/surrounding-context.d.ts +35 -0
- package/dist/review-inputs/surrounding-context.js +180 -0
- package/dist/review-inputs/triage.d.ts +57 -0
- package/dist/review-inputs/triage.js +81 -0
- package/dist/reviewers/executor.d.ts +41 -0
- package/dist/reviewers/executor.js +357 -0
- package/dist/reviewers/findings-merge.d.ts +9 -0
- package/dist/reviewers/findings-merge.js +131 -0
- package/dist/reviewers/iteration.d.ts +17 -0
- package/dist/reviewers/iteration.js +95 -0
- package/dist/reviewers/persistence.d.ts +17 -0
- package/dist/reviewers/persistence.js +55 -0
- package/dist/reviewers/progress-tracker.d.ts +115 -0
- package/dist/reviewers/progress-tracker.js +194 -0
- package/dist/reviewers/prompt.d.ts +42 -0
- package/dist/reviewers/prompt.js +246 -0
- package/dist/reviewers/tool-call-tracker.d.ts +18 -0
- package/dist/reviewers/tool-call-tracker.js +40 -0
- package/dist/reviewers/types.d.ts +12 -0
- package/dist/reviewers/types.js +1 -0
- package/dist/reviewers/validation-rules.d.ts +27 -0
- package/dist/reviewers/validation-rules.js +189 -0
- package/package.json +79 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Copyright (c) 2026 ContextRail. All rights reserved.
|
|
2
|
+
|
|
3
|
+
Source-Available License
|
|
4
|
+
|
|
5
|
+
You may:
|
|
6
|
+
- Use the Software for any purpose.
|
|
7
|
+
- Modify the Software for your own internal use.
|
|
8
|
+
|
|
9
|
+
You may not, without prior written consent from ContextRail:
|
|
10
|
+
- Redistribute the Software (original or modified), in source or binary form.
|
|
11
|
+
- Use the Software to provide a competing product or service.
|
|
12
|
+
- Use the Software (including its source code, outputs, or documentation) to
|
|
13
|
+
train, fine-tune, or improve machine learning models (including large
|
|
14
|
+
language models or other AI systems), or allow third parties to do so.
|
|
15
|
+
- Use automated systems (including LLMs or other AI) to scrape, replicate, or
|
|
16
|
+
redistribute the Software or to create derivative works for distribution.
|
|
17
|
+
|
|
18
|
+
Contact ContextRail contextrail@gmail.com for redistribution, commercial licensing, or ML/LLM use terms.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
23
|
+
CONTEXTRAIL OR ITS CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Model Recommendations for Code Review Agent
|
|
2
|
+
|
|
3
|
+
This guide helps you choose cost-effective models that work well with structured output for code reviews.
|
|
4
|
+
|
|
5
|
+
## Quick Reference: Low-Cost Models
|
|
6
|
+
|
|
7
|
+
### ✅ Recommended Low-Cost Models (Structured Output Compatible)
|
|
8
|
+
|
|
9
|
+
| Model | Cost (per 1M tokens) | Speed | Quality | Best For |
|
|
10
|
+
| ------------------------------------ | -------------------- | --------- | --------- | --------------------------------- |
|
|
11
|
+
| **google/gemini-2.0-flash-exp** | ~$0.075 | Very Fast | Good | Orchestrator, Synthesis, Decision |
|
|
12
|
+
| **google/gemini-3-flash-preview** | ~$0.075 | Very Fast | Good | Orchestrator, Synthesis, Decision |
|
|
13
|
+
| **anthropic/claude-haiku-4.5** | ~$0.25 | Fast | Excellent | Reviewers, Orchestrator (default) |
|
|
14
|
+
| **anthropic/claude-sonnet-4.5** | ~$3.00 | Medium | Excellent | Critic, Complex Reviews |
|
|
15
|
+
| **meta-llama/llama-3.1-8b-instruct** | ~$0.05 | Fast | Good | Budget Reviews |
|
|
16
|
+
|
|
17
|
+
### ⚠️ Models to Avoid (Structured Output Issues)
|
|
18
|
+
|
|
19
|
+
- `deepseek/deepseek-v3.2` - Known to return empty responses with structured output
|
|
20
|
+
- Models without explicit structured output support
|
|
21
|
+
- Very old model versions
|
|
22
|
+
|
|
23
|
+
## Cost Optimization Strategies
|
|
24
|
+
|
|
25
|
+
### Strategy 1: Tiered Model Approach (Recommended)
|
|
26
|
+
|
|
27
|
+
Use cheaper models for simpler tasks, more capable models for complex analysis:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Orchestrator: Fast, cheap model
|
|
31
|
+
export LLM_MODEL_ORCHESTRATOR="google/gemini-3-flash-preview"
|
|
32
|
+
|
|
33
|
+
# Reviewers: Balanced cost/quality
|
|
34
|
+
export LLM_MODEL_REVIEWER="anthropic/claude-haiku-4.5"
|
|
35
|
+
|
|
36
|
+
# Critic: Higher quality for validation
|
|
37
|
+
export LLM_MODEL_CRITIC="anthropic/claude-sonnet-4.5"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Estimated cost per review**: ~$0.01-0.05 (depending on PR size)
|
|
41
|
+
|
|
42
|
+
### Strategy 2: Ultra-Low-Cost (Budget)
|
|
43
|
+
|
|
44
|
+
Use cheapest models throughout:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
export LLM_MODEL_ORCHESTRATOR="google/gemini-2.0-flash-exp"
|
|
48
|
+
export LLM_MODEL_REVIEWER="google/gemini-2.0-flash-exp"
|
|
49
|
+
export LLM_MODEL_CRITIC="google/gemini-3-flash-preview"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Estimated cost per review**: ~$0.005-0.02
|
|
53
|
+
|
|
54
|
+
### Strategy 3: Quality-Focused (Higher Cost)
|
|
55
|
+
|
|
56
|
+
Use Claude models throughout for best quality:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
export LLM_MODEL_ORCHESTRATOR="anthropic/claude-haiku-4.5"
|
|
60
|
+
export LLM_MODEL_REVIEWER="anthropic/claude-haiku-4.5"
|
|
61
|
+
export LLM_MODEL_CRITIC="anthropic/claude-sonnet-4.5"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Estimated cost per review**: ~$0.02-0.10
|
|
65
|
+
|
|
66
|
+
## Model Selection Guide
|
|
67
|
+
|
|
68
|
+
### For Orchestrator
|
|
69
|
+
|
|
70
|
+
**Purpose**: Select reviewers, understand changes
|
|
71
|
+
**Requirements**: Fast, reliable structured output
|
|
72
|
+
**Recommendations**:
|
|
73
|
+
|
|
74
|
+
- ✅ `google/gemini-3-flash-preview` (cheapest, fast)
|
|
75
|
+
- ✅ `anthropic/claude-haiku-4.5` (default, reliable)
|
|
76
|
+
- ⚠️ Avoid: Very slow models, models without structured output
|
|
77
|
+
|
|
78
|
+
### For Reviewers
|
|
79
|
+
|
|
80
|
+
**Purpose**: Analyze code, find issues
|
|
81
|
+
**Requirements**: Good code understanding, structured output
|
|
82
|
+
**Recommendations**:
|
|
83
|
+
|
|
84
|
+
- ✅ `anthropic/claude-haiku-4.5` (default, excellent quality)
|
|
85
|
+
- ✅ `google/gemini-3-flash-preview` (cheaper alternative)
|
|
86
|
+
- ⚠️ Avoid: Models that struggle with code analysis
|
|
87
|
+
|
|
88
|
+
### For Critic
|
|
89
|
+
|
|
90
|
+
**Purpose**: Validate findings, reduce false positives
|
|
91
|
+
**Requirements**: High quality reasoning, structured output
|
|
92
|
+
**Recommendations**:
|
|
93
|
+
|
|
94
|
+
- ✅ `anthropic/claude-sonnet-4.5` (default, best quality)
|
|
95
|
+
- ✅ `anthropic/claude-haiku-4.5` (if cost is concern)
|
|
96
|
+
- ⚠️ Avoid: Low-quality models (defeats purpose of critic)
|
|
97
|
+
|
|
98
|
+
## Testing Model Compatibility
|
|
99
|
+
|
|
100
|
+
If you want to try a new model, test it first:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# Test with a small PR
|
|
104
|
+
export LLM_MODEL_REVIEWER="your-model-name"
|
|
105
|
+
export DEBUG=1 # Enable debug logging
|
|
106
|
+
|
|
107
|
+
# Run a review and check logs for:
|
|
108
|
+
# - "[LLM] No structured output received" warnings
|
|
109
|
+
# - Empty responses
|
|
110
|
+
# - JSON parsing errors
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Cost Estimation
|
|
114
|
+
|
|
115
|
+
Based on typical PR sizes:
|
|
116
|
+
|
|
117
|
+
| PR Size | Input Tokens | Output Tokens | Cost (Gemini Flash) | Cost (Claude Haiku) |
|
|
118
|
+
| ------------------- | ------------ | ------------- | ------------------- | ------------------- |
|
|
119
|
+
| Small (1-3 files) | ~5K | ~2K | ~$0.0005 | ~$0.0018 |
|
|
120
|
+
| Medium (5-10 files) | ~15K | ~5K | ~$0.0015 | ~$0.005 |
|
|
121
|
+
| Large (20+ files) | ~50K | ~15K | ~$0.005 | ~$0.016 |
|
|
122
|
+
|
|
123
|
+
**Note**: Costs vary based on:
|
|
124
|
+
|
|
125
|
+
- Number of reviewers selected
|
|
126
|
+
- Number of iterations (if findings need validation)
|
|
127
|
+
- Amount of context retrieved from ContextRail
|
|
128
|
+
- Model pricing (check OpenRouter for current rates)
|
|
129
|
+
|
|
130
|
+
## Finding Current Pricing
|
|
131
|
+
|
|
132
|
+
1. Visit [OpenRouter Models](https://openrouter.ai/models)
|
|
133
|
+
2. Filter by:
|
|
134
|
+
- `supported_parameters=structured_outputs`
|
|
135
|
+
- Sort by pricing (low to high)
|
|
136
|
+
3. Check the model card for:
|
|
137
|
+
- Input/output token pricing
|
|
138
|
+
- Structured output support
|
|
139
|
+
- Speed/latency information
|
|
140
|
+
|
|
141
|
+
## Troubleshooting Model Issues
|
|
142
|
+
|
|
143
|
+
### Problem: Empty responses / "No object generated"
|
|
144
|
+
|
|
145
|
+
**Symptoms**: Model consumes tokens but returns empty text
|
|
146
|
+
**Solution**:
|
|
147
|
+
|
|
148
|
+
- Try a different model (Gemini Flash or Claude Haiku)
|
|
149
|
+
- Check model compatibility with structured output
|
|
150
|
+
- Verify model name is correct
|
|
151
|
+
|
|
152
|
+
### Problem: JSON parsing errors
|
|
153
|
+
|
|
154
|
+
**Symptoms**: Model returns text but not valid JSON
|
|
155
|
+
**Solution**:
|
|
156
|
+
|
|
157
|
+
- Use a model with better structured output support
|
|
158
|
+
- Check if model requires special formatting
|
|
159
|
+
- Review debug logs for actual response
|
|
160
|
+
|
|
161
|
+
### Problem: High costs
|
|
162
|
+
|
|
163
|
+
**Solution**:
|
|
164
|
+
|
|
165
|
+
- Switch to Gemini Flash models
|
|
166
|
+
- Reduce `maxSteps` or `maxIterations`
|
|
167
|
+
- Use fewer reviewers
|
|
168
|
+
- Enable token caching if available
|
|
169
|
+
|
|
170
|
+
## Default Configuration
|
|
171
|
+
|
|
172
|
+
The agent defaults to:
|
|
173
|
+
|
|
174
|
+
- **Orchestrator**: `anthropic/claude-haiku-4.5` (~$0.25/1M tokens)
|
|
175
|
+
- **Reviewer**: `anthropic/claude-haiku-4.5` (~$0.25/1M tokens)
|
|
176
|
+
- **Critic**: `anthropic/claude-sonnet-4.5` (~$3.00/1M tokens)
|
|
177
|
+
|
|
178
|
+
These defaults balance cost and quality. For lower costs, switch to Gemini Flash models.
|
package/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'Code Review Agent Client Integration Guide'
|
|
3
|
+
owner: platform-eng
|
|
4
|
+
status: draft
|
|
5
|
+
last_reviewed: 2026-02-10
|
|
6
|
+
audience: developers integrating code-review-agent in client repositories
|
|
7
|
+
purpose: Provide user-facing installation and execution guidance for npm-distributed code-review-agent.
|
|
8
|
+
user_need: do
|
|
9
|
+
doc_form: how-to-guide
|
|
10
|
+
location: packages/code-review-agent/README.md
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# [Context Rail](https://contextrail.app): Code Review Agent Client Integration Guide
|
|
14
|
+
|
|
15
|
+
## Summary
|
|
16
|
+
|
|
17
|
+
`@contextrail/code-review-agent` is a CLI for running structured, AI-assisted PR/code reviews in your repository.
|
|
18
|
+
|
|
19
|
+
Use it to:
|
|
20
|
+
|
|
21
|
+
- review a git diff (`--from` / `--to`)
|
|
22
|
+
- review explicit files (`--files` / `--file`)
|
|
23
|
+
- emit machine-readable review artifacts (`result.json`, reviewer logs, token metrics)
|
|
24
|
+
|
|
25
|
+
## Install and Run
|
|
26
|
+
|
|
27
|
+
Choose one:
|
|
28
|
+
|
|
29
|
+
- **No install (recommended to start):**
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx -y @contextrail/code-review-agent review --help
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- **Project dependency:**
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm i -D @contextrail/code-review-agent
|
|
39
|
+
npx code-review-agent review --help
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- **Global install:**
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm i -g @contextrail/code-review-agent
|
|
46
|
+
code-review-agent review --help
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Required Environment Variables
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
export CONTEXTRAIL_MCP_SERVER_URL="https://<your-mcp-host>"
|
|
53
|
+
export CONTEXTRAIL_MCP_JWT_TOKEN="<your-contextrail-jwt-token>"
|
|
54
|
+
export OPENROUTER_API_KEY="<your-openrouter-key>"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Getting Your ContextRail MCP Token
|
|
58
|
+
|
|
59
|
+
To get your `CONTEXTRAIL_MCP_JWT_TOKEN`:
|
|
60
|
+
|
|
61
|
+
1. **Sign up** at [https://contextrail.app](https://contextrail.app) (if you don't have an account)
|
|
62
|
+
2. **Get your token** at [https://contextrail.app/settings/profile](https://contextrail.app/settings/profile)
|
|
63
|
+
|
|
64
|
+
The token is used to authenticate with the ContextRail MCP server to retrieve your organization's review standards and contexts.
|
|
65
|
+
|
|
66
|
+
Optional:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
export LLM_MODEL_ORCHESTRATOR="google/gemini-3-flash-preview"
|
|
70
|
+
export LLM_MODEL_REVIEWER="qwen/qwen3-coder-next"
|
|
71
|
+
export LLM_MODEL_CRITIC="qwen/qwen3-coder-next"
|
|
72
|
+
export REVIEW_DOMAINS="security,architecture"
|
|
73
|
+
export PR_DESCRIPTION="Optional PR context"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Run Against a PR Diff
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
git fetch origin
|
|
80
|
+
BASE_SHA="$(git merge-base origin/main HEAD)"
|
|
81
|
+
HEAD_SHA="$(git rev-parse HEAD)"
|
|
82
|
+
|
|
83
|
+
npx -y @contextrail/code-review-agent review \
|
|
84
|
+
--repo . \
|
|
85
|
+
--from "$BASE_SHA" \
|
|
86
|
+
--to "$HEAD_SHA" \
|
|
87
|
+
--output ./.review
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
With optional domain focus:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npx -y @contextrail/code-review-agent review \
|
|
94
|
+
--repo . \
|
|
95
|
+
--from "$BASE_SHA" \
|
|
96
|
+
--to "$HEAD_SHA" \
|
|
97
|
+
--domains "security,architecture" \
|
|
98
|
+
--output ./.review
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Run Against Explicit Files
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npx -y @contextrail/code-review-agent review \
|
|
105
|
+
--repo . \
|
|
106
|
+
--files "src/a.ts,src/b.ts" \
|
|
107
|
+
--output ./.review
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
or:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npx -y @contextrail/code-review-agent review \
|
|
114
|
+
--repo . \
|
|
115
|
+
--file src/a.ts \
|
|
116
|
+
--file src/b.ts \
|
|
117
|
+
--output ./.review
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Local Change Recipes
|
|
121
|
+
|
|
122
|
+
Staged files:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
FILES_CSV="$(git diff --name-only --cached | paste -sd, -)"
|
|
126
|
+
[ -z "$FILES_CSV" ] && echo "No staged tracked files to review." && exit 1
|
|
127
|
+
npx -y @contextrail/code-review-agent review --repo . --files "$FILES_CSV" --output ./.review
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Unstaged files:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
FILES_CSV="$(git diff --name-only | paste -sd, -)"
|
|
134
|
+
[ -z "$FILES_CSV" ] && echo "No unstaged tracked files to review." && exit 1
|
|
135
|
+
npx -y @contextrail/code-review-agent review --repo . --files "$FILES_CSV" --output ./.review
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Tracked changes vs `HEAD`:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
FILES_CSV="$(git diff --name-only HEAD | paste -sd, -)"
|
|
142
|
+
[ -z "$FILES_CSV" ] && echo "No tracked local changes vs HEAD." && exit 1
|
|
143
|
+
npx -y @contextrail/code-review-agent review --repo . --files "$FILES_CSV" --output ./.review
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Output and Exit Behavior
|
|
147
|
+
|
|
148
|
+
Primary output:
|
|
149
|
+
|
|
150
|
+
- `./.review/result.json`
|
|
151
|
+
|
|
152
|
+
Additional artifacts:
|
|
153
|
+
|
|
154
|
+
- `./.review/orchestrator/*`
|
|
155
|
+
- `./.review/reviewers/<reviewer>/*`
|
|
156
|
+
- `./.review/token-budget.json`
|
|
157
|
+
|
|
158
|
+
Exit codes:
|
|
159
|
+
|
|
160
|
+
- `0`: all reviewers validated and final decision is approve
|
|
161
|
+
- `1`: reviewer validation failed and/or final decision is request-changes
|
|
162
|
+
|
|
163
|
+
## Troubleshooting
|
|
164
|
+
|
|
165
|
+
- **`Command "code-review-agent" not found`**
|
|
166
|
+
- use `npx -y @contextrail/code-review-agent ...` or install globally
|
|
167
|
+
- **Missing required env vars**
|
|
168
|
+
- set `CONTEXTRAIL_MCP_SERVER_URL`, `CONTEXTRAIL_MCP_JWT_TOKEN`, and `OPENROUTER_API_KEY`
|
|
169
|
+
- **One reviewer failed with structured-output/schema errors**
|
|
170
|
+
- inspect `./.review/reviewers/<reviewer>/failures.md`
|
|
171
|
+
- inspect `./.review/reviewers/<reviewer>/progress.json`
|
|
172
|
+
|
|
173
|
+
## Related
|
|
174
|
+
|
|
175
|
+
- contributor guide: `packages/code-review-agent/CONTRIBUTORS.md`
|
|
176
|
+
- npm publishing guide: `packages/code-review-agent/NPM_PUBLISHING.md`
|
|
177
|
+
- model cost guide: `packages/code-review-agent/MODEL_RECOMMENDATIONS.md`
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default model for orchestrator calls.
|
|
3
|
+
* Chosen for fast planning/synthesis while preserving structured-output reliability.
|
|
4
|
+
*/
|
|
5
|
+
export declare const DEFAULT_ORCHESTRATOR_MODEL = "google/gemini-3-flash-preview";
|
|
6
|
+
/**
|
|
7
|
+
* Default model for reviewer iterations.
|
|
8
|
+
* Tuned for code-centric reasoning with acceptable latency/cost.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_REVIEWER_MODEL = "qwen/qwen3-coder-next";
|
|
11
|
+
/**
|
|
12
|
+
* Default critic model used when no critic model is explicitly configured.
|
|
13
|
+
*/
|
|
14
|
+
export declare const DEFAULT_CRITIC_MODEL = "google/gemini-3-flash-preview";
|
|
15
|
+
/**
|
|
16
|
+
* Maximum tool/LLM steps per call for orchestrator/reviewer operations.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_MAX_STEPS = 10;
|
|
19
|
+
/**
|
|
20
|
+
* Maximum validation loops per reviewer (initial pass + critic pass).
|
|
21
|
+
*/
|
|
22
|
+
export declare const DEFAULT_MAX_ITERATIONS = 2;
|
|
23
|
+
/**
|
|
24
|
+
* Maximum tool/LLM steps for synthesis and final decision generation.
|
|
25
|
+
*/
|
|
26
|
+
export declare const DEFAULT_AGGREGATION_MAX_STEPS = 5;
|
|
27
|
+
/**
|
|
28
|
+
* Default temperature for structured output generation.
|
|
29
|
+
* Lower temperature (0-0.3) improves reliability and consistency for structured output.
|
|
30
|
+
*/
|
|
31
|
+
export declare const DEFAULT_TEMPERATURE = 0.2;
|
|
32
|
+
/**
|
|
33
|
+
* Default timeout for LLM calls in milliseconds (2 minutes).
|
|
34
|
+
* Prevents hanging on slow or unresponsive models.
|
|
35
|
+
*/
|
|
36
|
+
export declare const DEFAULT_LLM_TIMEOUT_MS = 120000;
|
|
37
|
+
/**
|
|
38
|
+
* Default max tokens for structured output generation.
|
|
39
|
+
* Ensures enough tokens for complete structured responses.
|
|
40
|
+
* Set to undefined to use model default (typically 4096-8192).
|
|
41
|
+
*/
|
|
42
|
+
export declare const DEFAULT_MAX_TOKENS: undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Get default critic model based on reviewer model.
|
|
45
|
+
* If reviewer uses haiku, critic uses sonnet (and vice versa).
|
|
46
|
+
* This ensures structural independence between review and critique.
|
|
47
|
+
*/
|
|
48
|
+
export declare const getDefaultCriticModel: (reviewerModel: string | undefined) => string;
|
|
49
|
+
/**
|
|
50
|
+
* Default configuration for surrounding context extraction.
|
|
51
|
+
*/
|
|
52
|
+
export declare const DEFAULT_SURROUNDING_CONTEXT_CONFIG: {
|
|
53
|
+
readonly enabled: true;
|
|
54
|
+
readonly maxTokensPerFile: 20000;
|
|
55
|
+
readonly contextLines: 10;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Default file patterns to exclude from code review.
|
|
59
|
+
* These patterns match common build artifacts, lockfiles, and generated code.
|
|
60
|
+
* Uses glob patterns (minimatch) instead of regex to avoid ReDoS vulnerabilities.
|
|
61
|
+
*/
|
|
62
|
+
export declare const DEFAULT_EXCLUDE_PATTERNS: readonly ["**/*.lock", "**/package-lock.json", "**/pnpm-lock.yaml", "**/yarn.lock", "**/dist/**", "**/build/**", "**/out/**", "**/.next/**", "**/.turbo/**", "**/*.generated.*", "**/*.gen.*", "**/node_modules/**", "**/.vscode/**", "**/.idea/**", ".DS_Store", "**/coverage/**", "**/*.tmp", "**/*.temp"];
|
|
63
|
+
/**
|
|
64
|
+
* Default thresholds for trivial PR triage.
|
|
65
|
+
* PRs meeting these criteria may skip full reviewer flow or use lightweight review.
|
|
66
|
+
*/
|
|
67
|
+
export declare const DEFAULT_TRIAGE_MAX_FILES = 3;
|
|
68
|
+
export declare const DEFAULT_TRIAGE_MAX_TOTAL_LINES = 50;
|
|
69
|
+
/**
|
|
70
|
+
* File extensions considered documentation-only for triage purposes.
|
|
71
|
+
*/
|
|
72
|
+
export declare const DEFAULT_TRIAGE_DOCS_ONLY_EXTENSIONS: readonly [".md", ".txt", ".rst", ".adoc", ".org"];
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default model for orchestrator calls.
|
|
3
|
+
* Chosen for fast planning/synthesis while preserving structured-output reliability.
|
|
4
|
+
*/
|
|
5
|
+
export const DEFAULT_ORCHESTRATOR_MODEL = 'google/gemini-3-flash-preview';
|
|
6
|
+
/**
|
|
7
|
+
* Default model for reviewer iterations.
|
|
8
|
+
* Tuned for code-centric reasoning with acceptable latency/cost.
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_REVIEWER_MODEL = 'qwen/qwen3-coder-next';
|
|
11
|
+
/**
|
|
12
|
+
* Default critic model used when no critic model is explicitly configured.
|
|
13
|
+
*/
|
|
14
|
+
export const DEFAULT_CRITIC_MODEL = 'google/gemini-3-flash-preview';
|
|
15
|
+
/**
|
|
16
|
+
* Maximum tool/LLM steps per call for orchestrator/reviewer operations.
|
|
17
|
+
*/
|
|
18
|
+
export const DEFAULT_MAX_STEPS = 10;
|
|
19
|
+
/**
|
|
20
|
+
* Maximum validation loops per reviewer (initial pass + critic pass).
|
|
21
|
+
*/
|
|
22
|
+
export const DEFAULT_MAX_ITERATIONS = 2;
|
|
23
|
+
/**
|
|
24
|
+
* Maximum tool/LLM steps for synthesis and final decision generation.
|
|
25
|
+
*/
|
|
26
|
+
export const DEFAULT_AGGREGATION_MAX_STEPS = 5;
|
|
27
|
+
/**
|
|
28
|
+
* Default temperature for structured output generation.
|
|
29
|
+
* Lower temperature (0-0.3) improves reliability and consistency for structured output.
|
|
30
|
+
*/
|
|
31
|
+
export const DEFAULT_TEMPERATURE = 0.2;
|
|
32
|
+
/**
|
|
33
|
+
* Default timeout for LLM calls in milliseconds (2 minutes).
|
|
34
|
+
* Prevents hanging on slow or unresponsive models.
|
|
35
|
+
*/
|
|
36
|
+
export const DEFAULT_LLM_TIMEOUT_MS = 120_000;
|
|
37
|
+
/**
|
|
38
|
+
* Default max tokens for structured output generation.
|
|
39
|
+
* Ensures enough tokens for complete structured responses.
|
|
40
|
+
* Set to undefined to use model default (typically 4096-8192).
|
|
41
|
+
*/
|
|
42
|
+
export const DEFAULT_MAX_TOKENS = undefined; // Use model default
|
|
43
|
+
/**
|
|
44
|
+
* Get default critic model based on reviewer model.
|
|
45
|
+
* If reviewer uses haiku, critic uses sonnet (and vice versa).
|
|
46
|
+
* This ensures structural independence between review and critique.
|
|
47
|
+
*/
|
|
48
|
+
export const getDefaultCriticModel = (reviewerModel) => {
|
|
49
|
+
if (!reviewerModel) {
|
|
50
|
+
return DEFAULT_CRITIC_MODEL;
|
|
51
|
+
}
|
|
52
|
+
// If reviewer uses haiku, critic uses sonnet
|
|
53
|
+
if (reviewerModel.includes('haiku')) {
|
|
54
|
+
return reviewerModel.replace('haiku', 'sonnet');
|
|
55
|
+
}
|
|
56
|
+
// If reviewer uses sonnet, critic uses haiku
|
|
57
|
+
if (reviewerModel.includes('sonnet')) {
|
|
58
|
+
return reviewerModel.replace('sonnet', 'haiku');
|
|
59
|
+
}
|
|
60
|
+
// Default to sonnet for other models
|
|
61
|
+
return DEFAULT_CRITIC_MODEL;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Default configuration for surrounding context extraction.
|
|
65
|
+
*/
|
|
66
|
+
export const DEFAULT_SURROUNDING_CONTEXT_CONFIG = {
|
|
67
|
+
enabled: true,
|
|
68
|
+
maxTokensPerFile: 20_000,
|
|
69
|
+
contextLines: 10,
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Default file patterns to exclude from code review.
|
|
73
|
+
* These patterns match common build artifacts, lockfiles, and generated code.
|
|
74
|
+
* Uses glob patterns (minimatch) instead of regex to avoid ReDoS vulnerabilities.
|
|
75
|
+
*/
|
|
76
|
+
export const DEFAULT_EXCLUDE_PATTERNS = [
|
|
77
|
+
// Lockfiles
|
|
78
|
+
'**/*.lock',
|
|
79
|
+
'**/package-lock.json',
|
|
80
|
+
'**/pnpm-lock.yaml',
|
|
81
|
+
'**/yarn.lock',
|
|
82
|
+
// Build artifacts
|
|
83
|
+
'**/dist/**',
|
|
84
|
+
'**/build/**',
|
|
85
|
+
'**/out/**',
|
|
86
|
+
'**/.next/**',
|
|
87
|
+
'**/.turbo/**',
|
|
88
|
+
// Generated code
|
|
89
|
+
'**/*.generated.*',
|
|
90
|
+
'**/*.gen.*',
|
|
91
|
+
// Dependencies
|
|
92
|
+
'**/node_modules/**',
|
|
93
|
+
// IDE/Editor files
|
|
94
|
+
'**/.vscode/**',
|
|
95
|
+
'**/.idea/**',
|
|
96
|
+
// OS files
|
|
97
|
+
'.DS_Store',
|
|
98
|
+
// Coverage reports
|
|
99
|
+
'**/coverage/**',
|
|
100
|
+
// Temporary files
|
|
101
|
+
'**/*.tmp',
|
|
102
|
+
'**/*.temp',
|
|
103
|
+
];
|
|
104
|
+
/**
|
|
105
|
+
* Default thresholds for trivial PR triage.
|
|
106
|
+
* PRs meeting these criteria may skip full reviewer flow or use lightweight review.
|
|
107
|
+
*/
|
|
108
|
+
export const DEFAULT_TRIAGE_MAX_FILES = 3;
|
|
109
|
+
export const DEFAULT_TRIAGE_MAX_TOTAL_LINES = 50;
|
|
110
|
+
/**
|
|
111
|
+
* File extensions considered documentation-only for triage purposes.
|
|
112
|
+
*/
|
|
113
|
+
export const DEFAULT_TRIAGE_DOCS_ONLY_EXTENSIONS = ['.md', '.txt', '.rst', '.adoc', '.org'];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type LogLevel } from '../logging/logger.js';
|
|
2
|
+
export type ReviewAgentConfig = {
|
|
3
|
+
mcpServerUrl?: string;
|
|
4
|
+
mcpAuthToken?: string;
|
|
5
|
+
openRouterApiKey?: string;
|
|
6
|
+
orchestratorModel?: string;
|
|
7
|
+
reviewerModel?: string;
|
|
8
|
+
criticModel?: string;
|
|
9
|
+
logLevel: LogLevel;
|
|
10
|
+
maxSteps?: number;
|
|
11
|
+
maxIterations?: number;
|
|
12
|
+
aggregationMaxSteps?: number;
|
|
13
|
+
maxTokensPerFile?: number;
|
|
14
|
+
contextLines?: number;
|
|
15
|
+
prDescription?: string;
|
|
16
|
+
reviewDomains?: string[];
|
|
17
|
+
};
|
|
18
|
+
export type ValidatedReviewAgentConfig = {
|
|
19
|
+
mcpServerUrl: string;
|
|
20
|
+
mcpAuthToken: string;
|
|
21
|
+
openRouterApiKey: string;
|
|
22
|
+
orchestratorModel?: string;
|
|
23
|
+
reviewerModel?: string;
|
|
24
|
+
criticModel?: string;
|
|
25
|
+
logLevel: LogLevel;
|
|
26
|
+
maxSteps: number;
|
|
27
|
+
maxIterations: number;
|
|
28
|
+
aggregationMaxSteps: number;
|
|
29
|
+
maxTokensPerFile: number;
|
|
30
|
+
contextLines: number;
|
|
31
|
+
reviewDomains?: string[];
|
|
32
|
+
};
|
|
33
|
+
export declare const loadConfig: () => ReviewAgentConfig;
|
|
34
|
+
export declare const validateConfig: (config: ReviewAgentConfig) => ValidatedReviewAgentConfig;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import dotenv from 'dotenv';
|
|
4
|
+
import { parseLogLevel } from '../logging/logger.js';
|
|
5
|
+
import { DEFAULT_AGGREGATION_MAX_STEPS, DEFAULT_MAX_ITERATIONS, DEFAULT_MAX_STEPS, DEFAULT_SURROUNDING_CONTEXT_CONFIG, getDefaultCriticModel, } from './defaults.js';
|
|
6
|
+
// Only load .env file if not in test environment
|
|
7
|
+
// Tests should use explicit env vars or test setup files
|
|
8
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
9
|
+
if (process.env.NODE_ENV !== 'test' && !process.env.VITEST) {
|
|
10
|
+
dotenv.config({ path: path.join(packageRoot, '.env') });
|
|
11
|
+
}
|
|
12
|
+
const getMissingRequiredEnvVars = (config) => {
|
|
13
|
+
const missing = [];
|
|
14
|
+
if (!config.mcpServerUrl?.trim()) {
|
|
15
|
+
missing.push('CONTEXTRAIL_MCP_SERVER_URL');
|
|
16
|
+
}
|
|
17
|
+
if (!config.mcpAuthToken?.trim()) {
|
|
18
|
+
missing.push('CONTEXTRAIL_MCP_JWT_TOKEN');
|
|
19
|
+
}
|
|
20
|
+
if (!config.openRouterApiKey?.trim()) {
|
|
21
|
+
missing.push('OPENROUTER_API_KEY');
|
|
22
|
+
}
|
|
23
|
+
return missing;
|
|
24
|
+
};
|
|
25
|
+
const normalizeOptionalString = (value) => {
|
|
26
|
+
const trimmed = value?.trim();
|
|
27
|
+
return trimmed ? trimmed : undefined;
|
|
28
|
+
};
|
|
29
|
+
const parseOptionalInt = (value, defaultValue) => {
|
|
30
|
+
if (!value) {
|
|
31
|
+
return defaultValue;
|
|
32
|
+
}
|
|
33
|
+
const parsed = parseInt(value, 10);
|
|
34
|
+
return Number.isNaN(parsed) ? defaultValue : parsed;
|
|
35
|
+
};
|
|
36
|
+
export const loadConfig = () => {
|
|
37
|
+
const reviewDomains = process.env.REVIEW_DOMAINS
|
|
38
|
+
? process.env.REVIEW_DOMAINS.split(',')
|
|
39
|
+
.map((domain) => domain.trim())
|
|
40
|
+
.filter(Boolean)
|
|
41
|
+
: undefined;
|
|
42
|
+
const config = {
|
|
43
|
+
mcpServerUrl: normalizeOptionalString(process.env.CONTEXTRAIL_MCP_SERVER_URL),
|
|
44
|
+
mcpAuthToken: normalizeOptionalString(process.env.CONTEXTRAIL_MCP_JWT_TOKEN),
|
|
45
|
+
openRouterApiKey: normalizeOptionalString(process.env.OPENROUTER_API_KEY),
|
|
46
|
+
orchestratorModel: normalizeOptionalString(process.env.LLM_MODEL_ORCHESTRATOR),
|
|
47
|
+
reviewerModel: normalizeOptionalString(process.env.LLM_MODEL_REVIEWER),
|
|
48
|
+
criticModel: normalizeOptionalString(process.env.LLM_MODEL_CRITIC),
|
|
49
|
+
logLevel: parseLogLevel(normalizeOptionalString(process.env.DEBUG)),
|
|
50
|
+
maxSteps: parseOptionalInt(process.env.MAX_STEPS, DEFAULT_MAX_STEPS),
|
|
51
|
+
maxIterations: parseOptionalInt(process.env.MAX_ITERATIONS, DEFAULT_MAX_ITERATIONS),
|
|
52
|
+
aggregationMaxSteps: parseOptionalInt(process.env.AGGREGATION_MAX_STEPS, DEFAULT_AGGREGATION_MAX_STEPS),
|
|
53
|
+
maxTokensPerFile: parseOptionalInt(process.env.MAX_TOKENS_PER_FILE, DEFAULT_SURROUNDING_CONTEXT_CONFIG.maxTokensPerFile),
|
|
54
|
+
contextLines: parseOptionalInt(process.env.CONTEXT_LINES, DEFAULT_SURROUNDING_CONTEXT_CONFIG.contextLines),
|
|
55
|
+
prDescription: normalizeOptionalString(process.env.PR_DESCRIPTION),
|
|
56
|
+
reviewDomains,
|
|
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
|
+
return config;
|
|
65
|
+
};
|
|
66
|
+
export const validateConfig = (config) => {
|
|
67
|
+
const mcpServerUrl = config.mcpServerUrl;
|
|
68
|
+
const mcpAuthToken = config.mcpAuthToken;
|
|
69
|
+
const openRouterApiKey = config.openRouterApiKey;
|
|
70
|
+
const missing = getMissingRequiredEnvVars(config);
|
|
71
|
+
if (missing.length > 0) {
|
|
72
|
+
throw new Error(`Missing required environment variables: ${missing.join(', ')}`);
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
mcpServerUrl: mcpServerUrl,
|
|
76
|
+
mcpAuthToken: mcpAuthToken,
|
|
77
|
+
openRouterApiKey: openRouterApiKey,
|
|
78
|
+
orchestratorModel: config.orchestratorModel,
|
|
79
|
+
reviewerModel: config.reviewerModel,
|
|
80
|
+
criticModel: config.criticModel ?? getDefaultCriticModel(config.reviewerModel),
|
|
81
|
+
logLevel: config.logLevel,
|
|
82
|
+
maxSteps: config.maxSteps ?? DEFAULT_MAX_STEPS,
|
|
83
|
+
maxIterations: config.maxIterations ?? DEFAULT_MAX_ITERATIONS,
|
|
84
|
+
aggregationMaxSteps: config.aggregationMaxSteps ?? DEFAULT_AGGREGATION_MAX_STEPS,
|
|
85
|
+
maxTokensPerFile: config.maxTokensPerFile ?? DEFAULT_SURROUNDING_CONTEXT_CONFIG.maxTokensPerFile,
|
|
86
|
+
contextLines: config.contextLines ?? DEFAULT_SURROUNDING_CONTEXT_CONFIG.contextLines,
|
|
87
|
+
reviewDomains: config.reviewDomains,
|
|
88
|
+
};
|
|
89
|
+
};
|
package/dist/index.d.ts
ADDED