kodevu 0.1.8 → 0.1.10
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/README.md +1 -1
- package/config.example.json +1 -1
- package/package.json +1 -1
- package/src/config.js +43 -4
- package/src/review-runner.js +33 -9
package/README.md
CHANGED
|
@@ -66,7 +66,7 @@ npx kodevu /path/to/your/repo --config ./config.current.json
|
|
|
66
66
|
|
|
67
67
|
- `target`: required repository target; can be provided by config or as the CLI positional argument
|
|
68
68
|
- `reviewer`: `codex`, `gemini`, or `auto`; default `auto`
|
|
69
|
-
- `
|
|
69
|
+
- `prompt`: saved into the report as review context
|
|
70
70
|
- `outputDir`: report output directory; default `~/.kodevu`
|
|
71
71
|
- `stateFilePath`: review state file path; default `~/.kodevu/state.json`
|
|
72
72
|
- `commandTimeoutMs`: timeout for a single review command execution in milliseconds
|
package/config.example.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"target": "C:/path/to/your/repository-or-subdirectory",
|
|
3
3
|
"reviewer": "auto",
|
|
4
|
-
"
|
|
4
|
+
"prompt": "请严格审查当前变更,优先指出 bug、回归风险、兼容性问题、安全问题、边界条件缺陷和缺失测试。请使用简体中文输出 Markdown;如果没有明确缺陷,请写“未发现明确缺陷”,并补充剩余风险。",
|
|
5
5
|
"outputDir": "~/.kodevu",
|
|
6
6
|
"stateFilePath": "~/.kodevu/state.json",
|
|
7
7
|
"commandTimeoutMs": 600000,
|
package/package.json
CHANGED
package/src/config.js
CHANGED
|
@@ -14,7 +14,7 @@ const defaultConfig = {
|
|
|
14
14
|
outputDir: defaultStorageDir,
|
|
15
15
|
stateFilePath: path.join(defaultStorageDir, "state.json"),
|
|
16
16
|
commandTimeoutMs: 600000,
|
|
17
|
-
|
|
17
|
+
prompt:
|
|
18
18
|
"请严格审查当前变更,优先指出 bug、回归风险、兼容性问题、安全问题、边界条件缺陷和缺失测试。请使用简体中文输出 Markdown;如果没有明确缺陷,请写“未发现明确缺陷”,并补充剩余风险。",
|
|
19
19
|
maxRevisionsPerRun: 20
|
|
20
20
|
};
|
|
@@ -39,7 +39,7 @@ function resolveConfigPath(baseDir, value) {
|
|
|
39
39
|
return path.isAbsolute(value) ? value : path.resolve(baseDir, value);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
async function
|
|
42
|
+
async function resolveAutoReviewers(debug, loadedConfigPath) {
|
|
43
43
|
const availableReviewers = [];
|
|
44
44
|
|
|
45
45
|
for (const reviewerName of SUPPORTED_REVIEWERS) {
|
|
@@ -57,7 +57,12 @@ async function resolveAutoReviewer(debug, loadedConfigPath) {
|
|
|
57
57
|
);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
for (let i = availableReviewers.length - 1; i > 0; i--) {
|
|
61
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
62
|
+
[availableReviewers[i], availableReviewers[j]] = [availableReviewers[j], availableReviewers[i]];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return availableReviewers;
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
export function parseCliArgs(argv) {
|
|
@@ -68,6 +73,8 @@ export function parseCliArgs(argv) {
|
|
|
68
73
|
target: "",
|
|
69
74
|
debug: false,
|
|
70
75
|
help: false,
|
|
76
|
+
reviewer: "",
|
|
77
|
+
prompt: "",
|
|
71
78
|
commandExplicitlySet: false
|
|
72
79
|
};
|
|
73
80
|
|
|
@@ -101,6 +108,26 @@ export function parseCliArgs(argv) {
|
|
|
101
108
|
continue;
|
|
102
109
|
}
|
|
103
110
|
|
|
111
|
+
if (value === "--reviewer" || value === "-r") {
|
|
112
|
+
const reviewer = argv[index + 1];
|
|
113
|
+
if (!reviewer || reviewer.startsWith("-")) {
|
|
114
|
+
throw new Error(`Missing value for ${value}`);
|
|
115
|
+
}
|
|
116
|
+
args.reviewer = reviewer;
|
|
117
|
+
index += 1;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (value === "--prompt" || value === "-p") {
|
|
122
|
+
const prompt = argv[index + 1];
|
|
123
|
+
if (!prompt || prompt.startsWith("-")) {
|
|
124
|
+
throw new Error(`Missing value for ${value}`);
|
|
125
|
+
}
|
|
126
|
+
args.prompt = prompt;
|
|
127
|
+
index += 1;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
104
131
|
if (!value.startsWith("-") && args.command === "run" && !args.target) {
|
|
105
132
|
args.target = value;
|
|
106
133
|
continue;
|
|
@@ -139,6 +166,14 @@ export async function loadConfig(configPath, cliArgs = {}) {
|
|
|
139
166
|
config.target = cliArgs.target;
|
|
140
167
|
}
|
|
141
168
|
|
|
169
|
+
if (cliArgs.reviewer) {
|
|
170
|
+
config.reviewer = cliArgs.reviewer;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (cliArgs.prompt) {
|
|
174
|
+
config.prompt = cliArgs.prompt;
|
|
175
|
+
}
|
|
176
|
+
|
|
142
177
|
if (!config.target) {
|
|
143
178
|
throw new Error('Missing target. Pass `npx kodevu <repo-path>` or set "target" in config.json.');
|
|
144
179
|
}
|
|
@@ -147,9 +182,11 @@ export async function loadConfig(configPath, cliArgs = {}) {
|
|
|
147
182
|
config.reviewer = String(config.reviewer || "auto").toLowerCase();
|
|
148
183
|
|
|
149
184
|
if (config.reviewer === "auto") {
|
|
150
|
-
const
|
|
185
|
+
const availableReviewers = await resolveAutoReviewers(config.debug, loadedConfigPath);
|
|
186
|
+
const selectedReviewer = availableReviewers[0];
|
|
151
187
|
config.reviewer = selectedReviewer.reviewerName;
|
|
152
188
|
config.reviewerCommandPath = selectedReviewer.commandPath;
|
|
189
|
+
config.fallbackReviewers = availableReviewers.map(r => r.reviewerName).slice(1);
|
|
153
190
|
config.reviewerWasAutoSelected = true;
|
|
154
191
|
} else if (!SUPPORTED_REVIEWERS.includes(config.reviewer)) {
|
|
155
192
|
throw new Error(
|
|
@@ -188,6 +225,8 @@ Usage:
|
|
|
188
225
|
|
|
189
226
|
Options:
|
|
190
227
|
--config, -c Optional config json path. If omitted, ./config.json is loaded only when present
|
|
228
|
+
--reviewer, -r Override reviewer (codex | gemini | auto)
|
|
229
|
+
--prompt, -p Override prompt
|
|
191
230
|
--debug, -d Print extra debug information to the console
|
|
192
231
|
--help, -h Show help
|
|
193
232
|
|
package/src/review-runner.js
CHANGED
|
@@ -284,7 +284,7 @@ function buildPrompt(config, backend, targetInfo, details, reviewDiffPayload) {
|
|
|
284
284
|
const canReadRelatedFiles = backend.kind === "git" || Boolean(targetInfo.workingCopyPath);
|
|
285
285
|
|
|
286
286
|
return [
|
|
287
|
-
config.
|
|
287
|
+
config.prompt,
|
|
288
288
|
canReadRelatedFiles
|
|
289
289
|
? `You are running inside a read-only workspace rooted at: ${workspaceRoot}`
|
|
290
290
|
: "No local repository workspace is available for this review run.",
|
|
@@ -402,14 +402,38 @@ async function reviewChange(config, backend, targetInfo, changeId) {
|
|
|
402
402
|
}
|
|
403
403
|
|
|
404
404
|
const diffText = await backend.getChangeDiff(config, targetInfo, changeId);
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
405
|
+
const reviewersToTry = [config.reviewer, ...(config.fallbackReviewers || [])];
|
|
406
|
+
|
|
407
|
+
let reviewer;
|
|
408
|
+
let diffPayloads;
|
|
409
|
+
let reviewerResult;
|
|
410
|
+
let currentReviewerConfig;
|
|
411
|
+
|
|
412
|
+
for (const reviewerName of reviewersToTry) {
|
|
413
|
+
currentReviewerConfig = { ...config, reviewer: reviewerName };
|
|
414
|
+
debugLog(config, `Trying reviewer: ${reviewerName}`);
|
|
415
|
+
|
|
416
|
+
const res = await runReviewerPrompt(
|
|
417
|
+
currentReviewerConfig,
|
|
418
|
+
backend,
|
|
419
|
+
targetInfo,
|
|
420
|
+
details,
|
|
421
|
+
diffText
|
|
422
|
+
);
|
|
423
|
+
reviewer = res.reviewer;
|
|
424
|
+
diffPayloads = res.diffPayloads;
|
|
425
|
+
reviewerResult = res.result;
|
|
426
|
+
|
|
427
|
+
if (reviewerResult.code === 0 && !reviewerResult.timedOut) {
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (reviewerName !== reviewersToTry[reviewersToTry.length - 1]) {
|
|
432
|
+
console.log(`${reviewer.displayName} failed for ${details.displayId}; trying next reviewer...`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const report = buildReport(currentReviewerConfig, backend, targetInfo, details, diffPayloads, reviewer, reviewerResult);
|
|
413
437
|
const outputFile = path.join(config.outputDir, backend.getReportFileName(changeId));
|
|
414
438
|
await writeTextFile(outputFile, report);
|
|
415
439
|
|