sales-frontend-gemini-cli 0.4.3 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/common/helper.cjs +119 -3
  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 +118 -4
  6. package/dist/common/helper.js.map +1 -1
  7. package/dist/pr-review/claude/claude-commander.cjs +10 -3
  8. package/dist/pr-review/claude/claude-commander.cjs.map +1 -1
  9. package/dist/pr-review/claude/claude-commander.js +10 -3
  10. package/dist/pr-review/claude/claude-commander.js.map +1 -1
  11. package/dist/pr-review/claude/installation-claude.cjs +1 -1
  12. package/dist/pr-review/claude/installation-claude.cjs.map +1 -1
  13. package/dist/pr-review/claude/installation-claude.js +1 -1
  14. package/dist/pr-review/claude/installation-claude.js.map +1 -1
  15. package/dist/pr-review/codex/codex-commander.cjs +14 -4
  16. package/dist/pr-review/codex/codex-commander.cjs.map +1 -1
  17. package/dist/pr-review/codex/codex-commander.d.cts +1 -1
  18. package/dist/pr-review/codex/codex-commander.d.ts +1 -1
  19. package/dist/pr-review/codex/codex-commander.js +14 -4
  20. package/dist/pr-review/codex/codex-commander.js.map +1 -1
  21. package/dist/pr-review/codex/installation-codex.cjs +1 -1
  22. package/dist/pr-review/codex/installation-codex.cjs.map +1 -1
  23. package/dist/pr-review/codex/installation-codex.js +1 -1
  24. package/dist/pr-review/codex/installation-codex.js.map +1 -1
  25. package/dist/pr-review/gemini/gemini-commander.cjs +12 -12
  26. package/dist/pr-review/gemini/gemini-commander.cjs.map +1 -1
  27. package/dist/pr-review/gemini/gemini-commander.js +12 -12
  28. package/dist/pr-review/gemini/gemini-commander.js.map +1 -1
  29. package/dist/pr-review/gemini/installation-gemini.cjs +1 -1
  30. package/dist/pr-review/gemini/installation-gemini.cjs.map +1 -1
  31. package/dist/pr-review/gemini/installation-gemini.js +1 -1
  32. package/dist/pr-review/gemini/installation-gemini.js.map +1 -1
  33. package/dist/pr-review/review-one-by-one.cjs +223 -24
  34. package/dist/pr-review/review-one-by-one.cjs.map +1 -1
  35. package/dist/pr-review/review-one-by-one.js +223 -24
  36. package/dist/pr-review/review-one-by-one.js.map +1 -1
  37. package/dist/pr-review/review.cjs +220 -26
  38. package/dist/pr-review/review.cjs.map +1 -1
  39. package/dist/pr-review/review.js +220 -26
  40. package/dist/pr-review/review.js.map +1 -1
  41. 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
  }
@@ -240,11 +243,41 @@ async function executeShellCommandWithProgress(command, options = {}) {
240
243
  return;
241
244
  }
242
245
  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()}` : ""}`));
246
+ const failureDetails = {
247
+ code,
248
+ command,
249
+ signal,
250
+ stderr,
251
+ stdout
252
+ };
253
+ reject(createShellCommandExecutionError(failureDetails, exitSummary));
245
254
  });
246
255
  });
247
256
  }
