sales-frontend-gemini-cli 0.4.2 → 0.4.4
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/dist/common/helper.cjs +581 -53
- package/dist/common/helper.cjs.map +1 -1
- package/dist/common/helper.d.cts +114 -3
- package/dist/common/helper.d.ts +114 -3
- package/dist/common/helper.js +570 -54
- package/dist/common/helper.js.map +1 -1
- package/dist/common/types.d.cts +24 -1
- package/dist/common/types.d.ts +24 -1
- package/dist/pr-review/claude/claude-commander.cjs +60 -12
- package/dist/pr-review/claude/claude-commander.cjs.map +1 -1
- package/dist/pr-review/claude/claude-commander.js +60 -12
- package/dist/pr-review/claude/claude-commander.js.map +1 -1
- package/dist/pr-review/claude/installation-claude.cjs +42 -6
- package/dist/pr-review/claude/installation-claude.cjs.map +1 -1
- package/dist/pr-review/claude/installation-claude.js +42 -6
- package/dist/pr-review/claude/installation-claude.js.map +1 -1
- package/dist/pr-review/codex/codex-commander.cjs +63 -12
- package/dist/pr-review/codex/codex-commander.cjs.map +1 -1
- package/dist/pr-review/codex/codex-commander.d.cts +1 -1
- package/dist/pr-review/codex/codex-commander.d.ts +1 -1
- package/dist/pr-review/codex/codex-commander.js +63 -12
- package/dist/pr-review/codex/codex-commander.js.map +1 -1
- package/dist/pr-review/codex/installation-codex.cjs +42 -6
- package/dist/pr-review/codex/installation-codex.cjs.map +1 -1
- package/dist/pr-review/codex/installation-codex.js +42 -6
- package/dist/pr-review/codex/installation-codex.js.map +1 -1
- package/dist/pr-review/gemini/gemini-commander.cjs +76 -16
- package/dist/pr-review/gemini/gemini-commander.cjs.map +1 -1
- package/dist/pr-review/gemini/gemini-commander.js +76 -16
- package/dist/pr-review/gemini/gemini-commander.js.map +1 -1
- package/dist/pr-review/gemini/installation-gemini.cjs +42 -6
- package/dist/pr-review/gemini/installation-gemini.cjs.map +1 -1
- package/dist/pr-review/gemini/installation-gemini.js +42 -6
- package/dist/pr-review/gemini/installation-gemini.js.map +1 -1
- package/dist/pr-review/review-one-by-one.cjs +699 -106
- package/dist/pr-review/review-one-by-one.cjs.map +1 -1
- package/dist/pr-review/review-one-by-one.js +700 -107
- package/dist/pr-review/review-one-by-one.js.map +1 -1
- package/dist/pr-review/review.cjs +722 -105
- package/dist/pr-review/review.cjs.map +1 -1
- package/dist/pr-review/review.js +722 -105
- package/dist/pr-review/review.js.map +1 -1
- package/package.json +4 -7
- package/src/common/rules/coding-convention.md +393 -0
- package/src/common/rules/coding-convention.pdf +0 -0
- package/src/common/rules/naming-rule.md +347 -0
- package/src/common/rules/naming-rule.pdf +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { exec, execSync } from 'child_process';
|
|
2
|
+
import { exec, execSync, execFileSync } from 'child_process';
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import util, { inspect } from 'util';
|
|
5
5
|
import path from 'path';
|
|
@@ -8,14 +8,52 @@ import { fileURLToPath } from 'url';
|
|
|
8
8
|
|
|
9
9
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
var traceMessages = [];
|
|
11
|
-
var
|
|
12
|
-
var
|
|
13
|
-
|
|
14
|
-
path.
|
|
15
|
-
|
|
11
|
+
var GEMINI_CLI_PACKAGE_NAME = "sales-frontend-gemini-cli";
|
|
12
|
+
var cachedPackageRootPath = "";
|
|
13
|
+
function isGeminiCliPackageRoot(directory) {
|
|
14
|
+
const packageJsonPath = path.join(directory, "package.json");
|
|
15
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
20
|
+
return packageJson.name === GEMINI_CLI_PACKAGE_NAME;
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function resolveGeminiCliPackageRoot(startDirectory = __dirname) {
|
|
26
|
+
if (cachedPackageRootPath) {
|
|
27
|
+
return cachedPackageRootPath;
|
|
28
|
+
}
|
|
29
|
+
let currentDirectory = startDirectory;
|
|
30
|
+
while (true) {
|
|
31
|
+
if (isGeminiCliPackageRoot(currentDirectory)) {
|
|
32
|
+
cachedPackageRootPath = currentDirectory;
|
|
33
|
+
return cachedPackageRootPath;
|
|
34
|
+
}
|
|
35
|
+
const parentDirectory = path.dirname(currentDirectory);
|
|
36
|
+
if (parentDirectory === currentDirectory) {
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
currentDirectory = parentDirectory;
|
|
40
|
+
}
|
|
41
|
+
cachedPackageRootPath = path.resolve(startDirectory, "../..");
|
|
42
|
+
return cachedPackageRootPath;
|
|
43
|
+
}
|
|
44
|
+
function resolvePackageAssetPath(relativeFilePath) {
|
|
45
|
+
return path.resolve(resolveGeminiCliPackageRoot(), relativeFilePath);
|
|
46
|
+
}
|
|
47
|
+
var rulesPath = resolvePackageAssetPath("src/common/rules/review-rules.md");
|
|
48
|
+
var namingRulesPath = resolvePackageAssetPath("src/common/rules/naming-rule.md");
|
|
49
|
+
var codingConventionRulesPath = resolvePackageAssetPath("src/common/rules/coding-convention.md");
|
|
50
|
+
resolvePackageAssetPath("src/common/form/review-form.md");
|
|
51
|
+
var reviewFormOneByOnePath = resolvePackageAssetPath("src/common/form/review-form-one-by-one.md");
|
|
16
52
|
var REPORT_DIR = ".review-report";
|
|
17
53
|
var tempDiffPath = "temp_diff.txt";
|
|
18
54
|
var AIServices = ["gemini", "claude", "codex"];
|
|
55
|
+
var COMMIT_FETCH_LIMIT = 20;
|
|
56
|
+
var COMMIT_SELECTION_WINDOW = 8;
|
|
19
57
|
var ignoreList = [
|
|
20
58
|
"package.json",
|
|
21
59
|
"*.yml",
|
|
@@ -39,6 +77,137 @@ function clearTraceMessages() {
|
|
|
39
77
|
function getTraceMessages() {
|
|
40
78
|
return [...traceMessages];
|
|
41
79
|
}
|
|
80
|
+
var ANSI = {
|
|
81
|
+
bold: "\x1B[1m",
|
|
82
|
+
cyan: "\x1B[36m",
|
|
83
|
+
dim: "\x1B[2m",
|
|
84
|
+
green: "\x1B[32m",
|
|
85
|
+
reset: "\x1B[0m",
|
|
86
|
+
yellow: "\x1B[33m"
|
|
87
|
+
};
|
|
88
|
+
var ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
89
|
+
var COMBINING_MARK_PATTERN = /\p{Mark}/u;
|
|
90
|
+
var GRAPHEME_SEGMENTER = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter("ko", { granularity: "grapheme" }) : null;
|
|
91
|
+
function getGitDiffPathspecs() {
|
|
92
|
+
return {
|
|
93
|
+
excludePatterns: ignoreList.map((item) => `:(exclude)${item}`),
|
|
94
|
+
includePatterns: ["*.ts", "*.tsx", "*.js", "*.jsx"]
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function segmentGraphemes(value) {
|
|
98
|
+
if (!GRAPHEME_SEGMENTER) {
|
|
99
|
+
return [...value];
|
|
100
|
+
}
|
|
101
|
+
return [...GRAPHEME_SEGMENTER.segment(value)].map(({ segment }) => segment);
|
|
102
|
+
}
|
|
103
|
+
function isWideCodePoint(codePoint) {
|
|
104
|
+
return codePoint >= 4352 && (codePoint <= 4447 || codePoint === 9001 || codePoint === 9002 || codePoint >= 11904 && codePoint <= 12871 && codePoint !== 12351 || codePoint >= 12880 && codePoint <= 19903 || codePoint >= 19968 && codePoint <= 42182 || codePoint >= 43360 && codePoint <= 43388 || codePoint >= 44032 && codePoint <= 55203 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 65040 && codePoint <= 65049 || codePoint >= 65072 && codePoint <= 65131 || codePoint >= 65281 && codePoint <= 65376 || codePoint >= 65504 && codePoint <= 65510 || codePoint >= 127488 && codePoint <= 127569 || codePoint >= 131072 && codePoint <= 262141);
|
|
105
|
+
}
|
|
106
|
+
function isEmojiCodePoint(codePoint) {
|
|
107
|
+
return codePoint >= 127462 && codePoint <= 127487 || codePoint >= 127744 && codePoint <= 129791 || codePoint >= 9728 && codePoint <= 10175;
|
|
108
|
+
}
|
|
109
|
+
function getGraphemeWidth(grapheme) {
|
|
110
|
+
let width = 0;
|
|
111
|
+
for (const character of grapheme) {
|
|
112
|
+
const codePoint = character.codePointAt(0);
|
|
113
|
+
if (!codePoint || COMBINING_MARK_PATTERN.test(character) || codePoint === 8205) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (codePoint >= 65024 && codePoint <= 65039 || codePoint >= 917760 && codePoint <= 917999) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (isWideCodePoint(codePoint) || isEmojiCodePoint(codePoint)) {
|
|
120
|
+
width = Math.max(width, 2);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
width = Math.max(width, 1);
|
|
124
|
+
}
|
|
125
|
+
return width;
|
|
126
|
+
}
|
|
127
|
+
function tokenizePlainText(value) {
|
|
128
|
+
return segmentGraphemes(value).map((segment) => ({
|
|
129
|
+
value: segment,
|
|
130
|
+
visibleWidth: getGraphemeWidth(segment)
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
function tokenizeVisibleText(value) {
|
|
134
|
+
const tokens = [];
|
|
135
|
+
let lastIndex = 0;
|
|
136
|
+
for (const match of value.matchAll(ANSI_PATTERN)) {
|
|
137
|
+
const index = match.index ?? 0;
|
|
138
|
+
if (index > lastIndex) {
|
|
139
|
+
tokens.push(...tokenizePlainText(value.slice(lastIndex, index)));
|
|
140
|
+
}
|
|
141
|
+
tokens.push({
|
|
142
|
+
value: match[0],
|
|
143
|
+
visibleWidth: 0
|
|
144
|
+
});
|
|
145
|
+
lastIndex = index + match[0].length;
|
|
146
|
+
}
|
|
147
|
+
if (lastIndex < value.length) {
|
|
148
|
+
tokens.push(...tokenizePlainText(value.slice(lastIndex)));
|
|
149
|
+
}
|
|
150
|
+
return tokens;
|
|
151
|
+
}
|
|
152
|
+
function truncateLineForTerminal(value, maxWidth) {
|
|
153
|
+
if (maxWidth <= 0) {
|
|
154
|
+
return "";
|
|
155
|
+
}
|
|
156
|
+
const tokens = tokenizeVisibleText(value);
|
|
157
|
+
const totalWidth = tokens.reduce((sum, token) => sum + token.visibleWidth, 0);
|
|
158
|
+
if (totalWidth <= maxWidth) {
|
|
159
|
+
return value;
|
|
160
|
+
}
|
|
161
|
+
const ellipsis = "...";
|
|
162
|
+
const ellipsisWidth = 3;
|
|
163
|
+
const targetWidth = Math.max(0, maxWidth - ellipsisWidth);
|
|
164
|
+
let usedWidth = 0;
|
|
165
|
+
let result = "";
|
|
166
|
+
for (const token of tokens) {
|
|
167
|
+
if (token.visibleWidth === 0) {
|
|
168
|
+
result += token.value;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (usedWidth + token.visibleWidth > targetWidth) {
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
result += token.value;
|
|
175
|
+
usedWidth += token.visibleWidth;
|
|
176
|
+
}
|
|
177
|
+
return `${result}${ellipsis}${ANSI.reset}`;
|
|
178
|
+
}
|
|
179
|
+
function fitLinesToTerminal(lines) {
|
|
180
|
+
const maxWidth = Math.max(20, (process.stdout.columns || 120) - 1);
|
|
181
|
+
return lines.map((line) => truncateLineForTerminal(line, maxWidth));
|
|
182
|
+
}
|
|
183
|
+
function runGitCommand(args4, options = {}) {
|
|
184
|
+
const { allowFailure = false, trimOutput = true } = options;
|
|
185
|
+
try {
|
|
186
|
+
const output = execFileSync("git", args4, {
|
|
187
|
+
encoding: "utf8",
|
|
188
|
+
maxBuffer: 1024 * 1024 * 20,
|
|
189
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
190
|
+
});
|
|
191
|
+
return trimOutput ? output.trim() : output;
|
|
192
|
+
} catch (error) {
|
|
193
|
+
helperTrace("git-command:failed", `${args4.join(" ")} | ${getErrorSummary(error)}`);
|
|
194
|
+
if (allowFailure) {
|
|
195
|
+
return "";
|
|
196
|
+
}
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function formatReviewTargetFiles(files, visibleCount = 5) {
|
|
201
|
+
if (files.length === 0) {
|
|
202
|
+
return "(\uC5C6\uC74C)";
|
|
203
|
+
}
|
|
204
|
+
const visibleFiles = files.slice(0, visibleCount);
|
|
205
|
+
const hiddenCount = Math.max(0, files.length - visibleFiles.length);
|
|
206
|
+
if (hiddenCount === 0) {
|
|
207
|
+
return visibleFiles.join(", ");
|
|
208
|
+
}
|
|
209
|
+
return `${visibleFiles.join(", ")} \uC678 ${hiddenCount}\uAC1C`;
|
|
210
|
+
}
|
|
42
211
|
function createTraceLogger(scope, args4 = process.argv.slice(2)) {
|
|
43
212
|
const enabled = isTestMode(args4);
|
|
44
213
|
return (step, detail) => {
|
|
@@ -100,7 +269,7 @@ function serializeError(error) {
|
|
|
100
269
|
}
|
|
101
270
|
if (error && typeof error === "object") {
|
|
102
271
|
const errorLike = error;
|
|
103
|
-
const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs"];
|
|
272
|
+
const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs", "command"];
|
|
104
273
|
extraKeys.forEach((key) => {
|
|
105
274
|
if (errorLike[key] !== void 0) {
|
|
106
275
|
serialized[key] = errorLike[key];
|
|
@@ -206,6 +375,87 @@ ${section.markdown}`).join("\n")}
|
|
|
206
375
|
return "";
|
|
207
376
|
}
|
|
208
377
|
}
|
|
378
|
+
function getExecutionLogSummary(status, title) {
|
|
379
|
+
if (title) {
|
|
380
|
+
return title;
|
|
381
|
+
}
|
|
382
|
+
switch (status) {
|
|
383
|
+
case "success":
|
|
384
|
+
return "\uB9AC\uBDF0 \uC2E4\uD589\uC774 \uC131\uACF5\uC801\uC73C\uB85C \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.";
|
|
385
|
+
case "failed":
|
|
386
|
+
return "\uB9AC\uBDF0 \uC2E4\uD589 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
|
|
387
|
+
case "partial_failure":
|
|
388
|
+
return "\uB9AC\uBDF0 \uC2E4\uD589\uC740 \uC644\uB8CC\uB418\uC5C8\uC9C0\uB9CC \uC77C\uBD80 \uB2E8\uACC4\uC5D0\uC11C \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
|
|
389
|
+
default:
|
|
390
|
+
return "\uB9AC\uBDF0 \uC2E4\uD589\uC774 \uCDE8\uC18C\uB418\uC5C8\uAC70\uB098 \uB9AC\uBDF0 \uB300\uC0C1\uC774 \uC5C6\uC5B4 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.";
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
function formatExecutionDuration(startedAt, finishedAt) {
|
|
394
|
+
const durationMs = Math.max(0, finishedAt.getTime() - startedAt.getTime());
|
|
395
|
+
if (durationMs < 1e3) {
|
|
396
|
+
return `${durationMs}ms`;
|
|
397
|
+
}
|
|
398
|
+
const durationSeconds = durationMs / 1e3;
|
|
399
|
+
if (durationSeconds < 60) {
|
|
400
|
+
return `${durationSeconds.toFixed(1)}s`;
|
|
401
|
+
}
|
|
402
|
+
const minutes = Math.floor(durationSeconds / 60);
|
|
403
|
+
const seconds = Math.round(durationSeconds % 60);
|
|
404
|
+
return `${minutes}m ${seconds}s`;
|
|
405
|
+
}
|
|
406
|
+
function writeExecutionLog(options = {}) {
|
|
407
|
+
try {
|
|
408
|
+
const startedAt = options.startedAt ?? /* @__PURE__ */ new Date();
|
|
409
|
+
const finishedAt = options.finishedAt ?? /* @__PURE__ */ new Date();
|
|
410
|
+
const status = options.status ?? "success";
|
|
411
|
+
helperTrace("execution-log:write:start", options.scope || "unknown");
|
|
412
|
+
createReportDirectory();
|
|
413
|
+
const reportPath = getAvailableFilePath(REPORT_DIR, `${getNowString(finishedAt)}-execution-log`, ".md");
|
|
414
|
+
const traceSnapshot = options.traceMessages ?? getTraceMessages();
|
|
415
|
+
const extraSections = options.extraSections || [];
|
|
416
|
+
const serializedError = options.error ? serializeError(options.error) : null;
|
|
417
|
+
const report = `# Execution Log
|
|
418
|
+
|
|
419
|
+
- \uC2DC\uC791 \uC2DC\uAC01: ${getHumanReadableNowString(startedAt)}
|
|
420
|
+
- \uC885\uB8CC \uC2DC\uAC01: ${getHumanReadableNowString(finishedAt)}
|
|
421
|
+
- \uC2E4\uD589 \uC2DC\uAC04: ${formatExecutionDuration(startedAt, finishedAt)}
|
|
422
|
+
- \uC0C1\uD0DC: \`${status}\`
|
|
423
|
+
- Scope: \`${options.scope || "unknown"}\`
|
|
424
|
+
- \uC791\uC5C5 \uACBD\uB85C: \`${process.cwd()}\`
|
|
425
|
+
- \uC2E4\uD589 \uC778\uC790: \`${JSON.stringify(options.args ?? process.argv.slice(2))}\`
|
|
426
|
+
- \uC2E4\uD589 \uD658\uACBD: \`${process.platform} ${process.arch} / Node ${process.version}\`
|
|
427
|
+
|
|
428
|
+
## Summary
|
|
429
|
+
|
|
430
|
+
${getExecutionLogSummary(status, options.title)}
|
|
431
|
+
${serializedError ? `
|
|
432
|
+
|
|
433
|
+
## Error
|
|
434
|
+
|
|
435
|
+
\`\`\`json
|
|
436
|
+
${JSON.stringify(serializedError, null, 2)}
|
|
437
|
+
\`\`\`` : ""}
|
|
438
|
+
|
|
439
|
+
## Trace
|
|
440
|
+
|
|
441
|
+
\`\`\`json
|
|
442
|
+
${JSON.stringify(traceSnapshot, null, 2)}
|
|
443
|
+
\`\`\`${extraSections.length ? `
|
|
444
|
+
${extraSections.map((section) => `
|
|
445
|
+
## ${section.heading}
|
|
446
|
+
|
|
447
|
+
${section.markdown}`).join("\n")}
|
|
448
|
+
` : "\n"}
|
|
449
|
+
`;
|
|
450
|
+
fs.writeFileSync(reportPath, report);
|
|
451
|
+
helperTrace("execution-log:write:done", reportPath);
|
|
452
|
+
return reportPath;
|
|
453
|
+
} catch (writeError) {
|
|
454
|
+
console.error("\u26A0\uFE0F \uC2E4\uD589 \uB85C\uADF8 \uD30C\uC77C \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
|
|
455
|
+
console.error(writeError);
|
|
456
|
+
return "";
|
|
457
|
+
}
|
|
458
|
+
}
|
|
209
459
|
function exitWithError(message, options = {}) {
|
|
210
460
|
const reportPath = writeErrorReport(options.error || new Error(message), {
|
|
211
461
|
...options,
|
|
@@ -250,13 +500,83 @@ ${JSON.stringify(AIServices, null, 2)}
|
|
|
250
500
|
}
|
|
251
501
|
);
|
|
252
502
|
}
|
|
253
|
-
function
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
503
|
+
function truncateCommitSubject(subject) {
|
|
504
|
+
if (subject.length <= 72) {
|
|
505
|
+
return subject;
|
|
506
|
+
}
|
|
507
|
+
return `${subject.slice(0, 69)}...`;
|
|
508
|
+
}
|
|
509
|
+
function getRecentCommitOptions() {
|
|
510
|
+
const output = runGitCommand(
|
|
511
|
+
["log", `-${COMMIT_FETCH_LIMIT}`, "--date=relative", "--pretty=format:%h%x09%an%x09%ar%x09%s"],
|
|
512
|
+
{ allowFailure: true }
|
|
513
|
+
);
|
|
514
|
+
if (!output) {
|
|
515
|
+
return [];
|
|
516
|
+
}
|
|
517
|
+
return output.split("\n").map((line) => {
|
|
518
|
+
const [hash = "", author = "", relativeDate = "", ...subjectParts] = line.split(" ");
|
|
519
|
+
const subject = subjectParts.join(" ").trim();
|
|
520
|
+
return {
|
|
521
|
+
author,
|
|
522
|
+
description: `${author} | ${relativeDate}`,
|
|
523
|
+
hash,
|
|
524
|
+
label: `${hash} | ${truncateCommitSubject(subject)}`,
|
|
525
|
+
relativeDate,
|
|
526
|
+
subject
|
|
527
|
+
};
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
function buildSelectedCommitSummary(commits) {
|
|
531
|
+
return commits.map((commit) => `- ${commit.hash} | ${commit.subject} | ${commit.author} | ${commit.relativeDate}`).join("\n");
|
|
532
|
+
}
|
|
533
|
+
function getReviewPathspecArgs() {
|
|
534
|
+
const { includePatterns, excludePatterns } = getGitDiffPathspecs();
|
|
535
|
+
return [...includePatterns, ...excludePatterns];
|
|
536
|
+
}
|
|
537
|
+
function buildSelectedCommitDiff(commits) {
|
|
538
|
+
const reviewPathspecArgs = getReviewPathspecArgs();
|
|
539
|
+
const sections = commits.map((commit) => {
|
|
540
|
+
const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", ...reviewPathspecArgs], {
|
|
541
|
+
allowFailure: true,
|
|
542
|
+
trimOutput: false
|
|
543
|
+
}).trim();
|
|
544
|
+
if (!diff) {
|
|
545
|
+
return "";
|
|
546
|
+
}
|
|
547
|
+
return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
|
|
548
|
+
}).filter(Boolean).join("\n\n");
|
|
549
|
+
if (!sections) {
|
|
550
|
+
return "";
|
|
551
|
+
}
|
|
552
|
+
return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", "# \uB9AC\uBDF0 \uB300\uC0C1 diff", sections].join("\n");
|
|
553
|
+
}
|
|
554
|
+
function getSelectedCommitFiles(commits) {
|
|
555
|
+
const reviewPathspecArgs = getReviewPathspecArgs();
|
|
556
|
+
const files = /* @__PURE__ */ new Set();
|
|
557
|
+
commits.forEach((commit) => {
|
|
558
|
+
const output = runGitCommand(["show", "--pretty=format:", "--name-only", commit.hash, "--", ...reviewPathspecArgs], {
|
|
559
|
+
allowFailure: true
|
|
560
|
+
});
|
|
561
|
+
output.split("\n").map((line) => line.trim()).filter(Boolean).forEach((filePath) => files.add(filePath));
|
|
562
|
+
});
|
|
563
|
+
return [...files];
|
|
564
|
+
}
|
|
565
|
+
function buildSelectedFileDiff(commits, filePath) {
|
|
566
|
+
const sections = commits.map((commit) => {
|
|
567
|
+
const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", filePath], {
|
|
568
|
+
allowFailure: true,
|
|
569
|
+
trimOutput: false
|
|
570
|
+
}).trim();
|
|
571
|
+
if (!diff) {
|
|
572
|
+
return "";
|
|
573
|
+
}
|
|
574
|
+
return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
|
|
575
|
+
}).filter(Boolean).join("\n\n");
|
|
576
|
+
if (!sections) {
|
|
577
|
+
return "";
|
|
578
|
+
}
|
|
579
|
+
return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", `# \uD30C\uC77C: ${filePath}`, sections].join("\n\n");
|
|
260
580
|
}
|
|
261
581
|
function openReport(reportPath) {
|
|
262
582
|
const resolvedPath = path.resolve(reportPath);
|
|
@@ -307,59 +627,166 @@ function openReport(reportPath) {
|
|
|
307
627
|
helperTrace("open-report:unsupported-platform", platform);
|
|
308
628
|
console.error(`\u26A0\uFE0F \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD50C\uB7AB\uD3FC\uC785\uB2C8\uB2E4: ${platform}`);
|
|
309
629
|
}
|
|
310
|
-
function
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
helperTrace(
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
630
|
+
function ensureInteractiveSelectionAvailable(scope, message) {
|
|
631
|
+
if (process.stdin.isTTY && process.stdout.isTTY && typeof process.stdin.setRawMode === "function") {
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
helperTrace(`${scope}:tty-missing`);
|
|
635
|
+
exitWithError(message, {
|
|
636
|
+
scope: `helper:${scope}`
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
function renderSelectionBlock(lines, previousLineCount) {
|
|
640
|
+
if (previousLineCount > 0) {
|
|
641
|
+
readline.moveCursor(process.stdout, 0, -previousLineCount);
|
|
642
|
+
readline.clearScreenDown(process.stdout);
|
|
643
|
+
}
|
|
644
|
+
const fittedLines = fitLinesToTerminal(lines);
|
|
645
|
+
process.stdout.write(`${fittedLines.join("\n")}
|
|
646
|
+
`);
|
|
647
|
+
return fittedLines.length;
|
|
648
|
+
}
|
|
649
|
+
function getSelectionWindowRange(optionCount, selectedIndex, windowSize) {
|
|
650
|
+
if (optionCount <= windowSize) {
|
|
651
|
+
return {
|
|
652
|
+
end: optionCount,
|
|
653
|
+
start: 0
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
const halfWindow = Math.floor(windowSize / 2);
|
|
657
|
+
const maxStart = optionCount - windowSize;
|
|
658
|
+
const start = Math.max(0, Math.min(selectedIndex - halfWindow, maxStart));
|
|
659
|
+
return {
|
|
660
|
+
end: Math.min(optionCount, start + windowSize),
|
|
661
|
+
start
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
function buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize) {
|
|
665
|
+
const { start, end } = getSelectionWindowRange(options.length, selectedIndex, windowSize);
|
|
666
|
+
const lines = [
|
|
667
|
+
`${ANSI.bold}${question}${ANSI.reset}`,
|
|
668
|
+
`${ANSI.dim}\u2191\u2193 \uC774\uB3D9 | Space \uC120\uD0DD/\uD574\uC81C | Enter \uC644\uB8CC | Esc \uCDE8\uC18C${ANSI.reset}`,
|
|
669
|
+
`${ANSI.dim}\uC120\uD0DD\uB428: ${toggled.size}\uAC1C / \uC804\uCCB4: ${options.length}\uAC1C${ANSI.reset}`
|
|
670
|
+
];
|
|
671
|
+
for (let index = start; index < end; index += 1) {
|
|
672
|
+
const option = options[index];
|
|
673
|
+
if (!option) {
|
|
674
|
+
continue;
|
|
324
675
|
}
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
676
|
+
const cursor = index === selectedIndex ? `${ANSI.cyan}>${ANSI.reset}` : " ";
|
|
677
|
+
const checked = toggled.has(index) ? `${ANSI.green}\u2611${ANSI.reset}` : "\u2610";
|
|
678
|
+
const description = option.description ? ` ${ANSI.dim}${option.description}${ANSI.reset}` : "";
|
|
679
|
+
lines.push(`${cursor} ${checked} ${option.label}${description}`);
|
|
680
|
+
}
|
|
681
|
+
if (options.length > windowSize) {
|
|
682
|
+
lines.push(`${ANSI.dim}\uD45C\uC2DC \uBC94\uC704: ${start + 1}-${end} / ${options.length}${ANSI.reset}`);
|
|
683
|
+
}
|
|
684
|
+
return lines;
|
|
685
|
+
}
|
|
686
|
+
async function showMultiSelect(question, options, windowSize = COMMIT_SELECTION_WINDOW) {
|
|
687
|
+
ensureInteractiveSelectionAvailable("showMultiSelect", "\u274C \uCEE4\uBC0B \uC120\uD0DD \uBAA8\uB2EC\uC740 TTY \uD658\uACBD\uC5D0\uC11C\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
688
|
+
let selectedIndex = 0;
|
|
689
|
+
let renderedLineCount = 0;
|
|
690
|
+
const toggled = /* @__PURE__ */ new Set();
|
|
691
|
+
const rl = readline.createInterface({
|
|
692
|
+
input: process.stdin,
|
|
693
|
+
output: process.stdout,
|
|
694
|
+
terminal: true
|
|
695
|
+
});
|
|
696
|
+
process.stdout.write("\x1B[?25l");
|
|
697
|
+
const cleanup = () => {
|
|
698
|
+
if (renderedLineCount > 0) {
|
|
699
|
+
readline.moveCursor(process.stdout, 0, -renderedLineCount);
|
|
700
|
+
readline.clearScreenDown(process.stdout);
|
|
701
|
+
renderedLineCount = 0;
|
|
332
702
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
703
|
+
process.stdin.removeListener("data", onData);
|
|
704
|
+
process.stdin.setRawMode(false);
|
|
705
|
+
process.stdin.pause();
|
|
706
|
+
rl.close();
|
|
707
|
+
process.stdout.write("\x1B[?25h");
|
|
708
|
+
};
|
|
709
|
+
const render = () => {
|
|
710
|
+
const lines = buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize);
|
|
711
|
+
renderedLineCount = renderSelectionBlock(lines, renderedLineCount);
|
|
712
|
+
};
|
|
713
|
+
const confirmSelection = (resolve) => {
|
|
714
|
+
const values = [...toggled].sort((left, right) => left - right).map((index) => options[index]?.value).filter((value) => value !== void 0);
|
|
715
|
+
cleanup();
|
|
716
|
+
resolve(values);
|
|
717
|
+
};
|
|
718
|
+
const cancelSelection = (resolve) => {
|
|
719
|
+
cleanup();
|
|
720
|
+
resolve([]);
|
|
721
|
+
};
|
|
722
|
+
let onData = (_data) => {
|
|
723
|
+
};
|
|
724
|
+
render();
|
|
725
|
+
return new Promise((resolve) => {
|
|
726
|
+
onData = (data) => {
|
|
727
|
+
const key = data.toString();
|
|
728
|
+
if (key === "") {
|
|
729
|
+
cleanup();
|
|
730
|
+
process.exit(0);
|
|
346
731
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
732
|
+
if (key === "\x1B") {
|
|
733
|
+
cancelSelection(resolve);
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
if (key === "\x1B[A") {
|
|
737
|
+
selectedIndex = (selectedIndex - 1 + options.length) % options.length;
|
|
738
|
+
render();
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
if (key === "\x1B[B") {
|
|
742
|
+
selectedIndex = (selectedIndex + 1) % options.length;
|
|
743
|
+
render();
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
if (key === " ") {
|
|
747
|
+
if (toggled.has(selectedIndex)) {
|
|
748
|
+
toggled.delete(selectedIndex);
|
|
749
|
+
} else {
|
|
750
|
+
toggled.add(selectedIndex);
|
|
751
|
+
}
|
|
752
|
+
render();
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
if (key === "\r" || key === "\n") {
|
|
756
|
+
confirmSelection(resolve);
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
process.stdin.setRawMode(true);
|
|
760
|
+
process.stdin.resume();
|
|
761
|
+
process.stdin.on("data", onData);
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
async function selectReviewCommits() {
|
|
765
|
+
const commits = getRecentCommitOptions();
|
|
766
|
+
if (commits.length === 0) {
|
|
767
|
+
console.log("\u2139\uFE0F \uB9AC\uBDF0\uD560 \uCD5C\uADFC \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
768
|
+
return [];
|
|
350
769
|
}
|
|
351
|
-
|
|
352
|
-
|
|
770
|
+
return showMultiSelect(
|
|
771
|
+
"\uB9AC\uBDF0\uD560 \uCEE4\uBC0B\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.",
|
|
772
|
+
commits.map((commit) => ({
|
|
773
|
+
description: commit.description,
|
|
774
|
+
label: commit.label,
|
|
775
|
+
value: commit
|
|
776
|
+
})),
|
|
777
|
+
COMMIT_SELECTION_WINDOW
|
|
778
|
+
);
|
|
353
779
|
}
|
|
354
780
|
async function showSelectionAIService() {
|
|
355
781
|
const selectedServiceFromArgs = parseServiceFromArgs();
|
|
356
782
|
if (selectedServiceFromArgs) {
|
|
357
783
|
helperTrace("show-selection:from-args", selectedServiceFromArgs);
|
|
358
784
|
console.log(`
|
|
359
|
-
\u2705
|
|
785
|
+
\u2705 ${ANSI.green}${selectedServiceFromArgs}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (--service)
|
|
360
786
|
`);
|
|
361
787
|
return selectedServiceFromArgs;
|
|
362
788
|
}
|
|
789
|
+
ensureInteractiveSelectionAvailable("showSelectionAIService", "\u274C AI \uC11C\uBE44\uC2A4 \uC120\uD0DD UI\uB294 TTY \uD658\uACBD\uC5D0\uC11C\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
363
790
|
helperTrace("show-selection:interactive:start");
|
|
364
791
|
let selectedIndex = 0;
|
|
365
792
|
const rl = readline.createInterface({
|
|
@@ -377,11 +804,12 @@ async function showSelectionAIService() {
|
|
|
377
804
|
helperTrace("show-selection:interactive:render", AIServices[selectedIndex] || "unknown");
|
|
378
805
|
readline.clearScreenDown(process.stdout);
|
|
379
806
|
process.stdout.write(
|
|
380
|
-
|
|
807
|
+
`\u{1F916} AI \uC11C\uBE44\uC2A4\uB97C \uC120\uD0DD\uD574\uC8FC\uC138\uC694 (${ANSI.yellow}\u2191\u2193 \uBC29\uD5A5\uD0A4${ANSI.reset} \uC774\uB3D9, ${ANSI.yellow}Enter${ANSI.reset} \uC120\uD0DD):
|
|
808
|
+
`
|
|
381
809
|
);
|
|
382
810
|
AIServices.forEach((service, index) => {
|
|
383
811
|
if (index === selectedIndex) {
|
|
384
|
-
process.stdout.write(`
|
|
812
|
+
process.stdout.write(` ${ANSI.cyan}>${ANSI.reset} ${ANSI.cyan}\u25C9${ANSI.reset} ${ANSI.bold}${service}${ANSI.reset}
|
|
385
813
|
`);
|
|
386
814
|
} else {
|
|
387
815
|
process.stdout.write(` \u25EF ${service}
|
|
@@ -411,7 +839,7 @@ async function showSelectionAIService() {
|
|
|
411
839
|
rl.close();
|
|
412
840
|
process.stdout.write("\x1B[?25h");
|
|
413
841
|
console.log(`
|
|
414
|
-
\u2705
|
|
842
|
+
\u2705 ${ANSI.green}${AIServices[selectedIndex]}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
|
|
415
843
|
`);
|
|
416
844
|
const result = AIServices[selectedIndex];
|
|
417
845
|
if (result) {
|
|
@@ -431,6 +859,13 @@ var ALLOWED_REASONING_EFFORTS = ["minimal", "low", "medium", "high"];
|
|
|
431
859
|
function shellQuote(value) {
|
|
432
860
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
433
861
|
}
|
|
862
|
+
function toShellOptionToken(value) {
|
|
863
|
+
const SIMPLE_SHELL_TOKEN_PATTERN = /^[A-Za-z0-9._:/=-]+$/;
|
|
864
|
+
if (SIMPLE_SHELL_TOKEN_PATTERN.test(value)) {
|
|
865
|
+
return value;
|
|
866
|
+
}
|
|
867
|
+
return shellQuote(value);
|
|
868
|
+
}
|
|
434
869
|
function getArgValue(flag) {
|
|
435
870
|
const index = args.indexOf(flag);
|
|
436
871
|
if (index === -1 || !args[index + 1]) {
|
|
@@ -438,6 +873,11 @@ function getArgValue(flag) {
|
|
|
438
873
|
}
|
|
439
874
|
return args[index + 1];
|
|
440
875
|
}
|
|
876
|
+
function printNotice(message) {
|
|
877
|
+
if (args.includes("--test")) {
|
|
878
|
+
console.warn(message);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
441
881
|
function toUnique(values) {
|
|
442
882
|
const seen = /* @__PURE__ */ new Set();
|
|
443
883
|
return values.filter((value) => {
|
|
@@ -461,11 +901,11 @@ function resolveReasoningEffort() {
|
|
|
461
901
|
const normalized = normalizeEffort(customReasoningEffort);
|
|
462
902
|
trace("reasoning:custom", `${customReasoningEffort} -> ${normalized}`);
|
|
463
903
|
if (customReasoningEffort === "minimal") {
|
|
464
|
-
|
|
904
|
+
printNotice("\u26A0\uFE0F Claude\uB294 minimal\uC744 \uC9C1\uC811 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC544 low\uB85C \uB9E4\uD551\uD569\uB2C8\uB2E4.");
|
|
465
905
|
}
|
|
466
906
|
return normalized;
|
|
467
907
|
}
|
|
468
|
-
|
|
908
|
+
printNotice(
|
|
469
909
|
`\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS.join(
|
|
470
910
|
", "
|
|
471
911
|
)}`
|
|
@@ -505,10 +945,10 @@ function getAliasFallbacks(primaryAlias) {
|
|
|
505
945
|
}
|
|
506
946
|
function buildClaudeExecCommand(options) {
|
|
507
947
|
const { tempDiffPath: tempDiffPath2, prompt, systemPromptFiles, effort, model, fallbackModel } = options;
|
|
508
|
-
const modelOption = model ? `--model ${
|
|
509
|
-
const fallbackOption = model && fallbackModel ? `--fallback-model ${
|
|
510
|
-
const effortOption = `--effort ${
|
|
511
|
-
const appendedPromptFiles = systemPromptFiles.map((
|
|
948
|
+
const modelOption = model ? `--model ${toShellOptionToken(model)}` : "";
|
|
949
|
+
const fallbackOption = model && fallbackModel ? `--fallback-model ${toShellOptionToken(fallbackModel)}` : "";
|
|
950
|
+
const effortOption = `--effort ${toShellOptionToken(effort)}`;
|
|
951
|
+
const appendedPromptFiles = systemPromptFiles.map((path3) => `--append-system-prompt-file ${shellQuote(path3)}`).join(" ");
|
|
512
952
|
return `cat ${shellQuote(tempDiffPath2)} | claude ${[
|
|
513
953
|
modelOption,
|
|
514
954
|
fallbackOption,
|
|
@@ -541,13 +981,13 @@ var createClaudeCommand = (tempDiffPath2, reviewFormPath2) => {
|
|
|
541
981
|
trace("model:candidates", modelCandidates.join(", "));
|
|
542
982
|
trace("command:candidates:count", String(modelCandidates.length + 1));
|
|
543
983
|
if (customModel) {
|
|
544
|
-
|
|
984
|
+
printNotice(
|
|
545
985
|
`\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
|
|
546
986
|
" -> "
|
|
547
987
|
)}) \uBC0F \uAE30\uBCF8 \uBAA8\uB378 \uC21C\uC73C\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
|
|
548
988
|
);
|
|
549
989
|
} else {
|
|
550
|
-
|
|
990
|
+
printNotice(
|
|
551
991
|
`\u26A0\uFE0F \uBAA8\uB378\uC774 \uC9C0\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. alias(${aliasFallbacks.join(" -> ")})\uB97C \uC21C\uCC28 \uC2DC\uB3C4\uD558\uACE0 \uB9C8\uC9C0\uB9C9\uC5D0 \uAE30\uBCF8 \uBAA8\uB378\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
|
|
552
992
|
);
|
|
553
993
|
}
|
|
@@ -627,22 +1067,37 @@ function getArgValue2(flag) {
|
|
|
627
1067
|
}
|
|
628
1068
|
return args2[index + 1];
|
|
629
1069
|
}
|
|
1070
|
+
function printNotice2(message) {
|
|
1071
|
+
if (args2.includes("--test")) {
|
|
1072
|
+
console.warn(message);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
function normalizeEffort2(level) {
|
|
1076
|
+
if (level === "minimal") {
|
|
1077
|
+
return "low";
|
|
1078
|
+
}
|
|
1079
|
+
return level;
|
|
1080
|
+
}
|
|
630
1081
|
function resolveReasoningEffort2() {
|
|
631
1082
|
const customReasoningEffort = getArgValue2("--reasoning-effort");
|
|
632
1083
|
if (customReasoningEffort) {
|
|
633
1084
|
if (ALLOWED_REASONING_EFFORTS2.includes(customReasoningEffort)) {
|
|
634
|
-
|
|
635
|
-
|
|
1085
|
+
const normalized = normalizeEffort2(customReasoningEffort);
|
|
1086
|
+
trace3("reasoning:custom", `${customReasoningEffort} -> ${normalized}`);
|
|
1087
|
+
if (customReasoningEffort === "minimal") {
|
|
1088
|
+
printNotice2("\u26A0\uFE0F Codex\uB294 minimal\uC774 web_search \uB3C4\uAD6C\uC640 \uCDA9\uB3CC\uD560 \uC218 \uC788\uC5B4 low\uB85C \uB9E4\uD551\uD569\uB2C8\uB2E4.");
|
|
1089
|
+
}
|
|
1090
|
+
return normalized;
|
|
636
1091
|
}
|
|
637
|
-
|
|
1092
|
+
printNotice2(
|
|
638
1093
|
`\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS2.join(
|
|
639
1094
|
", "
|
|
640
1095
|
)}`
|
|
641
1096
|
);
|
|
642
1097
|
}
|
|
643
1098
|
if (args2.includes("--flash")) {
|
|
644
|
-
trace3("reasoning:flash-default", "
|
|
645
|
-
return "
|
|
1099
|
+
trace3("reasoning:flash-default", "low");
|
|
1100
|
+
return "low";
|
|
646
1101
|
}
|
|
647
1102
|
if (args2.includes("--review")) {
|
|
648
1103
|
trace3("reasoning:review-default", "high");
|
|
@@ -678,14 +1133,14 @@ ${reviewFormLine || "- (\uC5C6\uC74C)"}
|
|
|
678
1133
|
trace3("prompt:prepared", `length=${prompt.length}`);
|
|
679
1134
|
let command = "";
|
|
680
1135
|
if (customModel) {
|
|
681
|
-
|
|
1136
|
+
printNotice2("\u26A0\uFE0F \uC9C0\uC815\uD55C \uBAA8\uB378\uC774 \uC5C6\uB294 \uACBD\uC6B0, \uC5D0\uB7EC\uAC00 \uBC1C\uC0DD\uD558\uB2C8 \uC8FC\uC758\uD558\uC138\uC694.");
|
|
682
1137
|
trace3("model:custom", customModel);
|
|
683
1138
|
command = buildCodexExecCommand(prompt, reasoningEffort, customModel);
|
|
684
1139
|
} else {
|
|
685
1140
|
const preferredModelAlias = "gpt-5";
|
|
686
1141
|
const aliasCommand = buildCodexExecCommand(prompt, reasoningEffort, preferredModelAlias);
|
|
687
1142
|
const fallbackCommand = buildCodexExecCommand(prompt, reasoningEffort);
|
|
688
|
-
|
|
1143
|
+
printNotice2(
|
|
689
1144
|
`\u26A0\uFE0F \uBAA8\uB378\uC774 \uC9C0\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. alias(${preferredModelAlias})\uB97C \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 \uACC4\uC815 \uAE30\uBCF8 \uBAA8\uB378\uB85C \uC790\uB3D9 \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
|
|
690
1145
|
);
|
|
691
1146
|
trace3("model:alias-first", preferredModelAlias);
|
|
@@ -746,6 +1201,11 @@ function getArgValue3(flag) {
|
|
|
746
1201
|
}
|
|
747
1202
|
return args3[index + 1];
|
|
748
1203
|
}
|
|
1204
|
+
function printNotice3(message) {
|
|
1205
|
+
if (args3.includes("--test")) {
|
|
1206
|
+
console.warn(message);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
749
1209
|
function toUnique2(values) {
|
|
750
1210
|
const seen = /* @__PURE__ */ new Set();
|
|
751
1211
|
return values.filter((value) => {
|
|
@@ -763,7 +1223,7 @@ function resolveReasoningEffort3() {
|
|
|
763
1223
|
trace5("reasoning:custom", customReasoningEffort);
|
|
764
1224
|
return customReasoningEffort;
|
|
765
1225
|
}
|
|
766
|
-
|
|
1226
|
+
printNotice3(
|
|
767
1227
|
`\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS3.join(
|
|
768
1228
|
", "
|
|
769
1229
|
)}`
|
|
@@ -825,38 +1285,57 @@ function buildGeminiExecCommand(prompt, model) {
|
|
|
825
1285
|
const modelOption = model ? `--model ${shellQuote3(model)}` : "";
|
|
826
1286
|
return `gemini ${[modelOption, "-p", shellQuote3(prompt)].filter(Boolean).join(" ")}`;
|
|
827
1287
|
}
|
|
1288
|
+
function toGeminiFileReference(filePath) {
|
|
1289
|
+
return `@${filePath}`;
|
|
1290
|
+
}
|
|
1291
|
+
function buildGeminiFileReferenceSection(files) {
|
|
1292
|
+
const existingFiles = files.filter((file) => fs.existsSync(file.path));
|
|
1293
|
+
return {
|
|
1294
|
+
count: existingFiles.length,
|
|
1295
|
+
items: existingFiles.map((file) => `${file.display} ${toGeminiFileReference(file.path)}`)
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
828
1298
|
var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
|
|
829
1299
|
trace5("createGeminiCommand:start", `tempDiffPath=${tempDiffPath2}, reviewFormPath=${reviewFormPath2}`);
|
|
830
1300
|
const customModel = getArgValue3("--model");
|
|
831
1301
|
const reasoningEffort = resolveReasoningEffort3();
|
|
832
1302
|
const primaryAlias = resolvePrimaryAlias2(reasoningEffort);
|
|
833
1303
|
const aliasFallbacks = toUnique2(getAliasFallbacks2(primaryAlias));
|
|
1304
|
+
const resolvedTempDiffPath = path.resolve(tempDiffPath2);
|
|
1305
|
+
const resolvedReviewFormPath = path.resolve(reviewFormPath2);
|
|
834
1306
|
const rules = [
|
|
835
1307
|
{ path: rulesPath, display: "\uB8F0\uC14B" },
|
|
836
1308
|
{ path: namingRulesPath, display: "\uB124\uC774\uBC0D \uADDC\uCE59" },
|
|
837
1309
|
{ path: codingConventionRulesPath, display: "\uCF54\uB529 \uCEE8\uBCA4\uC158" }
|
|
838
1310
|
];
|
|
839
|
-
const
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
const
|
|
843
|
-
trace5("reviewForm:status",
|
|
1311
|
+
const ruleSection = buildGeminiFileReferenceSection(rules);
|
|
1312
|
+
trace5("rules:loaded", `count=${ruleSection.count}`);
|
|
1313
|
+
const reviewFormExists = fs.existsSync(resolvedReviewFormPath);
|
|
1314
|
+
const reviewFormText = reviewFormExists ? `\uB9AC\uBDF0 \uC591\uC2DD ${toGeminiFileReference(resolvedReviewFormPath)}` : "\uB9AC\uBDF0 \uC591\uC2DD (\uC5C6\uC74C)";
|
|
1315
|
+
trace5("reviewForm:status", reviewFormExists ? "exists" : "missing");
|
|
844
1316
|
const reasoningInstruction = getReasoningInstruction(reasoningEffort);
|
|
845
|
-
const
|
|
846
|
-
|
|
847
|
-
|
|
1317
|
+
const ruleText = ruleSection.items.join(" ") || "(\uC5C6\uC74C)";
|
|
1318
|
+
const diffText = `\uB9AC\uBDF0 \uB300\uC0C1 diff ${toGeminiFileReference(resolvedTempDiffPath)}`;
|
|
1319
|
+
const prompt = [
|
|
1320
|
+
"\uC544\uB798 \uD30C\uC77C\uB4E4\uC744 \uC21C\uC11C\uB300\uB85C \uC77D\uACE0 \uCF54\uB4DC \uB9AC\uBDF0\uB97C \uC9C4\uD589\uD574\uC918.",
|
|
1321
|
+
`\uADDC\uCE59 \uD30C\uC77C: ${ruleText}`,
|
|
1322
|
+
`\uB9AC\uBDF0 \uC591\uC2DD \uD30C\uC77C: ${reviewFormText}`,
|
|
1323
|
+
`\uB9AC\uBDF0 \uB300\uC0C1 diff \uD30C\uC77C: ${diffText}`,
|
|
1324
|
+
"\uBC18\uB4DC\uC2DC \uB9AC\uBDF0 \uC591\uC2DD\uC5D0 \uB9DE\uCDB0 \uC791\uC131\uD574\uC918.",
|
|
1325
|
+
`\uCD94\uB860 \uAC15\uB3C4 \uC9C0\uCE68: ${reasoningInstruction}`
|
|
1326
|
+
].join(" ");
|
|
848
1327
|
trace5("prompt:prepared", `length=${prompt.length}`);
|
|
849
1328
|
const modelCandidates = toUnique2(customModel ? [customModel, ...aliasFallbacks] : aliasFallbacks);
|
|
850
1329
|
trace5("model:candidates", modelCandidates.join(", "));
|
|
851
1330
|
trace5("command:candidates:count", String(modelCandidates.length + 1));
|
|
852
1331
|
if (customModel) {
|
|
853
|
-
|
|
1332
|
+
printNotice3(
|
|
854
1333
|
`\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
|
|
855
1334
|
" -> "
|
|
856
1335
|
)}) \uBC0F \uAE30\uBCF8 \uBAA8\uB378 \uC21C\uC73C\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
|
|
857
1336
|
);
|
|
858
1337
|
} else {
|
|
859
|
-
|
|
1338
|
+
printNotice3(
|
|
860
1339
|
`\u26A0\uFE0F \uBAA8\uB378\uC774 \uC9C0\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. alias(${aliasFallbacks.join(" -> ")})\uB97C \uC21C\uCC28 \uC2DC\uB3C4\uD558\uACE0 \uB9C8\uC9C0\uB9C9\uC5D0 \uAE30\uBCF8 \uBAA8\uB378\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
|
|
861
1340
|
);
|
|
862
1341
|
}
|
|
@@ -908,6 +1387,7 @@ function checkGeminiCliInstalled() {
|
|
|
908
1387
|
var execAsync = util.promisify(exec);
|
|
909
1388
|
async function main() {
|
|
910
1389
|
const args4 = process.argv.slice(2);
|
|
1390
|
+
const startedAt = /* @__PURE__ */ new Date();
|
|
911
1391
|
clearTraceMessages();
|
|
912
1392
|
const isTest = isTestMode(args4);
|
|
913
1393
|
const trace7 = createTraceLogger("review-one-by-one", args4);
|
|
@@ -915,6 +1395,14 @@ async function main() {
|
|
|
915
1395
|
let service = "";
|
|
916
1396
|
let savedDiffPath = "";
|
|
917
1397
|
let savedReportPath = "";
|
|
1398
|
+
let selectedCommitSummary = "";
|
|
1399
|
+
let fileList = [];
|
|
1400
|
+
let executionLogPath = "";
|
|
1401
|
+
let executionStatus = "cancelled";
|
|
1402
|
+
let executionTitle = "\uB9AC\uBDF0 \uC2E4\uD589\uC774 \uCDE8\uC18C\uB418\uC5C8\uAC70\uB098 \uB9AC\uBDF0 \uB300\uC0C1\uC774 \uC5C6\uC5B4 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.";
|
|
1403
|
+
let executionError = null;
|
|
1404
|
+
let exitCode = 0;
|
|
1405
|
+
const fileExecutionLogs = [];
|
|
918
1406
|
try {
|
|
919
1407
|
trace7("service-selection:start");
|
|
920
1408
|
service = await showSelectionAIService();
|
|
@@ -938,29 +1426,37 @@ async function main() {
|
|
|
938
1426
|
}
|
|
939
1427
|
trace7("review-flow:start");
|
|
940
1428
|
console.log("\u{1F680} AI Code Review\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...");
|
|
941
|
-
trace7("
|
|
942
|
-
|
|
943
|
-
trace7("
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
1429
|
+
trace7("commit-selection:start");
|
|
1430
|
+
const selectedCommits = await selectReviewCommits();
|
|
1431
|
+
trace7("commit-selection:done", `count=${selectedCommits.length}`);
|
|
1432
|
+
if (selectedCommits.length === 0) {
|
|
1433
|
+
trace7("commit-selection:empty");
|
|
1434
|
+
executionTitle = "\uC120\uD0DD\uB41C \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.";
|
|
1435
|
+
console.log("\u2139\uFE0F \uC120\uD0DD\uB41C \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1436
|
+
deleteTempDiff();
|
|
1437
|
+
return;
|
|
1438
|
+
}
|
|
1439
|
+
selectedCommitSummary = buildSelectedCommitSummary(selectedCommits);
|
|
1440
|
+
trace7("commit-summary:prepared", selectedCommitSummary);
|
|
1441
|
+
console.log("\u23F3 \uC120\uD0DD\uD55C \uCEE4\uBC0B\uC758 \uD30C\uC77C \uBAA9\uB85D\uACFC diff\uB97C \uC815\uB9AC\uD558\uB294 \uC911\uC785\uB2C8\uB2E4...");
|
|
1442
|
+
trace7("files-command:run");
|
|
1443
|
+
fileList = getSelectedCommitFiles(selectedCommits);
|
|
952
1444
|
trace7("files-command:done", `fileCount=${fileList.length}`);
|
|
1445
|
+
console.log(`\u{1F4C2} \uB9AC\uBDF0 \uB300\uC0C1 \uD30C\uC77C(${fileList.length}\uAC1C): ${formatReviewTargetFiles(fileList)}`);
|
|
953
1446
|
if (fileList.length === 0) {
|
|
954
1447
|
trace7("empty-file-list:exit");
|
|
955
|
-
|
|
1448
|
+
executionTitle = "\uC120\uD0DD\uD55C \uCEE4\uBC0B\uC5D0\uC11C \uB9AC\uBDF0 \uB300\uC0C1 \uD30C\uC77C \uBCC0\uACBD\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.";
|
|
1449
|
+
console.log("\u2139\uFE0F \uC120\uD0DD\uD55C \uCEE4\uBC0B\uC5D0\uC11C \uB9AC\uBDF0 \uB300\uC0C1 \uD30C\uC77C \uBCC0\uACBD\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
|
|
956
1450
|
deleteTempDiff();
|
|
957
|
-
|
|
1451
|
+
return;
|
|
958
1452
|
}
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1453
|
+
trace7("report-dir:create:start");
|
|
1454
|
+
createReportDirectory();
|
|
1455
|
+
trace7("report-dir:create:done");
|
|
1456
|
+
trace7("full-diff:build:start");
|
|
1457
|
+
const fullDiff = buildSelectedCommitDiff(selectedCommits);
|
|
962
1458
|
const nowStr = getNowString();
|
|
963
|
-
trace7("full-diff:done", `length=${fullDiff.length}`);
|
|
1459
|
+
trace7("full-diff:build:done", `length=${fullDiff.length}`);
|
|
964
1460
|
trace7("timestamp:created", nowStr);
|
|
965
1461
|
trace7("temp-diff:write:start", tempDiffPath);
|
|
966
1462
|
fs.writeFileSync(tempDiffPath, fullDiff);
|
|
@@ -971,15 +1467,28 @@ async function main() {
|
|
|
971
1467
|
trace7("saved-diff:copy:done", savedDiffPath);
|
|
972
1468
|
savedReportPath = getNextFilePath(REPORT_DIR, nowStr, ".md");
|
|
973
1469
|
trace7("saved-report:path", savedReportPath);
|
|
974
|
-
const promises = fileList.map(async (file) => {
|
|
1470
|
+
const promises = fileList.map(async (file, index) => {
|
|
975
1471
|
const tempOneFileDiffPath = `temp_diff_${file.replace(/\//g, "_")}.txt`;
|
|
976
1472
|
let command = "";
|
|
977
1473
|
try {
|
|
978
1474
|
trace7("file-review:start", file);
|
|
979
|
-
console.log(`\u{1F50D}
|
|
980
|
-
trace7("file-diff:
|
|
981
|
-
const fileDiff =
|
|
982
|
-
trace7("file-diff:done", `${file} | length=${fileDiff.length}`);
|
|
1475
|
+
console.log(`\u{1F50D} [${index + 1}/${fileList.length}] \uB9AC\uBDF0 \uC900\uBE44: ${file}`);
|
|
1476
|
+
trace7("file-diff:build:start", file);
|
|
1477
|
+
const fileDiff = buildSelectedFileDiff(selectedCommits, file);
|
|
1478
|
+
trace7("file-diff:build:done", `${file} | length=${fileDiff.length}`);
|
|
1479
|
+
if (!fileDiff.trim()) {
|
|
1480
|
+
trace7("file-diff:empty", file);
|
|
1481
|
+
fileExecutionLogs.push({
|
|
1482
|
+
command: null,
|
|
1483
|
+
errorSummary: null,
|
|
1484
|
+
file,
|
|
1485
|
+
index,
|
|
1486
|
+
resultLength: 0,
|
|
1487
|
+
status: "skipped",
|
|
1488
|
+
tempOneFileDiffPath
|
|
1489
|
+
});
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
983
1492
|
trace7("file-temp-diff:write:start", tempOneFileDiffPath);
|
|
984
1493
|
fs.writeFileSync(tempOneFileDiffPath, fileDiff);
|
|
985
1494
|
trace7("file-temp-diff:write:done", tempOneFileDiffPath);
|
|
@@ -996,6 +1505,7 @@ async function main() {
|
|
|
996
1505
|
break;
|
|
997
1506
|
}
|
|
998
1507
|
trace7("file-command:create:done", `${file} | commandLength=${command.length}`);
|
|
1508
|
+
console.log(`\u23F3 [${index + 1}/${fileList.length}] \uB9AC\uBDF0 \uC9C4\uD589: ${file}`);
|
|
999
1509
|
trace7("file-command:exec:start", file);
|
|
1000
1510
|
const { stdout } = await execAsync(command, { maxBuffer: 1024 * 1024 * 20 });
|
|
1001
1511
|
const result = stdout.toString();
|
|
@@ -1007,7 +1517,6 @@ async function main() {
|
|
|
1007
1517
|
${result}
|
|
1008
1518
|
|
|
1009
1519
|
`;
|
|
1010
|
-
console.log(tempReport);
|
|
1011
1520
|
trace7("file-report:append:start", file);
|
|
1012
1521
|
fs.appendFileSync(savedReportPath, tempReport);
|
|
1013
1522
|
trace7("file-report:append:done", file);
|
|
@@ -1020,9 +1529,27 @@ ${result}
|
|
|
1020
1529
|
${command}`);
|
|
1021
1530
|
trace7("file-test-command:append:done", file);
|
|
1022
1531
|
}
|
|
1532
|
+
fileExecutionLogs.push({
|
|
1533
|
+
command,
|
|
1534
|
+
errorSummary: null,
|
|
1535
|
+
file,
|
|
1536
|
+
index,
|
|
1537
|
+
resultLength: result.length,
|
|
1538
|
+
status: "success",
|
|
1539
|
+
tempOneFileDiffPath
|
|
1540
|
+
});
|
|
1023
1541
|
trace7("file-review:end", file);
|
|
1024
1542
|
} catch (err) {
|
|
1025
1543
|
trace7("file-review:catch", `${file} | ${getErrorSummary(err)}`);
|
|
1544
|
+
fileExecutionLogs.push({
|
|
1545
|
+
command: command || null,
|
|
1546
|
+
errorSummary: getErrorSummary(err),
|
|
1547
|
+
file,
|
|
1548
|
+
index,
|
|
1549
|
+
resultLength: 0,
|
|
1550
|
+
status: "failed",
|
|
1551
|
+
tempOneFileDiffPath
|
|
1552
|
+
});
|
|
1026
1553
|
const errorLogPath = writeErrorReport(err, {
|
|
1027
1554
|
scope: "review-one-by-one:file",
|
|
1028
1555
|
args: args4,
|
|
@@ -1033,6 +1560,7 @@ ${command}`);
|
|
|
1033
1560
|
${JSON.stringify(
|
|
1034
1561
|
{
|
|
1035
1562
|
service,
|
|
1563
|
+
selectedCommitSummary: selectedCommitSummary || null,
|
|
1036
1564
|
file,
|
|
1037
1565
|
command: command || null,
|
|
1038
1566
|
tempOneFileDiffPath,
|
|
@@ -1073,6 +1601,15 @@ ${getErrorSummary(err)}
|
|
|
1073
1601
|
trace7("parallel-review:await-start");
|
|
1074
1602
|
await Promise.all(promises);
|
|
1075
1603
|
trace7("parallel-review:await-done");
|
|
1604
|
+
const failedFileCount = fileExecutionLogs.filter((log) => log.status === "failed").length;
|
|
1605
|
+
const successfulFileCount = fileExecutionLogs.filter((log) => log.status === "success").length;
|
|
1606
|
+
if (failedFileCount > 0) {
|
|
1607
|
+
executionStatus = "partial_failure";
|
|
1608
|
+
executionTitle = `\uD30C\uC77C\uBCC4 \uB9AC\uBDF0 \uC911 \uC77C\uBD80\uAC00 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. (\uC131\uACF5 ${successfulFileCount}\uAC74 / \uC2E4\uD328 ${failedFileCount}\uAC74)`;
|
|
1609
|
+
} else {
|
|
1610
|
+
executionStatus = "success";
|
|
1611
|
+
executionTitle = "\uD30C\uC77C\uBCC4 \uB9AC\uBDF0\uAC00 \uC131\uACF5\uC801\uC73C\uB85C \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.";
|
|
1612
|
+
}
|
|
1076
1613
|
console.log(`
|
|
1077
1614
|
\u2705 \uB9AC\uBDF0\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.`);
|
|
1078
1615
|
console.log(`\u{1F4C4} \uB9AC\uD3EC\uD2B8 \uC800\uC7A5 \uC704\uCE58: ${savedReportPath}`);
|
|
@@ -1087,6 +1624,10 @@ ${getErrorSummary(err)}
|
|
|
1087
1624
|
} catch (error) {
|
|
1088
1625
|
trace7("review-flow:catch", getErrorSummary(error));
|
|
1089
1626
|
let errorReportPath = "";
|
|
1627
|
+
executionStatus = "failed";
|
|
1628
|
+
executionTitle = "\uD30C\uC77C\uBCC4 \uB9AC\uBDF0 \uC2E4\uD589 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
|
|
1629
|
+
executionError = error;
|
|
1630
|
+
exitCode = 1;
|
|
1090
1631
|
trace7("cleanup-temp-diff:start(catch)");
|
|
1091
1632
|
try {
|
|
1092
1633
|
deleteTempDiff();
|
|
@@ -1107,6 +1648,7 @@ ${getErrorSummary(err)}
|
|
|
1107
1648
|
${JSON.stringify(
|
|
1108
1649
|
{
|
|
1109
1650
|
service: service || null,
|
|
1651
|
+
selectedCommitSummary: selectedCommitSummary || null,
|
|
1110
1652
|
tempDiffPath,
|
|
1111
1653
|
savedDiffPath: savedDiffPath || null,
|
|
1112
1654
|
savedReportPath: savedReportPath || null
|
|
@@ -1123,7 +1665,58 @@ ${JSON.stringify(
|
|
|
1123
1665
|
if (errorReportPath) {
|
|
1124
1666
|
console.error(`\u{1F4C4} \uC5D0\uB7EC \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${errorReportPath}`);
|
|
1125
1667
|
}
|
|
1126
|
-
|
|
1668
|
+
} finally {
|
|
1669
|
+
executionLogPath = writeExecutionLog({
|
|
1670
|
+
scope: "review-one-by-one",
|
|
1671
|
+
status: executionStatus,
|
|
1672
|
+
title: executionTitle,
|
|
1673
|
+
args: args4,
|
|
1674
|
+
startedAt,
|
|
1675
|
+
error: executionError,
|
|
1676
|
+
extraSections: [
|
|
1677
|
+
{
|
|
1678
|
+
heading: "Execution Context",
|
|
1679
|
+
markdown: `\`\`\`json
|
|
1680
|
+
${JSON.stringify(
|
|
1681
|
+
{
|
|
1682
|
+
service: service || null,
|
|
1683
|
+
selectedCommitSummary: selectedCommitSummary || null,
|
|
1684
|
+
fileList,
|
|
1685
|
+
tempDiffPath,
|
|
1686
|
+
savedDiffPath: savedDiffPath || null,
|
|
1687
|
+
savedReportPath: savedReportPath || null
|
|
1688
|
+
},
|
|
1689
|
+
null,
|
|
1690
|
+
2
|
|
1691
|
+
)}
|
|
1692
|
+
\`\`\``
|
|
1693
|
+
},
|
|
1694
|
+
{
|
|
1695
|
+
heading: "Generated Commands",
|
|
1696
|
+
markdown: `\`\`\`json
|
|
1697
|
+
${JSON.stringify(
|
|
1698
|
+
fileExecutionLogs.slice().sort((left, right) => left.index - right.index).map((log) => ({
|
|
1699
|
+
file: log.file,
|
|
1700
|
+
status: log.status,
|
|
1701
|
+
resultLength: log.resultLength,
|
|
1702
|
+
errorSummary: log.errorSummary,
|
|
1703
|
+
tempOneFileDiffPath: log.tempOneFileDiffPath,
|
|
1704
|
+
command: log.command
|
|
1705
|
+
})),
|
|
1706
|
+
null,
|
|
1707
|
+
2
|
|
1708
|
+
)}
|
|
1709
|
+
\`\`\``
|
|
1710
|
+
}
|
|
1711
|
+
]
|
|
1712
|
+
});
|
|
1713
|
+
if (executionLogPath) {
|
|
1714
|
+
const writeLog = executionStatus === "failed" ? console.error : console.log;
|
|
1715
|
+
writeLog(`\u{1F4DD} \uC2E4\uD589 \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${executionLogPath}`);
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
if (exitCode !== 0) {
|
|
1719
|
+
process.exit(exitCode);
|
|
1127
1720
|
}
|
|
1128
1721
|
}
|
|
1129
1722
|
main();
|