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
@@ -80,6 +80,9 @@ var ignoreList = [
80
80
  function isTestMode(args4 = process.argv.slice(2)) {
81
81
  return args4.includes("--test");
82
82
  }
83
+ function shouldStreamAIOutput(args4 = process.argv.slice(2)) {
84
+ return args4.includes("--stream-output");
85
+ }
83
86
  function clearTraceMessages() {
84
87
  traceMessages.length = 0;
85
88
  }
@@ -206,15 +209,51 @@ function runGitCommand(args4, options = {}) {
206
209
  throw error;
207
210
  }
208
211
  }
212
+ function resolveShellLaunchConfig() {
213
+ const candidates = [];
214
+ const seen = /* @__PURE__ */ new Set();
215
+ const appendCandidate = (executable, shellArgs) => {
216
+ const normalizedExecutable = executable?.trim();
217
+ if (!normalizedExecutable) {
218
+ return;
219
+ }
220
+ if (path__default.default.isAbsolute(normalizedExecutable) && !fs__default.default.existsSync(normalizedExecutable)) {
221
+ return;
222
+ }
223
+ const key = `${normalizedExecutable}::${shellArgs.join("\0")}`;
224
+ if (seen.has(key)) {
225
+ return;
226
+ }
227
+ seen.add(key);
228
+ candidates.push({
229
+ executable: normalizedExecutable,
230
+ shellArgs
231
+ });
232
+ };
233
+ const shellFromEnv = process.env.SHELL?.trim();
234
+ const envShellName = shellFromEnv ? path__default.default.basename(shellFromEnv) : "";
235
+ if (["bash", "zsh", "ksh"].includes(envShellName)) {
236
+ appendCandidate(shellFromEnv, ["-lc"]);
237
+ }
238
+ ["/bin/bash", "/usr/bin/bash", "bash", "/bin/zsh", "/usr/bin/zsh", "zsh"].forEach((shellPath) => {
239
+ appendCandidate(shellPath, ["-lc"]);
240
+ });
241
+ ["/bin/sh", "/usr/bin/sh", "sh"].forEach((shellPath) => {
242
+ appendCandidate(shellPath, ["-c"]);
243
+ });
244
+ return candidates[0] || { executable: "sh", shellArgs: ["-c"] };
245
+ }
209
246
  async function executeShellCommandWithProgress(command, options = {}) {
210
247
  const { progressIntervalMs = 1e4, progressMessage = "\u23F3 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uB294 \uC911\uC785\uB2C8\uB2E4...", streamOutput = false } = options;
211
248
  const { spawn } = await import('child_process');
249
+ const shellLaunchConfig = resolveShellLaunchConfig();
212
250
  return new Promise((resolve, reject) => {
213
251
  let stdout = "";
214
252
  let stderr = "";
215
253
  const startedAt = Date.now();
216
254
  console.log(progressMessage);
217
- const child = spawn("/bin/zsh", ["-lc", command], {
255
+ helperTrace("shell-command:launcher", `${shellLaunchConfig.executable} ${shellLaunchConfig.shellArgs.join(" ")}`);
256
+ const child = spawn(shellLaunchConfig.executable, [...shellLaunchConfig.shellArgs, command], {
218
257
  stdio: ["ignore", "pipe", "pipe"]
219
258
  });
220
259
  const progressTimer = setInterval(() => {
@@ -249,11 +288,41 @@ async function executeShellCommandWithProgress(command, options = {}) {
249
288
  return;
250
289
  }
251
290
  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()}` : ""}`));
291
+ const failureDetails = {
292
+ code,
293
+ command,
294
+ signal,
295
+ stderr,
296
+ stdout
297
+ };
298
+ reject(createShellCommandExecutionError(failureDetails, exitSummary));
254
299
  });
255
300
  });
256
301
  }
