sales-frontend-gemini-cli 0.4.0 → 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 (45) hide show
  1. package/dist/common/helper.cjs +234 -20
  2. package/dist/common/helper.cjs.map +1 -1
  3. package/dist/common/helper.d.cts +23 -3
  4. package/dist/common/helper.d.ts +23 -3
  5. package/dist/common/helper.js +228 -21
  6. package/dist/common/helper.js.map +1 -1
  7. package/dist/pr-review/claude/claude-commander.cjs +142 -28
  8. package/dist/pr-review/claude/claude-commander.cjs.map +1 -1
  9. package/dist/pr-review/claude/claude-commander.d.cts +7 -8
  10. package/dist/pr-review/claude/claude-commander.d.ts +7 -8
  11. package/dist/pr-review/claude/claude-commander.js +142 -28
  12. package/dist/pr-review/claude/claude-commander.js.map +1 -1
  13. package/dist/pr-review/claude/installation-claude.cjs +178 -8
  14. package/dist/pr-review/claude/installation-claude.cjs.map +1 -1
  15. package/dist/pr-review/claude/installation-claude.js +177 -8
  16. package/dist/pr-review/claude/installation-claude.js.map +1 -1
  17. package/dist/pr-review/codex/codex-commander.cjs +64 -21
  18. package/dist/pr-review/codex/codex-commander.cjs.map +1 -1
  19. package/dist/pr-review/codex/codex-commander.d.cts +8 -3
  20. package/dist/pr-review/codex/codex-commander.d.ts +8 -3
  21. package/dist/pr-review/codex/codex-commander.js +64 -21
  22. package/dist/pr-review/codex/codex-commander.js.map +1 -1
  23. package/dist/pr-review/codex/installation-codex.cjs +178 -8
  24. package/dist/pr-review/codex/installation-codex.cjs.map +1 -1
  25. package/dist/pr-review/codex/installation-codex.js +177 -8
  26. package/dist/pr-review/codex/installation-codex.js.map +1 -1
  27. package/dist/pr-review/gemini/gemini-commander.cjs +122 -21
  28. package/dist/pr-review/gemini/gemini-commander.cjs.map +1 -1
  29. package/dist/pr-review/gemini/gemini-commander.d.cts +10 -13
  30. package/dist/pr-review/gemini/gemini-commander.d.ts +10 -13
  31. package/dist/pr-review/gemini/gemini-commander.js +122 -21
  32. package/dist/pr-review/gemini/gemini-commander.js.map +1 -1
  33. package/dist/pr-review/gemini/installation-gemini.cjs +178 -8
  34. package/dist/pr-review/gemini/installation-gemini.cjs.map +1 -1
  35. package/dist/pr-review/gemini/installation-gemini.js +177 -8
  36. package/dist/pr-review/gemini/installation-gemini.js.map +1 -1
  37. package/dist/pr-review/review-one-by-one.cjs +679 -157
  38. package/dist/pr-review/review-one-by-one.cjs.map +1 -1
  39. package/dist/pr-review/review-one-by-one.js +679 -157
  40. package/dist/pr-review/review-one-by-one.js.map +1 -1
  41. package/dist/pr-review/review.cjs +630 -132
  42. package/dist/pr-review/review.cjs.map +1 -1
  43. package/dist/pr-review/review.js +630 -132
  44. package/dist/pr-review/review.js.map +1 -1
  45. package/package.json +1 -1
@@ -4,8 +4,10 @@ import fs from 'fs';
4
4
  import path from 'path';
5
5
  import readline from 'readline';
6
6
  import { fileURLToPath } from 'url';
7
+ import { inspect } from 'util';
7
8
 
8
9
  var __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+ var traceMessages = [];
9
11
  var rulesPath = path.resolve(__dirname, "../../src/common/rules/review-rules.md");
10
12
  var namingRulesPath = path.resolve(__dirname, "../../src/common/rules/naming-rule.md");
11
13
  var codingConventionRulesPath = path.resolve(__dirname, "../../src/common/rules/coding-convention.md");
