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,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var child_process = require('child_process');
|
|
5
4
|
var fs = require('fs');
|
|
5
|
+
var child_process = require('child_process');
|
|
6
6
|
var path = require('path');
|
|
7
7
|
var readline = require('readline');
|
|
8
8
|
var url = require('url');
|
|
@@ -17,14 +17,52 @@ var readline__default = /*#__PURE__*/_interopDefault(readline);
|
|
|
17
17
|
|
|
18
18
|
var __dirname$1 = path__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('review.cjs', document.baseURI).href))));
|
|
19
19
|
var traceMessages = [];
|
|
20
|
-
var
|
|
21
|
-
var
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
var GEMINI_CLI_PACKAGE_NAME = "sales-frontend-gemini-cli";
|
|
21
|
+
var cachedPackageRootPath = "";
|
|
22
|
+
function isGeminiCliPackageRoot(directory) {
|
|
23
|
+
const packageJsonPath = path__default.default.join(directory, "package.json");
|
|
24
|
+
if (!fs__default.default.existsSync(packageJsonPath)) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const packageJson = JSON.parse(fs__default.default.readFileSync(packageJsonPath, "utf8"));
|
|
29
|
+
return packageJson.name === GEMINI_CLI_PACKAGE_NAME;
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function resolveGeminiCliPackageRoot(startDirectory = __dirname$1) {
|
|
35
|
+
if (cachedPackageRootPath) {
|
|
36
|
+
return cachedPackageRootPath;
|
|
37
|
+
}
|
|
38
|
+
let currentDirectory = startDirectory;
|
|
39
|
+
while (true) {
|
|
40
|
+
if (isGeminiCliPackageRoot(currentDirectory)) {
|
|
41
|
+
cachedPackageRootPath = currentDirectory;
|
|
42
|
+
return cachedPackageRootPath;
|
|
43
|
+
}
|
|
44
|
+
const parentDirectory = path__default.default.dirname(currentDirectory);
|
|
45
|
+
if (parentDirectory === currentDirectory) {
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
currentDirectory = parentDirectory;
|
|
49
|
+
}
|
|
50
|
+
cachedPackageRootPath = path__default.default.resolve(startDirectory, "../..");
|
|
51
|
+
return cachedPackageRootPath;
|
|
52
|
+
}
|
|
53
|
+
function resolvePackageAssetPath(relativeFilePath) {
|
|
54
|
+
return path__default.default.resolve(resolveGeminiCliPackageRoot(), relativeFilePath);
|
|
55
|
+
}
|
|
56
|
+
var rulesPath = resolvePackageAssetPath("src/common/rules/review-rules.md");
|
|
57
|
+
var namingRulesPath = resolvePackageAssetPath("src/common/rules/naming-rule.md");
|
|
58
|
+
var codingConventionRulesPath = resolvePackageAssetPath("src/common/rules/coding-convention.md");
|
|
59
|
+
var reviewFormPath = resolvePackageAssetPath("src/common/form/review-form.md");
|
|
60
|
+
resolvePackageAssetPath("src/common/form/review-form-one-by-one.md");
|
|
25
61
|
var REPORT_DIR = ".review-report";
|
|
26
62
|
var tempDiffPath = "temp_diff.txt";
|
|
27
63
|
var AIServices = ["gemini", "claude", "codex"];
|
|
64
|
+
var COMMIT_FETCH_LIMIT = 20;
|
|
65
|
+
var COMMIT_SELECTION_WINDOW = 8;
|
|
28
66
|
var ignoreList = [
|
|
29
67
|
"package.json",
|
|
30
68
|
"*.yml",
|
|
@@ -42,12 +80,224 @@ var ignoreList = [
|
|
|
42
80
|
function isTestMode(args4 = process.argv.slice(2)) {
|
|
43
81
|
return args4.includes("--test");
|
|
44
82
|
}
|
|
83
|
+
function shouldStreamAIOutput(args4 = process.argv.slice(2)) {
|
|
84
|
+
return args4.includes("--stream-output");
|
|
85
|
+
}
|
|
45
86
|
function clearTraceMessages() {
|
|
46
87
|
traceMessages.length = 0;
|
|
47
88
|
}
|
|
48
89
|
function getTraceMessages() {
|
|
49
90
|
return [...traceMessages];
|
|
50
91
|
}
|
|
92
|
+
var ANSI = {
|
|
93
|
+
bold: "\x1B[1m",
|
|
94
|
+
cyan: "\x1B[36m",
|
|
95
|
+
dim: "\x1B[2m",
|
|
96
|
+
green: "\x1B[32m",
|
|
97
|
+
reset: "\x1B[0m",
|
|
98
|
+
yellow: "\x1B[33m"
|
|
99
|
+
};
|
|
100
|
+
var ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
101
|
+
var COMBINING_MARK_PATTERN = /\p{Mark}/u;
|
|
102
|
+
var GRAPHEME_SEGMENTER = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter("ko", { granularity: "grapheme" }) : null;
|
|
103
|
+
function getGitDiffPathspecs() {
|
|
104
|
+
return {
|
|
105
|
+
excludePatterns: ignoreList.map((item) => `:(exclude)${item}`),
|
|
106
|
+
includePatterns: ["*.ts", "*.tsx", "*.js", "*.jsx"]
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function segmentGraphemes(value) {
|
|
110
|
+
if (!GRAPHEME_SEGMENTER) {
|
|
111
|
+
return [...value];
|
|
112
|
+
}
|
|
113
|
+
return [...GRAPHEME_SEGMENTER.segment(value)].map(({ segment }) => segment);
|
|
114
|
+
}
|
|
115
|
+
function isWideCodePoint(codePoint) {
|
|
116
|
+
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);
|
|
117
|
+
}
|
|
118
|
+
function isEmojiCodePoint(codePoint) {
|
|
119
|
+
return codePoint >= 127462 && codePoint <= 127487 || codePoint >= 127744 && codePoint <= 129791 || codePoint >= 9728 && codePoint <= 10175;
|
|
120
|
+
}
|
|
121
|
+
function getGraphemeWidth(grapheme) {
|
|
122
|
+
let width = 0;
|
|
123
|
+
for (const character of grapheme) {
|
|
124
|
+
const codePoint = character.codePointAt(0);
|
|
125
|
+
if (!codePoint || COMBINING_MARK_PATTERN.test(character) || codePoint === 8205) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (codePoint >= 65024 && codePoint <= 65039 || codePoint >= 917760 && codePoint <= 917999) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (isWideCodePoint(codePoint) || isEmojiCodePoint(codePoint)) {
|
|
132
|
+
width = Math.max(width, 2);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
width = Math.max(width, 1);
|
|
136
|
+
}
|
|
137
|
+
return width;
|
|
138
|
+
}
|
|
139
|
+
function tokenizePlainText(value) {
|
|
140
|
+
return segmentGraphemes(value).map((segment) => ({
|
|
141
|
+
value: segment,
|
|
142
|
+
visibleWidth: getGraphemeWidth(segment)
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
function tokenizeVisibleText(value) {
|
|
146
|
+
const tokens = [];
|
|
147
|
+
let lastIndex = 0;
|
|
148
|
+
for (const match of value.matchAll(ANSI_PATTERN)) {
|
|
149
|
+
const index = match.index ?? 0;
|
|
150
|
+
if (index > lastIndex) {
|
|
151
|
+
tokens.push(...tokenizePlainText(value.slice(lastIndex, index)));
|
|
152
|
+
}
|
|
153
|
+
tokens.push({
|
|
154
|
+
value: match[0],
|
|
155
|
+
visibleWidth: 0
|
|
156
|
+
});
|
|
157
|
+
lastIndex = index + match[0].length;
|
|
158
|
+
}
|
|
159
|
+
if (lastIndex < value.length) {
|
|
160
|
+
tokens.push(...tokenizePlainText(value.slice(lastIndex)));
|
|
161
|
+
}
|
|
162
|
+
return tokens;
|
|
163
|
+
}
|
|
164
|
+
function truncateLineForTerminal(value, maxWidth) {
|
|
165
|
+
if (maxWidth <= 0) {
|
|
166
|
+
return "";
|
|
167
|
+
}
|
|
168
|
+
const tokens = tokenizeVisibleText(value);
|
|
169
|
+
const totalWidth = tokens.reduce((sum, token) => sum + token.visibleWidth, 0);
|
|
170
|
+
if (totalWidth <= maxWidth) {
|
|
171
|
+
return value;
|
|
172
|
+
}
|
|
173
|
+
const ellipsis = "...";
|
|
174
|
+
const ellipsisWidth = 3;
|
|
175
|
+
const targetWidth = Math.max(0, maxWidth - ellipsisWidth);
|
|
176
|
+
let usedWidth = 0;
|
|
177
|
+
let result = "";
|
|
178
|
+
for (const token of tokens) {
|
|
179
|
+
if (token.visibleWidth === 0) {
|
|
180
|
+
result += token.value;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (usedWidth + token.visibleWidth > targetWidth) {
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
result += token.value;
|
|
187
|
+
usedWidth += token.visibleWidth;
|
|
188
|
+
}
|
|
189
|
+
return `${result}${ellipsis}${ANSI.reset}`;
|
|
190
|
+
}
|
|
191
|
+
function fitLinesToTerminal(lines) {
|
|
192
|
+
const maxWidth = Math.max(20, (process.stdout.columns || 120) - 1);
|
|
193
|
+
return lines.map((line) => truncateLineForTerminal(line, maxWidth));
|
|
194
|
+
}
|
|
195
|
+
function runGitCommand(args4, options = {}) {
|
|
196
|
+
const { allowFailure = false, trimOutput = true } = options;
|
|
197
|
+
try {
|
|
198
|
+
const output = child_process.execFileSync("git", args4, {
|
|
199
|
+
encoding: "utf8",
|
|
200
|
+
maxBuffer: 1024 * 1024 * 20,
|
|
201
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
202
|
+
});
|
|
203
|
+
return trimOutput ? output.trim() : output;
|
|
204
|
+
} catch (error) {
|
|
205
|
+
helperTrace("git-command:failed", `${args4.join(" ")} | ${getErrorSummary(error)}`);
|
|
206
|
+
if (allowFailure) {
|
|
207
|
+
return "";
|
|
208
|
+
}
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async function executeShellCommandWithProgress(command, options = {}) {
|
|
213
|
+
const { progressIntervalMs = 1e4, progressMessage = "\u23F3 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uB294 \uC911\uC785\uB2C8\uB2E4...", streamOutput = false } = options;
|
|
214
|
+
const { spawn } = await import('child_process');
|
|
215
|
+
return new Promise((resolve, reject) => {
|
|
216
|
+
let stdout = "";
|
|
217
|
+
let stderr = "";
|
|
218
|
+
const startedAt = Date.now();
|
|
219
|
+
console.log(progressMessage);
|
|
220
|
+
const child = spawn("/bin/zsh", ["-lc", command], {
|
|
221
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
222
|
+
});
|
|
223
|
+
const progressTimer = setInterval(() => {
|
|
224
|
+
const elapsedSeconds = Math.max(1, Math.floor((Date.now() - startedAt) / 1e3));
|
|
225
|
+
console.log(`${progressMessage} (${elapsedSeconds}s \uACBD\uACFC)`);
|
|
226
|
+
}, progressIntervalMs);
|
|
227
|
+
child.stdout.on("data", (chunk) => {
|
|
228
|
+
const text = chunk.toString();
|
|
229
|
+
stdout += text;
|
|
230
|
+
if (streamOutput) {
|
|
231
|
+
process.stdout.write(text);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
child.stderr.on("data", (chunk) => {
|
|
235
|
+
const text = chunk.toString();
|
|
236
|
+
stderr += text;
|
|
237
|
+
if (streamOutput) {
|
|
238
|
+
process.stderr.write(text);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
child.on("error", (error) => {
|
|
242
|
+
clearInterval(progressTimer);
|
|
243
|
+
reject(error);
|
|
244
|
+
});
|
|
245
|
+
child.on("close", (code, signal) => {
|
|
246
|
+
clearInterval(progressTimer);
|
|
247
|
+
if (code === 0) {
|
|
248
|
+
resolve({
|
|
249
|
+
stderr,
|
|
250
|
+
stdout
|
|
251
|
+
});
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const exitSummary = signal ? `signal=${signal}` : `code=${String(code ?? "unknown")}`;
|
|
255
|
+
const failureDetails = {
|
|
256
|
+
code,
|
|
257
|
+
command,
|
|
258
|
+
signal,
|
|
259
|
+
stderr,
|
|
260
|
+
stdout
|
|
261
|
+
};
|
|
262
|
+
reject(createShellCommandExecutionError(failureDetails, exitSummary));
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
function getShellCommandFailurePreview(failureDetails) {
|
|
267
|
+
const stderrText = failureDetails.stderr.trim();
|
|
268
|
+
const stdoutText = failureDetails.stdout.trim();
|
|
269
|
+
const combinedOutput = stderrText || stdoutText;
|
|
270
|
+
if (!combinedOutput) {
|
|
271
|
+
return "";
|
|
272
|
+
}
|
|
273
|
+
const MAX_PREVIEW_LENGTH = 4e3;
|
|
274
|
+
if (combinedOutput.length <= MAX_PREVIEW_LENGTH) {
|
|
275
|
+
return combinedOutput;
|
|
276
|
+
}
|
|
277
|
+
return combinedOutput.slice(-4e3);
|
|
278
|
+
}
|
|
279
|
+
function createShellCommandExecutionError(failureDetails, exitSummary) {
|
|
280
|
+
const failurePreview = getShellCommandFailurePreview(failureDetails);
|
|
281
|
+
const error = new Error(`\uC258 \uBA85\uB839 \uC2E4\uD589 \uC2E4\uD328 (${exitSummary})${failurePreview ? `
|
|
282
|
+
${failurePreview}` : ""}`);
|
|
283
|
+
error.code = failureDetails.code;
|
|
284
|
+
error.signal = failureDetails.signal;
|
|
285
|
+
error.stdout = failureDetails.stdout;
|
|
286
|
+
error.stderr = failureDetails.stderr;
|
|
287
|
+
error.command = failureDetails.command;
|
|
288
|
+
return error;
|
|
289
|
+
}
|
|
290
|
+
function formatReviewTargetFiles(files, visibleCount = 5) {
|
|
291
|
+
if (files.length === 0) {
|
|
292
|
+
return "(\uC5C6\uC74C)";
|
|
293
|
+
}
|
|
294
|
+
const visibleFiles = files.slice(0, visibleCount);
|
|
295
|
+
const hiddenCount = Math.max(0, files.length - visibleFiles.length);
|
|
296
|
+
if (hiddenCount === 0) {
|
|
297
|
+
return visibleFiles.join(", ");
|
|
298
|
+
}
|
|
299
|
+
return `${visibleFiles.join(", ")} \uC678 ${hiddenCount}\uAC1C`;
|
|
300
|
+
}
|
|
51
301
|
function createTraceLogger(scope, args4 = process.argv.slice(2)) {
|
|
52
302
|
const enabled = isTestMode(args4);
|
|
53
303
|
return (step, detail) => {
|
|
@@ -109,7 +359,7 @@ function serializeError(error) {
|
|
|
109
359
|
}
|
|
110
360
|
if (error && typeof error === "object") {
|
|
111
361
|
const errorLike = error;
|
|
112
|
-
const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs"];
|
|
362
|
+
const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs", "command"];
|
|
113
363
|
extraKeys.forEach((key) => {
|
|
114
364
|
if (errorLike[key] !== void 0) {
|
|
115
365
|
serialized[key] = errorLike[key];
|
|
@@ -215,6 +465,87 @@ ${section.markdown}`).join("\n")}
|
|
|
215
465
|
return "";
|
|
216
466
|
}
|
|
217
467
|
}
|
|
468
|
+
function getExecutionLogSummary(status, title) {
|
|
469
|
+
if (title) {
|
|
470
|
+
return title;
|
|
471
|
+
}
|
|
472
|
+
switch (status) {
|
|
473
|
+
case "success":
|
|
474
|
+
return "\uB9AC\uBDF0 \uC2E4\uD589\uC774 \uC131\uACF5\uC801\uC73C\uB85C \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.";
|
|
475
|
+
case "failed":
|
|
476
|
+
return "\uB9AC\uBDF0 \uC2E4\uD589 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
|
|
477
|
+
case "partial_failure":
|
|
478
|
+
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.";
|
|
479
|
+
default:
|
|
480
|
+
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.";
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
function formatExecutionDuration(startedAt, finishedAt) {
|
|
484
|
+
const durationMs = Math.max(0, finishedAt.getTime() - startedAt.getTime());
|
|
485
|
+
if (durationMs < 1e3) {
|
|
486
|
+
return `${durationMs}ms`;
|
|
487
|
+
}
|
|
488
|
+
const durationSeconds = durationMs / 1e3;
|
|
489
|
+
if (durationSeconds < 60) {
|
|
490
|
+
return `${durationSeconds.toFixed(1)}s`;
|
|
491
|
+
}
|
|
492
|
+
const minutes = Math.floor(durationSeconds / 60);
|
|
493
|
+
const seconds = Math.round(durationSeconds % 60);
|
|
494
|
+
return `${minutes}m ${seconds}s`;
|
|
495
|
+
}
|
|
496
|
+
function writeExecutionLog(options = {}) {
|
|
497
|
+
try {
|
|
498
|
+
const startedAt = options.startedAt ?? /* @__PURE__ */ new Date();
|
|
499
|
+
const finishedAt = options.finishedAt ?? /* @__PURE__ */ new Date();
|
|
500
|
+
const status = options.status ?? "success";
|
|
501
|
+
helperTrace("execution-log:write:start", options.scope || "unknown");
|
|
502
|
+
createReportDirectory();
|
|
503
|
+
const reportPath = getAvailableFilePath(REPORT_DIR, `${getNowString(finishedAt)}-execution-log`, ".md");
|
|
504
|
+
const traceSnapshot = options.traceMessages ?? getTraceMessages();
|
|
505
|
+
const extraSections = options.extraSections || [];
|
|
506
|
+
const serializedError = options.error ? serializeError(options.error) : null;
|
|
507
|
+
const report = `# Execution Log
|
|
508
|
+
|
|
509
|
+
- \uC2DC\uC791 \uC2DC\uAC01: ${getHumanReadableNowString(startedAt)}
|
|
510
|
+
- \uC885\uB8CC \uC2DC\uAC01: ${getHumanReadableNowString(finishedAt)}
|
|
511
|
+
- \uC2E4\uD589 \uC2DC\uAC04: ${formatExecutionDuration(startedAt, finishedAt)}
|
|
512
|
+
- \uC0C1\uD0DC: \`${status}\`
|
|
513
|
+
- Scope: \`${options.scope || "unknown"}\`
|
|
514
|
+
- \uC791\uC5C5 \uACBD\uB85C: \`${process.cwd()}\`
|
|
515
|
+
- \uC2E4\uD589 \uC778\uC790: \`${JSON.stringify(options.args ?? process.argv.slice(2))}\`
|
|
516
|
+
- \uC2E4\uD589 \uD658\uACBD: \`${process.platform} ${process.arch} / Node ${process.version}\`
|
|
517
|
+
|
|
518
|
+
## Summary
|
|
519
|
+
|
|
520
|
+
${getExecutionLogSummary(status, options.title)}
|
|
521
|
+
${serializedError ? `
|
|
522
|
+
|
|
523
|
+
## Error
|
|
524
|
+
|
|
525
|
+
\`\`\`json
|
|
526
|
+
${JSON.stringify(serializedError, null, 2)}
|
|
527
|
+
\`\`\`` : ""}
|
|
528
|
+
|
|
529
|
+
## Trace
|
|
530
|
+
|
|
531
|
+
\`\`\`json
|
|
532
|
+
${JSON.stringify(traceSnapshot, null, 2)}
|
|
533
|
+
\`\`\`${extraSections.length ? `
|
|
534
|
+
${extraSections.map((section) => `
|
|
535
|
+
## ${section.heading}
|
|
536
|
+
|
|
537
|
+
${section.markdown}`).join("\n")}
|
|
538
|
+
` : "\n"}
|
|
539
|
+
`;
|
|
540
|
+
fs__default.default.writeFileSync(reportPath, report);
|
|
541
|
+
helperTrace("execution-log:write:done", reportPath);
|
|
542
|
+
return reportPath;
|
|
543
|
+
} catch (writeError) {
|
|
544
|
+
console.error("\u26A0\uFE0F \uC2E4\uD589 \uB85C\uADF8 \uD30C\uC77C \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
|
|
545
|
+
console.error(writeError);
|
|
546
|
+
return "";
|
|
547
|
+
}
|
|
548
|
+
}
|
|
218
549
|
function exitWithError(message, options = {}) {
|
|
219
550
|
const reportPath = writeErrorReport(options.error || new Error(message), {
|
|
220
551
|
...options,
|
|
@@ -259,13 +590,67 @@ ${JSON.stringify(AIServices, null, 2)}
|
|
|
259
590
|
}
|
|
260
591
|
);
|
|
261
592
|
}
|
|
262
|
-
function
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
593
|
+
function truncateCommitSubject(subject) {
|
|
594
|
+
if (subject.length <= 72) {
|
|
595
|
+
return subject;
|
|
596
|
+
}
|
|
597
|
+
return `${subject.slice(0, 69)}...`;
|
|
598
|
+
}
|
|
599
|
+
function getRecentCommitOptions() {
|
|
600
|
+
const output = runGitCommand(
|
|
601
|
+
["log", `-${COMMIT_FETCH_LIMIT}`, "--date=relative", "--pretty=format:%h%x09%an%x09%ar%x09%s"],
|
|
602
|
+
{ allowFailure: true }
|
|
603
|
+
);
|
|
604
|
+
if (!output) {
|
|
605
|
+
return [];
|
|
606
|
+
}
|
|
607
|
+
return output.split("\n").map((line) => {
|
|
608
|
+
const [hash = "", author = "", relativeDate = "", ...subjectParts] = line.split(" ");
|
|
609
|
+
const subject = subjectParts.join(" ").trim();
|
|
610
|
+
return {
|
|
611
|
+
author,
|
|
612
|
+
description: `${author} | ${relativeDate}`,
|
|
613
|
+
hash,
|
|
614
|
+
label: `${hash} | ${truncateCommitSubject(subject)}`,
|
|
615
|
+
relativeDate,
|
|
616
|
+
subject
|
|
617
|
+
};
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
function buildSelectedCommitSummary(commits) {
|
|
621
|
+
return commits.map((commit) => `- ${commit.hash} | ${commit.subject} | ${commit.author} | ${commit.relativeDate}`).join("\n");
|
|
622
|
+
}
|
|
623
|
+
function getReviewPathspecArgs() {
|
|
624
|
+
const { includePatterns, excludePatterns } = getGitDiffPathspecs();
|
|
625
|
+
return [...includePatterns, ...excludePatterns];
|
|
626
|
+
}
|
|
627
|
+
function buildSelectedCommitDiff(commits) {
|
|
628
|
+
const reviewPathspecArgs = getReviewPathspecArgs();
|
|
629
|
+
const sections = commits.map((commit) => {
|
|
630
|
+
const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", ...reviewPathspecArgs], {
|
|
631
|
+
allowFailure: true,
|
|
632
|
+
trimOutput: false
|
|
633
|
+
}).trim();
|
|
634
|
+
if (!diff) {
|
|
635
|
+
return "";
|
|
636
|
+
}
|
|
637
|
+
return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
|
|
638
|
+
}).filter(Boolean).join("\n\n");
|
|
639
|
+
if (!sections) {
|
|
640
|
+
return "";
|
|
641
|
+
}
|
|
642
|
+
return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", "# \uB9AC\uBDF0 \uB300\uC0C1 diff", sections].join("\n");
|
|
643
|
+
}
|
|
644
|
+
function getSelectedCommitFiles(commits) {
|
|
645
|
+
const reviewPathspecArgs = getReviewPathspecArgs();
|
|
646
|
+
const files = /* @__PURE__ */ new Set();
|
|
647
|
+
commits.forEach((commit) => {
|
|
648
|
+
const output = runGitCommand(["show", "--pretty=format:", "--name-only", commit.hash, "--", ...reviewPathspecArgs], {
|
|
649
|
+
allowFailure: true
|
|
650
|
+
});
|
|
651
|
+
output.split("\n").map((line) => line.trim()).filter(Boolean).forEach((filePath) => files.add(filePath));
|
|
652
|
+
});
|
|
653
|
+
return [...files];
|
|
269
654
|
}
|
|
270
655
|
function openReport(reportPath) {
|
|
271
656
|
const resolvedPath = path__default.default.resolve(reportPath);
|
|
@@ -316,59 +701,166 @@ function openReport(reportPath) {
|
|
|
316
701
|
helperTrace("open-report:unsupported-platform", platform);
|
|
317
702
|
console.error(`\u26A0\uFE0F \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD50C\uB7AB\uD3FC\uC785\uB2C8\uB2E4: ${platform}`);
|
|
318
703
|
}
|
|
319
|
-
function
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
helperTrace(
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
704
|
+
function ensureInteractiveSelectionAvailable(scope, message) {
|
|
705
|
+
if (process.stdin.isTTY && process.stdout.isTTY && typeof process.stdin.setRawMode === "function") {
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
helperTrace(`${scope}:tty-missing`);
|
|
709
|
+
exitWithError(message, {
|
|
710
|
+
scope: `helper:${scope}`
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
function renderSelectionBlock(lines, previousLineCount) {
|
|
714
|
+
if (previousLineCount > 0) {
|
|
715
|
+
readline__default.default.moveCursor(process.stdout, 0, -previousLineCount);
|
|
716
|
+
readline__default.default.clearScreenDown(process.stdout);
|
|
717
|
+
}
|
|
718
|
+
const fittedLines = fitLinesToTerminal(lines);
|
|
719
|
+
process.stdout.write(`${fittedLines.join("\n")}
|
|
720
|
+
`);
|
|
721
|
+
return fittedLines.length;
|
|
722
|
+
}
|
|
723
|
+
function getSelectionWindowRange(optionCount, selectedIndex, windowSize) {
|
|
724
|
+
if (optionCount <= windowSize) {
|
|
725
|
+
return {
|
|
726
|
+
end: optionCount,
|
|
727
|
+
start: 0
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
const halfWindow = Math.floor(windowSize / 2);
|
|
731
|
+
const maxStart = optionCount - windowSize;
|
|
732
|
+
const start = Math.max(0, Math.min(selectedIndex - halfWindow, maxStart));
|
|
733
|
+
return {
|
|
734
|
+
end: Math.min(optionCount, start + windowSize),
|
|
735
|
+
start
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
function buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize) {
|
|
739
|
+
const { start, end } = getSelectionWindowRange(options.length, selectedIndex, windowSize);
|
|
740
|
+
const lines = [
|
|
741
|
+
`${ANSI.bold}${question}${ANSI.reset}`,
|
|
742
|
+
`${ANSI.dim}\u2191\u2193 \uC774\uB3D9 | Space \uC120\uD0DD/\uD574\uC81C | Enter \uC644\uB8CC | Esc \uCDE8\uC18C${ANSI.reset}`,
|
|
743
|
+
`${ANSI.dim}\uC120\uD0DD\uB428: ${toggled.size}\uAC1C / \uC804\uCCB4: ${options.length}\uAC1C${ANSI.reset}`
|
|
744
|
+
];
|
|
745
|
+
for (let index = start; index < end; index += 1) {
|
|
746
|
+
const option = options[index];
|
|
747
|
+
if (!option) {
|
|
748
|
+
continue;
|
|
333
749
|
}
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
750
|
+
const cursor = index === selectedIndex ? `${ANSI.cyan}>${ANSI.reset}` : " ";
|
|
751
|
+
const checked = toggled.has(index) ? `${ANSI.green}\u2611${ANSI.reset}` : "\u2610";
|
|
752
|
+
const description = option.description ? ` ${ANSI.dim}${option.description}${ANSI.reset}` : "";
|
|
753
|
+
lines.push(`${cursor} ${checked} ${option.label}${description}`);
|
|
754
|
+
}
|
|
755
|
+
if (options.length > windowSize) {
|
|
756
|
+
lines.push(`${ANSI.dim}\uD45C\uC2DC \uBC94\uC704: ${start + 1}-${end} / ${options.length}${ANSI.reset}`);
|
|
757
|
+
}
|
|
758
|
+
return lines;
|
|
759
|
+
}
|
|
760
|
+
async function showMultiSelect(question, options, windowSize = COMMIT_SELECTION_WINDOW) {
|
|
761
|
+
ensureInteractiveSelectionAvailable("showMultiSelect", "\u274C \uCEE4\uBC0B \uC120\uD0DD \uBAA8\uB2EC\uC740 TTY \uD658\uACBD\uC5D0\uC11C\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
762
|
+
let selectedIndex = 0;
|
|
763
|
+
let renderedLineCount = 0;
|
|
764
|
+
const toggled = /* @__PURE__ */ new Set();
|
|
765
|
+
const rl = readline__default.default.createInterface({
|
|
766
|
+
input: process.stdin,
|
|
767
|
+
output: process.stdout,
|
|
768
|
+
terminal: true
|
|
769
|
+
});
|
|
770
|
+
process.stdout.write("\x1B[?25l");
|
|
771
|
+
const cleanup = () => {
|
|
772
|
+
if (renderedLineCount > 0) {
|
|
773
|
+
readline__default.default.moveCursor(process.stdout, 0, -renderedLineCount);
|
|
774
|
+
readline__default.default.clearScreenDown(process.stdout);
|
|
775
|
+
renderedLineCount = 0;
|
|
341
776
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
777
|
+
process.stdin.removeListener("data", onData);
|
|
778
|
+
process.stdin.setRawMode(false);
|
|
779
|
+
process.stdin.pause();
|
|
780
|
+
rl.close();
|
|
781
|
+
process.stdout.write("\x1B[?25h");
|
|
782
|
+
};
|
|
783
|
+
const render = () => {
|
|
784
|
+
const lines = buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize);
|
|
785
|
+
renderedLineCount = renderSelectionBlock(lines, renderedLineCount);
|
|
786
|
+
};
|
|
787
|
+
const confirmSelection = (resolve) => {
|
|
788
|
+
const values = [...toggled].sort((left, right) => left - right).map((index) => options[index]?.value).filter((value) => value !== void 0);
|
|
789
|
+
cleanup();
|
|
790
|
+
resolve(values);
|
|
791
|
+
};
|
|
792
|
+
const cancelSelection = (resolve) => {
|
|
793
|
+
cleanup();
|
|
794
|
+
resolve([]);
|
|
795
|
+
};
|
|
796
|
+
let onData = (_data) => {
|
|
797
|
+
};
|
|
798
|
+
render();
|
|
799
|
+
return new Promise((resolve) => {
|
|
800
|
+
onData = (data) => {
|
|
801
|
+
const key = data.toString();
|
|
802
|
+
if (key === "") {
|
|
803
|
+
cleanup();
|
|
804
|
+
process.exit(0);
|
|
355
805
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
806
|
+
if (key === "\x1B") {
|
|
807
|
+
cancelSelection(resolve);
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
if (key === "\x1B[A") {
|
|
811
|
+
selectedIndex = (selectedIndex - 1 + options.length) % options.length;
|
|
812
|
+
render();
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
if (key === "\x1B[B") {
|
|
816
|
+
selectedIndex = (selectedIndex + 1) % options.length;
|
|
817
|
+
render();
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
if (key === " ") {
|
|
821
|
+
if (toggled.has(selectedIndex)) {
|
|
822
|
+
toggled.delete(selectedIndex);
|
|
823
|
+
} else {
|
|
824
|
+
toggled.add(selectedIndex);
|
|
825
|
+
}
|
|
826
|
+
render();
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
if (key === "\r" || key === "\n") {
|
|
830
|
+
confirmSelection(resolve);
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
process.stdin.setRawMode(true);
|
|
834
|
+
process.stdin.resume();
|
|
835
|
+
process.stdin.on("data", onData);
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
async function selectReviewCommits() {
|
|
839
|
+
const commits = getRecentCommitOptions();
|
|
840
|
+
if (commits.length === 0) {
|
|
841
|
+
console.log("\u2139\uFE0F \uB9AC\uBDF0\uD560 \uCD5C\uADFC \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
842
|
+
return [];
|
|
359
843
|
}
|
|
360
|
-
|
|
361
|
-
|
|
844
|
+
return showMultiSelect(
|
|
845
|
+
"\uB9AC\uBDF0\uD560 \uCEE4\uBC0B\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.",
|
|
846
|
+
commits.map((commit) => ({
|
|
847
|
+
description: commit.description,
|
|
848
|
+
label: commit.label,
|
|
849
|
+
value: commit
|
|
850
|
+
})),
|
|
851
|
+
COMMIT_SELECTION_WINDOW
|
|
852
|
+
);
|
|
362
853
|
}
|
|
363
854
|
async function showSelectionAIService() {
|
|
364
855
|
const selectedServiceFromArgs = parseServiceFromArgs();
|
|
365
856
|
if (selectedServiceFromArgs) {
|
|
366
857
|
helperTrace("show-selection:from-args", selectedServiceFromArgs);
|
|
367
858
|
console.log(`
|
|
368
|
-
\u2705
|
|
859
|
+
\u2705 ${ANSI.green}${selectedServiceFromArgs}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (--service)
|
|
369
860
|
`);
|
|
370
861
|
return selectedServiceFromArgs;
|
|
371
862
|
}
|
|
863
|
+
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.");
|
|
372
864
|
helperTrace("show-selection:interactive:start");
|
|
373
865
|
let selectedIndex = 0;
|
|
374
866
|
const rl = readline__default.default.createInterface({
|
|
@@ -386,11 +878,12 @@ async function showSelectionAIService() {
|
|
|
386
878
|
helperTrace("show-selection:interactive:render", AIServices[selectedIndex] || "unknown");
|
|
387
879
|
readline__default.default.clearScreenDown(process.stdout);
|
|
388
880
|
process.stdout.write(
|
|
389
|
-
|
|
881
|
+
`\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):
|
|
882
|
+
`
|
|
390
883
|
);
|
|
391
884
|
AIServices.forEach((service, index) => {
|
|
392
885
|
if (index === selectedIndex) {
|
|
393
|
-
process.stdout.write(`
|
|
886
|
+
process.stdout.write(` ${ANSI.cyan}>${ANSI.reset} ${ANSI.cyan}\u25C9${ANSI.reset} ${ANSI.bold}${service}${ANSI.reset}
|
|
394
887
|
`);
|
|
395
888
|
} else {
|
|
396
889
|
process.stdout.write(` \u25EF ${service}
|
|
@@ -420,7 +913,7 @@ async function showSelectionAIService() {
|
|
|
420
913
|
rl.close();
|
|
421
914
|
process.stdout.write("\x1B[?25h");
|
|
422
915
|
console.log(`
|
|
423
|
-
\u2705
|
|
916
|
+
\u2705 ${ANSI.green}${AIServices[selectedIndex]}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
|
|
424
917
|
`);
|
|
425
918
|
const result = AIServices[selectedIndex];
|
|
426
919
|
if (result) {
|
|
@@ -440,6 +933,13 @@ var ALLOWED_REASONING_EFFORTS = ["minimal", "low", "medium", "high"];
|
|
|
440
933
|
function shellQuote(value) {
|
|
441
934
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
442
935
|
}
|
|
936
|
+
function toShellOptionToken(value) {
|
|
937
|
+
const SIMPLE_SHELL_TOKEN_PATTERN = /^[A-Za-z0-9._:/=-]+$/;
|
|
938
|
+
if (SIMPLE_SHELL_TOKEN_PATTERN.test(value)) {
|
|
939
|
+
return value;
|
|
940
|
+
}
|
|
941
|
+
return shellQuote(value);
|
|
942
|
+
}
|
|
443
943
|
function getArgValue(flag) {
|
|
444
944
|
const index = args.indexOf(flag);
|
|
445
945
|
if (index === -1 || !args[index + 1]) {
|
|
@@ -447,6 +947,11 @@ function getArgValue(flag) {
|
|
|
447
947
|
}
|
|
448
948
|
return args[index + 1];
|
|
449
949
|
}
|
|
950
|
+
function printNotice(message) {
|
|
951
|
+
if (args.includes("--test")) {
|
|
952
|
+
console.warn(message);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
450
955
|
function toUnique(values) {
|
|
451
956
|
const seen = /* @__PURE__ */ new Set();
|
|
452
957
|
return values.filter((value) => {
|
|
@@ -470,11 +975,11 @@ function resolveReasoningEffort() {
|
|
|
470
975
|
const normalized = normalizeEffort(customReasoningEffort);
|
|
471
976
|
trace("reasoning:custom", `${customReasoningEffort} -> ${normalized}`);
|
|
472
977
|
if (customReasoningEffort === "minimal") {
|
|
473
|
-
|
|
978
|
+
printNotice("\u26A0\uFE0F Claude\uB294 minimal\uC744 \uC9C1\uC811 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC544 low\uB85C \uB9E4\uD551\uD569\uB2C8\uB2E4.");
|
|
474
979
|
}
|
|
475
980
|
return normalized;
|
|
476
981
|
}
|
|
477
|
-
|
|
982
|
+
printNotice(
|
|
478
983
|
`\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS.join(
|
|
479
984
|
", "
|
|
480
985
|
)}`
|
|
@@ -514,10 +1019,10 @@ function getAliasFallbacks(primaryAlias) {
|
|
|
514
1019
|
}
|
|
515
1020
|
function buildClaudeExecCommand(options) {
|
|
516
1021
|
const { tempDiffPath: tempDiffPath2, prompt, systemPromptFiles, effort, model, fallbackModel } = options;
|
|
517
|
-
const modelOption = model ? `--model ${
|
|
518
|
-
const fallbackOption = model && fallbackModel ? `--fallback-model ${
|
|
519
|
-
const effortOption = `--effort ${
|
|
520
|
-
const appendedPromptFiles = systemPromptFiles.map((
|
|
1022
|
+
const modelOption = model ? `--model ${toShellOptionToken(model)}` : "";
|
|
1023
|
+
const fallbackOption = model && fallbackModel ? `--fallback-model ${toShellOptionToken(fallbackModel)}` : "";
|
|
1024
|
+
const effortOption = `--effort ${toShellOptionToken(effort)}`;
|
|
1025
|
+
const appendedPromptFiles = systemPromptFiles.map((path3) => `--append-system-prompt-file ${shellQuote(path3)}`).join(" ");
|
|
521
1026
|
return `cat ${shellQuote(tempDiffPath2)} | claude ${[
|
|
522
1027
|
modelOption,
|
|
523
1028
|
fallbackOption,
|
|
@@ -550,13 +1055,13 @@ var createClaudeCommand = (tempDiffPath2, reviewFormPath2) => {
|
|
|
550
1055
|
trace("model:candidates", modelCandidates.join(", "));
|
|
551
1056
|
trace("command:candidates:count", String(modelCandidates.length + 1));
|
|
552
1057
|
if (customModel) {
|
|
553
|
-
|
|
1058
|
+
printNotice(
|
|
554
1059
|
`\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
|
|
555
1060
|
" -> "
|
|
556
1061
|
)}) \uBC0F \uAE30\uBCF8 \uBAA8\uB378 \uC21C\uC73C\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
|
|
557
1062
|
);
|
|
558
1063
|
} else {
|
|
559
|
-
|
|
1064
|
+
printNotice(
|
|
560
1065
|
`\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.`
|
|
561
1066
|
);
|
|
562
1067
|
}
|
|
@@ -636,22 +1141,37 @@ function getArgValue2(flag) {
|
|
|
636
1141
|
}
|
|
637
1142
|
return args2[index + 1];
|
|
638
1143
|
}
|
|
1144
|
+
function printNotice2(message) {
|
|
1145
|
+
if (args2.includes("--test")) {
|
|
1146
|
+
console.warn(message);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
function normalizeEffort2(level) {
|
|
1150
|
+
if (level === "minimal") {
|
|
1151
|
+
return "low";
|
|
1152
|
+
}
|
|
1153
|
+
return level;
|
|
1154
|
+
}
|
|
639
1155
|
function resolveReasoningEffort2() {
|
|
640
1156
|
const customReasoningEffort = getArgValue2("--reasoning-effort");
|
|
641
1157
|
if (customReasoningEffort) {
|
|
642
1158
|
if (ALLOWED_REASONING_EFFORTS2.includes(customReasoningEffort)) {
|
|
643
|
-
|
|
644
|
-
|
|
1159
|
+
const normalized = normalizeEffort2(customReasoningEffort);
|
|
1160
|
+
trace3("reasoning:custom", `${customReasoningEffort} -> ${normalized}`);
|
|
1161
|
+
if (customReasoningEffort === "minimal") {
|
|
1162
|
+
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.");
|
|
1163
|
+
}
|
|
1164
|
+
return normalized;
|
|
645
1165
|
}
|
|
646
|
-
|
|
1166
|
+
printNotice2(
|
|
647
1167
|
`\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS2.join(
|
|
648
1168
|
", "
|
|
649
1169
|
)}`
|
|
650
1170
|
);
|
|
651
1171
|
}
|
|
652
1172
|
if (args2.includes("--flash")) {
|
|
653
|
-
trace3("reasoning:flash-default", "
|
|
654
|
-
return "
|
|
1173
|
+
trace3("reasoning:flash-default", "low");
|
|
1174
|
+
return "low";
|
|
655
1175
|
}
|
|
656
1176
|
if (args2.includes("--review")) {
|
|
657
1177
|
trace3("reasoning:review-default", "high");
|
|
@@ -687,14 +1207,14 @@ ${reviewFormLine || "- (\uC5C6\uC74C)"}
|
|
|
687
1207
|
trace3("prompt:prepared", `length=${prompt.length}`);
|
|
688
1208
|
let command = "";
|
|
689
1209
|
if (customModel) {
|
|
690
|
-
|
|
1210
|
+
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.");
|
|
691
1211
|
trace3("model:custom", customModel);
|
|
692
1212
|
command = buildCodexExecCommand(prompt, reasoningEffort, customModel);
|
|
693
1213
|
} else {
|
|
694
1214
|
const preferredModelAlias = "gpt-5";
|
|
695
1215
|
const aliasCommand = buildCodexExecCommand(prompt, reasoningEffort, preferredModelAlias);
|
|
696
1216
|
const fallbackCommand = buildCodexExecCommand(prompt, reasoningEffort);
|
|
697
|
-
|
|
1217
|
+
printNotice2(
|
|
698
1218
|
`\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.`
|
|
699
1219
|
);
|
|
700
1220
|
trace3("model:alias-first", preferredModelAlias);
|
|
@@ -755,6 +1275,11 @@ function getArgValue3(flag) {
|
|
|
755
1275
|
}
|
|
756
1276
|
return args3[index + 1];
|
|
757
1277
|
}
|
|
1278
|
+
function printNotice3(message) {
|
|
1279
|
+
if (args3.includes("--test")) {
|
|
1280
|
+
console.warn(message);
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
758
1283
|
function toUnique2(values) {
|
|
759
1284
|
const seen = /* @__PURE__ */ new Set();
|
|
760
1285
|
return values.filter((value) => {
|
|
@@ -772,7 +1297,7 @@ function resolveReasoningEffort3() {
|
|
|
772
1297
|
trace5("reasoning:custom", customReasoningEffort);
|
|
773
1298
|
return customReasoningEffort;
|
|
774
1299
|
}
|
|
775
|
-
|
|
1300
|
+
printNotice3(
|
|
776
1301
|
`\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS3.join(
|
|
777
1302
|
", "
|
|
778
1303
|
)}`
|
|
@@ -834,38 +1359,57 @@ function buildGeminiExecCommand(prompt, model) {
|
|
|
834
1359
|
const modelOption = model ? `--model ${shellQuote3(model)}` : "";
|
|
835
1360
|
return `gemini ${[modelOption, "-p", shellQuote3(prompt)].filter(Boolean).join(" ")}`;
|
|
836
1361
|
}
|
|
1362
|
+
function toGeminiFileReference(filePath) {
|
|
1363
|
+
return `@${filePath}`;
|
|
1364
|
+
}
|
|
1365
|
+
function buildGeminiFileReferenceSection(files) {
|
|
1366
|
+
const existingFiles = files.filter((file) => fs__default.default.existsSync(file.path));
|
|
1367
|
+
return {
|
|
1368
|
+
count: existingFiles.length,
|
|
1369
|
+
items: existingFiles.map((file) => `${file.display} ${toGeminiFileReference(file.path)}`)
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
837
1372
|
var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
|
|
838
1373
|
trace5("createGeminiCommand:start", `tempDiffPath=${tempDiffPath2}, reviewFormPath=${reviewFormPath2}`);
|
|
839
1374
|
const customModel = getArgValue3("--model");
|
|
840
1375
|
const reasoningEffort = resolveReasoningEffort3();
|
|
841
1376
|
const primaryAlias = resolvePrimaryAlias2(reasoningEffort);
|
|
842
1377
|
const aliasFallbacks = toUnique2(getAliasFallbacks2(primaryAlias));
|
|
1378
|
+
const resolvedTempDiffPath = path__default.default.resolve(tempDiffPath2);
|
|
1379
|
+
const resolvedReviewFormPath = path__default.default.resolve(reviewFormPath2);
|
|
843
1380
|
const rules = [
|
|
844
1381
|
{ path: rulesPath, display: "\uB8F0\uC14B" },
|
|
845
1382
|
{ path: namingRulesPath, display: "\uB124\uC774\uBC0D \uADDC\uCE59" },
|
|
846
1383
|
{ path: codingConventionRulesPath, display: "\uCF54\uB529 \uCEE8\uBCA4\uC158" }
|
|
847
1384
|
];
|
|
848
|
-
const
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
const
|
|
852
|
-
trace5("reviewForm:status",
|
|
1385
|
+
const ruleSection = buildGeminiFileReferenceSection(rules);
|
|
1386
|
+
trace5("rules:loaded", `count=${ruleSection.count}`);
|
|
1387
|
+
const reviewFormExists = fs__default.default.existsSync(resolvedReviewFormPath);
|
|
1388
|
+
const reviewFormText = reviewFormExists ? `\uB9AC\uBDF0 \uC591\uC2DD ${toGeminiFileReference(resolvedReviewFormPath)}` : "\uB9AC\uBDF0 \uC591\uC2DD (\uC5C6\uC74C)";
|
|
1389
|
+
trace5("reviewForm:status", reviewFormExists ? "exists" : "missing");
|
|
853
1390
|
const reasoningInstruction = getReasoningInstruction(reasoningEffort);
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
1391
|
+
const ruleText = ruleSection.items.join(" ") || "(\uC5C6\uC74C)";
|
|
1392
|
+
const diffText = `\uB9AC\uBDF0 \uB300\uC0C1 diff ${toGeminiFileReference(resolvedTempDiffPath)}`;
|
|
1393
|
+
const prompt = [
|
|
1394
|
+
"\uC544\uB798 \uD30C\uC77C\uB4E4\uC744 \uC21C\uC11C\uB300\uB85C \uC77D\uACE0 \uCF54\uB4DC \uB9AC\uBDF0\uB97C \uC9C4\uD589\uD574\uC918.",
|
|
1395
|
+
`\uADDC\uCE59 \uD30C\uC77C: ${ruleText}`,
|
|
1396
|
+
`\uB9AC\uBDF0 \uC591\uC2DD \uD30C\uC77C: ${reviewFormText}`,
|
|
1397
|
+
`\uB9AC\uBDF0 \uB300\uC0C1 diff \uD30C\uC77C: ${diffText}`,
|
|
1398
|
+
"\uBC18\uB4DC\uC2DC \uB9AC\uBDF0 \uC591\uC2DD\uC5D0 \uB9DE\uCDB0 \uC791\uC131\uD574\uC918.",
|
|
1399
|
+
`\uCD94\uB860 \uAC15\uB3C4 \uC9C0\uCE68: ${reasoningInstruction}`
|
|
1400
|
+
].join(" ");
|
|
857
1401
|
trace5("prompt:prepared", `length=${prompt.length}`);
|
|
858
1402
|
const modelCandidates = toUnique2(customModel ? [customModel, ...aliasFallbacks] : aliasFallbacks);
|
|
859
1403
|
trace5("model:candidates", modelCandidates.join(", "));
|
|
860
1404
|
trace5("command:candidates:count", String(modelCandidates.length + 1));
|
|
861
1405
|
if (customModel) {
|
|
862
|
-
|
|
1406
|
+
printNotice3(
|
|
863
1407
|
`\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
|
|
864
1408
|
" -> "
|
|
865
1409
|
)}) \uBC0F \uAE30\uBCF8 \uBAA8\uB378 \uC21C\uC73C\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
|
|
866
1410
|
);
|
|
867
1411
|
} else {
|
|
868
|
-
|
|
1412
|
+
printNotice3(
|
|
869
1413
|
`\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.`
|
|
870
1414
|
);
|
|
871
1415
|
}
|
|
@@ -916,14 +1460,24 @@ function checkGeminiCliInstalled() {
|
|
|
916
1460
|
// src/pr-review/review.ts
|
|
917
1461
|
async function main() {
|
|
918
1462
|
const args4 = process.argv.slice(2);
|
|
1463
|
+
const startedAt = /* @__PURE__ */ new Date();
|
|
919
1464
|
clearTraceMessages();
|
|
920
1465
|
const isTest = isTestMode(args4);
|
|
1466
|
+
const shouldStreamOutput = shouldStreamAIOutput(args4);
|
|
921
1467
|
const trace7 = createTraceLogger("review", args4);
|
|
922
1468
|
trace7("main:start", `args=${JSON.stringify(args4)}`);
|
|
923
1469
|
let command = "";
|
|
924
1470
|
let savedDiffPath = "";
|
|
925
1471
|
let savedReportPath = "";
|
|
926
1472
|
let service = "";
|
|
1473
|
+
let selectedCommitSummary = "";
|
|
1474
|
+
let reviewTargetFiles = [];
|
|
1475
|
+
let executionLogPath = "";
|
|
1476
|
+
let executionStatus = "cancelled";
|
|
1477
|
+
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.";
|
|
1478
|
+
let executionError = null;
|
|
1479
|
+
let resultLength = 0;
|
|
1480
|
+
let exitCode = 0;
|
|
927
1481
|
try {
|
|
928
1482
|
trace7("service-selection:start");
|
|
929
1483
|
service = await showSelectionAIService();
|
|
@@ -947,30 +1501,39 @@ async function main() {
|
|
|
947
1501
|
}
|
|
948
1502
|
trace7("review-flow:start");
|
|
949
1503
|
console.log("\u{1F680} AI Code Review\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...");
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
trace7("report-dir:create:start");
|
|
953
|
-
createReportDirectory();
|
|
954
|
-
trace7("report-dir:create:done");
|
|
955
|
-
trace7("diff-args:build:start");
|
|
956
|
-
const diffArgs = getDiffArgs();
|
|
957
|
-
trace7("diff-args:build:done", `diffArgs=${diffArgs || "(default)"}`);
|
|
958
|
-
let diff = "";
|
|
959
|
-
const { includeParams, excludeParams } = getGitDiffFilter();
|
|
960
|
-
trace7("diff-filter:loaded", `include=${includeParams} | exclude=${excludeParams}`);
|
|
961
|
-
try {
|
|
962
|
-
trace7("git-diff:run");
|
|
963
|
-
diff = child_process.execSync(`git diff ${diffArgs} -- ${includeParams} ${excludeParams}`).toString();
|
|
964
|
-
trace7("git-diff:done", `length=${diff.length}`);
|
|
965
|
-
} catch (error) {
|
|
966
|
-
trace7("git-diff:error", getErrorSummary(error));
|
|
1504
|
+
if (shouldStreamOutput) {
|
|
1505
|
+
console.log("\u2139\uFE0F AI \uC2E4\uC2DC\uAC04 \uC751\uB2F5 \uD45C\uC2DC \uBAA8\uB4DC\uAC00 \uD65C\uC131\uD654\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
967
1506
|
}
|
|
1507
|
+
trace7("commit-selection:start");
|
|
1508
|
+
const selectedCommits = await selectReviewCommits();
|
|
1509
|
+
trace7("commit-selection:done", `count=${selectedCommits.length}`);
|
|
1510
|
+
if (selectedCommits.length === 0) {
|
|
1511
|
+
trace7("commit-selection:empty");
|
|
1512
|
+
executionTitle = "\uC120\uD0DD\uB41C \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.";
|
|
1513
|
+
console.log("\u2139\uFE0F \uC120\uD0DD\uB41C \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1514
|
+
deleteTempDiff();
|
|
1515
|
+
return;
|
|
1516
|
+
}
|
|
1517
|
+
selectedCommitSummary = buildSelectedCommitSummary(selectedCommits);
|
|
1518
|
+
trace7("commit-summary:prepared", selectedCommitSummary);
|
|
1519
|
+
reviewTargetFiles = getSelectedCommitFiles(selectedCommits);
|
|
1520
|
+
console.log(`\u{1F4C2} \uB9AC\uBDF0 \uB300\uC0C1 \uD30C\uC77C(${reviewTargetFiles.length}\uAC1C): ${formatReviewTargetFiles(reviewTargetFiles)}`);
|
|
1521
|
+
console.log("\u23F3 \uC120\uD0DD\uD55C \uCEE4\uBC0B diff\uB97C \uC815\uB9AC\uD558\uB294 \uC911\uC785\uB2C8\uB2E4...");
|
|
1522
|
+
trace7("git-diff:build:start");
|
|
1523
|
+
const diff = buildSelectedCommitDiff(selectedCommits);
|
|
1524
|
+
trace7("git-diff:build:done", `length=${diff.length}`);
|
|
968
1525
|
if (!diff.trim() && !isTest) {
|
|
969
1526
|
trace7("empty-diff:exit");
|
|
970
|
-
|
|
1527
|
+
executionTitle = "\uC120\uD0DD\uD55C \uCEE4\uBC0B\uC5D0\uC11C \uB9AC\uBDF0 \uB300\uC0C1 \uD30C\uC77C \uBCC0\uACBD\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.";
|
|
1528
|
+
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.");
|
|
971
1529
|
deleteTempDiff();
|
|
972
|
-
|
|
1530
|
+
return;
|
|
973
1531
|
}
|
|
1532
|
+
const nowStr = getNowString();
|
|
1533
|
+
trace7("timestamp:created", nowStr);
|
|
1534
|
+
trace7("report-dir:create:start");
|
|
1535
|
+
createReportDirectory();
|
|
1536
|
+
trace7("report-dir:create:done");
|
|
974
1537
|
trace7("temp-diff:write:start", tempDiffPath);
|
|
975
1538
|
fs__default.default.writeFileSync(tempDiffPath, diff);
|
|
976
1539
|
trace7("temp-diff:write:done");
|
|
@@ -992,9 +1555,12 @@ async function main() {
|
|
|
992
1555
|
}
|
|
993
1556
|
trace7("command:create:done");
|
|
994
1557
|
trace7("command:exec:start");
|
|
995
|
-
const result =
|
|
1558
|
+
const result = (await executeShellCommandWithProgress(command, {
|
|
1559
|
+
progressMessage: `\u23F3 [\uB9AC\uBDF0 \uC9C4\uD589] ${service} | \uB300\uC0C1 ${reviewTargetFiles.length}\uAC1C \uD30C\uC77C`,
|
|
1560
|
+
streamOutput: isTest || shouldStreamOutput
|
|
1561
|
+
})).stdout;
|
|
1562
|
+
resultLength = result.length;
|
|
996
1563
|
trace7("command:exec:done", `resultLength=${result.length}`);
|
|
997
|
-
console.log(result);
|
|
998
1564
|
trace7("report:write:start");
|
|
999
1565
|
savedReportPath = getNextFilePath(REPORT_DIR, nowStr, ".md");
|
|
1000
1566
|
fs__default.default.writeFileSync(savedReportPath, result);
|
|
@@ -1019,9 +1585,15 @@ ${command}`);
|
|
|
1019
1585
|
deleteTempDiff();
|
|
1020
1586
|
trace7("cleanup-temp-diff:done");
|
|
1021
1587
|
trace7("review-flow:end");
|
|
1588
|
+
executionStatus = "success";
|
|
1589
|
+
executionTitle = "\uB9AC\uBDF0\uAC00 \uC131\uACF5\uC801\uC73C\uB85C \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.";
|
|
1022
1590
|
} catch (error) {
|
|
1023
1591
|
trace7("review-flow:catch", getErrorSummary(error));
|
|
1024
1592
|
let errorReportPath = "";
|
|
1593
|
+
executionStatus = "failed";
|
|
1594
|
+
executionTitle = "\uB9AC\uBDF0 \uC2E4\uD589 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
|
|
1595
|
+
executionError = error;
|
|
1596
|
+
exitCode = 1;
|
|
1025
1597
|
trace7("cleanup-temp-diff:start(catch)");
|
|
1026
1598
|
try {
|
|
1027
1599
|
deleteTempDiff();
|
|
@@ -1042,6 +1614,8 @@ ${command}`);
|
|
|
1042
1614
|
${JSON.stringify(
|
|
1043
1615
|
{
|
|
1044
1616
|
service,
|
|
1617
|
+
selectedCommitSummary: selectedCommitSummary || null,
|
|
1618
|
+
reviewTargetFiles,
|
|
1045
1619
|
command: command || null,
|
|
1046
1620
|
tempDiffPath,
|
|
1047
1621
|
savedDiffPath: savedDiffPath || null,
|
|
@@ -1059,7 +1633,50 @@ ${JSON.stringify(
|
|
|
1059
1633
|
if (errorReportPath) {
|
|
1060
1634
|
console.error(`\u{1F4C4} \uC5D0\uB7EC \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${errorReportPath}`);
|
|
1061
1635
|
}
|
|
1062
|
-
|
|
1636
|
+
} finally {
|
|
1637
|
+
executionLogPath = writeExecutionLog({
|
|
1638
|
+
scope: "review",
|
|
1639
|
+
status: executionStatus,
|
|
1640
|
+
title: executionTitle,
|
|
1641
|
+
args: args4,
|
|
1642
|
+
startedAt,
|
|
1643
|
+
error: executionError,
|
|
1644
|
+
extraSections: [
|
|
1645
|
+
{
|
|
1646
|
+
heading: "Execution Context",
|
|
1647
|
+
markdown: `\`\`\`json
|
|
1648
|
+
${JSON.stringify(
|
|
1649
|
+
{
|
|
1650
|
+
service: service || null,
|
|
1651
|
+
selectedCommitSummary: selectedCommitSummary || null,
|
|
1652
|
+
reviewTargetFiles,
|
|
1653
|
+
command: command || null,
|
|
1654
|
+
tempDiffPath,
|
|
1655
|
+
savedDiffPath: savedDiffPath || null,
|
|
1656
|
+
savedReportPath: savedReportPath || null,
|
|
1657
|
+
shouldStreamOutput,
|
|
1658
|
+
resultLength
|
|
1659
|
+
},
|
|
1660
|
+
null,
|
|
1661
|
+
2
|
|
1662
|
+
)}
|
|
1663
|
+
\`\`\``
|
|
1664
|
+
},
|
|
1665
|
+
{
|
|
1666
|
+
heading: "Generated Command",
|
|
1667
|
+
markdown: command ? `\`\`\`sh
|
|
1668
|
+
${command}
|
|
1669
|
+
\`\`\`` : "(\uC5C6\uC74C)"
|
|
1670
|
+
}
|
|
1671
|
+
]
|
|
1672
|
+
});
|
|
1673
|
+
if (executionLogPath) {
|
|
1674
|
+
const writeLog = executionStatus === "failed" ? console.error : console.log;
|
|
1675
|
+
writeLog(`\u{1F4DD} \uC2E4\uD589 \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${executionLogPath}`);
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
if (exitCode !== 0) {
|
|
1679
|
+
process.exit(exitCode);
|
|
1063
1680
|
}
|
|
1064
1681
|
}
|
|
1065
1682
|
main();
|