302
+ function getShellCommandFailurePreview(failureDetails) {
303
+ const stderrText = failureDetails.stderr.trim();
304
+ const stdoutText = failureDetails.stdout.trim();
305
+ const combinedOutput = stderrText || stdoutText;
306
+ if (!combinedOutput) {
307
+ return "";
308
+ }
309
+ const MAX_PREVIEW_LENGTH = 4e3;
310
+ if (combinedOutput.length <= MAX_PREVIEW_LENGTH) {
311
+ return combinedOutput;
312
+ }
313
+ return combinedOutput.slice(-4e3);
314
+ }
315
+ function createShellCommandExecutionError(failureDetails, exitSummary) {
316
+ const failurePreview = getShellCommandFailurePreview(failureDetails);
317
+ const error = new Error(`\uC258 \uBA85\uB839 \uC2E4\uD589 \uC2E4\uD328 (${exitSummary})${failurePreview ? `
318
+ ${failurePreview}` : ""}`);
319
+ error.code = failureDetails.code;
320
+ error.signal = failureDetails.signal;
321
+ error.stdout = failureDetails.stdout;
322
+ error.stderr = failureDetails.stderr;
323
+ error.command = failureDetails.command;
324
+ return error;
325
+ }
257
326
  function formatReviewTargetFiles(files, visibleCount = 5) {
258
327
  if (files.length === 0) {
259
328
  return "(\uC5C6\uC74C)";
@@ -326,7 +395,7 @@ function serializeError(error) {
326
395
  }
327
396
  if (error && typeof error === "object") {
328
397
  const errorLike = error;
329
- const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs"];
398
+ const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs", "command"];
330
399
  extraKeys.forEach((key) => {
331
400
  if (errorLike[key] !== void 0) {
332
401
  serialized[key] = errorLike[key];
@@ -432,6 +501,87 @@ ${section.markdown}`).join("\n")}
432
501
  return "";
433
502
  }
434
503
  }
504
+ function getExecutionLogSummary(status, title) {
505
+ if (title) {
506
+ return title;
507
+ }
508
+ switch (status) {
509
+ case "success":
510
+ return "\uB9AC\uBDF0 \uC2E4\uD589\uC774 \uC131\uACF5\uC801\uC73C\uB85C \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.";
511
+ case "failed":
512
+ return "\uB9AC\uBDF0 \uC2E4\uD589 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
513
+ case "partial_failure":
514
+ 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.";
515
+ default:
516
+ 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.";
517
+ }
518
+ }
519
+ function formatExecutionDuration(startedAt, finishedAt) {
520
+ const durationMs = Math.max(0, finishedAt.getTime() - startedAt.getTime());
521
+ if (durationMs < 1e3) {
522
+ return `${durationMs}ms`;
523
+ }
524
+ const durationSeconds = durationMs / 1e3;
525
+ if (durationSeconds < 60) {
526
+ return `${durationSeconds.toFixed(1)}s`;
527
+ }
528
+ const minutes = Math.floor(durationSeconds / 60);
529
+ const seconds = Math.round(durationSeconds % 60);
530
+ return `${minutes}m ${seconds}s`;
531
+ }
532
+ function writeExecutionLog(options = {}) {
533
+ try {
534
+ const startedAt = options.startedAt ?? /* @__PURE__ */ new Date();
535
+ const finishedAt = options.finishedAt ?? /* @__PURE__ */ new Date();
536
+ const status = options.status ?? "success";
537
+ helperTrace("execution-log:write:start", options.scope || "unknown");
538
+ createReportDirectory();
539
+ const reportPath = getAvailableFilePath(REPORT_DIR, `${getNowString(finishedAt)}-execution-log`, ".md");
540
+ const traceSnapshot = options.traceMessages ?? getTraceMessages();
541
+ const extraSections = options.extraSections || [];
542
+ const serializedError = options.error ? serializeError(options.error) : null;
543
+ const report = `# Execution Log
544
+
545
+ - \uC2DC\uC791 \uC2DC\uAC01: ${getHumanReadableNowString(startedAt)}
546
+ - \uC885\uB8CC \uC2DC\uAC01: ${getHumanReadableNowString(finishedAt)}
547
+ - \uC2E4\uD589 \uC2DC\uAC04: ${formatExecutionDuration(startedAt, finishedAt)}
548
+ - \uC0C1\uD0DC: \`${status}\`
549
+ - Scope: \`${options.scope || "unknown"}\`
550
+ - \uC791\uC5C5 \uACBD\uB85C: \`${process.cwd()}\`
551
+ - \uC2E4\uD589 \uC778\uC790: \`${JSON.stringify(options.args ?? process.argv.slice(2))}\`
552
+ - \uC2E4\uD589 \uD658\uACBD: \`${process.platform} ${process.arch} / Node ${process.version}\`
553
+
554
+ ## Summary
555
+
556
+ ${getExecutionLogSummary(status, options.title)}
557
+ ${serializedError ? `
558
+
559
+ ## Error
560
+
561
+ \`\`\`json
562
+ ${JSON.stringify(serializedError, null, 2)}
563
+ \`\`\`` : ""}
564
+
565
+ ## Trace
566
+
567
+ \`\`\`json
568
+ ${JSON.stringify(traceSnapshot, null, 2)}
569
+ \`\`\`${extraSections.length ? `
570
+ ${extraSections.map((section) => `
571
+ ## ${section.heading}
572
+
573
+ ${section.markdown}`).join("\n")}
574
+ ` : "\n"}
575
+ `;
576
+ fs__default.default.writeFileSync(reportPath, report);
577
+ helperTrace("execution-log:write:done", reportPath);
578
+ return reportPath;
579
+ } catch (writeError) {
580
+ console.error("\u26A0\uFE0F \uC2E4\uD589 \uB85C\uADF8 \uD30C\uC77C \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
581
+ console.error(writeError);
582
+ return "";
583
+ }
584
+ }
435
585
  function exitWithError(message, options = {}) {
436
586
  const reportPath = writeErrorReport(options.error || new Error(message), {
437
587
  ...options,
@@ -482,6 +632,62 @@ function truncateCommitSubject(subject) {
482
632
  }
483
633
  return `${subject.slice(0, 69)}...`;
484
634
  }
635
+ function formatReviewTargetFileCount(fileCount) {
636
+ return fileCount === 0 ? "\uBCC0\uACBD \uC5C6\uC74C" : `${fileCount}\uAC1C \uD30C\uC77C`;
637
+ }
638
+ function buildReviewTargetSectionTitle(target) {
639
+ return target.kind === "commit" ? `${target.hash} ${target.subject}` : `${target.hash} | ${target.subject}`;
640
+ }
641
+ function buildReviewTargetSummaryLine(target) {
642
+ if (target.kind === "commit") {
643
+ return `- ${target.hash} | ${target.subject} | ${target.author} | ${target.relativeDate}`;
644
+ }
645
+ return `- ${target.hash} | ${target.subject}`;
646
+ }
647
+ function buildReviewTargetDiffArgs(target, filePath) {
648
+ const reviewPathspecArgs = getReviewPathspecArgs();
649
+ if (target.kind === "commit") {
650
+ return ["show", "--stat", "--patch", "--format=", target.hash, "--", ...reviewPathspecArgs];
651
+ }
652
+ const diffArgs = ["diff"];
653
+ if (target.kind === "staged") {
654
+ diffArgs.push("--cached");
655
+ }
656
+ diffArgs.push("--stat", "--patch", "--");
657
+ return [...diffArgs, ...reviewPathspecArgs];
658
+ }
659
+ function buildReviewTargetFileArgs(target) {
660
+ const reviewPathspecArgs = getReviewPathspecArgs();
661
+ if (target.kind === "commit") {
662
+ return ["show", "--pretty=format:", "--name-only", target.hash, "--", ...reviewPathspecArgs];
663
+ }
664
+ const diffArgs = ["diff"];
665
+ if (target.kind === "staged") {
666
+ diffArgs.push("--cached");
667
+ }
668
+ return [...diffArgs, "--name-only", "--", ...reviewPathspecArgs];
669
+ }
670
+ function getReviewTargetFiles(target) {
671
+ const output = runGitCommand(buildReviewTargetFileArgs(target), {
672
+ allowFailure: true
673
+ });
674
+ return output.split("\n").map((line) => line.trim()).filter(Boolean);
675
+ }
676
+ function createWorkingTreeReviewOption(kind) {
677
+ const files = getReviewTargetFiles({
678
+ hash: kind,
679
+ kind});
680
+ const subject = kind === "unstaged" ? "\uC544\uC9C1 git add \uD558\uC9C0 \uC54A\uC740 \uBCC0\uACBD\uC0AC\uD56D" : "git add \uB41C \uBCC0\uACBD\uC0AC\uD56D";
681
+ return {
682
+ author: "",
683
+ description: `${subject} | ${formatReviewTargetFileCount(files.length)}`,
684
+ hash: kind,
685
+ kind,
686
+ label: kind,
687
+ relativeDate: "",
688
+ subject
689
+ };
690
+ }
485
691
  function getRecentCommitOptions() {
486
692
  const output = runGitCommand(
487
693
  ["log", `-${COMMIT_FETCH_LIMIT}`, "--date=relative", "--pretty=format:%h%x09%an%x09%ar%x09%s"],
@@ -497,44 +703,43 @@ function getRecentCommitOptions() {
497
703
  author,
498
704
  description: `${author} | ${relativeDate}`,
499
705
  hash,
706
+ kind: "commit",
500
707
  label: `${hash} | ${truncateCommitSubject(subject)}`,
501
708
  relativeDate,
502
709
  subject
503
710
  };
504
711
  });
505
712
  }
713
+ function getReviewTargetOptions() {
714
+ return [createWorkingTreeReviewOption("unstaged"), createWorkingTreeReviewOption("staged"), ...getRecentCommitOptions()];
715
+ }
506
716
  function buildSelectedCommitSummary(commits) {
507
- return commits.map((commit) => `- ${commit.hash} | ${commit.subject} | ${commit.author} | ${commit.relativeDate}`).join("\n");
717
+ return commits.map((commit) => buildReviewTargetSummaryLine(commit)).join("\n");
508
718
  }
509
719
  function getReviewPathspecArgs() {
510
720
  const { includePatterns, excludePatterns } = getGitDiffPathspecs();
511
721
  return [...includePatterns, ...excludePatterns];
512
722
  }
513
723
  function buildSelectedCommitDiff(commits) {
514
- const reviewPathspecArgs = getReviewPathspecArgs();
515
724
  const sections = commits.map((commit) => {
516
- const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", ...reviewPathspecArgs], {
725
+ const diff = runGitCommand(buildReviewTargetDiffArgs(commit), {
517
726
  allowFailure: true,
518
727
  trimOutput: false
519
728
  }).trim();
520
729
  if (!diff) {
521
730
  return "";
522
731
  }
523
- return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
732
+ return [`## ${buildReviewTargetSectionTitle(commit)}`, diff].join("\n\n");
524
733
  }).filter(Boolean).join("\n\n");
525
734
  if (!sections) {
526
735
  return "";
527
736
  }
528
- return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", "# \uB9AC\uBDF0 \uB300\uC0C1 diff", sections].join("\n");
737
+ return ["# \uC120\uD0DD\uD55C \uB9AC\uBDF0 \uB300\uC0C1", buildSelectedCommitSummary(commits), "", "# \uB9AC\uBDF0 \uB300\uC0C1 diff", sections].join("\n");
529
738
  }
530
739
  function getSelectedCommitFiles(commits) {
531
- const reviewPathspecArgs = getReviewPathspecArgs();
532
740
  const files = /* @__PURE__ */ new Set();
533
741
  commits.forEach((commit) => {
534
- const output = runGitCommand(["show", "--pretty=format:", "--name-only", commit.hash, "--", ...reviewPathspecArgs], {
535
- allowFailure: true
536
- });
537
- output.split("\n").map((line) => line.trim()).filter(Boolean).forEach((filePath) => files.add(filePath));
742
+ getReviewTargetFiles(commit).forEach((filePath) => files.add(filePath));
538
743
  });
539
744
  return [...files];
540
745
  }
@@ -722,13 +927,13 @@ async function showMultiSelect(question, options, windowSize = COMMIT_SELECTION_
722
927
  });
723
928
  }
724
929
  async function selectReviewCommits() {
725
- const commits = getRecentCommitOptions();
930
+ const commits = getReviewTargetOptions();
726
931
  if (commits.length === 0) {
727
- console.log("\u2139\uFE0F \uB9AC\uBDF0\uD560 \uCD5C\uADFC \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
932
+ console.log("\u2139\uFE0F \uB9AC\uBDF0\uD560 \uB300\uC0C1\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
728
933
  return [];
729
934
  }
730
935
  return showMultiSelect(
731
- "\uB9AC\uBDF0\uD560 \uCEE4\uBC0B\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.",
936
+ "\uB9AC\uBDF0\uD560 \uB300\uC0C1\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.",
732
937
  commits.map((commit) => ({
733
938
  description: commit.description,
734
939
  label: commit.label,
@@ -819,6 +1024,13 @@ var ALLOWED_REASONING_EFFORTS = ["minimal", "low", "medium", "high"];
819
1024
  function shellQuote(value) {
820
1025
  return `'${value.replace(/'/g, `'\\''`)}'`;
821
1026
  }
1027
+ function toShellOptionToken(value) {
1028
+ const SIMPLE_SHELL_TOKEN_PATTERN = /^[A-Za-z0-9._:/=-]+$/;
1029
+ if (SIMPLE_SHELL_TOKEN_PATTERN.test(value)) {
1030
+ return value;
1031
+ }
1032
+ return shellQuote(value);
1033
+ }
822
1034
  function getArgValue(flag) {
823
1035
  const index = args.indexOf(flag);
824
1036
  if (index === -1 || !args[index + 1]) {
@@ -898,9 +1110,9 @@ function getAliasFallbacks(primaryAlias) {
898
1110
  }
899
1111
  function buildClaudeExecCommand(options) {
900
1112
  const { tempDiffPath: tempDiffPath2, prompt, systemPromptFiles, effort, model, fallbackModel } = options;
901
- const modelOption = model ? `--model ${shellQuote(model)}` : "";
902
- const fallbackOption = model && fallbackModel ? `--fallback-model ${shellQuote(fallbackModel)}` : "";
903
- const effortOption = `--effort ${shellQuote(effort)}`;
1113
+ const modelOption = model ? `--model ${toShellOptionToken(model)}` : "";
1114
+ const fallbackOption = model && fallbackModel ? `--fallback-model ${toShellOptionToken(fallbackModel)}` : "";
1115
+ const effortOption = `--effort ${toShellOptionToken(effort)}`;
904
1116
  const appendedPromptFiles = systemPromptFiles.map((path3) => `--append-system-prompt-file ${shellQuote(path3)}`).join(" ");
905
1117
  return `cat ${shellQuote(tempDiffPath2)} | claude ${[
906
1118
  modelOption,
@@ -1025,12 +1237,22 @@ function printNotice2(message) {
1025
1237
  console.warn(message);
1026
1238
  }
1027
1239
  }
1240
+ function normalizeEffort2(level) {
1241
+ if (level === "minimal") {
1242
+ return "low";
1243
+ }
1244
+ return level;
1245
+ }
1028
1246
  function resolveReasoningEffort2() {
1029
1247
  const customReasoningEffort = getArgValue2("--reasoning-effort");
1030
1248
  if (customReasoningEffort) {
1031
1249
  if (ALLOWED_REASONING_EFFORTS2.includes(customReasoningEffort)) {
1032
- trace3("reasoning:custom", customReasoningEffort);
1033
- return customReasoningEffort;
1250
+ const normalized = normalizeEffort2(customReasoningEffort);
1251
+ trace3("reasoning:custom", `${customReasoningEffort} -> ${normalized}`);
1252
+ if (customReasoningEffort === "minimal") {
1253
+ 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.");
1254
+ }
1255
+ return normalized;
1034
1256
  }
1035
1257
  printNotice2(
1036
1258
  `\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS2.join(
@@ -1039,8 +1261,8 @@ function resolveReasoningEffort2() {
1039
1261
  );
1040
1262
  }
1041
1263
  if (args2.includes("--flash")) {
1042
- trace3("reasoning:flash-default", "minimal");
1043
- return "minimal";
1264
+ trace3("reasoning:flash-default", "low");
1265
+ return "low";
1044
1266
  }
1045
1267
  if (args2.includes("--review")) {
1046
1268
  trace3("reasoning:review-default", "high");
@@ -1235,7 +1457,7 @@ function buildGeminiFileReferenceSection(files) {
1235
1457
  const existingFiles = files.filter((file) => fs__default.default.existsSync(file.path));
1236
1458
  return {
1237
1459
  count: existingFiles.length,
1238
- lines: existingFiles.map((file) => `- ${file.display}: ${toGeminiFileReference(file.path)}`)
1460
+ items: existingFiles.map((file) => `${file.display} ${toGeminiFileReference(file.path)}`)
1239
1461
  };
1240
1462
  }
1241
1463
  var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
@@ -1254,19 +1476,19 @@ var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
1254
1476
  const ruleSection = buildGeminiFileReferenceSection(rules);
1255
1477
  trace5("rules:loaded", `count=${ruleSection.count}`);
1256
1478
  const reviewFormExists = fs__default.default.existsSync(resolvedReviewFormPath);
1257
- const reviewFormLine = reviewFormExists ? `- \uB9AC\uBDF0 \uC591\uC2DD: ${toGeminiFileReference(resolvedReviewFormPath)}` : "- \uB9AC\uBDF0 \uC591\uC2DD: (\uC5C6\uC74C)";
1479
+ const reviewFormText = reviewFormExists ? `\uB9AC\uBDF0 \uC591\uC2DD ${toGeminiFileReference(resolvedReviewFormPath)}` : "\uB9AC\uBDF0 \uC591\uC2DD (\uC5C6\uC74C)";
1258
1480
  trace5("reviewForm:status", reviewFormExists ? "exists" : "missing");
1259
1481
  const reasoningInstruction = getReasoningInstruction(reasoningEffort);
1260
- const prompt = `\uC544\uB798 \uD30C\uC77C\uB4E4\uC744 \uC21C\uC11C\uB300\uB85C \uC77D\uACE0 \uCF54\uB4DC \uB9AC\uBDF0\uB97C \uC9C4\uD589\uD574\uC918.
1261
- \uADDC\uCE59 \uD30C\uC77C:
1262
- ${ruleSection.lines.join("\n") || "- (\uC5C6\uC74C)"}
1263
- \uB9AC\uBDF0 \uC591\uC2DD \uD30C\uC77C:
1264
- ${reviewFormLine}
1265
- \uB9AC\uBDF0 \uB300\uC0C1 diff \uD30C\uC77C:
1266
- - \uB9AC\uBDF0 \uB300\uC0C1 diff: ${toGeminiFileReference(resolvedTempDiffPath)}
1267
-
1268
- \uBC18\uB4DC\uC2DC \uB9AC\uBDF0 \uC591\uC2DD\uC5D0 \uB9DE\uCDB0 \uC791\uC131\uD574\uC918.
1269
- \uCD94\uB860 \uAC15\uB3C4 \uC9C0\uCE68: ${reasoningInstruction}`;
1482
+ const ruleText = ruleSection.items.join(" ") || "(\uC5C6\uC74C)";
1483
+ const diffText = `\uB9AC\uBDF0 \uB300\uC0C1 diff ${toGeminiFileReference(resolvedTempDiffPath)}`;
1484
+ const prompt = [
1485
+ "\uC544\uB798 \uD30C\uC77C\uB4E4\uC744 \uC21C\uC11C\uB300\uB85C \uC77D\uACE0 \uCF54\uB4DC \uB9AC\uBDF0\uB97C \uC9C4\uD589\uD574\uC918.",
1486
+ `\uADDC\uCE59 \uD30C\uC77C: ${ruleText}`,
1487
+ `\uB9AC\uBDF0 \uC591\uC2DD \uD30C\uC77C: ${reviewFormText}`,
1488
+ `\uB9AC\uBDF0 \uB300\uC0C1 diff \uD30C\uC77C: ${diffText}`,
1489
+ "\uBC18\uB4DC\uC2DC \uB9AC\uBDF0 \uC591\uC2DD\uC5D0 \uB9DE\uCDB0 \uC791\uC131\uD574\uC918.",
1490
+ `\uCD94\uB860 \uAC15\uB3C4 \uC9C0\uCE68: ${reasoningInstruction}`
1491
+ ].join(" ");
1270
1492
  trace5("prompt:prepared", `length=${prompt.length}`);
1271
1493
  const modelCandidates = toUnique2(customModel ? [customModel, ...aliasFallbacks] : aliasFallbacks);
1272
1494
  trace5("model:candidates", modelCandidates.join(", "));
@@ -1329,8 +1551,10 @@ function checkGeminiCliInstalled() {
1329
1551
  // src/pr-review/review.ts
1330
1552
  async function main() {
1331
1553
  const args4 = process.argv.slice(2);
1554
+ const startedAt = /* @__PURE__ */ new Date();
1332
1555
  clearTraceMessages();
1333
1556
  const isTest = isTestMode(args4);
1557
+ const shouldStreamOutput = shouldStreamAIOutput(args4);
1334
1558
  const trace7 = createTraceLogger("review", args4);
1335
1559
  trace7("main:start", `args=${JSON.stringify(args4)}`);
1336
1560
  let command = "";
@@ -1339,6 +1563,12 @@ async function main() {
1339
1563
  let service = "";
1340
1564
  let selectedCommitSummary = "";
1341
1565
  let reviewTargetFiles = [];
1566
+ let executionLogPath = "";
1567
+ let executionStatus = "cancelled";
1568
+ 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.";
1569
+ let executionError = null;
1570
+ let resultLength = 0;
1571
+ let exitCode = 0;
1342
1572
  try {
1343
1573
  trace7("service-selection:start");
1344
1574
  service = await showSelectionAIService();
@@ -1362,14 +1592,18 @@ async function main() {
1362
1592
  }
1363
1593
  trace7("review-flow:start");
1364
1594
  console.log("\u{1F680} AI Code Review\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...");
1595
+ if (shouldStreamOutput) {
1596
+ console.log("\u2139\uFE0F AI \uC2E4\uC2DC\uAC04 \uC751\uB2F5 \uD45C\uC2DC \uBAA8\uB4DC\uAC00 \uD65C\uC131\uD654\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
1597
+ }
1365
1598
  trace7("commit-selection:start");
1366
1599
  const selectedCommits = await selectReviewCommits();
1367
1600
  trace7("commit-selection:done", `count=${selectedCommits.length}`);
1368
1601
  if (selectedCommits.length === 0) {
1369
1602
  trace7("commit-selection:empty");
1370
- console.log("\u2139\uFE0F \uC120\uD0DD\uB41C \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
1603
+ executionTitle = "\uC120\uD0DD\uB41C \uB9AC\uBDF0 \uB300\uC0C1\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.";
1604
+ console.log("\u2139\uFE0F \uC120\uD0DD\uB41C \uB9AC\uBDF0 \uB300\uC0C1\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
1371
1605
  deleteTempDiff();
1372
- process.exit(0);
1606
+ return;
1373
1607
  }
1374
1608
  selectedCommitSummary = buildSelectedCommitSummary(selectedCommits);
1375
1609
  trace7("commit-summary:prepared", selectedCommitSummary);
@@ -1381,9 +1615,10 @@ async function main() {
1381
1615
  trace7("git-diff:build:done", `length=${diff.length}`);
1382
1616
  if (!diff.trim() && !isTest) {
1383
1617
  trace7("empty-diff:exit");
1384
- 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.");
1618
+ executionTitle = "\uC120\uD0DD\uD55C \uB9AC\uBDF0 \uB300\uC0C1\uC5D0\uC11C \uB9AC\uBDF0\uD560 \uBCC0\uACBD\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.";
1619
+ 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.");
1385
1620
  deleteTempDiff();
1386
- process.exit(0);
1621
+ return;
1387
1622
  }
1388
1623
  const nowStr = getNowString();
1389
1624
  trace7("timestamp:created", nowStr);
@@ -1413,8 +1648,9 @@ async function main() {
1413
1648
  trace7("command:exec:start");
1414
1649
  const result = (await executeShellCommandWithProgress(command, {
1415
1650
  progressMessage: `\u23F3 [\uB9AC\uBDF0 \uC9C4\uD589] ${service} | \uB300\uC0C1 ${reviewTargetFiles.length}\uAC1C \uD30C\uC77C`,
1416
- streamOutput: isTest
1651
+ streamOutput: isTest || shouldStreamOutput
1417
1652
  })).stdout;
1653
+ resultLength = result.length;
1418
1654
  trace7("command:exec:done", `resultLength=${result.length}`);
1419
1655
  trace7("report:write:start");
1420
1656
  savedReportPath = getNextFilePath(REPORT_DIR, nowStr, ".md");
@@ -1440,9 +1676,15 @@ ${command}`);
1440
1676
  deleteTempDiff();
1441
1677
  trace7("cleanup-temp-diff:done");
1442
1678
  trace7("review-flow:end");
1679
+ executionStatus = "success";
1680
+ executionTitle = "\uB9AC\uBDF0\uAC00 \uC131\uACF5\uC801\uC73C\uB85C \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.";
1443
1681
  } catch (error) {
1444
1682
  trace7("review-flow:catch", getErrorSummary(error));
1445
1683
  let errorReportPath = "";
1684
+ executionStatus = "failed";
1685
+ executionTitle = "\uB9AC\uBDF0 \uC2E4\uD589 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
1686
+ executionError = error;
1687
+ exitCode = 1;
1446
1688
  trace7("cleanup-temp-diff:start(catch)");
1447
1689
  try {
1448
1690
  deleteTempDiff();
@@ -1482,7 +1724,50 @@ ${JSON.stringify(
1482
1724
  if (errorReportPath) {
1483
1725
  console.error(`\u{1F4C4} \uC5D0\uB7EC \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${errorReportPath}`);
1484
1726
  }
1485
- process.exit(1);
1727
+ } finally {
1728
+ executionLogPath = writeExecutionLog({
1729
+ scope: "review",
1730
+ status: executionStatus,
1731
+ title: executionTitle,
1732
+ args: args4,
1733
+ startedAt,
1734
+ error: executionError,
1735
+ extraSections: [
1736
+ {
1737
+ heading: "Execution Context",
1738
+ markdown: `\`\`\`json
1739
+ ${JSON.stringify(
1740
+ {
1741
+ service: service || null,
1742
+ selectedCommitSummary: selectedCommitSummary || null,
1743
+ reviewTargetFiles,
1744
+ command: command || null,
1745
+ tempDiffPath,
1746
+ savedDiffPath: savedDiffPath || null,
1747
+ savedReportPath: savedReportPath || null,
1748
+ shouldStreamOutput,
1749
+ resultLength
1750
+ },
1751
+ null,
1752
+ 2
1753
+ )}
1754
+ \`\`\``
1755
+ },
1756
+ {
1757
+ heading: "Generated Command",
1758
+ markdown: command ? `\`\`\`sh
1759
+ ${command}
1760
+ \`\`\`` : "(\uC5C6\uC74C)"
1761
+ }
1762
+ ]
1763
+ });
1764
+ if (executionLogPath) {
1765
+ const writeLog = executionStatus === "failed" ? console.error : console.log;
1766
+ writeLog(`\u{1F4DD} \uC2E4\uD589 \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${executionLogPath}`);
1767
+ }
1768
+ }
1769
+ if (exitCode !== 0) {
1770
+ process.exit(exitCode);
1486
1771
  }
1487
1772
  }
1488
1773
  main();