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.
Files changed (45) hide show
  1. package/dist/common/helper.cjs +464 -52
  2. package/dist/common/helper.cjs.map +1 -1
  3. package/dist/common/helper.d.cts +85 -3
  4. package/dist/common/helper.d.ts +85 -3
  5. package/dist/common/helper.js +455 -53
  6. package/dist/common/helper.js.map +1 -1
  7. package/dist/common/types.d.cts +24 -1
  8. package/dist/common/types.d.ts +24 -1
  9. package/dist/pr-review/claude/claude-commander.cjs +50 -9
  10. package/dist/pr-review/claude/claude-commander.cjs.map +1 -1
  11. package/dist/pr-review/claude/claude-commander.js +50 -9
  12. package/dist/pr-review/claude/claude-commander.js.map +1 -1
  13. package/dist/pr-review/claude/installation-claude.cjs +41 -5
  14. package/dist/pr-review/claude/installation-claude.cjs.map +1 -1
  15. package/dist/pr-review/claude/installation-claude.js +41 -5
  16. package/dist/pr-review/claude/installation-claude.js.map +1 -1
  17. package/dist/pr-review/codex/codex-commander.cjs +49 -8
  18. package/dist/pr-review/codex/codex-commander.cjs.map +1 -1
  19. package/dist/pr-review/codex/codex-commander.js +49 -8
  20. package/dist/pr-review/codex/codex-commander.js.map +1 -1
  21. package/dist/pr-review/codex/installation-codex.cjs +41 -5
  22. package/dist/pr-review/codex/installation-codex.cjs.map +1 -1
  23. package/dist/pr-review/codex/installation-codex.js +41 -5
  24. package/dist/pr-review/codex/installation-codex.js.map +1 -1
  25. package/dist/pr-review/gemini/gemini-commander.cjs +75 -15
  26. package/dist/pr-review/gemini/gemini-commander.cjs.map +1 -1
  27. package/dist/pr-review/gemini/gemini-commander.js +75 -15
  28. package/dist/pr-review/gemini/gemini-commander.js.map +1 -1
  29. package/dist/pr-review/gemini/installation-gemini.cjs +41 -5
  30. package/dist/pr-review/gemini/installation-gemini.cjs.map +1 -1
  31. package/dist/pr-review/gemini/installation-gemini.js +41 -5
  32. package/dist/pr-review/gemini/installation-gemini.js.map +1 -1
  33. package/dist/pr-review/review-one-by-one.cjs +489 -95
  34. package/dist/pr-review/review-one-by-one.cjs.map +1 -1
  35. package/dist/pr-review/review-one-by-one.js +490 -96
  36. package/dist/pr-review/review-one-by-one.js.map +1 -1
  37. package/dist/pr-review/review.cjs +517 -94
  38. package/dist/pr-review/review.cjs.map +1 -1
  39. package/dist/pr-review/review.js +517 -94
  40. package/dist/pr-review/review.js.map +1 -1
  41. package/package.json +4 -7
  42. package/src/common/rules/coding-convention.md +393 -0
  43. package/src/common/rules/coding-convention.pdf +0 -0
  44. package/src/common/rules/naming-rule.md +347 -0
  45. package/src/common/rules/naming-rule.pdf +0 -0
@@ -18,14 +18,52 @@ var readline__default = /*#__PURE__*/_interopDefault(readline);
18
18
 
19
19
  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-one-by-one.cjs', document.baseURI).href))));
20
20
  var traceMessages = [];
21
- var rulesPath = path__default.default.resolve(__dirname$1, "../../src/common/rules/review-rules.md");
22
- var namingRulesPath = path__default.default.resolve(__dirname$1, "../../src/common/rules/naming-rule.md");
23
- var codingConventionRulesPath = path__default.default.resolve(__dirname$1, "../../src/common/rules/coding-convention.md");
24
- path__default.default.resolve(__dirname$1, "../../src/common/form/review-form.md");
25
- var reviewFormOneByOnePath = path__default.default.resolve(__dirname$1, "../../src/common/form/review-form-one-by-one.md");
21
+ var GEMINI_CLI_PACKAGE_NAME = "sales-frontend-gemini-cli";
22
+ var cachedPackageRootPath = "";
23
+ function isGeminiCliPackageRoot(directory) {
24
+ const packageJsonPath = path__default.default.join(directory, "package.json");
25
+ if (!fs__default.default.existsSync(packageJsonPath)) {
26
+ return false;
27
+ }
28
+ try {
29
+ const packageJson = JSON.parse(fs__default.default.readFileSync(packageJsonPath, "utf8"));
30
+ return packageJson.name === GEMINI_CLI_PACKAGE_NAME;
31
+ } catch {
32
+ return false;
33
+ }
34
+ }
35
+ function resolveGeminiCliPackageRoot(startDirectory = __dirname$1) {
36
+ if (cachedPackageRootPath) {
37
+ return cachedPackageRootPath;
38
+ }
39
+ let currentDirectory = startDirectory;
40
+ while (true) {
41
+ if (isGeminiCliPackageRoot(currentDirectory)) {
42
+ cachedPackageRootPath = currentDirectory;
43
+ return cachedPackageRootPath;
44
+ }
45
+ const parentDirectory = path__default.default.dirname(currentDirectory);
46
+ if (parentDirectory === currentDirectory) {
47
+ break;
48
+ }
49
+ currentDirectory = parentDirectory;
50
+ }
51
+ cachedPackageRootPath = path__default.default.resolve(startDirectory, "../..");
52
+ return cachedPackageRootPath;
53
+ }
54
+ function resolvePackageAssetPath(relativeFilePath) {
55
+ return path__default.default.resolve(resolveGeminiCliPackageRoot(), relativeFilePath);
56
+ }
57
+ var rulesPath = resolvePackageAssetPath("src/common/rules/review-rules.md");
58
+ var namingRulesPath = resolvePackageAssetPath("src/common/rules/naming-rule.md");
59
+ var codingConventionRulesPath = resolvePackageAssetPath("src/common/rules/coding-convention.md");
60
+ resolvePackageAssetPath("src/common/form/review-form.md");
61
+ var reviewFormOneByOnePath = resolvePackageAssetPath("src/common/form/review-form-one-by-one.md");
26
62
  var REPORT_DIR = ".review-report";
