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
|
@@ -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":"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"}
|
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
|
+
}
|