opencode-review-helper 0.1.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 +21 -0
- package/README.md +117 -0
- package/agents/code-reviewer.md +97 -0
- package/agents/impact-explorer.md +41 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +88 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +27 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +121 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/impact-analysis.d.ts +3 -0
- package/dist/tools/impact-analysis.d.ts.map +1 -0
- package/dist/tools/impact-analysis.js +187 -0
- package/dist/tools/impact-analysis.js.map +1 -0
- package/dist/tools/review-order.d.ts +3 -0
- package/dist/tools/review-order.d.ts.map +1 -0
- package/dist/tools/review-order.js +150 -0
- package/dist/tools/review-order.js.map +1 -0
- package/dist/utils/git.d.ts +24 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +96 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/imports.d.ts +40 -0
- package/dist/utils/imports.d.ts.map +1 -0
- package/dist/utils/imports.js +255 -0
- package/dist/utils/imports.js.map +1 -0
- package/dist/utils/references.d.ts +28 -0
- package/dist/utils/references.d.ts.map +1 -0
- package/dist/utils/references.js +154 -0
- package/dist/utils/references.js.map +1 -0
- package/package.json +46 -0
- package/schema.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 shamashel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# opencode-review-helper
|
|
2
|
+
|
|
3
|
+
OpenCode plugin for reviewing AI-generated code changes. Helps humans efficiently review large changesets by suggesting optimal review order and identifying potential breaking changes.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Review Order**: Analyzes changed files and suggests optimal review sequence based on dependencies, file type priority, and complexity
|
|
8
|
+
- **Impact Analysis**: Finds code outside the changeset that could be affected by the changes (direct consumers, transitive dependencies, test coverage gaps)
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install -g opencode-review-helper
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Then add to your `~/.config/opencode/opencode.jsonc`:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"plugin": ["opencode-review-helper"]
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Setup
|
|
25
|
+
|
|
26
|
+
Configure the sub-agent model (optional):
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx opencode-review-helper setup
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This lets you choose which model to use for the impact-explorer sub-agent (default: `google/gemini-3-flash`).
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
Once installed, the plugin provides two tools:
|
|
37
|
+
|
|
38
|
+
### `review_order`
|
|
39
|
+
|
|
40
|
+
Suggests optimal order to review changed files.
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
Use review_order to analyze the changed files
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Output:
|
|
47
|
+
- Prioritized file list with rationale
|
|
48
|
+
- Dependency graph showing import relationships
|
|
49
|
+
- Scores based on: type priority, dependents count, complexity
|
|
50
|
+
|
|
51
|
+
### `impact_analysis`
|
|
52
|
+
|
|
53
|
+
Finds code outside the changeset that could break.
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
Use impact_analysis on these files: ["src/models/user.ts", "src/api/auth.ts"]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Output:
|
|
60
|
+
- Direct consumers (files that import changed code)
|
|
61
|
+
- Transitive impact (files that use files that use changed code)
|
|
62
|
+
- Test coverage gaps (changed files without tests)
|
|
63
|
+
- Recommendations
|
|
64
|
+
|
|
65
|
+
## Configuration
|
|
66
|
+
|
|
67
|
+
Create `~/.config/opencode/review-helper.json` or `.opencode/review-helper.json` in your project:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"models": {
|
|
72
|
+
"explorer": "google/gemini-3-flash"
|
|
73
|
+
},
|
|
74
|
+
"review_order": {
|
|
75
|
+
"type_priority": [
|
|
76
|
+
"migration",
|
|
77
|
+
"schema",
|
|
78
|
+
"model",
|
|
79
|
+
"service",
|
|
80
|
+
"resolver",
|
|
81
|
+
"component",
|
|
82
|
+
"test"
|
|
83
|
+
],
|
|
84
|
+
"instructions": "Always review database changes before business logic"
|
|
85
|
+
},
|
|
86
|
+
"impact_analysis": {
|
|
87
|
+
"max_depth": 2,
|
|
88
|
+
"max_results_per_level": 50,
|
|
89
|
+
"exclude_patterns": ["**/*.test.ts", "**/__tests__/**"]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Configuration Options
|
|
95
|
+
|
|
96
|
+
| Option | Description | Default |
|
|
97
|
+
|--------|-------------|---------|
|
|
98
|
+
| `models.explorer` | Model for sub-agent exploration | `google/gemini-3-flash` |
|
|
99
|
+
| `review_order.type_priority` | File type keywords in priority order | See above |
|
|
100
|
+
| `review_order.instructions` | Custom ordering instructions | - |
|
|
101
|
+
| `impact_analysis.max_depth` | Transitive analysis depth (1-3) | `2` |
|
|
102
|
+
| `impact_analysis.max_results_per_level` | Max results per category | `50` |
|
|
103
|
+
| `impact_analysis.exclude_patterns` | Glob patterns to skip | Test files |
|
|
104
|
+
|
|
105
|
+
## Agents
|
|
106
|
+
|
|
107
|
+
The plugin includes a `review-helper:code-reviewer` agent that combines both tools for comprehensive review. It:
|
|
108
|
+
|
|
109
|
+
1. Runs `review_order` to determine file review sequence
|
|
110
|
+
2. Runs `impact_analysis` to find affected external code
|
|
111
|
+
3. Generates a combined report with recommendations
|
|
112
|
+
|
|
113
|
+
The agent does NOT automatically run tests or apply fixes.
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: review-helper:code-reviewer
|
|
3
|
+
description: |
|
|
4
|
+
Comprehensive code reviewer for AI-generated changes. Analyzes:
|
|
5
|
+
- Optimal review order based on file dependencies
|
|
6
|
+
- Impact on code outside the changeset (transitive)
|
|
7
|
+
|
|
8
|
+
Does NOT automatically run tests or fix code. For debugging help, explicitly ask.
|
|
9
|
+
|
|
10
|
+
Examples:
|
|
11
|
+
<example>
|
|
12
|
+
Context: User completed AI-assisted implementation
|
|
13
|
+
user: "Review the changes Claude just made"
|
|
14
|
+
assistant: "I'll analyze the changes for review order and impact."
|
|
15
|
+
</example>
|
|
16
|
+
|
|
17
|
+
<example>
|
|
18
|
+
Context: Large PR review
|
|
19
|
+
user: "What should I look at first in this PR?"
|
|
20
|
+
assistant: "I'll determine the optimal review order based on dependencies."
|
|
21
|
+
</example>
|
|
22
|
+
|
|
23
|
+
<example>
|
|
24
|
+
Context: Concern about breaking changes
|
|
25
|
+
user: "What else might this break?"
|
|
26
|
+
assistant: "I'll analyze transitive impact on code outside these changes."
|
|
27
|
+
</example>
|
|
28
|
+
|
|
29
|
+
model: inherit
|
|
30
|
+
color: red
|
|
31
|
+
tools: [review_order, impact_analysis, Read, Grep, Glob, task]
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
You are an expert code reviewer specialized in reviewing AI-generated code changes.
|
|
35
|
+
|
|
36
|
+
## Your Role
|
|
37
|
+
|
|
38
|
+
Help humans review large AI-generated changesets by:
|
|
39
|
+
1. Suggesting optimal review order (dependencies first)
|
|
40
|
+
2. Identifying code outside the changeset that could break
|
|
41
|
+
|
|
42
|
+
You do NOT automatically run tests or apply fixes. If the user wants debugging help, they will ask explicitly.
|
|
43
|
+
|
|
44
|
+
## Default Workflow
|
|
45
|
+
|
|
46
|
+
When asked to review changes:
|
|
47
|
+
|
|
48
|
+
### Step 1: Determine Review Order
|
|
49
|
+
|
|
50
|
+
Use the `review_order` tool:
|
|
51
|
+
- Analyzes changed files from git diff
|
|
52
|
+
- Builds import dependency graph
|
|
53
|
+
- Scores files by: type priority, centrality, complexity
|
|
54
|
+
- Returns prioritized list with rationale
|
|
55
|
+
|
|
56
|
+
### Step 2: Analyze Impact
|
|
57
|
+
|
|
58
|
+
Use the `impact_analysis` tool:
|
|
59
|
+
- Finds code outside changeset that uses changed exports
|
|
60
|
+
- Identifies direct consumers and transitive dependencies
|
|
61
|
+
- Flags files without test coverage
|
|
62
|
+
- Reports potential breaking changes
|
|
63
|
+
|
|
64
|
+
### Step 3: Generate Report
|
|
65
|
+
|
|
66
|
+
Present findings clearly:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
## Review Order
|
|
70
|
+
| # | File | Reason |
|
|
71
|
+
|---|------|--------|
|
|
72
|
+
| 1 | migrations/... | Schema change - affects downstream |
|
|
73
|
+
| 2 | src/models/... | Core model - 4 files depend on this |
|
|
74
|
+
|
|
75
|
+
## Impact Analysis
|
|
76
|
+
### Direct Consumers (not in changeset)
|
|
77
|
+
- `src/api/auth.ts:42` imports `User` from `src/models/user.ts`
|
|
78
|
+
|
|
79
|
+
### Transitive Impact
|
|
80
|
+
- `src/api/reports.ts` → via `src/api/orders.ts` → changed file
|
|
81
|
+
|
|
82
|
+
### Test Coverage Gaps
|
|
83
|
+
- `src/models/user.ts` has no corresponding test file
|
|
84
|
+
|
|
85
|
+
## Recommendations
|
|
86
|
+
- Review migrations first - they affect all downstream code
|
|
87
|
+
- Check `src/api/auth.ts` for compatibility with User changes
|
|
88
|
+
- Consider adding tests for `src/models/user.ts`
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Key Principles
|
|
92
|
+
|
|
93
|
+
- **Focus on integration boundaries** - where AI often misses issues
|
|
94
|
+
- **Flag files with many dependents** as high-risk
|
|
95
|
+
- **Note missing test coverage** for changed code
|
|
96
|
+
- **Be concise** - prioritize actionable insights over exhaustive lists
|
|
97
|
+
- **Don't run tests automatically** - only analyze, let user decide next steps
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: review-helper:impact-explorer
|
|
3
|
+
description: Sub-agent for transitive reference exploration. Called by impact_analysis tool for deep dependency crawling.
|
|
4
|
+
mode: subagent
|
|
5
|
+
model: google/gemini-3-flash
|
|
6
|
+
color: cyan
|
|
7
|
+
tools: [Read, Grep, Glob]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
You explore references to symbols transitively.
|
|
11
|
+
|
|
12
|
+
## Input
|
|
13
|
+
|
|
14
|
+
You receive:
|
|
15
|
+
- `symbol`: Name to find references for
|
|
16
|
+
- `source_file`: Where symbol is defined
|
|
17
|
+
- `exclude_files`: Files already in changeset (skip these)
|
|
18
|
+
- `depth`: Current depth level
|
|
19
|
+
|
|
20
|
+
## Process
|
|
21
|
+
|
|
22
|
+
1. Find all references to `symbol`:
|
|
23
|
+
- Search for import statements containing the symbol
|
|
24
|
+
- Search for direct usage of the symbol name
|
|
25
|
+
2. Filter out files in `exclude_files`
|
|
26
|
+
3. For each reference, note:
|
|
27
|
+
- File path
|
|
28
|
+
- Line number
|
|
29
|
+
- Usage type (import, call, extends, etc.)
|
|
30
|
+
|
|
31
|
+
## Output
|
|
32
|
+
|
|
33
|
+
Return structured list:
|
|
34
|
+
```
|
|
35
|
+
References to `UserModel`:
|
|
36
|
+
- src/api/auth.ts:15 (import)
|
|
37
|
+
- src/api/orders.ts:42 (call)
|
|
38
|
+
- src/services/email.ts:8 (import)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Be fast and focused. Find references, don't analyze the code deeply.
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { saveGlobalConfig } from "./config.js";
|
|
3
|
+
import { createInterface } from "node:readline";
|
|
4
|
+
const MODELS = [
|
|
5
|
+
{ value: "google/gemini-3-flash", label: "Google Gemini 3 Flash (fast, recommended)" },
|
|
6
|
+
{ value: "google/gemini-3-flash-lite", label: "Google Gemini 3 Flash Lite (fastest)" },
|
|
7
|
+
{ value: "anthropic/claude-3-haiku", label: "Anthropic Claude 3 Haiku" },
|
|
8
|
+
{ value: "openai/gpt-4o-mini", label: "OpenAI GPT-4o Mini" },
|
|
9
|
+
];
|
|
10
|
+
async function prompt(question) {
|
|
11
|
+
const rl = createInterface({
|
|
12
|
+
input: process.stdin,
|
|
13
|
+
output: process.stdout,
|
|
14
|
+
});
|
|
15
|
+
return new Promise((resolve) => {
|
|
16
|
+
rl.question(question, (answer) => {
|
|
17
|
+
rl.close();
|
|
18
|
+
resolve(answer.trim());
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
async function setup() {
|
|
23
|
+
console.log("\n🔍 OpenCode Review Helper - Setup\n");
|
|
24
|
+
console.log("Select a model for the impact-explorer sub-agent:\n");
|
|
25
|
+
MODELS.forEach((m, i) => {
|
|
26
|
+
console.log(` ${i + 1}. ${m.label}`);
|
|
27
|
+
});
|
|
28
|
+
console.log(` ${MODELS.length + 1}. Custom (enter model string)`);
|
|
29
|
+
console.log();
|
|
30
|
+
const choice = await prompt(`Choice [1-${MODELS.length + 1}]: `);
|
|
31
|
+
const choiceNum = parseInt(choice, 10);
|
|
32
|
+
let selectedModel;
|
|
33
|
+
if (choiceNum >= 1 && choiceNum <= MODELS.length) {
|
|
34
|
+
selectedModel = MODELS[choiceNum - 1].value;
|
|
35
|
+
}
|
|
36
|
+
else if (choiceNum === MODELS.length + 1) {
|
|
37
|
+
selectedModel = await prompt("Enter model string (e.g., provider/model): ");
|
|
38
|
+
if (!selectedModel.includes("/")) {
|
|
39
|
+
console.error("Invalid model format. Expected: provider/model");
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
console.log("Invalid choice, using default: google/gemini-3-flash");
|
|
45
|
+
selectedModel = "google/gemini-3-flash";
|
|
46
|
+
}
|
|
47
|
+
await saveGlobalConfig({
|
|
48
|
+
models: {
|
|
49
|
+
explorer: selectedModel,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
console.log(`\n✅ Configuration saved!`);
|
|
53
|
+
console.log(` Explorer model: ${selectedModel}`);
|
|
54
|
+
console.log(` Config location: ~/.config/opencode/review-helper.json\n`);
|
|
55
|
+
console.log("To use the plugin, add to your opencode.jsonc:\n");
|
|
56
|
+
console.log(' { "plugin": ["opencode-review-helper"] }\n');
|
|
57
|
+
}
|
|
58
|
+
function showHelp() {
|
|
59
|
+
console.log(`
|
|
60
|
+
opencode-review-helper - OpenCode plugin for AI code review
|
|
61
|
+
|
|
62
|
+
Commands:
|
|
63
|
+
setup Configure the plugin (model selection)
|
|
64
|
+
help Show this help message
|
|
65
|
+
|
|
66
|
+
Usage in opencode.jsonc:
|
|
67
|
+
{ "plugin": ["opencode-review-helper"] }
|
|
68
|
+
|
|
69
|
+
Tools provided:
|
|
70
|
+
review_order - Suggests optimal order to review changed files
|
|
71
|
+
impact_analysis - Finds code outside changeset that could be affected
|
|
72
|
+
|
|
73
|
+
Learn more: https://github.com/shamashel/opencode-review-helper
|
|
74
|
+
`);
|
|
75
|
+
}
|
|
76
|
+
const command = process.argv[2];
|
|
77
|
+
switch (command) {
|
|
78
|
+
case "setup":
|
|
79
|
+
setup().catch(console.error);
|
|
80
|
+
break;
|
|
81
|
+
case "help":
|
|
82
|
+
case "--help":
|
|
83
|
+
case "-h":
|
|
84
|
+
default:
|
|
85
|
+
showHelp();
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,gBAAgB,EAAc,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,MAAM,GAAG;IACb,EAAE,KAAK,EAAE,uBAAuB,EAAE,KAAK,EAAE,2CAA2C,EAAE;IACtF,EAAE,KAAK,EAAE,4BAA4B,EAAE,KAAK,EAAE,sCAAsC,EAAE;IACtF,EAAE,KAAK,EAAE,0BAA0B,EAAE,KAAK,EAAE,0BAA0B,EAAE;IACxE,EAAE,KAAK,EAAE,oBAAoB,EAAE,KAAK,EAAE,oBAAoB,EAAE;CAC7D,CAAC;AAEF,KAAK,UAAU,MAAM,CAAC,QAAgB;IACpC,MAAM,EAAE,GAAG,eAAe,CAAC;QACzB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;IAEH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;YAC/B,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,KAAK;IAClB,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;IAErD,OAAO,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAAC;IACnE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACtB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,MAAM,GAAG,CAAC,+BAA+B,CAAC,CAAC;IACnE,OAAO,CAAC,GAAG,EAAE,CAAC;IAEd,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,MAAM,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC;IACjE,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAEvC,IAAI,aAAqB,CAAC;IAE1B,IAAI,SAAS,IAAI,CAAC,IAAI,SAAS,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QACjD,aAAa,GAAG,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;IAC9C,CAAC;SAAM,IAAI,SAAS,KAAK,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3C,aAAa,GAAG,MAAM,MAAM,CAAC,6CAA6C,CAAC,CAAC;QAC5E,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;YAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;QACpE,aAAa,GAAG,uBAAuB,CAAC;IAC1C,CAAC;IAED,MAAM,gBAAgB,CAAC;QACrB,MAAM,EAAE;YACN,QAAQ,EAAE,aAAa;SACxB;KACF,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;IACxC,OAAO,CAAC,GAAG,CAAC,sBAAsB,aAAa,EAAE,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;IAE3E,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAC;IAChE,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,QAAQ;IACf,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;CAeb,CAAC,CAAC;AACH,CAAC;AAED,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAEhC,QAAQ,OAAO,EAAE,CAAC;IAChB,KAAK,OAAO;QACV,KAAK,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC7B,MAAM;IACR,KAAK,MAAM,CAAC;IACZ,KAAK,QAAQ,CAAC;IACd,KAAK,IAAI,CAAC;IACV;QACE,QAAQ,EAAE,CAAC;QACX,MAAM;AACV,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface ReviewHelperConfig {
|
|
2
|
+
models: {
|
|
3
|
+
explorer: string;
|
|
4
|
+
};
|
|
5
|
+
review_order: {
|
|
6
|
+
type_priority: string[];
|
|
7
|
+
instructions?: string;
|
|
8
|
+
};
|
|
9
|
+
impact_analysis: {
|
|
10
|
+
max_depth: number;
|
|
11
|
+
max_results_per_level: number;
|
|
12
|
+
exclude_patterns: string[];
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Load and merge config from all sources
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadConfig(projectDir?: string): Promise<ReviewHelperConfig>;
|
|
19
|
+
/**
|
|
20
|
+
* Save config to the global config file
|
|
21
|
+
*/
|
|
22
|
+
export declare function saveGlobalConfig(config: Partial<ReviewHelperConfig>): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Get the explorer model from config
|
|
25
|
+
*/
|
|
26
|
+
export declare function getExplorerModel(projectDir?: string): Promise<string>;
|
|
27
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,YAAY,EAAE;QACZ,aAAa,EAAE,MAAM,EAAE,CAAC;QACxB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,eAAe,EAAE;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,qBAAqB,EAAE,MAAM,CAAC;QAC9B,gBAAgB,EAAE,MAAM,EAAE,CAAC;KAC5B,CAAC;CACH;AAoDD;;GAEG;AACH,wBAAsB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAiBjF;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBzF;AAgCD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAG3E"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
const DEFAULT_CONFIG = {
|
|
6
|
+
models: {
|
|
7
|
+
explorer: "google/gemini-3-flash",
|
|
8
|
+
},
|
|
9
|
+
review_order: {
|
|
10
|
+
type_priority: [
|
|
11
|
+
"migration",
|
|
12
|
+
"schema",
|
|
13
|
+
"prisma",
|
|
14
|
+
"model",
|
|
15
|
+
"entity",
|
|
16
|
+
"type",
|
|
17
|
+
"interface",
|
|
18
|
+
"util",
|
|
19
|
+
"lib",
|
|
20
|
+
"service",
|
|
21
|
+
"resolver",
|
|
22
|
+
"controller",
|
|
23
|
+
"api",
|
|
24
|
+
"route",
|
|
25
|
+
"component",
|
|
26
|
+
"hook",
|
|
27
|
+
"test",
|
|
28
|
+
"spec",
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
impact_analysis: {
|
|
32
|
+
max_depth: 2,
|
|
33
|
+
max_results_per_level: 50,
|
|
34
|
+
exclude_patterns: ["**/*.test.ts", "**/*.spec.ts", "**/__tests__/**", "**/__mocks__/**"],
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Get config file paths in order of precedence (higher = overrides lower)
|
|
39
|
+
*/
|
|
40
|
+
function getConfigPaths(projectDir) {
|
|
41
|
+
const paths = [];
|
|
42
|
+
// Global config
|
|
43
|
+
paths.push(join(homedir(), ".config", "opencode", "review-helper.json"));
|
|
44
|
+
// Project-local config
|
|
45
|
+
if (projectDir) {
|
|
46
|
+
paths.push(join(projectDir, ".opencode", "review-helper.json"));
|
|
47
|
+
}
|
|
48
|
+
return paths;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Load and merge config from all sources
|
|
52
|
+
*/
|
|
53
|
+
export async function loadConfig(projectDir) {
|
|
54
|
+
let config = { ...DEFAULT_CONFIG };
|
|
55
|
+
for (const configPath of getConfigPaths(projectDir)) {
|
|
56
|
+
try {
|
|
57
|
+
if (existsSync(configPath)) {
|
|
58
|
+
const content = await readFile(configPath, "utf-8");
|
|
59
|
+
const parsed = JSON.parse(content);
|
|
60
|
+
config = deepMerge(config, parsed);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
// Skip invalid config files
|
|
65
|
+
console.warn(`Warning: Could not load config from ${configPath}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return config;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Save config to the global config file
|
|
72
|
+
*/
|
|
73
|
+
export async function saveGlobalConfig(config) {
|
|
74
|
+
const configPath = join(homedir(), ".config", "opencode", "review-helper.json");
|
|
75
|
+
const configDir = dirname(configPath);
|
|
76
|
+
// Ensure directory exists
|
|
77
|
+
await mkdir(configDir, { recursive: true });
|
|
78
|
+
// Load existing config and merge
|
|
79
|
+
let existing = {};
|
|
80
|
+
try {
|
|
81
|
+
if (existsSync(configPath)) {
|
|
82
|
+
const content = await readFile(configPath, "utf-8");
|
|
83
|
+
existing = JSON.parse(content);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Start fresh
|
|
88
|
+
}
|
|
89
|
+
const merged = deepMerge(existing, config);
|
|
90
|
+
await writeFile(configPath, JSON.stringify(merged, null, 2) + "\n");
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Deep merge two objects
|
|
94
|
+
*/
|
|
95
|
+
function deepMerge(target, source) {
|
|
96
|
+
const result = { ...target };
|
|
97
|
+
for (const key in source) {
|
|
98
|
+
const sourceValue = source[key];
|
|
99
|
+
const targetValue = result[key];
|
|
100
|
+
if (sourceValue &&
|
|
101
|
+
typeof sourceValue === "object" &&
|
|
102
|
+
!Array.isArray(sourceValue) &&
|
|
103
|
+
targetValue &&
|
|
104
|
+
typeof targetValue === "object" &&
|
|
105
|
+
!Array.isArray(targetValue)) {
|
|
106
|
+
result[key] = deepMerge(targetValue, sourceValue);
|
|
107
|
+
}
|
|
108
|
+
else if (sourceValue !== undefined) {
|
|
109
|
+
result[key] = sourceValue;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get the explorer model from config
|
|
116
|
+
*/
|
|
117
|
+
export async function getExplorerModel(projectDir) {
|
|
118
|
+
const config = await loadConfig(projectDir);
|
|
119
|
+
return config.models.explorer;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAiBrC,MAAM,cAAc,GAAuB;IACzC,MAAM,EAAE;QACN,QAAQ,EAAE,uBAAuB;KAClC;IACD,YAAY,EAAE;QACZ,aAAa,EAAE;YACb,WAAW;YACX,QAAQ;YACR,QAAQ;YACR,OAAO;YACP,QAAQ;YACR,MAAM;YACN,WAAW;YACX,MAAM;YACN,KAAK;YACL,SAAS;YACT,UAAU;YACV,YAAY;YACZ,KAAK;YACL,OAAO;YACP,WAAW;YACX,MAAM;YACN,MAAM;YACN,MAAM;SACP;KACF;IACD,eAAe,EAAE;QACf,SAAS,EAAE,CAAC;QACZ,qBAAqB,EAAE,EAAE;QACzB,gBAAgB,EAAE,CAAC,cAAc,EAAE,cAAc,EAAE,iBAAiB,EAAE,iBAAiB,CAAC;KACzF;CACF,CAAC;AAEF;;GAEG;AACH,SAAS,cAAc,CAAC,UAAmB;IACzC,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,gBAAgB;IAChB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,oBAAoB,CAAC,CAAC,CAAC;IAEzE,uBAAuB;IACvB,IAAI,UAAU,EAAE,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,EAAE,oBAAoB,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,UAAmB;IAClD,IAAI,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,CAAC;IAEnC,KAAK,MAAM,UAAU,IAAI,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC;QACpD,IAAI,CAAC;YACH,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC3B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;gBACpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBACnC,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,4BAA4B;YAC5B,OAAO,CAAC,IAAI,CAAC,uCAAuC,UAAU,EAAE,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAmC;IACxE,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,oBAAoB,CAAC,CAAC;IAChF,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAEtC,0BAA0B;IAC1B,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5C,iCAAiC;IACjC,IAAI,QAAQ,GAAgC,EAAE,CAAC;IAC/C,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC3B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACpD,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,cAAc;IAChB,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC3C,MAAM,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AACtE,CAAC;AAED;;GAEG;AACH,SAAS,SAAS,CAAoC,MAAS,EAAE,MAAkB;IACjF,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;IAE7B,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAEhC,IACE,WAAW;YACX,OAAO,WAAW,KAAK,QAAQ;YAC/B,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC;YAC3B,WAAW;YACX,OAAO,WAAW,KAAK,QAAQ;YAC/B,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAC3B,CAAC;YACA,MAAkC,CAAC,GAAG,CAAC,GAAG,SAAS,CAClD,WAAsC,EACtC,WAAsC,CACvC,CAAC;QACJ,CAAC;aAAM,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YACpC,MAAkC,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC;QACzD,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,UAAmB;IACxD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC;IAC5C,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;AAChC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAIlD,eAAO,MAAM,kBAAkB,EAAE,MAOhC,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { reviewOrderTool } from "./tools/review-order.js";
|
|
2
|
+
import { impactAnalysisTool } from "./tools/impact-analysis.js";
|
|
3
|
+
export const ReviewHelperPlugin = async ({ project, client, $, directory }) => {
|
|
4
|
+
return {
|
|
5
|
+
tool: {
|
|
6
|
+
review_order: reviewOrderTool,
|
|
7
|
+
impact_analysis: impactAnalysisTool,
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export default ReviewHelperPlugin;
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhE,MAAM,CAAC,MAAM,kBAAkB,GAAW,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE;IACpF,OAAO;QACL,IAAI,EAAE;YACJ,YAAY,EAAE,eAAe;YAC7B,eAAe,EAAE,kBAAkB;SACpC;KACF,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"impact-analysis.d.ts","sourceRoot":"","sources":["../../src/tools/impact-analysis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AA8CrE,eAAO,MAAM,kBAAkB,EAAE,cAgM/B,CAAC"}
|