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
package/dist/pr-review/review.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { execSync } from 'child_process';
|
|
3
2
|
import fs from 'fs';
|
|
3
|
+
import { execSync, execFileSync } from 'child_process';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import readline from 'readline';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
@@ -8,14 +8,52 @@ import { inspect } from 'util';
|
|
|
8
8
|
|
|
9
9
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
var traceMessages = [];
|
|
11
|
-
var
|
|
12
|
-
var
|
|
13
|
-
|
|
14
|
-
|
|
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
|
+
var reviewFormPath = resolvePackageAssetPath("src/common/form/review-form.md");
|
|
51
|
+
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",
|
|
@@ -33,12 +71,224 @@ var ignoreList = [
|
|
|
33
71
|
function isTestMode(args4 = process.argv.slice(2)) {
|
|
34
72
|
return args4.includes("--test");
|
|
35
73
|
}
|
|
74
|
+
function shouldStreamAIOutput(args4 = process.argv.slice(2)) {
|
|
75
|
+
return args4.includes("--stream-output");
|
|
76
|
+
}
|
|
36
77
|
function clearTraceMessages() {
|
|
37
78
|
traceMessages.length = 0;
|
|
38
79
|
}
|
|
39
80
|
function getTraceMessages() {
|
|
40
81
|
return [...traceMessages];
|
|
41
82
|
}
|
|
83
|
+
var ANSI = {
|
|
84
|
+
bold: "\x1B[1m",
|
|
85
|
+
cyan: "\x1B[36m",
|
|
86
|
+
dim: "\x1B[2m",
|
|
87
|
+
green: "\x1B[32m",
|
|
88
|
+
reset: "\x1B[0m",
|
|
89
|
+
yellow: "\x1B[33m"
|
|
90
|
+
};
|
|
91
|
+
var ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
92
|
+
var COMBINING_MARK_PATTERN = /\p{Mark}/u;
|
|
93
|
+
var GRAPHEME_SEGMENTER = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter("ko", { granularity: "grapheme" }) : null;
|
|
94
|
+
function getGitDiffPathspecs() {
|
|
95
|
+
return {
|
|
96
|
+
excludePatterns: ignoreList.map((item) => `:(exclude)${item}`),
|
|
97
|
+
includePatterns: ["*.ts", "*.tsx", "*.js", "*.jsx"]
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function segmentGraphemes(value) {
|
|
101
|
+
if (!GRAPHEME_SEGMENTER) {
|
|
102
|
+
return [...value];
|
|
103
|
+
}
|
|
104
|
+
return [...GRAPHEME_SEGMENTER.segment(value)].map(({ segment }) => segment);
|
|
105
|
+
}
|
|
106
|
+
function isWideCodePoint(codePoint) {
|
|
107
|
+
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);
|
|
108
|
+
}
|
|
109
|
+
function isEmojiCodePoint(codePoint) {
|
|
110
|
+
return codePoint >= 127462 && codePoint <= 127487 || codePoint >= 127744 && codePoint <= 129791 || codePoint >= 9728 && codePoint <= 10175;
|
|
111
|
+
}
|
|
112
|
+
function getGraphemeWidth(grapheme) {
|
|
113
|
+
let width = 0;
|
|
114
|
+
for (const character of grapheme) {
|
|
115
|
+
const codePoint = character.codePointAt(0);
|
|
116
|
+
if (!codePoint || COMBINING_MARK_PATTERN.test(character) || codePoint === 8205) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (codePoint >= 65024 && codePoint <= 65039 || codePoint >= 917760 && codePoint <= 917999) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (isWideCodePoint(codePoint) || isEmojiCodePoint(codePoint)) {
|
|
123
|
+
width = Math.max(width, 2);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
width = Math.max(width, 1);
|
|
127
|
+
}
|
|
128
|
+
return width;
|
|
129
|
+
}
|
|
130
|
+
function tokenizePlainText(value) {
|
|
131
|
+
return segmentGraphemes(value).map((segment) => ({
|
|
132
|
+
value: segment,
|
|
133
|
+
visibleWidth: getGraphemeWidth(segment)
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
function tokenizeVisibleText(value) {
|
|
137
|
+
const tokens = [];
|
|
138
|
+
let lastIndex = 0;
|
|
139
|
+
for (const match of value.matchAll(ANSI_PATTERN)) {
|
|
140
|
+
const index = match.index ?? 0;
|
|
141
|
+
if (index > lastIndex) {
|
|
142
|
+
tokens.push(...tokenizePlainText(value.slice(lastIndex, index)));
|
|
143
|
+
}
|
|
144
|
+
tokens.push({
|
|
145
|
+
value: match[0],
|
|
146
|
+
visibleWidth: 0
|
|
147
|
+
});
|
|
148
|
+
lastIndex = index + match[0].length;
|
|
149
|
+
}
|
|
150
|
+
if (lastIndex < value.length) {
|
|
151
|
+
tokens.push(...tokenizePlainText(value.slice(lastIndex)));
|
|
152
|
+
}
|
|
153
|
+
return tokens;
|
|
154
|
+
}
|
|
155
|
+
function truncateLineForTerminal(value, maxWidth) {
|
|
156
|
+
if (maxWidth <= 0) {
|
|
157
|
+
return "";
|
|
158
|
+
}
|
|
159
|
+
const tokens = tokenizeVisibleText(value);
|
|
160
|
+
const totalWidth = tokens.reduce((sum, token) => sum + token.visibleWidth, 0);
|
|
161
|
+
if (totalWidth <= maxWidth) {
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
const ellipsis = "...";
|
|
165
|
+
const ellipsisWidth = 3;
|
|
166
|
+
const targetWidth = Math.max(0, maxWidth - ellipsisWidth);
|
|
167
|
+
let usedWidth = 0;
|
|
168
|
+
let result = "";
|
|
169
|
+
for (const token of tokens) {
|
|
170
|
+
if (token.visibleWidth === 0) {
|
|
171
|
+
result += token.value;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (usedWidth + token.visibleWidth > targetWidth) {
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
result += token.value;
|
|
178
|
+
usedWidth += token.visibleWidth;
|
|
179
|
+
}
|
|
180
|
+
return `${result}${ellipsis}${ANSI.reset}`;
|
|
181
|
+
}
|
|
182
|
+
function fitLinesToTerminal(lines) {
|
|
183
|
+
const maxWidth = Math.max(20, (process.stdout.columns || 120) - 1);
|
|
184
|
+
return lines.map((line) => truncateLineForTerminal(line, maxWidth));
|
|
185
|
+
}
|
|
186
|
+
function runGitCommand(args4, options = {}) {
|
|
187
|
+
const { allowFailure = false, trimOutput = true } = options;
|
|
188
|
+
try {
|
|
189
|
+
const output = execFileSync("git", args4, {
|
|
190
|
+
encoding: "utf8",
|
|
191
|
+
maxBuffer: 1024 * 1024 * 20,
|
|
192
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
193
|
+
});
|
|
194
|
+
return trimOutput ? output.trim() : output;
|
|
195
|
+
} catch (error) {
|
|
196
|
+
helperTrace("git-command:failed", `${args4.join(" ")} | ${getErrorSummary(error)}`);
|
|
197
|
+
if (allowFailure) {
|
|
198
|
+
return "";
|
|
199
|
+
}
|
|
200
|
+
throw error;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async function executeShellCommandWithProgress(command, options = {}) {
|
|
204
|
+
const { progressIntervalMs = 1e4, progressMessage = "\u23F3 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uB294 \uC911\uC785\uB2C8\uB2E4...", streamOutput = false } = options;
|
|
205
|
+
const { spawn } = await import('child_process');
|
|
206
|
+
return new Promise((resolve, reject) => {
|
|
207
|
+
let stdout = "";
|
|
208
|
+
let stderr = "";
|
|
209
|
+
const startedAt = Date.now();
|
|
210
|
+
console.log(progressMessage);
|
|
211
|
+
const child = spawn("/bin/zsh", ["-lc", command], {
|
|
212
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
213
|
+
});
|
|
214
|
+
const progressTimer = setInterval(() => {
|
|
215
|
+
const elapsedSeconds = Math.max(1, Math.floor((Date.now() - startedAt) / 1e3));
|
|
216
|
+
console.log(`${progressMessage} (${elapsedSeconds}s \uACBD\uACFC)`);
|
|
217
|
+
}, progressIntervalMs);
|
|
218
|
+
child.stdout.on("data", (chunk) => {
|
|
219
|
+
const text = chunk.toString();
|
|
220
|
+
stdout += text;
|
|
221
|
+
if (streamOutput) {
|
|
222
|
+
process.stdout.write(text);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
child.stderr.on("data", (chunk) => {
|
|
226
|
+
const text = chunk.toString();
|
|
227
|
+
stderr += text;
|
|
228
|
+
if (streamOutput) {
|
|
229
|
+
process.stderr.write(text);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
child.on("error", (error) => {
|
|
233
|
+
clearInterval(progressTimer);
|
|
234
|
+
reject(error);
|
|
235
|
+
});
|
|
236
|
+
child.on("close", (code, signal) => {
|
|
237
|
+
clearInterval(progressTimer);
|
|
238
|
+
if (code === 0) {
|
|
239
|
+
resolve({
|
|
240
|
+
stderr,
|
|
241
|
+
stdout
|
|
242
|
+
});
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const exitSummary = signal ? `signal=${signal}` : `code=${String(code ?? "unknown")}`;
|
|
246
|
+
const failureDetails = {
|
|
247
|
+
code,
|
|
248
|
+
command,
|
|
249
|
+
signal,
|
|
250
|
+
stderr,
|
|
251
|
+
stdout
|
|
252
|
+
};
|
|
253
|
+
reject(createShellCommandExecutionError(failureDetails, exitSummary));
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
function getShellCommandFailurePreview(failureDetails) {
|
|
258
|
+
const stderrText = failureDetails.stderr.trim();
|
|
259
|
+
const stdoutText = failureDetails.stdout.trim();
|
|
260
|
+
const combinedOutput = stderrText || stdoutText;
|
|
261
|
+
if (!combinedOutput) {
|
|
262
|
+
return "";
|
|
263
|
+
}
|
|
264
|
+
const MAX_PREVIEW_LENGTH = 4e3;
|
|
265
|
+
if (combinedOutput.length <= MAX_PREVIEW_LENGTH) {
|
|
266
|
+
return combinedOutput;
|
|
267
|
+
}
|
|
268
|
+
return combinedOutput.slice(-4e3);
|
|
269
|
+
}
|
|
270
|
+
function createShellCommandExecutionError(failureDetails, exitSummary) {
|
|
271
|
+
const failurePreview = getShellCommandFailurePreview(failureDetails);
|
|
272
|
+
const error = new Error(`\uC258 \uBA85\uB839 \uC2E4\uD589 \uC2E4\uD328 (${exitSummary})${failurePreview ? `
|
|
273
|
+
${failurePreview}` : ""}`);
|
|
274
|
+
error.code = failureDetails.code;
|
|
275
|
+
error.signal = failureDetails.signal;
|
|
276
|
+
error.stdout = failureDetails.stdout;
|
|
277
|
+
error.stderr = failureDetails.stderr;
|
|
278
|
+
error.command = failureDetails.command;
|
|
279
|
+
return error;
|
|
280
|
+
}
|
|
281
|
+
function formatReviewTargetFiles(files, visibleCount = 5) {
|
|
282
|
+
if (files.length === 0) {
|
|
283
|
+
return "(\uC5C6\uC74C)";
|
|
284
|
+
}
|
|
285
|
+
const visibleFiles = files.slice(0, visibleCount);
|
|
286
|
+
const hiddenCount = Math.max(0, files.length - visibleFiles.length);
|
|
287
|
+
if (hiddenCount === 0) {
|
|
288
|
+
return visibleFiles.join(", ");
|
|
289
|
+
}
|
|
290
|
+
return `${visibleFiles.join(", ")} \uC678 ${hiddenCount}\uAC1C`;
|
|
291
|
+
}
|
|
42
292
|
function createTraceLogger(scope, args4 = process.argv.slice(2)) {
|
|
43
293
|
const enabled = isTestMode(args4);
|
|
44
294
|
return (step, detail) => {
|
|
@@ -100,7 +350,7 @@ function serializeError(error) {
|
|
|
100
350
|
}
|
|
101
351
|
if (error && typeof error === "object") {
|
|
102
352
|
const errorLike = error;
|
|
103
|
-
const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs"];
|
|
353
|
+
const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs", "command"];
|
|
104
354
|
extraKeys.forEach((key) => {
|
|
105
355
|
if (errorLike[key] !== void 0) {
|
|
106
356
|
serialized[key] = errorLike[key];
|
|
@@ -206,6 +456,87 @@ ${section.markdown}`).join("\n")}
|
|
|
206
456
|
return "";
|
|
207
457
|
}
|
|
208
458
|
}
|
|
459
|
+
function getExecutionLogSummary(status, title) {
|
|
460
|
+
if (title) {
|
|
461
|
+
return title;
|
|
462
|
+
}
|
|
463
|
+
switch (status) {
|
|
464
|
+
case "success":
|
|
465
|
+
return "\uB9AC\uBDF0 \uC2E4\uD589\uC774 \uC131\uACF5\uC801\uC73C\uB85C \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.";
|
|
466
|
+
case "failed":
|
|
467
|
+
return "\uB9AC\uBDF0 \uC2E4\uD589 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
|
|
468
|
+
case "partial_failure":
|
|
469
|
+
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.";
|
|
470
|
+
default:
|
|
471
|
+
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.";
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
function formatExecutionDuration(startedAt, finishedAt) {
|
|
475
|
+
const durationMs = Math.max(0, finishedAt.getTime() - startedAt.getTime());
|
|
476
|
+
if (durationMs < 1e3) {
|
|
477
|
+
return `${durationMs}ms`;
|
|
478
|
+
}
|
|
479
|
+
const durationSeconds = durationMs / 1e3;
|
|
480
|
+
if (durationSeconds < 60) {
|
|
481
|
+
return `${durationSeconds.toFixed(1)}s`;
|
|
482
|
+
}
|
|
483
|
+
const minutes = Math.floor(durationSeconds / 60);
|
|
484
|
+
const seconds = Math.round(durationSeconds % 60);
|
|
485
|
+
return `${minutes}m ${seconds}s`;
|
|
486
|
+
}
|
|
487
|
+
function writeExecutionLog(options = {}) {
|
|
488
|
+
try {
|
|
489
|
+
const startedAt = options.startedAt ?? /* @__PURE__ */ new Date();
|
|
490
|
+
const finishedAt = options.finishedAt ?? /* @__PURE__ */ new Date();
|
|
491
|
+
const status = options.status ?? "success";
|
|
492
|
+
helperTrace("execution-log:write:start", options.scope || "unknown");
|
|
493
|
+
createReportDirectory();
|
|
494
|
+
const reportPath = getAvailableFilePath(REPORT_DIR, `${getNowString(finishedAt)}-execution-log`, ".md");
|
|
495
|
+
const traceSnapshot = options.traceMessages ?? getTraceMessages();
|
|
496
|
+
const extraSections = options.extraSections || [];
|
|
497
|
+
const serializedError = options.error ? serializeError(options.error) : null;
|
|
498
|
+
const report = `# Execution Log
|
|
499
|
+
|
|
500
|
+
- \uC2DC\uC791 \uC2DC\uAC01: ${getHumanReadableNowString(startedAt)}
|
|
501
|
+
- \uC885\uB8CC \uC2DC\uAC01: ${getHumanReadableNowString(finishedAt)}
|
|
502
|
+
- \uC2E4\uD589 \uC2DC\uAC04: ${formatExecutionDuration(startedAt, finishedAt)}
|
|
503
|
+
- \uC0C1\uD0DC: \`${status}\`
|
|
504
|
+
- Scope: \`${options.scope || "unknown"}\`
|
|
505
|
+
- \uC791\uC5C5 \uACBD\uB85C: \`${process.cwd()}\`
|
|
506
|
+
- \uC2E4\uD589 \uC778\uC790: \`${JSON.stringify(options.args ?? process.argv.slice(2))}\`
|
|
507
|
+
- \uC2E4\uD589 \uD658\uACBD: \`${process.platform} ${process.arch} / Node ${process.version}\`
|
|
508
|
+
|
|
509
|
+
## Summary
|
|
510
|
+
|
|
511
|
+
${getExecutionLogSummary(status, options.title)}
|
|
512
|
+
${serializedError ? `
|
|
513
|
+
|
|
514
|
+
## Error
|
|
515
|
+
|
|
516
|
+
\`\`\`json
|
|
517
|
+
${JSON.stringify(serializedError, null, 2)}
|
|
518
|
+
\`\`\`` : ""}
|
|
519
|
+
|
|
520
|
+
## Trace
|
|
521
|
+
|
|
522
|
+
\`\`\`json
|
|
523
|
+
${JSON.stringify(traceSnapshot, null, 2)}
|
|
524
|
+
\`\`\`${extraSections.length ? `
|
|
525
|
+
${extraSections.map((section) => `
|
|
526
|
+
## ${section.heading}
|
|
527
|
+
|
|
528
|
+
${section.markdown}`).join("\n")}
|
|
529
|
+
` : "\n"}
|
|
530
|
+
`;
|
|
531
|
+
fs.writeFileSync(reportPath, report);
|
|
532
|
+
helperTrace("execution-log:write:done", reportPath);
|
|
533
|
+
return reportPath;
|
|
534
|
+
} catch (writeError) {
|
|
535
|
+
console.error("\u26A0\uFE0F \uC2E4\uD589 \uB85C\uADF8 \uD30C\uC77C \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
|
|
536
|
+
console.error(writeError);
|
|
537
|
+
return "";
|
|
538
|
+
}
|
|
539
|
+
}
|
|
209
540
|
function exitWithError(message, options = {}) {
|
|
210
541
|
const reportPath = writeErrorReport(options.error || new Error(message), {
|
|
211
542
|
...options,
|
|
@@ -250,13 +581,67 @@ ${JSON.stringify(AIServices, null, 2)}
|
|
|
250
581
|
}
|
|
251
582
|
);
|
|
252
583
|
}
|
|
253
|
-
function
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
584
|
+
function truncateCommitSubject(subject) {
|
|
585
|
+
if (subject.length <= 72) {
|
|
586
|
+
return subject;
|
|
587
|
+
}
|
|
588
|
+
return `${subject.slice(0, 69)}...`;
|
|
589
|
+
}
|
|
590
|
+
function getRecentCommitOptions() {
|
|
591
|
+
const output = runGitCommand(
|
|
592
|
+
["log", `-${COMMIT_FETCH_LIMIT}`, "--date=relative", "--pretty=format:%h%x09%an%x09%ar%x09%s"],
|
|
593
|
+
{ allowFailure: true }
|
|
594
|
+
);
|
|
595
|
+
if (!output) {
|
|
596
|
+
return [];
|
|
597
|
+
}
|
|
598
|
+
return output.split("\n").map((line) => {
|
|
599
|
+
const [hash = "", author = "", relativeDate = "", ...subjectParts] = line.split(" ");
|
|
600
|
+
const subject = subjectParts.join(" ").trim();
|
|
601
|
+
return {
|
|
602
|
+
author,
|
|
603
|
+
description: `${author} | ${relativeDate}`,
|
|
604
|
+
hash,
|
|
605
|
+
label: `${hash} | ${truncateCommitSubject(subject)}`,
|
|
606
|
+
relativeDate,
|
|
607
|
+
subject
|
|
608
|
+
};
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
function buildSelectedCommitSummary(commits) {
|
|
612
|
+
return commits.map((commit) => `- ${commit.hash} | ${commit.subject} | ${commit.author} | ${commit.relativeDate}`).join("\n");
|
|
613
|
+
}
|
|
614
|
+
function getReviewPathspecArgs() {
|
|
615
|
+
const { includePatterns, excludePatterns } = getGitDiffPathspecs();
|
|
616
|
+
return [...includePatterns, ...excludePatterns];
|
|
617
|
+
}
|
|
618
|
+
function buildSelectedCommitDiff(commits) {
|
|
619
|
+
const reviewPathspecArgs = getReviewPathspecArgs();
|
|
620
|
+
const sections = commits.map((commit) => {
|
|
621
|
+
const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", ...reviewPathspecArgs], {
|
|
622
|
+
allowFailure: true,
|
|
623
|
+
trimOutput: false
|
|
624
|
+
}).trim();
|
|
625
|
+
if (!diff) {
|
|
626
|
+
return "";
|
|
627
|
+
}
|
|
628
|
+
return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
|
|
629
|
+
}).filter(Boolean).join("\n\n");
|
|
630
|
+
if (!sections) {
|
|
631
|
+
return "";
|
|
632
|
+
}
|
|
633
|
+
return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", "# \uB9AC\uBDF0 \uB300\uC0C1 diff", sections].join("\n");
|
|
634
|
+
}
|
|
635
|
+
function getSelectedCommitFiles(commits) {
|
|
636
|
+
const reviewPathspecArgs = getReviewPathspecArgs();
|
|
637
|
+
const files = /* @__PURE__ */ new Set();
|
|
638
|
+
commits.forEach((commit) => {
|
|
639
|
+
const output = runGitCommand(["show", "--pretty=format:", "--name-only", commit.hash, "--", ...reviewPathspecArgs], {
|
|
640
|
+
allowFailure: true
|
|
641
|
+
});
|
|
642
|
+
output.split("\n").map((line) => line.trim()).filter(Boolean).forEach((filePath) => files.add(filePath));
|
|
643
|
+
});
|
|
644
|
+
return [...files];
|
|
260
645
|
}
|
|
261
646
|
function openReport(reportPath) {
|
|
262
647
|
const resolvedPath = path.resolve(reportPath);
|
|
@@ -307,59 +692,166 @@ function openReport(reportPath) {
|
|
|
307
692
|
helperTrace("open-report:unsupported-platform", platform);
|
|
308
693
|
console.error(`\u26A0\uFE0F \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD50C\uB7AB\uD3FC\uC785\uB2C8\uB2E4: ${platform}`);
|
|
309
694
|
}
|
|
310
|
-
function
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
helperTrace(
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
695
|
+
function ensureInteractiveSelectionAvailable(scope, message) {
|
|
696
|
+
if (process.stdin.isTTY && process.stdout.isTTY && typeof process.stdin.setRawMode === "function") {
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
helperTrace(`${scope}:tty-missing`);
|
|
700
|
+
exitWithError(message, {
|
|
701
|
+
scope: `helper:${scope}`
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
function renderSelectionBlock(lines, previousLineCount) {
|
|
705
|
+
if (previousLineCount > 0) {
|
|
706
|
+
readline.moveCursor(process.stdout, 0, -previousLineCount);
|
|
707
|
+
readline.clearScreenDown(process.stdout);
|
|
708
|
+
}
|
|
709
|
+
const fittedLines = fitLinesToTerminal(lines);
|
|
710
|
+
process.stdout.write(`${fittedLines.join("\n")}
|
|
711
|
+
`);
|
|
712
|
+
return fittedLines.length;
|
|
713
|
+
}
|
|
714
|
+
function getSelectionWindowRange(optionCount, selectedIndex, windowSize) {
|
|
715
|
+
if (optionCount <= windowSize) {
|
|
716
|
+
return {
|
|
717
|
+
end: optionCount,
|
|
718
|
+
start: 0
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
const halfWindow = Math.floor(windowSize / 2);
|
|
722
|
+
const maxStart = optionCount - windowSize;
|
|
723
|
+
const start = Math.max(0, Math.min(selectedIndex - halfWindow, maxStart));
|
|
724
|
+
return {
|
|
725
|
+
end: Math.min(optionCount, start + windowSize),
|
|
726
|
+
start
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
function buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize) {
|
|
730
|
+
const { start, end } = getSelectionWindowRange(options.length, selectedIndex, windowSize);
|
|
731
|
+
const lines = [
|
|
732
|
+
`${ANSI.bold}${question}${ANSI.reset}`,
|
|
733
|
+
`${ANSI.dim}\u2191\u2193 \uC774\uB3D9 | Space \uC120\uD0DD/\uD574\uC81C | Enter \uC644\uB8CC | Esc \uCDE8\uC18C${ANSI.reset}`,
|
|
734
|
+
`${ANSI.dim}\uC120\uD0DD\uB428: ${toggled.size}\uAC1C / \uC804\uCCB4: ${options.length}\uAC1C${ANSI.reset}`
|
|
735
|
+
];
|
|
736
|
+
for (let index = start; index < end; index += 1) {
|
|
737
|
+
const option = options[index];
|
|
738
|
+
if (!option) {
|
|
739
|
+
continue;
|
|
324
740
|
}
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
741
|
+
const cursor = index === selectedIndex ? `${ANSI.cyan}>${ANSI.reset}` : " ";
|
|
742
|
+
const checked = toggled.has(index) ? `${ANSI.green}\u2611${ANSI.reset}` : "\u2610";
|
|
743
|
+
const description = option.description ? ` ${ANSI.dim}${option.description}${ANSI.reset}` : "";
|
|
744
|
+
lines.push(`${cursor} ${checked} ${option.label}${description}`);
|
|
745
|
+
}
|
|
746
|
+
if (options.length > windowSize) {
|
|
747
|
+
lines.push(`${ANSI.dim}\uD45C\uC2DC \uBC94\uC704: ${start + 1}-${end} / ${options.length}${ANSI.reset}`);
|
|
748
|
+
}
|
|
749
|
+
return lines;
|
|
750
|
+
}
|
|
751
|
+
async function showMultiSelect(question, options, windowSize = COMMIT_SELECTION_WINDOW) {
|
|
752
|
+
ensureInteractiveSelectionAvailable("showMultiSelect", "\u274C \uCEE4\uBC0B \uC120\uD0DD \uBAA8\uB2EC\uC740 TTY \uD658\uACBD\uC5D0\uC11C\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
753
|
+
let selectedIndex = 0;
|
|
754
|
+
let renderedLineCount = 0;
|
|
755
|
+
const toggled = /* @__PURE__ */ new Set();
|
|
756
|
+
const rl = readline.createInterface({
|
|
757
|
+
input: process.stdin,
|
|
758
|
+
output: process.stdout,
|
|
759
|
+
terminal: true
|
|
760
|
+
});
|
|
761
|
+
process.stdout.write("\x1B[?25l");
|
|
762
|
+
const cleanup = () => {
|
|
763
|
+
if (renderedLineCount > 0) {
|
|
764
|
+
readline.moveCursor(process.stdout, 0, -renderedLineCount);
|
|
765
|
+
readline.clearScreenDown(process.stdout);
|
|
766
|
+
renderedLineCount = 0;
|
|
332
767
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
768
|
+
process.stdin.removeListener("data", onData);
|
|
769
|
+
process.stdin.setRawMode(false);
|
|
770
|
+
process.stdin.pause();
|
|
771
|
+
rl.close();
|
|
772
|
+
process.stdout.write("\x1B[?25h");
|
|
773
|
+
};
|
|
774
|
+
const render = () => {
|
|
775
|
+
const lines = buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize);
|
|
776
|
+
renderedLineCount = renderSelectionBlock(lines, renderedLineCount);
|
|
777
|
+
};
|
|
778
|
+
const confirmSelection = (resolve) => {
|
|
779
|
+
const values = [...toggled].sort((left, right) => left - right).map((index) => options[index]?.value).filter((value) => value !== void 0);
|
|
780
|
+
cleanup();
|
|
781
|
+
resolve(values);
|
|
782
|
+
};
|
|
783
|
+
const cancelSelection = (resolve) => {
|
|
784
|
+
cleanup();
|
|
785
|
+
resolve([]);
|
|
786
|
+
};
|
|
787
|
+
let onData = (_data) => {
|
|
788
|
+
};
|
|
789
|
+
render();
|
|
790
|
+
return new Promise((resolve) => {
|
|
791
|
+
onData = (data) => {
|
|
792
|
+
const key = data.toString();
|
|
793
|
+
if (key === "") {
|
|
794
|
+
cleanup();
|
|
795
|
+
process.exit(0);
|
|
346
796
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
797
|
+
if (key === "\x1B") {
|
|
798
|
+
cancelSelection(resolve);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
if (key === "\x1B[A") {
|
|
802
|
+
selectedIndex = (selectedIndex - 1 + options.length) % options.length;
|
|
803
|
+
render();
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
if (key === "\x1B[B") {
|
|
807
|
+
selectedIndex = (selectedIndex + 1) % options.length;
|
|
808
|
+
render();
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
if (key === " ") {
|
|
812
|
+
if (toggled.has(selectedIndex)) {
|
|
813
|
+
toggled.delete(selectedIndex);
|
|
814
|
+
} else {
|
|
815
|
+
toggled.add(selectedIndex);
|
|
816
|
+
}
|
|
817
|
+
render();
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
if (key === "\r" || key === "\n") {
|
|
821
|
+
confirmSelection(resolve);
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
process.stdin.setRawMode(true);
|
|
825
|
+
process.stdin.resume();
|
|
826
|
+
process.stdin.on("data", onData);
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
async function selectReviewCommits() {
|
|
830
|
+
const commits = getRecentCommitOptions();
|
|
831
|
+
if (commits.length === 0) {
|
|
832
|
+
console.log("\u2139\uFE0F \uB9AC\uBDF0\uD560 \uCD5C\uADFC \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
833
|
+
return [];
|
|
350
834
|
}
|
|
351
|
-
|
|
352
|
-
|
|
835
|
+
return showMultiSelect(
|
|
836
|
+
"\uB9AC\uBDF0\uD560 \uCEE4\uBC0B\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.",
|
|
837
|
+
commits.map((commit) => ({
|
|
838
|
+
description: commit.description,
|
|
839
|
+
label: commit.label,
|
|
840
|
+
value: commit
|
|
841
|
+
})),
|
|
842
|
+
COMMIT_SELECTION_WINDOW
|
|
843
|
+
);
|
|
353
844
|
}
|
|
354
845
|
async function showSelectionAIService() {
|
|
355
846
|
const selectedServiceFromArgs = parseServiceFromArgs();
|
|
356
847
|
if (selectedServiceFromArgs) {
|
|
357
848
|
helperTrace("show-selection:from-args", selectedServiceFromArgs);
|
|
358
849
|
console.log(`
|
|
359
|
-
\u2705
|
|
850
|
+
\u2705 ${ANSI.green}${selectedServiceFromArgs}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (--service)
|
|
360
851
|
`);
|
|
361
852
|
return selectedServiceFromArgs;
|
|
362
853
|
}
|
|
854
|
+
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
855
|
helperTrace("show-selection:interactive:start");
|
|
364
856
|
let selectedIndex = 0;
|
|
365
857
|
const rl = readline.createInterface({
|
|
@@ -377,11 +869,12 @@ async function showSelectionAIService() {
|
|
|
377
869
|
helperTrace("show-selection:interactive:render", AIServices[selectedIndex] || "unknown");
|
|
378
870
|
readline.clearScreenDown(process.stdout);
|
|
379
871
|
process.stdout.write(
|
|
380
|
-
|
|
872
|
+
`\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):
|
|
873
|
+
`
|
|
381
874
|
);
|
|
382
875
|
AIServices.forEach((service, index) => {
|
|
383
876
|
if (index === selectedIndex) {
|
|
384
|
-
process.stdout.write(`
|
|
877
|
+
process.stdout.write(` ${ANSI.cyan}>${ANSI.reset} ${ANSI.cyan}\u25C9${ANSI.reset} ${ANSI.bold}${service}${ANSI.reset}
|
|
385
878
|
`);
|
|
386
879
|
} else {
|
|
387
880
|
process.stdout.write(` \u25EF ${service}
|
|
@@ -411,7 +904,7 @@ async function showSelectionAIService() {
|
|
|
411
904
|
rl.close();
|
|
412
905
|
process.stdout.write("\x1B[?25h");
|
|
413
906
|
console.log(`
|
|
414
|
-
\u2705
|
|
907
|
+
\u2705 ${ANSI.green}${AIServices[selectedIndex]}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
|
|
415
908
|
`);
|
|
416
909
|
const result = AIServices[selectedIndex];
|
|
417
910
|
if (result) {
|
|
@@ -431,6 +924,13 @@ var ALLOWED_REASONING_EFFORTS = ["minimal", "low", "medium", "high"];
|
|
|
431
924
|
function shellQuote(value) {
|
|
432
925
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
433
926
|
}
|
|
927
|
+
function toShellOptionToken(value) {
|
|
928
|
+
const SIMPLE_SHELL_TOKEN_PATTERN = /^[A-Za-z0-9._:/=-]+$/;
|
|
929
|
+
if (SIMPLE_SHELL_TOKEN_PATTERN.test(value)) {
|
|
930
|
+
return value;
|
|
931
|
+
}
|
|
932
|
+
return shellQuote(value);
|
|
933
|
+
}
|
|
434
934
|
function getArgValue(flag) {
|
|
435
935
|
const index = args.indexOf(flag);
|
|
436
936
|
if (index === -1 || !args[index + 1]) {
|
|
@@ -438,6 +938,11 @@ function getArgValue(flag) {
|
|
|
438
938
|
}
|
|
439
939
|
return args[index + 1];
|
|
440
940
|
}
|
|
941
|
+
function printNotice(message) {
|
|
942
|
+
if (args.includes("--test")) {
|
|
943
|
+
console.warn(message);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
441
946
|
function toUnique(values) {
|
|
442
947
|
const seen = /* @__PURE__ */ new Set();
|
|
443
948
|
return values.filter((value) => {
|
|
@@ -461,11 +966,11 @@ function resolveReasoningEffort() {
|
|
|
461
966
|
const normalized = normalizeEffort(customReasoningEffort);
|
|
462
967
|
trace("reasoning:custom", `${customReasoningEffort} -> ${normalized}`);
|
|
463
968
|
if (customReasoningEffort === "minimal") {
|
|
464
|
-
|
|
969
|
+
printNotice("\u26A0\uFE0F Claude\uB294 minimal\uC744 \uC9C1\uC811 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC544 low\uB85C \uB9E4\uD551\uD569\uB2C8\uB2E4.");
|
|
465
970
|
}
|
|
466
971
|
return normalized;
|
|
467
972
|
}
|
|
468
|
-
|
|
973
|
+
printNotice(
|
|
469
974
|
`\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS.join(
|
|
470
975
|
", "
|
|
471
976
|
)}`
|
|
@@ -505,10 +1010,10 @@ function getAliasFallbacks(primaryAlias) {
|
|
|
505
1010
|
}
|
|
506
1011
|
function buildClaudeExecCommand(options) {
|
|
507
1012
|
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((
|
|
1013
|
+
const modelOption = model ? `--model ${toShellOptionToken(model)}` : "";
|
|
1014
|
+
const fallbackOption = model && fallbackModel ? `--fallback-model ${toShellOptionToken(fallbackModel)}` : "";
|
|
1015
|
+
const effortOption = `--effort ${toShellOptionToken(effort)}`;
|
|
1016
|
+
const appendedPromptFiles = systemPromptFiles.map((path3) => `--append-system-prompt-file ${shellQuote(path3)}`).join(" ");
|
|
512
1017
|
return `cat ${shellQuote(tempDiffPath2)} | claude ${[
|
|
513
1018
|
modelOption,
|
|
514
1019
|
fallbackOption,
|
|
@@ -541,13 +1046,13 @@ var createClaudeCommand = (tempDiffPath2, reviewFormPath2) => {
|
|
|
541
1046
|
trace("model:candidates", modelCandidates.join(", "));
|
|
542
1047
|
trace("command:candidates:count", String(modelCandidates.length + 1));
|
|
543
1048
|
if (customModel) {
|
|
544
|
-
|
|
1049
|
+
printNotice(
|
|
545
1050
|
`\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
|
|
546
1051
|
" -> "
|
|
547
1052
|
)}) \uBC0F \uAE30\uBCF8 \uBAA8\uB378 \uC21C\uC73C\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
|
|
548
1053
|
);
|
|
549
1054
|
} else {
|
|
550
|
-
|
|
1055
|
+
printNotice(
|
|
551
1056
|
`\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
1057
|
);
|
|
553
1058
|
}
|
|
@@ -627,22 +1132,37 @@ function getArgValue2(flag) {
|
|
|
627
1132
|
}
|
|
628
1133
|
return args2[index + 1];
|
|
629
1134
|
}
|
|
1135
|
+
function printNotice2(message) {
|
|
1136
|
+
if (args2.includes("--test")) {
|
|
1137
|
+
console.warn(message);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
function normalizeEffort2(level) {
|
|
1141
|
+
if (level === "minimal") {
|
|
1142
|
+
return "low";
|
|
1143
|
+
}
|
|
1144
|
+
return level;
|
|
1145
|
+
}
|
|
630
1146
|
function resolveReasoningEffort2() {
|
|
631
1147
|
const customReasoningEffort = getArgValue2("--reasoning-effort");
|
|
632
1148
|
if (customReasoningEffort) {
|
|
633
1149
|
if (ALLOWED_REASONING_EFFORTS2.includes(customReasoningEffort)) {
|
|
634
|
-
|
|
635
|
-
|
|
1150
|
+
const normalized = normalizeEffort2(customReasoningEffort);
|
|
1151
|
+
trace3("reasoning:custom", `${customReasoningEffort} -> ${normalized}`);
|
|
1152
|
+
if (customReasoningEffort === "minimal") {
|
|
1153
|
+
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.");
|
|
1154
|
+
}
|
|
1155
|
+
return normalized;
|
|
636
1156
|
}
|
|
637
|
-
|
|
1157
|
+
printNotice2(
|
|
638
1158
|
`\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS2.join(
|
|
639
1159
|
", "
|
|
640
1160
|
)}`
|
|
641
1161
|
);
|
|
642
1162
|
}
|
|
643
1163
|
if (args2.includes("--flash")) {
|
|
644
|
-
trace3("reasoning:flash-default", "
|
|
645
|
-
return "
|
|
1164
|
+
trace3("reasoning:flash-default", "low");
|
|
1165
|
+
return "low";
|
|
646
1166
|
}
|
|
647
1167
|
if (args2.includes("--review")) {
|
|
648
1168
|
trace3("reasoning:review-default", "high");
|
|
@@ -678,14 +1198,14 @@ ${reviewFormLine || "- (\uC5C6\uC74C)"}
|
|
|
678
1198
|
trace3("prompt:prepared", `length=${prompt.length}`);
|
|
679
1199
|
let command = "";
|
|
680
1200
|
if (customModel) {
|
|
681
|
-
|
|
1201
|
+
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
1202
|
trace3("model:custom", customModel);
|
|
683
1203
|
command = buildCodexExecCommand(prompt, reasoningEffort, customModel);
|
|
684
1204
|
} else {
|
|
685
1205
|
const preferredModelAlias = "gpt-5";
|
|
686
1206
|
const aliasCommand = buildCodexExecCommand(prompt, reasoningEffort, preferredModelAlias);
|
|
687
1207
|
const fallbackCommand = buildCodexExecCommand(prompt, reasoningEffort);
|
|
688
|
-
|
|
1208
|
+
printNotice2(
|
|
689
1209
|
`\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
1210
|
);
|
|
691
1211
|
trace3("model:alias-first", preferredModelAlias);
|
|
@@ -746,6 +1266,11 @@ function getArgValue3(flag) {
|
|
|
746
1266
|
}
|
|
747
1267
|
return args3[index + 1];
|
|
748
1268
|
}
|
|
1269
|
+
function printNotice3(message) {
|
|
1270
|
+
if (args3.includes("--test")) {
|
|
1271
|
+
console.warn(message);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
749
1274
|
function toUnique2(values) {
|
|
750
1275
|
const seen = /* @__PURE__ */ new Set();
|
|
751
1276
|
return values.filter((value) => {
|
|
@@ -763,7 +1288,7 @@ function resolveReasoningEffort3() {
|
|
|
763
1288
|
trace5("reasoning:custom", customReasoningEffort);
|
|
764
1289
|
return customReasoningEffort;
|
|
765
1290
|
}
|
|
766
|
-
|
|
1291
|
+
printNotice3(
|
|
767
1292
|
`\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS3.join(
|
|
768
1293
|
", "
|
|
769
1294
|
)}`
|
|
@@ -825,38 +1350,57 @@ function buildGeminiExecCommand(prompt, model) {
|
|
|
825
1350
|
const modelOption = model ? `--model ${shellQuote3(model)}` : "";
|
|
826
1351
|
return `gemini ${[modelOption, "-p", shellQuote3(prompt)].filter(Boolean).join(" ")}`;
|
|
827
1352
|
}
|
|
1353
|
+
function toGeminiFileReference(filePath) {
|
|
1354
|
+
return `@${filePath}`;
|
|
1355
|
+
}
|
|
1356
|
+
function buildGeminiFileReferenceSection(files) {
|
|
1357
|
+
const existingFiles = files.filter((file) => fs.existsSync(file.path));
|
|
1358
|
+
return {
|
|
1359
|
+
count: existingFiles.length,
|
|
1360
|
+
items: existingFiles.map((file) => `${file.display} ${toGeminiFileReference(file.path)}`)
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
828
1363
|
var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
|
|
829
1364
|
trace5("createGeminiCommand:start", `tempDiffPath=${tempDiffPath2}, reviewFormPath=${reviewFormPath2}`);
|
|
830
1365
|
const customModel = getArgValue3("--model");
|
|
831
1366
|
const reasoningEffort = resolveReasoningEffort3();
|
|
832
1367
|
const primaryAlias = resolvePrimaryAlias2(reasoningEffort);
|
|
833
1368
|
const aliasFallbacks = toUnique2(getAliasFallbacks2(primaryAlias));
|
|
1369
|
+
const resolvedTempDiffPath = path.resolve(tempDiffPath2);
|
|
1370
|
+
const resolvedReviewFormPath = path.resolve(reviewFormPath2);
|
|
834
1371
|
const rules = [
|
|
835
1372
|
{ path: rulesPath, display: "\uB8F0\uC14B" },
|
|
836
1373
|
{ path: namingRulesPath, display: "\uB124\uC774\uBC0D \uADDC\uCE59" },
|
|
837
1374
|
{ path: codingConventionRulesPath, display: "\uCF54\uB529 \uCEE8\uBCA4\uC158" }
|
|
838
1375
|
];
|
|
839
|
-
const
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
const
|
|
843
|
-
trace5("reviewForm:status",
|
|
1376
|
+
const ruleSection = buildGeminiFileReferenceSection(rules);
|
|
1377
|
+
trace5("rules:loaded", `count=${ruleSection.count}`);
|
|
1378
|
+
const reviewFormExists = fs.existsSync(resolvedReviewFormPath);
|
|
1379
|
+
const reviewFormText = reviewFormExists ? `\uB9AC\uBDF0 \uC591\uC2DD ${toGeminiFileReference(resolvedReviewFormPath)}` : "\uB9AC\uBDF0 \uC591\uC2DD (\uC5C6\uC74C)";
|
|
1380
|
+
trace5("reviewForm:status", reviewFormExists ? "exists" : "missing");
|
|
844
1381
|
const reasoningInstruction = getReasoningInstruction(reasoningEffort);
|
|
845
|
-
const
|
|
846
|
-
|
|
847
|
-
|
|
1382
|
+
const ruleText = ruleSection.items.join(" ") || "(\uC5C6\uC74C)";
|
|
1383
|
+
const diffText = `\uB9AC\uBDF0 \uB300\uC0C1 diff ${toGeminiFileReference(resolvedTempDiffPath)}`;
|
|
1384
|
+
const prompt = [
|
|
1385
|
+
"\uC544\uB798 \uD30C\uC77C\uB4E4\uC744 \uC21C\uC11C\uB300\uB85C \uC77D\uACE0 \uCF54\uB4DC \uB9AC\uBDF0\uB97C \uC9C4\uD589\uD574\uC918.",
|
|
1386
|
+
`\uADDC\uCE59 \uD30C\uC77C: ${ruleText}`,
|
|
1387
|
+
`\uB9AC\uBDF0 \uC591\uC2DD \uD30C\uC77C: ${reviewFormText}`,
|
|
1388
|
+
`\uB9AC\uBDF0 \uB300\uC0C1 diff \uD30C\uC77C: ${diffText}`,
|
|
1389
|
+
"\uBC18\uB4DC\uC2DC \uB9AC\uBDF0 \uC591\uC2DD\uC5D0 \uB9DE\uCDB0 \uC791\uC131\uD574\uC918.",
|
|
1390
|
+
`\uCD94\uB860 \uAC15\uB3C4 \uC9C0\uCE68: ${reasoningInstruction}`
|
|
1391
|
+
].join(" ");
|
|
848
1392
|
trace5("prompt:prepared", `length=${prompt.length}`);
|
|
849
1393
|
const modelCandidates = toUnique2(customModel ? [customModel, ...aliasFallbacks] : aliasFallbacks);
|
|
850
1394
|
trace5("model:candidates", modelCandidates.join(", "));
|
|
851
1395
|
trace5("command:candidates:count", String(modelCandidates.length + 1));
|
|
852
1396
|
if (customModel) {
|
|
853
|
-
|
|
1397
|
+
printNotice3(
|
|
854
1398
|
`\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
|
|
855
1399
|
" -> "
|
|
856
1400
|
)}) \uBC0F \uAE30\uBCF8 \uBAA8\uB378 \uC21C\uC73C\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
|
|
857
1401
|
);
|
|
858
1402
|
} else {
|
|
859
|
-
|
|
1403
|
+
printNotice3(
|
|
860
1404
|
`\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
1405
|
);
|
|
862
1406
|
}
|
|
@@ -907,14 +1451,24 @@ function checkGeminiCliInstalled() {
|
|
|
907
1451
|
// src/pr-review/review.ts
|
|
908
1452
|
async function main() {
|
|
909
1453
|
const args4 = process.argv.slice(2);
|
|
1454
|
+
const startedAt = /* @__PURE__ */ new Date();
|
|
910
1455
|
clearTraceMessages();
|
|
911
1456
|
const isTest = isTestMode(args4);
|
|
1457
|
+
const shouldStreamOutput = shouldStreamAIOutput(args4);
|
|
912
1458
|
const trace7 = createTraceLogger("review", args4);
|
|
913
1459
|
trace7("main:start", `args=${JSON.stringify(args4)}`);
|
|
914
1460
|
let command = "";
|
|
915
1461
|
let savedDiffPath = "";
|
|
916
1462
|
let savedReportPath = "";
|
|
917
1463
|
let service = "";
|
|
1464
|
+
let selectedCommitSummary = "";
|
|
1465
|
+
let reviewTargetFiles = [];
|
|
1466
|
+
let executionLogPath = "";
|
|
1467
|
+
let executionStatus = "cancelled";
|
|
1468
|
+
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.";
|
|
1469
|
+
let executionError = null;
|
|
1470
|
+
let resultLength = 0;
|
|
1471
|
+
let exitCode = 0;
|
|
918
1472
|
try {
|
|
919
1473
|
trace7("service-selection:start");
|
|
920
1474
|
service = await showSelectionAIService();
|
|
@@ -938,30 +1492,39 @@ async function main() {
|
|
|
938
1492
|
}
|
|
939
1493
|
trace7("review-flow:start");
|
|
940
1494
|
console.log("\u{1F680} AI Code Review\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...");
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
trace7("report-dir:create:start");
|
|
944
|
-
createReportDirectory();
|
|
945
|
-
trace7("report-dir:create:done");
|
|
946
|
-
trace7("diff-args:build:start");
|
|
947
|
-
const diffArgs = getDiffArgs();
|
|
948
|
-
trace7("diff-args:build:done", `diffArgs=${diffArgs || "(default)"}`);
|
|
949
|
-
let diff = "";
|
|
950
|
-
const { includeParams, excludeParams } = getGitDiffFilter();
|
|
951
|
-
trace7("diff-filter:loaded", `include=${includeParams} | exclude=${excludeParams}`);
|
|
952
|
-
try {
|
|
953
|
-
trace7("git-diff:run");
|
|
954
|
-
diff = execSync(`git diff ${diffArgs} -- ${includeParams} ${excludeParams}`).toString();
|
|
955
|
-
trace7("git-diff:done", `length=${diff.length}`);
|
|
956
|
-
} catch (error) {
|
|
957
|
-
trace7("git-diff:error", getErrorSummary(error));
|
|
1495
|
+
if (shouldStreamOutput) {
|
|
1496
|
+
console.log("\u2139\uFE0F AI \uC2E4\uC2DC\uAC04 \uC751\uB2F5 \uD45C\uC2DC \uBAA8\uB4DC\uAC00 \uD65C\uC131\uD654\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
958
1497
|
}
|
|
1498
|
+
trace7("commit-selection:start");
|
|
1499
|
+
const selectedCommits = await selectReviewCommits();
|
|
1500
|
+
trace7("commit-selection:done", `count=${selectedCommits.length}`);
|
|
1501
|
+
if (selectedCommits.length === 0) {
|
|
1502
|
+
trace7("commit-selection:empty");
|
|
1503
|
+
executionTitle = "\uC120\uD0DD\uB41C \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.";
|
|
1504
|
+
console.log("\u2139\uFE0F \uC120\uD0DD\uB41C \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1505
|
+
deleteTempDiff();
|
|
1506
|
+
return;
|
|
1507
|
+
}
|
|
1508
|
+
selectedCommitSummary = buildSelectedCommitSummary(selectedCommits);
|
|
1509
|
+
trace7("commit-summary:prepared", selectedCommitSummary);
|
|
1510
|
+
reviewTargetFiles = getSelectedCommitFiles(selectedCommits);
|
|
1511
|
+
console.log(`\u{1F4C2} \uB9AC\uBDF0 \uB300\uC0C1 \uD30C\uC77C(${reviewTargetFiles.length}\uAC1C): ${formatReviewTargetFiles(reviewTargetFiles)}`);
|
|
1512
|
+
console.log("\u23F3 \uC120\uD0DD\uD55C \uCEE4\uBC0B diff\uB97C \uC815\uB9AC\uD558\uB294 \uC911\uC785\uB2C8\uB2E4...");
|
|
1513
|
+
trace7("git-diff:build:start");
|
|
1514
|
+
const diff = buildSelectedCommitDiff(selectedCommits);
|
|
1515
|
+
trace7("git-diff:build:done", `length=${diff.length}`);
|
|
959
1516
|
if (!diff.trim() && !isTest) {
|
|
960
1517
|
trace7("empty-diff:exit");
|
|
961
|
-
|
|
1518
|
+
executionTitle = "\uC120\uD0DD\uD55C \uCEE4\uBC0B\uC5D0\uC11C \uB9AC\uBDF0 \uB300\uC0C1 \uD30C\uC77C \uBCC0\uACBD\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.";
|
|
1519
|
+
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.");
|
|
962
1520
|
deleteTempDiff();
|
|
963
|
-
|
|
1521
|
+
return;
|
|
964
1522
|
}
|
|
1523
|
+
const nowStr = getNowString();
|
|
1524
|
+
trace7("timestamp:created", nowStr);
|
|
1525
|
+
trace7("report-dir:create:start");
|
|
1526
|
+
createReportDirectory();
|
|
1527
|
+
trace7("report-dir:create:done");
|
|
965
1528
|
trace7("temp-diff:write:start", tempDiffPath);
|
|
966
1529
|
fs.writeFileSync(tempDiffPath, diff);
|
|
967
1530
|
trace7("temp-diff:write:done");
|
|
@@ -983,9 +1546,12 @@ async function main() {
|
|
|
983
1546
|
}
|
|
984
1547
|
trace7("command:create:done");
|
|
985
1548
|
trace7("command:exec:start");
|
|
986
|
-
const result =
|
|
1549
|
+
const result = (await executeShellCommandWithProgress(command, {
|
|
1550
|
+
progressMessage: `\u23F3 [\uB9AC\uBDF0 \uC9C4\uD589] ${service} | \uB300\uC0C1 ${reviewTargetFiles.length}\uAC1C \uD30C\uC77C`,
|
|
1551
|
+
streamOutput: isTest || shouldStreamOutput
|
|
1552
|
+
})).stdout;
|
|
1553
|
+
resultLength = result.length;
|
|
987
1554
|
trace7("command:exec:done", `resultLength=${result.length}`);
|
|
988
|
-
console.log(result);
|
|
989
1555
|
trace7("report:write:start");
|
|
990
1556
|
savedReportPath = getNextFilePath(REPORT_DIR, nowStr, ".md");
|
|
991
1557
|
fs.writeFileSync(savedReportPath, result);
|
|
@@ -1010,9 +1576,15 @@ ${command}`);
|
|
|
1010
1576
|
deleteTempDiff();
|
|
1011
1577
|
trace7("cleanup-temp-diff:done");
|
|
1012
1578
|
trace7("review-flow:end");
|
|
1579
|
+
executionStatus = "success";
|
|
1580
|
+
executionTitle = "\uB9AC\uBDF0\uAC00 \uC131\uACF5\uC801\uC73C\uB85C \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.";
|
|
1013
1581
|
} catch (error) {
|
|
1014
1582
|
trace7("review-flow:catch", getErrorSummary(error));
|
|
1015
1583
|
let errorReportPath = "";
|
|
1584
|
+
executionStatus = "failed";
|
|
1585
|
+
executionTitle = "\uB9AC\uBDF0 \uC2E4\uD589 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
|
|
1586
|
+
executionError = error;
|
|
1587
|
+
exitCode = 1;
|
|
1016
1588
|
trace7("cleanup-temp-diff:start(catch)");
|
|
1017
1589
|
try {
|
|
1018
1590
|
deleteTempDiff();
|
|
@@ -1033,6 +1605,8 @@ ${command}`);
|
|
|
1033
1605
|
${JSON.stringify(
|
|
1034
1606
|
{
|
|
1035
1607
|
service,
|
|
1608
|
+
selectedCommitSummary: selectedCommitSummary || null,
|
|
1609
|
+
reviewTargetFiles,
|
|
1036
1610
|
command: command || null,
|
|
1037
1611
|
tempDiffPath,
|
|
1038
1612
|
savedDiffPath: savedDiffPath || null,
|
|
@@ -1050,7 +1624,50 @@ ${JSON.stringify(
|
|
|
1050
1624
|
if (errorReportPath) {
|
|
1051
1625
|
console.error(`\u{1F4C4} \uC5D0\uB7EC \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${errorReportPath}`);
|
|
1052
1626
|
}
|
|
1053
|
-
|
|
1627
|
+
} finally {
|
|
1628
|
+
executionLogPath = writeExecutionLog({
|
|
1629
|
+
scope: "review",
|
|
1630
|
+
status: executionStatus,
|
|
1631
|
+
title: executionTitle,
|
|
1632
|
+
args: args4,
|
|
1633
|
+
startedAt,
|
|
1634
|
+
error: executionError,
|
|
1635
|
+
extraSections: [
|
|
1636
|
+
{
|
|
1637
|
+
heading: "Execution Context",
|
|
1638
|
+
markdown: `\`\`\`json
|
|
1639
|
+
${JSON.stringify(
|
|
1640
|
+
{
|
|
1641
|
+
service: service || null,
|
|
1642
|
+
selectedCommitSummary: selectedCommitSummary || null,
|
|
1643
|
+
reviewTargetFiles,
|
|
1644
|
+
command: command || null,
|
|
1645
|
+
tempDiffPath,
|
|
1646
|
+
savedDiffPath: savedDiffPath || null,
|
|
1647
|
+
savedReportPath: savedReportPath || null,
|
|
1648
|
+
shouldStreamOutput,
|
|
1649
|
+
resultLength
|
|
1650
|
+
},
|
|
1651
|
+
null,
|
|
1652
|
+
2
|
|
1653
|
+
)}
|
|
1654
|
+
\`\`\``
|
|
1655
|
+
},
|
|
1656
|
+
{
|
|
1657
|
+
heading: "Generated Command",
|
|
1658
|
+
markdown: command ? `\`\`\`sh
|
|
1659
|
+
${command}
|
|
1660
|
+
\`\`\`` : "(\uC5C6\uC74C)"
|
|
1661
|
+
}
|
|
1662
|
+
]
|
|
1663
|
+
});
|
|
1664
|
+
if (executionLogPath) {
|
|
1665
|
+
const writeLog = executionStatus === "failed" ? console.error : console.log;
|
|
1666
|
+
writeLog(`\u{1F4DD} \uC2E4\uD589 \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${executionLogPath}`);
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
if (exitCode !== 0) {
|
|
1670
|
+
process.exit(exitCode);
|
|
1054
1671
|
}
|
|
1055
1672
|
}
|
|
1056
1673
|
main();
|