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
@@ -6,6 +6,7 @@ var fs = require('fs');
6
6
  var path = require('path');
7
7
  var readline = require('readline');
8
8
  var url = require('url');
9
+ var util = require('util');
9
10
 
10
11
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
11
12
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -15,6 +16,7 @@ var path__default = /*#__PURE__*/_interopDefault(path);
15
16
  var readline__default = /*#__PURE__*/_interopDefault(readline);
16
17
 
17
18
  var __dirname$1 = path__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('review.cjs', document.baseURI).href))));
19
+ var traceMessages = [];
18
20
  var rulesPath = path__default.default.resolve(__dirname$1, "../../src/common/rules/review-rules.md");
19
21
  var namingRulesPath = path__default.default.resolve(__dirname$1, "../../src/common/rules/naming-rule.md");
20
22
  var codingConventionRulesPath = path__default.default.resolve(__dirname$1, "../../src/common/rules/coding-convention.md");
@@ -37,32 +39,96 @@ var ignoreList = [
37
39
  ".review-report/"
38
40
  // 생성되는 리포트 폴더도 제외
39
41
  ];
40
- function parseServiceFromArgs(args4 = process.argv.slice(2)) {
41
- const serviceIndex = args4.indexOf("--service");
42
- const rawService = serviceIndex !== -1 ? args4[serviceIndex + 1] : "";
43
- if (!rawService) {
44
- return "";
45
- }
46
- const normalizedService = rawService.toLowerCase();
47
- if (AIServices.includes(normalizedService)) {
48
- return normalizedService;
49
- }
50
- console.error(
51
- `\u274C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC11C\uBE44\uC2A4\uC785\uB2C8\uB2E4: ${rawService}. \uC0AC\uC6A9 \uAC00\uB2A5 \uAC12: ${AIServices.join(", ")} (\uC608: --service codex)`
52
- );
53
- process.exit(1);
54
- }
55
42
  function isTestMode(args4 = process.argv.slice(2)) {
56
43
  return args4.includes("--test");
57
44
  }
45
+ function clearTraceMessages() {
46
+ traceMessages.length = 0;
47
+ }
48
+ function getTraceMessages() {
49
+ return [...traceMessages];
50
+ }
58
51
  function createTraceLogger(scope, args4 = process.argv.slice(2)) {
59
52
  const enabled = isTestMode(args4);
60
53
  return (step, detail) => {
54
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
55
+ const message = `[${timestamp}][TRACE][${scope}] ${step}${detail ? ` | ${detail}` : ""}`;
56
+ traceMessages.push(message);
61
57
  if (!enabled) {
62
58
  return;
63
59
  }
64
- console.log(`[TRACE][${scope}] ${step}${detail ? ` | ${detail}` : ""}`);
60
+ console.log(message);
61
+ };
62
+ }
63
+ var helperTrace = createTraceLogger("helper");
64
+ function getTimestampParts(now = /* @__PURE__ */ new Date()) {
65
+ return {
66
+ YYYY: now.getFullYear(),
67
+ MM: String(now.getMonth() + 1).padStart(2, "0"),
68
+ DD: String(now.getDate()).padStart(2, "0"),
69
+ HH: String(now.getHours()).padStart(2, "0"),
70
+ mm: String(now.getMinutes()).padStart(2, "0"),
71
+ ss: String(now.getSeconds()).padStart(2, "0")
72
+ };
73
+ }
74
+ function getHumanReadableNowString(now = /* @__PURE__ */ new Date()) {
75
+ const { YYYY, MM, DD, HH, mm, ss } = getTimestampParts(now);
76
+ return `${YYYY}-${MM}-${DD} ${HH}:${mm}:${ss}`;
77
+ }
78
+ function stringifyUnknown(value) {
79
+ if (value === void 0 || value === null) {
80
+ return "";
81
+ }
82
+ if (typeof value === "string") {
83
+ return value;
84
+ }
85
+ if (Buffer.isBuffer(value)) {
86
+ return value.toString();
87
+ }
88
+ if (value instanceof Error) {
89
+ return value.stack || value.message;
90
+ }
91
+ return util.inspect(value, { depth: 5, breakLength: 120 });
92
+ }
93
+ function getErrorSummary(error) {
94
+ if (error instanceof Error) {
95
+ return `${error.name}: ${error.message}`;
96
+ }
97
+ return stringifyUnknown(error) || "Unknown error";
98
+ }
99
+ function serializeError(error) {
100
+ const serialized = {
101
+ summary: getErrorSummary(error)
65
102
  };
103
+ if (error instanceof Error) {
104
+ serialized.name = error.name;
105
+ serialized.message = error.message;
106
+ serialized.stack = error.stack;
107
+ } else {
108
+ serialized.value = stringifyUnknown(error);
109
+ }
110
+ if (error && typeof error === "object") {
111
+ const errorLike = error;
112
+ const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs"];
113
+ extraKeys.forEach((key) => {
114
+ if (errorLike[key] !== void 0) {
115
+ serialized[key] = errorLike[key];
116
+ }
117
+ });
118
+ const stdout = stringifyUnknown(errorLike.stdout);
119
+ if (stdout) {
120
+ serialized.stdout = stdout;
121
+ }
122
+ const stderr = stringifyUnknown(errorLike.stderr);
123
+ if (stderr) {
124
+ serialized.stderr = stderr;
125
+ }
126
+ const cause = stringifyUnknown(errorLike.cause);
127
+ if (cause) {
128
+ serialized.cause = cause;
129
+ }
130
+ }
131
+ return serialized;
66
132
  }
