sales-frontend-gemini-cli 0.4.2 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/common/helper.cjs +581 -53
  2. package/dist/common/helper.cjs.map +1 -1
  3. package/dist/common/helper.d.cts +114 -3
  4. package/dist/common/helper.d.ts +114 -3
  5. package/dist/common/helper.js +570 -54
  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 +60 -12
  10. package/dist/pr-review/claude/claude-commander.cjs.map +1 -1
  11. package/dist/pr-review/claude/claude-commander.js +60 -12
  12. package/dist/pr-review/claude/claude-commander.js.map +1 -1
  13. package/dist/pr-review/claude/installation-claude.cjs +42 -6
  14. package/dist/pr-review/claude/installation-claude.cjs.map +1 -1
  15. package/dist/pr-review/claude/installation-claude.js +42 -6
  16. package/dist/pr-review/claude/installation-claude.js.map +1 -1
  17. package/dist/pr-review/codex/codex-commander.cjs +63 -12
  18. package/dist/pr-review/codex/codex-commander.cjs.map +1 -1
  19. package/dist/pr-review/codex/codex-commander.d.cts +1 -1
  20. package/dist/pr-review/codex/codex-commander.d.ts +1 -1
  21. package/dist/pr-review/codex/codex-commander.js +63 -12
  22. package/dist/pr-review/codex/codex-commander.js.map +1 -1
  23. package/dist/pr-review/codex/installation-codex.cjs +42 -6
  24. package/dist/pr-review/codex/installation-codex.cjs.map +1 -1
  25. package/dist/pr-review/codex/installation-codex.js +42 -6
  26. package/dist/pr-review/codex/installation-codex.js.map +1 -1
  27. package/dist/pr-review/gemini/gemini-commander.cjs +76 -16
  28. package/dist/pr-review/gemini/gemini-commander.cjs.map +1 -1
  29. package/dist/pr-review/gemini/gemini-commander.js +76 -16
  30. package/dist/pr-review/gemini/gemini-commander.js.map +1 -1
  31. package/dist/pr-review/gemini/installation-gemini.cjs +42 -6
  32. package/dist/pr-review/gemini/installation-gemini.cjs.map +1 -1
  33. package/dist/pr-review/gemini/installation-gemini.js +42 -6
  34. package/dist/pr-review/gemini/installation-gemini.js.map +1 -1
  35. package/dist/pr-review/review-one-by-one.cjs +699 -106
  36. package/dist/pr-review/review-one-by-one.cjs.map +1 -1
  37. package/dist/pr-review/review-one-by-one.js +700 -107
  38. package/dist/pr-review/review-one-by-one.js.map +1 -1
  39. package/dist/pr-review/review.cjs +722 -105
  40. package/dist/pr-review/review.cjs.map +1 -1
  41. package/dist/pr-review/review.js +722 -105
  42. package/dist/pr-review/review.js.map +1 -1
  43. package/package.json +4 -7
  44. package/src/common/rules/coding-convention.md +393 -0
  45. package/src/common/rules/coding-convention.pdf +0 -0
  46. package/src/common/rules/naming-rule.md +347 -0
  47. package/src/common/rules/naming-rule.pdf +0 -0
@@ -17,14 +17,52 @@ var readline__default = /*#__PURE__*/_interopDefault(readline);
17
17
  // src/common/helper.ts
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('helper.cjs', document.baseURI).href))));
19
19
  var traceMessages = [];
20
- var rulesPath = path__default.default.resolve(__dirname$1, "../../src/common/rules/review-rules.md");
21
- var namingRulesPath = path__default.default.resolve(__dirname$1, "../../src/common/rules/naming-rule.md");
22
- var codingConventionRulesPath = path__default.default.resolve(__dirname$1, "../../src/common/rules/coding-convention.md");
23
- var reviewFormPath = path__default.default.resolve(__dirname$1, "../../src/common/form/review-form.md");
24
- var reviewFormOneByOnePath = path__default.default.resolve(__dirname$1, "../../src/common/form/review-form-one-by-one.md");
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
+ var reviewFormOneByOnePath = resolvePackageAssetPath("src/common/form/review-form-one-by-one.md");
25
61
  var REPORT_DIR = ".review-report";
26
62
  var tempDiffPath = "temp_diff.txt";
27
63
  var AIServices = ["gemini", "claude", "codex"];