@@ -31,15 +33,94 @@ var ignoreList = [
31
33
  function isTestMode(args4 = process.argv.slice(2)) {
32
34
  return args4.includes("--test");
33
35
  }
36
+ function clearTraceMessages() {
37
+ traceMessages.length = 0;
38
+ }
39
+ function getTraceMessages() {
40
+ return [...traceMessages];
41
+ }
34
42
  function createTraceLogger(scope, args4 = process.argv.slice(2)) {
35
43
  const enabled = isTestMode(args4);
36
44
  return (step, detail) => {
45
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
46
+ const message = `[${timestamp}][TRACE][${scope}] ${step}${detail ? ` | ${detail}` : ""}`;
47
+ traceMessages.push(message);
37
48
  if (!enabled) {
38
49
  return;
39
50
  }
40
- 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")
41
63
  };
42
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
+ }
43
124
  function getNextFilePath(dir, baseName, extension) {
44
125
  let counter = 1;
45
126
  while (true) {
@@ -50,6 +131,13 @@ function getNextFilePath(dir, baseName, extension) {
50
131
  counter++;
51
132
  }
52
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
+ }
53
141
  function deleteFile(filePath) {
54
142
  if (fs.existsSync(filePath)) {
55
143
  fs.unlinkSync(filePath);
@@ -63,16 +151,105 @@ function createReportDirectory() {
63
151
  fs.mkdirSync(REPORT_DIR, { recursive: true });
64
152
  }
65
153
  }
66
- function getNowString() {
67
- const now = /* @__PURE__ */ new Date();
68
- const YYYY = now.getFullYear();
69
- const MM = String(now.getMonth() + 1).padStart(2, "0");
70
- const DD = String(now.getDate()).padStart(2, "0");
71
- const HH = String(now.getHours()).padStart(2, "0");
72
- const mm = String(now.getMinutes()).padStart(2, "0");
73
- const ss = String(now.getSeconds()).padStart(2, "0");
154
+ function getNowString(now = /* @__PURE__ */ new Date()) {
155
+ const { YYYY, MM, DD, HH, mm, ss } = getTimestampParts(now);
74
156
  return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;
75
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
+ }
76
253
  function getGitDiffFilter() {
77
254
  const includeExtensions = ["*.ts", "*.tsx", "*.js", "*.jsx"];
78
255
  const excludePatterns = ignoreList.map((item) => `:(exclude)${item}`);
@@ -84,6 +261,7 @@ function getGitDiffFilter() {
84
261
  function openReport(reportPath) {
85
262
  const resolvedPath = path.resolve(reportPath);
86
263
  const { platform } = process;
264
+ helperTrace("open-report:start", resolvedPath);
87
265
  const openWithChrome = () => {
88
266
  if (platform === "darwin") {
89
267
  execSync(`open -a "Google Chrome" "${resolvedPath}"`, { stdio: "ignore" });
@@ -108,32 +286,41 @@ function openReport(reportPath) {
108
286
  };
109
287
  try {
110
288
  if (openWithChrome()) {
289
+ helperTrace("open-report:chrome:success", platform);
111
290
  console.log("\u{1F680} Google Chrome\uC5D0\uC11C \uB9AC\uD3EC\uD2B8\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4.");
112
291
  return;
113
292
  }
114
- } catch {
293
+ } catch (error) {
294
+ helperTrace("open-report:chrome:failed", getErrorSummary(error));
115
295
  }
116
296
  try {
117
297
  if (openWithDefaultBrowser()) {
298
+ helperTrace("open-report:default-browser:success", platform);
118
299
  console.log("\u{1F680} \uAE30\uBCF8 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB9AC\uD3EC\uD2B8\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4.");
119
300
  return;
120
301
  }
121
- } catch (e) {
122
- 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);
123
305
  return;
124
306
  }
307
+ helperTrace("open-report:unsupported-platform", platform);
125
308
  console.error(`\u26A0\uFE0F \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD50C\uB7AB\uD3FC\uC785\uB2C8\uB2E4: ${platform}`);
126
309
  }
127
310
  function getDiffArgs() {
128
311
  const args4 = process.argv.slice(2);
129
312
  const commitIndex = args4.indexOf("--commit");
130
313
  const { includeParams, excludeParams } = getGitDiffFilter();
314
+ helperTrace("diff-args:resolve:start", `args=${JSON.stringify(args4)}`);
131
315
  let diffArgs = "";
132
316
  if (commitIndex !== -1) {
133
317
  const commitHash = args4[commitIndex + 1];
134
318
  if (!commitHash) {
135
- console.error("\u274C \uCEE4\uBC0B \uD574\uC2DC\uAC00 \uC81C\uACF5\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
136
- 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
+ });
137
324
  }
138
325
  const nextArg = args4[commitIndex + 2];
139
326
  let n = 0;
@@ -143,21 +330,37 @@ function getDiffArgs() {
143
330
  n = 0;
144
331
  }
145
332
  }
333
+ helperTrace("diff-args:commit-mode", `${commitHash}~${n + 1} ${commitHash}`);
146
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...`);
147
335
  diffArgs = `${commitHash}~${n + 1} ${commitHash}`;
148
336
  } else {
149
337
  try {
338
+ helperTrace("diff-args:unstaged-check:start");
150
339
  const check = execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();
151
340
  if (!check.trim()) {
341
+ helperTrace("diff-args:unstaged-check:empty", "use HEAD~1 HEAD");
152
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...");
153
343
  diffArgs = "HEAD~1 HEAD";
344
+ } else {
345
+ helperTrace("diff-args:unstaged-check:has-changes", `length=${check.length}`);
154
346
  }
155
- } catch {
347
+ } catch (error) {
348
+ helperTrace("diff-args:unstaged-check:failed", getErrorSummary(error));
156
349
  }
157
350
  }
351
+ helperTrace("diff-args:resolve:done", diffArgs || "(default)");
158
352
  return diffArgs;
159
353
  }
160
354
  async function showSelectionAIService() {
355
+ const selectedServiceFromArgs = parseServiceFromArgs();
356
+ if (selectedServiceFromArgs) {
357
+ helperTrace("show-selection:from-args", selectedServiceFromArgs);
358
+ console.log(`
359
+ \u2705 \x1B[32m${selectedServiceFromArgs}\x1B[0m \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (--service)
360
+ `);
361
+ return selectedServiceFromArgs;
362
+ }
363
+ helperTrace("show-selection:interactive:start");
161
364
  let selectedIndex = 0;
162
365
  const rl = readline.createInterface({
163
366
  input: process.stdin,
@@ -171,6 +374,7 @@ async function showSelectionAIService() {
171
374
  readline.moveCursor(process.stdout, 0, -(AIServices.length + 1));
172
375
  }
173
376
  firstRender = false;
377
+ helperTrace("show-selection:interactive:render", AIServices[selectedIndex] || "unknown");
174
378
  readline.clearScreenDown(process.stdout);
175
379
  process.stdout.write(
176
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"
@@ -190,6 +394,7 @@ async function showSelectionAIService() {
190
394
  const onData = (data) => {
191
395
  const key = data.toString();
192
396
  if (key === "") {
397
+ helperTrace("show-selection:interactive:ctrl-c");
193
398
  process.stdout.write("\x1B[?25h");
194
399
  process.exit(0);
195
400
  }
@@ -210,6 +415,7 @@ async function showSelectionAIService() {
210
415
  `);
211
416
  const result = AIServices[selectedIndex];
212
417
  if (result) {
418
+ helperTrace("show-selection:interactive:confirmed", result);
213
419
  resolve(result);
214
420
  }
215
421
  }
@@ -221,40 +427,150 @@ async function showSelectionAIService() {
221
427
  }
222
428
  var args = process.argv.slice(2);
223
429
  var trace = createTraceLogger("claude-commander", args);
224
- var createClaudeCommand = (tempDiffPath2, reviewFormPath2) => {
225
- trace("createClaudeCommand:start", `tempDiffPath=${tempDiffPath2}, reviewFormPath=${reviewFormPath2}`);
226
- let modelOption = "";
227
- if (args.includes("--review")) {
228
- modelOption = "--model opus";
229
- trace("model:review", modelOption);
230
- } else if (args.includes("--flash")) {
231
- modelOption = "--model haiku";
232
- trace("model:flash", modelOption);
233
- } else {
234
- const modelIndex = args.indexOf("--model");
235
- if (modelIndex !== -1 && args[modelIndex + 1]) {
236
- 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.");
237
- modelOption = `--model ${args[modelIndex + 1]}`;
238
- trace("model:custom", modelOption);
239
- } else {
240
- console.warn("\u26A0\uFE0F \uBAA8\uB378\uC774 \uC9C0\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uAE30\uBCF8 \uBAA8\uB378\uC778 sonnet\uC744 \uC0AC\uC6A9\uD569\uB2C8\uB2E4.");
241
- modelOption = "--model sonnet";
242
- trace("model:default", modelOption);
430
+ var ALLOWED_REASONING_EFFORTS = ["minimal", "low", "medium", "high"];
431
+ function shellQuote(value) {
432
+ return `'${value.replace(/'/g, `'\\''`)}'`;
433
+ }
434
+ function getArgValue(flag) {
435
+ const index = args.indexOf(flag);
436
+ if (index === -1 || !args[index + 1]) {
437
+ return "";
438
+ }
439
+ return args[index + 1];
440
+ }
441
+ function toUnique(values) {
442
+ const seen = /* @__PURE__ */ new Set();
443
+ return values.filter((value) => {
444
+ if (!value || seen.has(value)) {
445
+ return false;
243
446
  }
447
+ seen.add(value);
448
+ return true;
449
+ });
450
+ }
451
+ function normalizeEffort(level) {
452
+ if (level === "minimal") {
453
+ return "low";
244
454
  }
455
+ return level;
456
+ }
457
+ function resolveReasoningEffort() {
458
+ const customReasoningEffort = getArgValue("--reasoning-effort") || getArgValue("--effort");
459
+ if (customReasoningEffort) {
460
+ if (ALLOWED_REASONING_EFFORTS.includes(customReasoningEffort)) {
461
+ const normalized = normalizeEffort(customReasoningEffort);
462
+ trace("reasoning:custom", `${customReasoningEffort} -> ${normalized}`);
463
+ if (customReasoningEffort === "minimal") {
464
+ console.warn("\u26A0\uFE0F Claude\uB294 minimal\uC744 \uC9C1\uC811 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC544 low\uB85C \uB9E4\uD551\uD569\uB2C8\uB2E4.");
465
+ }
466
+ return normalized;
467
+ }
468
+ console.warn(
469
+ `\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS.join(
470
+ ", "
471
+ )}`
472
+ );
473
+ }
474
+ if (args.includes("--flash")) {
475
+ trace("reasoning:flash-default", "low");
476
+ return "low";
477
+ }
478
+ if (args.includes("--review")) {
479
+ trace("reasoning:review-default", "high");
480
+ return "high";
481
+ }
482
+ trace("reasoning:default", "medium");
483
+ return "medium";
484
+ }
485
+ function resolvePrimaryAlias() {
486
+ if (args.includes("--review")) {
487
+ trace("model:mode-alias", "opus");
488
+ return "opus";
489
+ }
490
+ if (args.includes("--flash")) {
491
+ trace("model:mode-alias", "haiku");
492
+ return "haiku";
493
+ }
494
+ trace("model:default-alias", "sonnet");
495
+ return "sonnet";
496
+ }
497
+ function getAliasFallbacks(primaryAlias) {
498
+ if (primaryAlias === "opus") {
499
+ return ["opus", "sonnet", "haiku"];
500
+ }
501
+ if (primaryAlias === "haiku") {
502
+ return ["haiku", "sonnet"];
503
+ }
504
+ return [primaryAlias, "sonnet", "haiku"];
505
+ }
506
+ function buildClaudeExecCommand(options) {
507
+ const { tempDiffPath: tempDiffPath2, prompt, systemPromptFiles, effort, model, fallbackModel } = options;
508
+ const modelOption = model ? `--model ${shellQuote(model)}` : "";
509
+ const fallbackOption = model && fallbackModel ? `--fallback-model ${shellQuote(fallbackModel)}` : "";
510
+ const effortOption = `--effort ${shellQuote(effort)}`;
511
+ const appendedPromptFiles = systemPromptFiles.map((path2) => `--append-system-prompt-file ${shellQuote(path2)}`).join(" ");
512
+ return `cat ${shellQuote(tempDiffPath2)} | claude ${[
513
+ modelOption,
514
+ fallbackOption,
515
+ effortOption,
516
+ appendedPromptFiles,
517
+ "-p",
518
+ shellQuote(prompt)
519
+ ].filter(Boolean).join(" ")}`;
520
+ }
521
+ var createClaudeCommand = (tempDiffPath2, reviewFormPath2) => {
522
+ trace("createClaudeCommand:start", `tempDiffPath=${tempDiffPath2}, reviewFormPath=${reviewFormPath2}`);
523
+ const customModel = getArgValue("--model");
524
+ const effort = resolveReasoningEffort();
525
+ const primaryAlias = resolvePrimaryAlias();
526
+ const aliasFallbacks = toUnique(getAliasFallbacks(primaryAlias));
245
527
  const rules = [
246
528
  { path: rulesPath, display: "\uB8F0\uC14B" },
247
529
  { path: namingRulesPath, display: "\uB124\uC774\uBC0D \uADDC\uCE59" },
248
530
  { path: codingConventionRulesPath, display: "\uCF54\uB529 \uCEE8\uBCA4\uC158" }
249
531
  ];
250
- const systemPromptFiles = rules.filter((rule) => fs.existsSync(rule.path)).map((rule) => `--append-system-prompt-file ${rule.path}`).join(" ");
251
- trace(
252
- "rules:loaded",
253
- `count=${systemPromptFiles ? systemPromptFiles.split("--append-system-prompt-file").length - 1 : 0}`
254
- );
255
- const reviewFormOption = fs.existsSync(reviewFormPath2) ? `--append-system-prompt-file ${reviewFormPath2}` : "";
256
- trace("reviewForm:status", reviewFormOption ? "exists" : "missing");
257
- const command = `cat ${tempDiffPath2} | claude ${modelOption} ${systemPromptFiles} ${reviewFormOption} -p "\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."`;
532
+ const existingRuleFiles = rules.filter((rule) => fs.existsSync(rule.path)).map((rule) => rule.path);
533
+ trace("rules:loaded", `count=${existingRuleFiles.length}`);
534
+ const reviewFormExists = fs.existsSync(reviewFormPath2);
535
+ trace("reviewForm:status", reviewFormExists ? "exists" : "missing");
536
+ const systemPromptFiles = reviewFormExists ? [...existingRuleFiles, reviewFormPath2] : existingRuleFiles;
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}`);
540
+ const modelCandidates = toUnique(customModel ? [customModel, ...aliasFallbacks] : aliasFallbacks);
541
+ trace("model:candidates", modelCandidates.join(", "));
542
+ trace("command:candidates:count", String(modelCandidates.length + 1));
543
+ if (customModel) {
544
+ console.warn(
545
+ `\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
546
+ " -> "
547
+ )}) \uBC0F \uAE30\uBCF8 \uBAA8\uB378 \uC21C\uC73C\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
548
+ );
549
+ } else {
550
+ console.warn(
551
+ `\u26A0\uFE0F \uBAA8\uB378\uC774 \uC9C0\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. alias(${aliasFallbacks.join(" -> ")})\uB97C \uC21C\uCC28 \uC2DC\uB3C4\uD558\uACE0 \uB9C8\uC9C0\uB9C9\uC5D0 \uAE30\uBCF8 \uBAA8\uB378\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
552
+ );
553
+ }
554
+ const commandCandidates = modelCandidates.map((model, index) => {
555
+ const fallbackModel = modelCandidates[index + 1];
556
+ return buildClaudeExecCommand({
557
+ tempDiffPath: tempDiffPath2,
558
+ prompt,
559
+ systemPromptFiles,
560
+ effort,
561
+ model,
562
+ fallbackModel
563
+ });
564
+ });
565
+ const command = [
566
+ ...commandCandidates,
567
+ buildClaudeExecCommand({
568
+ tempDiffPath: tempDiffPath2,
569
+ prompt,
570
+ systemPromptFiles,
571
+ effort
572
+ })
573
+ ].join(" || ");
258
574
  trace("command:created");
259
575
  if (args.includes("--test")) {
260
576
  const safeCommand = command.replace(/"/g, '\\"');
@@ -264,6 +580,7 @@ var createClaudeCommand = (tempDiffPath2, reviewFormPath2) => {
264
580
  \uC0DD\uC131\uB420 \uBA85\uB839\uC5B4 \uBBF8\uB9AC\uBCF4\uAE30:
265
581
  ${safeCommand}"`;
266
582
  }
583
+ trace("command:mode", "execute");
267
584
  trace("createClaudeCommand:end");
268
585
  return command;
269
586
  };
@@ -274,50 +591,75 @@ function checkClaudeCliInstalled() {
274
591
  trace2("version-check:run", "claude --version");
275
592
  execSync("claude --version", { stdio: "ignore" });
276
593
  trace2("version-check:ok");
277
- } catch {
278
- trace2("version-check:failed", "install-start");
594
+ } catch (error) {
595
+ trace2("version-check:failed", getErrorSummary(error));
596
+ trace2("install:start", "@anthropic-ai/claude-code");
279
597
  console.log(
280
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"
281
599
  );
282
600
  try {
283
601
  execSync("npm install -g @anthropic-ai/claude-code", { stdio: "inherit" });
284
- trace2("install:ok", "exit(1) for login");
602
+ trace2("install:ok", "login-required");
285
603
  console.log("\u2705 claude-cli \uC124\uCE58\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
286
604
  console.log("\u26A0\uFE0F claude-cli \uC0AC\uC6A9\uC744 \uC704\uD574 \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
287
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.');
288
606
  process.exit(1);
289
607
  } catch (installError) {
290
- trace2("install:failed");
291
- 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).");
292
- console.error(installError);
293
- 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
+ });
294
613
  }
295
614
  }
296
615
  trace2("checkClaudeCliInstalled:end");
297
616
  }
298
617
  var args2 = process.argv.slice(2);
299
618
  var trace3 = createTraceLogger("codex-commander", args2);
300
- var createCodexCommand = (tempDiffPath2, reviewFormPath2) => {
301
- trace3("createCodexCommand:start", `tempDiffPath=${tempDiffPath2}, reviewFormPath=${reviewFormPath2}`);
302
- let modelOption = "";
303
- if (args2.includes("--review")) {
304
- modelOption = "--model gpt-5";
305
- trace3("model:review", modelOption);
306
- } else if (args2.includes("--flash")) {
307
- modelOption = "--model gpt-5-mini";
308
- trace3("model:flash", modelOption);
309
- } else {
310
- const modelIndex = args2.indexOf("--model");
311
- if (modelIndex !== -1 && args2[modelIndex + 1]) {
312
- 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.");
313
- modelOption = `--model ${args2[modelIndex + 1]}`;
314
- trace3("model:custom", modelOption);
315
- } else {
316
- console.warn("\u26A0\uFE0F \uBAA8\uB378\uC774 \uC9C0\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uAE30\uBCF8 \uBAA8\uB378\uC778 gpt-5-mini\uB97C \uC0AC\uC6A9\uD569\uB2C8\uB2E4.");
317
- modelOption = "--model gpt-5-mini";
318
- trace3("model:default", modelOption);
619
+ var ALLOWED_REASONING_EFFORTS2 = ["minimal", "low", "medium", "high"];
620
+ function shellQuote2(value) {
621
+ return `'${value.replace(/'/g, `'\\''`)}'`;
622
+ }
623
+ function getArgValue2(flag) {
624
+ const index = args2.indexOf(flag);
625
+ if (index === -1 || !args2[index + 1]) {
626
+ return "";
627
+ }
628
+ return args2[index + 1];
629
+ }
630
+ function resolveReasoningEffort2() {
631
+ const customReasoningEffort = getArgValue2("--reasoning-effort");
632
+ if (customReasoningEffort) {
633
+ if (ALLOWED_REASONING_EFFORTS2.includes(customReasoningEffort)) {
634
+ trace3("reasoning:custom", customReasoningEffort);
635
+ return customReasoningEffort;
319
636
  }
637
+ console.warn(
638
+ `\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS2.join(
639
+ ", "
640
+ )}`
641
+ );
320
642
  }
643
+ if (args2.includes("--flash")) {
644
+ trace3("reasoning:flash-default", "minimal");
645
+ return "minimal";
646
+ }
647
+ if (args2.includes("--review")) {
648
+ trace3("reasoning:review-default", "high");
649
+ return "high";
650
+ }
651
+ trace3("reasoning:default", "medium");
652
+ return "medium";
653
+ }
654
+ function buildCodexExecCommand(prompt, reasoningEffort, model) {
655
+ const modelOption = model ? `--model ${shellQuote2(model)}` : "";
656
+ const reasoningOption = `-c ${shellQuote2(`model_reasoning_effort="${reasoningEffort}"`)}`;
657
+ return `codex exec ${[modelOption, reasoningOption, shellQuote2(prompt)].filter(Boolean).join(" ")}`;
658
+ }
659
+ var createCodexCommand = (tempDiffPath2, reviewFormPath2) => {
660
+ trace3("createCodexCommand:start", `tempDiffPath=${tempDiffPath2}, reviewFormPath=${reviewFormPath2}`);
661
+ const customModel = getArgValue2("--model");
662
+ const reasoningEffort = resolveReasoningEffort2();
321
663
  const rules = [rulesPath, namingRulesPath, codingConventionRulesPath].filter((filePath) => fs.existsSync(filePath)).map((filePath) => `- ${filePath}`).join("\n");
322
664
  const rulesCount = rules ? rules.split("\n").length : 0;
323
665
  trace3("rules:loaded", `count=${rulesCount}`);
@@ -333,7 +675,23 @@ ${reviewFormLine || "- (\uC5C6\uC74C)"}
333
675
  - ${tempDiffPath2}
334
676
 
335
677
  \uBC18\uB4DC\uC2DC \uB9AC\uBDF0 \uC591\uC2DD\uC5D0 \uB9DE\uCDB0 \uC791\uC131\uD574\uC918.`;
336
- const command = `codex exec ${modelOption} "${prompt.replace(/"/g, '\\"')}"`;
678
+ trace3("prompt:prepared", `length=${prompt.length}`);
679
+ let command = "";
680
+ if (customModel) {
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.");
682
+ trace3("model:custom", customModel);
683
+ command = buildCodexExecCommand(prompt, reasoningEffort, customModel);
684
+ } else {
685
+ const preferredModelAlias = "gpt-5";
686
+ const aliasCommand = buildCodexExecCommand(prompt, reasoningEffort, preferredModelAlias);
687
+ const fallbackCommand = buildCodexExecCommand(prompt, reasoningEffort);
688
+ console.warn(
689
+ `\u26A0\uFE0F \uBAA8\uB378\uC774 \uC9C0\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. alias(${preferredModelAlias})\uB97C \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 \uACC4\uC815 \uAE30\uBCF8 \uBAA8\uB378\uB85C \uC790\uB3D9 \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
690
+ );
691
+ trace3("model:alias-first", preferredModelAlias);
692
+ trace3("model:fallback", "account-default");
693
+ command = `${aliasCommand} || ${fallbackCommand}`;
694
+ }
337
695
  trace3("command:created");
338
696
  if (args2.includes("--test")) {
339
697
  const safeCommand = command.replace(/"/g, '\\"');
@@ -343,6 +701,7 @@ ${reviewFormLine || "- (\uC5C6\uC74C)"}
343
701
  \uC0DD\uC131\uB420 \uBA85\uB839\uC5B4 \uBBF8\uB9AC\uBCF4\uAE30:
344
702
  ${safeCommand}"`;
345
703
  }
704
+ trace3("command:mode", "execute");
346
705
  trace3("createCodexCommand:end");
347
706
  return command;
348
707
  };
@@ -353,48 +712,125 @@ function checkCodexCliInstalled() {
353
712
  trace4("version-check:run", "codex --version");
354
713
  execSync("codex --version", { stdio: "ignore" });
355
714
  trace4("version-check:ok");
356
- } catch {
357
- trace4("version-check:failed", "install-start");
715
+ } catch (error) {
716
+ trace4("version-check:failed", getErrorSummary(error));
717
+ trace4("install:start", "@openai/codex");
358
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");
359
719
  try {
360
720
  execSync("npm install -g @openai/codex", { stdio: "inherit" });
361
- trace4("install:ok", "exit(1) for login");
721
+ trace4("install:ok", "login-required");
362
722
  console.log("\u2705 codex-cli \uC124\uCE58\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
363
723
  console.log("\u26A0\uFE0F codex-cli \uC0AC\uC6A9\uC744 \uC704\uD574 \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
364
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.');
365
725
  process.exit(1);
366
726
  } catch (installError) {
367
- trace4("install:failed");
368
- 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).");
369
- console.error(installError);
370
- 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
+ });
371
732
  }
372
733
  }
373
734
  trace4("checkCodexCliInstalled:end");
374
735
  }
375
736
  var args3 = process.argv.slice(2);
376
737
  var trace5 = createTraceLogger("gemini-commander", args3);
377
- var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
378
- trace5("createGeminiCommand:start", `tempDiffPath=${tempDiffPath2}, reviewFormPath=${reviewFormPath2}`);
379
- let modelOption = "";
380
- if (args3.includes("--review")) {
381
- modelOption = "--model pro";
382
- trace5("model:review", modelOption);
383
- } else if (args3.includes("--flash")) {
384
- modelOption = "--model flash";
385
- trace5("model:flash", modelOption);
386
- } else {
387
- const modelIndex = args3.indexOf("--model");
388
- if (modelIndex !== -1 && args3[modelIndex + 1]) {
389
- 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.");
390
- modelOption = `--model ${args3[modelIndex + 1]}`;
391
- trace5("model:custom", modelOption);
392
- } else {
393
- console.warn("\u26A0\uFE0F \uBAA8\uB378\uC774 \uC9C0\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uAE30\uBCF8 \uBAA8\uB378\uC778 gemini-flash\uB97C \uC0AC\uC6A9\uD569\uB2C8\uB2E4.");
394
- modelOption = "--model flash";
395
- trace5("model:default", modelOption);
738
+ var ALLOWED_REASONING_EFFORTS3 = ["minimal", "low", "medium", "high"];
739
+ function shellQuote3(value) {
740
+ return `'${value.replace(/'/g, `'\\''`)}'`;
741
+ }
742
+ function getArgValue3(flag) {
743
+ const index = args3.indexOf(flag);
744
+ if (index === -1 || !args3[index + 1]) {
745
+ return "";
746
+ }
747
+ return args3[index + 1];
748
+ }
749
+ function toUnique2(values) {
750
+ const seen = /* @__PURE__ */ new Set();
751
+ return values.filter((value) => {
752
+ if (!value || seen.has(value)) {
753
+ return false;
754
+ }
755
+ seen.add(value);
756
+ return true;
757
+ });
758
+ }
759
+ function resolveReasoningEffort3() {
760
+ const customReasoningEffort = getArgValue3("--reasoning-effort");
761
+ if (customReasoningEffort) {
762
+ if (ALLOWED_REASONING_EFFORTS3.includes(customReasoningEffort)) {
763
+ trace5("reasoning:custom", customReasoningEffort);
764
+ return customReasoningEffort;
396
765
  }
766
+ console.warn(
767
+ `\u26A0\uFE0F \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 reasoning effort(${customReasoningEffort})\uC785\uB2C8\uB2E4. allowed: ${ALLOWED_REASONING_EFFORTS3.join(
768
+ ", "
769
+ )}`
770
+ );
771
+ }
772
+ if (args3.includes("--flash")) {
773
+ trace5("reasoning:flash-default", "minimal");
774
+ return "minimal";
775
+ }
776
+ if (args3.includes("--review")) {
777
+ trace5("reasoning:review-default", "high");
778
+ return "high";
397
779
  }
780
+ trace5("reasoning:default", "medium");
781
+ return "medium";
782
+ }
783
+ function resolvePrimaryAlias2(reasoningEffort) {
784
+ if (args3.includes("--review")) {
785
+ trace5("model:mode-alias", "pro");
786
+ return "pro";
787
+ }
788
+ if (args3.includes("--flash")) {
789
+ trace5("model:mode-alias", "flash");
790
+ return "flash";
791
+ }
792
+ if (reasoningEffort === "high") {
793
+ trace5("model:reasoning-alias", "pro");
794
+ return "pro";
795
+ }
796
+ if (reasoningEffort === "minimal" || reasoningEffort === "low") {
797
+ trace5("model:reasoning-alias", "flash");
798
+ return "flash";
799
+ }
800
+ trace5("model:default-alias", "auto");
801
+ return "auto";
802
+ }
803
+ function getAliasFallbacks2(primaryAlias) {
804
+ if (primaryAlias === "pro") {
805
+ return ["pro", "flash", "auto"];
806
+ }
807
+ if (primaryAlias === "flash") {
808
+ return ["flash", "auto", "pro"];
809
+ }
810
+ return [primaryAlias, "auto", "flash", "pro"];
811
+ }
812
+ function getReasoningInstruction(reasoningEffort) {
813
+ if (reasoningEffort === "high") {
814
+ return "high (\uAE4A\uC774 \uC788\uB294 \uBD84\uC11D, \uC7A0\uC7AC\uC801 \uB9AC\uC2A4\uD06C\uAE4C\uC9C0 \uC810\uAC80)";
815
+ }
816
+ if (reasoningEffort === "medium") {
817
+ return "medium (\uADE0\uD615 \uC7A1\uD78C \uBD84\uC11D\uACFC \uD575\uC2EC \uC774\uC288 \uC911\uC2EC)";
818
+ }
819
+ if (reasoningEffort === "low") {
820
+ return "low (\uD575\uC2EC \uACB0\uD568 \uC704\uC8FC\uB85C \uAC04\uACB0\uD558\uAC8C \uBD84\uC11D)";
821
+ }
822
+ return "minimal (\uCE58\uBA85\uB3C4 \uB192\uC740 \uC774\uC288\uB9CC \uB9E4\uC6B0 \uAC04\uACB0\uD558\uAC8C \uBD84\uC11D)";
823
+ }
824
+ function buildGeminiExecCommand(prompt, model) {
825
+ const modelOption = model ? `--model ${shellQuote3(model)}` : "";
826
+ return `gemini ${[modelOption, "-p", shellQuote3(prompt)].filter(Boolean).join(" ")}`;
827
+ }
828
+ var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
829
+ trace5("createGeminiCommand:start", `tempDiffPath=${tempDiffPath2}, reviewFormPath=${reviewFormPath2}`);
830
+ const customModel = getArgValue3("--model");
831
+ const reasoningEffort = resolveReasoningEffort3();
832
+ const primaryAlias = resolvePrimaryAlias2(reasoningEffort);
833
+ const aliasFallbacks = toUnique2(getAliasFallbacks2(primaryAlias));
398
834
  const rules = [
399
835
  { path: rulesPath, display: "\uB8F0\uC14B" },
400
836
  { path: namingRulesPath, display: "\uB124\uC774\uBC0D \uADDC\uCE59" },
@@ -403,7 +839,29 @@ var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
403
839
  const validRules = rules.filter((rule) => fs.existsSync(rule.path)).map((rule) => `@${rule.path}`).join(", ");
404
840
  const rulesCount = validRules ? validRules.split(",").length : 0;
405
841
  trace5("rules:loaded", `count=${rulesCount}`);
406
- const command = `gemini ${modelOption} -p "\uB2E4\uC74C \uADDC\uCE59\uB4E4\uC744 \uCC38\uACE0\uD574\uC11C(${validRules}) \uC774 diff(@${tempDiffPath2})\uB97C \uB9AC\uBDF0\uD574\uC918. \uB9AC\uBDF0\uC591\uC2DD\uC740 @${reviewFormPath2} \uC5D0 \uB9DE\uCDB0\uC11C \uC791\uC131\uD574\uC918. "`;
842
+ const reviewFormRef = fs.existsSync(reviewFormPath2) ? `@${reviewFormPath2}` : "(\uC5C6\uC74C)";
843
+ trace5("reviewForm:status", reviewFormRef === "(\uC5C6\uC74C)" ? "missing" : "exists");
844
+ const reasoningInstruction = getReasoningInstruction(reasoningEffort);
845
+ const prompt = `\uB2E4\uC74C \uADDC\uCE59\uB4E4\uC744 \uCC38\uACE0\uD574\uC11C(${validRules || "(\uC5C6\uC74C)"}) \uC774 diff(@${tempDiffPath2})\uB97C \uB9AC\uBDF0\uD574\uC918.
846
+ \uB9AC\uBDF0 \uC591\uC2DD\uC740 ${reviewFormRef} \uC5D0 \uB9DE\uCDB0\uC11C \uC791\uC131\uD574\uC918.
847
+ \uCD94\uB860 \uAC15\uB3C4 \uC9C0\uCE68: ${reasoningInstruction}`;
848
+ trace5("prompt:prepared", `length=${prompt.length}`);
849
+ const modelCandidates = toUnique2(customModel ? [customModel, ...aliasFallbacks] : aliasFallbacks);
850
+ trace5("model:candidates", modelCandidates.join(", "));
851
+ trace5("command:candidates:count", String(modelCandidates.length + 1));
852
+ if (customModel) {
853
+ console.warn(
854
+ `\u26A0\uFE0F \uCEE4\uC2A4\uD140 \uBAA8\uB378(${customModel})\uC744 \uC6B0\uC120 \uC2DC\uB3C4\uD558\uACE0 \uC2E4\uD328\uD558\uBA74 alias(${aliasFallbacks.join(
855
+ " -> "
856
+ )}) \uBC0F \uAE30\uBCF8 \uBAA8\uB378 \uC21C\uC73C\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
857
+ );
858
+ } else {
859
+ console.warn(
860
+ `\u26A0\uFE0F \uBAA8\uB378\uC774 \uC9C0\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. alias(${aliasFallbacks.join(" -> ")})\uB97C \uC21C\uCC28 \uC2DC\uB3C4\uD558\uACE0 \uB9C8\uC9C0\uB9C9\uC5D0 \uAE30\uBCF8 \uBAA8\uB378\uB85C \uD3F4\uBC31\uD569\uB2C8\uB2E4.`
861
+ );
862
+ }
863
+ const commandCandidates = modelCandidates.map((model) => buildGeminiExecCommand(prompt, model));
864
+ const command = [...commandCandidates, buildGeminiExecCommand(prompt)].join(" || ");
407
865
  trace5("command:created");
408
866
  if (args3.includes("--test")) {
409
867
  const safeCommand = command.replace(/"/g, '\\"');
@@ -413,6 +871,7 @@ var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
413
871
  \uC0DD\uC131\uB420 \uBA85\uB839\uC5B4 \uBBF8\uB9AC\uBCF4\uAE30:
414
872
  ${safeCommand}"`;
415
873
  }
874
+ trace5("command:mode", "execute");
416
875
  trace5("createGeminiCommand:end");
417
876
  return command;
418
877
  };
@@ -423,21 +882,23 @@ function checkGeminiCliInstalled() {
423
882
  trace6("version-check:run", "gemini --version");
424
883
  execSync("gemini --version", { stdio: "ignore" });
425
884
  trace6("version-check:ok");
426
- } catch {
427
- trace6("version-check:failed", "install-start");
885
+ } catch (error) {
886
+ trace6("version-check:failed", getErrorSummary(error));
887
+ trace6("install:start", "@google/gemini-cli");
428
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");
429
889
  try {
430
890
  execSync("npm install -g @google/gemini-cli", { stdio: "inherit" });
431
- trace6("install:ok", "exit(1) for login");
891
+ trace6("install:ok", "login-required");
432
892
  console.log("\u2705 gemini-cli \uC124\uCE58\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
433
893
  console.log("\u26A0\uFE0F Gemini API \uC0AC\uC6A9\uC744 \uC704\uD574 \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
434
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.');
435
895
  process.exit(1);
436
896
  } catch (installError) {
437
- trace6("install:failed");
438
- 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).");
439
- console.error(installError);
440
- 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
+ });
441
902
  }
442
903
  }
443
904
  trace6("checkGeminiCliInstalled:end");
@@ -446,30 +907,35 @@ function checkGeminiCliInstalled() {
446
907
  // src/pr-review/review.ts
447
908
  async function main() {
448
909
  const args4 = process.argv.slice(2);
910
+ clearTraceMessages();
449
911
  const isTest = isTestMode(args4);
450
912
  const trace7 = createTraceLogger("review", args4);
451
913
  trace7("main:start", `args=${JSON.stringify(args4)}`);
452
- trace7("service-selection:start");
453
- const service = await showSelectionAIService();
454
- trace7("service-selection:done", `service=${service}`);
455
- switch (service) {
456
- case "gemini":
457
- trace7("install-check:start", "service=gemini");
458
- checkGeminiCliInstalled();
459
- trace7("install-check:done", "service=gemini");
460
- break;
461
- case "claude":
462
- trace7("install-check:start", "service=claude");
463
- checkClaudeCliInstalled();
464
- trace7("install-check:done", "service=claude");
465
- break;
466
- case "codex":
467
- trace7("install-check:start", "service=codex");
468
- checkCodexCliInstalled();
469
- trace7("install-check:done", "service=codex");
470
- break;
471
- }
914
+ let command = "";
915
+ let savedDiffPath = "";
916
+ let savedReportPath = "";
917
+ let service = "";
472
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
+ }
473
939
  trace7("review-flow:start");
474
940
  console.log("\u{1F680} AI Code Review\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...");
475
941
  const nowStr = getNowString();
@@ -487,8 +953,8 @@ async function main() {
487
953
  trace7("git-diff:run");
488
954
  diff = execSync(`git diff ${diffArgs} -- ${includeParams} ${excludeParams}`).toString();
489
955
  trace7("git-diff:done", `length=${diff.length}`);
490
- } catch {
491
- trace7("git-diff:error", "fallback-empty-diff");
956
+ } catch (error) {
957
+ trace7("git-diff:error", getErrorSummary(error));
492
958
  }
493
959
  if (!diff.trim() && !isTest) {
494
960
  trace7("empty-diff:exit");
@@ -500,10 +966,9 @@ async function main() {
500
966
  fs.writeFileSync(tempDiffPath, diff);
501
967
  trace7("temp-diff:write:done");
502
968
  trace7("saved-diff:copy:start");
503
- const savedDiffPath = getNextFilePath(REPORT_DIR, `${nowStr}-diff`, ".txt");
969
+ savedDiffPath = getNextFilePath(REPORT_DIR, `${nowStr}-diff`, ".txt");
504
970
  fs.copyFileSync(tempDiffPath, savedDiffPath);
505
971
  trace7("saved-diff:copy:done", savedDiffPath);
506
- let command = "";
507
972
  trace7("command:create:start", `service=${service}`);
508
973
  switch (service) {
509
974
  case "gemini":
@@ -522,7 +987,7 @@ async function main() {
522
987
  trace7("command:exec:done", `resultLength=${result.length}`);
523
988
  console.log(result);
524
989
  trace7("report:write:start");
525
- const savedReportPath = getNextFilePath(REPORT_DIR, nowStr, ".md");
990
+ savedReportPath = getNextFilePath(REPORT_DIR, nowStr, ".md");
526
991
  fs.writeFileSync(savedReportPath, result);
527
992
  trace7("report:write:done", savedReportPath);
528
993
  if (isTest) {
@@ -546,12 +1011,45 @@ ${command}`);
546
1011
  trace7("cleanup-temp-diff:done");
547
1012
  trace7("review-flow:end");
548
1013
  } catch (error) {
549
- trace7("review-flow:catch");
1014
+ trace7("review-flow:catch", getErrorSummary(error));
1015
+ let errorReportPath = "";
1016
+ trace7("cleanup-temp-diff:start(catch)");
1017
+ try {
1018
+ deleteTempDiff();
1019
+ trace7("cleanup-temp-diff:done(catch)");
1020
+ } catch (cleanupError) {
1021
+ trace7("cleanup-temp-diff:failed(catch)", getErrorSummary(cleanupError));
1022
+ console.error("\u26A0\uFE0F \uC784\uC2DC diff \uC815\uB9AC \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.");
1023
+ console.error(cleanupError);
1024
+ }
1025
+ trace7("error-report:prepare", `service=${service}`);
1026
+ errorReportPath = writeErrorReport(error, {
1027
+ scope: "review",
1028
+ args: args4,
1029
+ extraSections: [
1030
+ {
1031
+ heading: "Execution Context",
1032
+ markdown: `\`\`\`json
1033
+ ${JSON.stringify(
1034
+ {
1035
+ service,
1036
+ command: command || null,
1037
+ tempDiffPath,
1038
+ savedDiffPath: savedDiffPath || null,
1039
+ savedReportPath: savedReportPath || null
1040
+ },
1041
+ null,
1042
+ 2
1043
+ )}
1044
+ \`\`\``
1045
+ }
1046
+ ]
1047
+ });
550
1048
  console.error("\u274C \uB9AC\uBDF0 \uB3C4\uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.");
551
1049
  console.error(error);
552
- trace7("cleanup-temp-diff:start(catch)");
553
- deleteTempDiff();
554
- trace7("cleanup-temp-diff:done(catch)");
1050
+ if (errorReportPath) {
1051
+ console.error(`\u{1F4C4} \uC5D0\uB7EC \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${errorReportPath}`);
1052
+ }
555
1053
  process.exit(1);
556
1054
  }
557
1055
  }