sales-frontend-gemini-cli 0.4.2 → 0.4.3
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 +464 -52
- package/dist/common/helper.cjs.map +1 -1
- package/dist/common/helper.d.cts +85 -3
- package/dist/common/helper.d.ts +85 -3
- package/dist/common/helper.js +455 -53
- 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 +50 -9
- package/dist/pr-review/claude/claude-commander.cjs.map +1 -1
- package/dist/pr-review/claude/claude-commander.js +50 -9
- package/dist/pr-review/claude/claude-commander.js.map +1 -1
- package/dist/pr-review/claude/installation-claude.cjs +41 -5
- package/dist/pr-review/claude/installation-claude.cjs.map +1 -1
- package/dist/pr-review/claude/installation-claude.js +41 -5
- package/dist/pr-review/claude/installation-claude.js.map +1 -1
- package/dist/pr-review/codex/codex-commander.cjs +49 -8
- package/dist/pr-review/codex/codex-commander.cjs.map +1 -1
- package/dist/pr-review/codex/codex-commander.js +49 -8
- package/dist/pr-review/codex/codex-commander.js.map +1 -1
- package/dist/pr-review/codex/installation-codex.cjs +41 -5
- package/dist/pr-review/codex/installation-codex.cjs.map +1 -1
- package/dist/pr-review/codex/installation-codex.js +41 -5
- package/dist/pr-review/codex/installation-codex.js.map +1 -1
- package/dist/pr-review/gemini/gemini-commander.cjs +75 -15
- package/dist/pr-review/gemini/gemini-commander.cjs.map +1 -1
- package/dist/pr-review/gemini/gemini-commander.js +75 -15
- package/dist/pr-review/gemini/gemini-commander.js.map +1 -1
- package/dist/pr-review/gemini/installation-gemini.cjs +41 -5
- package/dist/pr-review/gemini/installation-gemini.cjs.map +1 -1
- package/dist/pr-review/gemini/installation-gemini.js +41 -5
- package/dist/pr-review/gemini/installation-gemini.js.map +1 -1
- package/dist/pr-review/review-one-by-one.cjs +489 -95
- package/dist/pr-review/review-one-by-one.cjs.map +1 -1
- package/dist/pr-review/review-one-by-one.js +490 -96
- package/dist/pr-review/review-one-by-one.js.map +1 -1
- package/dist/pr-review/review.cjs +517 -94
- package/dist/pr-review/review.cjs.map +1 -1
- package/dist/pr-review/review.js +517 -94
- 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",
|
|
@@ -48,6 +86,185 @@ function clearTraceMessages() {
|
|
|
48
86
|
function getTraceMessages() {
|
|
49
87
|
return [...traceMessages];
|
|
50
88
|
}
|
|
89
|
+
var ANSI = {
|
|
90
|
+
bold: "\x1B[1m",
|
|
91
|
+
cyan: "\x1B[36m",
|
|
92
|
+
dim: "\x1B[2m",
|
|
93
|
+
green: "\x1B[32m",
|
|
94
|
+
reset: "\x1B[0m",
|
|
95
|
+
yellow: "\x1B[33m"
|
|
96
|
+
};
|
|
97
|
+
var ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
98
|
+
var COMBINING_MARK_PATTERN = /\p{Mark}/u;
|
|
99
|
+
var GRAPHEME_SEGMENTER = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter("ko", { granularity: "grapheme" }) : null;
|
|
100
|
+
function getGitDiffPathspecs() {
|
|
101
|
+
return {
|
|
102
|
+
excludePatterns: ignoreList.map((item) => `:(exclude)${item}`),
|
|
103
|
+
includePatterns: ["*.ts", "*.tsx", "*.js", "*.jsx"]
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function segmentGraphemes(value) {
|
|
107
|
+
if (!GRAPHEME_SEGMENTER) {
|
|
108
|
+
return [...value];
|
|
109
|
+
}
|
|
110
|
+
return [...GRAPHEME_SEGMENTER.segment(value)].map(({ segment }) => segment);
|
|
111
|
+
}
|
|
112
|
+
function isWideCodePoint(codePoint) {
|
|
113
|
+
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);
|
|
114
|
+
}
|
|
115
|
+
function isEmojiCodePoint(codePoint) {
|
|
116
|
+
return codePoint >= 127462 && codePoint <= 127487 || codePoint >= 127744 && codePoint <= 129791 || codePoint >= 9728 && codePoint <= 10175;
|
|
117
|
+
}
|
|
118
|
+
function getGraphemeWidth(grapheme) {
|
|
119
|
+
let width = 0;
|
|
120
|
+
for (const character of grapheme) {
|
|
121
|
+
const codePoint = character.codePointAt(0);
|
|
122
|
+
if (!codePoint || COMBINING_MARK_PATTERN.test(character) || codePoint === 8205) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (codePoint >= 65024 && codePoint <= 65039 || codePoint >= 917760 && codePoint <= 917999) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (isWideCodePoint(codePoint) || isEmojiCodePoint(codePoint)) {
|
|
129
|
+
width = Math.max(width, 2);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
width = Math.max(width, 1);
|
|
133
|
+
}
|
|
134
|
+
return width;
|
|
135
|
+
}
|
|
136
|
+
function tokenizePlainText(value) {
|
|
137
|
+
return segmentGraphemes(value).map((segment) => ({
|
|
138
|
+
value: segment,
|
|
139
|
+
visibleWidth: getGraphemeWidth(segment)
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
function tokenizeVisibleText(value) {
|
|
143
|
+
const tokens = [];
|
|
144
|
+
let lastIndex = 0;
|
|
145
|
+
for (const match of value.matchAll(ANSI_PATTERN)) {
|
|
146
|
+
const index = match.index ?? 0;
|
|
147
|
+
if (index > lastIndex) {
|
|
148
|
+
tokens.push(...tokenizePlainText(value.slice(lastIndex, index)));
|
|
149
|
+
}
|
|
150
|
+
tokens.push({
|
|
151
|
+
value: match[0],
|
|
152
|
+
visibleWidth: 0
|
|
153
|
+
});
|
|
154
|
+
lastIndex = index + match[0].length;
|
|
155
|
+
}
|
|
156
|
+
if (lastIndex < value.length) {
|
|
157
|
+
tokens.push(...tokenizePlainText(value.slice(lastIndex)));
|
|
158
|
+
}
|
|
159
|
+
return tokens;
|
|
160
|
+
}
|
|
161
|
+
function truncateLineForTerminal(value, maxWidth) {
|
|
162
|
+
if (maxWidth <= 0) {
|
|
163
|
+
return "";
|
|
164
|
+
}
|
|
165
|
+
const tokens = tokenizeVisibleText(value);
|
|
166
|
+
const totalWidth = tokens.reduce((sum, token) => sum + token.visibleWidth, 0);
|
|
167
|
+
if (totalWidth <= maxWidth) {
|
|
168
|
+
return value;
|
|
169
|
+
}
|
|
170
|
+
const ellipsis = "...";
|
|
171
|
+
const ellipsisWidth = 3;
|
|
172
|
+
const targetWidth = Math.max(0, maxWidth - ellipsisWidth);
|
|
173
|
+
let usedWidth = 0;
|
|
174
|
+
let result = "";
|
|
175
|
+
for (const token of tokens) {
|
|
176
|
+
if (token.visibleWidth === 0) {
|
|
177
|
+
result += token.value;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (usedWidth + token.visibleWidth > targetWidth) {
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
result += token.value;
|
|
184
|
+
usedWidth += token.visibleWidth;
|
|
185
|
+
}
|
|
186
|
+
return `${result}${ellipsis}${ANSI.reset}`;
|
|
187
|
+
}
|
|
188
|
+
function fitLinesToTerminal(lines) {
|
|
189
|
+
const maxWidth = Math.max(20, (process.stdout.columns || 120) - 1);
|
|
190
|
+
return lines.map((line) => truncateLineForTerminal(line, maxWidth));
|
|
191
|
+
}
|
|
192
|
+
function runGitCommand(args4, options = {}) {
|
|
193
|
+
const { allowFailure = false, trimOutput = true } = options;
|
|
194
|
+
try {
|
|
195
|
+
const output = child_process.execFileSync("git", args4, {
|
|
196
|
+
encoding: "utf8",
|
|
197
|
+
maxBuffer: 1024 * 1024 * 20,
|
|
198
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
199
|
+
});
|
|
200
|
+
return trimOutput ? output.trim() : output;
|
|
201
|
+
} catch (error) {
|
|
202
|
+
helperTrace("git-command:failed", `${args4.join(" ")} | ${getErrorSummary(error)}`);
|
|
203
|
+
if (allowFailure) {
|
|
204
|
+
return "";
|
|
205
|
+
}
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async function executeShellCommandWithProgress(command, options = {}) {
|
|
210
|
+
const { progressIntervalMs = 1e4, progressMessage = "\u23F3 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uB294 \uC911\uC785\uB2C8\uB2E4...", streamOutput = false } = options;
|
|
211
|
+
const { spawn } = await import('child_process');
|
|
212
|
+
return new Promise((resolve, reject) => {
|
|
213
|
+
let stdout = "";
|
|
214
|
+
let stderr = "";
|
|
215
|
+
const startedAt = Date.now();
|
|
216
|
+
console.log(progressMessage);
|
|
217
|
+
const child = spawn("/bin/zsh", ["-lc", command], {
|
|
218
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
219
|
+
});
|
|
220
|
+
const progressTimer = setInterval(() => {
|
|
221
|
+
const elapsedSeconds = Math.max(1, Math.floor((Date.now() - startedAt) / 1e3));
|
|
222
|
+
console.log(`${progressMessage} (${elapsedSeconds}s \uACBD\uACFC)`);
|
|
223
|
+
}, progressIntervalMs);
|
|
224
|
+
child.stdout.on("data", (chunk) => {
|
|
225
|
+
const text = chunk.toString();
|
|
226
|
+
stdout += text;
|
|
227
|
+
if (streamOutput) {
|
|
228
|
+
process.stdout.write(text);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
child.stderr.on("data", (chunk) => {
|
|
232
|
+
const text = chunk.toString();
|
|
233
|
+
stderr += text;
|
|
234
|
+
if (streamOutput) {
|
|
235
|
+
process.stderr.write(text);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
child.on("error", (error) => {
|
|
239
|
+
clearInterval(progressTimer);
|
|
240
|
+
reject(error);
|
|
241
|
+
});
|
|
242
|
+
child.on("close", (code, signal) => {
|
|
243
|
+
clearInterval(progressTimer);
|
|
244
|
+
if (code === 0) {
|
|
245
|
+
resolve({
|
|
246
|
+
stderr,
|
|
247
|
+
stdout
|
|
248
|
+
});
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const exitSummary = signal ? `signal=${signal}` : `code=${String(code ?? "unknown")}`;
|
|
252
|
+
reject(new Error(`\uC258 \uBA85\uB839 \uC2E4\uD589 \uC2E4\uD328 (${exitSummary})${stderr.trim() ? `
|
|
253
|
+
${stderr.trim()}` : ""}`));
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
function formatReviewTargetFiles(files, visibleCount = 5) {
|
|
258
|
+
if (files.length === 0) {
|
|
259
|
+
return "(\uC5C6\uC74C)";
|
|
260
|
+
}
|
|
261
|
+
const visibleFiles = files.slice(0, visibleCount);
|
|
262
|
+
const hiddenCount = Math.max(0, files.length - visibleFiles.length);
|
|
263
|
+
if (hiddenCount === 0) {
|
|
264
|
+
return visibleFiles.join(", ");
|
|
265
|
+
}
|
|
266
|
+
return `${visibleFiles.join(", ")} \uC678 ${hiddenCount}\uAC1C`;
|
|
267
|
+
}
|
|
51
268
|
function createTraceLogger(scope, args4 = process.argv.slice(2)) {
|
|
52
269
|
const enabled = isTestMode(args4);
|
|
53
270
|
return (step, detail) => {
|
|
@@ -259,13 +476,67 @@ ${JSON.stringify(AIServices, null, 2)}
|
|
|
259
476
|
}
|
|
260
477
|
);
|
|
261
478
|
}
|
|
262
|
-
function
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
479
|
+
function truncateCommitSubject(subject) {
|
|
480
|
+
if (subject.length <= 72) {
|
|
481
|
+
return subject;
|
|
482
|
+
}
|
|
483
|
+
return `${subject.slice(0, 69)}...`;
|
|
484
|
+
}
|
|
485
|
+
function getRecentCommitOptions() {
|
|
486
|
+
const output = runGitCommand(
|
|
487
|
+
["log", `-${COMMIT_FETCH_LIMIT}`, "--date=relative", "--pretty=format:%h%x09%an%x09%ar%x09%s"],
|
|
488
|
+
{ allowFailure: true }
|
|
489
|
+
);
|
|
490
|
+
if (!output) {
|
|
491
|
+
return [];
|
|
492
|
+
}
|
|
493
|
+
return output.split("\n").map((line) => {
|
|
494
|
+
const [hash = "", author = "", relativeDate = "", ...subjectParts] = line.split(" ");
|
|
495
|
+
const subject = subjectParts.join(" ").trim();
|
|
496
|
+
return {
|
|
497
|
+
author,
|
|
498
|
+
description: `${author} | ${relativeDate}`,
|
|
499
|
+
hash,
|
|
500
|
+
label: `${hash} | ${truncateCommitSubject(subject)}`,
|
|
501
|
+
relativeDate,
|
|
502
|
+
subject
|
|
503
|
+
};
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
function buildSelectedCommitSummary(commits) {
|
|
507
|
+
return commits.map((commit) => `- ${commit.hash} | ${commit.subject} | ${commit.author} | ${commit.relativeDate}`).join("\n");
|
|
508
|
+
}
|
|
509
|
+
function getReviewPathspecArgs() {
|
|
510
|
+
const { includePatterns, excludePatterns } = getGitDiffPathspecs();
|
|
511
|
+
return [...includePatterns, ...excludePatterns];
|
|
512
|
+
}
|
|
513
|
+
function buildSelectedCommitDiff(commits) {
|
|
514
|
+
const reviewPathspecArgs = getReviewPathspecArgs();
|
|
515
|
+
const sections = commits.map((commit) => {
|
|
516
|
+
const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", ...reviewPathspecArgs], {
|
|
517
|
+
allowFailure: true,
|
|
518
|
+
trimOutput: false
|
|
519
|
+
}).trim();
|
|
520
|
+
if (!diff) {
|
|
521
|
+
return "";
|
|
522
|
+
}
|
|
523
|
+
return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
|
|
524
|
+
}).filter(Boolean).join("\n\n");
|
|
525
|
+
if (!sections) {
|
|
526
|
+
return "";
|
|
527
|
+
}
|
|
528
|
+
return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", "# \uB9AC\uBDF0 \uB300\uC0C1 diff", sections].join("\n");
|
|
529
|
+
}
|
|
530
|
+
function getSelectedCommitFiles(commits) {
|
|
531
|
+
const reviewPathspecArgs = getReviewPathspecArgs();
|
|
532
|
+
const files = /* @__PURE__ */ new Set();
|
|
533
|
+
commits.forEach((commit) => {
|
|
534
|
+
const output = runGitCommand(["show", "--pretty=format:", "--name-only", commit.hash, "--", ...reviewPathspecArgs], {
|
|
535
|
+
allowFailure: true
|
|
536
|
+
});
|
|
537
|
+
output.split("\n").map((line) => line.trim()).filter(Boolean).forEach((filePath) => files.add(filePath));
|
|
538
|
+
});
|
|
539
|
+
return [...files];
|
|
269
540
|
}
|
|
270
541
|
function openReport(reportPath) {
|
|
271
542
|
const resolvedPath = path__default.default.resolve(reportPath);
|
|
@@ -316,59 +587,166 @@ function openReport(reportPath) {
|
|
|
316
587
|
helperTrace("open-report:unsupported-platform", platform);
|
|
317
588
|
console.error(`\u26A0\uFE0F \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD50C\uB7AB\uD3FC\uC785\uB2C8\uB2E4: ${platform}`);
|
|
318
589
|
}
|
|
319
|
-
function
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
helperTrace(
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
590
|
+
function ensureInteractiveSelectionAvailable(scope, message) {
|
|
591
|
+
if (process.stdin.isTTY && process.stdout.isTTY && typeof process.stdin.setRawMode === "function") {
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
helperTrace(`${scope}:tty-missing`);
|
|
595
|
+
exitWithError(message, {
|
|
596
|
+
scope: `helper:${scope}`
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
function renderSelectionBlock(lines, previousLineCount) {
|
|
600
|
+
if (previousLineCount > 0) {
|
|
601
|
+
readline__default.default.moveCursor(process.stdout, 0, -previousLineCount);
|
|
602
|
+
readline__default.default.clearScreenDown(process.stdout);
|
|
603
|
+
}
|
|
604
|
+
const fittedLines = fitLinesToTerminal(lines);
|
|
605
|
+
process.stdout.write(`${fittedLines.join("\n")}
|
|
606
|
+
`);
|
|
607
|
+
return fittedLines.length;
|
|
608
|
+
}
|
|
609
|
+
function getSelectionWindowRange(optionCount, selectedIndex, windowSize) {
|
|
610
|
+
if (optionCount <= windowSize) {
|
|
611
|
+
return {
|
|
612
|
+
end: optionCount,
|
|
613
|
+
start: 0
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
const halfWindow = Math.floor(windowSize / 2);
|
|
617
|
+
const maxStart = optionCount - windowSize;
|
|
618
|
+
const start = Math.max(0, Math.min(selectedIndex - halfWindow, maxStart));
|
|
619
|
+
return {
|
|
620
|
+
end: Math.min(optionCount, start + windowSize),
|
|
621
|
+
start
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
function buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize) {
|
|
625
|
+
const { start, end } = getSelectionWindowRange(options.length, selectedIndex, windowSize);
|
|
626
|
+
const lines = [
|
|
627
|
+
`${ANSI.bold}${question}${ANSI.reset}`,
|
|
628
|
+
`${ANSI.dim}\u2191\u2193 \uC774\uB3D9 | Space \uC120\uD0DD/\uD574\uC81C | Enter \uC644\uB8CC | Esc \uCDE8\uC18C${ANSI.reset}`,
|
|
629
|
+
`${ANSI.dim}\uC120\uD0DD\uB428: ${toggled.size}\uAC1C / \uC804\uCCB4: ${options.length}\uAC1C${ANSI.reset}`
|
|
630
|
+
];
|
|
631
|
+
for (let index = start; index < end; index += 1) {
|
|
632
|
+
const option = options[index];
|
|
633
|
+
if (!option) {
|
|
634
|
+
continue;
|
|
333
635
|
}
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
636
|
+
const cursor = index === selectedIndex ? `${ANSI.cyan}>${ANSI.reset}` : " ";
|
|
637
|
+
const checked = toggled.has(index) ? `${ANSI.green}\u2611${ANSI.reset}` : "\u2610";
|
|
638
|
+
const description = option.description ? ` ${ANSI.dim}${option.description}${ANSI.reset}` : "";
|
|
639
|
+
lines.push(`${cursor} ${checked} ${option.label}${description}`);
|
|
640
|
+
}
|
|
641
|
+
if (options.length > windowSize) {
|
|
642
|
+
lines.push(`${ANSI.dim}\uD45C\uC2DC \uBC94\uC704: ${start + 1}-${end} / ${options.length}${ANSI.reset}`);
|
|
643
|
+
}
|
|
644
|
+
return lines;
|
|
645
|
+
}
|
|
646
|
+
async function showMultiSelect(question, options, windowSize = COMMIT_SELECTION_WINDOW) {
|
|
647
|
+
ensureInteractiveSelectionAvailable("showMultiSelect", "\u274C \uCEE4\uBC0B \uC120\uD0DD \uBAA8\uB2EC\uC740 TTY \uD658\uACBD\uC5D0\uC11C\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
648
|
+
let selectedIndex = 0;
|
|
649
|
+
let renderedLineCount = 0;
|
|
650
|
+
const toggled = /* @__PURE__ */ new Set();
|
|
651
|
+
const rl = readline__default.default.createInterface({
|
|
652
|
+
input: process.stdin,
|
|
653
|
+
output: process.stdout,
|
|
654
|
+
terminal: true
|
|
655
|
+
});
|
|
656
|
+
process.stdout.write("\x1B[?25l");
|
|
657
|
+
const cleanup = () => {
|
|
658
|
+
if (renderedLineCount > 0) {
|
|
659
|
+
readline__default.default.moveCursor(process.stdout, 0, -renderedLineCount);
|
|
660
|
+
readline__default.default.clearScreenDown(process.stdout);
|
|
661
|
+
renderedLineCount = 0;
|
|
341
662
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
663
|
+
process.stdin.removeListener("data", onData);
|
|
664
|
+
process.stdin.setRawMode(false);
|
|
665
|
+
process.stdin.pause();
|
|
666
|
+
rl.close();
|
|
667
|
+
process.stdout.write("\x1B[?25h");
|
|
668
|
+
};
|
|
669
|
+
const render = () => {
|
|
670
|
+
const lines = buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize);
|
|
671
|
+
renderedLineCount = renderSelectionBlock(lines, renderedLineCount);
|
|
672
|
+
};
|
|
673
|
+
const confirmSelection = (resolve) => {
|
|
674
|
+
const values = [...toggled].sort((left, right) => left - right).map((index) => options[index]?.value).filter((value) => value !== void 0);
|
|
675
|
+
cleanup();
|
|
676
|
+
resolve(values);
|
|
677
|
+
};
|
|
678
|
+
const cancelSelection = (resolve) => {
|
|
679
|
+
cleanup();
|
|
680
|
+
resolve([]);
|
|
681
|
+
};
|
|
682
|
+
let onData = (_data) => {
|
|
683
|
+
};
|
|
684
|
+
render();
|
|
685
|
+
return new Promise((resolve) => {
|
|
686
|
+
onData = (data) => {
|
|
687
|
+
const key = data.toString();
|
|
688
|
+
if (key === "") {
|
|
689
|
+
cleanup();
|
|
690
|
+
process.exit(0);
|
|
355
691
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
692
|
+
if (key === "\x1B") {
|
|
693
|
+
cancelSelection(resolve);
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
if (key === "\x1B[A") {
|
|
697
|
+
selectedIndex = (selectedIndex - 1 + options.length) % options.length;
|
|
698
|
+
render();
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
if (key === "\x1B[B") {
|
|
702
|
+
selectedIndex = (selectedIndex + 1) % options.length;
|
|
703
|
+
render();
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
if (key === " ") {
|
|
707
|
+
if (toggled.has(selectedIndex)) {
|
|
708
|
+
toggled.delete(selectedIndex);
|
|
709
|
+
} else {
|
|
710
|
+
toggled.add(selectedIndex);
|
|
711
|
+
}
|
|
712
|
+
render();
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
if (key === "\r" || key === "\n") {
|
|
716
|
+
confirmSelection(resolve);
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
process.stdin.setRawMode(true);
|
|
720
|
+
process.stdin.resume();
|
|
721
|
+
process.stdin.on("data", onData);
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
async function selectReviewCommits() {
|
|
725
|
+
const commits = getRecentCommitOptions();
|
|
726
|
+
if (commits.length === 0) {
|
|
727
|
+
console.log("\u2139\uFE0F \uB9AC\uBDF0\uD560 \uCD5C\uADFC \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
728
|
+
return [];
|
|
359
729
|
}
|
|
360
|
-
|
|
361
|
-
|
|
730
|
+
return showMultiSelect(
|
|
731
|
+
"\uB9AC\uBDF0\uD560 \uCEE4\uBC0B\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.",
|
|
732
|
+
commits.map((commit) => ({
|
|
733
|
+
description: commit.description,
|
|
734
|
+
label: commit.label,
|
|
735
|
+
value: commit
|
|
736
|
+
})),
|
|
737
|
+
COMMIT_SELECTION_WINDOW
|
|
738
|
+
);
|
|
362
739
|
}
|
|
363
740
|
async function showSelectionAIService() {
|
|
364
741
|
const selectedServiceFromArgs = parseServiceFromArgs();
|
|
365
742
|
if (selectedServiceFromArgs) {
|
|
366
743
|
helperTrace("show-selection:from-args", selectedServiceFromArgs);
|
|
367
744
|
console.log(`
|
|
368
|
-
\u2705
|
|
745
|
+
\u2705 ${ANSI.green}${selectedServiceFromArgs}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (--service)
|
|
369
746
|
`);
|
|
370
747
|
return selectedServiceFromArgs;
|
|
371
748
|
}
|
|
749
|
+
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
750
|
helperTrace("show-selection:interactive:start");
|
|
373
751
|
let selectedIndex = 0;
|
|
374
752
|
const rl = readline__default.default.createInterface({
|
|
@@ -386,11 +764,12 @@ async function showSelectionAIService() {
|
|
|
386
764
|
helperTrace("show-selection:interactive:render", AIServices[selectedIndex] || "unknown");
|
|
387
765
|
readline__default.default.clearScreenDown(process.stdout);
|
|
388
766
|
process.stdout.write(
|
|
389
|
-
|
|
767
|
+
`\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):
|
|
768
|
+
`
|
|
390
769
|
);
|
|
391
770
|
AIServices.forEach((service, index) => {
|
|
392
771
|
if (index === selectedIndex) {
|
|
393
|
-
process.stdout.write(`
|
|
772
|
+
process.stdout.write(` ${ANSI.cyan}>${ANSI.reset} ${ANSI.cyan}\u25C9${ANSI.reset} ${ANSI.bold}${service}${ANSI.reset}
|
|
394
773
|
`);
|
|
395
774
|
} else {
|
|
396
775
|
process.stdout.write(` \u25EF ${service}
|
|
@@ -420,7 +799,7 @@ async function showSelectionAIService() {
|
|
|
420
799
|
rl.close();
|
|
421
800
|
process.stdout.write("\x1B[?25h");
|
|
422
801
|
console.log(`
|
|
423
|
-
\u2705
|
|
802
|
+
\u2705 ${ANSI.green}${AIServices[selectedIndex]}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
|
|
424
803
|
`);
|
|
425
804
|
const result = AIServices[selectedIndex];
|
|
426
805
|
if (result) {
|
|
@@ -447,6 +826,11 @@ function getArgValue(flag) {
|
|
|
447
826
|
}
|
|
448
827
|
return args[index + 1];
|
|
449
828
|
}
|
|
829
|
+
function printNotice(message) {
|
|
830
|
+
if (args.includes("--test")) {
|
|
831
|
+
console.warn(message);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
450
834
|
function toUnique(values) {
|
|
451
835
|
const seen = /* @__PURE__ */ new Set();
|
|
452
836
|
return values.filter((value) => {
|
|
@@ -470,11 +854,11 @@ function resolveReasoningEffort() {
|
|
|
470
854
|
const normalized = normalizeEffort(customReasoningEffort);
|
|
471
855
|
trace("reasoning:custom", `${customReasoningEffort} -> ${normalized}`);
|
|
472
856
|
if (customReasoningEffort === "minimal") {
|
|
473
|
-
|
|
857
|
+
printNotice("\u26A0\uFE0F Claude\uB294 minimal\uC744 \uC9C1\uC811 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC544 low\uB85C \uB9E4\uD551\uD569\uB2C8\uB2E4.");
|
|
474
858
|
}
|
|
475
859
|
return normalized;
|
|
476
860
|
}
|
|
477
|
-
|
|
861
|
+
printNotice(
|
|
478
862
|
`\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS.join(
|
|
479
863
|
", "
|
|
480
864
|
)}`
|
|
@@ -517,7 +901,7 @@ function buildClaudeExecCommand(options) {
|
|
|
517
901
|
const modelOption = model ? `--model ${shellQuote(model)}` : "";
|
|
518
902
|
const fallbackOption = model && fallbackModel ? `--fallback-model ${shellQuote(fallbackModel)}` : "";
|
|
519
903
|
const effortOption = `--effort ${shellQuote(effort)}`;
|
|
520
|
-
const appendedPromptFiles = systemPromptFiles.map((
|
|
904
|
+
const appendedPromptFiles = systemPromptFiles.map((path3) => `--append-system-prompt-file ${shellQuote(path3)}`).join(" ");
|
|
521
905
|
return `cat ${shellQuote(tempDiffPath2)} | claude ${[
|
|
522
906
|
modelOption,
|
|
523
907
|
fallbackOption,
|
|
@@ -550,13 +934,13 @@ var createClaudeCommand = (tempDiffPath2, reviewFormPath2) => {
|
|
|
550
934
|
trace("model:candidates", modelCandidates.join(", "));
|
|
551
935
|
trace("command:candidates:count", String(modelCandidates.length + 1));
|
|
552
936
|
if (customModel) {
|
|
553
|
-
|
|
937
|
+
printNotice(
|
|
554
938
|
`\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
|
|
555
939
|
" -> "
|
|
556
940
|
)}) \uBC0F \uAE30\uBCF8 \uBAA8\uB378 \uC21C\uC73C\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
|
|
557
941
|
);
|
|
558
942
|
} else {
|
|
559
|
-
|
|
943
|
+
printNotice(
|
|
560
944
|
`\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
945
|
);
|
|
562
946
|
}
|
|
@@ -636,6 +1020,11 @@ function getArgValue2(flag) {
|
|
|
636
1020
|
}
|
|
637
1021
|
return args2[index + 1];
|
|
638
1022
|
}
|
|
1023
|
+
function printNotice2(message) {
|
|
1024
|
+
if (args2.includes("--test")) {
|
|
1025
|
+
console.warn(message);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
639
1028
|
function resolveReasoningEffort2() {
|
|
640
1029
|
const customReasoningEffort = getArgValue2("--reasoning-effort");
|
|
641
1030
|
if (customReasoningEffort) {
|
|
@@ -643,7 +1032,7 @@ function resolveReasoningEffort2() {
|
|
|
643
1032
|
trace3("reasoning:custom", customReasoningEffort);
|
|
644
1033
|
return customReasoningEffort;
|
|
645
1034
|
}
|
|
646
|
-
|
|
1035
|
+
printNotice2(
|
|
647
1036
|
`\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS2.join(
|
|
648
1037
|
", "
|
|
649
1038
|
)}`
|
|
@@ -687,14 +1076,14 @@ ${reviewFormLine || "- (\uC5C6\uC74C)"}
|
|
|
687
1076
|
trace3("prompt:prepared", `length=${prompt.length}`);
|
|
688
1077
|
let command = "";
|
|
689
1078
|
if (customModel) {
|
|
690
|
-
|
|
1079
|
+
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
1080
|
trace3("model:custom", customModel);
|
|
692
1081
|
command = buildCodexExecCommand(prompt, reasoningEffort, customModel);
|
|
693
1082
|
} else {
|
|
694
1083
|
const preferredModelAlias = "gpt-5";
|
|
695
1084
|
const aliasCommand = buildCodexExecCommand(prompt, reasoningEffort, preferredModelAlias);
|
|
696
1085
|
const fallbackCommand = buildCodexExecCommand(prompt, reasoningEffort);
|
|
697
|
-
|
|
1086
|
+
printNotice2(
|
|
698
1087
|
`\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
1088
|
);
|
|
700
1089
|
trace3("model:alias-first", preferredModelAlias);
|
|
@@ -755,6 +1144,11 @@ function getArgValue3(flag) {
|
|
|
755
1144
|
}
|
|
756
1145
|
return args3[index + 1];
|
|
757
1146
|
}
|
|
1147
|
+
function printNotice3(message) {
|
|
1148
|
+
if (args3.includes("--test")) {
|
|
1149
|
+
console.warn(message);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
758
1152
|
function toUnique2(values) {
|
|
759
1153
|
const seen = /* @__PURE__ */ new Set();
|
|
760
1154
|
return values.filter((value) => {
|
|
@@ -772,7 +1166,7 @@ function resolveReasoningEffort3() {
|
|
|
772
1166
|
trace5("reasoning:custom", customReasoningEffort);
|
|
773
1167
|
return customReasoningEffort;
|
|
774
1168
|
}
|
|
775
|
-
|
|
1169
|
+
printNotice3(
|
|
776
1170
|
`\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS3.join(
|
|
777
1171
|
", "
|
|
778
1172
|
)}`
|
|
@@ -834,38 +1228,57 @@ function buildGeminiExecCommand(prompt, model) {
|
|
|
834
1228
|
const modelOption = model ? `--model ${shellQuote3(model)}` : "";
|
|
835
1229
|
return `gemini ${[modelOption, "-p", shellQuote3(prompt)].filter(Boolean).join(" ")}`;
|
|
836
1230
|
}
|
|
1231
|
+
function toGeminiFileReference(filePath) {
|
|
1232
|
+
return `@${filePath}`;
|
|
1233
|
+
}
|
|
1234
|
+
function buildGeminiFileReferenceSection(files) {
|
|
1235
|
+
const existingFiles = files.filter((file) => fs__default.default.existsSync(file.path));
|
|
1236
|
+
return {
|
|
1237
|
+
count: existingFiles.length,
|
|
1238
|
+
lines: existingFiles.map((file) => `- ${file.display}: ${toGeminiFileReference(file.path)}`)
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
837
1241
|
var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
|
|
838
1242
|
trace5("createGeminiCommand:start", `tempDiffPath=${tempDiffPath2}, reviewFormPath=${reviewFormPath2}`);
|
|
839
1243
|
const customModel = getArgValue3("--model");
|
|
840
1244
|
const reasoningEffort = resolveReasoningEffort3();
|
|
841
1245
|
const primaryAlias = resolvePrimaryAlias2(reasoningEffort);
|
|
842
1246
|
const aliasFallbacks = toUnique2(getAliasFallbacks2(primaryAlias));
|
|
1247
|
+
const resolvedTempDiffPath = path__default.default.resolve(tempDiffPath2);
|
|
1248
|
+
const resolvedReviewFormPath = path__default.default.resolve(reviewFormPath2);
|
|
843
1249
|
const rules = [
|
|
844
1250
|
{ path: rulesPath, display: "\uB8F0\uC14B" },
|
|
845
1251
|
{ path: namingRulesPath, display: "\uB124\uC774\uBC0D \uADDC\uCE59" },
|
|
846
1252
|
{ path: codingConventionRulesPath, display: "\uCF54\uB529 \uCEE8\uBCA4\uC158" }
|
|
847
1253
|
];
|
|
848
|
-
const
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
const
|
|
852
|
-
trace5("reviewForm:status",
|
|
1254
|
+
const ruleSection = buildGeminiFileReferenceSection(rules);
|
|
1255
|
+
trace5("rules:loaded", `count=${ruleSection.count}`);
|
|
1256
|
+
const reviewFormExists = fs__default.default.existsSync(resolvedReviewFormPath);
|
|
1257
|
+
const reviewFormLine = reviewFormExists ? `- \uB9AC\uBDF0 \uC591\uC2DD: ${toGeminiFileReference(resolvedReviewFormPath)}` : "- \uB9AC\uBDF0 \uC591\uC2DD: (\uC5C6\uC74C)";
|
|
1258
|
+
trace5("reviewForm:status", reviewFormExists ? "exists" : "missing");
|
|
853
1259
|
const reasoningInstruction = getReasoningInstruction(reasoningEffort);
|
|
854
|
-
const prompt = `\
|
|
855
|
-
\
|
|
1260
|
+
const prompt = `\uC544\uB798 \uD30C\uC77C\uB4E4\uC744 \uC21C\uC11C\uB300\uB85C \uC77D\uACE0 \uCF54\uB4DC \uB9AC\uBDF0\uB97C \uC9C4\uD589\uD574\uC918.
|
|
1261
|
+
\uADDC\uCE59 \uD30C\uC77C:
|
|
1262
|
+
${ruleSection.lines.join("\n") || "- (\uC5C6\uC74C)"}
|
|
1263
|
+
\uB9AC\uBDF0 \uC591\uC2DD \uD30C\uC77C:
|
|
1264
|
+
${reviewFormLine}
|
|
1265
|
+
\uB9AC\uBDF0 \uB300\uC0C1 diff \uD30C\uC77C:
|
|
1266
|
+
- \uB9AC\uBDF0 \uB300\uC0C1 diff: ${toGeminiFileReference(resolvedTempDiffPath)}
|
|
1267
|
+
|
|
1268
|
+
\uBC18\uB4DC\uC2DC \uB9AC\uBDF0 \uC591\uC2DD\uC5D0 \uB9DE\uCDB0 \uC791\uC131\uD574\uC918.
|
|
856
1269
|
\uCD94\uB860 \uAC15\uB3C4 \uC9C0\uCE68: ${reasoningInstruction}`;
|
|
857
1270
|
trace5("prompt:prepared", `length=${prompt.length}`);
|
|
858
1271
|
const modelCandidates = toUnique2(customModel ? [customModel, ...aliasFallbacks] : aliasFallbacks);
|
|
859
1272
|
trace5("model:candidates", modelCandidates.join(", "));
|
|
860
1273
|
trace5("command:candidates:count", String(modelCandidates.length + 1));
|
|
861
1274
|
if (customModel) {
|
|
862
|
-
|
|
1275
|
+
printNotice3(
|
|
863
1276
|
`\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
|
|
864
1277
|
" -> "
|
|
865
1278
|
)}) \uBC0F \uAE30\uBCF8 \uBAA8\uB378 \uC21C\uC73C\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
|
|
866
1279
|
);
|
|
867
1280
|
} else {
|
|
868
|
-
|
|
1281
|
+
printNotice3(
|
|
869
1282
|
`\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
1283
|
);
|
|
871
1284
|
}
|
|
@@ -924,6 +1337,8 @@ async function main() {
|
|
|
924
1337
|
let savedDiffPath = "";
|
|
925
1338
|
let savedReportPath = "";
|
|
926
1339
|
let service = "";
|
|
1340
|
+
let selectedCommitSummary = "";
|
|
1341
|
+
let reviewTargetFiles = [];
|
|
927
1342
|
try {
|
|
928
1343
|
trace7("service-selection:start");
|
|
929
1344
|
service = await showSelectionAIService();
|
|
@@ -947,30 +1362,34 @@ async function main() {
|
|
|
947
1362
|
}
|
|
948
1363
|
trace7("review-flow:start");
|
|
949
1364
|
console.log("\u{1F680} AI Code Review\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...");
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
trace7("
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
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));
|
|
1365
|
+
trace7("commit-selection:start");
|
|
1366
|
+
const selectedCommits = await selectReviewCommits();
|
|
1367
|
+
trace7("commit-selection:done", `count=${selectedCommits.length}`);
|
|
1368
|
+
if (selectedCommits.length === 0) {
|
|
1369
|
+
trace7("commit-selection:empty");
|
|
1370
|
+
console.log("\u2139\uFE0F \uC120\uD0DD\uB41C \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1371
|
+
deleteTempDiff();
|
|
1372
|
+
process.exit(0);
|
|
967
1373
|
}
|
|
1374
|
+
selectedCommitSummary = buildSelectedCommitSummary(selectedCommits);
|
|
1375
|
+
trace7("commit-summary:prepared", selectedCommitSummary);
|
|
1376
|
+
reviewTargetFiles = getSelectedCommitFiles(selectedCommits);
|
|
1377
|
+
console.log(`\u{1F4C2} \uB9AC\uBDF0 \uB300\uC0C1 \uD30C\uC77C(${reviewTargetFiles.length}\uAC1C): ${formatReviewTargetFiles(reviewTargetFiles)}`);
|
|
1378
|
+
console.log("\u23F3 \uC120\uD0DD\uD55C \uCEE4\uBC0B diff\uB97C \uC815\uB9AC\uD558\uB294 \uC911\uC785\uB2C8\uB2E4...");
|
|
1379
|
+
trace7("git-diff:build:start");
|
|
1380
|
+
const diff = buildSelectedCommitDiff(selectedCommits);
|
|
1381
|
+
trace7("git-diff:build:done", `length=${diff.length}`);
|
|
968
1382
|
if (!diff.trim() && !isTest) {
|
|
969
1383
|
trace7("empty-diff:exit");
|
|
970
|
-
console.log("\u2139\uFE0F \uB9AC\uBDF0\
|
|
1384
|
+
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
1385
|
deleteTempDiff();
|
|
972
1386
|
process.exit(0);
|
|
973
1387
|
}
|
|
1388
|
+
const nowStr = getNowString();
|
|
1389
|
+
trace7("timestamp:created", nowStr);
|
|
1390
|
+
trace7("report-dir:create:start");
|
|
1391
|
+
createReportDirectory();
|
|
1392
|
+
trace7("report-dir:create:done");
|
|
974
1393
|
trace7("temp-diff:write:start", tempDiffPath);
|
|
975
1394
|
fs__default.default.writeFileSync(tempDiffPath, diff);
|
|
976
1395
|
trace7("temp-diff:write:done");
|
|
@@ -992,9 +1411,11 @@ async function main() {
|
|
|
992
1411
|
}
|
|
993
1412
|
trace7("command:create:done");
|
|
994
1413
|
trace7("command:exec:start");
|
|
995
|
-
const result =
|
|
1414
|
+
const result = (await executeShellCommandWithProgress(command, {
|
|
1415
|
+
progressMessage: `\u23F3 [\uB9AC\uBDF0 \uC9C4\uD589] ${service} | \uB300\uC0C1 ${reviewTargetFiles.length}\uAC1C \uD30C\uC77C`,
|
|
1416
|
+
streamOutput: isTest
|
|
1417
|
+
})).stdout;
|
|
996
1418
|
trace7("command:exec:done", `resultLength=${result.length}`);
|
|
997
|
-
console.log(result);
|
|
998
1419
|
trace7("report:write:start");
|
|
999
1420
|
savedReportPath = getNextFilePath(REPORT_DIR, nowStr, ".md");
|
|
1000
1421
|
fs__default.default.writeFileSync(savedReportPath, result);
|
|
@@ -1042,6 +1463,8 @@ ${command}`);
|
|
|
1042
1463
|
${JSON.stringify(
|
|
1043
1464
|
{
|
|
1044
1465
|
service,
|
|
1466
|
+
selectedCommitSummary: selectedCommitSummary || null,
|
|
1467
|
+
reviewTargetFiles,
|
|
1045
1468
|
command: command || null,
|
|
1046
1469
|
tempDiffPath,
|
|
1047
1470
|
savedDiffPath: savedDiffPath || null,
|