sales-frontend-gemini-cli 0.4.3 → 0.5.0

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 (43) hide show
  1. package/dist/common/helper.cjs +227 -20
  2. package/dist/common/helper.cjs.map +1 -1
  3. package/dist/common/helper.d.cts +30 -1
  4. package/dist/common/helper.d.ts +30 -1
  5. package/dist/common/helper.js +226 -21
  6. package/dist/common/helper.js.map +1 -1
  7. package/dist/common/types.d.cts +3 -1
  8. package/dist/common/types.d.ts +3 -1
  9. package/dist/pr-review/claude/claude-commander.cjs +10 -3
  10. package/dist/pr-review/claude/claude-commander.cjs.map +1 -1
  11. package/dist/pr-review/claude/claude-commander.js +10 -3
  12. package/dist/pr-review/claude/claude-commander.js.map +1 -1
  13. package/dist/pr-review/claude/installation-claude.cjs +1 -1
  14. package/dist/pr-review/claude/installation-claude.cjs.map +1 -1
  15. package/dist/pr-review/claude/installation-claude.js +1 -1
  16. package/dist/pr-review/claude/installation-claude.js.map +1 -1
  17. package/dist/pr-review/codex/codex-commander.cjs +14 -4
  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 +14 -4
  22. package/dist/pr-review/codex/codex-commander.js.map +1 -1
  23. package/dist/pr-review/codex/installation-codex.cjs +1 -1
  24. package/dist/pr-review/codex/installation-codex.cjs.map +1 -1
  25. package/dist/pr-review/codex/installation-codex.js +1 -1
  26. package/dist/pr-review/codex/installation-codex.js.map +1 -1
  27. package/dist/pr-review/gemini/gemini-commander.cjs +12 -12
  28. package/dist/pr-review/gemini/gemini-commander.cjs.map +1 -1
  29. package/dist/pr-review/gemini/gemini-commander.js +12 -12
  30. package/dist/pr-review/gemini/gemini-commander.js.map +1 -1
  31. package/dist/pr-review/gemini/installation-gemini.cjs +1 -1
  32. package/dist/pr-review/gemini/installation-gemini.cjs.map +1 -1
  33. package/dist/pr-review/gemini/installation-gemini.js +1 -1
  34. package/dist/pr-review/gemini/installation-gemini.js.map +1 -1
  35. package/dist/pr-review/review-one-by-one.cjs +296 -42
  36. package/dist/pr-review/review-one-by-one.cjs.map +1 -1
  37. package/dist/pr-review/review-one-by-one.js +296 -42
  38. package/dist/pr-review/review-one-by-one.js.map +1 -1
  39. package/dist/pr-review/review.cjs +327 -42
  40. package/dist/pr-review/review.cjs.map +1 -1
  41. package/dist/pr-review/review.js +327 -42
  42. package/dist/pr-review/review.js.map +1 -1
  43. package/package.json +1 -1
@@ -71,6 +71,9 @@ var ignoreList = [
71
71
  function isTestMode(args4 = process.argv.slice(2)) {
72
72
  return args4.includes("--test");
73
73
  }
74
+ function shouldStreamAIOutput(args4 = process.argv.slice(2)) {
75
+ return args4.includes("--stream-output");
76
+ }
74
77
  function clearTraceMessages() {
75
78
  traceMessages.length = 0;
76
79
  }
@@ -197,15 +200,51 @@ function runGitCommand(args4, options = {}) {
197
200
  throw error;
198
201
  }
199
202
  }