67
133
  function getNextFilePath(dir, baseName, extension) {
68
134
  let counter = 1;
@@ -74,6 +140,13 @@ function getNextFilePath(dir, baseName, extension) {
74
140
  counter++;
75
141
  }
76
142
  }
143
+ function getAvailableFilePath(dir, baseName, extension) {
144
+ const firstFilePath = path__default.default.join(dir, `${baseName}${extension}`);
145
+ if (!fs__default.default.existsSync(firstFilePath)) {
146
+ return firstFilePath;
147
+ }
148
+ return getNextFilePath(dir, baseName, extension);
149
+ }
77
150
  function deleteFile(filePath) {
78
151
  if (fs__default.default.existsSync(filePath)) {
79
152
  fs__default.default.unlinkSync(filePath);
@@ -87,16 +160,105 @@ function createReportDirectory() {
87
160
  fs__default.default.mkdirSync(REPORT_DIR, { recursive: true });
88
161
  }
89
162
  }
90
- function getNowString() {
91
- const now = /* @__PURE__ */ new Date();
92
- const YYYY = now.getFullYear();
93
- const MM = String(now.getMonth() + 1).padStart(2, "0");
94
- const DD = String(now.getDate()).padStart(2, "0");
95
- const HH = String(now.getHours()).padStart(2, "0");
96
- const mm = String(now.getMinutes()).padStart(2, "0");
97
- const ss = String(now.getSeconds()).padStart(2, "0");
163
+ function getNowString(now = /* @__PURE__ */ new Date()) {
164
+ const { YYYY, MM, DD, HH, mm, ss } = getTimestampParts(now);
98
165
  return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;
99
166
  }
167
+ function getErrorLogTimestamp(now = /* @__PURE__ */ new Date()) {
168
+ const { YYYY, MM, DD, HH, mm, ss } = getTimestampParts(now);
169
+ return `${YYYY}-${MM}-${DD}-${HH}\uC2DC-${mm}\uBD84-${ss}\uCD08`;
170
+ }
171
+ function writeErrorReport(error, options = {}) {
172
+ try {
173
+ const now = /* @__PURE__ */ new Date();
174
+ helperTrace("error-report:write:start", options.scope || "unknown");
175
+ createReportDirectory();
176
+ const reportPath = getAvailableFilePath(REPORT_DIR, `error-log-${getErrorLogTimestamp(now)}`, ".md");
177
+ const serializedError = serializeError(error);
178
+ const traceSnapshot = options.traceMessages ?? getTraceMessages();
179
+ const extraSections = options.extraSections || [];
180
+ const report = `# Error Log
181
+
182
+ - \uBC1C\uC0DD \uC2DC\uAC01: ${getHumanReadableNowString(now)}
183
+ - Scope: \`${options.scope || "unknown"}\`
184
+ - \uC791\uC5C5 \uACBD\uB85C: \`${process.cwd()}\`
185
+ - \uC2E4\uD589 \uC778\uC790: \`${JSON.stringify(options.args ?? process.argv.slice(2))}\`
186
+ - \uC2E4\uD589 \uD658\uACBD: \`${process.platform} ${process.arch} / Node ${process.version}\`
187
+
188
+ ## Summary
189
+
190
+ ${options.title || serializedError.summary || "Unknown error"}
191
+
192
+ ## Error
193
+
194
+ \`\`\`json
195
+ ${JSON.stringify(serializedError, null, 2)}
196
+ \`\`\`
197
+
198
+ ## Trace
199
+
200
+ \`\`\`json
201
+ ${JSON.stringify(traceSnapshot, null, 2)}
202
+ \`\`\`${extraSections.length ? `
203
+ ${extraSections.map((section) => `
204
+ ## ${section.heading}
205
+
206
+ ${section.markdown}`).join("\n")}
207
+ ` : "\n"}
208
+ `;
209
+ fs__default.default.writeFileSync(reportPath, report);
210
+ helperTrace("error-report:write:done", reportPath);
211
+ return reportPath;
212
+ } catch (writeError) {
213
+ console.error("\u26A0\uFE0F \uC5D0\uB7EC \uB85C\uADF8 \uD30C\uC77C \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
214
+ console.error(writeError);
215
+ return "";
216
+ }
217
+ }
218
+ function exitWithError(message, options = {}) {
219
+ const reportPath = writeErrorReport(options.error || new Error(message), {
220
+ ...options,
221
+ title: message
222
+ });
223
+ console.error(message);
224
+ if (options.error) {
225
+ console.error(options.error);
226
+ }
227
+ if (reportPath) {
228
+ console.error(`\u{1F4C4} \uC5D0\uB7EC \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${reportPath}`);
229
+ }
230
+ process.exit(1);
231
+ }
232
+ function parseServiceFromArgs(args4 = process.argv.slice(2)) {
233
+ helperTrace("parse-service:start", `args=${JSON.stringify(args4)}`);
234
+ const serviceIndex = args4.indexOf("--service");
235
+ const rawService = serviceIndex !== -1 ? args4[serviceIndex + 1] : "";
236
+ if (!rawService) {
237
+ helperTrace("parse-service:empty");
238
+ return "";
239
+ }
240
+ const normalizedService = rawService.toLowerCase();
241
+ if (AIServices.includes(normalizedService)) {
242
+ helperTrace("parse-service:resolved", normalizedService);
243
+ return normalizedService;
244
+ }
245
+ helperTrace("parse-service:invalid", rawService);
246
+ exitWithError(
247
+ `\u274C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC11C\uBE44\uC2A4\uC785\uB2C8\uB2E4: ${rawService}. \uC0AC\uC6A9 \uAC00\uB2A5 \uAC12: ${AIServices.join(", ")} (\uC608: --service codex)`,
248
+ {
249
+ scope: "helper:parseServiceFromArgs",
250
+ args: args4,
251
+ extraSections: [
252
+ {
253
+ heading: "Allowed Services",
254
+ markdown: `\`\`\`json
255
+ ${JSON.stringify(AIServices, null, 2)}
256
+ \`\`\``
257
+ }
258
+ ]
259
+ }
260
+ );
261
+ }
100
262
  function getGitDiffFilter() {
101
263
  const includeExtensions = ["*.ts", "*.tsx", "*.js", "*.jsx"];
102
264
  const excludePatterns = ignoreList.map((item) => `:(exclude)${item}`);
@@ -108,6 +270,7 @@ function getGitDiffFilter() {
108
270
  function openReport(reportPath) {
109
271
  const resolvedPath = path__default.default.resolve(reportPath);
110
272
  const { platform } = process;
273
+ helperTrace("open-report:start", resolvedPath);
111
274
  const openWithChrome = () => {
112
275
  if (platform === "darwin") {
113
276
  child_process.execSync(`open -a "Google Chrome" "${resolvedPath}"`, { stdio: "ignore" });
@@ -132,32 +295,41 @@ function openReport(reportPath) {
132
295
  };
133
296
  try {
134
297
  if (openWithChrome()) {
298
+ helperTrace("open-report:chrome:success", platform);
135
299
  console.log("\u{1F680} Google Chrome\uC5D0\uC11C \uB9AC\uD3EC\uD2B8\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4.");
136
300
  return;
137
301
  }
138
- } catch {
302
+ } catch (error) {
303
+ helperTrace("open-report:chrome:failed", getErrorSummary(error));
139
304
  }
140
305
  try {
141
306
  if (openWithDefaultBrowser()) {
307
+ helperTrace("open-report:default-browser:success", platform);
142
308
  console.log("\u{1F680} \uAE30\uBCF8 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB9AC\uD3EC\uD2B8\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4.");
143
309
  return;
144
310
  }
145
- } catch (e) {
146
- console.error("\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800 \uC5F4\uAE30 \uC2E4\uD328:", e);
311
+ } catch (error) {
312
+ helperTrace("open-report:default-browser:failed", getErrorSummary(error));
313
+ console.error("\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800 \uC5F4\uAE30 \uC2E4\uD328:", error);
147
314
  return;
148
315
  }
316
+ helperTrace("open-report:unsupported-platform", platform);
149
317
  console.error(`\u26A0\uFE0F \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD50C\uB7AB\uD3FC\uC785\uB2C8\uB2E4: ${platform}`);
150
318
  }
151
319
  function getDiffArgs() {
152
320
  const args4 = process.argv.slice(2);
153
321
  const commitIndex = args4.indexOf("--commit");
154
322
  const { includeParams, excludeParams } = getGitDiffFilter();
323
+ helperTrace("diff-args:resolve:start", `args=${JSON.stringify(args4)}`);
155
324
  let diffArgs = "";
156
325
  if (commitIndex !== -1) {
157
326
  const commitHash = args4[commitIndex + 1];
158
327
  if (!commitHash) {
159
- console.error("\u274C \uCEE4\uBC0B \uD574\uC2DC\uAC00 \uC81C\uACF5\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
160
- process.exit(1);
328
+ helperTrace("diff-args:commit-hash-missing");
329
+ exitWithError("\u274C \uCEE4\uBC0B \uD574\uC2DC\uAC00 \uC81C\uACF5\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.", {
330
+ scope: "helper:getDiffArgs",
331
+ args: args4
332
+ });
161
333
  }
162
334
  const nextArg = args4[commitIndex + 2];
163
335
  let n = 0;
@@ -167,28 +339,37 @@ function getDiffArgs() {
167
339
  n = 0;
168
340
  }
169
341
  }
342
+ helperTrace("diff-args:commit-mode", `${commitHash}~${n + 1} ${commitHash}`);
170
343
  console.log(`\u2139\uFE0F \uCEE4\uBC0B '${commitHash}' ${n > 0 ? ` \uD3EC\uD568 \uCD1D ${n + 1}\uAC1C\uC758 \uCEE4\uBC0B` : ""}\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...`);
171
344
  diffArgs = `${commitHash}~${n + 1} ${commitHash}`;
172
345
  } else {
173
346
  try {
347
+ helperTrace("diff-args:unstaged-check:start");
174
348
  const check = child_process.execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();
175
349
  if (!check.trim()) {
350
+ helperTrace("diff-args:unstaged-check:empty", "use HEAD~1 HEAD");
176
351
  console.log("\u2139\uFE0F Unstaged \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uB9C8\uC9C0\uB9C9 \uCEE4\uBC0B(HEAD)\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...");
177
352
  diffArgs = "HEAD~1 HEAD";
353
+ } else {
354
+ helperTrace("diff-args:unstaged-check:has-changes", `length=${check.length}`);
178
355
  }
179
- } catch {
356
+ } catch (error) {
357
+ helperTrace("diff-args:unstaged-check:failed", getErrorSummary(error));
180
358
  }
181
359
  }
360
+ helperTrace("diff-args:resolve:done", diffArgs || "(default)");
182
361
  return diffArgs;
183
362
  }
184
363
  async function showSelectionAIService() {
185
364
  const selectedServiceFromArgs = parseServiceFromArgs();
186
365
  if (selectedServiceFromArgs) {
366
+ helperTrace("show-selection:from-args", selectedServiceFromArgs);
187
367
  console.log(`
188
368
  \u2705 \x1B[32m${selectedServiceFromArgs}\x1B[0m \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (--service)
189
369
  `);
190
370
  return selectedServiceFromArgs;
191
371
  }
372
+ helperTrace("show-selection:interactive:start");
192
373
  let selectedIndex = 0;
193
374
  const rl = readline__default.default.createInterface({
194
375
  input: process.stdin,
@@ -202,6 +383,7 @@ async function showSelectionAIService() {
202
383
  readline__default.default.moveCursor(process.stdout, 0, -(AIServices.length + 1));
203
384
  }
204
385
  firstRender = false;
386
+ helperTrace("show-selection:interactive:render", AIServices[selectedIndex] || "unknown");
205
387
  readline__default.default.clearScreenDown(process.stdout);
206
388
  process.stdout.write(
207
389
  "\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"
@@ -221,6 +403,7 @@ async function showSelectionAIService() {
221
403
  const onData = (data) => {
222
404
  const key = data.toString();
223
405
  if (key === "") {
406
+ helperTrace("show-selection:interactive:ctrl-c");
224
407
  process.stdout.write("\x1B[?25h");
225
408
  process.exit(0);
226
409
  }
@@ -241,6 +424,7 @@ async function showSelectionAIService() {
241
424
  `);
242
425
  const result = AIServices[selectedIndex];
243
426
  if (result) {
427
+ helperTrace("show-selection:interactive:confirmed", result);
244
428
  resolve(result);
245
429
  }
246
430
  }
@@ -360,8 +544,11 @@ var createClaudeCommand = (tempDiffPath2, reviewFormPath2) => {
360
544
  trace("reviewForm:status", reviewFormExists ? "exists" : "missing");
361
545
  const systemPromptFiles = reviewFormExists ? [...existingRuleFiles, reviewFormPath2] : existingRuleFiles;
362
546
  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.";
547
+ trace("prompt:prepared", `length=${prompt.length}`);
548
+ trace("system-prompt-files", `count=${systemPromptFiles.length}`);
363
549
  const modelCandidates = toUnique(customModel ? [customModel, ...aliasFallbacks] : aliasFallbacks);
364
550
  trace("model:candidates", modelCandidates.join(", "));
551
+ trace("command:candidates:count", String(modelCandidates.length + 1));
365
552
  if (customModel) {
366
553
  console.warn(
367
554
  `\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
@@ -402,6 +589,7 @@ var createClaudeCommand = (tempDiffPath2, reviewFormPath2) => {
402
589
  \uC0DD\uC131\uB420 \uBA85\uB839\uC5B4 \uBBF8\uB9AC\uBCF4\uAE30:
403
590
  ${safeCommand}"`;
404
591
  }
592
+ trace("command:mode", "execute");
405
593
  trace("createClaudeCommand:end");
406
594
  return command;
407
595
  };
@@ -412,23 +600,25 @@ function checkClaudeCliInstalled() {
412
600
  trace2("version-check:run", "claude --version");
413
601
  child_process.execSync("claude --version", { stdio: "ignore" });
414
602
  trace2("version-check:ok");
415
- } catch {
416
- trace2("version-check:failed", "install-start");
603
+ } catch (error) {
604
+ trace2("version-check:failed", getErrorSummary(error));
605
+ trace2("install:start", "@anthropic-ai/claude-code");
417
606
  console.log(
418
607
  "\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"
419
608
  );
420
609
  try {
421
610
  child_process.execSync("npm install -g @anthropic-ai/claude-code", { stdio: "inherit" });
422
- trace2("install:ok", "exit(1) for login");
611
+ trace2("install:ok", "login-required");
423
612
  console.log("\u2705 claude-cli \uC124\uCE58\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
424
613
  console.log("\u26A0\uFE0F claude-cli \uC0AC\uC6A9\uC744 \uC704\uD574 \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
425
614
  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.');
426
615
  process.exit(1);
427
616
  } catch (installError) {
428
- trace2("install:failed");
429
- 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).");
430
- console.error(installError);
431
- process.exit(1);
617
+ trace2("install:failed", getErrorSummary(installError));
618
+ 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).", {
619
+ scope: "installation-claude",
620
+ error: installError
621
+ });
432
622
  }
433
623
  }
434
624
  trace2("checkClaudeCliInstalled:end");
@@ -494,6 +684,7 @@ ${reviewFormLine || "- (\uC5C6\uC74C)"}
494
684
  - ${tempDiffPath2}
495
685
 
496
686
  \uBC18\uB4DC\uC2DC \uB9AC\uBDF0 \uC591\uC2DD\uC5D0 \uB9DE\uCDB0 \uC791\uC131\uD574\uC918.`;
687
+ trace3("prompt:prepared", `length=${prompt.length}`);
497
688
  let command = "";
498
689
  if (customModel) {
499
690
  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.");
@@ -519,6 +710,7 @@ ${reviewFormLine || "- (\uC5C6\uC74C)"}
519
710
  \uC0DD\uC131\uB420 \uBA85\uB839\uC5B4 \uBBF8\uB9AC\uBCF4\uAE30:
520
711
  ${safeCommand}"`;
521
712
  }
713
+ trace3("command:mode", "execute");
522
714
  trace3("createCodexCommand:end");
523
715
  return command;
524
716
  };
@@ -529,21 +721,23 @@ function checkCodexCliInstalled() {
529
721
  trace4("version-check:run", "codex --version");
530
722
  child_process.execSync("codex --version", { stdio: "ignore" });
531
723
  trace4("version-check:ok");
532
- } catch {
533
- trace4("version-check:failed", "install-start");
724
+ } catch (error) {
725
+ trace4("version-check:failed", getErrorSummary(error));
726
+ trace4("install:start", "@openai/codex");
534
727
  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");
535
728
  try {
536
729
  child_process.execSync("npm install -g @openai/codex", { stdio: "inherit" });
537
- trace4("install:ok", "exit(1) for login");
730
+ trace4("install:ok", "login-required");
538
731
  console.log("\u2705 codex-cli \uC124\uCE58\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
539
732
  console.log("\u26A0\uFE0F codex-cli \uC0AC\uC6A9\uC744 \uC704\uD574 \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
540
733
  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.');
541
734
  process.exit(1);
542
735
  } catch (installError) {
543
- trace4("install:failed");
544
- 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).");
545
- console.error(installError);
546
- process.exit(1);
736
+ trace4("install:failed", getErrorSummary(installError));
737
+ 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).", {
738
+ scope: "installation-codex",
739
+ error: installError
740
+ });
547
741
  }
548
742
  }
549
743
  trace4("checkCodexCliInstalled:end");
@@ -660,8 +854,10 @@ var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
660
854
  const prompt = `\uB2E4\uC74C \uADDC\uCE59\uB4E4\uC744 \uCC38\uACE0\uD574\uC11C(${validRules || "(\uC5C6\uC74C)"}) \uC774 diff(@${tempDiffPath2})\uB97C \uB9AC\uBDF0\uD574\uC918.
661
855
  \uB9AC\uBDF0 \uC591\uC2DD\uC740 ${reviewFormRef} \uC5D0 \uB9DE\uCDB0\uC11C \uC791\uC131\uD574\uC918.
662
856
  \uCD94\uB860 \uAC15\uB3C4 \uC9C0\uCE68: ${reasoningInstruction}`;
857
+ trace5("prompt:prepared", `length=${prompt.length}`);
663
858
  const modelCandidates = toUnique2(customModel ? [customModel, ...aliasFallbacks] : aliasFallbacks);
664
859
  trace5("model:candidates", modelCandidates.join(", "));
860
+ trace5("command:candidates:count", String(modelCandidates.length + 1));
665
861
  if (customModel) {
666
862
  console.warn(
667
863
  `\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
@@ -684,6 +880,7 @@ var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
684
880
  \uC0DD\uC131\uB420 \uBA85\uB839\uC5B4 \uBBF8\uB9AC\uBCF4\uAE30:
685
881
  ${safeCommand}"`;
686
882
  }
883
+ trace5("command:mode", "execute");
687
884
  trace5("createGeminiCommand:end");
688
885
  return command;
689
886
  };
@@ -694,21 +891,23 @@ function checkGeminiCliInstalled() {
694
891
  trace6("version-check:run", "gemini --version");
695
892
  child_process.execSync("gemini --version", { stdio: "ignore" });
696
893
  trace6("version-check:ok");
697
- } catch {
698
- trace6("version-check:failed", "install-start");
894
+ } catch (error) {
895
+ trace6("version-check:failed", getErrorSummary(error));
896
+ trace6("install:start", "@google/gemini-cli");
699
897
  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");
700
898
  try {
701
899
  child_process.execSync("npm install -g @google/gemini-cli", { stdio: "inherit" });
702
- trace6("install:ok", "exit(1) for login");
900
+ trace6("install:ok", "login-required");
703
901
  console.log("\u2705 gemini-cli \uC124\uCE58\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
704
902
  console.log("\u26A0\uFE0F Gemini API \uC0AC\uC6A9\uC744 \uC704\uD574 \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
705
903
  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.');
706
904
  process.exit(1);
707
905
  } catch (installError) {
708
- trace6("install:failed");
709
- 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).");
710
- console.error(installError);
711
- process.exit(1);
906
+ trace6("install:failed", getErrorSummary(installError));
907
+ 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).", {
908
+ scope: "installation-gemini",
909
+ error: installError
910
+ });
712
911
  }
713
912
  }
714
913
  trace6("checkGeminiCliInstalled:end");
@@ -717,30 +916,35 @@ function checkGeminiCliInstalled() {
717
916
  // src/pr-review/review.ts
718
917
  async function main() {
719
918
  const args4 = process.argv.slice(2);
919
+ clearTraceMessages();
720
920
  const isTest = isTestMode(args4);
721
921
  const trace7 = createTraceLogger("review", args4);
722
922
  trace7("main:start", `args=${JSON.stringify(args4)}`);
723
- trace7("service-selection:start");
724
- const service = await showSelectionAIService();
725
- trace7("service-selection:done", `service=${service}`);
726
- switch (service) {
727
- case "gemini":
728
- trace7("install-check:start", "service=gemini");
729
- checkGeminiCliInstalled();
730
- trace7("install-check:done", "service=gemini");
731
- break;
732
- case "claude":
733
- trace7("install-check:start", "service=claude");
734
- checkClaudeCliInstalled();
735
- trace7("install-check:done", "service=claude");
736
- break;
737
- case "codex":
738
- trace7("install-check:start", "service=codex");
739
- checkCodexCliInstalled();
740
- trace7("install-check:done", "service=codex");
741
- break;
742
- }
923
+ let command = "";
924
+ let savedDiffPath = "";
925
+ let savedReportPath = "";
926
+ let service = "";
743
927
  try {
928
+ trace7("service-selection:start");
929
+ service = await showSelectionAIService();
930
+ trace7("service-selection:done", `service=${service}`);
931
+ switch (service) {
932
+ case "gemini":
933
+ trace7("install-check:start", "service=gemini");
934
+ checkGeminiCliInstalled();
935
+ trace7("install-check:done", "service=gemini");
936
+ break;
937
+ case "claude":
938
+ trace7("install-check:start", "service=claude");
939
+ checkClaudeCliInstalled();
940
+ trace7("install-check:done", "service=claude");
941
+ break;
942
+ case "codex":
943
+ trace7("install-check:start", "service=codex");
944
+ checkCodexCliInstalled();
945
+ trace7("install-check:done", "service=codex");
946
+ break;
947
+ }
744
948
  trace7("review-flow:start");
745
949
  console.log("\u{1F680} AI Code Review\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...");
746
950
  const nowStr = getNowString();
@@ -758,8 +962,8 @@ async function main() {
758
962
  trace7("git-diff:run");
759
963
  diff = child_process.execSync(`git diff ${diffArgs} -- ${includeParams} ${excludeParams}`).toString();
760
964
  trace7("git-diff:done", `length=${diff.length}`);
761
- } catch {
762
- trace7("git-diff:error", "fallback-empty-diff");
965
+ } catch (error) {
966
+ trace7("git-diff:error", getErrorSummary(error));
763
967
  }
764
968
  if (!diff.trim() && !isTest) {
765
969
  trace7("empty-diff:exit");
@@ -771,10 +975,9 @@ async function main() {
771
975
  fs__default.default.writeFileSync(tempDiffPath, diff);
772
976
  trace7("temp-diff:write:done");
773
977
  trace7("saved-diff:copy:start");
774
- const savedDiffPath = getNextFilePath(REPORT_DIR, `${nowStr}-diff`, ".txt");
978
+ savedDiffPath = getNextFilePath(REPORT_DIR, `${nowStr}-diff`, ".txt");
775
979
  fs__default.default.copyFileSync(tempDiffPath, savedDiffPath);
776
980
  trace7("saved-diff:copy:done", savedDiffPath);
777
- let command = "";
778
981
  trace7("command:create:start", `service=${service}`);
779
982
  switch (service) {
780
983
  case "gemini":
@@ -793,7 +996,7 @@ async function main() {
793
996
  trace7("command:exec:done", `resultLength=${result.length}`);
794
997
  console.log(result);
795
998
  trace7("report:write:start");
796
- const savedReportPath = getNextFilePath(REPORT_DIR, nowStr, ".md");
999
+ savedReportPath = getNextFilePath(REPORT_DIR, nowStr, ".md");
797
1000
  fs__default.default.writeFileSync(savedReportPath, result);
798
1001
  trace7("report:write:done", savedReportPath);
799
1002
  if (isTest) {
@@ -817,12 +1020,45 @@ ${command}`);
817
1020
  trace7("cleanup-temp-diff:done");
818
1021
  trace7("review-flow:end");
819
1022
  } catch (error) {
820
- trace7("review-flow:catch");
1023
+ trace7("review-flow:catch", getErrorSummary(error));
1024
+ let errorReportPath = "";
1025
+ trace7("cleanup-temp-diff:start(catch)");
1026
+ try {
1027
+ deleteTempDiff();
1028
+ trace7("cleanup-temp-diff:done(catch)");
1029
+ } catch (cleanupError) {
1030
+ trace7("cleanup-temp-diff:failed(catch)", getErrorSummary(cleanupError));
1031
+ console.error("\u26A0\uFE0F \uC784\uC2DC diff \uC815\uB9AC \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.");
1032
+ console.error(cleanupError);
1033
+ }
1034
+ trace7("error-report:prepare", `service=${service}`);
1035
+ errorReportPath = writeErrorReport(error, {
1036
+ scope: "review",
1037
+ args: args4,
1038
+ extraSections: [
1039
+ {
1040
+ heading: "Execution Context",
1041
+ markdown: `\`\`\`json
1042
+ ${JSON.stringify(
1043
+ {
1044
+ service,
1045
+ command: command || null,
1046
+ tempDiffPath,
1047
+ savedDiffPath: savedDiffPath || null,
1048
+ savedReportPath: savedReportPath || null
1049
+ },
1050
+ null,
1051
+ 2
1052
+ )}
1053
+ \`\`\``
1054
+ }
1055
+ ]
1056
+ });
821
1057
  console.error("\u274C \uB9AC\uBDF0 \uB3C4\uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.");
822
1058
  console.error(error);
823
- trace7("cleanup-temp-diff:start(catch)");
824
- deleteTempDiff();
825
- trace7("cleanup-temp-diff:done(catch)");
1059
+ if (errorReportPath) {
1060
+ console.error(`\u{1F4C4} \uC5D0\uB7EC \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${errorReportPath}`);
1061
+ }
826
1062
  process.exit(1);
827
1063
  }
828
1064
  }