sales-frontend-gemini-cli 0.4.1 → 0.4.2

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 (39) hide show
  1. package/dist/common/helper.cjs +226 -32
  2. package/dist/common/helper.cjs.map +1 -1
  3. package/dist/common/helper.d.cts +22 -2
  4. package/dist/common/helper.d.ts +22 -2
  5. package/dist/common/helper.js +220 -33
  6. package/dist/common/helper.js.map +1 -1
  7. package/dist/pr-review/claude/claude-commander.cjs +8 -1
  8. package/dist/pr-review/claude/claude-commander.cjs.map +1 -1
  9. package/dist/pr-review/claude/claude-commander.js +8 -1
  10. package/dist/pr-review/claude/claude-commander.js.map +1 -1
  11. package/dist/pr-review/claude/installation-claude.cjs +178 -8
  12. package/dist/pr-review/claude/installation-claude.cjs.map +1 -1
  13. package/dist/pr-review/claude/installation-claude.js +177 -8
  14. package/dist/pr-review/claude/installation-claude.js.map +1 -1
  15. package/dist/pr-review/codex/codex-commander.cjs +6 -1
  16. package/dist/pr-review/codex/codex-commander.cjs.map +1 -1
  17. package/dist/pr-review/codex/codex-commander.js +6 -1
  18. package/dist/pr-review/codex/codex-commander.js.map +1 -1
  19. package/dist/pr-review/codex/installation-codex.cjs +178 -8
  20. package/dist/pr-review/codex/installation-codex.cjs.map +1 -1
  21. package/dist/pr-review/codex/installation-codex.js +177 -8
  22. package/dist/pr-review/codex/installation-codex.js.map +1 -1
  23. package/dist/pr-review/gemini/gemini-commander.cjs +7 -1
  24. package/dist/pr-review/gemini/gemini-commander.cjs.map +1 -1
  25. package/dist/pr-review/gemini/gemini-commander.js +7 -1
  26. package/dist/pr-review/gemini/gemini-commander.js.map +1 -1
  27. package/dist/pr-review/gemini/installation-gemini.cjs +178 -8
  28. package/dist/pr-review/gemini/installation-gemini.cjs.map +1 -1
  29. package/dist/pr-review/gemini/installation-gemini.js +177 -8
  30. package/dist/pr-review/gemini/installation-gemini.js.map +1 -1
  31. package/dist/pr-review/review-one-by-one.cjs +368 -108
  32. package/dist/pr-review/review-one-by-one.cjs.map +1 -1
  33. package/dist/pr-review/review-one-by-one.js +368 -108
  34. package/dist/pr-review/review-one-by-one.js.map +1 -1
  35. package/dist/pr-review/review.cjs +316 -80
  36. package/dist/pr-review/review.cjs.map +1 -1
  37. package/dist/pr-review/review.js +316 -80
  38. package/dist/pr-review/review.js.map +1 -1
  39. package/package.json +1 -1
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { exec, execSync } from 'child_process';
3
- import fs5 from 'fs';
4
- import util from 'util';
3
+ import fs from 'fs';
4
+ import util, { inspect } from 'util';
5
5
  import path from 'path';
6
6
  import readline from 'readline';
7
7
  import { fileURLToPath } from 'url';
8
8
 
9
9
  var __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+ var traceMessages = [];
10
11
  var rulesPath = path.resolve(__dirname, "../../src/common/rules/review-rules.md");
11
12
  var namingRulesPath = path.resolve(__dirname, "../../src/common/rules/naming-rule.md");
12
13
  var codingConventionRulesPath = path.resolve(__dirname, "../../src/common/rules/coding-convention.md");
@@ -29,66 +30,226 @@ var ignoreList = [
29
30
  ".review-report/"
30
31
  // 생성되는 리포트 폴더도 제외
31
32
  ];
32
- function parseServiceFromArgs(args4 = process.argv.slice(2)) {
33
- const serviceIndex = args4.indexOf("--service");
34
- const rawService = serviceIndex !== -1 ? args4[serviceIndex + 1] : "";
35
- if (!rawService) {
36
- return "";
37
- }
38
- const normalizedService = rawService.toLowerCase();
39
- if (AIServices.includes(normalizedService)) {
40
- return normalizedService;
41
- }
42
- console.error(
43
- `\u274C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC11C\uBE44\uC2A4\uC785\uB2C8\uB2E4: ${rawService}. \uC0AC\uC6A9 \uAC00\uB2A5 \uAC12: ${AIServices.join(", ")} (\uC608: --service codex)`
44
- );
45
- process.exit(1);
46
- }
47
33
  function isTestMode(args4 = process.argv.slice(2)) {
48
34
  return args4.includes("--test");
49
35
  }
36
+ function clearTraceMessages() {
37
+ traceMessages.length = 0;
38
+ }
39
+ function getTraceMessages() {
40
+ return [...traceMessages];
41
+ }
50
42
  function createTraceLogger(scope, args4 = process.argv.slice(2)) {
51
43
  const enabled = isTestMode(args4);
52
44
  return (step, detail) => {
45
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
46
+ const message = `[${timestamp}][TRACE][${scope}] ${step}${detail ? ` | ${detail}` : ""}`;
47
+ traceMessages.push(message);
53
48
  if (!enabled) {
54
49
  return;
55
50
  }
56
- console.log(`[TRACE][${scope}] ${step}${detail ? ` | ${detail}` : ""}`);
51
+ console.log(message);
52
+ };
53
+ }
54
+ var helperTrace = createTraceLogger("helper");
55
+ function getTimestampParts(now = /* @__PURE__ */ new Date()) {
56
+ return {
57
+ YYYY: now.getFullYear(),
58
+ MM: String(now.getMonth() + 1).padStart(2, "0"),
59
+ DD: String(now.getDate()).padStart(2, "0"),
60
+ HH: String(now.getHours()).padStart(2, "0"),
61
+ mm: String(now.getMinutes()).padStart(2, "0"),
62
+ ss: String(now.getSeconds()).padStart(2, "0")
57
63
  };
58
64
  }
