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.
@@ -0,0 +1,255 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { dirname, resolve, extname } from "node:path";
3
+ /**
4
+ * Extract imports from a TypeScript/JavaScript file
5
+ * Uses regex for speed - not perfect but handles common cases
6
+ */
7
+ export async function extractImports(filePath) {
8
+ const ext = extname(filePath);
9
+ if (![".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].includes(ext)) {
10
+ return [];
11
+ }
12
+ try {
13
+ const content = await readFile(filePath, "utf-8");
14
+ return parseImports(content);
15
+ }
16
+ catch {
17
+ return [];
18
+ }
19
+ }
20
+ /**
21
+ * Parse imports from file content
22
+ */
23
+ export function parseImports(content) {
24
+ const imports = [];
25
+ const lines = content.split("\n");
26
+ // Patterns for different import styles
27
+ const patterns = [
28
+ // import { a, b } from "module"
29
+ /^import\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/,
30
+ // import X from "module"
31
+ /^import\s+(\w+)\s+from\s+['"]([^'"]+)['"]/,
32
+ // import * as X from "module"
33
+ /^import\s+\*\s+as\s+(\w+)\s+from\s+['"]([^'"]+)['"]/,
34
+ // import "module" (side effect)
35
+ /^import\s+['"]([^'"]+)['"]/,
36
+ // import X, { a, b } from "module"
37
+ /^import\s+(\w+)\s*,\s*\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/,
38
+ // Dynamic import: const x = await import("module")
39
+ /import\s*\(\s*['"]([^'"]+)['"]\s*\)/,
40
+ // require (CommonJS)
41
+ /require\s*\(\s*['"]([^'"]+)['"]\s*\)/,
42
+ ];
43
+ for (let i = 0; i < lines.length; i++) {
44
+ const line = lines[i].trim();
45
+ const lineNum = i + 1;
46
+ // Skip comments
47
+ if (line.startsWith("//") || line.startsWith("/*"))
48
+ continue;
49
+ // Try each pattern
50
+ // import { a, b } from "module"
51
+ let match = line.match(/^import\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/);
52
+ if (match) {
53
+ const specifiers = match[1].split(",").map((s) => {
54
+ // Handle "X as Y" syntax
55
+ const parts = s.trim().split(/\s+as\s+/);
56
+ return parts[0].trim();
57
+ }).filter(Boolean);
58
+ imports.push({
59
+ source: match[2],
60
+ specifiers,
61
+ isDefault: false,
62
+ isNamespace: false,
63
+ line: lineNum,
64
+ });
65
+ continue;
66
+ }
67
+ // import X, { a, b } from "module"
68
+ match = line.match(/^import\s+(\w+)\s*,\s*\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/);
69
+ if (match) {
70
+ const specifiers = match[2].split(",").map((s) => s.trim().split(/\s+as\s+/)[0].trim()).filter(Boolean);
71
+ imports.push({
72
+ source: match[3],
73
+ specifiers: [match[1], ...specifiers],
74
+ isDefault: true,
75
+ isNamespace: false,
76
+ line: lineNum,
77
+ });
78
+ continue;
79
+ }
80
+ // import * as X from "module"
81
+ match = line.match(/^import\s+\*\s+as\s+(\w+)\s+from\s+['"]([^'"]+)['"]/);
82
+ if (match) {
83
+ imports.push({
84
+ source: match[2],
85
+ specifiers: [match[1]],
86
+ isDefault: false,
87
+ isNamespace: true,
88
+ line: lineNum,
89
+ });
90
+ continue;
91
+ }
92
+ // import X from "module"
93
+ match = line.match(/^import\s+(\w+)\s+from\s+['"]([^'"]+)['"]/);
94
+ if (match) {
95
+ imports.push({
96
+ source: match[2],
97
+ specifiers: [match[1]],
98
+ isDefault: true,
99
+ isNamespace: false,
100
+ line: lineNum,
101
+ });
102
+ continue;
103
+ }
104
+ // import "module" (side effect only)
105
+ match = line.match(/^import\s+['"]([^'"]+)['"]/);
106
+ if (match) {
107
+ imports.push({
108
+ source: match[1],
109
+ specifiers: [],
110
+ isDefault: false,
111
+ isNamespace: false,
112
+ line: lineNum,
113
+ });
114
+ continue;
115
+ }
116
+ }
117
+ return imports;
118
+ }
119
+ /**
120
+ * Extract exports from a TypeScript/JavaScript file
121
+ */
122
+ export async function extractExports(filePath) {
123
+ const ext = extname(filePath);
124
+ if (![".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].includes(ext)) {
125
+ return [];
126
+ }
127
+ try {
128
+ const content = await readFile(filePath, "utf-8");
129
+ return parseExports(content);
130
+ }
131
+ catch {
132
+ return [];
133
+ }
134
+ }
135
+ /**
136
+ * Parse exports from file content
137
+ */
138
+ export function parseExports(content) {
139
+ const exports = [];
140
+ const lines = content.split("\n");
141
+ for (let i = 0; i < lines.length; i++) {
142
+ const line = lines[i].trim();
143
+ const lineNum = i + 1;
144
+ // Skip comments
145
+ if (line.startsWith("//") || line.startsWith("/*"))
146
+ continue;
147
+ // export default
148
+ if (line.match(/^export\s+default\s+/)) {
149
+ // Try to extract name: export default function X, export default class X
150
+ const match = line.match(/^export\s+default\s+(?:function|class)\s+(\w+)/);
151
+ exports.push({
152
+ name: match ? match[1] : "default",
153
+ type: "default",
154
+ line: lineNum,
155
+ });
156
+ continue;
157
+ }
158
+ // export { a, b } or export { a, b } from "module"
159
+ let match = line.match(/^export\s+\{([^}]+)\}(?:\s+from\s+['"]([^'"]+)['"])?/);
160
+ if (match) {
161
+ const isReexport = !!match[2];
162
+ const names = match[1].split(",").map((s) => {
163
+ // Handle "X as Y" - export the "Y" name
164
+ const parts = s.trim().split(/\s+as\s+/);
165
+ return parts[parts.length - 1].trim();
166
+ }).filter(Boolean);
167
+ for (const name of names) {
168
+ exports.push({
169
+ name,
170
+ type: isReexport ? "reexport" : "named",
171
+ line: lineNum,
172
+ });
173
+ }
174
+ continue;
175
+ }
176
+ // export const/let/var/function/class/type/interface
177
+ match = line.match(/^export\s+(?:const|let|var|function|class|type|interface|enum)\s+(\w+)/);
178
+ if (match) {
179
+ exports.push({
180
+ name: match[1],
181
+ type: "named",
182
+ line: lineNum,
183
+ });
184
+ continue;
185
+ }
186
+ // export * from "module"
187
+ match = line.match(/^export\s+\*\s+from\s+['"]([^'"]+)['"]/);
188
+ if (match) {
189
+ exports.push({
190
+ name: `* from ${match[1]}`,
191
+ type: "reexport",
192
+ line: lineNum,
193
+ });
194
+ continue;
195
+ }
196
+ }
197
+ return exports;
198
+ }
199
+ /**
200
+ * Resolve an import path to a file path
201
+ * Handles relative imports, not aliases (those need tsconfig)
202
+ */
203
+ export function resolveImportPath(importSource, fromFile, extensions = [".ts", ".tsx", ".js", ".jsx"]) {
204
+ // Skip node_modules and non-relative imports
205
+ if (!importSource.startsWith(".") && !importSource.startsWith("/")) {
206
+ return null;
207
+ }
208
+ const dir = dirname(fromFile);
209
+ const basePath = resolve(dir, importSource);
210
+ // Return with original path - caller should check existence
211
+ // Could be basePath, basePath.ts, basePath/index.ts, etc.
212
+ return basePath;
213
+ }
214
+ /**
215
+ * Build a dependency graph from changed files
216
+ * Returns map of file -> files it imports (that are also in the changeset)
217
+ */
218
+ export async function buildDependencyGraph(files, cwd) {
219
+ const graph = new Map();
220
+ const fileSet = new Set(files.map((f) => resolve(cwd, f)));
221
+ for (const file of files) {
222
+ const fullPath = resolve(cwd, file);
223
+ const imports = await extractImports(fullPath);
224
+ const deps = [];
225
+ for (const imp of imports) {
226
+ const resolved = resolveImportPath(imp.source, fullPath);
227
+ if (!resolved)
228
+ continue;
229
+ // Check if any variation of this path is in our file set
230
+ const possiblePaths = [
231
+ resolved,
232
+ resolved + ".ts",
233
+ resolved + ".tsx",
234
+ resolved + ".js",
235
+ resolved + ".jsx",
236
+ resolved + "/index.ts",
237
+ resolved + "/index.tsx",
238
+ resolved + "/index.js",
239
+ ];
240
+ for (const possible of possiblePaths) {
241
+ if (fileSet.has(possible)) {
242
+ // Convert back to relative path
243
+ const relPath = files.find((f) => resolve(cwd, f) === possible);
244
+ if (relPath && !deps.includes(relPath)) {
245
+ deps.push(relPath);
246
+ }
247
+ break;
248
+ }
249
+ }
250
+ }
251
+ graph.set(file, deps);
252
+ }
253
+ return graph;
254
+ }
255
+ //# sourceMappingURL=imports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imports.js","sourceRoot":"","sources":["../../src/utils/imports.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ export interface Reference {
2
+ file: string;
3
+ line: number;
4
+ column?: number;
5
+ context: string;
6
+ type: "import" | "usage" | "unknown";
7
+ }
8
+ export interface ReferenceFinderOptions {
9
+ cwd: string;
10
+ excludeFiles?: string[];
11
+ maxResults?: number;
12
+ }
13
+ /**
14
+ * Find references to a symbol using grep (fallback when LSP unavailable)
15
+ * This is a best-effort search - not as accurate as LSP but works everywhere
16
+ */
17
+ export declare function findReferencesGrep(symbol: string, options: ReferenceFinderOptions): Promise<Reference[]>;
18
+ /**
19
+ * Find files that import a specific file
20
+ * More accurate than symbol search for finding dependents
21
+ */
22
+ export declare function findImportersOf(targetFile: string, options: ReferenceFinderOptions): Promise<Reference[]>;
23
+ /**
24
+ * Find all references to exports from a file
25
+ * Combines symbol search with import search for better coverage
26
+ */
27
+ export declare function findAllReferencesToFile(targetFile: string, exportedSymbols: string[], options: ReferenceFinderOptions): Promise<Reference[]>;
28
+ //# sourceMappingURL=references.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"references.d.ts","sourceRoot":"","sources":["../../src/utils/references.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,SAAS,EAAE,CAAC,CAqDtB;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,SAAS,EAAE,CAAC,CAyDtB;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,EAAE,EACzB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,SAAS,EAAE,CAAC,CAsCtB"}
@@ -0,0 +1,154 @@
1
+ import { exec } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import { resolve, relative } from "node:path";
4
+ const execAsync = promisify(exec);
5
+ /**
6
+ * Find references to a symbol using grep (fallback when LSP unavailable)
7
+ * This is a best-effort search - not as accurate as LSP but works everywhere
8
+ */
9
+ export async function findReferencesGrep(symbol, options) {
10
+ const { cwd, excludeFiles = [], maxResults = 50 } = options;
11
+ const references = [];
12
+ const excludeSet = new Set(excludeFiles.map((f) => resolve(cwd, f)));
13
+ try {
14
+ // Use ripgrep if available, fall back to grep
15
+ let cmd;
16
+ try {
17
+ await execAsync("which rg");
18
+ // ripgrep with word boundary
19
+ cmd = `rg -n --no-heading -w "${symbol}" --type ts --type js --type tsx --type jsx 2>/dev/null || true`;
20
+ }
21
+ catch {
22
+ // Fall back to grep
23
+ cmd = `grep -rn --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" -w "${symbol}" . 2>/dev/null || true`;
24
+ }
25
+ const { stdout } = await execAsync(cmd, { cwd, maxBuffer: 10 * 1024 * 1024 });
26
+ for (const line of stdout.trim().split("\n")) {
27
+ if (!line || references.length >= maxResults)
28
+ break;
29
+ // Parse grep/rg output: file:line:content
30
+ const match = line.match(/^([^:]+):(\d+):(.*)$/);
31
+ if (!match)
32
+ continue;
33
+ const [, filePath, lineNum, context] = match;
34
+ const fullPath = resolve(cwd, filePath);
35
+ // Skip excluded files
36
+ if (excludeSet.has(fullPath))
37
+ continue;
38
+ // Determine reference type
39
+ let type = "unknown";
40
+ const trimmedContext = context.trim();
41
+ if (trimmedContext.startsWith("import ") || trimmedContext.includes(" from ")) {
42
+ type = "import";
43
+ }
44
+ else if (trimmedContext.includes(symbol)) {
45
+ type = "usage";
46
+ }
47
+ references.push({
48
+ file: relative(cwd, fullPath),
49
+ line: parseInt(lineNum, 10),
50
+ context: trimmedContext.slice(0, 200), // Truncate long lines
51
+ type,
52
+ });
53
+ }
54
+ }
55
+ catch (error) {
56
+ // Grep failed, return empty
57
+ }
58
+ return references;
59
+ }
60
+ /**
61
+ * Find files that import a specific file
62
+ * More accurate than symbol search for finding dependents
63
+ */
64
+ export async function findImportersOf(targetFile, options) {
65
+ const { cwd, excludeFiles = [], maxResults = 50 } = options;
66
+ const references = [];
67
+ const excludeSet = new Set(excludeFiles.map((f) => resolve(cwd, f)));
68
+ // Generate patterns to search for
69
+ // e.g., "./utils/auth" or "../utils/auth" or "@/utils/auth"
70
+ const baseName = targetFile
71
+ .replace(/\.(ts|tsx|js|jsx)$/, "")
72
+ .replace(/\/index$/, "");
73
+ // Search for imports containing this file path
74
+ const searchPatterns = [
75
+ baseName,
76
+ baseName.split("/").pop() || baseName, // Just the filename
77
+ ];
78
+ try {
79
+ for (const pattern of searchPatterns) {
80
+ if (references.length >= maxResults)
81
+ break;
82
+ // Search for import statements containing this pattern
83
+ const cmd = `grep -rn --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" -E "from ['\"].*${pattern}['\"]" . 2>/dev/null || true`;
84
+ const { stdout } = await execAsync(cmd, { cwd, maxBuffer: 10 * 1024 * 1024 });
85
+ for (const line of stdout.trim().split("\n")) {
86
+ if (!line || references.length >= maxResults)
87
+ break;
88
+ const match = line.match(/^([^:]+):(\d+):(.*)$/);
89
+ if (!match)
90
+ continue;
91
+ const [, filePath, lineNum, context] = match;
92
+ const fullPath = resolve(cwd, filePath);
93
+ // Skip excluded files and the target file itself
94
+ if (excludeSet.has(fullPath))
95
+ continue;
96
+ if (resolve(cwd, targetFile) === fullPath)
97
+ continue;
98
+ // Avoid duplicates
99
+ if (references.some((r) => r.file === relative(cwd, fullPath) && r.line === parseInt(lineNum, 10))) {
100
+ continue;
101
+ }
102
+ references.push({
103
+ file: relative(cwd, fullPath),
104
+ line: parseInt(lineNum, 10),
105
+ context: context.trim().slice(0, 200),
106
+ type: "import",
107
+ });
108
+ }
109
+ }
110
+ }
111
+ catch (error) {
112
+ // Grep failed
113
+ }
114
+ return references;
115
+ }
116
+ /**
117
+ * Find all references to exports from a file
118
+ * Combines symbol search with import search for better coverage
119
+ */
120
+ export async function findAllReferencesToFile(targetFile, exportedSymbols, options) {
121
+ const allRefs = [];
122
+ const seen = new Set();
123
+ // First, find direct importers
124
+ const importers = await findImportersOf(targetFile, options);
125
+ for (const ref of importers) {
126
+ const key = `${ref.file}:${ref.line}`;
127
+ if (!seen.has(key)) {
128
+ seen.add(key);
129
+ allRefs.push(ref);
130
+ }
131
+ }
132
+ // Then search for each exported symbol
133
+ for (const symbol of exportedSymbols) {
134
+ if (allRefs.length >= (options.maxResults || 50))
135
+ break;
136
+ // Skip common/generic names that would have too many false positives
137
+ if (symbol.length < 3 || ["get", "set", "map", "use", "is"].includes(symbol.toLowerCase())) {
138
+ continue;
139
+ }
140
+ const symbolRefs = await findReferencesGrep(symbol, {
141
+ ...options,
142
+ maxResults: Math.min(20, (options.maxResults || 50) - allRefs.length),
143
+ });
144
+ for (const ref of symbolRefs) {
145
+ const key = `${ref.file}:${ref.line}`;
146
+ if (!seen.has(key)) {
147
+ seen.add(key);
148
+ allRefs.push(ref);
149
+ }
150
+ }
151
+ }
152
+ return allRefs.slice(0, options.maxResults || 50);
153
+ }
154
+ //# sourceMappingURL=references.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"references.js","sourceRoot":"","sources":["../../src/utils/references.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE9C,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAgBlC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAc,EACd,OAA+B;IAE/B,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,EAAE,EAAE,UAAU,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC;IAC5D,MAAM,UAAU,GAAgB,EAAE,CAAC;IACnC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAErE,IAAI,CAAC;QACH,8CAA8C;QAC9C,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,UAAU,CAAC,CAAC;YAC5B,6BAA6B;YAC7B,GAAG,GAAG,0BAA0B,MAAM,iEAAiE,CAAC;QAC1G,CAAC;QAAC,MAAM,CAAC;YACP,oBAAoB;YACpB,GAAG,GAAG,sFAAsF,MAAM,yBAAyB,CAAC;QAC9H,CAAC;QAED,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;QAE9E,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,IAAI,CAAC,IAAI,IAAI,UAAU,CAAC,MAAM,IAAI,UAAU;gBAAE,MAAM;YAEpD,0CAA0C;YAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;YACjD,IAAI,CAAC,KAAK;gBAAE,SAAS;YAErB,MAAM,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC;YAC7C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YAExC,sBAAsB;YACtB,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAEvC,2BAA2B;YAC3B,IAAI,IAAI,GAAsB,SAAS,CAAC;YACxC,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;YACtC,IAAI,cAAc,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,cAAc,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC9E,IAAI,GAAG,QAAQ,CAAC;YAClB,CAAC;iBAAM,IAAI,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC3C,IAAI,GAAG,OAAO,CAAC;YACjB,CAAC;YAED,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC;gBAC7B,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;gBAC3B,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,sBAAsB;gBAC7D,IAAI;aACL,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,4BAA4B;IAC9B,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,UAAkB,EAClB,OAA+B;IAE/B,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,EAAE,EAAE,UAAU,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC;IAC5D,MAAM,UAAU,GAAgB,EAAE,CAAC;IACnC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAErE,kCAAkC;IAClC,4DAA4D;IAC5D,MAAM,QAAQ,GAAG,UAAU;SACxB,OAAO,CAAC,oBAAoB,EAAE,EAAE,CAAC;SACjC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAE3B,+CAA+C;IAC/C,MAAM,cAAc,GAAG;QACrB,QAAQ;QACR,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,QAAQ,EAAE,oBAAoB;KAC5D,CAAC;IAEF,IAAI,CAAC;QACH,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;YACrC,IAAI,UAAU,CAAC,MAAM,IAAI,UAAU;gBAAE,MAAM;YAE3C,uDAAuD;YACvD,MAAM,GAAG,GAAG,kGAAkG,OAAO,8BAA8B,CAAC;YAEpJ,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;YAE9E,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7C,IAAI,CAAC,IAAI,IAAI,UAAU,CAAC,MAAM,IAAI,UAAU;oBAAE,MAAM;gBAEpD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;gBACjD,IAAI,CAAC,KAAK;oBAAE,SAAS;gBAErB,MAAM,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC;gBAC7C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;gBAExC,iDAAiD;gBACjD,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC;oBAAE,SAAS;gBACvC,IAAI,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,KAAK,QAAQ;oBAAE,SAAS;gBAEpD,mBAAmB;gBACnB,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;oBACnG,SAAS;gBACX,CAAC;gBAED,UAAU,CAAC,IAAI,CAAC;oBACd,IAAI,EAAE,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC;oBAC7B,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;oBAC3B,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;oBACrC,IAAI,EAAE,QAAQ;iBACf,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,cAAc;IAChB,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,UAAkB,EAClB,eAAyB,EACzB,OAA+B;IAE/B,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,+BAA+B;IAC/B,MAAM,SAAS,GAAG,MAAM,eAAe,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC7D,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QACtC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACnB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IAED,uCAAuC;IACvC,KAAK,MAAM,MAAM,IAAI,eAAe,EAAE,CAAC;QACrC,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC;YAAE,MAAM;QAExD,qEAAqE;QACrE,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YAC3F,SAAS;QACX,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE;YAClD,GAAG,OAAO;YACV,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;SACtE,CAAC,CAAC;QAEH,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAC7B,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;YACtC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACnB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACd,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;AACpD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "opencode-review-helper",
3
+ "version": "0.1.0",
4
+ "description": "OpenCode plugin for reviewing AI-generated code changes - suggests review order and analyzes impact",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "opencode-review-helper": "dist/cli.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "agents",
14
+ "schema.json"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "dev": "tsc --watch",
19
+ "prepublishOnly": "npm run build",
20
+ "typecheck": "tsc --noEmit"
21
+ },
22
+ "dependencies": {
23
+ "@opencode-ai/plugin": "^1.0.209",
24
+ "zod": "^4.1.8"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "^20.0.0",
28
+ "typescript": "^5.0.0"
29
+ },
30
+ "keywords": [
31
+ "opencode",
32
+ "plugin",
33
+ "code-review",
34
+ "ai",
35
+ "impact-analysis"
36
+ ],
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/shamashel/opencode-review-helper"
40
+ },
41
+ "author": "shamashel",
42
+ "license": "MIT",
43
+ "engines": {
44
+ "node": ">=18.0.0"
45
+ }
46
+ }
package/schema.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "OpenCode Review Helper Configuration",
4
+ "type": "object",
5
+ "properties": {
6
+ "models": {
7
+ "type": "object",
8
+ "properties": {
9
+ "explorer": {
10
+ "type": "string",
11
+ "description": "Model for the impact-explorer sub-agent (e.g., google/gemini-3-flash)",
12
+ "default": "google/gemini-3-flash"
13
+ }
14
+ }
15
+ },
16
+ "review_order": {
17
+ "type": "object",
18
+ "properties": {
19
+ "type_priority": {
20
+ "type": "array",
21
+ "items": { "type": "string" },
22
+ "description": "File type keywords in priority order (highest priority first)",
23
+ "default": ["migration", "schema", "model", "service", "resolver", "component", "test"]
24
+ },
25
+ "instructions": {
26
+ "type": "string",
27
+ "description": "Additional ordering instructions for the review agent"
28
+ }
29
+ }
30
+ },
31
+ "impact_analysis": {
32
+ "type": "object",
33
+ "properties": {
34
+ "max_depth": {
35
+ "type": "integer",
36
+ "minimum": 1,
37
+ "maximum": 3,
38
+ "default": 2,
39
+ "description": "Maximum transitive depth for impact analysis"
40
+ },
41
+ "max_results_per_level": {
42
+ "type": "integer",
43
+ "minimum": 10,
44
+ "maximum": 200,
45
+ "default": 50,
46
+ "description": "Maximum results per depth level"
47
+ },
48
+ "exclude_patterns": {
49
+ "type": "array",
50
+ "items": { "type": "string" },
51
+ "description": "Glob patterns to exclude from analysis",
52
+ "default": ["**/*.test.ts", "**/*.spec.ts", "**/__tests__/**"]
53
+ }
54
+ }
55
+ }
56
+ }
57
+ }