27
63
  var tempDiffPath = "temp_diff.txt";
28
64
  var AIServices = ["gemini", "claude", "codex"];
65
+ var COMMIT_FETCH_LIMIT = 20;
66
+ var COMMIT_SELECTION_WINDOW = 8;
29
67
  var ignoreList = [
30
68
  "package.json",
31
69
  "*.yml",
@@ -49,6 +87,137 @@ function clearTraceMessages() {
49
87
  function getTraceMessages() {
50
88
  return [...traceMessages];
51
89
  }
90
+ var ANSI = {
91
+ bold: "\x1B[1m",
92
+ cyan: "\x1B[36m",
93
+ dim: "\x1B[2m",
94
+ green: "\x1B[32m",
95
+ reset: "\x1B[0m",
96
+ yellow: "\x1B[33m"
97
+ };
98
+ var ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
99
+ var COMBINING_MARK_PATTERN = /\p{Mark}/u;
100
+ var GRAPHEME_SEGMENTER = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter("ko", { granularity: "grapheme" }) : null;
101
+ function getGitDiffPathspecs() {
102
+ return {
103
+ excludePatterns: ignoreList.map((item) => `:(exclude)${item}`),
104
+ includePatterns: ["*.ts", "*.tsx", "*.js", "*.jsx"]
105
+ };
106
+ }
107
+ function segmentGraphemes(value) {
108
+ if (!GRAPHEME_SEGMENTER) {
109
+ return [...value];
110
+ }
111
+ return [...GRAPHEME_SEGMENTER.segment(value)].map(({ segment }) => segment);
112
+ }
113
+ function isWideCodePoint(codePoint) {
114
+ 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);
115
+ }
116
+ function isEmojiCodePoint(codePoint) {
117
+ return codePoint >= 127462 && codePoint <= 127487 || codePoint >= 127744 && codePoint <= 129791 || codePoint >= 9728 && codePoint <= 10175;
118
+ }
119
+ function getGraphemeWidth(grapheme) {
120
+ let width = 0;
121
+ for (const character of grapheme) {
122
+ const codePoint = character.codePointAt(0);
123
+ if (!codePoint || COMBINING_MARK_PATTERN.test(character) || codePoint === 8205) {
124
+ continue;
125
+ }
126
+ if (codePoint >= 65024 && codePoint <= 65039 || codePoint >= 917760 && codePoint <= 917999) {
127
+ continue;
128
+ }
129
+ if (isWideCodePoint(codePoint) || isEmojiCodePoint(codePoint)) {
130
+ width = Math.max(width, 2);
131
+ continue;
132
+ }
133
+ width = Math.max(width, 1);
134
+ }
135
+ return width;
136
+ }
137
+ function tokenizePlainText(value) {
138
+ return segmentGraphemes(value).map((segment) => ({
139
+ value: segment,
140
+ visibleWidth: getGraphemeWidth(segment)
141
+ }));
142
+ }
143
+ function tokenizeVisibleText(value) {
144
+ const tokens = [];
145
+ let lastIndex = 0;
146
+ for (const match of value.matchAll(ANSI_PATTERN)) {
147
+ const index = match.index ?? 0;
148
+ if (index > lastIndex) {
149
+ tokens.push(...tokenizePlainText(value.slice(lastIndex, index)));
150
+ }
151
+ tokens.push({
152
+ value: match[0],
153
+ visibleWidth: 0
154
+ });
155
+ lastIndex = index + match[0].length;
156
+ }
157
+ if (lastIndex < value.length) {
158
+ tokens.push(...tokenizePlainText(value.slice(lastIndex)));
159
+ }
160
+ return tokens;
161
+ }
162
+ function truncateLineForTerminal(value, maxWidth) {
163
+ if (maxWidth <= 0) {
164
+ return "";
165
+ }
166
+ const tokens = tokenizeVisibleText(value);
167
+ const totalWidth = tokens.reduce((sum, token) => sum + token.visibleWidth, 0);
168
+ if (totalWidth <= maxWidth) {
169
+ return value;
170
+ }
171
+ const ellipsis = "...";
172
+ const ellipsisWidth = 3;
173
+ const targetWidth = Math.max(0, maxWidth - ellipsisWidth);
174
+ let usedWidth = 0;
175
+ let result = "";
176
+ for (const token of tokens) {
177
+ if (token.visibleWidth === 0) {
178
+ result += token.value;
179
+ continue;
180
+ }
181
+ if (usedWidth + token.visibleWidth > targetWidth) {
182
+ break;
183
+ }
184
+ result += token.value;
185
+ usedWidth += token.visibleWidth;
186
+ }
187
+ return `${result}${ellipsis}${ANSI.reset}`;
188
+ }
189
+ function fitLinesToTerminal(lines) {
190
+ const maxWidth = Math.max(20, (process.stdout.columns || 120) - 1);
191
+ return lines.map((line) => truncateLineForTerminal(line, maxWidth));
192
+ }
193
+ function runGitCommand(args4, options = {}) {
194
+ const { allowFailure = false, trimOutput = true } = options;
195
+ try {
196
+ const output = child_process.execFileSync("git", args4, {
197
+ encoding: "utf8",
198
+ maxBuffer: 1024 * 1024 * 20,
199
+ stdio: ["ignore", "pipe", "pipe"]
200
+ });
201
+ return trimOutput ? output.trim() : output;
202
+ } catch (error) {
203
+ helperTrace("git-command:failed", `${args4.join(" ")} | ${getErrorSummary(error)}`);
204
+ if (allowFailure) {
205
+ return "";
206
+ }
207
+ throw error;
208
+ }
209
+ }
210
+ function formatReviewTargetFiles(files, visibleCount = 5) {
211
+ if (files.length === 0) {
212
+ return "(\uC5C6\uC74C)";
213
+ }
214
+ const visibleFiles = files.slice(0, visibleCount);
215
+ const hiddenCount = Math.max(0, files.length - visibleFiles.length);
216
+ if (hiddenCount === 0) {
217
+ return visibleFiles.join(", ");
218
+ }
219
+ return `${visibleFiles.join(", ")} \uC678 ${hiddenCount}\uAC1C`;
220
+ }
52
221
  function createTraceLogger(scope, args4 = process.argv.slice(2)) {
53
222
  const enabled = isTestMode(args4);
54
223
  return (step, detail) => {
@@ -260,13 +429,83 @@ ${JSON.stringify(AIServices, null, 2)}
260
429
  }
261
430
  );
262
431
  }