203
+ function resolveShellLaunchConfig() {
204
+ const candidates = [];
205
+ const seen = /* @__PURE__ */ new Set();
206
+ const appendCandidate = (executable, shellArgs) => {
207
+ const normalizedExecutable = executable?.trim();
208
+ if (!normalizedExecutable) {
209
+ return;
210
+ }
211
+ if (path.isAbsolute(normalizedExecutable) && !fs.existsSync(normalizedExecutable)) {
212
+ return;
213
+ }
214
+ const key = `${normalizedExecutable}::${shellArgs.join("\0")}`;
215
+ if (seen.has(key)) {
216
+ return;
217
+ }
218
+ seen.add(key);
219
+ candidates.push({
220
+ executable: normalizedExecutable,
221
+ shellArgs
222
+ });
223
+ };
224
+ const shellFromEnv = process.env.SHELL?.trim();
225
+ const envShellName = shellFromEnv ? path.basename(shellFromEnv) : "";
226
+ if (["bash", "zsh", "ksh"].includes(envShellName)) {
227
+ appendCandidate(shellFromEnv, ["-lc"]);
228
+ }
229
+ ["/bin/bash", "/usr/bin/bash", "bash", "/bin/zsh", "/usr/bin/zsh", "zsh"].forEach((shellPath) => {
230
+ appendCandidate(shellPath, ["-lc"]);
231
+ });
232
+ ["/bin/sh", "/usr/bin/sh", "sh"].forEach((shellPath) => {
233
+ appendCandidate(shellPath, ["-c"]);
234
+ });
235
+ return candidates[0] || { executable: "sh", shellArgs: ["-c"] };
236
+ }
200
237
  async function executeShellCommandWithProgress(command, options = {}) {
201
238
  const { progressIntervalMs = 1e4, progressMessage = "\u23F3 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uB294 \uC911\uC785\uB2C8\uB2E4...", streamOutput = false } = options;
202
239
  const { spawn } = await import('child_process');
240
+ const shellLaunchConfig = resolveShellLaunchConfig();
203
241
  return new Promise((resolve, reject) => {
204
242
  let stdout = "";
205
243
  let stderr = "";
206
244
  const startedAt = Date.now();
207
245
  console.log(progressMessage);
208
- const child = spawn("/bin/zsh", ["-lc", command], {
246
+ helperTrace("shell-command:launcher", `${shellLaunchConfig.executable} ${shellLaunchConfig.shellArgs.join(" ")}`);
247
+ const child = spawn(shellLaunchConfig.executable, [...shellLaunchConfig.shellArgs, command], {
209
248
  stdio: ["ignore", "pipe", "pipe"]
210
249
  });
211
250
  const progressTimer = setInterval(() => {
@@ -240,11 +279,41 @@ async function executeShellCommandWithProgress(command, options = {}) {
240
279
  return;
241
280
  }
242
281
  const exitSummary = signal ? `signal=${signal}` : `code=${String(code ?? "unknown")}`;
243
- reject(new Error(`\uC258 \uBA85\uB839 \uC2E4\uD589 \uC2E4\uD328 (${exitSummary})${stderr.trim() ? `
244
- ${stderr.trim()}` : ""}`));
282
+ const failureDetails = {
283
+ code,
284
+ command,
285
+ signal,
286
+ stderr,
287
+ stdout
288
+ };
289
+ reject(createShellCommandExecutionError(failureDetails, exitSummary));
245
290
  });
246
291
  });
247
292
  }
293
+ function getShellCommandFailurePreview(failureDetails) {
294
+ const stderrText = failureDetails.stderr.trim();
295
+ const stdoutText = failureDetails.stdout.trim();
296
+ const combinedOutput = stderrText || stdoutText;
297
+ if (!combinedOutput) {
298
+ return "";
299
+ }
300
+ const MAX_PREVIEW_LENGTH = 4e3;
301
+ if (combinedOutput.length <= MAX_PREVIEW_LENGTH) {
302
+ return combinedOutput;
303
+ }
304
+ return combinedOutput.slice(-4e3);
305
+ }
306
+ function createShellCommandExecutionError(failureDetails, exitSummary) {
307
+ const failurePreview = getShellCommandFailurePreview(failureDetails);
308
+ const error = new Error(`\uC258 \uBA85\uB839 \uC2E4\uD589 \uC2E4\uD328 (${exitSummary})${failurePreview ? `
309
+ ${failurePreview}` : ""}`);
310
+ error.code = failureDetails.code;
311
+ error.signal = failureDetails.signal;
312
+ error.stdout = failureDetails.stdout;
313
+ error.stderr = failureDetails.stderr;
314
+ error.command = failureDetails.command;
315
+ return error;
316
+ }
248
317
  function formatReviewTargetFiles(files, visibleCount = 5) {
249
318
  if (files.length === 0) {
250
319
  return "(\uC5C6\uC74C)";
@@ -317,7 +386,7 @@ function serializeError(error) {
317
386
  }
318
387
  if (error && typeof error === "object") {
319
388
  const errorLike = error;
320
- const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs"];
389
+ const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs", "command"];
321
390
  extraKeys.forEach((key) => {
322
391
  if (errorLike[key] !== void 0) {
323
392
  serialized[key] = errorLike[key];
@@ -423,6 +492,87 @@ ${section.markdown}`).join("\n")}
423
492
  return "";
424
493
  }
425
494
  }
495
+ function getExecutionLogSummary(status, title) {
496
+ if (title) {
497
+ return title;
498
+ }
499
+ switch (status) {
500
+ case "success":
501
+ return "\uB9AC\uBDF0 \uC2E4\uD589\uC774 \uC131\uACF5\uC801\uC73C\uB85C \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.";
502
+ case "failed":
503
+ return "\uB9AC\uBDF0 \uC2E4\uD589 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
504
+ case "partial_failure":
505
+ 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.";
506
+ default:
507
+ 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.";
508
+ }
509
+ }
510
+ function formatExecutionDuration(startedAt, finishedAt) {
511
+ const durationMs = Math.max(0, finishedAt.getTime() - startedAt.getTime());
512
+ if (durationMs < 1e3) {
513
+ return `${durationMs}ms`;
514
+ }
515
+ const durationSeconds = durationMs / 1e3;
516
+ if (durationSeconds < 60) {
517
+ return `${durationSeconds.toFixed(1)}s`;
518
+ }
519
+ const minutes = Math.floor(durationSeconds / 60);
520
+ const seconds = Math.round(durationSeconds % 60);
521
+ return `${minutes}m ${seconds}s`;
522
+ }
523
+ function writeExecutionLog(options = {}) {
524
+ try {
525
+ const startedAt = options.startedAt ?? /* @__PURE__ */ new Date();
526
+ const finishedAt = options.finishedAt ?? /* @__PURE__ */ new Date();
527
+ const status = options.status ?? "success";
528
+ helperTrace("execution-log:write:start", options.scope || "unknown");
529
+ createReportDirectory();
530
+ const reportPath = getAvailableFilePath(REPORT_DIR, `${getNowString(finishedAt)}-execution-log`, ".md");
531
+ const traceSnapshot = options.traceMessages ?? getTraceMessages();
532
+ const extraSections = options.extraSections || [];
533
+ const serializedError = options.error ? serializeError(options.error) : null;
534
+ const report = `# Execution Log
535
+
536
+ - \uC2DC\uC791 \uC2DC\uAC01: ${getHumanReadableNowString(startedAt)}
537
+ - \uC885\uB8CC \uC2DC\uAC01: ${getHumanReadableNowString(finishedAt)}
538
+ - \uC2E4\uD589 \uC2DC\uAC04: ${formatExecutionDuration(startedAt, finishedAt)}
539
+ - \uC0C1\uD0DC: \`${status}\`
540
+ - Scope: \`${options.scope || "unknown"}\`
541
+ - \uC791\uC5C5 \uACBD\uB85C: \`${process.cwd()}\`
542
+ - \uC2E4\uD589 \uC778\uC790: \`${JSON.stringify(options.args ?? process.argv.slice(2))}\`
543
+ - \uC2E4\uD589 \uD658\uACBD: \`${process.platform} ${process.arch} / Node ${process.version}\`
544
+
545
+ ## Summary
546
+
547
+ ${getExecutionLogSummary(status, options.title)}
548
+ ${serializedError ? `
549
+
550
+ ## Error
551
+
552
+ \`\`\`json
553
+ ${JSON.stringify(serializedError, null, 2)}
554
+ \`\`\`` : ""}
555
+
556
+ ## Trace
557
+
558
+ \`\`\`json
559
+ ${JSON.stringify(traceSnapshot, null, 2)}
560
+ \`\`\`${extraSections.length ? `
561
+ ${extraSections.map((section) => `
562
+ ## ${section.heading}
563
+
564
+ ${section.markdown}`).join("\n")}
565
+ ` : "\n"}
566
+ `;
567
+ fs.writeFileSync(reportPath, report);
568
+ helperTrace("execution-log:write:done", reportPath);
569
+ return reportPath;
570
+ } catch (writeError) {
571
+ console.error("\u26A0\uFE0F \uC2E4\uD589 \uB85C\uADF8 \uD30C\uC77C \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
572
+ console.error(writeError);
573
+ return "";
574
+ }
575
+ }
426
576
  function exitWithError(message, options = {}) {
427
577
  const reportPath = writeErrorReport(options.error || new Error(message), {
428
578
  ...options,
@@ -473,6 +623,62 @@ function truncateCommitSubject(subject) {
473
623
  }
474
624
  return `${subject.slice(0, 69)}...`;
475
625
  }
626
+ function formatReviewTargetFileCount(fileCount) {
627
+ return fileCount === 0 ? "\uBCC0\uACBD \uC5C6\uC74C" : `${fileCount}\uAC1C \uD30C\uC77C`;
628
+ }
629
+ function buildReviewTargetSectionTitle(target) {
630
+ return target.kind === "commit" ? `${target.hash} ${target.subject}` : `${target.hash} | ${target.subject}`;
631
+ }
632
+ function buildReviewTargetSummaryLine(target) {
633
+ if (target.kind === "commit") {
634
+ return `- ${target.hash} | ${target.subject} | ${target.author} | ${target.relativeDate}`;
635
+ }
636
+ return `- ${target.hash} | ${target.subject}`;
637
+ }
638
+ function buildReviewTargetDiffArgs(target, filePath) {
639
+ const reviewPathspecArgs = getReviewPathspecArgs();
640
+ if (target.kind === "commit") {
641
+ return ["show", "--stat", "--patch", "--format=", target.hash, "--", ...reviewPathspecArgs];
642
+ }
643
+ const diffArgs = ["diff"];
644
+ if (target.kind === "staged") {
645
+ diffArgs.push("--cached");
646
+ }
647
+ diffArgs.push("--stat", "--patch", "--");
648
+ return [...diffArgs, ...reviewPathspecArgs];
649
+ }
650
+ function buildReviewTargetFileArgs(target) {
651
+ const reviewPathspecArgs = getReviewPathspecArgs();
652
+ if (target.kind === "commit") {
653
+ return ["show", "--pretty=format:", "--name-only", target.hash, "--", ...reviewPathspecArgs];
654
+ }
655
+ const diffArgs = ["diff"];
656
+ if (target.kind === "staged") {
657
+ diffArgs.push("--cached");
658
+ }
659
+ return [...diffArgs, "--name-only", "--", ...reviewPathspecArgs];
660
+ }
661
+ function getReviewTargetFiles(target) {
662
+ const output = runGitCommand(buildReviewTargetFileArgs(target), {
663
+ allowFailure: true
664
+ });
665
+ return output.split("\n").map((line) => line.trim()).filter(Boolean);
666
+ }
667
+ function createWorkingTreeReviewOption(kind) {
668
+ const files = getReviewTargetFiles({
669
+ hash: kind,
670
+ kind});
671
+ const subject = kind === "unstaged" ? "\uC544\uC9C1 git add \uD558\uC9C0 \uC54A\uC740 \uBCC0\uACBD\uC0AC\uD56D" : "git add \uB41C \uBCC0\uACBD\uC0AC\uD56D";
672
+ return {
673
+ author: "",
674
+ description: `${subject} | ${formatReviewTargetFileCount(files.length)}`,
675
+ hash: kind,
676
+ kind,
677
+ label: kind,
678
+ relativeDate: "",
679
+ subject
680
+ };
681
+ }
476
682
  function getRecentCommitOptions() {
477
683
  const output = runGitCommand(
478
684
  ["log", `-${COMMIT_FETCH_LIMIT}`, "--date=relative", "--pretty=format:%h%x09%an%x09%ar%x09%s"],
@@ -488,44 +694,43 @@ function getRecentCommitOptions() {
488
694
  author,
489
695
  description: `${author} | ${relativeDate}`,
490
696
  hash,
697
+ kind: "commit",
491
698
  label: `${hash} | ${truncateCommitSubject(subject)}`,
492
699
  relativeDate,
493
700
  subject
494
701
  };
495
702
  });
496
703
  }
704
+ function getReviewTargetOptions() {
705
+ return [createWorkingTreeReviewOption("unstaged"), createWorkingTreeReviewOption("staged"), ...getRecentCommitOptions()];
706
+ }
497
707
  function buildSelectedCommitSummary(commits) {
498
- return commits.map((commit) => `- ${commit.hash} | ${commit.subject} | ${commit.author} | ${commit.relativeDate}`).join("\n");
708
+ return commits.map((commit) => buildReviewTargetSummaryLine(commit)).join("\n");
499
709
  }
500
710
  function getReviewPathspecArgs() {
501
711
  const { includePatterns, excludePatterns } = getGitDiffPathspecs();
502
712
  return [...includePatterns, ...excludePatterns];
503
713
  }
504
714
  function buildSelectedCommitDiff(commits) {
505
- const reviewPathspecArgs = getReviewPathspecArgs();
506
715
  const sections = commits.map((commit) => {
507
- const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", ...reviewPathspecArgs], {
716
+ const diff = runGitCommand(buildReviewTargetDiffArgs(commit), {
508
717
  allowFailure: true,
509
718
  trimOutput: false
510
719
  }).trim();
511
720
  if (!diff) {
512
721
  return "";
513
722
  }
514
- return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
723
+ return [`## ${buildReviewTargetSectionTitle(commit)}`, diff].join("\n\n");
515
724
  }).filter(Boolean).join("\n\n");
516
725
  if (!sections) {
517
726
  return "";
518
727
  }
519
- return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", "# \uB9AC\uBDF0 \uB300\uC0C1 diff", sections].join("\n");
728
+ return ["# \uC120\uD0DD\uD55C \uB9AC\uBDF0 \uB300\uC0C1", buildSelectedCommitSummary(commits), "", "# \uB9AC\uBDF0 \uB300\uC0C1 diff", sections].join("\n");
520
729
  }
521
730
  function getSelectedCommitFiles(commits) {
522
- const reviewPathspecArgs = getReviewPathspecArgs();
523
731
  const files = /* @__PURE__ */ new Set();
524
732
  commits.forEach((commit) => {
525
- const output = runGitCommand(["show", "--pretty=format:", "--name-only", commit.hash, "--", ...reviewPathspecArgs], {
526
- allowFailure: true
527
- });
528
- output.split("\n").map((line) => line.trim()).filter(Boolean).forEach((filePath) => files.add(filePath));
733
+ getReviewTargetFiles(commit).forEach((filePath) => files.add(filePath));
529
734
  });
530
735
  return [...files];
531
736
  }
@@ -713,13 +918,13 @@ async function showMultiSelect(question, options, windowSize = COMMIT_SELECTION_
713
918
  });
714
919
  }
715
920
  async function selectReviewCommits() {
716
- const commits = getRecentCommitOptions();
921
+ const commits = getReviewTargetOptions();
717
922
  if (commits.length === 0) {
718
- console.log("\u2139\uFE0F \uB9AC\uBDF0\uD560 \uCD5C\uADFC \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
923
+ console.log("\u2139\uFE0F \uB9AC\uBDF0\uD560 \uB300\uC0C1\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
719
924
  return [];
720
925
  }
721
926
  return showMultiSelect(
722
- "\uB9AC\uBDF0\uD560 \uCEE4\uBC0B\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.",
927
+ "\uB9AC\uBDF0\uD560 \uB300\uC0C1\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.",
723
928
  commits.map((commit) => ({
724
929
  description: commit.description,
725
930
  label: commit.label,
@@ -810,6 +1015,13 @@ var ALLOWED_REASONING_EFFORTS = ["minimal", "low", "medium", "high"];
810
1015
  function shellQuote(value) {
811
1016
  return `'${value.replace(/'/g, `'\\''`)}'`;
812
1017
  }
1018
+ function toShellOptionToken(value) {
1019
+ const SIMPLE_SHELL_TOKEN_PATTERN = /^[A-Za-z0-9._:/=-]+$/;
1020
+ if (SIMPLE_SHELL_TOKEN_PATTERN.test(value)) {
1021
+ return value;
1022
+ }
1023
+ return shellQuote(value);
1024
+ }
813
1025
  function getArgValue(flag) {
814
1026
  const index = args.indexOf(flag);
815
1027
  if (index === -1 || !args[index + 1]) {
@@ -889,9 +1101,9 @@ function getAliasFallbacks(primaryAlias) {
889
1101
  }
890
1102
  function buildClaudeExecCommand(options) {
891
1103
  const { tempDiffPath: tempDiffPath2, prompt, systemPromptFiles, effort, model, fallbackModel } = options;
892
- const modelOption = model ? `--model ${shellQuote(model)}` : "";
893
- const fallbackOption = model && fallbackModel ? `--fallback-model ${shellQuote(fallbackModel)}` : "";
894
- const effortOption = `--effort ${shellQuote(effort)}`;
1104
+ const modelOption = model ? `--model ${toShellOptionToken(model)}` : "";
1105
+ const fallbackOption = model && fallbackModel ? `--fallback-model ${toShellOptionToken(fallbackModel)}` : "";
1106
+ const effortOption = `--effort ${toShellOptionToken(effort)}`;
895
1107
  const appendedPromptFiles = systemPromptFiles.map((path3) => `--append-system-prompt-file ${shellQuote(path3)}`).join(" ");
896
1108
  return `cat ${shellQuote(tempDiffPath2)} | claude ${[
897
1109
  modelOption,
@@ -1016,12 +1228,22 @@ function printNotice2(message) {
1016
1228
  console.warn(message);
1017
1229
  }
1018
1230
  }
1231
+ function normalizeEffort2(level) {
1232
+ if (level === "minimal") {
1233
+ return "low";
1234
+ }
1235
+ return level;
1236
+ }
1019
1237
  function resolveReasoningEffort2() {
1020
1238
  const customReasoningEffort = getArgValue2("--reasoning-effort");
1021
1239
  if (customReasoningEffort) {
1022
1240
  if (ALLOWED_REASONING_EFFORTS2.includes(customReasoningEffort)) {
1023
- trace3("reasoning:custom", customReasoningEffort);
1024
- return customReasoningEffort;
1241
+ const normalized = normalizeEffort2(customReasoningEffort);
1242
+ trace3("reasoning:custom", `${customReasoningEffort} -> ${normalized}`);
1243
+ if (customReasoningEffort === "minimal") {
1244
+ printNotice2("\u26A0\uFE0F Codex\uB294 minimal\uC774 web_search \uB3C4\uAD6C\uC640 \uCDA9\uB3CC\uD560 \uC218 \uC788\uC5B4 low\uB85C \uB9E4\uD551\uD569\uB2C8\uB2E4.");
1245
+ }
1246
+ return normalized;
1025
1247
  }
1026
1248
  printNotice2(
1027
1249
  `\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS2.join(
@@ -1030,8 +1252,8 @@ function resolveReasoningEffort2() {
1030
1252
  );
1031
1253
  }
1032
1254
  if (args2.includes("--flash")) {
1033
- trace3("reasoning:flash-default", "minimal");
1034
- return "minimal";
1255
+ trace3("reasoning:flash-default", "low");
1256
+ return "low";
1035
1257
  }
1036
1258
  if (args2.includes("--review")) {
1037
1259
  trace3("reasoning:review-default", "high");
@@ -1226,7 +1448,7 @@ function buildGeminiFileReferenceSection(files) {
1226
1448
  const existingFiles = files.filter((file) => fs.existsSync(file.path));
1227
1449
  return {
1228
1450
  count: existingFiles.length,
1229
- lines: existingFiles.map((file) => `- ${file.display}: ${toGeminiFileReference(file.path)}`)
1451
+ items: existingFiles.map((file) => `${file.display} ${toGeminiFileReference(file.path)}`)
1230
1452
  };
1231
1453
  }
1232
1454
  var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
@@ -1245,19 +1467,19 @@ var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
1245
1467
  const ruleSection = buildGeminiFileReferenceSection(rules);
1246
1468
  trace5("rules:loaded", `count=${ruleSection.count}`);
1247
1469
  const reviewFormExists = fs.existsSync(resolvedReviewFormPath);
1248
- const reviewFormLine = reviewFormExists ? `- \uB9AC\uBDF0 \uC591\uC2DD: ${toGeminiFileReference(resolvedReviewFormPath)}` : "- \uB9AC\uBDF0 \uC591\uC2DD: (\uC5C6\uC74C)";
1470
+ const reviewFormText = reviewFormExists ? `\uB9AC\uBDF0 \uC591\uC2DD ${toGeminiFileReference(resolvedReviewFormPath)}` : "\uB9AC\uBDF0 \uC591\uC2DD (\uC5C6\uC74C)";
1249
1471
  trace5("reviewForm:status", reviewFormExists ? "exists" : "missing");
1250
1472
  const reasoningInstruction = getReasoningInstruction(reasoningEffort);
1251
- const prompt = `\uC544\uB798 \uD30C\uC77C\uB4E4\uC744 \uC21C\uC11C\uB300\uB85C \uC77D\uACE0 \uCF54\uB4DC \uB9AC\uBDF0\uB97C \uC9C4\uD589\uD574\uC918.
1252
- \uADDC\uCE59 \uD30C\uC77C:
1253
- ${ruleSection.lines.join("\n") || "- (\uC5C6\uC74C)"}
1254
- \uB9AC\uBDF0 \uC591\uC2DD \uD30C\uC77C:
1255
- ${reviewFormLine}
1256
- \uB9AC\uBDF0 \uB300\uC0C1 diff \uD30C\uC77C:
1257
- - \uB9AC\uBDF0 \uB300\uC0C1 diff: ${toGeminiFileReference(resolvedTempDiffPath)}
1258
-
1259
- \uBC18\uB4DC\uC2DC \uB9AC\uBDF0 \uC591\uC2DD\uC5D0 \uB9DE\uCDB0 \uC791\uC131\uD574\uC918.
1260
- \uCD94\uB860 \uAC15\uB3C4 \uC9C0\uCE68: ${reasoningInstruction}`;
1473
+ const ruleText = ruleSection.items.join(" ") || "(\uC5C6\uC74C)";
1474
+ const diffText = `\uB9AC\uBDF0 \uB300\uC0C1 diff ${toGeminiFileReference(resolvedTempDiffPath)}`;
1475
+ const prompt = [
1476
+ "\uC544\uB798 \uD30C\uC77C\uB4E4\uC744 \uC21C\uC11C\uB300\uB85C \uC77D\uACE0 \uCF54\uB4DC \uB9AC\uBDF0\uB97C \uC9C4\uD589\uD574\uC918.",
1477
+ `\uADDC\uCE59 \uD30C\uC77C: ${ruleText}`,
1478
+ `\uB9AC\uBDF0 \uC591\uC2DD \uD30C\uC77C: ${reviewFormText}`,
1479
+ `\uB9AC\uBDF0 \uB300\uC0C1 diff \uD30C\uC77C: ${diffText}`,
1480
+ "\uBC18\uB4DC\uC2DC \uB9AC\uBDF0 \uC591\uC2DD\uC5D0 \uB9DE\uCDB0 \uC791\uC131\uD574\uC918.",
1481
+ `\uCD94\uB860 \uAC15\uB3C4 \uC9C0\uCE68: ${reasoningInstruction}`
1482
+ ].join(" ");
1261
1483
  trace5("prompt:prepared", `length=${prompt.length}`);
1262
1484
  const modelCandidates = toUnique2(customModel ? [customModel, ...aliasFallbacks] : aliasFallbacks);
1263
1485
  trace5("model:candidates", modelCandidates.join(", "));
@@ -1320,8 +1542,10 @@ function checkGeminiCliInstalled() {
1320
1542
  // src/pr-review/review.ts
1321
1543
  async function main() {
1322
1544
  const args4 = process.argv.slice(2);
1545
+ const startedAt = /* @__PURE__ */ new Date();
1323
1546
  clearTraceMessages();
1324
1547
  const isTest = isTestMode(args4);
1548
+ const shouldStreamOutput = shouldStreamAIOutput(args4);
1325
1549
  const trace7 = createTraceLogger("review", args4);
1326
1550
  trace7("main:start", `args=${JSON.stringify(args4)}`);
1327
1551
  let command = "";
@@ -1330,6 +1554,12 @@ async function main() {
1330
1554
  let service = "";
1331
1555
  let selectedCommitSummary = "";
1332
1556
  let reviewTargetFiles = [];
1557
+ let executionLogPath = "";
1558
+ let executionStatus = "cancelled";
1559
+ let executionTitle = "\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.";
1560
+ let executionError = null;
1561
+ let resultLength = 0;
1562
+ let exitCode = 0;
1333
1563
  try {
1334
1564
  trace7("service-selection:start");
1335
1565
  service = await showSelectionAIService();
@@ -1353,14 +1583,18 @@ async function main() {
1353
1583
  }
1354
1584
  trace7("review-flow:start");
1355
1585
  console.log("\u{1F680} AI Code Review\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...");
1586
+ if (shouldStreamOutput) {
1587
+ console.log("\u2139\uFE0F AI \uC2E4\uC2DC\uAC04 \uC751\uB2F5 \uD45C\uC2DC \uBAA8\uB4DC\uAC00 \uD65C\uC131\uD654\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
1588
+ }
1356
1589
  trace7("commit-selection:start");
1357
1590
  const selectedCommits = await selectReviewCommits();
1358
1591
  trace7("commit-selection:done", `count=${selectedCommits.length}`);
1359
1592
  if (selectedCommits.length === 0) {
1360
1593
  trace7("commit-selection:empty");
1361
- console.log("\u2139\uFE0F \uC120\uD0DD\uB41C \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
1594
+ executionTitle = "\uC120\uD0DD\uB41C \uB9AC\uBDF0 \uB300\uC0C1\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.";
1595
+ console.log("\u2139\uFE0F \uC120\uD0DD\uB41C \uB9AC\uBDF0 \uB300\uC0C1\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
1362
1596
  deleteTempDiff();
1363
- process.exit(0);
1597
+ return;
1364
1598
  }
1365
1599
  selectedCommitSummary = buildSelectedCommitSummary(selectedCommits);
1366
1600
  trace7("commit-summary:prepared", selectedCommitSummary);
@@ -1372,9 +1606,10 @@ async function main() {
1372
1606
  trace7("git-diff:build:done", `length=${diff.length}`);
1373
1607
  if (!diff.trim() && !isTest) {
1374
1608
  trace7("empty-diff:exit");
1375
- 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.");
1609
+ executionTitle = "\uC120\uD0DD\uD55C \uB9AC\uBDF0 \uB300\uC0C1\uC5D0\uC11C \uB9AC\uBDF0\uD560 \uBCC0\uACBD\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.";
1610
+ console.log("\u2139\uFE0F \uC120\uD0DD\uD55C \uB9AC\uBDF0 \uB300\uC0C1\uC5D0\uC11C \uB9AC\uBDF0\uD560 \uBCC0\uACBD\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
1376
1611
  deleteTempDiff();
1377
- process.exit(0);
1612
+ return;
1378
1613
  }
1379
1614
  const nowStr = getNowString();
1380
1615
  trace7("timestamp:created", nowStr);
@@ -1404,8 +1639,9 @@ async function main() {
1404
1639
  trace7("command:exec:start");
1405
1640
  const result = (await executeShellCommandWithProgress(command, {
1406
1641
  progressMessage: `\u23F3 [\uB9AC\uBDF0 \uC9C4\uD589] ${service} | \uB300\uC0C1 ${reviewTargetFiles.length}\uAC1C \uD30C\uC77C`,
1407
- streamOutput: isTest
1642
+ streamOutput: isTest || shouldStreamOutput
1408
1643
  })).stdout;
1644
+ resultLength = result.length;
1409
1645
  trace7("command:exec:done", `resultLength=${result.length}`);
1410
1646
  trace7("report:write:start");
1411
1647
  savedReportPath = getNextFilePath(REPORT_DIR, nowStr, ".md");
@@ -1431,9 +1667,15 @@ ${command}`);
1431
1667
  deleteTempDiff();
1432
1668
  trace7("cleanup-temp-diff:done");
1433
1669
  trace7("review-flow:end");
1670
+ executionStatus = "success";
1671
+ executionTitle = "\uB9AC\uBDF0\uAC00 \uC131\uACF5\uC801\uC73C\uB85C \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.";
1434
1672
  } catch (error) {
1435
1673
  trace7("review-flow:catch", getErrorSummary(error));
1436
1674
  let errorReportPath = "";
1675
+ executionStatus = "failed";
1676
+ executionTitle = "\uB9AC\uBDF0 \uC2E4\uD589 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
1677
+ executionError = error;
1678
+ exitCode = 1;
1437
1679
  trace7("cleanup-temp-diff:start(catch)");
1438
1680
  try {
1439
1681
  deleteTempDiff();
@@ -1473,7 +1715,50 @@ ${JSON.stringify(
1473
1715
  if (errorReportPath) {
1474
1716
  console.error(`\u{1F4C4} \uC5D0\uB7EC \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${errorReportPath}`);
1475
1717
  }
1476
- process.exit(1);
1718
+ } finally {
1719
+ executionLogPath = writeExecutionLog({
1720
+ scope: "review",
1721
+ status: executionStatus,
1722
+ title: executionTitle,
1723
+ args: args4,
1724
+ startedAt,
1725
+ error: executionError,
1726
+ extraSections: [
1727
+ {
1728
+ heading: "Execution Context",
1729
+ markdown: `\`\`\`json
1730
+ ${JSON.stringify(
1731
+ {
1732
+ service: service || null,
1733
+ selectedCommitSummary: selectedCommitSummary || null,
1734
+ reviewTargetFiles,
1735
+ command: command || null,
1736
+ tempDiffPath,
1737
+ savedDiffPath: savedDiffPath || null,
1738
+ savedReportPath: savedReportPath || null,
1739
+ shouldStreamOutput,
1740
+ resultLength
1741
+ },
1742
+ null,
1743
+ 2
1744
+ )}
1745
+ \`\`\``
1746
+ },
1747
+ {
1748
+ heading: "Generated Command",
1749
+ markdown: command ? `\`\`\`sh
1750
+ ${command}
1751
+ \`\`\`` : "(\uC5C6\uC74C)"
1752
+ }
1753
+ ]
1754
+ });
1755
+ if (executionLogPath) {
1756
+ const writeLog = executionStatus === "failed" ? console.error : console.log;
1757
+ writeLog(`\u{1F4DD} \uC2E4\uD589 \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${executionLogPath}`);
1758
+ }
1759
+ }
1760
+ if (exitCode !== 0) {
1761
+ process.exit(exitCode);
1477
1762
  }
1478
1763
  }
1479
1764
  main();