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,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { exec, execSync } from 'child_process';
|
|
2
|
+
import { exec, execSync, execFileSync } from 'child_process';
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import util, { inspect } from 'util';
|
|
5
5
|
import path from 'path';
|
|
@@ -8,14 +8,52 @@ import { fileURLToPath } from 'url';
|
|
|
8
8
|
|
|
9
9
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
var traceMessages = [];
|
|
11
|
-
var
|
|
12
|
-
var
|
|
13
|
-
|
|
14
|
-
path.
|
|
15
|
-
|
|
11
|
+
var GEMINI_CLI_PACKAGE_NAME = "sales-frontend-gemini-cli";
|
|
12
|
+
var cachedPackageRootPath = "";
|
|
13
|
+
function isGeminiCliPackageRoot(directory) {
|
|
14
|
+
const packageJsonPath = path.join(directory, "package.json");
|
|
15
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
20
|
+
return packageJson.name === GEMINI_CLI_PACKAGE_NAME;
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function resolveGeminiCliPackageRoot(startDirectory = __dirname) {
|
|
26
|
+
if (cachedPackageRootPath) {
|
|
27
|
+
return cachedPackageRootPath;
|
|
28
|
+
}
|
|
29
|
+
let currentDirectory = startDirectory;
|
|
30
|
+
while (true) {
|
|
31
|
+
if (isGeminiCliPackageRoot(currentDirectory)) {
|
|
32
|
+
cachedPackageRootPath = currentDirectory;
|
|
33
|
+
return cachedPackageRootPath;
|
|
34
|
+
}
|
|
35
|
+
const parentDirectory = path.dirname(currentDirectory);
|
|
36
|
+
if (parentDirectory === currentDirectory) {
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
currentDirectory = parentDirectory;
|
|
40
|
+
}
|
|
41
|
+
cachedPackageRootPath = path.resolve(startDirectory, "../..");
|
|
42
|
+
return cachedPackageRootPath;
|
|
43
|
+
}
|
|
44
|
+
function resolvePackageAssetPath(relativeFilePath) {
|
|
45
|
+
return path.resolve(resolveGeminiCliPackageRoot(), relativeFilePath);
|
|
46
|
+
}
|
|
47
|
+
var rulesPath = resolvePackageAssetPath("src/common/rules/review-rules.md");
|
|
48
|
+
var namingRulesPath = resolvePackageAssetPath("src/common/rules/naming-rule.md");
|
|
49
|
+
var codingConventionRulesPath = resolvePackageAssetPath("src/common/rules/coding-convention.md");
|
|
50
|
+
resolvePackageAssetPath("src/common/form/review-form.md");
|
|
51
|
+
var reviewFormOneByOnePath = resolvePackageAssetPath("src/common/form/review-form-one-by-one.md");
|
|
16
52
|
var REPORT_DIR = ".review-report";
|
|
17
53
|
var tempDiffPath = "temp_diff.txt";
|
|
18
54
|
var AIServices = ["gemini", "claude", "codex"];
|
|
55
|
+
var COMMIT_FETCH_LIMIT = 20;
|
|
56
|
+
var COMMIT_SELECTION_WINDOW = 8;
|
|
19
57
|
var ignoreList = [
|
|
20
58
|
"package.json",
|
|
21
59
|
"*.yml",
|
|
@@ -39,6 +77,137 @@ function clearTraceMessages() {
|
|
|
39
77
|
function getTraceMessages() {
|
|
40
78
|
return [...traceMessages];
|
|
41
79
|
}
|
|
80
|
+
var ANSI = {
|
|
81
|
+
bold: "\x1B[1m",
|
|
82
|
+
cyan: "\x1B[36m",
|
|
83
|
+
dim: "\x1B[2m",
|
|
84
|
+
green: "\x1B[32m",
|
|
85
|
+
reset: "\x1B[0m",
|
|
86
|
+
yellow: "\x1B[33m"
|
|
87
|
+
};
|
|
88
|
+
var ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
89
|
+
var COMBINING_MARK_PATTERN = /\p{Mark}/u;
|
|
90
|
+
var GRAPHEME_SEGMENTER = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter("ko", { granularity: "grapheme" }) : null;
|
|
91
|
+
function getGitDiffPathspecs() {
|
|
92
|
+
return {
|
|
93
|
+
excludePatterns: ignoreList.map((item) => `:(exclude)${item}`),
|
|
94
|
+
includePatterns: ["*.ts", "*.tsx", "*.js", "*.jsx"]
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function segmentGraphemes(value) {
|
|
98
|
+
if (!GRAPHEME_SEGMENTER) {
|
|
99
|
+
return [...value];
|
|
100
|
+
}
|
|
101
|
+
return [...GRAPHEME_SEGMENTER.segment(value)].map(({ segment }) => segment);
|
|
102
|
+
}
|
|
103
|
+
function isWideCodePoint(codePoint) {
|
|
104
|
+
return codePoint >= 4352 && (codePoint <= 4447 || codePoint === 9001 || codePoint === 9002 || codePoint >= 11904 && codePoint <= 12871 && codePoint !== 12351 || codePoint >= 12880 && codePoint <= 19903 || codePoint >= 19968 && codePoint <= 42182 || codePoint >= 43360 && codePoint <= 43388 || codePoint >= 44032 && codePoint <= 55203 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 65040 && codePoint <= 65049 || codePoint >= 65072 && codePoint <= 65131 || codePoint >= 65281 && codePoint <= 65376 || codePoint >= 65504 && codePoint <= 65510 || codePoint >= 127488 && codePoint <= 127569 || codePoint >= 131072 && codePoint <= 262141);
|
|
105
|
+
}
|
|
106
|
+
function isEmojiCodePoint(codePoint) {
|
|
107
|
+
return codePoint >= 127462 && codePoint <= 127487 || codePoint >= 127744 && codePoint <= 129791 || codePoint >= 9728 && codePoint <= 10175;
|
|
108
|
+
}
|
|
109
|
+
function getGraphemeWidth(grapheme) {
|
|
110
|
+
let width = 0;
|
|
111
|
+
for (const character of grapheme) {
|
|
112
|
+
const codePoint = character.codePointAt(0);
|
|
113
|
+
if (!codePoint || COMBINING_MARK_PATTERN.test(character) || codePoint === 8205) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (codePoint >= 65024 && codePoint <= 65039 || codePoint >= 917760 && codePoint <= 917999) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (isWideCodePoint(codePoint) || isEmojiCodePoint(codePoint)) {
|
|
120
|
+
width = Math.max(width, 2);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
width = Math.max(width, 1);
|
|
124
|
+
}
|
|
125
|
+
return width;
|
|
126
|
+
}
|
|
127
|
+
function tokenizePlainText(value) {
|
|
128
|
+
return segmentGraphemes(value).map((segment) => ({
|
|
129
|
+
value: segment,
|
|
130
|
+
visibleWidth: getGraphemeWidth(segment)
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
function tokenizeVisibleText(value) {
|
|
134
|
+
const tokens = [];
|
|
135
|
+
let lastIndex = 0;
|
|
136
|
+
for (const match of value.matchAll(ANSI_PATTERN)) {
|
|
137
|
+
const index = match.index ?? 0;
|
|
138
|
+
if (index > lastIndex) {
|
|
139
|
+
tokens.push(...tokenizePlainText(value.slice(lastIndex, index)));
|
|
140
|
+
}
|
|
141
|
+
tokens.push({
|
|
142
|
+
value: match[0],
|
|
143
|
+
visibleWidth: 0
|
|
144
|
+
});
|
|
145
|
+
lastIndex = index + match[0].length;
|
|
146
|
+
}
|
|
147
|
+
if (lastIndex < value.length) {
|
|
148
|
+
tokens.push(...tokenizePlainText(value.slice(lastIndex)));
|
|
149
|
+
}
|
|
150
|
+
return tokens;
|
|
151
|
+
}
|
|
152
|
+
function truncateLineForTerminal(value, maxWidth) {
|
|
153
|
+
if (maxWidth <= 0) {
|
|
154
|
+
return "";
|
|
155
|
+
}
|
|
156
|
+
const tokens = tokenizeVisibleText(value);
|
|
157
|
+
const totalWidth = tokens.reduce((sum, token) => sum + token.visibleWidth, 0);
|
|
158
|
+
if (totalWidth <= maxWidth) {
|
|
159
|
+
return value;
|
|
160
|
+
}
|
|
161
|
+
const ellipsis = "...";
|
|
162
|
+
const ellipsisWidth = 3;
|
|
163
|
+
const targetWidth = Math.max(0, maxWidth - ellipsisWidth);
|
|
164
|
+
let usedWidth = 0;
|
|
165
|
+
let result = "";
|
|
166
|
+
for (const token of tokens) {
|
|
167
|
+
if (token.visibleWidth === 0) {
|
|
168
|
+
result += token.value;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (usedWidth + token.visibleWidth > targetWidth) {
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
result += token.value;
|
|
175
|
+
usedWidth += token.visibleWidth;
|
|
176
|
+
}
|
|
177
|
+
return `${result}${ellipsis}${ANSI.reset}`;
|
|
178
|
+
}
|
|
179
|
+
function fitLinesToTerminal(lines) {
|
|
180
|
+
const maxWidth = Math.max(20, (process.stdout.columns || 120) - 1);
|
|
181
|
+
return lines.map((line) => truncateLineForTerminal(line, maxWidth));
|
|
182
|
+
}
|
|
183
|
+
function runGitCommand(args4, options = {}) {
|
|
184
|
+
const { allowFailure = false, trimOutput = true } = options;
|
|
185
|
+
try {
|
|
186
|
+
const output = execFileSync("git", args4, {
|
|
187
|
+
encoding: "utf8",
|
|
188
|
+
maxBuffer: 1024 * 1024 * 20,
|
|
189
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
190
|
+
});
|
|
191
|
+
return trimOutput ? output.trim() : output;
|
|
192
|
+
} catch (error) {
|
|
193
|
+
helperTrace("git-command:failed", `${args4.join(" ")} | ${getErrorSummary(error)}`);
|
|
194
|
+
if (allowFailure) {
|
|
195
|
+
return "";
|
|
196
|
+
}
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function formatReviewTargetFiles(files, visibleCount = 5) {
|
|
201
|
+
if (files.length === 0) {
|
|
202
|
+
return "(\uC5C6\uC74C)";
|
|
203
|
+
}
|
|
204
|
+
const visibleFiles = files.slice(0, visibleCount);
|
|
205
|
+
const hiddenCount = Math.max(0, files.length - visibleFiles.length);
|
|
206
|
+
if (hiddenCount === 0) {
|
|
207
|
+
return visibleFiles.join(", ");
|
|
208
|
+
}
|
|
209
|
+
return `${visibleFiles.join(", ")} \uC678 ${hiddenCount}\uAC1C`;
|
|
210
|
+
}
|
|
42
211
|
function createTraceLogger(scope, args4 = process.argv.slice(2)) {
|
|
43
212
|
const enabled = isTestMode(args4);
|
|
44
213
|
return (step, detail) => {
|
|
@@ -250,13 +419,83 @@ ${JSON.stringify(AIServices, null, 2)}
|
|
|
250
419
|
}
|
|
251
420
|
);
|
|
252
421
|
}
|
|
253
|
-
function
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
422
|
+
function truncateCommitSubject(subject) {
|
|
423
|
+
if (subject.length <= 72) {
|
|
424
|
+
return subject;
|
|
425
|
+
}
|
|
426
|
+
return `${subject.slice(0, 69)}...`;
|
|
427
|
+
}
|
|
428
|
+
function getRecentCommitOptions() {
|
|
429
|
+
const output = runGitCommand(
|
|
430
|
+
["log", `-${COMMIT_FETCH_LIMIT}`, "--date=relative", "--pretty=format:%h%x09%an%x09%ar%x09%s"],
|
|
431
|
+
{ allowFailure: true }
|
|
432
|
+
);
|
|
433
|
+
if (!output) {
|
|
434
|
+
return [];
|
|
435
|
+
}
|
|
436
|
+
return output.split("\n").map((line) => {
|
|
437
|
+
const [hash = "", author = "", relativeDate = "", ...subjectParts] = line.split(" ");
|
|
438
|
+
const subject = subjectParts.join(" ").trim();
|
|
439
|
+
return {
|
|
440
|
+
author,
|
|
441
|
+
description: `${author} | ${relativeDate}`,
|
|
442
|
+
hash,
|
|
443
|
+
label: `${hash} | ${truncateCommitSubject(subject)}`,
|
|
444
|
+
relativeDate,
|
|
445
|
+
subject
|
|
446
|
+
};
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
function buildSelectedCommitSummary(commits) {
|
|
450
|
+
return commits.map((commit) => `- ${commit.hash} | ${commit.subject} | ${commit.author} | ${commit.relativeDate}`).join("\n");
|
|
451
|
+
}
|
|
452
|
+
function getReviewPathspecArgs() {
|
|
453
|
+
const { includePatterns, excludePatterns } = getGitDiffPathspecs();
|
|
454
|
+
return [...includePatterns, ...excludePatterns];
|
|
455
|
+
}
|
|
456
|
+
function buildSelectedCommitDiff(commits) {
|
|
457
|
+
const reviewPathspecArgs = getReviewPathspecArgs();
|
|
458
|
+
const sections = commits.map((commit) => {
|
|
459
|
+
const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", ...reviewPathspecArgs], {
|
|
460
|
+
allowFailure: true,
|
|
461
|
+
trimOutput: false
|
|
462
|
+
}).trim();
|
|
463
|
+
if (!diff) {
|
|
464
|
+
return "";
|
|
465
|
+
}
|
|
466
|
+
return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
|
|
467
|
+
}).filter(Boolean).join("\n\n");
|
|
468
|
+
if (!sections) {
|
|
469
|
+
return "";
|
|
470
|
+
}
|
|
471
|
+
return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", "# \uB9AC\uBDF0 \uB300\uC0C1 diff", sections].join("\n");
|
|
472
|
+
}
|
|
473
|
+
function getSelectedCommitFiles(commits) {
|
|
474
|
+
const reviewPathspecArgs = getReviewPathspecArgs();
|
|
475
|
+
const files = /* @__PURE__ */ new Set();
|
|
476
|
+
commits.forEach((commit) => {
|
|
477
|
+
const output = runGitCommand(["show", "--pretty=format:", "--name-only", commit.hash, "--", ...reviewPathspecArgs], {
|
|
478
|
+
allowFailure: true
|
|
479
|
+
});
|
|
480
|
+
output.split("\n").map((line) => line.trim()).filter(Boolean).forEach((filePath) => files.add(filePath));
|
|
481
|
+
});
|
|
482
|
+
return [...files];
|
|
483
|
+
}
|
|
484
|
+
function buildSelectedFileDiff(commits, filePath) {
|
|
485
|
+
const sections = commits.map((commit) => {
|
|
486
|
+
const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", filePath], {
|
|
487
|
+
allowFailure: true,
|
|
488
|
+
trimOutput: false
|
|
489
|
+
}).trim();
|
|
490
|
+
if (!diff) {
|
|
491
|
+
return "";
|
|
492
|
+
}
|
|
493
|
+
return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
|
|
494
|
+
}).filter(Boolean).join("\n\n");
|
|
495
|
+
if (!sections) {
|
|
496
|
+
return "";
|
|
497
|
+
}
|
|
498
|
+
return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", `# \uD30C\uC77C: ${filePath}`, sections].join("\n\n");
|
|
260
499
|
}
|
|
261
500
|
function openReport(reportPath) {
|
|
262
501
|
const resolvedPath = path.resolve(reportPath);
|
|
@@ -307,59 +546,166 @@ function openReport(reportPath) {
|
|
|
307
546
|
helperTrace("open-report:unsupported-platform", platform);
|
|
308
547
|
console.error(`\u26A0\uFE0F \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD50C\uB7AB\uD3FC\uC785\uB2C8\uB2E4: ${platform}`);
|
|
309
548
|
}
|
|
310
|
-
function
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
helperTrace(
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
549
|
+
function ensureInteractiveSelectionAvailable(scope, message) {
|
|
550
|
+
if (process.stdin.isTTY && process.stdout.isTTY && typeof process.stdin.setRawMode === "function") {
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
helperTrace(`${scope}:tty-missing`);
|
|
554
|
+
exitWithError(message, {
|
|
555
|
+
scope: `helper:${scope}`
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
function renderSelectionBlock(lines, previousLineCount) {
|
|
559
|
+
if (previousLineCount > 0) {
|
|
560
|
+
readline.moveCursor(process.stdout, 0, -previousLineCount);
|
|
561
|
+
readline.clearScreenDown(process.stdout);
|
|
562
|
+
}
|
|
563
|
+
const fittedLines = fitLinesToTerminal(lines);
|
|
564
|
+
process.stdout.write(`${fittedLines.join("\n")}
|
|
565
|
+
`);
|
|
566
|
+
return fittedLines.length;
|
|
567
|
+
}
|
|
568
|
+
function getSelectionWindowRange(optionCount, selectedIndex, windowSize) {
|
|
569
|
+
if (optionCount <= windowSize) {
|
|
570
|
+
return {
|
|
571
|
+
end: optionCount,
|
|
572
|
+
start: 0
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
const halfWindow = Math.floor(windowSize / 2);
|
|
576
|
+
const maxStart = optionCount - windowSize;
|
|
577
|
+
const start = Math.max(0, Math.min(selectedIndex - halfWindow, maxStart));
|
|
578
|
+
return {
|
|
579
|
+
end: Math.min(optionCount, start + windowSize),
|
|
580
|
+
start
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
function buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize) {
|
|
584
|
+
const { start, end } = getSelectionWindowRange(options.length, selectedIndex, windowSize);
|
|
585
|
+
const lines = [
|
|
586
|
+
`${ANSI.bold}${question}${ANSI.reset}`,
|
|
587
|
+
`${ANSI.dim}\u2191\u2193 \uC774\uB3D9 | Space \uC120\uD0DD/\uD574\uC81C | Enter \uC644\uB8CC | Esc \uCDE8\uC18C${ANSI.reset}`,
|
|
588
|
+
`${ANSI.dim}\uC120\uD0DD\uB428: ${toggled.size}\uAC1C / \uC804\uCCB4: ${options.length}\uAC1C${ANSI.reset}`
|
|
589
|
+
];
|
|
590
|
+
for (let index = start; index < end; index += 1) {
|
|
591
|
+
const option = options[index];
|
|
592
|
+
if (!option) {
|
|
593
|
+
continue;
|
|
324
594
|
}
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
595
|
+
const cursor = index === selectedIndex ? `${ANSI.cyan}>${ANSI.reset}` : " ";
|
|
596
|
+
const checked = toggled.has(index) ? `${ANSI.green}\u2611${ANSI.reset}` : "\u2610";
|
|
597
|
+
const description = option.description ? ` ${ANSI.dim}${option.description}${ANSI.reset}` : "";
|
|
598
|
+
lines.push(`${cursor} ${checked} ${option.label}${description}`);
|
|
599
|
+
}
|
|
600
|
+
if (options.length > windowSize) {
|
|
601
|
+
lines.push(`${ANSI.dim}\uD45C\uC2DC \uBC94\uC704: ${start + 1}-${end} / ${options.length}${ANSI.reset}`);
|
|
602
|
+
}
|
|
603
|
+
return lines;
|
|
604
|
+
}
|
|
605
|
+
async function showMultiSelect(question, options, windowSize = COMMIT_SELECTION_WINDOW) {
|
|
606
|
+
ensureInteractiveSelectionAvailable("showMultiSelect", "\u274C \uCEE4\uBC0B \uC120\uD0DD \uBAA8\uB2EC\uC740 TTY \uD658\uACBD\uC5D0\uC11C\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
607
|
+
let selectedIndex = 0;
|
|
608
|
+
let renderedLineCount = 0;
|
|
609
|
+
const toggled = /* @__PURE__ */ new Set();
|
|
610
|
+
const rl = readline.createInterface({
|
|
611
|
+
input: process.stdin,
|
|
612
|
+
output: process.stdout,
|
|
613
|
+
terminal: true
|
|
614
|
+
});
|
|
615
|
+
process.stdout.write("\x1B[?25l");
|
|
616
|
+
const cleanup = () => {
|
|
617
|
+
if (renderedLineCount > 0) {
|
|
618
|
+
readline.moveCursor(process.stdout, 0, -renderedLineCount);
|
|
619
|
+
readline.clearScreenDown(process.stdout);
|
|
620
|
+
renderedLineCount = 0;
|
|
332
621
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
622
|
+
process.stdin.removeListener("data", onData);
|
|
623
|
+
process.stdin.setRawMode(false);
|
|
624
|
+
process.stdin.pause();
|
|
625
|
+
rl.close();
|
|
626
|
+
process.stdout.write("\x1B[?25h");
|
|
627
|
+
};
|
|
628
|
+
const render = () => {
|
|
629
|
+
const lines = buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize);
|
|
630
|
+
renderedLineCount = renderSelectionBlock(lines, renderedLineCount);
|
|
631
|
+
};
|
|
632
|
+
const confirmSelection = (resolve) => {
|
|
633
|
+
const values = [...toggled].sort((left, right) => left - right).map((index) => options[index]?.value).filter((value) => value !== void 0);
|
|
634
|
+
cleanup();
|
|
635
|
+
resolve(values);
|
|
636
|
+
};
|
|
637
|
+
const cancelSelection = (resolve) => {
|
|
638
|
+
cleanup();
|
|
639
|
+
resolve([]);
|
|
640
|
+
};
|
|
641
|
+
let onData = (_data) => {
|
|
642
|
+
};
|
|
643
|
+
render();
|
|
644
|
+
return new Promise((resolve) => {
|
|
645
|
+
onData = (data) => {
|
|
646
|
+
const key = data.toString();
|
|
647
|
+
if (key === "") {
|
|
648
|
+
cleanup();
|
|
649
|
+
process.exit(0);
|
|
346
650
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
651
|
+
if (key === "\x1B") {
|
|
652
|
+
cancelSelection(resolve);
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
if (key === "\x1B[A") {
|
|
656
|
+
selectedIndex = (selectedIndex - 1 + options.length) % options.length;
|
|
657
|
+
render();
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
if (key === "\x1B[B") {
|
|
661
|
+
selectedIndex = (selectedIndex + 1) % options.length;
|
|
662
|
+
render();
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
if (key === " ") {
|
|
666
|
+
if (toggled.has(selectedIndex)) {
|
|
667
|
+
toggled.delete(selectedIndex);
|
|
668
|
+
} else {
|
|
669
|
+
toggled.add(selectedIndex);
|
|
670
|
+
}
|
|
671
|
+
render();
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
if (key === "\r" || key === "\n") {
|
|
675
|
+
confirmSelection(resolve);
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
process.stdin.setRawMode(true);
|
|
679
|
+
process.stdin.resume();
|
|
680
|
+
process.stdin.on("data", onData);
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
async function selectReviewCommits() {
|
|
684
|
+
const commits = getRecentCommitOptions();
|
|
685
|
+
if (commits.length === 0) {
|
|
686
|
+
console.log("\u2139\uFE0F \uB9AC\uBDF0\uD560 \uCD5C\uADFC \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
687
|
+
return [];
|
|
350
688
|
}
|
|
351
|
-
|
|
352
|
-
|
|
689
|
+
return showMultiSelect(
|
|
690
|
+
"\uB9AC\uBDF0\uD560 \uCEE4\uBC0B\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.",
|
|
691
|
+
commits.map((commit) => ({
|
|
692
|
+
description: commit.description,
|
|
693
|
+
label: commit.label,
|
|
694
|
+
value: commit
|
|
695
|
+
})),
|
|
696
|
+
COMMIT_SELECTION_WINDOW
|
|
697
|
+
);
|
|
353
698
|
}
|
|
354
699
|
async function showSelectionAIService() {
|
|
355
700
|
const selectedServiceFromArgs = parseServiceFromArgs();
|
|
356
701
|
if (selectedServiceFromArgs) {
|
|
357
702
|
helperTrace("show-selection:from-args", selectedServiceFromArgs);
|
|
358
703
|
console.log(`
|
|
359
|
-
\u2705
|
|
704
|
+
\u2705 ${ANSI.green}${selectedServiceFromArgs}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (--service)
|
|
360
705
|
`);
|
|
361
706
|
return selectedServiceFromArgs;
|
|
362
707
|
}
|
|
708
|
+
ensureInteractiveSelectionAvailable("showSelectionAIService", "\u274C AI \uC11C\uBE44\uC2A4 \uC120\uD0DD UI\uB294 TTY \uD658\uACBD\uC5D0\uC11C\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
363
709
|
helperTrace("show-selection:interactive:start");
|
|
364
710
|
let selectedIndex = 0;
|
|
365
711
|
const rl = readline.createInterface({
|
|
@@ -377,11 +723,12 @@ async function showSelectionAIService() {
|
|
|
377
723
|
helperTrace("show-selection:interactive:render", AIServices[selectedIndex] || "unknown");
|
|
378
724
|
readline.clearScreenDown(process.stdout);
|
|
379
725
|
process.stdout.write(
|
|
380
|
-
|
|
726
|
+
`\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):
|
|
727
|
+
`
|
|
381
728
|
);
|
|
382
729
|
AIServices.forEach((service, index) => {
|
|
383
730
|
if (index === selectedIndex) {
|
|
384
|
-
process.stdout.write(`
|
|
731
|
+
process.stdout.write(` ${ANSI.cyan}>${ANSI.reset} ${ANSI.cyan}\u25C9${ANSI.reset} ${ANSI.bold}${service}${ANSI.reset}
|
|
385
732
|
`);
|
|
386
733
|
} else {
|
|
387
734
|
process.stdout.write(` \u25EF ${service}
|
|
@@ -411,7 +758,7 @@ async function showSelectionAIService() {
|
|
|
411
758
|
rl.close();
|
|
412
759
|
process.stdout.write("\x1B[?25h");
|
|
413
760
|
console.log(`
|
|
414
|
-
\u2705
|
|
761
|
+
\u2705 ${ANSI.green}${AIServices[selectedIndex]}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
|
|
415
762
|
`);
|
|
416
763
|
const result = AIServices[selectedIndex];
|
|
417
764
|
if (result) {
|
|
@@ -438,6 +785,11 @@ function getArgValue(flag) {
|
|
|
438
785
|
}
|
|
439
786
|
return args[index + 1];
|
|
440
787
|
}
|
|
788
|
+
function printNotice(message) {
|
|
789
|
+
if (args.includes("--test")) {
|
|
790
|
+
console.warn(message);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
441
793
|
function toUnique(values) {
|
|
442
794
|
const seen = /* @__PURE__ */ new Set();
|
|
443
795
|
return values.filter((value) => {
|
|
@@ -461,11 +813,11 @@ function resolveReasoningEffort() {
|
|
|
461
813
|
const normalized = normalizeEffort(customReasoningEffort);
|
|
462
814
|
trace("reasoning:custom", `${customReasoningEffort} -> ${normalized}`);
|
|
463
815
|
if (customReasoningEffort === "minimal") {
|
|
464
|
-
|
|
816
|
+
printNotice("\u26A0\uFE0F Claude\uB294 minimal\uC744 \uC9C1\uC811 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC544 low\uB85C \uB9E4\uD551\uD569\uB2C8\uB2E4.");
|
|
465
817
|
}
|
|
466
818
|
return normalized;
|
|
467
819
|
}
|
|
468
|
-
|
|
820
|
+
printNotice(
|
|
469
821
|
`\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS.join(
|
|
470
822
|
", "
|
|
471
823
|
)}`
|
|
@@ -508,7 +860,7 @@ function buildClaudeExecCommand(options) {
|
|
|
508
860
|
const modelOption = model ? `--model ${shellQuote(model)}` : "";
|
|
509
861
|
const fallbackOption = model && fallbackModel ? `--fallback-model ${shellQuote(fallbackModel)}` : "";
|
|
510
862
|
const effortOption = `--effort ${shellQuote(effort)}`;
|
|
511
|
-
const appendedPromptFiles = systemPromptFiles.map((
|
|
863
|
+
const appendedPromptFiles = systemPromptFiles.map((path3) => `--append-system-prompt-file ${shellQuote(path3)}`).join(" ");
|
|
512
864
|
return `cat ${shellQuote(tempDiffPath2)} | claude ${[
|
|
513
865
|
modelOption,
|
|
514
866
|
fallbackOption,
|
|
@@ -541,13 +893,13 @@ var createClaudeCommand = (tempDiffPath2, reviewFormPath2) => {
|
|
|
541
893
|
trace("model:candidates", modelCandidates.join(", "));
|
|
542
894
|
trace("command:candidates:count", String(modelCandidates.length + 1));
|
|
543
895
|
if (customModel) {
|
|
544
|
-
|
|
896
|
+
printNotice(
|
|
545
897
|
`\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
|
|
546
898
|
" -> "
|
|
547
899
|
)}) \uBC0F \uAE30\uBCF8 \uBAA8\uB378 \uC21C\uC73C\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
|
|
548
900
|
);
|
|
549
901
|
} else {
|
|
550
|
-
|
|
902
|
+
printNotice(
|
|
551
903
|
`\u26A0\uFE0F \uBAA8\uB378\uC774 \uC9C0\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. alias(${aliasFallbacks.join(" -> ")})\uB97C \uC21C\uCC28 \uC2DC\uB3C4\uD558\uACE0 \uB9C8\uC9C0\uB9C9\uC5D0 \uAE30\uBCF8 \uBAA8\uB378\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
|
|
552
904
|
);
|
|
553
905
|
}
|
|
@@ -627,6 +979,11 @@ function getArgValue2(flag) {
|
|
|
627
979
|
}
|
|
628
980
|
return args2[index + 1];
|
|
629
981
|
}
|
|
982
|
+
function printNotice2(message) {
|
|
983
|
+
if (args2.includes("--test")) {
|
|
984
|
+
console.warn(message);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
630
987
|
function resolveReasoningEffort2() {
|
|
631
988
|
const customReasoningEffort = getArgValue2("--reasoning-effort");
|
|
632
989
|
if (customReasoningEffort) {
|
|
@@ -634,7 +991,7 @@ function resolveReasoningEffort2() {
|
|
|
634
991
|
trace3("reasoning:custom", customReasoningEffort);
|
|
635
992
|
return customReasoningEffort;
|
|
636
993
|
}
|
|
637
|
-
|
|
994
|
+
printNotice2(
|
|
638
995
|
`\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS2.join(
|
|
639
996
|
", "
|
|
640
997
|
)}`
|
|
@@ -678,14 +1035,14 @@ ${reviewFormLine || "- (\uC5C6\uC74C)"}
|
|
|
678
1035
|
trace3("prompt:prepared", `length=${prompt.length}`);
|
|
679
1036
|
let command = "";
|
|
680
1037
|
if (customModel) {
|
|
681
|
-
|
|
1038
|
+
printNotice2("\u26A0\uFE0F \uC9C0\uC815\uD55C \uBAA8\uB378\uC774 \uC5C6\uB294 \uACBD\uC6B0, \uC5D0\uB7EC\uAC00 \uBC1C\uC0DD\uD558\uB2C8 \uC8FC\uC758\uD558\uC138\uC694.");
|
|
682
1039
|
trace3("model:custom", customModel);
|
|
683
1040
|
command = buildCodexExecCommand(prompt, reasoningEffort, customModel);
|
|
684
1041
|
} else {
|
|
685
1042
|
const preferredModelAlias = "gpt-5";
|
|
686
1043
|
const aliasCommand = buildCodexExecCommand(prompt, reasoningEffort, preferredModelAlias);
|
|
687
1044
|
const fallbackCommand = buildCodexExecCommand(prompt, reasoningEffort);
|
|
688
|
-
|
|
1045
|
+
printNotice2(
|
|
689
1046
|
`\u26A0\uFE0F \uBAA8\uB378\uC774 \uC9C0\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. alias(${preferredModelAlias})\uB97C \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 \uACC4\uC815 \uAE30\uBCF8 \uBAA8\uB378\uB85C \uC790\uB3D9 \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
|
|
690
1047
|
);
|
|
691
1048
|
trace3("model:alias-first", preferredModelAlias);
|
|
@@ -746,6 +1103,11 @@ function getArgValue3(flag) {
|
|
|
746
1103
|
}
|
|
747
1104
|
return args3[index + 1];
|
|
748
1105
|
}
|
|
1106
|
+
function printNotice3(message) {
|
|
1107
|
+
if (args3.includes("--test")) {
|
|
1108
|
+
console.warn(message);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
749
1111
|
function toUnique2(values) {
|
|
750
1112
|
const seen = /* @__PURE__ */ new Set();
|
|
751
1113
|
return values.filter((value) => {
|
|
@@ -763,7 +1125,7 @@ function resolveReasoningEffort3() {
|
|
|
763
1125
|
trace5("reasoning:custom", customReasoningEffort);
|
|
764
1126
|
return customReasoningEffort;
|
|
765
1127
|
}
|
|
766
|
-
|
|
1128
|
+
printNotice3(
|
|
767
1129
|
`\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS3.join(
|
|
768
1130
|
", "
|
|
769
1131
|
)}`
|
|
@@ -825,38 +1187,57 @@ function buildGeminiExecCommand(prompt, model) {
|
|
|
825
1187
|
const modelOption = model ? `--model ${shellQuote3(model)}` : "";
|
|
826
1188
|
return `gemini ${[modelOption, "-p", shellQuote3(prompt)].filter(Boolean).join(" ")}`;
|
|
827
1189
|
}
|
|
1190
|
+
function toGeminiFileReference(filePath) {
|
|
1191
|
+
return `@${filePath}`;
|
|
1192
|
+
}
|
|
1193
|
+
function buildGeminiFileReferenceSection(files) {
|
|
1194
|
+
const existingFiles = files.filter((file) => fs.existsSync(file.path));
|
|
1195
|
+
return {
|
|
1196
|
+
count: existingFiles.length,
|
|
1197
|
+
lines: existingFiles.map((file) => `- ${file.display}: ${toGeminiFileReference(file.path)}`)
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
828
1200
|
var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
|
|
829
1201
|
trace5("createGeminiCommand:start", `tempDiffPath=${tempDiffPath2}, reviewFormPath=${reviewFormPath2}`);
|
|
830
1202
|
const customModel = getArgValue3("--model");
|
|
831
1203
|
const reasoningEffort = resolveReasoningEffort3();
|
|
832
1204
|
const primaryAlias = resolvePrimaryAlias2(reasoningEffort);
|
|
833
1205
|
const aliasFallbacks = toUnique2(getAliasFallbacks2(primaryAlias));
|
|
1206
|
+
const resolvedTempDiffPath = path.resolve(tempDiffPath2);
|
|
1207
|
+
const resolvedReviewFormPath = path.resolve(reviewFormPath2);
|
|
834
1208
|
const rules = [
|
|
835
1209
|
{ path: rulesPath, display: "\uB8F0\uC14B" },
|
|
836
1210
|
{ path: namingRulesPath, display: "\uB124\uC774\uBC0D \uADDC\uCE59" },
|
|
837
1211
|
{ path: codingConventionRulesPath, display: "\uCF54\uB529 \uCEE8\uBCA4\uC158" }
|
|
838
1212
|
];
|
|
839
|
-
const
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
const
|
|
843
|
-
trace5("reviewForm:status",
|
|
1213
|
+
const ruleSection = buildGeminiFileReferenceSection(rules);
|
|
1214
|
+
trace5("rules:loaded", `count=${ruleSection.count}`);
|
|
1215
|
+
const reviewFormExists = fs.existsSync(resolvedReviewFormPath);
|
|
1216
|
+
const reviewFormLine = reviewFormExists ? `- \uB9AC\uBDF0 \uC591\uC2DD: ${toGeminiFileReference(resolvedReviewFormPath)}` : "- \uB9AC\uBDF0 \uC591\uC2DD: (\uC5C6\uC74C)";
|
|
1217
|
+
trace5("reviewForm:status", reviewFormExists ? "exists" : "missing");
|
|
844
1218
|
const reasoningInstruction = getReasoningInstruction(reasoningEffort);
|
|
845
|
-
const prompt = `\
|
|
846
|
-
\
|
|
1219
|
+
const prompt = `\uC544\uB798 \uD30C\uC77C\uB4E4\uC744 \uC21C\uC11C\uB300\uB85C \uC77D\uACE0 \uCF54\uB4DC \uB9AC\uBDF0\uB97C \uC9C4\uD589\uD574\uC918.
|
|
1220
|
+
\uADDC\uCE59 \uD30C\uC77C:
|
|
1221
|
+
${ruleSection.lines.join("\n") || "- (\uC5C6\uC74C)"}
|
|
1222
|
+
\uB9AC\uBDF0 \uC591\uC2DD \uD30C\uC77C:
|
|
1223
|
+
${reviewFormLine}
|
|
1224
|
+
\uB9AC\uBDF0 \uB300\uC0C1 diff \uD30C\uC77C:
|
|
1225
|
+
- \uB9AC\uBDF0 \uB300\uC0C1 diff: ${toGeminiFileReference(resolvedTempDiffPath)}
|
|
1226
|
+
|
|
1227
|
+
\uBC18\uB4DC\uC2DC \uB9AC\uBDF0 \uC591\uC2DD\uC5D0 \uB9DE\uCDB0 \uC791\uC131\uD574\uC918.
|
|
847
1228
|
\uCD94\uB860 \uAC15\uB3C4 \uC9C0\uCE68: ${reasoningInstruction}`;
|
|
848
1229
|
trace5("prompt:prepared", `length=${prompt.length}`);
|
|
849
1230
|
const modelCandidates = toUnique2(customModel ? [customModel, ...aliasFallbacks] : aliasFallbacks);
|
|
850
1231
|
trace5("model:candidates", modelCandidates.join(", "));
|
|
851
1232
|
trace5("command:candidates:count", String(modelCandidates.length + 1));
|
|
852
1233
|
if (customModel) {
|
|
853
|
-
|
|
1234
|
+
printNotice3(
|
|
854
1235
|
`\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
|
|
855
1236
|
" -> "
|
|
856
1237
|
)}) \uBC0F \uAE30\uBCF8 \uBAA8\uB378 \uC21C\uC73C\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
|
|
857
1238
|
);
|
|
858
1239
|
} else {
|
|
859
|
-
|
|
1240
|
+
printNotice3(
|
|
860
1241
|
`\u26A0\uFE0F \uBAA8\uB378\uC774 \uC9C0\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. alias(${aliasFallbacks.join(" -> ")})\uB97C \uC21C\uCC28 \uC2DC\uB3C4\uD558\uACE0 \uB9C8\uC9C0\uB9C9\uC5D0 \uAE30\uBCF8 \uBAA8\uB378\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
|
|
861
1242
|
);
|
|
862
1243
|
}
|
|
@@ -915,6 +1296,7 @@ async function main() {
|
|
|
915
1296
|
let service = "";
|
|
916
1297
|
let savedDiffPath = "";
|
|
917
1298
|
let savedReportPath = "";
|
|
1299
|
+
let selectedCommitSummary = "";
|
|
918
1300
|
try {
|
|
919
1301
|
trace7("service-selection:start");
|
|
920
1302
|
service = await showSelectionAIService();
|
|
@@ -938,29 +1320,35 @@ async function main() {
|
|
|
938
1320
|
}
|
|
939
1321
|
trace7("review-flow:start");
|
|
940
1322
|
console.log("\u{1F680} AI Code Review\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...");
|
|
941
|
-
trace7("
|
|
942
|
-
|
|
943
|
-
trace7("
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
1323
|
+
trace7("commit-selection:start");
|
|
1324
|
+
const selectedCommits = await selectReviewCommits();
|
|
1325
|
+
trace7("commit-selection:done", `count=${selectedCommits.length}`);
|
|
1326
|
+
if (selectedCommits.length === 0) {
|
|
1327
|
+
trace7("commit-selection:empty");
|
|
1328
|
+
console.log("\u2139\uFE0F \uC120\uD0DD\uB41C \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1329
|
+
deleteTempDiff();
|
|
1330
|
+
process.exit(0);
|
|
1331
|
+
}
|
|
1332
|
+
selectedCommitSummary = buildSelectedCommitSummary(selectedCommits);
|
|
1333
|
+
trace7("commit-summary:prepared", selectedCommitSummary);
|
|
1334
|
+
console.log("\u23F3 \uC120\uD0DD\uD55C \uCEE4\uBC0B\uC758 \uD30C\uC77C \uBAA9\uB85D\uACFC diff\uB97C \uC815\uB9AC\uD558\uB294 \uC911\uC785\uB2C8\uB2E4...");
|
|
1335
|
+
trace7("files-command:run");
|
|
1336
|
+
const fileList = getSelectedCommitFiles(selectedCommits);
|
|
952
1337
|
trace7("files-command:done", `fileCount=${fileList.length}`);
|
|
1338
|
+
console.log(`\u{1F4C2} \uB9AC\uBDF0 \uB300\uC0C1 \uD30C\uC77C(${fileList.length}\uAC1C): ${formatReviewTargetFiles(fileList)}`);
|
|
953
1339
|
if (fileList.length === 0) {
|
|
954
1340
|
trace7("empty-file-list:exit");
|
|
955
|
-
console.log("\u2139\uFE0F \uBCC0\uACBD \
|
|
1341
|
+
console.log("\u2139\uFE0F \uC120\uD0DD\uD55C \uCEE4\uBC0B\uC5D0\uC11C \uB9AC\uBDF0 \uB300\uC0C1 \uD30C\uC77C \uBCC0\uACBD\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
|
|
956
1342
|
deleteTempDiff();
|
|
957
1343
|
process.exit(0);
|
|
958
1344
|
}
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1345
|
+
trace7("report-dir:create:start");
|
|
1346
|
+
createReportDirectory();
|
|
1347
|
+
trace7("report-dir:create:done");
|
|
1348
|
+
trace7("full-diff:build:start");
|
|
1349
|
+
const fullDiff = buildSelectedCommitDiff(selectedCommits);
|
|
962
1350
|
const nowStr = getNowString();
|
|
963
|
-
trace7("full-diff:done", `length=${fullDiff.length}`);
|
|
1351
|
+
trace7("full-diff:build:done", `length=${fullDiff.length}`);
|
|
964
1352
|
trace7("timestamp:created", nowStr);
|
|
965
1353
|
trace7("temp-diff:write:start", tempDiffPath);
|
|
966
1354
|
fs.writeFileSync(tempDiffPath, fullDiff);
|
|
@@ -971,15 +1359,19 @@ async function main() {
|
|
|
971
1359
|
trace7("saved-diff:copy:done", savedDiffPath);
|
|
972
1360
|
savedReportPath = getNextFilePath(REPORT_DIR, nowStr, ".md");
|
|
973
1361
|
trace7("saved-report:path", savedReportPath);
|
|
974
|
-
const promises = fileList.map(async (file) => {
|
|
1362
|
+
const promises = fileList.map(async (file, index) => {
|
|
975
1363
|
const tempOneFileDiffPath = `temp_diff_${file.replace(/\//g, "_")}.txt`;
|
|
976
1364
|
let command = "";
|
|
977
1365
|
try {
|
|
978
1366
|
trace7("file-review:start", file);
|
|
979
|
-
console.log(`\u{1F50D}
|
|
980
|
-
trace7("file-diff:
|
|
981
|
-
const fileDiff =
|
|
982
|
-
trace7("file-diff:done", `${file} | length=${fileDiff.length}`);
|
|
1367
|
+
console.log(`\u{1F50D} [${index + 1}/${fileList.length}] \uB9AC\uBDF0 \uC900\uBE44: ${file}`);
|
|
1368
|
+
trace7("file-diff:build:start", file);
|
|
1369
|
+
const fileDiff = buildSelectedFileDiff(selectedCommits, file);
|
|
1370
|
+
trace7("file-diff:build:done", `${file} | length=${fileDiff.length}`);
|
|
1371
|
+
if (!fileDiff.trim()) {
|
|
1372
|
+
trace7("file-diff:empty", file);
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
983
1375
|
trace7("file-temp-diff:write:start", tempOneFileDiffPath);
|
|
984
1376
|
fs.writeFileSync(tempOneFileDiffPath, fileDiff);
|
|
985
1377
|
trace7("file-temp-diff:write:done", tempOneFileDiffPath);
|
|
@@ -996,6 +1388,7 @@ async function main() {
|
|
|
996
1388
|
break;
|
|
997
1389
|
}
|
|
998
1390
|
trace7("file-command:create:done", `${file} | commandLength=${command.length}`);
|
|
1391
|
+
console.log(`\u23F3 [${index + 1}/${fileList.length}] \uB9AC\uBDF0 \uC9C4\uD589: ${file}`);
|
|
999
1392
|
trace7("file-command:exec:start", file);
|
|
1000
1393
|
const { stdout } = await execAsync(command, { maxBuffer: 1024 * 1024 * 20 });
|
|
1001
1394
|
const result = stdout.toString();
|
|
@@ -1007,7 +1400,6 @@ async function main() {
|
|
|
1007
1400
|
${result}
|
|
1008
1401
|
|
|
1009
1402
|
`;
|
|
1010
|
-
console.log(tempReport);
|
|
1011
1403
|
trace7("file-report:append:start", file);
|
|
1012
1404
|
fs.appendFileSync(savedReportPath, tempReport);
|
|
1013
1405
|
trace7("file-report:append:done", file);
|
|
@@ -1033,6 +1425,7 @@ ${command}`);
|
|
|
1033
1425
|
${JSON.stringify(
|
|
1034
1426
|
{
|
|
1035
1427
|
service,
|
|
1428
|
+
selectedCommitSummary: selectedCommitSummary || null,
|
|
1036
1429
|
file,
|
|
1037
1430
|
command: command || null,
|
|
1038
1431
|
tempOneFileDiffPath,
|
|
@@ -1107,6 +1500,7 @@ ${getErrorSummary(err)}
|
|
|
1107
1500
|
${JSON.stringify(
|
|
1108
1501
|
{
|
|
1109
1502
|
service: service || null,
|
|
1503
|
+
selectedCommitSummary: selectedCommitSummary || null,
|
|
1110
1504
|
tempDiffPath,
|
|
1111
1505
|
savedDiffPath: savedDiffPath || null,
|
|
1112
1506
|
savedReportPath: savedReportPath || null
|