263
- function getGitDiffFilter() {
264
- const includeExtensions = ["*.ts", "*.tsx", "*.js", "*.jsx"];
265
- const excludePatterns = ignoreList.map((item) => `:(exclude)${item}`);
266
- const quote = (pattern) => `"${pattern}"`;
267
- const includeParams = includeExtensions.map(quote).join(" ");
268
- const excludeParams = excludePatterns.map(quote).join(" ");
269
- return { includeParams, excludeParams };
432
+ function truncateCommitSubject(subject) {
433
+ if (subject.length <= 72) {
434
+ return subject;
435
+ }
436
+ return `${subject.slice(0, 69)}...`;
437
+ }
438
+ function getRecentCommitOptions() {
439
+ const output = runGitCommand(
440
+ ["log", `-${COMMIT_FETCH_LIMIT}`, "--date=relative", "--pretty=format:%h%x09%an%x09%ar%x09%s"],
441
+ { allowFailure: true }
442
+ );
443
+ if (!output) {
444
+ return [];
445
+ }
446
+ return output.split("\n").map((line) => {
447
+ const [hash = "", author = "", relativeDate = "", ...subjectParts] = line.split(" ");
448
+ const subject = subjectParts.join(" ").trim();
449
+ return {
450
+ author,
451
+ description: `${author} | ${relativeDate}`,
452
+ hash,
453
+ label: `${hash} | ${truncateCommitSubject(subject)}`,
454
+ relativeDate,
455
+ subject
456
+ };
457
+ });
458
+ }
459
+ function buildSelectedCommitSummary(commits) {
460
+ return commits.map((commit) => `- ${commit.hash} | ${commit.subject} | ${commit.author} | ${commit.relativeDate}`).join("\n");
461
+ }
462
+ function getReviewPathspecArgs() {
463
+ const { includePatterns, excludePatterns } = getGitDiffPathspecs();
464
+ return [...includePatterns, ...excludePatterns];
465
+ }
466
+ function buildSelectedCommitDiff(commits) {
467
+ const reviewPathspecArgs = getReviewPathspecArgs();
468
+ const sections = commits.map((commit) => {
469
+ const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", ...reviewPathspecArgs], {
470
+ allowFailure: true,
471
+ trimOutput: false
472
+ }).trim();
473
+ if (!diff) {
474
+ return "";
475
+ }
476
+ return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
477
+ }).filter(Boolean).join("\n\n");
478
+ if (!sections) {
479
+ return "";
480
+ }
481
+ return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", "# \uB9AC\uBDF0 \uB300\uC0C1 diff", sections].join("\n");
482
+ }
483
+ function getSelectedCommitFiles(commits) {
484
+ const reviewPathspecArgs = getReviewPathspecArgs();
485
+ const files = /* @__PURE__ */ new Set();
486
+ commits.forEach((commit) => {
487
+ const output = runGitCommand(["show", "--pretty=format:", "--name-only", commit.hash, "--", ...reviewPathspecArgs], {
488
+ allowFailure: true
489
+ });
490
+ output.split("\n").map((line) => line.trim()).filter(Boolean).forEach((filePath) => files.add(filePath));
491
+ });
492
+ return [...files];
493
+ }
494
+ function buildSelectedFileDiff(commits, filePath) {
495
+ const sections = commits.map((commit) => {
496
+ const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", filePath], {
497
+ allowFailure: true,
498
+ trimOutput: false
499
+ }).trim();
500
+ if (!diff) {
501
+ return "";
502
+ }
503
+ return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
504
+ }).filter(Boolean).join("\n\n");
505
+ if (!sections) {
506
+ return "";
507
+ }
508
+ return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", `# \uD30C\uC77C: ${filePath}`, sections].join("\n\n");
270
509
  }