64
+ var COMMIT_FETCH_LIMIT = 20;
65
+ var COMMIT_SELECTION_WINDOW = 8;
28
66
  var ignoreList = [
29
67
  "package.json",
30
68
  "*.yml",
@@ -42,12 +80,224 @@ var ignoreList = [
42
80
  function isTestMode(args = process.argv.slice(2)) {
43
81
  return args.includes("--test");
44
82
  }
83
+ function shouldStreamAIOutput(args = process.argv.slice(2)) {
84
+ return args.includes("--stream-output");
85
+ }
45
86
  function clearTraceMessages() {
46
87
  traceMessages.length = 0;
47
88
  }
48
89
  function getTraceMessages() {
49
90
  return [...traceMessages];
50
91
  }
92
+ var ANSI = {
93
+ bold: "\x1B[1m",
94
+ cyan: "\x1B[36m",
95
+ dim: "\x1B[2m",
96
+ green: "\x1B[32m",
97
+ reset: "\x1B[0m",
98
+ yellow: "\x1B[33m"
99
+ };
100
+ var ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
101
+ var COMBINING_MARK_PATTERN = /\p{Mark}/u;
102
+ var GRAPHEME_SEGMENTER = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter("ko", { granularity: "grapheme" }) : null;
103
+ function getGitDiffPathspecs() {
104
+ return {
105
+ excludePatterns: ignoreList.map((item) => `:(exclude)${item}`),
106
+ includePatterns: ["*.ts", "*.tsx", "*.js", "*.jsx"]
107
+ };
108
+ }
109
+ function segmentGraphemes(value) {
110
+ if (!GRAPHEME_SEGMENTER) {
111
+ return [...value];
112
+ }
113
+ return [...GRAPHEME_SEGMENTER.segment(value)].map(({ segment }) => segment);
114
+ }
115
+ function isWideCodePoint(codePoint) {
116
+ return codePoint >= 4352 && (codePoint <= 4447 || codePoint === 9001 || codePoint === 9002 || codePoint >= 11904 && codePoint <= 12871 && codePoint !== 12351 || codePoint >= 12880 && codePoint <= 19903 || codePoint >= 19968 && codePoint <= 42182 || codePoint >= 43360 && codePoint <= 43388 || codePoint >= 44032 && codePoint <= 55203 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 65040 && codePoint <= 65049 || codePoint >= 65072 && codePoint <= 65131 || codePoint >= 65281 && codePoint <= 65376 || codePoint >= 65504 && codePoint <= 65510 || codePoint >= 127488 && codePoint <= 127569 || codePoint >= 131072 && codePoint <= 262141);
117
+ }
118
+ function isEmojiCodePoint(codePoint) {
119
+ return codePoint >= 127462 && codePoint <= 127487 || codePoint >= 127744 && codePoint <= 129791 || codePoint >= 9728 && codePoint <= 10175;
120
+ }
121
+ function getGraphemeWidth(grapheme) {
122
+ let width = 0;
123
+ for (const character of grapheme) {
124
+ const codePoint = character.codePointAt(0);
125
+ if (!codePoint || COMBINING_MARK_PATTERN.test(character) || codePoint === 8205) {
126
+ continue;
127
+ }
128
+ if (codePoint >= 65024 && codePoint <= 65039 || codePoint >= 917760 && codePoint <= 917999) {
129
+ continue;
130
+ }
131
+ if (isWideCodePoint(codePoint) || isEmojiCodePoint(codePoint)) {
132
+ width = Math.max(width, 2);
133
+ continue;
134
+ }
135
+ width = Math.max(width, 1);
136
+ }
137
+ return width;
138
+ }
139
+ function tokenizePlainText(value) {
140
+ return segmentGraphemes(value).map((segment) => ({
141
+ value: segment,
142
+ visibleWidth: getGraphemeWidth(segment)
143
+ }));
144
+ }
145
+ function tokenizeVisibleText(value) {
146
+ const tokens = [];
147
+ let lastIndex = 0;
148
+ for (const match of value.matchAll(ANSI_PATTERN)) {
149
+ const index = match.index ?? 0;
150
+ if (index > lastIndex) {
151
+ tokens.push(...tokenizePlainText(value.slice(lastIndex, index)));
152
+ }
153
+ tokens.push({
154
+ value: match[0],
155
+ visibleWidth: 0
156
+ });
157
+ lastIndex = index + match[0].length;
158
+ }
159
+ if (lastIndex < value.length) {
160
+ tokens.push(...tokenizePlainText(value.slice(lastIndex)));
161
+ }
162
+ return tokens;
163
+ }
164
+ function truncateLineForTerminal(value, maxWidth) {
165
+ if (maxWidth <= 0) {
166
+ return "";
167
+ }
168
+ const tokens = tokenizeVisibleText(value);
169
+ const totalWidth = tokens.reduce((sum, token) => sum + token.visibleWidth, 0);
170
+ if (totalWidth <= maxWidth) {
171
+ return value;
172
+ }
173
+ const ellipsis = "...";
174
+ const ellipsisWidth = 3;
175
+ const targetWidth = Math.max(0, maxWidth - ellipsisWidth);
176
+ let usedWidth = 0;
177
+ let result = "";
178
+ for (const token of tokens) {
179
+ if (token.visibleWidth === 0) {
180
+ result += token.value;
181
+ continue;
182
+ }
183
+ if (usedWidth + token.visibleWidth > targetWidth) {
184
+ break;
185
+ }
186
+ result += token.value;
187
+ usedWidth += token.visibleWidth;
188
+ }
189
+ return `${result}${ellipsis}${ANSI.reset}`;
190
+ }
191
+ function fitLinesToTerminal(lines) {
192
+ const maxWidth = Math.max(20, (process.stdout.columns || 120) - 1);
193
+ return lines.map((line) => truncateLineForTerminal(line, maxWidth));
194
+ }
195
+ function runGitCommand(args, options = {}) {
196
+ const { allowFailure = false, trimOutput = true } = options;
197
+ try {
198
+ const output = child_process.execFileSync("git", args, {
199
+ encoding: "utf8",
200
+ maxBuffer: 1024 * 1024 * 20,
201
+ stdio: ["ignore", "pipe", "pipe"]
202
+ });
203
+ return trimOutput ? output.trim() : output;
204
+ } catch (error) {
205
+ helperTrace("git-command:failed", `${args.join(" ")} | ${getErrorSummary(error)}`);
206
+ if (allowFailure) {
207
+ return "";
208
+ }
209
+ throw error;
210
+ }
211
+ }
212
+ async function executeShellCommandWithProgress(command, options = {}) {
213
+ const { progressIntervalMs = 1e4, progressMessage = "\u23F3 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uB294 \uC911\uC785\uB2C8\uB2E4...", streamOutput = false } = options;
214
+ const { spawn } = await import('child_process');
215
+ return new Promise((resolve, reject) => {
216
+ let stdout = "";
217
+ let stderr = "";
218
+ const startedAt = Date.now();
219
+ console.log(progressMessage);
220
+ const child = spawn("/bin/zsh", ["-lc", command], {
221
+ stdio: ["ignore", "pipe", "pipe"]
222
+ });
223
+ const progressTimer = setInterval(() => {
224
+ const elapsedSeconds = Math.max(1, Math.floor((Date.now() - startedAt) / 1e3));
225
+ console.log(`${progressMessage} (${elapsedSeconds}s \uACBD\uACFC)`);
226
+ }, progressIntervalMs);
227
+ child.stdout.on("data", (chunk) => {
228
+ const text = chunk.toString();
229
+ stdout += text;
230
+ if (streamOutput) {
231
+ process.stdout.write(text);
232
+ }
233
+ });
234
+ child.stderr.on("data", (chunk) => {
235
+ const text = chunk.toString();
236
+ stderr += text;
237
+ if (streamOutput) {
238
+ process.stderr.write(text);
239
+ }
240
+ });
241
+ child.on("error", (error) => {
242
+ clearInterval(progressTimer);
243
+ reject(error);
244
+ });
245
+ child.on("close", (code, signal) => {
246
+ clearInterval(progressTimer);
247
+ if (code === 0) {
248
+ resolve({
249
+ stderr,
250
+ stdout
251
+ });
252
+ return;
253
+ }
254
+ const exitSummary = signal ? `signal=${signal}` : `code=${String(code ?? "unknown")}`;
255
+ const failureDetails = {
256
+ code,
257
+ command,
258
+ signal,
259
+ stderr,
260
+ stdout
261
+ };
262
+ reject(createShellCommandExecutionError(failureDetails, exitSummary));
263
+ });
264
+ });
265
+ }
266
+ function getShellCommandFailurePreview(failureDetails) {
267
+ const stderrText = failureDetails.stderr.trim();
268
+ const stdoutText = failureDetails.stdout.trim();
269
+ const combinedOutput = stderrText || stdoutText;
270
+ if (!combinedOutput) {
271
+ return "";
272
+ }
273
+ const MAX_PREVIEW_LENGTH = 4e3;
274
+ if (combinedOutput.length <= MAX_PREVIEW_LENGTH) {
275
+ return combinedOutput;
276
+ }
277
+ return combinedOutput.slice(-4e3);
278
+ }
279
+ function createShellCommandExecutionError(failureDetails, exitSummary) {
280
+ const failurePreview = getShellCommandFailurePreview(failureDetails);
281
+ const error = new Error(`\uC258 \uBA85\uB839 \uC2E4\uD589 \uC2E4\uD328 (${exitSummary})${failurePreview ? `
282
+ ${failurePreview}` : ""}`);
283
+ error.code = failureDetails.code;
284
+ error.signal = failureDetails.signal;
285
+ error.stdout = failureDetails.stdout;
286
+ error.stderr = failureDetails.stderr;
287
+ error.command = failureDetails.command;
288
+ return error;
289
+ }
290
+ function formatReviewTargetFiles(files, visibleCount = 5) {
291
+ if (files.length === 0) {
292
+ return "(\uC5C6\uC74C)";
293
+ }
294
+ const visibleFiles = files.slice(0, visibleCount);
295
+ const hiddenCount = Math.max(0, files.length - visibleFiles.length);
296
+ if (hiddenCount === 0) {
297
+ return visibleFiles.join(", ");
298
+ }
299
+ return `${visibleFiles.join(", ")} \uC678 ${hiddenCount}\uAC1C`;
300
+ }
51
301
  function createTraceLogger(scope, args = process.argv.slice(2)) {
52
302
  const enabled = isTestMode(args);
53
303
  return (step, detail) => {
@@ -109,7 +359,7 @@ function serializeError(error) {
109
359
  }
110
360
  if (error && typeof error === "object") {
111
361
  const errorLike = error;
112
- const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs"];
362
+ const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs", "command"];
113
363
  extraKeys.forEach((key) => {
114
364
  if (errorLike[key] !== void 0) {
115
365
  serialized[key] = errorLike[key];
@@ -215,6 +465,87 @@ ${section.markdown}`).join("\n")}
215
465
  return "";
216
466
  }
217
467
  }
468
+ function getExecutionLogSummary(status, title) {
469
+ if (title) {
470
+ return title;
471
+ }
472
+ switch (status) {
473
+ case "success":
474
+ return "\uB9AC\uBDF0 \uC2E4\uD589\uC774 \uC131\uACF5\uC801\uC73C\uB85C \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.";
475
+ case "failed":
476
+ return "\uB9AC\uBDF0 \uC2E4\uD589 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
477
+ case "partial_failure":
478
+ return "\uB9AC\uBDF0 \uC2E4\uD589\uC740 \uC644\uB8CC\uB418\uC5C8\uC9C0\uB9CC \uC77C\uBD80 \uB2E8\uACC4\uC5D0\uC11C \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
479
+ default:
480
+ return "\uB9AC\uBDF0 \uC2E4\uD589\uC774 \uCDE8\uC18C\uB418\uC5C8\uAC70\uB098 \uB9AC\uBDF0 \uB300\uC0C1\uC774 \uC5C6\uC5B4 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.";
481
+ }
482
+ }
483
+ function formatExecutionDuration(startedAt, finishedAt) {
484
+ const durationMs = Math.max(0, finishedAt.getTime() - startedAt.getTime());
485
+ if (durationMs < 1e3) {
486
+ return `${durationMs}ms`;
487
+ }
488
+ const durationSeconds = durationMs / 1e3;
489
+ if (durationSeconds < 60) {
490
+ return `${durationSeconds.toFixed(1)}s`;
491
+ }
492
+ const minutes = Math.floor(durationSeconds / 60);
493
+ const seconds = Math.round(durationSeconds % 60);
494
+ return `${minutes}m ${seconds}s`;
495
+ }
496
+ function writeExecutionLog(options = {}) {
497
+ try {
498
+ const startedAt = options.startedAt ?? /* @__PURE__ */ new Date();
499
+ const finishedAt = options.finishedAt ?? /* @__PURE__ */ new Date();
500
+ const status = options.status ?? "success";
501
+ helperTrace("execution-log:write:start", options.scope || "unknown");
502
+ createReportDirectory();
503
+ const reportPath = getAvailableFilePath(REPORT_DIR, `${getNowString(finishedAt)}-execution-log`, ".md");
504
+ const traceSnapshot = options.traceMessages ?? getTraceMessages();
505
+ const extraSections = options.extraSections || [];
506
+ const serializedError = options.error ? serializeError(options.error) : null;
507
+ const report = `# Execution Log
508
+
509
+ - \uC2DC\uC791 \uC2DC\uAC01: ${getHumanReadableNowString(startedAt)}
510
+ - \uC885\uB8CC \uC2DC\uAC01: ${getHumanReadableNowString(finishedAt)}
511
+ - \uC2E4\uD589 \uC2DC\uAC04: ${formatExecutionDuration(startedAt, finishedAt)}
512
+ - \uC0C1\uD0DC: \`${status}\`
513
+ - Scope: \`${options.scope || "unknown"}\`
514
+ - \uC791\uC5C5 \uACBD\uB85C: \`${process.cwd()}\`
515
+ - \uC2E4\uD589 \uC778\uC790: \`${JSON.stringify(options.args ?? process.argv.slice(2))}\`
516
+ - \uC2E4\uD589 \uD658\uACBD: \`${process.platform} ${process.arch} / Node ${process.version}\`
517
+
518
+ ## Summary
519
+
520
+ ${getExecutionLogSummary(status, options.title)}
521
+ ${serializedError ? `
522
+
523
+ ## Error
524
+
525
+ \`\`\`json
526
+ ${JSON.stringify(serializedError, null, 2)}
527
+ \`\`\`` : ""}
528
+
529
+ ## Trace
530
+
531
+ \`\`\`json
532
+ ${JSON.stringify(traceSnapshot, null, 2)}
533
+ \`\`\`${extraSections.length ? `
534
+ ${extraSections.map((section) => `
535
+ ## ${section.heading}
536
+
537
+ ${section.markdown}`).join("\n")}
538
+ ` : "\n"}
539
+ `;
540
+ fs__default.default.writeFileSync(reportPath, report);
541
+ helperTrace("execution-log:write:done", reportPath);
542
+ return reportPath;
543
+ } catch (writeError) {
544
+ console.error("\u26A0\uFE0F \uC2E4\uD589 \uB85C\uADF8 \uD30C\uC77C \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
545
+ console.error(writeError);
546
+ return "";
547
+ }
548
+ }
218
549
  function exitWithError(message, options = {}) {
219
550
  const reportPath = writeErrorReport(options.error || new Error(message), {
220
551
  ...options,
@@ -260,13 +591,90 @@ ${JSON.stringify(AIServices, null, 2)}
260
591
  );
261
592
  }
262
593
  function getGitDiffFilter() {
263
- const includeExtensions = ["*.ts", "*.tsx", "*.js", "*.jsx"];
264
- const excludePatterns = ignoreList.map((item) => `:(exclude)${item}`);
594
+ const { includePatterns, excludePatterns } = getGitDiffPathspecs();
265
595
  const quote = (pattern) => `"${pattern}"`;
266
- const includeParams = includeExtensions.map(quote).join(" ");
596
+ const includeParams = includePatterns.map(quote).join(" ");
267
597
  const excludeParams = excludePatterns.map(quote).join(" ");
268
598
  return { includeParams, excludeParams };
269
599
  }
600
+ function truncateCommitSubject(subject) {
601
+ if (subject.length <= 72) {
602
+ return subject;
603
+ }
604
+ return `${subject.slice(0, 69)}...`;
605
+ }
606
+ function getRecentCommitOptions() {
607
+ const output = runGitCommand(
608
+ ["log", `-${COMMIT_FETCH_LIMIT}`, "--date=relative", "--pretty=format:%h%x09%an%x09%ar%x09%s"],
609
+ { allowFailure: true }
610
+ );
611
+ if (!output) {
612
+ return [];
613
+ }
614
+ return output.split("\n").map((line) => {
615
+ const [hash = "", author = "", relativeDate = "", ...subjectParts] = line.split(" ");
616
+ const subject = subjectParts.join(" ").trim();
617
+ return {
618
+ author,
619
+ description: `${author} | ${relativeDate}`,
620
+ hash,
621
+ label: `${hash} | ${truncateCommitSubject(subject)}`,
622
+ relativeDate,
623
+ subject
624
+ };
625
+ });
626
+ }
627
+ function buildSelectedCommitSummary(commits) {
628
+ return commits.map((commit) => `- ${commit.hash} | ${commit.subject} | ${commit.author} | ${commit.relativeDate}`).join("\n");
629
+ }
630
+ function getReviewPathspecArgs() {
631
+ const { includePatterns, excludePatterns } = getGitDiffPathspecs();
632
+ return [...includePatterns, ...excludePatterns];
633
+ }
634
+ function buildSelectedCommitDiff(commits) {
635
+ const reviewPathspecArgs = getReviewPathspecArgs();
636
+ const sections = commits.map((commit) => {
637
+ const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", ...reviewPathspecArgs], {
638
+ allowFailure: true,
639
+ trimOutput: false
640
+ }).trim();
641
+ if (!diff) {
642
+ return "";
643
+ }
644
+ return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
645
+ }).filter(Boolean).join("\n\n");
646
+ if (!sections) {
647
+ return "";
648
+ }
649
+ return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", "# \uB9AC\uBDF0 \uB300\uC0C1 diff", sections].join("\n");
650
+ }
651
+ function getSelectedCommitFiles(commits) {
652
+ const reviewPathspecArgs = getReviewPathspecArgs();
653
+ const files = /* @__PURE__ */ new Set();
654
+ commits.forEach((commit) => {
655
+ const output = runGitCommand(["show", "--pretty=format:", "--name-only", commit.hash, "--", ...reviewPathspecArgs], {
656
+ allowFailure: true
657
+ });
658
+ output.split("\n").map((line) => line.trim()).filter(Boolean).forEach((filePath) => files.add(filePath));
659
+ });
660
+ return [...files];
661
+ }
662
+ function buildSelectedFileDiff(commits, filePath) {
663
+ const sections = commits.map((commit) => {
664
+ const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", filePath], {
665
+ allowFailure: true,
666
+ trimOutput: false
667
+ }).trim();
668
+ if (!diff) {
669
+ return "";
670
+ }
671
+ return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
672
+ }).filter(Boolean).join("\n\n");
673
+ if (!sections) {
674
+ return "";
675
+ }
676
+ return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", `# \uD30C\uC77C: ${filePath}`, sections].join("\n\n");
677
+ }
270
678
  function openReport(reportPath) {
271
679
  const resolvedPath = path__default.default.resolve(reportPath);
272
680
  const { platform } = process;
@@ -316,49 +724,155 @@ function openReport(reportPath) {
316
724
  helperTrace("open-report:unsupported-platform", platform);
317
725
  console.error(`\u26A0\uFE0F \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD50C\uB7AB\uD3FC\uC785\uB2C8\uB2E4: ${platform}`);
318
726
  }
319
- function getDiffArgs() {
320
- const args = process.argv.slice(2);
321
- const commitIndex = args.indexOf("--commit");
322
- const { includeParams, excludeParams } = getGitDiffFilter();
323
- helperTrace("diff-args:resolve:start", `args=${JSON.stringify(args)}`);
324
- let diffArgs = "";
325
- if (commitIndex !== -1) {
326
- const commitHash = args[commitIndex + 1];
327
- if (!commitHash) {
328
- helperTrace("diff-args:commit-hash-missing");
329
- exitWithError("\u274C \uCEE4\uBC0B \uD574\uC2DC\uAC00 \uC81C\uACF5\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.", {
330
- scope: "helper:getDiffArgs",
331
- args
332
- });
727
+ function ensureInteractiveSelectionAvailable(scope, message) {
728
+ if (process.stdin.isTTY && process.stdout.isTTY && typeof process.stdin.setRawMode === "function") {
729
+ return;
730
+ }
731
+ helperTrace(`${scope}:tty-missing`);
732
+ exitWithError(message, {
733
+ scope: `helper:${scope}`
734
+ });
735
+ }
736
+ function renderSelectionBlock(lines, previousLineCount) {
737
+ if (previousLineCount > 0) {
738
+ readline__default.default.moveCursor(process.stdout, 0, -previousLineCount);
739
+ readline__default.default.clearScreenDown(process.stdout);
740
+ }
741
+ const fittedLines = fitLinesToTerminal(lines);
742
+ process.stdout.write(`${fittedLines.join("\n")}
743
+ `);
744
+ return fittedLines.length;
745
+ }
746
+ function getSelectionWindowRange(optionCount, selectedIndex, windowSize) {
747
+ if (optionCount <= windowSize) {
748
+ return {
749
+ end: optionCount,
750
+ start: 0
751
+ };
752
+ }
753
+ const halfWindow = Math.floor(windowSize / 2);
754
+ const maxStart = optionCount - windowSize;
755
+ const start = Math.max(0, Math.min(selectedIndex - halfWindow, maxStart));
756
+ return {
757
+ end: Math.min(optionCount, start + windowSize),
758
+ start
759
+ };
760
+ }
761
+ function buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize) {
762
+ const { start, end } = getSelectionWindowRange(options.length, selectedIndex, windowSize);
763
+ const lines = [
764
+ `${ANSI.bold}${question}${ANSI.reset}`,
765
+ `${ANSI.dim}\u2191\u2193 \uC774\uB3D9 | Space \uC120\uD0DD/\uD574\uC81C | Enter \uC644\uB8CC | Esc \uCDE8\uC18C${ANSI.reset}`,
766
+ `${ANSI.dim}\uC120\uD0DD\uB428: ${toggled.size}\uAC1C / \uC804\uCCB4: ${options.length}\uAC1C${ANSI.reset}`
767
+ ];
768
+ for (let index = start; index < end; index += 1) {
769
+ const option = options[index];
770
+ if (!option) {
771
+ continue;
333
772
  }
334
- const nextArg = args[commitIndex + 2];
335
- let n = 0;
336
- if (nextArg && !nextArg.startsWith("--")) {
337
- n = parseInt(nextArg, 10);
338
- if (isNaN(n)) {
339
- n = 0;
340
- }
773
+ const cursor = index === selectedIndex ? `${ANSI.cyan}>${ANSI.reset}` : " ";
774
+ const checked = toggled.has(index) ? `${ANSI.green}\u2611${ANSI.reset}` : "\u2610";
775
+ const description = option.description ? ` ${ANSI.dim}${option.description}${ANSI.reset}` : "";
776
+ lines.push(`${cursor} ${checked} ${option.label}${description}`);
777
+ }
778
+ if (options.length > windowSize) {
779
+ lines.push(`${ANSI.dim}\uD45C\uC2DC \uBC94\uC704: ${start + 1}-${end} / ${options.length}${ANSI.reset}`);
780
+ }
781
+ return lines;
782
+ }
783
+ async function showMultiSelect(question, options, windowSize = COMMIT_SELECTION_WINDOW) {
784
+ ensureInteractiveSelectionAvailable("showMultiSelect", "\u274C \uCEE4\uBC0B \uC120\uD0DD \uBAA8\uB2EC\uC740 TTY \uD658\uACBD\uC5D0\uC11C\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
785
+ let selectedIndex = 0;
786
+ let renderedLineCount = 0;
787
+ const toggled = /* @__PURE__ */ new Set();
788
+ const rl = readline__default.default.createInterface({
789
+ input: process.stdin,
790
+ output: process.stdout,
791
+ terminal: true
792
+ });
793
+ process.stdout.write("\x1B[?25l");
794
+ const cleanup = () => {
795
+ if (renderedLineCount > 0) {
796
+ readline__default.default.moveCursor(process.stdout, 0, -renderedLineCount);
797
+ readline__default.default.clearScreenDown(process.stdout);
798
+ renderedLineCount = 0;
341
799
  }
342
- helperTrace("diff-args:commit-mode", `${commitHash}~${n + 1} ${commitHash}`);
343
- console.log(`\u2139\uFE0F \uCEE4\uBC0B '${commitHash}' ${n > 0 ? ` \uD3EC\uD568 \uCD1D ${n + 1}\uAC1C\uC758 \uCEE4\uBC0B` : ""}\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...`);
344
- diffArgs = `${commitHash}~${n + 1} ${commitHash}`;
345
- } else {
346
- try {
347
- helperTrace("diff-args:unstaged-check:start");
348
- const check = child_process.execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();
349
- if (!check.trim()) {
350
- helperTrace("diff-args:unstaged-check:empty", "use HEAD~1 HEAD");
351
- 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...");
352
- diffArgs = "HEAD~1 HEAD";
353
- } else {
354
- helperTrace("diff-args:unstaged-check:has-changes", `length=${check.length}`);
800
+ process.stdin.removeListener("data", onData);
801
+ process.stdin.setRawMode(false);
802
+ process.stdin.pause();
803
+ rl.close();
804
+ process.stdout.write("\x1B[?25h");
805
+ };
806
+ const render = () => {
807
+ const lines = buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize);
808
+ renderedLineCount = renderSelectionBlock(lines, renderedLineCount);
809
+ };
810
+ const confirmSelection = (resolve) => {
811
+ const values = [...toggled].sort((left, right) => left - right).map((index) => options[index]?.value).filter((value) => value !== void 0);
812
+ cleanup();
813
+ resolve(values);
814
+ };
815
+ const cancelSelection = (resolve) => {
816
+ cleanup();
817
+ resolve([]);
818
+ };
819
+ let onData = (_data) => {
820
+ };
821
+ render();
822
+ return new Promise((resolve) => {
823
+ onData = (data) => {
824
+ const key = data.toString();
825
+ if (key === "") {
826
+ cleanup();
827
+ process.exit(0);
355
828
  }
356
- } catch (error) {
357
- helperTrace("diff-args:unstaged-check:failed", getErrorSummary(error));
358
- }
829
+ if (key === "\x1B") {
830
+ cancelSelection(resolve);
831
+ return;
832
+ }
833
+ if (key === "\x1B[A") {
834
+ selectedIndex = (selectedIndex - 1 + options.length) % options.length;
835
+ render();
836
+ return;
837
+ }
838
+ if (key === "\x1B[B") {
839
+ selectedIndex = (selectedIndex + 1) % options.length;
840
+ render();
841
+ return;
842
+ }
843
+ if (key === " ") {
844
+ if (toggled.has(selectedIndex)) {
845
+ toggled.delete(selectedIndex);
846
+ } else {
847
+ toggled.add(selectedIndex);
848
+ }
849
+ render();
850
+ return;
851
+ }
852
+ if (key === "\r" || key === "\n") {
853
+ confirmSelection(resolve);
854
+ }
855
+ };
856
+ process.stdin.setRawMode(true);
857
+ process.stdin.resume();
858
+ process.stdin.on("data", onData);
859
+ });
860
+ }
861
+ async function selectReviewCommits() {
862
+ const commits = getRecentCommitOptions();
863
+ if (commits.length === 0) {
864
+ console.log("\u2139\uFE0F \uB9AC\uBDF0\uD560 \uCD5C\uADFC \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
865
+ return [];
359
866
  }
360
- helperTrace("diff-args:resolve:done", diffArgs || "(default)");
361
- return diffArgs;
867
+ return showMultiSelect(
868
+ "\uB9AC\uBDF0\uD560 \uCEE4\uBC0B\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.",
869
+ commits.map((commit) => ({
870
+ description: commit.description,
871
+ label: commit.label,
872
+ value: commit
873
+ })),
874
+ COMMIT_SELECTION_WINDOW
875
+ );
362
876
  }
363
877
  function selectAIService() {
364
878
  const service = parseServiceFromArgs();
@@ -376,10 +890,11 @@ async function showSelectionAIService() {
376
890
  if (selectedServiceFromArgs) {
377
891
  helperTrace("show-selection:from-args", selectedServiceFromArgs);
378
892
  console.log(`
379
- \u2705 \x1B[32m${selectedServiceFromArgs}\x1B[0m \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (--service)
893
+ \u2705 ${ANSI.green}${selectedServiceFromArgs}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (--service)
380
894
  `);
381
895
  return selectedServiceFromArgs;
382
896
  }
897
+ 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.");
383
898
  helperTrace("show-selection:interactive:start");
384
899
  let selectedIndex = 0;
385
900
  const rl = readline__default.default.createInterface({
@@ -397,11 +912,12 @@ async function showSelectionAIService() {
397
912
  helperTrace("show-selection:interactive:render", AIServices[selectedIndex] || "unknown");
398
913
  readline__default.default.clearScreenDown(process.stdout);
399
914
  process.stdout.write(
400
- "\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"
915
+ `\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):
916
+ `
401
917
  );
402
918
  AIServices.forEach((service, index) => {
403
919
  if (index === selectedIndex) {
404
- process.stdout.write(` \x1B[36m>\x1B[0m \x1B[36m\u25C9\x1B[0m \x1B[1m${service}\x1B[0m
920
+ process.stdout.write(` ${ANSI.cyan}>${ANSI.reset} ${ANSI.cyan}\u25C9${ANSI.reset} ${ANSI.bold}${service}${ANSI.reset}
405
921
  `);
406
922
  } else {
407
923
  process.stdout.write(` \u25EF ${service}
@@ -431,7 +947,7 @@ async function showSelectionAIService() {
431
947
  rl.close();
432
948
  process.stdout.write("\x1B[?25h");
433
949
  console.log(`
434
- \u2705 \x1B[32m${AIServices[selectedIndex]}\x1B[0m \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
950
+ \u2705 ${ANSI.green}${AIServices[selectedIndex]}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
435
951
  `);
436
952
  const result = AIServices[selectedIndex];
437
953
  if (result) {
@@ -447,21 +963,29 @@ async function showSelectionAIService() {
447
963
  }
448
964
 
449
965
  exports.AIServices = AIServices;
966
+ exports.COMMIT_FETCH_LIMIT = COMMIT_FETCH_LIMIT;
967
+ exports.COMMIT_SELECTION_WINDOW = COMMIT_SELECTION_WINDOW;
450
968
  exports.REPORT_DIR = REPORT_DIR;
969
+ exports.buildSelectedCommitDiff = buildSelectedCommitDiff;
970
+ exports.buildSelectedCommitSummary = buildSelectedCommitSummary;
971
+ exports.buildSelectedFileDiff = buildSelectedFileDiff;
451
972
  exports.clearTraceMessages = clearTraceMessages;
452
973
  exports.codingConventionRulesPath = codingConventionRulesPath;
453
974
  exports.createReportDirectory = createReportDirectory;
454
975
  exports.createTraceLogger = createTraceLogger;
455
976
  exports.deleteFile = deleteFile;
456
977
  exports.deleteTempDiff = deleteTempDiff;
978
+ exports.executeShellCommandWithProgress = executeShellCommandWithProgress;
457
979
  exports.exitWithError = exitWithError;
980
+ exports.formatReviewTargetFiles = formatReviewTargetFiles;
458
981
  exports.getAvailableFilePath = getAvailableFilePath;
459
- exports.getDiffArgs = getDiffArgs;
460
982
  exports.getErrorLogTimestamp = getErrorLogTimestamp;
461
983
  exports.getErrorSummary = getErrorSummary;
462
984
  exports.getGitDiffFilter = getGitDiffFilter;
463
985
  exports.getNextFilePath = getNextFilePath;
464
986
  exports.getNowString = getNowString;
987
+ exports.getRecentCommitOptions = getRecentCommitOptions;
988
+ exports.getSelectedCommitFiles = getSelectedCommitFiles;
465
989
  exports.getTraceMessages = getTraceMessages;
466
990
  exports.ignoreList = ignoreList;
467
991
  exports.isTestMode = isTestMode;
@@ -471,8 +995,12 @@ exports.reviewFormOneByOnePath = reviewFormOneByOnePath;
471
995
  exports.reviewFormPath = reviewFormPath;
472
996
  exports.rulesPath = rulesPath;
473
997
  exports.selectAIService = selectAIService;
998
+ exports.selectReviewCommits = selectReviewCommits;
999
+ exports.shouldStreamAIOutput = shouldStreamAIOutput;
1000
+ exports.showMultiSelect = showMultiSelect;
474
1001
  exports.showSelectionAIService = showSelectionAIService;
475
1002
  exports.tempDiffPath = tempDiffPath;
476
1003
  exports.writeErrorReport = writeErrorReport;
1004
+ exports.writeExecutionLog = writeExecutionLog;
477
1005
  //# sourceMappingURL=helper.cjs.map
478
1006
  //# sourceMappingURL=helper.cjs.map