65
+ function getHumanReadableNowString(now = /* @__PURE__ */ new Date()) {
66
+ const { YYYY, MM, DD, HH, mm, ss } = getTimestampParts(now);
67
+ return `${YYYY}-${MM}-${DD} ${HH}:${mm}:${ss}`;
68
+ }
69
+ function stringifyUnknown(value) {
70
+ if (value === void 0 || value === null) {
71
+ return "";
72
+ }
73
+ if (typeof value === "string") {
74
+ return value;
75
+ }
76
+ if (Buffer.isBuffer(value)) {
77
+ return value.toString();
78
+ }
79
+ if (value instanceof Error) {
80
+ return value.stack || value.message;
81
+ }
82
+ return inspect(value, { depth: 5, breakLength: 120 });
83
+ }
84
+ function getErrorSummary(error) {
85
+ if (error instanceof Error) {
86
+ return `${error.name}: ${error.message}`;
87
+ }
88
+ return stringifyUnknown(error) || "Unknown error";
89
+ }
90
+ function serializeError(error) {
91
+ const serialized = {
92
+ summary: getErrorSummary(error)
93
+ };
94
+ if (error instanceof Error) {
95
+ serialized.name = error.name;
96
+ serialized.message = error.message;
97
+ serialized.stack = error.stack;
98
+ } else {
99
+ serialized.value = stringifyUnknown(error);
100
+ }
101
+ if (error && typeof error === "object") {
102
+ const errorLike = error;
103
+ const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs"];
104
+ extraKeys.forEach((key) => {
105
+ if (errorLike[key] !== void 0) {
106
+ serialized[key] = errorLike[key];
107
+ }
108
+ });
109
+ const stdout = stringifyUnknown(errorLike.stdout);
110
+ if (stdout) {
111
+ serialized.stdout = stdout;
112
+ }
113
+ const stderr = stringifyUnknown(errorLike.stderr);
114
+ if (stderr) {
115
+ serialized.stderr = stderr;
116
+ }
117
+ const cause = stringifyUnknown(errorLike.cause);
118
+ if (cause) {
119
+ serialized.cause = cause;
120
+ }
121
+ }
122
+ return serialized;
123
+ }
59
124
  function getNextFilePath(dir, baseName, extension) {
60
125
  let counter = 1;
61
126
  while (true) {
62
127
  const filePath = path.join(dir, `${baseName}-${counter}${extension}`);
63
- if (!fs5.existsSync(filePath)) {
128
+ if (!fs.existsSync(filePath)) {
64
129
  return filePath;
65
130
  }
66
131
  counter++;
67
132
  }
68
133
  }
134
+ function getAvailableFilePath(dir, baseName, extension) {
135
+ const firstFilePath = path.join(dir, `${baseName}${extension}`);
136
+ if (!fs.existsSync(firstFilePath)) {
137
+ return firstFilePath;
138
+ }
139
+ return getNextFilePath(dir, baseName, extension);
140
+ }
69
141
  function deleteFile(filePath) {
70
- if (fs5.existsSync(filePath)) {
71
- fs5.unlinkSync(filePath);
142
+ if (fs.existsSync(filePath)) {
143
+ fs.unlinkSync(filePath);
72
144
  }
73
145
  }
74
146
  function deleteTempDiff() {
75
147
  deleteFile(tempDiffPath);
76
148
  }
77
149
  function createReportDirectory() {
78
- if (!fs5.existsSync(REPORT_DIR)) {
79
- fs5.mkdirSync(REPORT_DIR, { recursive: true });
80
- }
81
- }
82
- function getNowString() {
83
- const now = /* @__PURE__ */ new Date();
84
- const YYYY = now.getFullYear();
85
- const MM = String(now.getMonth() + 1).padStart(2, "0");
86
- const DD = String(now.getDate()).padStart(2, "0");
87
- const HH = String(now.getHours()).padStart(2, "0");
88
- const mm = String(now.getMinutes()).padStart(2, "0");
89
- const ss = String(now.getSeconds()).padStart(2, "0");
150
+ if (!fs.existsSync(REPORT_DIR)) {
151
+ fs.mkdirSync(REPORT_DIR, { recursive: true });
152
+ }
153
+ }
154
+ function getNowString(now = /* @__PURE__ */ new Date()) {
155
+ const { YYYY, MM, DD, HH, mm, ss } = getTimestampParts(now);
90
156
  return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;
91
157
  }
158
+ function getErrorLogTimestamp(now = /* @__PURE__ */ new Date()) {
159
+ const { YYYY, MM, DD, HH, mm, ss } = getTimestampParts(now);
160
+ return `${YYYY}-${MM}-${DD}-${HH}\uC2DC-${mm}\uBD84-${ss}\uCD08`;
161
+ }
162
+ function writeErrorReport(error, options = {}) {
163
+ try {
164
+ const now = /* @__PURE__ */ new Date();
165
+ helperTrace("error-report:write:start", options.scope || "unknown");
166
+ createReportDirectory();
167
+ const reportPath = getAvailableFilePath(REPORT_DIR, `error-log-${getErrorLogTimestamp(now)}`, ".md");
168
+ const serializedError = serializeError(error);
169
+ const traceSnapshot = options.traceMessages ?? getTraceMessages();
170
+ const extraSections = options.extraSections || [];
171
+ const report = `# Error Log
172
+
173
+ - \uBC1C\uC0DD \uC2DC\uAC01: ${getHumanReadableNowString(now)}
174
+ - Scope: \`${options.scope || "unknown"}\`
175
+ - \uC791\uC5C5 \uACBD\uB85C: \`${process.cwd()}\`
176
+ - \uC2E4\uD589 \uC778\uC790: \`${JSON.stringify(options.args ?? process.argv.slice(2))}\`
177
+ - \uC2E4\uD589 \uD658\uACBD: \`${process.platform} ${process.arch} / Node ${process.version}\`
178
+
179
+ ## Summary
180
+
181
+ ${options.title || serializedError.summary || "Unknown error"}
182
+
183
+ ## Error
184
+
185
+ \`\`\`json
186
+ ${JSON.stringify(serializedError, null, 2)}
187
+ \`\`\`
188
+
189
+ ## Trace
190
+
191
+ \`\`\`json
192
+ ${JSON.stringify(traceSnapshot, null, 2)}
193
+ \`\`\`${extraSections.length ? `
194
+ ${extraSections.map((section) => `
195
+ ## ${section.heading}
196
+
197
+ ${section.markdown}`).join("\n")}
198
+ ` : "\n"}
199
+ `;
200
+ fs.writeFileSync(reportPath, report);
201
+ helperTrace("error-report:write:done", reportPath);
202
+ return reportPath;
203
+ } catch (writeError) {
204
+ console.error("\u26A0\uFE0F \uC5D0\uB7EC \uB85C\uADF8 \uD30C\uC77C \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
205
+ console.error(writeError);
206
+ return "";
207
+ }
208
+ }
209
+ function exitWithError(message, options = {}) {
210
+ const reportPath = writeErrorReport(options.error || new Error(message), {
211
+ ...options,
212
+ title: message
213
+ });
214
+ console.error(message);
215
+ if (options.error) {
216
+ console.error(options.error);
217
+ }
218
+ if (reportPath) {
219
+ console.error(`\u{1F4C4} \uC5D0\uB7EC \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${reportPath}`);
220
+ }
221
+ process.exit(1);
222
+ }
223
+ function parseServiceFromArgs(args4 = process.argv.slice(2)) {
224
+ helperTrace("parse-service:start", `args=${JSON.stringify(args4)}`);
225
+ const serviceIndex = args4.indexOf("--service");
226
+ const rawService = serviceIndex !== -1 ? args4[serviceIndex + 1] : "";
227
+ if (!rawService) {
228
+ helperTrace("parse-service:empty");
229
+ return "";
230
+ }
231
+ const normalizedService = rawService.toLowerCase();
232
+ if (AIServices.includes(normalizedService)) {
233
+ helperTrace("parse-service:resolved", normalizedService);
234
+ return normalizedService;
235
+ }
236
+ helperTrace("parse-service:invalid", rawService);
237
+ exitWithError(
238
+ `\u274C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC11C\uBE44\uC2A4\uC785\uB2C8\uB2E4: ${rawService}. \uC0AC\uC6A9 \uAC00\uB2A5 \uAC12: ${AIServices.join(", ")} (\uC608: --service codex)`,
239
+ {
240
+ scope: "helper:parseServiceFromArgs",
241
+ args: args4,
242
+ extraSections: [
243
+ {
244
+ heading: "Allowed Services",
245
+ markdown: `\`\`\`json
246
+ ${JSON.stringify(AIServices, null, 2)}
247
+ \`\`\``
248
+ }
249
+ ]
250
+ }
251
+ );
252
+ }
92
253
  function getGitDiffFilter() {
93
254
  const includeExtensions = ["*.ts", "*.tsx", "*.js", "*.jsx"];
94
255
  const excludePatterns = ignoreList.map((item) => `:(exclude)${item}`);
@@ -100,6 +261,7 @@ function getGitDiffFilter() {
100
261
  function openReport(reportPath) {
101
262
  const resolvedPath = path.resolve(reportPath);
102
263
  const { platform } = process;
264
+ helperTrace("open-report:start", resolvedPath);
103
265
  const openWithChrome = () => {
104
266
  if (platform === "darwin") {
105
267
  execSync(`open -a "Google Chrome" "${resolvedPath}"`, { stdio: "ignore" });
@@ -124,32 +286,41 @@ function openReport(reportPath) {
124
286
  };
125
287
  try {
126
288
  if (openWithChrome()) {
289
+ helperTrace("open-report:chrome:success", platform);
127
290
  console.log("\u{1F680} Google Chrome\uC5D0\uC11C \uB9AC\uD3EC\uD2B8\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4.");
128
291
  return;
129
292
  }
130
- } catch {
293
+ } catch (error) {
294
+ helperTrace("open-report:chrome:failed", getErrorSummary(error));
131
295
  }
132
296
  try {
133
297
  if (openWithDefaultBrowser()) {
298
+ helperTrace("open-report:default-browser:success", platform);
134
299
  console.log("\u{1F680} \uAE30\uBCF8 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB9AC\uD3EC\uD2B8\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4.");
135
300
  return;
136
301
  }
137
- } catch (e) {
138
- console.error("\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800 \uC5F4\uAE30 \uC2E4\uD328:", e);
302
+ } catch (error) {
303
+ helperTrace("open-report:default-browser:failed", getErrorSummary(error));
304
+ console.error("\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800 \uC5F4\uAE30 \uC2E4\uD328:", error);
139
305
  return;
140
306
  }
307
+ helperTrace("open-report:unsupported-platform", platform);
141
308
  console.error(`\u26A0\uFE0F \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD50C\uB7AB\uD3FC\uC785\uB2C8\uB2E4: ${platform}`);
142
309
  }
143
310
  function getDiffArgs() {
144
311
  const args4 = process.argv.slice(2);
145
312
  const commitIndex = args4.indexOf("--commit");
146
313
  const { includeParams, excludeParams } = getGitDiffFilter();
314
+ helperTrace("diff-args:resolve:start", `args=${JSON.stringify(args4)}`);
147
315
  let diffArgs = "";
148
316
  if (commitIndex !== -1) {
149
317
  const commitHash = args4[commitIndex + 1];
150
318
  if (!commitHash) {
151
- console.error("\u274C \uCEE4\uBC0B \uD574\uC2DC\uAC00 \uC81C\uACF5\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
152
- process.exit(1);
319
+ helperTrace("diff-args:commit-hash-missing");
320
+ exitWithError("\u274C \uCEE4\uBC0B \uD574\uC2DC\uAC00 \uC81C\uACF5\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.", {
321
+ scope: "helper:getDiffArgs",
322
+ args: args4
323
+ });
153
324
  }
154
325
  const nextArg = args4[commitIndex + 2];
155
326
  let n = 0;
@@ -159,28 +330,37 @@ function getDiffArgs() {
159
330
  n = 0;
160
331
  }
161
332
  }
333
+ helperTrace("diff-args:commit-mode", `${commitHash}~${n + 1} ${commitHash}`);
162
334
  console.log(`\u2139\uFE0F \uCEE4\uBC0B '${commitHash}' ${n > 0 ? ` \uD3EC\uD568 \uCD1D ${n + 1}\uAC1C\uC758 \uCEE4\uBC0B` : ""}\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...`);
163
335
  diffArgs = `${commitHash}~${n + 1} ${commitHash}`;
164
336
  } else {
165
337
  try {
338
+ helperTrace("diff-args:unstaged-check:start");
166
339
  const check = execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();
167
340
  if (!check.trim()) {
341
+ helperTrace("diff-args:unstaged-check:empty", "use HEAD~1 HEAD");
168
342
  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...");
169
343
  diffArgs = "HEAD~1 HEAD";
344
+ } else {
345
+ helperTrace("diff-args:unstaged-check:has-changes", `length=${check.length}`);
170
346
  }
171
- } catch {
347
+ } catch (error) {
348
+ helperTrace("diff-args:unstaged-check:failed", getErrorSummary(error));
172
349
  }
173
350
  }
351
+ helperTrace("diff-args:resolve:done", diffArgs || "(default)");
174
352
  return diffArgs;
175
353
  }
176
354
  async function showSelectionAIService() {
177
355
  const selectedServiceFromArgs = parseServiceFromArgs();
178
356
  if (selectedServiceFromArgs) {
357
+ helperTrace("show-selection:from-args", selectedServiceFromArgs);
179
358
  console.log(`
180
359
  \u2705 \x1B[32m${selectedServiceFromArgs}\x1B[0m \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (--service)
181
360
  `);
182
361
  return selectedServiceFromArgs;
183
362
  }
363
+ helperTrace("show-selection:interactive:start");
184
364
  let selectedIndex = 0;
185
365
  const rl = readline.createInterface({
186
366
  input: process.stdin,
@@ -194,6 +374,7 @@ async function showSelectionAIService() {
194
374
  readline.moveCursor(process.stdout, 0, -(AIServices.length + 1));
195
375
  }
196
376
  firstRender = false;
377
+ helperTrace("show-selection:interactive:render", AIServices[selectedIndex] || "unknown");
197
378
  readline.clearScreenDown(process.stdout);
198
379
  process.stdout.write(
199
380
  "\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"
@@ -213,6 +394,7 @@ async function showSelectionAIService() {
213
394
  const onData = (data) => {
214
395
  const key = data.toString();
215
396
  if (key === "") {
397
+ helperTrace("show-selection:interactive:ctrl-c");
216
398
  process.stdout.write("\x1B[?25h");
217
399
  process.exit(0);
218
400
  }
@@ -233,6 +415,7 @@ async function showSelectionAIService() {
233
415
  `);
234
416
  const result = AIServices[selectedIndex];
235
417
  if (result) {
418
+ helperTrace("show-selection:interactive:confirmed", result);
236
419
  resolve(result);
237
420
  }
238
421
  }
@@ -346,14 +529,17 @@ var createClaudeCommand = (tempDiffPath2, reviewFormPath2) => {
346
529
  { path: namingRulesPath, display: "\uB124\uC774\uBC0D \uADDC\uCE59" },
347
530
  { path: codingConventionRulesPath, display: "\uCF54\uB529 \uCEE8\uBCA4\uC158" }
348
531
  ];
349
- const existingRuleFiles = rules.filter((rule) => fs5.existsSync(rule.path)).map((rule) => rule.path);
532
+ const existingRuleFiles = rules.filter((rule) => fs.existsSync(rule.path)).map((rule) => rule.path);
350
533
  trace("rules:loaded", `count=${existingRuleFiles.length}`);
351
- const reviewFormExists = fs5.existsSync(reviewFormPath2);
534
+ const reviewFormExists = fs.existsSync(reviewFormPath2);
352
535
  trace("reviewForm:status", reviewFormExists ? "exists" : "missing");
353
536
  const systemPromptFiles = reviewFormExists ? [...existingRuleFiles, reviewFormPath2] : existingRuleFiles;
354
537
  const prompt = "\uC704 \uADDC\uCE59\uB4E4\uC744 \uCC38\uACE0\uD558\uC5EC \uC774 diff\uB97C \uCF54\uB4DC\uB9AC\uBDF0\uD574\uC918. \uB9AC\uBDF0\uC591\uC2DD\uC5D0 \uB9DE\uCDB0\uC11C \uC791\uC131\uD574\uC918.";
538
+ trace("prompt:prepared", `length=${prompt.length}`);
539
+ trace("system-prompt-files", `count=${systemPromptFiles.length}`);
355
540
  const modelCandidates = toUnique(customModel ? [customModel, ...aliasFallbacks] : aliasFallbacks);
356
541
  trace("model:candidates", modelCandidates.join(", "));
542
+ trace("command:candidates:count", String(modelCandidates.length + 1));
357
543
  if (customModel) {
358
544
  console.warn(
359
545
  `\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
@@ -394,6 +580,7 @@ var createClaudeCommand = (tempDiffPath2, reviewFormPath2) => {
394
580
  \uC0DD\uC131\uB420 \uBA85\uB839\uC5B4 \uBBF8\uB9AC\uBCF4\uAE30:
395
581
  ${safeCommand}"`;
396
582
  }
583
+ trace("command:mode", "execute");
397
584
  trace("createClaudeCommand:end");
398
585
  return command;
399
586
  };
@@ -404,23 +591,25 @@ function checkClaudeCliInstalled() {
404
591
  trace2("version-check:run", "claude --version");
405
592
  execSync("claude --version", { stdio: "ignore" });
406
593
  trace2("version-check:ok");
407
- } catch {
408
- trace2("version-check:failed", "install-start");
594
+ } catch (error) {
595
+ trace2("version-check:failed", getErrorSummary(error));
596
+ trace2("install:start", "@anthropic-ai/claude-code");
409
597
  console.log(
410
598
  "\u2139\uFE0F claude-cli\uAC00 \uC124\uCE58\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC124\uCE58\uB97C \uC9C4\uD589\uD569\uB2C8\uB2E4... npm install -g @anthropic-ai/claude-code"
411
599
  );
412
600
  try {
413
601
  execSync("npm install -g @anthropic-ai/claude-code", { stdio: "inherit" });
414
- trace2("install:ok", "exit(1) for login");
602
+ trace2("install:ok", "login-required");
415
603
  console.log("\u2705 claude-cli \uC124\uCE58\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
416
604
  console.log("\u26A0\uFE0F claude-cli \uC0AC\uC6A9\uC744 \uC704\uD574 \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
417
605
  console.log(' \uD130\uBBF8\uB110\uC5D0\uC11C "claude" \uB97C \uC785\uB825\uD558\uC5EC \uBE0C\uB77C\uC6B0\uC800 \uB85C\uADF8\uC778\uC744 \uC644\uB8CC\uD55C \uD6C4, \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694.');
418
606
  process.exit(1);
419
607
  } catch (installError) {
420
- trace2("install:failed");
421
- console.error("\u274C claude-cli \uC124\uCE58 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. \uAD8C\uD55C \uBB38\uC81C\uC77C \uC218 \uC788\uC2B5\uB2C8\uB2E4 (sudo \uD544\uC694).");
422
- console.error(installError);
423
- process.exit(1);
608
+ trace2("install:failed", getErrorSummary(installError));
609
+ exitWithError("\u274C claude-cli \uC124\uCE58 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. \uAD8C\uD55C \uBB38\uC81C\uC77C \uC218 \uC788\uC2B5\uB2C8\uB2E4 (sudo \uD544\uC694).", {
610
+ scope: "installation-claude",
611
+ error: installError
612
+ });
424
613
  }
425
614
  }
426
615
  trace2("checkClaudeCliInstalled:end");
@@ -471,10 +660,10 @@ var createCodexCommand = (tempDiffPath2, reviewFormPath2) => {
471
660
  trace3("createCodexCommand:start", `tempDiffPath=${tempDiffPath2}, reviewFormPath=${reviewFormPath2}`);
472
661
  const customModel = getArgValue2("--model");
473
662
  const reasoningEffort = resolveReasoningEffort2();
474
- const rules = [rulesPath, namingRulesPath, codingConventionRulesPath].filter((filePath) => fs5.existsSync(filePath)).map((filePath) => `- ${filePath}`).join("\n");
663
+ const rules = [rulesPath, namingRulesPath, codingConventionRulesPath].filter((filePath) => fs.existsSync(filePath)).map((filePath) => `- ${filePath}`).join("\n");
475
664
  const rulesCount = rules ? rules.split("\n").length : 0;
476
665
  trace3("rules:loaded", `count=${rulesCount}`);
477
- const hasReviewForm = fs5.existsSync(reviewFormPath2);
666
+ const hasReviewForm = fs.existsSync(reviewFormPath2);
478
667
  const reviewFormLine = hasReviewForm ? `- ${reviewFormPath2}` : "";
479
668
  trace3("reviewForm:status", reviewFormLine ? "exists" : "missing");
480
669
  const prompt = `\uC544\uB798 \uD30C\uC77C\uB4E4\uC744 \uCC38\uACE0\uD574\uC11C \uCF54\uB4DC \uB9AC\uBDF0\uB97C \uC9C4\uD589\uD574\uC918.
@@ -486,6 +675,7 @@ ${reviewFormLine || "- (\uC5C6\uC74C)"}
486
675
  - ${tempDiffPath2}
487
676
 
488
677
  \uBC18\uB4DC\uC2DC \uB9AC\uBDF0 \uC591\uC2DD\uC5D0 \uB9DE\uCDB0 \uC791\uC131\uD574\uC918.`;
678
+ trace3("prompt:prepared", `length=${prompt.length}`);
489
679
  let command = "";
490
680
  if (customModel) {
491
681
  console.warn("\u26A0\uFE0F \uC9C0\uC815\uD55C \uBAA8\uB378\uC774 \uC5C6\uB294 \uACBD\uC6B0, \uC5D0\uB7EC\uAC00 \uBC1C\uC0DD\uD558\uB2C8 \uC8FC\uC758\uD558\uC138\uC694.");
@@ -511,6 +701,7 @@ ${reviewFormLine || "- (\uC5C6\uC74C)"}
511
701
  \uC0DD\uC131\uB420 \uBA85\uB839\uC5B4 \uBBF8\uB9AC\uBCF4\uAE30:
512
702
  ${safeCommand}"`;
513
703
  }
704
+ trace3("command:mode", "execute");
514
705
  trace3("createCodexCommand:end");
515
706
  return command;
516
707
  };
@@ -521,21 +712,23 @@ function checkCodexCliInstalled() {
521
712
  trace4("version-check:run", "codex --version");
522
713
  execSync("codex --version", { stdio: "ignore" });
523
714
  trace4("version-check:ok");
524
- } catch {
525
- trace4("version-check:failed", "install-start");
715
+ } catch (error) {
716
+ trace4("version-check:failed", getErrorSummary(error));
717
+ trace4("install:start", "@openai/codex");
526
718
  console.log("\u2139\uFE0F codex-cli\uAC00 \uC124\uCE58\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC124\uCE58\uB97C \uC9C4\uD589\uD569\uB2C8\uB2E4... npm install -g @openai/codex");
527
719
  try {
528
720
  execSync("npm install -g @openai/codex", { stdio: "inherit" });
529
- trace4("install:ok", "exit(1) for login");
721
+ trace4("install:ok", "login-required");
530
722
  console.log("\u2705 codex-cli \uC124\uCE58\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
531
723
  console.log("\u26A0\uFE0F codex-cli \uC0AC\uC6A9\uC744 \uC704\uD574 \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
532
724
  console.log(' \uD130\uBBF8\uB110\uC5D0\uC11C "codex login" \uC744 \uC785\uB825\uD558\uC5EC \uB85C\uADF8\uC778\uC744 \uC644\uB8CC\uD55C \uD6C4, \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694.');
533
725
  process.exit(1);
534
726
  } catch (installError) {
535
- trace4("install:failed");
536
- console.error("\u274C codex-cli \uC124\uCE58 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. \uAD8C\uD55C \uBB38\uC81C\uC77C \uC218 \uC788\uC2B5\uB2C8\uB2E4 (sudo \uD544\uC694).");
537
- console.error(installError);
538
- process.exit(1);
727
+ trace4("install:failed", getErrorSummary(installError));
728
+ exitWithError("\u274C codex-cli \uC124\uCE58 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. \uAD8C\uD55C \uBB38\uC81C\uC77C \uC218 \uC788\uC2B5\uB2C8\uB2E4 (sudo \uD544\uC694).", {
729
+ scope: "installation-codex",
730
+ error: installError
731
+ });
539
732
  }
540
733
  }
541
734
  trace4("checkCodexCliInstalled:end");
@@ -643,17 +836,19 @@ var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
643
836
  { path: namingRulesPath, display: "\uB124\uC774\uBC0D \uADDC\uCE59" },
644
837
  { path: codingConventionRulesPath, display: "\uCF54\uB529 \uCEE8\uBCA4\uC158" }
645
838
  ];
646
- const validRules = rules.filter((rule) => fs5.existsSync(rule.path)).map((rule) => `@${rule.path}`).join(", ");
839
+ const validRules = rules.filter((rule) => fs.existsSync(rule.path)).map((rule) => `@${rule.path}`).join(", ");
647
840
  const rulesCount = validRules ? validRules.split(",").length : 0;
648
841
  trace5("rules:loaded", `count=${rulesCount}`);
649
- const reviewFormRef = fs5.existsSync(reviewFormPath2) ? `@${reviewFormPath2}` : "(\uC5C6\uC74C)";
842
+ const reviewFormRef = fs.existsSync(reviewFormPath2) ? `@${reviewFormPath2}` : "(\uC5C6\uC74C)";
650
843
  trace5("reviewForm:status", reviewFormRef === "(\uC5C6\uC74C)" ? "missing" : "exists");
651
844
  const reasoningInstruction = getReasoningInstruction(reasoningEffort);
652
845
  const prompt = `\uB2E4\uC74C \uADDC\uCE59\uB4E4\uC744 \uCC38\uACE0\uD574\uC11C(${validRules || "(\uC5C6\uC74C)"}) \uC774 diff(@${tempDiffPath2})\uB97C \uB9AC\uBDF0\uD574\uC918.
653
846
  \uB9AC\uBDF0 \uC591\uC2DD\uC740 ${reviewFormRef} \uC5D0 \uB9DE\uCDB0\uC11C \uC791\uC131\uD574\uC918.
654
847
  \uCD94\uB860 \uAC15\uB3C4 \uC9C0\uCE68: ${reasoningInstruction}`;
848
+ trace5("prompt:prepared", `length=${prompt.length}`);
655
849
  const modelCandidates = toUnique2(customModel ? [customModel, ...aliasFallbacks] : aliasFallbacks);
656
850
  trace5("model:candidates", modelCandidates.join(", "));
851
+ trace5("command:candidates:count", String(modelCandidates.length + 1));
657
852
  if (customModel) {
658
853
  console.warn(
659
854
  `\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
@@ -676,6 +871,7 @@ var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
676
871
  \uC0DD\uC131\uB420 \uBA85\uB839\uC5B4 \uBBF8\uB9AC\uBCF4\uAE30:
677
872
  ${safeCommand}"`;
678
873
  }
874
+ trace5("command:mode", "execute");
679
875
  trace5("createGeminiCommand:end");
680
876
  return command;
681
877
  };
@@ -686,21 +882,23 @@ function checkGeminiCliInstalled() {
686
882
  trace6("version-check:run", "gemini --version");
687
883
  execSync("gemini --version", { stdio: "ignore" });
688
884
  trace6("version-check:ok");
689
- } catch {
690
- trace6("version-check:failed", "install-start");
885
+ } catch (error) {
886
+ trace6("version-check:failed", getErrorSummary(error));
887
+ trace6("install:start", "@google/gemini-cli");
691
888
  console.log("\u2139\uFE0F gemini-cli\uAC00 \uC124\uCE58\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC124\uCE58\uB97C \uC9C4\uD589\uD569\uB2C8\uB2E4... npm install -g @google/gemini-cli");
692
889
  try {
693
890
  execSync("npm install -g @google/gemini-cli", { stdio: "inherit" });
694
- trace6("install:ok", "exit(1) for login");
891
+ trace6("install:ok", "login-required");
695
892
  console.log("\u2705 gemini-cli \uC124\uCE58\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
696
893
  console.log("\u26A0\uFE0F Gemini API \uC0AC\uC6A9\uC744 \uC704\uD574 \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
697
894
  console.log(' \uD130\uBBF8\uB110\uC5D0\uC11C "gemini" \uB97C \uC785\uB825\uD558\uC5EC \uBE0C\uB77C\uC6B0\uC800 \uB85C\uADF8\uC778\uC744 \uC644\uB8CC\uD55C \uD6C4, \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694.');
698
895
  process.exit(1);
699
896
  } catch (installError) {
700
- trace6("install:failed");
701
- console.error("\u274C gemini-cli \uC124\uCE58 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. \uAD8C\uD55C \uBB38\uC81C\uC77C \uC218 \uC788\uC2B5\uB2C8\uB2E4 (sudo \uD544\uC694).");
702
- console.error(installError);
703
- process.exit(1);
897
+ trace6("install:failed", getErrorSummary(installError));
898
+ exitWithError("\u274C gemini-cli \uC124\uCE58 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. \uAD8C\uD55C \uBB38\uC81C\uC77C \uC218 \uC788\uC2B5\uB2C8\uB2E4 (sudo \uD544\uC694).", {
899
+ scope: "installation-gemini",
900
+ error: installError
901
+ });
704
902
  }
705
903
  }
706
904
  trace6("checkGeminiCliInstalled:end");
@@ -710,30 +908,34 @@ function checkGeminiCliInstalled() {
710
908
  var execAsync = util.promisify(exec);
711
909
  async function main() {
712
910
  const args4 = process.argv.slice(2);
911
+ clearTraceMessages();
713
912
  const isTest = isTestMode(args4);
714
913
  const trace7 = createTraceLogger("review-one-by-one", args4);
715
914
  trace7("main:start", `args=${JSON.stringify(args4)}`);
716
- trace7("service-selection:start");
717
- const service = await showSelectionAIService();
718
- trace7("service-selection:done", `service=${service}`);
719
- switch (service) {
720
- case "gemini":
721
- trace7("install-check:start", "service=gemini");
722
- checkGeminiCliInstalled();
723
- trace7("install-check:done", "service=gemini");
724
- break;
725
- case "claude":
726
- trace7("install-check:start", "service=claude");
727
- checkClaudeCliInstalled();
728
- trace7("install-check:done", "service=claude");
729
- break;
730
- case "codex":
731
- trace7("install-check:start", "service=codex");
732
- checkCodexCliInstalled();
733
- trace7("install-check:done", "service=codex");
734
- break;
735
- }
915
+ let service = "";
916
+ let savedDiffPath = "";
917
+ let savedReportPath = "";
736
918
  try {
919
+ trace7("service-selection:start");
920
+ service = await showSelectionAIService();
921
+ trace7("service-selection:done", `service=${service}`);
922
+ switch (service) {
923
+ case "gemini":
924
+ trace7("install-check:start", "service=gemini");
925
+ checkGeminiCliInstalled();
926
+ trace7("install-check:done", "service=gemini");
927
+ break;
928
+ case "claude":
929
+ trace7("install-check:start", "service=claude");
930
+ checkClaudeCliInstalled();
931
+ trace7("install-check:done", "service=claude");
932
+ break;
933
+ case "codex":
934
+ trace7("install-check:start", "service=codex");
935
+ checkCodexCliInstalled();
936
+ trace7("install-check:done", "service=codex");
937
+ break;
938
+ }
737
939
  trace7("review-flow:start");
738
940
  console.log("\u{1F680} AI Code Review\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...");
739
941
  trace7("report-dir:create:start");
@@ -741,6 +943,7 @@ async function main() {
741
943
  trace7("report-dir:create:done");
742
944
  const { includeParams, excludeParams } = getGitDiffFilter();
743
945
  trace7("diff-filter:loaded", `include=${includeParams} | exclude=${excludeParams}`);
946
+ trace7("diff-args:build:start");
744
947
  const diffArgs = getDiffArgs();
745
948
  trace7("diff-args:build:done", `diffArgs=${diffArgs || "(default)"}`);
746
949
  const filesCommand = `git diff --name-only ${diffArgs} -- ${includeParams} ${excludeParams}`;
@@ -760,26 +963,26 @@ async function main() {
760
963
  trace7("full-diff:done", `length=${fullDiff.length}`);
761
964
  trace7("timestamp:created", nowStr);
762
965
  trace7("temp-diff:write:start", tempDiffPath);
763
- fs5.writeFileSync(tempDiffPath, fullDiff);
966
+ fs.writeFileSync(tempDiffPath, fullDiff);
764
967
  trace7("temp-diff:write:done");
765
968
  trace7("saved-diff:copy:start");
766
- const savedDiffPath = getNextFilePath(REPORT_DIR, `${nowStr}-diff`, ".txt");
767
- fs5.copyFileSync(tempDiffPath, savedDiffPath);
969
+ savedDiffPath = getNextFilePath(REPORT_DIR, `${nowStr}-diff`, ".txt");
970
+ fs.copyFileSync(tempDiffPath, savedDiffPath);
768
971
  trace7("saved-diff:copy:done", savedDiffPath);
769
- const savedReportPath = getNextFilePath(REPORT_DIR, nowStr, ".md");
972
+ savedReportPath = getNextFilePath(REPORT_DIR, nowStr, ".md");
770
973
  trace7("saved-report:path", savedReportPath);
771
974
  const promises = fileList.map(async (file) => {
975
+ const tempOneFileDiffPath = `temp_diff_${file.replace(/\//g, "_")}.txt`;
976
+ let command = "";
772
977
  try {
773
978
  trace7("file-review:start", file);
774
979
  console.log(`\u{1F50D} Reviewing: ${file}...`);
775
980
  trace7("file-diff:run", file);
776
981
  const fileDiff = execSync(`git diff ${diffArgs} -- "${file}"`).toString();
777
982
  trace7("file-diff:done", `${file} | length=${fileDiff.length}`);
778
- const tempOneFileDiffPath = `temp_diff_${file.replace(/\//g, "_")}.txt`;
779
983
  trace7("file-temp-diff:write:start", tempOneFileDiffPath);
780
- fs5.writeFileSync(tempOneFileDiffPath, fileDiff);
984
+ fs.writeFileSync(tempOneFileDiffPath, fileDiff);
781
985
  trace7("file-temp-diff:write:done", tempOneFileDiffPath);
782
- let command = "";
783
986
  trace7("file-command:create:start", file);
784
987
  switch (service) {
785
988
  case "gemini":
@@ -792,7 +995,7 @@ async function main() {
792
995
  command = createCodexCommand(tempOneFileDiffPath, reviewFormOneByOnePath);
793
996
  break;
794
997
  }
795
- trace7("file-command:create:done", file);
998
+ trace7("file-command:create:done", `${file} | commandLength=${command.length}`);
796
999
  trace7("file-command:exec:start", file);
797
1000
  const { stdout } = await execAsync(command, { maxBuffer: 1024 * 1024 * 20 });
798
1001
  const result = stdout.toString();
@@ -806,11 +1009,11 @@ ${result}
806
1009
  `;
807
1010
  console.log(tempReport);
808
1011
  trace7("file-report:append:start", file);
809
- fs5.appendFileSync(savedReportPath, tempReport);
1012
+ fs.appendFileSync(savedReportPath, tempReport);
810
1013
  trace7("file-report:append:done", file);
811
1014
  if (isTest) {
812
1015
  trace7("file-test-command:append:start", file);
813
- fs5.appendFileSync(savedReportPath, `
1016
+ fs.appendFileSync(savedReportPath, `
814
1017
 
815
1018
  ## \uC0AC\uC6A9\uB41C \uBA85\uB839\uC5B4
816
1019
 
@@ -819,26 +1022,51 @@ ${command}`);
819
1022
  }
820
1023
  trace7("file-review:end", file);
821
1024
  } catch (err) {
822
- trace7("file-review:catch", file);
1025
+ trace7("file-review:catch", `${file} | ${getErrorSummary(err)}`);
1026
+ const errorLogPath = writeErrorReport(err, {
1027
+ scope: "review-one-by-one:file",
1028
+ args: args4,
1029
+ extraSections: [
1030
+ {
1031
+ heading: "Execution Context",
1032
+ markdown: `\`\`\`json
1033
+ ${JSON.stringify(
1034
+ {
1035
+ service,
1036
+ file,
1037
+ command: command || null,
1038
+ tempOneFileDiffPath,
1039
+ savedDiffPath: savedDiffPath || null,
1040
+ savedReportPath: savedReportPath || null
1041
+ },
1042
+ null,
1043
+ 2
1044
+ )}
1045
+ \`\`\``
1046
+ }
1047
+ ]
1048
+ });
823
1049
  console.error(`\u274C Error reviewing file ${file}:`, err);
1050
+ if (errorLogPath) {
1051
+ console.error(`\u{1F4C4} \uC5D0\uB7EC \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${errorLogPath}`);
1052
+ }
824
1053
  const errorReport = `### File: ${file}
825
1054
  \u274C Review Failed
826
1055
  \`\`\`
827
- ${err}
1056
+ ${getErrorSummary(err)}
828
1057
  \`\`\`
829
1058
 
830
1059
  `;
831
- fs5.appendFileSync(savedReportPath, errorReport);
1060
+ fs.appendFileSync(savedReportPath, errorReport);
832
1061
  try {
833
- const tempOneFileDiffPath = `temp_diff_${file.replace(/\//g, "_")}.txt`;
834
- if (fs5.existsSync(tempOneFileDiffPath)) {
1062
+ if (fs.existsSync(tempOneFileDiffPath)) {
835
1063
  trace7("file-temp-diff:delete:start(catch)", tempOneFileDiffPath);
836
1064
  deleteFile(tempOneFileDiffPath);
837
1065
  trace7("file-temp-diff:delete:done(catch)", tempOneFileDiffPath);
838
1066
  }
839
- } catch (e) {
840
- trace7("file-temp-diff:delete:failed(catch)", file);
841
- console.error(`\u274C Error deleting temp file for ${file}:`, e);
1067
+ } catch (cleanupError) {
1068
+ trace7("file-temp-diff:delete:failed(catch)", `${file} | ${getErrorSummary(cleanupError)}`);
1069
+ console.error(`\u274C Error deleting temp file for ${file}:`, cleanupError);
842
1070
  }
843
1071
  }
844
1072
  });
@@ -857,12 +1085,44 @@ ${err}
857
1085
  trace7("cleanup-temp-diff:done");
858
1086
  trace7("review-flow:end");
859
1087
  } catch (error) {
860
- trace7("review-flow:catch");
1088
+ trace7("review-flow:catch", getErrorSummary(error));
1089
+ let errorReportPath = "";
1090
+ trace7("cleanup-temp-diff:start(catch)");
1091
+ try {
1092
+ deleteTempDiff();
1093
+ trace7("cleanup-temp-diff:done(catch)");
1094
+ } catch (cleanupError) {
1095
+ trace7("cleanup-temp-diff:failed(catch)", getErrorSummary(cleanupError));
1096
+ console.error("\u26A0\uFE0F \uC784\uC2DC diff \uC815\uB9AC \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.");
1097
+ console.error(cleanupError);
1098
+ }
1099
+ trace7("error-report:prepare", `service=${service || "unknown"}`);
1100
+ errorReportPath = writeErrorReport(error, {
1101
+ scope: "review-one-by-one",
1102
+ args: args4,
1103
+ extraSections: [
1104
+ {
1105
+ heading: "Execution Context",
1106
+ markdown: `\`\`\`json
1107
+ ${JSON.stringify(
1108
+ {
1109
+ service: service || null,
1110
+ tempDiffPath,
1111
+ savedDiffPath: savedDiffPath || null,
1112
+ savedReportPath: savedReportPath || null
1113
+ },
1114
+ null,
1115
+ 2
1116
+ )}
1117
+ \`\`\``
1118
+ }
1119
+ ]
1120
+ });
861
1121
  console.error("\u274C \uB9AC\uBDF0 \uB3C4\uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.");
862
1122
  console.error(error);
863
- trace7("cleanup-temp-diff:start(catch)");
864
- deleteTempDiff();
865
- trace7("cleanup-temp-diff:done(catch)");
1123
+ if (errorReportPath) {
1124
+ console.error(`\u{1F4C4} \uC5D0\uB7EC \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${errorReportPath}`);
1125
+ }
866
1126
  process.exit(1);
867
1127
  }
868
1128
  }