sales-frontend-gemini-cli 0.4.1 → 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 +674 -68
  2. package/dist/common/helper.cjs.map +1 -1
  3. package/dist/common/helper.d.cts +106 -4
  4. package/dist/common/helper.d.ts +106 -4
  5. package/dist/common/helper.js +659 -70
  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 +58 -10
  10. package/dist/pr-review/claude/claude-commander.cjs.map +1 -1
  11. package/dist/pr-review/claude/claude-commander.js +58 -10
  12. package/dist/pr-review/claude/claude-commander.js.map +1 -1
  13. package/dist/pr-review/claude/installation-claude.cjs +219 -13
  14. package/dist/pr-review/claude/installation-claude.cjs.map +1 -1
  15. package/dist/pr-review/claude/installation-claude.js +218 -13
  16. package/dist/pr-review/claude/installation-claude.js.map +1 -1
  17. package/dist/pr-review/codex/codex-commander.cjs +55 -9
  18. package/dist/pr-review/codex/codex-commander.cjs.map +1 -1
  19. package/dist/pr-review/codex/codex-commander.js +55 -9
  20. package/dist/pr-review/codex/codex-commander.js.map +1 -1
  21. package/dist/pr-review/codex/installation-codex.cjs +219 -13
  22. package/dist/pr-review/codex/installation-codex.cjs.map +1 -1
  23. package/dist/pr-review/codex/installation-codex.js +218 -13
  24. package/dist/pr-review/codex/installation-codex.js.map +1 -1
  25. package/dist/pr-review/gemini/gemini-commander.cjs +82 -16
  26. package/dist/pr-review/gemini/gemini-commander.cjs.map +1 -1
  27. package/dist/pr-review/gemini/gemini-commander.js +82 -16
  28. package/dist/pr-review/gemini/gemini-commander.js.map +1 -1
  29. package/dist/pr-review/gemini/installation-gemini.cjs +219 -13
  30. package/dist/pr-review/gemini/installation-gemini.cjs.map +1 -1
  31. package/dist/pr-review/gemini/installation-gemini.js +218 -13
  32. package/dist/pr-review/gemini/installation-gemini.js.map +1 -1
  33. package/dist/pr-review/review-one-by-one.cjs +838 -184
  34. package/dist/pr-review/review-one-by-one.cjs.map +1 -1
  35. package/dist/pr-review/review-one-by-one.js +839 -185
  36. package/dist/pr-review/review-one-by-one.js.map +1 -1
  37. package/dist/pr-review/review.cjs +815 -156
  38. package/dist/pr-review/review.cjs.map +1 -1
  39. package/dist/pr-review/review.js +815 -156
  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
@@ -5,6 +5,7 @@ var fs = require('fs');
5
5
  var path = require('path');
6
6
  var readline = require('readline');
7
7
  var url = require('url');
8
+ var util = require('util');
8
9
 
9
10
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
10
11
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -15,14 +16,53 @@ var readline__default = /*#__PURE__*/_interopDefault(readline);
15
16
 
16
17
  // src/common/helper.ts
17
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))));
18
- var rulesPath = path__default.default.resolve(__dirname$1, "../../src/common/rules/review-rules.md");
19
- var namingRulesPath = path__default.default.resolve(__dirname$1, "../../src/common/rules/naming-rule.md");
20
- var codingConventionRulesPath = path__default.default.resolve(__dirname$1, "../../src/common/rules/coding-convention.md");
21
- var reviewFormPath = path__default.default.resolve(__dirname$1, "../../src/common/form/review-form.md");
22
- var reviewFormOneByOnePath = path__default.default.resolve(__dirname$1, "../../src/common/form/review-form-one-by-one.md");
19
+ var traceMessages = [];
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");
23
61
  var REPORT_DIR = ".review-report";
24
62
  var tempDiffPath = "temp_diff.txt";
25
63
  var AIServices = ["gemini", "claude", "codex"];
