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 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,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -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":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from "@opencode-ai/plugin";
2
+ export declare const ReviewHelperPlugin: Plugin;
3
+ export default ReviewHelperPlugin;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -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,3 @@
1
+ import { type ToolDefinition } from "@opencode-ai/plugin/tool";
2
+ export declare const impactAnalysisTool: ToolDefinition;
3
+ //# sourceMappingURL=impact-analysis.d.ts.map
@@ -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"}