257
+ function getShellCommandFailurePreview(failureDetails) {
258
+ const stderrText = failureDetails.stderr.trim();
259
+ const stdoutText = failureDetails.stdout.trim();
260
+ const combinedOutput = stderrText || stdoutText;
261
+ if (!combinedOutput) {
262
+ return "";
263
+ }
264
+ const MAX_PREVIEW_LENGTH = 4e3;
265
+ if (combinedOutput.length <= MAX_PREVIEW_LENGTH) {
266
+ return combinedOutput;
267
+ }
268
+ return combinedOutput.slice(-4e3);
269
+ }
270
+ function createShellCommandExecutionError(failureDetails, exitSummary) {
271
+ const failurePreview = getShellCommandFailurePreview(failureDetails);
272
+ const error = new Error(`\uC258 \uBA85\uB839 \uC2E4\uD589 \uC2E4\uD328 (${exitSummary})${failurePreview ? `
273
+ ${failurePreview}` : ""}`);
274
+ error.code = failureDetails.code;
275
+ error.signal = failureDetails.signal;
276
+ error.stdout = failureDetails.stdout;
277
+ error.stderr = failureDetails.stderr;
278
+ error.command = failureDetails.command;
279
+ return error;
280
+ }
248
281
  function formatReviewTargetFiles(files, visibleCount = 5) {
249
282
  if (files.length === 0) {
250
283
  return "(\uC5C6\uC74C)";
@@ -317,7 +350,7 @@ function serializeError(error) {
317
350
  }
318
351
  if (error && typeof error === "object") {
319
352
  const errorLike = error;
320
- const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs"];
353
+ const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs", "command"];
321
354
  extraKeys.forEach((key) => {
322
355
  if (errorLike[key] !== void 0) {
323
356
  serialized[key] = errorLike[key];
@@ -423,6 +456,87 @@ ${section.markdown}`).join("\n")}
423
456
  return "";
424
457
  }
425
458
  }
459
+ function getExecutionLogSummary(status, title) {
460
+ if (title) {
461
+ return title;
462
+ }
463
+ switch (status) {
464
+ case "success":
465
+ return "\uB9AC\uBDF0 \uC2E4\uD589\uC774 \uC131\uACF5\uC801\uC73C\uB85C \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.";
466
+ case "failed":
467
+ return "\uB9AC\uBDF0 \uC2E4\uD589 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
468
+ case "partial_failure":
469
+ 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.";
470
+ default:
471
+ 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.";
472
+ }
473
+ }
474
+ function formatExecutionDuration(startedAt, finishedAt) {
475
+ const durationMs = Math.max(0, finishedAt.getTime() - startedAt.getTime());
476
+ if (durationMs < 1e3) {
477
+ return `${durationMs}ms`;
478
+ }
479
+ const durationSeconds = durationMs / 1e3;
480
+ if (durationSeconds < 60) {
481
+ return `${durationSeconds.toFixed(1)}s`;
482
+ }
483
+ const minutes = Math.floor(durationSeconds / 60);
484
+ const seconds = Math.round(durationSeconds % 60);
485
+ return `${minutes}m ${seconds}s`;
486
+ }
487
+ function writeExecutionLog(options = {}) {
488
+ try {
489
+ const startedAt = options.startedAt ?? /* @__PURE__ */ new Date();
490
+ const finishedAt = options.finishedAt ?? /* @__PURE__ */ new Date();
491
+ const status = options.status ?? "success";
492
+ helperTrace("execution-log:write:start", options.scope || "unknown");
493
+ createReportDirectory();
494
+ const reportPath = getAvailableFilePath(REPORT_DIR, `${getNowString(finishedAt)}-execution-log`, ".md");
495
+ const traceSnapshot = options.traceMessages ?? getTraceMessages();
496
+ const extraSections = options.extraSections || [];
497
+ const serializedError = options.error ? serializeError(options.error) : null;
498
+ const report = `# Execution Log
499
+
500
+ - \uC2DC\uC791 \uC2DC\uAC01: ${getHumanReadableNowString(startedAt)}
501
+ - \uC885\uB8CC \uC2DC\uAC01: ${getHumanReadableNowString(finishedAt)}
502
+ - \uC2E4\uD589 \uC2DC\uAC04: ${formatExecutionDuration(startedAt, finishedAt)}
503
+ - \uC0C1\uD0DC: \`${status}\`
504
+ - Scope: \`${options.scope || "unknown"}\`
505
+ - \uC791\uC5C5 \uACBD\uB85C: \`${process.cwd()}\`
506
+ - \uC2E4\uD589 \uC778\uC790: \`${JSON.stringify(options.args ?? process.argv.slice(2))}\`
507
+ - \uC2E4\uD589 \uD658\uACBD: \`${process.platform} ${process.arch} / Node ${process.version}\`
508
+
509
+ ## Summary
510
+
511
+ ${getExecutionLogSummary(status, options.title)}
512
+ ${serializedError ? `
513
+
514
+ ## Error
515
+
516
+ \`\`\`json
517
+ ${JSON.stringify(serializedError, null, 2)}
518
+ \`\`\`` : ""}
519
+
520
+ ## Trace
521
+
522
+ \`\`\`json
523
+ ${JSON.stringify(traceSnapshot, null, 2)}
524
+ \`\`\`${extraSections.length ? `
525
+ ${extraSections.map((section) => `
526
+ ## ${section.heading}
527
+
528
+ ${section.markdown}`).join("\n")}
529
+ ` : "\n"}
530
+ `;
531
+ fs.writeFileSync(reportPath, report);
532
+ helperTrace("execution-log:write:done", reportPath);
533
+ return reportPath;
534
+ } catch (writeError) {
535
+ console.error("\u26A0\uFE0F \uC2E4\uD589 \uB85C\uADF8 \uD30C\uC77C \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
536
+ console.error(writeError);
537
+ return "";
538
+ }
539
+ }
426
540
  function exitWithError(message, options = {}) {
427
541
  const reportPath = writeErrorReport(options.error || new Error(message), {
428
542
  ...options,
@@ -810,6 +924,13 @@ var ALLOWED_REASONING_EFFORTS = ["minimal", "low", "medium", "high"];
810
924
  function shellQuote(value) {
811
925
  return `'${value.replace(/'/g, `'\\''`)}'`;
812
926
  }
927
+ function toShellOptionToken(value) {
928
+ const SIMPLE_SHELL_TOKEN_PATTERN = /^[A-Za-z0-9._:/=-]+$/;
929
+ if (SIMPLE_SHELL_TOKEN_PATTERN.test(value)) {
930
+ return value;
931
+ }
932
+ return shellQuote(value);
933
+ }
813
934
  function getArgValue(flag) {
814
935
  const index = args.indexOf(flag);
815
936
  if (index === -1 || !args[index + 1]) {
@@ -889,9 +1010,9 @@ function getAliasFallbacks(primaryAlias) {
889
1010
  }
890
1011
  function buildClaudeExecCommand(options) {
891
1012
  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)}`;
1013
+ const modelOption = model ? `--model ${toShellOptionToken(model)}` : "";
1014
+ const fallbackOption = model && fallbackModel ? `--fallback-model ${toShellOptionToken(fallbackModel)}` : "";
1015
+ const effortOption = `--effort ${toShellOptionToken(effort)}`;
895
1016
  const appendedPromptFiles = systemPromptFiles.map((path3) => `--append-system-prompt-file ${shellQuote(path3)}`).join(" ");
896
1017
  return `cat ${shellQuote(tempDiffPath2)} | claude ${[
897
1018
  modelOption,
@@ -1016,12 +1137,22 @@ function printNotice2(message) {
1016
1137
  console.warn(message);
1017
1138
  }
1018
1139
  }
1140
+ function normalizeEffort2(level) {
1141
+ if (level === "minimal") {
1142
+ return "low";
1143
+ }
1144
+ return level;
1145
+ }
1019
1146
  function resolveReasoningEffort2() {
1020
1147
  const customReasoningEffort = getArgValue2("--reasoning-effort");
1021
1148
  if (customReasoningEffort) {
1022
1149
  if (ALLOWED_REASONING_EFFORTS2.includes(customReasoningEffort)) {
1023
- trace3("reasoning:custom", customReasoningEffort);
1024
- return customReasoningEffort;
1150
+ const normalized = normalizeEffort2(customReasoningEffort);
1151
+ trace3("reasoning:custom", `${customReasoningEffort} -> ${normalized}`);
1152
+ if (customReasoningEffort === "minimal") {
1153
+ 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.");
1154
+ }
1155
+ return normalized;
1025
1156
  }
1026
1157
  printNotice2(
1027
1158
  `\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS2.join(
@@ -1030,8 +1161,8 @@ function resolveReasoningEffort2() {
1030
1161
  );
1031
1162
  }
1032
1163
  if (args2.includes("--flash")) {
1033
- trace3("reasoning:flash-default", "minimal");
1034
- return "minimal";
1164
+ trace3("reasoning:flash-default", "low");
1165
+ return "low";
1035
1166
  }
1036
1167
  if (args2.includes("--review")) {
1037
1168
  trace3("reasoning:review-default", "high");
@@ -1226,7 +1357,7 @@ function buildGeminiFileReferenceSection(files) {
1226
1357
  const existingFiles = files.filter((file) => fs.existsSync(file.path));
1227
1358
  return {
1228
1359
  count: existingFiles.length,
1229
- lines: existingFiles.map((file) => `- ${file.display}: ${toGeminiFileReference(file.path)}`)
1360
+ items: existingFiles.map((file) => `${file.display} ${toGeminiFileReference(file.path)}`)
1230
1361
  };
1231
1362
  }
1232
1363
  var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
@@ -1245,19 +1376,19 @@ var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
1245
1376
  const ruleSection = buildGeminiFileReferenceSection(rules);
1246
1377
  trace5("rules:loaded", `count=${ruleSection.count}`);
1247
1378
  const reviewFormExists = fs.existsSync(resolvedReviewFormPath);
1248
- const reviewFormLine = reviewFormExists ? `- \uB9AC\uBDF0 \uC591\uC2DD: ${toGeminiFileReference(resolvedReviewFormPath)}` : "- \uB9AC\uBDF0 \uC591\uC2DD: (\uC5C6\uC74C)";
1379
+ const reviewFormText = reviewFormExists ? `\uB9AC\uBDF0 \uC591\uC2DD ${toGeminiFileReference(resolvedReviewFormPath)}` : "\uB9AC\uBDF0 \uC591\uC2DD (\uC5C6\uC74C)";
1249
1380
  trace5("reviewForm:status", reviewFormExists ? "exists" : "missing");
1250
1381
  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}`;
1382
+ const ruleText = ruleSection.items.join(" ") || "(\uC5C6\uC74C)";
1383
+ const diffText = `\uB9AC\uBDF0 \uB300\uC0C1 diff ${toGeminiFileReference(resolvedTempDiffPath)}`;
1384
+ const prompt = [
1385
+ "\uC544\uB798 \uD30C\uC77C\uB4E4\uC744 \uC21C\uC11C\uB300\uB85C \uC77D\uACE0 \uCF54\uB4DC \uB9AC\uBDF0\uB97C \uC9C4\uD589\uD574\uC918.",
1386
+ `\uADDC\uCE59 \uD30C\uC77C: ${ruleText}`,
1387
+ `\uB9AC\uBDF0 \uC591\uC2DD \uD30C\uC77C: ${reviewFormText}`,
1388
+ `\uB9AC\uBDF0 \uB300\uC0C1 diff \uD30C\uC77C: ${diffText}`,
1389
+ "\uBC18\uB4DC\uC2DC \uB9AC\uBDF0 \uC591\uC2DD\uC5D0 \uB9DE\uCDB0 \uC791\uC131\uD574\uC918.",
1390
+ `\uCD94\uB860 \uAC15\uB3C4 \uC9C0\uCE68: ${reasoningInstruction}`
1391
+ ].join(" ");
1261
1392
  trace5("prompt:prepared", `length=${prompt.length}`);
1262
1393
  const modelCandidates = toUnique2(customModel ? [customModel, ...aliasFallbacks] : aliasFallbacks);
1263
1394
  trace5("model:candidates", modelCandidates.join(", "));
@@ -1320,8 +1451,10 @@ function checkGeminiCliInstalled() {
1320
1451
  // src/pr-review/review.ts
1321
1452
  async function main() {
1322
1453
  const args4 = process.argv.slice(2);
1454
+ const startedAt = /* @__PURE__ */ new Date();
1323
1455
  clearTraceMessages();
1324
1456
  const isTest = isTestMode(args4);
1457
+ const shouldStreamOutput = shouldStreamAIOutput(args4);
1325
1458
  const trace7 = createTraceLogger("review", args4);
1326
1459
  trace7("main:start", `args=${JSON.stringify(args4)}`);
1327
1460
  let command = "";
@@ -1330,6 +1463,12 @@ async function main() {
1330
1463
  let service = "";
1331
1464
  let selectedCommitSummary = "";
1332
1465
  let reviewTargetFiles = [];
1466
+ let executionLogPath = "";
1467
+ let executionStatus = "cancelled";
1468
+ 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.";
1469
+ let executionError = null;
1470
+ let resultLength = 0;
1471
+ let exitCode = 0;
1333
1472
  try {
1334
1473
  trace7("service-selection:start");
1335
1474
  service = await showSelectionAIService();
@@ -1353,14 +1492,18 @@ async function main() {
1353
1492
  }
1354
1493
  trace7("review-flow:start");
1355
1494
  console.log("\u{1F680} AI Code Review\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...");
1495
+ if (shouldStreamOutput) {
1496
+ console.log("\u2139\uFE0F AI \uC2E4\uC2DC\uAC04 \uC751\uB2F5 \uD45C\uC2DC \uBAA8\uB4DC\uAC00 \uD65C\uC131\uD654\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
1497
+ }
1356
1498
  trace7("commit-selection:start");
1357
1499
  const selectedCommits = await selectReviewCommits();
1358
1500
  trace7("commit-selection:done", `count=${selectedCommits.length}`);
1359
1501
  if (selectedCommits.length === 0) {
1360
1502
  trace7("commit-selection:empty");
1503
+ executionTitle = "\uC120\uD0DD\uB41C \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.";
1361
1504
  console.log("\u2139\uFE0F \uC120\uD0DD\uB41C \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
1362
1505
  deleteTempDiff();
1363
- process.exit(0);
1506
+ return;
1364
1507
  }
1365
1508
  selectedCommitSummary = buildSelectedCommitSummary(selectedCommits);
1366
1509
  trace7("commit-summary:prepared", selectedCommitSummary);
@@ -1372,9 +1515,10 @@ async function main() {
1372
1515
  trace7("git-diff:build:done", `length=${diff.length}`);
1373
1516
  if (!diff.trim() && !isTest) {
1374
1517
  trace7("empty-diff:exit");
1518
+ executionTitle = "\uC120\uD0DD\uD55C \uCEE4\uBC0B\uC5D0\uC11C \uB9AC\uBDF0 \uB300\uC0C1 \uD30C\uC77C \uBCC0\uACBD\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.";
1375
1519
  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.");
1376
1520
  deleteTempDiff();
1377
- process.exit(0);
1521
+ return;
1378
1522
  }
1379
1523
  const nowStr = getNowString();
1380
1524
  trace7("timestamp:created", nowStr);
@@ -1404,8 +1548,9 @@ async function main() {
1404
1548
  trace7("command:exec:start");
1405
1549
  const result = (await executeShellCommandWithProgress(command, {
1406
1550
  progressMessage: `\u23F3 [\uB9AC\uBDF0 \uC9C4\uD589] ${service} | \uB300\uC0C1 ${reviewTargetFiles.length}\uAC1C \uD30C\uC77C`,
1407
- streamOutput: isTest
1551
+ streamOutput: isTest || shouldStreamOutput
1408
1552
  })).stdout;
1553
+ resultLength = result.length;
1409
1554
  trace7("command:exec:done", `resultLength=${result.length}`);
1410
1555
  trace7("report:write:start");
1411
1556
  savedReportPath = getNextFilePath(REPORT_DIR, nowStr, ".md");
@@ -1431,9 +1576,15 @@ ${command}`);
1431
1576
  deleteTempDiff();
1432
1577
  trace7("cleanup-temp-diff:done");
1433
1578
  trace7("review-flow:end");
1579
+ executionStatus = "success";
1580
+ executionTitle = "\uB9AC\uBDF0\uAC00 \uC131\uACF5\uC801\uC73C\uB85C \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.";
1434
1581
  } catch (error) {
1435
1582
  trace7("review-flow:catch", getErrorSummary(error));
1436
1583
  let errorReportPath = "";
1584
+ executionStatus = "failed";
1585
+ executionTitle = "\uB9AC\uBDF0 \uC2E4\uD589 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
1586
+ executionError = error;
1587
+ exitCode = 1;
1437
1588
  trace7("cleanup-temp-diff:start(catch)");
1438
1589
  try {
1439
1590
  deleteTempDiff();
@@ -1473,7 +1624,50 @@ ${JSON.stringify(
1473
1624
  if (errorReportPath) {
1474
1625
  console.error(`\u{1F4C4} \uC5D0\uB7EC \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${errorReportPath}`);
1475
1626
  }
1476
- process.exit(1);
1627
+ } finally {
1628
+ executionLogPath = writeExecutionLog({
1629
+ scope: "review",
1630
+ status: executionStatus,
1631
+ title: executionTitle,
1632
+ args: args4,
1633
+ startedAt,
1634
+ error: executionError,
1635
+ extraSections: [
1636
+ {
1637
+ heading: "Execution Context",
1638
+ markdown: `\`\`\`json
1639
+ ${JSON.stringify(
1640
+ {
1641
+ service: service || null,
1642
+ selectedCommitSummary: selectedCommitSummary || null,
1643
+ reviewTargetFiles,
1644
+ command: command || null,
1645
+ tempDiffPath,
1646
+ savedDiffPath: savedDiffPath || null,
1647
+ savedReportPath: savedReportPath || null,
1648
+ shouldStreamOutput,
1649
+ resultLength
1650
+ },
1651
+ null,
1652
+ 2
1653
+ )}
1654
+ \`\`\``
1655
+ },
1656
+ {
1657
+ heading: "Generated Command",
1658
+ markdown: command ? `\`\`\`sh
1659
+ ${command}
1660
+ \`\`\`` : "(\uC5C6\uC74C)"
1661
+ }
1662
+ ]
1663
+ });
1664
+ if (executionLogPath) {
1665
+ const writeLog = executionStatus === "failed" ? console.error : console.log;
1666
+ writeLog(`\u{1F4DD} \uC2E4\uD589 \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${executionLogPath}`);
1667
+ }
1668
+ }
1669
+ if (exitCode !== 0) {
1670
+ process.exit(exitCode);
1477
1671
  }
1478
1672
  }
1479
1673
  main();