271
510
  function openReport(reportPath) {
272
511
  const resolvedPath = path__default.default.resolve(reportPath);
@@ -317,59 +556,166 @@ function openReport(reportPath) {
317
556
  helperTrace("open-report:unsupported-platform", platform);
318
557
  console.error(`\u26A0\uFE0F \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD50C\uB7AB\uD3FC\uC785\uB2C8\uB2E4: ${platform}`);
319
558
  }
320
- function getDiffArgs() {
321
- const args4 = process.argv.slice(2);
322
- const commitIndex = args4.indexOf("--commit");
323
- const { includeParams, excludeParams } = getGitDiffFilter();
324
- helperTrace("diff-args:resolve:start", `args=${JSON.stringify(args4)}`);
325
- let diffArgs = "";
326
- if (commitIndex !== -1) {
327
- const commitHash = args4[commitIndex + 1];
328
- if (!commitHash) {
329
- helperTrace("diff-args:commit-hash-missing");
330
- exitWithError("\u274C \uCEE4\uBC0B \uD574\uC2DC\uAC00 \uC81C\uACF5\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.", {
331
- scope: "helper:getDiffArgs",
332
- args: args4
333
- });
559
+ function ensureInteractiveSelectionAvailable(scope, message) {
560
+ if (process.stdin.isTTY && process.stdout.isTTY && typeof process.stdin.setRawMode === "function") {
561
+ return;
562
+ }
563
+ helperTrace(`${scope}:tty-missing`);
564
+ exitWithError(message, {
565
+ scope: `helper:${scope}`
566
+ });
567
+ }
568
+ function renderSelectionBlock(lines, previousLineCount) {
569
+ if (previousLineCount > 0) {
570
+ readline__default.default.moveCursor(process.stdout, 0, -previousLineCount);
571
+ readline__default.default.clearScreenDown(process.stdout);
572
+ }
573
+ const fittedLines = fitLinesToTerminal(lines);
574
+ process.stdout.write(`${fittedLines.join("\n")}
575
+ `);
576
+ return fittedLines.length;
577
+ }
578
+ function getSelectionWindowRange(optionCount, selectedIndex, windowSize) {
579
+ if (optionCount <= windowSize) {
580
+ return {
581
+ end: optionCount,
582
+ start: 0
583
+ };
584
+ }
585
+ const halfWindow = Math.floor(windowSize / 2);
586
+ const maxStart = optionCount - windowSize;
587
+ const start = Math.max(0, Math.min(selectedIndex - halfWindow, maxStart));
588
+ return {
589
+ end: Math.min(optionCount, start + windowSize),
590
+ start
591
+ };
592
+ }
593
+ function buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize) {
594
+ const { start, end } = getSelectionWindowRange(options.length, selectedIndex, windowSize);
595
+ const lines = [
596
+ `${ANSI.bold}${question}${ANSI.reset}`,
597
+ `${ANSI.dim}\u2191\u2193 \uC774\uB3D9 | Space \uC120\uD0DD/\uD574\uC81C | Enter \uC644\uB8CC | Esc \uCDE8\uC18C${ANSI.reset}`,
598
+ `${ANSI.dim}\uC120\uD0DD\uB428: ${toggled.size}\uAC1C / \uC804\uCCB4: ${options.length}\uAC1C${ANSI.reset}`
599
+ ];
600
+ for (let index = start; index < end; index += 1) {
601
+ const option = options[index];
602
+ if (!option) {
603
+ continue;
334
604
  }
335
- const nextArg = args4[commitIndex + 2];
336
- let n = 0;
337
- if (nextArg && !nextArg.startsWith("--")) {
338
- n = parseInt(nextArg, 10);
339
- if (isNaN(n)) {
340
- n = 0;
341
- }
605
+ const cursor = index === selectedIndex ? `${ANSI.cyan}>${ANSI.reset}` : " ";
606
+ const checked = toggled.has(index) ? `${ANSI.green}\u2611${ANSI.reset}` : "\u2610";
607
+ const description = option.description ? ` ${ANSI.dim}${option.description}${ANSI.reset}` : "";
608
+ lines.push(`${cursor} ${checked} ${option.label}${description}`);
609
+ }
610
+ if (options.length > windowSize) {
611
+ lines.push(`${ANSI.dim}\uD45C\uC2DC \uBC94\uC704: ${start + 1}-${end} / ${options.length}${ANSI.reset}`);
612
+ }
613
+ return lines;
614
+ }
615
+ async function showMultiSelect(question, options, windowSize = COMMIT_SELECTION_WINDOW) {
616
+ ensureInteractiveSelectionAvailable("showMultiSelect", "\u274C \uCEE4\uBC0B \uC120\uD0DD \uBAA8\uB2EC\uC740 TTY \uD658\uACBD\uC5D0\uC11C\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
617
+ let selectedIndex = 0;
618
+ let renderedLineCount = 0;
619
+ const toggled = /* @__PURE__ */ new Set();
620
+ const rl = readline__default.default.createInterface({
621
+ input: process.stdin,
622
+ output: process.stdout,
623
+ terminal: true
624
+ });
625
+ process.stdout.write("\x1B[?25l");
626
+ const cleanup = () => {
627
+ if (renderedLineCount > 0) {
628
+ readline__default.default.moveCursor(process.stdout, 0, -renderedLineCount);
629
+ readline__default.default.clearScreenDown(process.stdout);
630
+ renderedLineCount = 0;
342
631
  }
343
- helperTrace("diff-args:commit-mode", `${commitHash}~${n + 1} ${commitHash}`);
344
- console.log(`\u2139\uFE0F \uCEE4\uBC0B '${commitHash}' ${n > 0 ? ` \uD3EC\uD568 \uCD1D ${n + 1}\uAC1C\uC758 \uCEE4\uBC0B` : ""}\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...`);
345
- diffArgs = `${commitHash}~${n + 1} ${commitHash}`;
346
- } else {
347
- try {
348
- helperTrace("diff-args:unstaged-check:start");
349
- const check = child_process.execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();
350
- if (!check.trim()) {
351
- helperTrace("diff-args:unstaged-check:empty", "use HEAD~1 HEAD");
352
- console.log("\u2139\uFE0F Unstaged \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uB9C8\uC9C0\uB9C9 \uCEE4\uBC0B(HEAD)\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...");
353
- diffArgs = "HEAD~1 HEAD";
354
- } else {
355
- helperTrace("diff-args:unstaged-check:has-changes", `length=${check.length}`);
632
+ process.stdin.removeListener("data", onData);
633
+ process.stdin.setRawMode(false);
634
+ process.stdin.pause();
635
+ rl.close();
636
+ process.stdout.write("\x1B[?25h");
637
+ };
638
+ const render = () => {
639
+ const lines = buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize);
640
+ renderedLineCount = renderSelectionBlock(lines, renderedLineCount);
641
+ };
642
+ const confirmSelection = (resolve) => {
643
+ const values = [...toggled].sort((left, right) => left - right).map((index) => options[index]?.value).filter((value) => value !== void 0);
644
+ cleanup();
645
+ resolve(values);
646
+ };
647
+ const cancelSelection = (resolve) => {
648
+ cleanup();
649
+ resolve([]);
650
+ };
651
+ let onData = (_data) => {
652
+ };
653
+ render();
654
+ return new Promise((resolve) => {
655
+ onData = (data) => {
656
+ const key = data.toString();
657
+ if (key === "") {
658
+ cleanup();
659
+ process.exit(0);
356
660
  }
357
- } catch (error) {
358
- helperTrace("diff-args:unstaged-check:failed", getErrorSummary(error));
359
- }
661
+ if (key === "\x1B") {
662
+ cancelSelection(resolve);
663
+ return;
664
+ }
665
+ if (key === "\x1B[A") {
666
+ selectedIndex = (selectedIndex - 1 + options.length) % options.length;
667
+ render();
668
+ return;
669
+ }
670
+ if (key === "\x1B[B") {
671
+ selectedIndex = (selectedIndex + 1) % options.length;
672
+ render();
673
+ return;
674
+ }
675
+ if (key === " ") {
676
+ if (toggled.has(selectedIndex)) {
677
+ toggled.delete(selectedIndex);
678
+ } else {
679
+ toggled.add(selectedIndex);
680
+ }
681
+ render();
682
+ return;
683
+ }
684
+ if (key === "\r" || key === "\n") {
685
+ confirmSelection(resolve);
686
+ }
687
+ };
688
+ process.stdin.setRawMode(true);
689
+ process.stdin.resume();
690
+ process.stdin.on("data", onData);
691
+ });
692
+ }
693
+ async function selectReviewCommits() {
694
+ const commits = getRecentCommitOptions();
695
+ if (commits.length === 0) {
696
+ console.log("\u2139\uFE0F \uB9AC\uBDF0\uD560 \uCD5C\uADFC \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
697
+ return [];
360
698
  }
361
- helperTrace("diff-args:resolve:done", diffArgs || "(default)");
362
- return diffArgs;
699
+ return showMultiSelect(
700
+ "\uB9AC\uBDF0\uD560 \uCEE4\uBC0B\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.",
701
+ commits.map((commit) => ({
702
+ description: commit.description,
703
+ label: commit.label,
704
+ value: commit
705
+ })),
706
+ COMMIT_SELECTION_WINDOW
707
+ );
363
708
  }
364
709
  async function showSelectionAIService() {
365
710
  const selectedServiceFromArgs = parseServiceFromArgs();
366
711
  if (selectedServiceFromArgs) {
367
712
  helperTrace("show-selection:from-args", selectedServiceFromArgs);
368
713
  console.log(`
369
- \u2705 \x1B[32m${selectedServiceFromArgs}\x1B[0m \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (--service)
714
+ \u2705 ${ANSI.green}${selectedServiceFromArgs}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (--service)
370
715
  `);
371
716
  return selectedServiceFromArgs;
372
717
  }
718
+ 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.");
373
719
  helperTrace("show-selection:interactive:start");
374
720
  let selectedIndex = 0;
375
721
  const rl = readline__default.default.createInterface({
@@ -387,11 +733,12 @@ async function showSelectionAIService() {
387
733
  helperTrace("show-selection:interactive:render", AIServices[selectedIndex] || "unknown");
388
734
  readline__default.default.clearScreenDown(process.stdout);
389
735
  process.stdout.write(
390
- "\u{1F916} AI \uC11C\uBE44\uC2A4\uB97C \uC120\uD0DD\uD574\uC8FC\uC138\uC694 (\x1B[33m\u2191\u2193 \uBC29\uD5A5\uD0A4\x1B[0m \uC774\uB3D9, \x1B[33mEnter\x1B[0m \uC120\uD0DD):\n"
736
+ `\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):
737
+ `
391
738
  );
392
739
  AIServices.forEach((service, index) => {
393
740
  if (index === selectedIndex) {
394
- process.stdout.write(` \x1B[36m>\x1B[0m \x1B[36m\u25C9\x1B[0m \x1B[1m${service}\x1B[0m
741
+ process.stdout.write(` ${ANSI.cyan}>${ANSI.reset} ${ANSI.cyan}\u25C9${ANSI.reset} ${ANSI.bold}${service}${ANSI.reset}
395
742
  `);
396
743
  } else {
397
744
  process.stdout.write(` \u25EF ${service}
@@ -421,7 +768,7 @@ async function showSelectionAIService() {
421
768
  rl.close();
422
769
  process.stdout.write("\x1B[?25h");
423
770
  console.log(`
424
- \u2705 \x1B[32m${AIServices[selectedIndex]}\x1B[0m \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
771
+ \u2705 ${ANSI.green}${AIServices[selectedIndex]}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
425
772
  `);
426
773
  const result = AIServices[selectedIndex];
427
774
  if (result) {
@@ -448,6 +795,11 @@ function getArgValue(flag) {
448
795
  }
449
796
  return args[index + 1];
450
797
  }
798
+ function printNotice(message) {
799
+ if (args.includes("--test")) {
800
+ console.warn(message);
801
+ }
802
+ }
451
803
  function toUnique(values) {
452
804
  const seen = /* @__PURE__ */ new Set();
453
805
  return values.filter((value) => {
@@ -471,11 +823,11 @@ function resolveReasoningEffort() {
471
823
  const normalized = normalizeEffort(customReasoningEffort);
472
824
  trace("reasoning:custom", `${customReasoningEffort} -> ${normalized}`);
473
825
  if (customReasoningEffort === "minimal") {
474
- console.warn("\u26A0\uFE0F Claude\uB294 minimal\uC744 \uC9C1\uC811 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC544 low\uB85C \uB9E4\uD551\uD569\uB2C8\uB2E4.");
826
+ printNotice("\u26A0\uFE0F Claude\uB294 minimal\uC744 \uC9C1\uC811 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC544 low\uB85C \uB9E4\uD551\uD569\uB2C8\uB2E4.");
475
827
  }
476
828
  return normalized;
477
829
  }
478
- console.warn(
830
+ printNotice(
479
831
  `\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS.join(
480
832
  ", "
481
833
  )}`
@@ -518,7 +870,7 @@ function buildClaudeExecCommand(options) {
518
870
  const modelOption = model ? `--model ${shellQuote(model)}` : "";
519
871
  const fallbackOption = model && fallbackModel ? `--fallback-model ${shellQuote(fallbackModel)}` : "";
520
872
  const effortOption = `--effort ${shellQuote(effort)}`;
521
- const appendedPromptFiles = systemPromptFiles.map((path2) => `--append-system-prompt-file ${shellQuote(path2)}`).join(" ");
873
+ const appendedPromptFiles = systemPromptFiles.map((path3) => `--append-system-prompt-file ${shellQuote(path3)}`).join(" ");
522
874
  return `cat ${shellQuote(tempDiffPath2)} | claude ${[
523
875
  modelOption,
524
876
  fallbackOption,
@@ -551,13 +903,13 @@ var createClaudeCommand = (tempDiffPath2, reviewFormPath2) => {
551
903
  trace("model:candidates", modelCandidates.join(", "));
552
904
  trace("command:candidates:count", String(modelCandidates.length + 1));
553
905
  if (customModel) {
554
- console.warn(
906
+ printNotice(
555
907
  `\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
556
908
  " -> "
557
909
  )}) \uBC0F \uAE30\uBCF8 \uBAA8\uB378 \uC21C\uC73C\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
558
910
  );
559
911
  } else {
560
- console.warn(
912
+ printNotice(
561
913
  `\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.`
562
914
  );
563
915
  }
@@ -637,6 +989,11 @@ function getArgValue2(flag) {
637
989
  }
638
990
  return args2[index + 1];
639
991
  }
992
+ function printNotice2(message) {
993
+ if (args2.includes("--test")) {
994
+ console.warn(message);
995
+ }
996
+ }
640
997
  function resolveReasoningEffort2() {
641
998
  const customReasoningEffort = getArgValue2("--reasoning-effort");
642
999
  if (customReasoningEffort) {
@@ -644,7 +1001,7 @@ function resolveReasoningEffort2() {
644
1001
  trace3("reasoning:custom", customReasoningEffort);
645
1002
  return customReasoningEffort;
646
1003
  }
647
- console.warn(
1004
+ printNotice2(
648
1005
  `\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS2.join(
649
1006
  ", "
650
1007
  )}`
@@ -688,14 +1045,14 @@ ${reviewFormLine || "- (\uC5C6\uC74C)"}
688
1045
  trace3("prompt:prepared", `length=${prompt.length}`);
689
1046
  let command = "";
690
1047
  if (customModel) {
691
- console.warn("\u26A0\uFE0F \uC9C0\uC815\uD55C \uBAA8\uB378\uC774 \uC5C6\uB294 \uACBD\uC6B0, \uC5D0\uB7EC\uAC00 \uBC1C\uC0DD\uD558\uB2C8 \uC8FC\uC758\uD558\uC138\uC694.");
1048
+ 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.");
692
1049
  trace3("model:custom", customModel);
693
1050
  command = buildCodexExecCommand(prompt, reasoningEffort, customModel);
694
1051
  } else {
695
1052
  const preferredModelAlias = "gpt-5";
696
1053
  const aliasCommand = buildCodexExecCommand(prompt, reasoningEffort, preferredModelAlias);
697
1054
  const fallbackCommand = buildCodexExecCommand(prompt, reasoningEffort);
698
- console.warn(
1055
+ printNotice2(
699
1056
  `\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.`
700
1057
  );
701
1058
  trace3("model:alias-first", preferredModelAlias);
@@ -756,6 +1113,11 @@ function getArgValue3(flag) {
756
1113
  }
757
1114
  return args3[index + 1];
758
1115
  }
1116
+ function printNotice3(message) {
1117
+ if (args3.includes("--test")) {
1118
+ console.warn(message);
1119
+ }
1120
+ }
759
1121
  function toUnique2(values) {
760
1122
  const seen = /* @__PURE__ */ new Set();
761
1123
  return values.filter((value) => {
@@ -773,7 +1135,7 @@ function resolveReasoningEffort3() {
773
1135
  trace5("reasoning:custom", customReasoningEffort);
774
1136
  return customReasoningEffort;
775
1137
  }
776
- console.warn(
1138
+ printNotice3(
777
1139
  `\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS3.join(
778
1140
  ", "
779
1141
  )}`
@@ -835,38 +1197,57 @@ function buildGeminiExecCommand(prompt, model) {
835
1197
  const modelOption = model ? `--model ${shellQuote3(model)}` : "";
836
1198
  return `gemini ${[modelOption, "-p", shellQuote3(prompt)].filter(Boolean).join(" ")}`;
837
1199
  }
1200
+ function toGeminiFileReference(filePath) {
1201
+ return `@${filePath}`;
1202
+ }
1203
+ function buildGeminiFileReferenceSection(files) {
1204
+ const existingFiles = files.filter((file) => fs__default.default.existsSync(file.path));
1205
+ return {
1206
+ count: existingFiles.length,
1207
+ lines: existingFiles.map((file) => `- ${file.display}: ${toGeminiFileReference(file.path)}`)
1208
+ };
1209
+ }
838
1210
  var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
839
1211
  trace5("createGeminiCommand:start", `tempDiffPath=${tempDiffPath2}, reviewFormPath=${reviewFormPath2}`);
840
1212
  const customModel = getArgValue3("--model");
841
1213
  const reasoningEffort = resolveReasoningEffort3();
842
1214
  const primaryAlias = resolvePrimaryAlias2(reasoningEffort);
843
1215
  const aliasFallbacks = toUnique2(getAliasFallbacks2(primaryAlias));
1216
+ const resolvedTempDiffPath = path__default.default.resolve(tempDiffPath2);
1217
+ const resolvedReviewFormPath = path__default.default.resolve(reviewFormPath2);
844
1218
  const rules = [
845
1219
  { path: rulesPath, display: "\uB8F0\uC14B" },
846
1220
  { path: namingRulesPath, display: "\uB124\uC774\uBC0D \uADDC\uCE59" },
847
1221
  { path: codingConventionRulesPath, display: "\uCF54\uB529 \uCEE8\uBCA4\uC158" }
848
1222
  ];
849
- const validRules = rules.filter((rule) => fs__default.default.existsSync(rule.path)).map((rule) => `@${rule.path}`).join(", ");
850
- const rulesCount = validRules ? validRules.split(",").length : 0;
851
- trace5("rules:loaded", `count=${rulesCount}`);
852
- const reviewFormRef = fs__default.default.existsSync(reviewFormPath2) ? `@${reviewFormPath2}` : "(\uC5C6\uC74C)";
853
- trace5("reviewForm:status", reviewFormRef === "(\uC5C6\uC74C)" ? "missing" : "exists");
1223
+ const ruleSection = buildGeminiFileReferenceSection(rules);
1224
+ trace5("rules:loaded", `count=${ruleSection.count}`);
1225
+ const reviewFormExists = fs__default.default.existsSync(resolvedReviewFormPath);
1226
+ const reviewFormLine = reviewFormExists ? `- \uB9AC\uBDF0 \uC591\uC2DD: ${toGeminiFileReference(resolvedReviewFormPath)}` : "- \uB9AC\uBDF0 \uC591\uC2DD: (\uC5C6\uC74C)";
1227
+ trace5("reviewForm:status", reviewFormExists ? "exists" : "missing");
854
1228
  const reasoningInstruction = getReasoningInstruction(reasoningEffort);
855
- const prompt = `\uB2E4\uC74C \uADDC\uCE59\uB4E4\uC744 \uCC38\uACE0\uD574\uC11C(${validRules || "(\uC5C6\uC74C)"}) \uC774 diff(@${tempDiffPath2})\uB97C \uB9AC\uBDF0\uD574\uC918.
856
- \uB9AC\uBDF0 \uC591\uC2DD\uC740 ${reviewFormRef} \uC5D0 \uB9DE\uCDB0\uC11C \uC791\uC131\uD574\uC918.
1229
+ const prompt = `\uC544\uB798 \uD30C\uC77C\uB4E4\uC744 \uC21C\uC11C\uB300\uB85C \uC77D\uACE0 \uCF54\uB4DC \uB9AC\uBDF0\uB97C \uC9C4\uD589\uD574\uC918.
1230
+ \uADDC\uCE59 \uD30C\uC77C:
1231
+ ${ruleSection.lines.join("\n") || "- (\uC5C6\uC74C)"}
1232
+ \uB9AC\uBDF0 \uC591\uC2DD \uD30C\uC77C:
1233
+ ${reviewFormLine}
1234
+ \uB9AC\uBDF0 \uB300\uC0C1 diff \uD30C\uC77C:
1235
+ - \uB9AC\uBDF0 \uB300\uC0C1 diff: ${toGeminiFileReference(resolvedTempDiffPath)}
1236
+
1237
+ \uBC18\uB4DC\uC2DC \uB9AC\uBDF0 \uC591\uC2DD\uC5D0 \uB9DE\uCDB0 \uC791\uC131\uD574\uC918.
857
1238
  \uCD94\uB860 \uAC15\uB3C4 \uC9C0\uCE68: ${reasoningInstruction}`;
858
1239
  trace5("prompt:prepared", `length=${prompt.length}`);
859
1240
  const modelCandidates = toUnique2(customModel ? [customModel, ...aliasFallbacks] : aliasFallbacks);
860
1241
  trace5("model:candidates", modelCandidates.join(", "));
861
1242
  trace5("command:candidates:count", String(modelCandidates.length + 1));
862
1243
  if (customModel) {
863
- console.warn(
1244
+ printNotice3(
864
1245
  `\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
865
1246
  " -> "
866
1247
  )}) \uBC0F \uAE30\uBCF8 \uBAA8\uB378 \uC21C\uC73C\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
867
1248
  );
868
1249
  } else {
869
- console.warn(
1250
+ printNotice3(
870
1251
  `\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.`
871
1252
  );
872
1253
  }
@@ -925,6 +1306,7 @@ async function main() {
925
1306
  let service = "";
926
1307
  let savedDiffPath = "";
927
1308
  let savedReportPath = "";
1309
+ let selectedCommitSummary = "";
928
1310
  try {
929
1311
  trace7("service-selection:start");
930
1312
  service = await showSelectionAIService();
@@ -948,29 +1330,35 @@ async function main() {
948
1330
  }
949
1331
  trace7("review-flow:start");
950
1332
  console.log("\u{1F680} AI Code Review\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...");
951
- trace7("report-dir:create:start");
952
- createReportDirectory();
953
- trace7("report-dir:create:done");
954
- const { includeParams, excludeParams } = getGitDiffFilter();
955
- trace7("diff-filter:loaded", `include=${includeParams} | exclude=${excludeParams}`);
956
- trace7("diff-args:build:start");
957
- const diffArgs = getDiffArgs();
958
- trace7("diff-args:build:done", `diffArgs=${diffArgs || "(default)"}`);
959
- const filesCommand = `git diff --name-only ${diffArgs} -- ${includeParams} ${excludeParams}`;
960
- trace7("files-command:run", filesCommand);
961
- const fileList = child_process.execSync(filesCommand).toString().split("\n").filter(Boolean);
1333
+ trace7("commit-selection:start");
1334
+ const selectedCommits = await selectReviewCommits();
1335
+ trace7("commit-selection:done", `count=${selectedCommits.length}`);
1336
+ if (selectedCommits.length === 0) {
1337
+ trace7("commit-selection:empty");
1338
+ console.log("\u2139\uFE0F \uC120\uD0DD\uB41C \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
1339
+ deleteTempDiff();
1340
+ process.exit(0);
1341
+ }
1342
+ selectedCommitSummary = buildSelectedCommitSummary(selectedCommits);
1343
+ trace7("commit-summary:prepared", selectedCommitSummary);
1344
+ console.log("\u23F3 \uC120\uD0DD\uD55C \uCEE4\uBC0B\uC758 \uD30C\uC77C \uBAA9\uB85D\uACFC diff\uB97C \uC815\uB9AC\uD558\uB294 \uC911\uC785\uB2C8\uB2E4...");
1345
+ trace7("files-command:run");
1346
+ const fileList = getSelectedCommitFiles(selectedCommits);
962
1347
  trace7("files-command:done", `fileCount=${fileList.length}`);
1348
+ console.log(`\u{1F4C2} \uB9AC\uBDF0 \uB300\uC0C1 \uD30C\uC77C(${fileList.length}\uAC1C): ${formatReviewTargetFiles(fileList)}`);
963
1349
  if (fileList.length === 0) {
964
1350
  trace7("empty-file-list:exit");
965
- console.log("\u2139\uFE0F \uBCC0\uACBD \uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
1351
+ 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.");
966
1352
  deleteTempDiff();
967
1353
  process.exit(0);
968
1354
  }
969
- const fullDiffCommand = `git diff ${diffArgs} -- ${includeParams} ${excludeParams}`;
970
- trace7("full-diff:run");
971
- const fullDiff = child_process.execSync(fullDiffCommand).toString();
1355
+ trace7("report-dir:create:start");
1356
+ createReportDirectory();
1357
+ trace7("report-dir:create:done");
1358
+ trace7("full-diff:build:start");
1359
+ const fullDiff = buildSelectedCommitDiff(selectedCommits);
972
1360
  const nowStr = getNowString();
973
- trace7("full-diff:done", `length=${fullDiff.length}`);
1361
+ trace7("full-diff:build:done", `length=${fullDiff.length}`);
974
1362
  trace7("timestamp:created", nowStr);
975
1363
  trace7("temp-diff:write:start", tempDiffPath);
976
1364
  fs__default.default.writeFileSync(tempDiffPath, fullDiff);
@@ -981,15 +1369,19 @@ async function main() {
981
1369
  trace7("saved-diff:copy:done", savedDiffPath);
982
1370
  savedReportPath = getNextFilePath(REPORT_DIR, nowStr, ".md");
983
1371
  trace7("saved-report:path", savedReportPath);
984
- const promises = fileList.map(async (file) => {
1372
+ const promises = fileList.map(async (file, index) => {
985
1373
  const tempOneFileDiffPath = `temp_diff_${file.replace(/\//g, "_")}.txt`;
986
1374
  let command = "";
987
1375
  try {
988
1376
  trace7("file-review:start", file);
989
- console.log(`\u{1F50D} Reviewing: ${file}...`);
990
- trace7("file-diff:run", file);
991
- const fileDiff = child_process.execSync(`git diff ${diffArgs} -- "${file}"`).toString();
992
- trace7("file-diff:done", `${file} | length=${fileDiff.length}`);
1377
+ console.log(`\u{1F50D} [${index + 1}/${fileList.length}] \uB9AC\uBDF0 \uC900\uBE44: ${file}`);
1378
+ trace7("file-diff:build:start", file);
1379
+ const fileDiff = buildSelectedFileDiff(selectedCommits, file);
1380
+ trace7("file-diff:build:done", `${file} | length=${fileDiff.length}`);
1381
+ if (!fileDiff.trim()) {
1382
+ trace7("file-diff:empty", file);
1383
+ return;
1384
+ }
993
1385
  trace7("file-temp-diff:write:start", tempOneFileDiffPath);
994
1386
  fs__default.default.writeFileSync(tempOneFileDiffPath, fileDiff);
995
1387
  trace7("file-temp-diff:write:done", tempOneFileDiffPath);
@@ -1006,6 +1398,7 @@ async function main() {
1006
1398
  break;
1007
1399
  }
1008
1400
  trace7("file-command:create:done", `${file} | commandLength=${command.length}`);
1401
+ console.log(`\u23F3 [${index + 1}/${fileList.length}] \uB9AC\uBDF0 \uC9C4\uD589: ${file}`);
1009
1402
  trace7("file-command:exec:start", file);
1010
1403
  const { stdout } = await execAsync(command, { maxBuffer: 1024 * 1024 * 20 });
1011
1404
  const result = stdout.toString();
@@ -1017,7 +1410,6 @@ async function main() {
1017
1410
  ${result}
1018
1411
 
1019
1412
  `;
1020
- console.log(tempReport);
1021
1413
  trace7("file-report:append:start", file);
1022
1414
  fs__default.default.appendFileSync(savedReportPath, tempReport);
1023
1415
  trace7("file-report:append:done", file);
@@ -1043,6 +1435,7 @@ ${command}`);
1043
1435
  ${JSON.stringify(
1044
1436
  {
1045
1437
  service,
1438
+ selectedCommitSummary: selectedCommitSummary || null,
1046
1439
  file,
1047
1440
  command: command || null,
1048
1441
  tempOneFileDiffPath,
@@ -1117,6 +1510,7 @@ ${getErrorSummary(err)}
1117
1510
  ${JSON.stringify(
1118
1511
  {
1119
1512
  service: service || null,
1513
+ selectedCommitSummary: selectedCommitSummary || null,
1120
1514
  tempDiffPath,
1121
1515
  savedDiffPath: savedDiffPath || null,
1122
1516
  savedReportPath: savedReportPath || null