64
+ var COMMIT_FETCH_LIMIT = 20;
65
+ var COMMIT_SELECTION_WINDOW = 8;
26
66
  var ignoreList = [
27
67
  "package.json",
28
68
  "*.yml",
@@ -37,33 +77,276 @@ var ignoreList = [
37
77
  ".review-report/"
38
78
  // 생성되는 리포트 폴더도 제외
39
79
  ];
40
- function parseServiceFromArgs(args = process.argv.slice(2)) {
41
- const serviceIndex = args.indexOf("--service");
42
- const rawService = serviceIndex !== -1 ? args[serviceIndex + 1] : "";
43
- if (!rawService) {
80
+ function isTestMode(args = process.argv.slice(2)) {
81
+ return args.includes("--test");
82
+ }
83
+ function clearTraceMessages() {
84
+ traceMessages.length = 0;
85
+ }
86
+ function getTraceMessages() {
87
+ return [...traceMessages];
88
+ }
89
+ var ANSI = {
90
+ bold: "\x1B[1m",
91
+ cyan: "\x1B[36m",
92
+ dim: "\x1B[2m",
93
+ green: "\x1B[32m",
94
+ reset: "\x1B[0m",
95
+ yellow: "\x1B[33m"
96
+ };
97
+ var ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
98
+ var COMBINING_MARK_PATTERN = /\p{Mark}/u;
99
+ var GRAPHEME_SEGMENTER = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter("ko", { granularity: "grapheme" }) : null;
100
+ function getGitDiffPathspecs() {
101
+ return {
102
+ excludePatterns: ignoreList.map((item) => `:(exclude)${item}`),
103
+ includePatterns: ["*.ts", "*.tsx", "*.js", "*.jsx"]
104
+ };
105
+ }
106
+ function segmentGraphemes(value) {
107
+ if (!GRAPHEME_SEGMENTER) {
108
+ return [...value];
109
+ }
110
+ return [...GRAPHEME_SEGMENTER.segment(value)].map(({ segment }) => segment);
111
+ }
112
+ function isWideCodePoint(codePoint) {
113
+ return codePoint >= 4352 && (codePoint <= 4447 || codePoint === 9001 || codePoint === 9002 || codePoint >= 11904 && codePoint <= 12871 && codePoint !== 12351 || codePoint >= 12880 && codePoint <= 19903 || codePoint >= 19968 && codePoint <= 42182 || codePoint >= 43360 && codePoint <= 43388 || codePoint >= 44032 && codePoint <= 55203 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 65040 && codePoint <= 65049 || codePoint >= 65072 && codePoint <= 65131 || codePoint >= 65281 && codePoint <= 65376 || codePoint >= 65504 && codePoint <= 65510 || codePoint >= 127488 && codePoint <= 127569 || codePoint >= 131072 && codePoint <= 262141);
114
+ }
115
+ function isEmojiCodePoint(codePoint) {
116
+ return codePoint >= 127462 && codePoint <= 127487 || codePoint >= 127744 && codePoint <= 129791 || codePoint >= 9728 && codePoint <= 10175;
117
+ }
118
+ function getGraphemeWidth(grapheme) {
119
+ let width = 0;
120
+ for (const character of grapheme) {
121
+ const codePoint = character.codePointAt(0);
122
+ if (!codePoint || COMBINING_MARK_PATTERN.test(character) || codePoint === 8205) {
123
+ continue;
124
+ }
125
+ if (codePoint >= 65024 && codePoint <= 65039 || codePoint >= 917760 && codePoint <= 917999) {
126
+ continue;
127
+ }
128
+ if (isWideCodePoint(codePoint) || isEmojiCodePoint(codePoint)) {
129
+ width = Math.max(width, 2);
130
+ continue;
131
+ }
132
+ width = Math.max(width, 1);
133
+ }
134
+ return width;
135
+ }
136
+ function tokenizePlainText(value) {
137
+ return segmentGraphemes(value).map((segment) => ({
138
+ value: segment,
139
+ visibleWidth: getGraphemeWidth(segment)
140
+ }));
141
+ }
142
+ function tokenizeVisibleText(value) {
143
+ const tokens = [];
144
+ let lastIndex = 0;
145
+ for (const match of value.matchAll(ANSI_PATTERN)) {
146
+ const index = match.index ?? 0;
147
+ if (index > lastIndex) {
148
+ tokens.push(...tokenizePlainText(value.slice(lastIndex, index)));
149
+ }
150
+ tokens.push({
151
+ value: match[0],
152
+ visibleWidth: 0
153
+ });
154
+ lastIndex = index + match[0].length;
155
+ }
156
+ if (lastIndex < value.length) {
157
+ tokens.push(...tokenizePlainText(value.slice(lastIndex)));
158
+ }
159
+ return tokens;
160
+ }
161
+ function truncateLineForTerminal(value, maxWidth) {
162
+ if (maxWidth <= 0) {
44
163
  return "";
45
164
  }
46
- const normalizedService = rawService.toLowerCase();
47
- if (AIServices.includes(normalizedService)) {
48
- return normalizedService;
165
+ const tokens = tokenizeVisibleText(value);
166
+ const totalWidth = tokens.reduce((sum, token) => sum + token.visibleWidth, 0);
167
+ if (totalWidth <= maxWidth) {
168
+ return value;
49
169
  }
50
- console.error(
51
- `\u274C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC11C\uBE44\uC2A4\uC785\uB2C8\uB2E4: ${rawService}. \uC0AC\uC6A9 \uAC00\uB2A5 \uAC12: ${AIServices.join(", ")} (\uC608: --service codex)`
52
- );
53
- process.exit(1);
170
+ const ellipsis = "...";
171
+ const ellipsisWidth = 3;
172
+ const targetWidth = Math.max(0, maxWidth - ellipsisWidth);
173
+ let usedWidth = 0;
174
+ let result = "";
175
+ for (const token of tokens) {
176
+ if (token.visibleWidth === 0) {
177
+ result += token.value;
178
+ continue;
179
+ }
180
+ if (usedWidth + token.visibleWidth > targetWidth) {
181
+ break;
182
+ }
183
+ result += token.value;
184
+ usedWidth += token.visibleWidth;
185
+ }
186
+ return `${result}${ellipsis}${ANSI.reset}`;
54
187
  }
55
- function isTestMode(args = process.argv.slice(2)) {
56
- return args.includes("--test");
188
+ function fitLinesToTerminal(lines) {
189
+ const maxWidth = Math.max(20, (process.stdout.columns || 120) - 1);
190
+ return lines.map((line) => truncateLineForTerminal(line, maxWidth));
191
+ }
192
+ function runGitCommand(args, options = {}) {
193
+ const { allowFailure = false, trimOutput = true } = options;
194
+ try {
195
+ const output = child_process.execFileSync("git", args, {
196
+ encoding: "utf8",
197
+ maxBuffer: 1024 * 1024 * 20,
198
+ stdio: ["ignore", "pipe", "pipe"]
199
+ });
200
+ return trimOutput ? output.trim() : output;
201
+ } catch (error) {
202
+ helperTrace("git-command:failed", `${args.join(" ")} | ${getErrorSummary(error)}`);
203
+ if (allowFailure) {
204
+ return "";
205
+ }
206
+ throw error;
207
+ }
208
+ }
209
+ async function executeShellCommandWithProgress(command, options = {}) {
210
+ const { progressIntervalMs = 1e4, progressMessage = "\u23F3 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uB294 \uC911\uC785\uB2C8\uB2E4...", streamOutput = false } = options;
211
+ const { spawn } = await import('child_process');
212
+ return new Promise((resolve, reject) => {
213
+ let stdout = "";
214
+ let stderr = "";
215
+ const startedAt = Date.now();
216
+ console.log(progressMessage);
217
+ const child = spawn("/bin/zsh", ["-lc", command], {
218
+ stdio: ["ignore", "pipe", "pipe"]
219
+ });
220
+ const progressTimer = setInterval(() => {
221
+ const elapsedSeconds = Math.max(1, Math.floor((Date.now() - startedAt) / 1e3));
222
+ console.log(`${progressMessage} (${elapsedSeconds}s \uACBD\uACFC)`);
223
+ }, progressIntervalMs);
224
+ child.stdout.on("data", (chunk) => {
225
+ const text = chunk.toString();
226
+ stdout += text;
227
+ if (streamOutput) {
228
+ process.stdout.write(text);
229
+ }
230
+ });
231
+ child.stderr.on("data", (chunk) => {
232
+ const text = chunk.toString();
233
+ stderr += text;
234
+ if (streamOutput) {
235
+ process.stderr.write(text);
236
+ }
237
+ });
238
+ child.on("error", (error) => {
239
+ clearInterval(progressTimer);
240
+ reject(error);
241
+ });
242
+ child.on("close", (code, signal) => {
243
+ clearInterval(progressTimer);
244
+ if (code === 0) {
245
+ resolve({
246
+ stderr,
247
+ stdout
248
+ });
249
+ return;
250
+ }
251
+ const exitSummary = signal ? `signal=${signal}` : `code=${String(code ?? "unknown")}`;
252
+ reject(new Error(`\uC258 \uBA85\uB839 \uC2E4\uD589 \uC2E4\uD328 (${exitSummary})${stderr.trim() ? `
253
+ ${stderr.trim()}` : ""}`));
254
+ });
255
+ });
256
+ }
257
+ function formatReviewTargetFiles(files, visibleCount = 5) {
258
+ if (files.length === 0) {
259
+ return "(\uC5C6\uC74C)";
260
+ }
261
+ const visibleFiles = files.slice(0, visibleCount);
262
+ const hiddenCount = Math.max(0, files.length - visibleFiles.length);
263
+ if (hiddenCount === 0) {
264
+ return visibleFiles.join(", ");
265
+ }
266
+ return `${visibleFiles.join(", ")} \uC678 ${hiddenCount}\uAC1C`;
57
267
  }
58
268
  function createTraceLogger(scope, args = process.argv.slice(2)) {
59
269
  const enabled = isTestMode(args);
60
270
  return (step, detail) => {
271
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
272
+ const message = `[${timestamp}][TRACE][${scope}] ${step}${detail ? ` | ${detail}` : ""}`;
273
+ traceMessages.push(message);
61
274
  if (!enabled) {
62
275
  return;
63
276
  }
64
- console.log(`[TRACE][${scope}] ${step}${detail ? ` | ${detail}` : ""}`);
277
+ console.log(message);
278
+ };
279
+ }
280
+ var helperTrace = createTraceLogger("helper");
281
+ function getTimestampParts(now = /* @__PURE__ */ new Date()) {
282
+ return {
283
+ YYYY: now.getFullYear(),
284
+ MM: String(now.getMonth() + 1).padStart(2, "0"),
285
+ DD: String(now.getDate()).padStart(2, "0"),
286
+ HH: String(now.getHours()).padStart(2, "0"),
287
+ mm: String(now.getMinutes()).padStart(2, "0"),
288
+ ss: String(now.getSeconds()).padStart(2, "0")
65
289
  };
66
290
  }
291
+ function getHumanReadableNowString(now = /* @__PURE__ */ new Date()) {
292
+ const { YYYY, MM, DD, HH, mm, ss } = getTimestampParts(now);
293
+ return `${YYYY}-${MM}-${DD} ${HH}:${mm}:${ss}`;
294
+ }
295
+ function stringifyUnknown(value) {
296
+ if (value === void 0 || value === null) {
297
+ return "";
298
+ }
299
+ if (typeof value === "string") {
300
+ return value;
301
+ }
302
+ if (Buffer.isBuffer(value)) {
303
+ return value.toString();
304
+ }
305
+ if (value instanceof Error) {
306
+ return value.stack || value.message;
307
+ }
308
+ return util.inspect(value, { depth: 5, breakLength: 120 });
309
+ }
310
+ function getErrorSummary(error) {
311
+ if (error instanceof Error) {
312
+ return `${error.name}: ${error.message}`;
313
+ }
314
+ return stringifyUnknown(error) || "Unknown error";
315
+ }
316
+ function serializeError(error) {
317
+ const serialized = {
318
+ summary: getErrorSummary(error)
319
+ };
320
+ if (error instanceof Error) {
321
+ serialized.name = error.name;
322
+ serialized.message = error.message;
323
+ serialized.stack = error.stack;
324
+ } else {
325
+ serialized.value = stringifyUnknown(error);
326
+ }
327
+ if (error && typeof error === "object") {
328
+ const errorLike = error;
329
+ const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs"];
330
+ extraKeys.forEach((key) => {
331
+ if (errorLike[key] !== void 0) {
332
+ serialized[key] = errorLike[key];
333
+ }
334
+ });
335
+ const stdout = stringifyUnknown(errorLike.stdout);
336
+ if (stdout) {
337
+ serialized.stdout = stdout;
338
+ }
339
+ const stderr = stringifyUnknown(errorLike.stderr);
340
+ if (stderr) {
341
+ serialized.stderr = stderr;
342
+ }
343
+ const cause = stringifyUnknown(errorLike.cause);
344
+ if (cause) {
345
+ serialized.cause = cause;
346
+ }
347
+ }
348
+ return serialized;
349
+ }
67
350
  function getNextFilePath(dir, baseName, extension) {
68
351
  let counter = 1;
69
352
  while (true) {
@@ -74,6 +357,13 @@ function getNextFilePath(dir, baseName, extension) {
74
357
  counter++;
75
358
  }
76
359
  }
360
+ function getAvailableFilePath(dir, baseName, extension) {
361
+ const firstFilePath = path__default.default.join(dir, `${baseName}${extension}`);
362
+ if (!fs__default.default.existsSync(firstFilePath)) {
363
+ return firstFilePath;
364
+ }
365
+ return getNextFilePath(dir, baseName, extension);
366
+ }
77
367
  function deleteFile(filePath) {
78
368
  if (fs__default.default.existsSync(filePath)) {
79
369
  fs__default.default.unlinkSync(filePath);
@@ -87,27 +377,194 @@ function createReportDirectory() {
87
377
  fs__default.default.mkdirSync(REPORT_DIR, { recursive: true });
88
378
  }
89
379
  }
90
- function getNowString() {
91
- const now = /* @__PURE__ */ new Date();
92
- const YYYY = now.getFullYear();
93
- const MM = String(now.getMonth() + 1).padStart(2, "0");
94
- const DD = String(now.getDate()).padStart(2, "0");
95
- const HH = String(now.getHours()).padStart(2, "0");
96
- const mm = String(now.getMinutes()).padStart(2, "0");
97
- const ss = String(now.getSeconds()).padStart(2, "0");
380
+ function getNowString(now = /* @__PURE__ */ new Date()) {
381
+ const { YYYY, MM, DD, HH, mm, ss } = getTimestampParts(now);
98
382
  return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;
99
383
  }
384
+ function getErrorLogTimestamp(now = /* @__PURE__ */ new Date()) {
385
+ const { YYYY, MM, DD, HH, mm, ss } = getTimestampParts(now);
386
+ return `${YYYY}-${MM}-${DD}-${HH}\uC2DC-${mm}\uBD84-${ss}\uCD08`;
387
+ }
388
+ function writeErrorReport(error, options = {}) {
389
+ try {
390
+ const now = /* @__PURE__ */ new Date();
391
+ helperTrace("error-report:write:start", options.scope || "unknown");
392
+ createReportDirectory();
393
+ const reportPath = getAvailableFilePath(REPORT_DIR, `error-log-${getErrorLogTimestamp(now)}`, ".md");
394
+ const serializedError = serializeError(error);
395
+ const traceSnapshot = options.traceMessages ?? getTraceMessages();
396
+ const extraSections = options.extraSections || [];
397
+ const report = `# Error Log
398
+
399
+ - \uBC1C\uC0DD \uC2DC\uAC01: ${getHumanReadableNowString(now)}
400
+ - Scope: \`${options.scope || "unknown"}\`
401
+ - \uC791\uC5C5 \uACBD\uB85C: \`${process.cwd()}\`
402
+ - \uC2E4\uD589 \uC778\uC790: \`${JSON.stringify(options.args ?? process.argv.slice(2))}\`
403
+ - \uC2E4\uD589 \uD658\uACBD: \`${process.platform} ${process.arch} / Node ${process.version}\`
404
+
405
+ ## Summary
406
+
407
+ ${options.title || serializedError.summary || "Unknown error"}
408
+
409
+ ## Error
410
+
411
+ \`\`\`json
412
+ ${JSON.stringify(serializedError, null, 2)}
413
+ \`\`\`
414
+
415
+ ## Trace
416
+
417
+ \`\`\`json
418
+ ${JSON.stringify(traceSnapshot, null, 2)}
419
+ \`\`\`${extraSections.length ? `
420
+ ${extraSections.map((section) => `
421
+ ## ${section.heading}
422
+
423
+ ${section.markdown}`).join("\n")}
424
+ ` : "\n"}
425
+ `;
426
+ fs__default.default.writeFileSync(reportPath, report);
427
+ helperTrace("error-report:write:done", reportPath);
428
+ return reportPath;
429
+ } catch (writeError) {
430
+ console.error("\u26A0\uFE0F \uC5D0\uB7EC \uB85C\uADF8 \uD30C\uC77C \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
431
+ console.error(writeError);
432
+ return "";
433
+ }
434
+ }
435
+ function exitWithError(message, options = {}) {
436
+ const reportPath = writeErrorReport(options.error || new Error(message), {
437
+ ...options,
438
+ title: message
439
+ });
440
+ console.error(message);
441
+ if (options.error) {
442
+ console.error(options.error);
443
+ }
444
+ if (reportPath) {
445
+ console.error(`\u{1F4C4} \uC5D0\uB7EC \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${reportPath}`);
446
+ }
447
+ process.exit(1);
448
+ }
449
+ function parseServiceFromArgs(args = process.argv.slice(2)) {
450
+ helperTrace("parse-service:start", `args=${JSON.stringify(args)}`);
451
+ const serviceIndex = args.indexOf("--service");
452
+ const rawService = serviceIndex !== -1 ? args[serviceIndex + 1] : "";
453
+ if (!rawService) {
454
+ helperTrace("parse-service:empty");
455
+ return "";
456
+ }
457
+ const normalizedService = rawService.toLowerCase();
458
+ if (AIServices.includes(normalizedService)) {
459
+ helperTrace("parse-service:resolved", normalizedService);
460
+ return normalizedService;
461
+ }
462
+ helperTrace("parse-service:invalid", rawService);
463
+ exitWithError(
464
+ `\u274C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC11C\uBE44\uC2A4\uC785\uB2C8\uB2E4: ${rawService}. \uC0AC\uC6A9 \uAC00\uB2A5 \uAC12: ${AIServices.join(", ")} (\uC608: --service codex)`,
465
+ {
466
+ scope: "helper:parseServiceFromArgs",
467
+ args,
468
+ extraSections: [
469
+ {
470
+ heading: "Allowed Services",
471
+ markdown: `\`\`\`json
472
+ ${JSON.stringify(AIServices, null, 2)}
473
+ \`\`\``
474
+ }
475
+ ]
476
+ }
477
+ );
478
+ }
100
479
  function getGitDiffFilter() {
101
- const includeExtensions = ["*.ts", "*.tsx", "*.js", "*.jsx"];
102
- const excludePatterns = ignoreList.map((item) => `:(exclude)${item}`);
480
+ const { includePatterns, excludePatterns } = getGitDiffPathspecs();
103
481
  const quote = (pattern) => `"${pattern}"`;
104
- const includeParams = includeExtensions.map(quote).join(" ");
482
+ const includeParams = includePatterns.map(quote).join(" ");
105
483
  const excludeParams = excludePatterns.map(quote).join(" ");
106
484
  return { includeParams, excludeParams };
107
485
  }
486
+ function truncateCommitSubject(subject) {
487
+ if (subject.length <= 72) {
488
+ return subject;
489
+ }
490
+ return `${subject.slice(0, 69)}...`;
491
+ }
492
+ function getRecentCommitOptions() {
493
+ const output = runGitCommand(
494
+ ["log", `-${COMMIT_FETCH_LIMIT}`, "--date=relative", "--pretty=format:%h%x09%an%x09%ar%x09%s"],
495
+ { allowFailure: true }
496
+ );
497
+ if (!output) {
498
+ return [];
499
+ }
500
+ return output.split("\n").map((line) => {
501
+ const [hash = "", author = "", relativeDate = "", ...subjectParts] = line.split(" ");
502
+ const subject = subjectParts.join(" ").trim();
503
+ return {
504
+ author,
505
+ description: `${author} | ${relativeDate}`,
506
+ hash,
507
+ label: `${hash} | ${truncateCommitSubject(subject)}`,
508
+ relativeDate,
509
+ subject
510
+ };
511
+ });
512
+ }
513
+ function buildSelectedCommitSummary(commits) {
514
+ return commits.map((commit) => `- ${commit.hash} | ${commit.subject} | ${commit.author} | ${commit.relativeDate}`).join("\n");
515
+ }
516
+ function getReviewPathspecArgs() {
517
+ const { includePatterns, excludePatterns } = getGitDiffPathspecs();
518
+ return [...includePatterns, ...excludePatterns];
519
+ }
520
+ function buildSelectedCommitDiff(commits) {
521
+ const reviewPathspecArgs = getReviewPathspecArgs();
522
+ const sections = commits.map((commit) => {
523
+ const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", ...reviewPathspecArgs], {
524
+ allowFailure: true,
525
+ trimOutput: false
526
+ }).trim();
527
+ if (!diff) {
528
+ return "";
529
+ }
530
+ return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
531
+ }).filter(Boolean).join("\n\n");
532
+ if (!sections) {
533
+ return "";
534
+ }
535
+ return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", "# \uB9AC\uBDF0 \uB300\uC0C1 diff", sections].join("\n");
536
+ }
537
+ function getSelectedCommitFiles(commits) {
538
+ const reviewPathspecArgs = getReviewPathspecArgs();
539
+ const files = /* @__PURE__ */ new Set();
540
+ commits.forEach((commit) => {
541
+ const output = runGitCommand(["show", "--pretty=format:", "--name-only", commit.hash, "--", ...reviewPathspecArgs], {
542
+ allowFailure: true
543
+ });
544
+ output.split("\n").map((line) => line.trim()).filter(Boolean).forEach((filePath) => files.add(filePath));
545
+ });
546
+ return [...files];
547
+ }
548
+ function buildSelectedFileDiff(commits, filePath) {
549
+ const sections = commits.map((commit) => {
550
+ const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", filePath], {
551
+ allowFailure: true,
552
+ trimOutput: false
553
+ }).trim();
554
+ if (!diff) {
555
+ return "";
556
+ }
557
+ return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
558
+ }).filter(Boolean).join("\n\n");
559
+ if (!sections) {
560
+ return "";
561
+ }
562
+ return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", `# \uD30C\uC77C: ${filePath}`, sections].join("\n\n");
563
+ }
108
564
  function openReport(reportPath) {
109
565
  const resolvedPath = path__default.default.resolve(reportPath);
110
566
  const { platform } = process;
567
+ helperTrace("open-report:start", resolvedPath);
111
568
  const openWithChrome = () => {
112
569
  if (platform === "darwin") {
113
570
  child_process.execSync(`open -a "Google Chrome" "${resolvedPath}"`, { stdio: "ignore" });
@@ -132,71 +589,199 @@ function openReport(reportPath) {
132
589
  };
133
590
  try {
134
591
  if (openWithChrome()) {
592
+ helperTrace("open-report:chrome:success", platform);
135
593
  console.log("\u{1F680} Google Chrome\uC5D0\uC11C \uB9AC\uD3EC\uD2B8\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4.");
136
594
  return;
137
595
  }
138
- } catch {
596
+ } catch (error) {
597
+ helperTrace("open-report:chrome:failed", getErrorSummary(error));
139
598
  }
140
599
  try {
141
600
  if (openWithDefaultBrowser()) {
601
+ helperTrace("open-report:default-browser:success", platform);
142
602
  console.log("\u{1F680} \uAE30\uBCF8 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB9AC\uD3EC\uD2B8\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4.");
143
603
  return;
144
604
  }
145
- } catch (e) {
146
- console.error("\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800 \uC5F4\uAE30 \uC2E4\uD328:", e);
605
+ } catch (error) {
606
+ helperTrace("open-report:default-browser:failed", getErrorSummary(error));
607
+ console.error("\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800 \uC5F4\uAE30 \uC2E4\uD328:", error);
147
608
  return;
148
609
  }
610
+ helperTrace("open-report:unsupported-platform", platform);
149
611
  console.error(`\u26A0\uFE0F \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD50C\uB7AB\uD3FC\uC785\uB2C8\uB2E4: ${platform}`);
150
612
  }
151
- function getDiffArgs() {
152
- const args = process.argv.slice(2);
153
- const commitIndex = args.indexOf("--commit");
154
- const { includeParams, excludeParams } = getGitDiffFilter();
155
- let diffArgs = "";
156
- if (commitIndex !== -1) {
157
- const commitHash = args[commitIndex + 1];
158
- if (!commitHash) {
159
- console.error("\u274C \uCEE4\uBC0B \uD574\uC2DC\uAC00 \uC81C\uACF5\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
160
- process.exit(1);
613
+ function ensureInteractiveSelectionAvailable(scope, message) {
614
+ if (process.stdin.isTTY && process.stdout.isTTY && typeof process.stdin.setRawMode === "function") {
615
+ return;
616
+ }
617
+ helperTrace(`${scope}:tty-missing`);
618
+ exitWithError(message, {
619
+ scope: `helper:${scope}`
620
+ });
621
+ }
622
+ function renderSelectionBlock(lines, previousLineCount) {
623
+ if (previousLineCount > 0) {
624
+ readline__default.default.moveCursor(process.stdout, 0, -previousLineCount);
625
+ readline__default.default.clearScreenDown(process.stdout);
626
+ }
627
+ const fittedLines = fitLinesToTerminal(lines);
628
+ process.stdout.write(`${fittedLines.join("\n")}
629
+ `);
630
+ return fittedLines.length;
631
+ }
632
+ function getSelectionWindowRange(optionCount, selectedIndex, windowSize) {
633
+ if (optionCount <= windowSize) {
634
+ return {
635
+ end: optionCount,
636
+ start: 0
637
+ };
638
+ }
639
+ const halfWindow = Math.floor(windowSize / 2);
640
+ const maxStart = optionCount - windowSize;
641
+ const start = Math.max(0, Math.min(selectedIndex - halfWindow, maxStart));
642
+ return {
643
+ end: Math.min(optionCount, start + windowSize),
644
+ start
645
+ };
646
+ }
647
+ function buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize) {
648
+ const { start, end } = getSelectionWindowRange(options.length, selectedIndex, windowSize);
649
+ const lines = [
650
+ `${ANSI.bold}${question}${ANSI.reset}`,
651
+ `${ANSI.dim}\u2191\u2193 \uC774\uB3D9 | Space \uC120\uD0DD/\uD574\uC81C | Enter \uC644\uB8CC | Esc \uCDE8\uC18C${ANSI.reset}`,
652
+ `${ANSI.dim}\uC120\uD0DD\uB428: ${toggled.size}\uAC1C / \uC804\uCCB4: ${options.length}\uAC1C${ANSI.reset}`
653
+ ];
654
+ for (let index = start; index < end; index += 1) {
655
+ const option = options[index];
656
+ if (!option) {
657
+ continue;
161
658
  }
162
- const nextArg = args[commitIndex + 2];
163
- let n = 0;
164
- if (nextArg && !nextArg.startsWith("--")) {
165
- n = parseInt(nextArg, 10);
166
- if (isNaN(n)) {
167
- n = 0;
168
- }
659
+ const cursor = index === selectedIndex ? `${ANSI.cyan}>${ANSI.reset}` : " ";
660
+ const checked = toggled.has(index) ? `${ANSI.green}\u2611${ANSI.reset}` : "\u2610";
661
+ const description = option.description ? ` ${ANSI.dim}${option.description}${ANSI.reset}` : "";
662
+ lines.push(`${cursor} ${checked} ${option.label}${description}`);
663
+ }
664
+ if (options.length > windowSize) {
665
+ lines.push(`${ANSI.dim}\uD45C\uC2DC \uBC94\uC704: ${start + 1}-${end} / ${options.length}${ANSI.reset}`);
666
+ }
667
+ return lines;
668
+ }
669
+ async function showMultiSelect(question, options, windowSize = COMMIT_SELECTION_WINDOW) {
670
+ ensureInteractiveSelectionAvailable("showMultiSelect", "\u274C \uCEE4\uBC0B \uC120\uD0DD \uBAA8\uB2EC\uC740 TTY \uD658\uACBD\uC5D0\uC11C\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
671
+ let selectedIndex = 0;
672
+ let renderedLineCount = 0;
673
+ const toggled = /* @__PURE__ */ new Set();
674
+ const rl = readline__default.default.createInterface({
675
+ input: process.stdin,
676
+ output: process.stdout,
677
+ terminal: true
678
+ });
679
+ process.stdout.write("\x1B[?25l");
680
+ const cleanup = () => {
681
+ if (renderedLineCount > 0) {
682
+ readline__default.default.moveCursor(process.stdout, 0, -renderedLineCount);
683
+ readline__default.default.clearScreenDown(process.stdout);
684
+ renderedLineCount = 0;
169
685
  }
170
- console.log(`\u2139\uFE0F \uCEE4\uBC0B '${commitHash}' ${n > 0 ? ` \uD3EC\uD568 \uCD1D ${n + 1}\uAC1C\uC758 \uCEE4\uBC0B` : ""}\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...`);
171
- diffArgs = `${commitHash}~${n + 1} ${commitHash}`;
172
- } else {
173
- try {
174
- const check = child_process.execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();
175
- if (!check.trim()) {
176
- 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...");
177
- diffArgs = "HEAD~1 HEAD";
686
+ process.stdin.removeListener("data", onData);
687
+ process.stdin.setRawMode(false);
688
+ process.stdin.pause();
689
+ rl.close();
690
+ process.stdout.write("\x1B[?25h");
691
+ };
692
+ const render = () => {
693
+ const lines = buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize);
694
+ renderedLineCount = renderSelectionBlock(lines, renderedLineCount);
695
+ };
696
+ const confirmSelection = (resolve) => {
697
+ const values = [...toggled].sort((left, right) => left - right).map((index) => options[index]?.value).filter((value) => value !== void 0);
698
+ cleanup();
699
+ resolve(values);
700
+ };
701
+ const cancelSelection = (resolve) => {
702
+ cleanup();
703
+ resolve([]);
704
+ };
705
+ let onData = (_data) => {
706
+ };
707
+ render();
708
+ return new Promise((resolve) => {
709
+ onData = (data) => {
710
+ const key = data.toString();
711
+ if (key === "") {
712
+ cleanup();
713
+ process.exit(0);
178
714
  }
179
- } catch {
180
- }
715
+ if (key === "\x1B") {
716
+ cancelSelection(resolve);
717
+ return;
718
+ }
719
+ if (key === "\x1B[A") {
720
+ selectedIndex = (selectedIndex - 1 + options.length) % options.length;
721
+ render();
722
+ return;
723
+ }
724
+ if (key === "\x1B[B") {
725
+ selectedIndex = (selectedIndex + 1) % options.length;
726
+ render();
727
+ return;
728
+ }
729
+ if (key === " ") {
730
+ if (toggled.has(selectedIndex)) {
731
+ toggled.delete(selectedIndex);
732
+ } else {
733
+ toggled.add(selectedIndex);
734
+ }
735
+ render();
736
+ return;
737
+ }
738
+ if (key === "\r" || key === "\n") {
739
+ confirmSelection(resolve);
740
+ }
741
+ };
742
+ process.stdin.setRawMode(true);
743
+ process.stdin.resume();
744
+ process.stdin.on("data", onData);
745
+ });
746
+ }
747
+ async function selectReviewCommits() {
748
+ const commits = getRecentCommitOptions();
749
+ if (commits.length === 0) {
750
+ console.log("\u2139\uFE0F \uB9AC\uBDF0\uD560 \uCD5C\uADFC \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
751
+ return [];
181
752
  }
182
- return diffArgs;
753
+ return showMultiSelect(
754
+ "\uB9AC\uBDF0\uD560 \uCEE4\uBC0B\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.",
755
+ commits.map((commit) => ({
756
+ description: commit.description,
757
+ label: commit.label,
758
+ value: commit
759
+ })),
760
+ COMMIT_SELECTION_WINDOW
761
+ );
183
762
  }
184
763
  function selectAIService() {
185
764
  const service = parseServiceFromArgs();
186
765
  if (!service) {
187
- console.error("\u274C \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
188
- process.exit(1);
766
+ helperTrace("select-service:missing");
767
+ exitWithError("\u274C \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.", {
768
+ scope: "helper:selectAIService"
769
+ });
189
770
  }
771
+ helperTrace("select-service:done", service);
190
772
  return service;
191
773
  }
192
774
  async function showSelectionAIService() {
193
775
  const selectedServiceFromArgs = parseServiceFromArgs();
194
776
  if (selectedServiceFromArgs) {
777
+ helperTrace("show-selection:from-args", selectedServiceFromArgs);
195
778
  console.log(`
196
- \u2705 \x1B[32m${selectedServiceFromArgs}\x1B[0m \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (--service)
779
+ \u2705 ${ANSI.green}${selectedServiceFromArgs}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (--service)
197
780
  `);
198
781
  return selectedServiceFromArgs;
199
782
  }
783
+ 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.");
784
+ helperTrace("show-selection:interactive:start");
200
785
  let selectedIndex = 0;
201
786
  const rl = readline__default.default.createInterface({
202
787
  input: process.stdin,
@@ -210,13 +795,15 @@ async function showSelectionAIService() {
210
795
  readline__default.default.moveCursor(process.stdout, 0, -(AIServices.length + 1));
211
796
  }
212
797
  firstRender = false;
798
+ helperTrace("show-selection:interactive:render", AIServices[selectedIndex] || "unknown");
213
799
  readline__default.default.clearScreenDown(process.stdout);
214
800
  process.stdout.write(
215
- "\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"
801
+ `\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):
802
+ `
216
803
  );
217
804
  AIServices.forEach((service, index) => {
218
805
  if (index === selectedIndex) {
219
- process.stdout.write(` \x1B[36m>\x1B[0m \x1B[36m\u25C9\x1B[0m \x1B[1m${service}\x1B[0m
806
+ process.stdout.write(` ${ANSI.cyan}>${ANSI.reset} ${ANSI.cyan}\u25C9${ANSI.reset} ${ANSI.bold}${service}${ANSI.reset}
220
807
  `);
221
808
  } else {
222
809
  process.stdout.write(` \u25EF ${service}
@@ -229,6 +816,7 @@ async function showSelectionAIService() {
229
816
  const onData = (data) => {
230
817
  const key = data.toString();
231
818
  if (key === "") {
819
+ helperTrace("show-selection:interactive:ctrl-c");
232
820
  process.stdout.write("\x1B[?25h");
233
821
  process.exit(0);
234
822
  }
@@ -245,10 +833,11 @@ async function showSelectionAIService() {
245
833
  rl.close();
246
834
  process.stdout.write("\x1B[?25h");
247
835
  console.log(`
248
- \u2705 \x1B[32m${AIServices[selectedIndex]}\x1B[0m \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
836
+ \u2705 ${ANSI.green}${AIServices[selectedIndex]}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
249
837
  `);
250
838
  const result = AIServices[selectedIndex];
251
839
  if (result) {
840
+ helperTrace("show-selection:interactive:confirmed", result);
252
841
  resolve(result);
253
842
  }
254
843
  }
@@ -260,16 +849,30 @@ async function showSelectionAIService() {
260
849
  }
261
850
 
262
851
  exports.AIServices = AIServices;
852
+ exports.COMMIT_FETCH_LIMIT = COMMIT_FETCH_LIMIT;
853
+ exports.COMMIT_SELECTION_WINDOW = COMMIT_SELECTION_WINDOW;
263
854
  exports.REPORT_DIR = REPORT_DIR;
855
+ exports.buildSelectedCommitDiff = buildSelectedCommitDiff;
856
+ exports.buildSelectedCommitSummary = buildSelectedCommitSummary;
857
+ exports.buildSelectedFileDiff = buildSelectedFileDiff;
858
+ exports.clearTraceMessages = clearTraceMessages;
264
859
  exports.codingConventionRulesPath = codingConventionRulesPath;
265
860
  exports.createReportDirectory = createReportDirectory;
266
861
  exports.createTraceLogger = createTraceLogger;
267
862
  exports.deleteFile = deleteFile;
268
863
  exports.deleteTempDiff = deleteTempDiff;
269
- exports.getDiffArgs = getDiffArgs;
864
+ exports.executeShellCommandWithProgress = executeShellCommandWithProgress;
865
+ exports.exitWithError = exitWithError;
866
+ exports.formatReviewTargetFiles = formatReviewTargetFiles;
867
+ exports.getAvailableFilePath = getAvailableFilePath;
868
+ exports.getErrorLogTimestamp = getErrorLogTimestamp;
869
+ exports.getErrorSummary = getErrorSummary;
270
870
  exports.getGitDiffFilter = getGitDiffFilter;
271
871
  exports.getNextFilePath = getNextFilePath;
272
872
  exports.getNowString = getNowString;
873
+ exports.getRecentCommitOptions = getRecentCommitOptions;
874
+ exports.getSelectedCommitFiles = getSelectedCommitFiles;
875
+ exports.getTraceMessages = getTraceMessages;
273
876
  exports.ignoreList = ignoreList;
274
877
  exports.isTestMode = isTestMode;
275
878
  exports.namingRulesPath = namingRulesPath;
@@ -278,7 +881,10 @@ exports.reviewFormOneByOnePath = reviewFormOneByOnePath;
278
881
  exports.reviewFormPath = reviewFormPath;
279
882
  exports.rulesPath = rulesPath;
280
883
  exports.selectAIService = selectAIService;
884
+ exports.selectReviewCommits = selectReviewCommits;
885
+ exports.showMultiSelect = showMultiSelect;
281
886
  exports.showSelectionAIService = showSelectionAIService;
282
887
  exports.tempDiffPath = tempDiffPath;
888
+ exports.writeErrorReport = writeErrorReport;
283
889
  //# sourceMappingURL=helper.cjs.map
284
890
  //# sourceMappingURL=helper.cjs.map