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
@@ -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");
@@ -32,48 +33,223 @@ var ignoreList = [
32
33
  function isTestMode(args4 = process.argv.slice(2)) {
33
34
  return args4.includes("--test");
34
35
  }
36
+ function clearTraceMessages() {
37
+ traceMessages.length = 0;
38
+ }
39
+ function getTraceMessages() {
40
+ return [...traceMessages];
41
+ }
35
42
  function createTraceLogger(scope, args4 = process.argv.slice(2)) {
36
43
  const enabled = isTestMode(args4);
37
44
  return (step, detail) => {
45
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
46
+ const message = `[${timestamp}][TRACE][${scope}] ${step}${detail ? ` | ${detail}` : ""}`;
47
+ traceMessages.push(message);
38
48
  if (!enabled) {
39
49
  return;
40
50
  }
41
- 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")
42
63
  };
43
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
+ }
44
124
  function getNextFilePath(dir, baseName, extension) {
45
125
  let counter = 1;
46
126
  while (true) {
47
127
  const filePath = path.join(dir, `${baseName}-${counter}${extension}`);
48
- if (!fs5.existsSync(filePath)) {
128
+ if (!fs.existsSync(filePath)) {
49
129
  return filePath;
50
130
  }
51
131
  counter++;
52
132
  }
53
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
+ }
54
141
  function deleteFile(filePath) {
55
- if (fs5.existsSync(filePath)) {
56
- fs5.unlinkSync(filePath);
142
+ if (fs.existsSync(filePath)) {
143
+ fs.unlinkSync(filePath);
57
144
  }
58
145
  }
59
146
  function deleteTempDiff() {
60
147
  deleteFile(tempDiffPath);
61
148
  }
62
149
  function createReportDirectory() {
63
- if (!fs5.existsSync(REPORT_DIR)) {
64
- fs5.mkdirSync(REPORT_DIR, { recursive: true });
65
- }
66
- }
67
- function getNowString() {
68
- const now = /* @__PURE__ */ new Date();
69
- const YYYY = now.getFullYear();
70
- const MM = String(now.getMonth() + 1).padStart(2, "0");
71
- const DD = String(now.getDate()).padStart(2, "0");
72
- const HH = String(now.getHours()).padStart(2, "0");
73
- const mm = String(now.getMinutes()).padStart(2, "0");
74
- 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);
75
156
  return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;
76
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
+ }
77
253
  function getGitDiffFilter() {
78
254
  const includeExtensions = ["*.ts", "*.tsx", "*.js", "*.jsx"];
79
255
  const excludePatterns = ignoreList.map((item) => `:(exclude)${item}`);
@@ -85,6 +261,7 @@ function getGitDiffFilter() {
85
261
  function openReport(reportPath) {
86
262
  const resolvedPath = path.resolve(reportPath);
87
263
  const { platform } = process;
264
+ helperTrace("open-report:start", resolvedPath);
88
265
  const openWithChrome = () => {
89
266
  if (platform === "darwin") {
90
267
  execSync(`open -a "Google Chrome" "${resolvedPath}"`, { stdio: "ignore" });
@@ -109,32 +286,41 @@ function openReport(reportPath) {
109
286
  };
110
287
  try {
111
288
  if (openWithChrome()) {
289
+ helperTrace("open-report:chrome:success", platform);
112
290
  console.log("\u{1F680} Google Chrome\uC5D0\uC11C \uB9AC\uD3EC\uD2B8\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4.");
113
291
  return;
114
292
  }
115
- } catch {
293
+ } catch (error) {
294
+ helperTrace("open-report:chrome:failed", getErrorSummary(error));
116
295
  }
117
296
  try {
118
297
  if (openWithDefaultBrowser()) {
298
+ helperTrace("open-report:default-browser:success", platform);
119
299
  console.log("\u{1F680} \uAE30\uBCF8 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB9AC\uD3EC\uD2B8\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4.");
120
300
  return;
121
301
  }
122
- } catch (e) {
123
- 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);
124
305
  return;
125
306
  }
307
+ helperTrace("open-report:unsupported-platform", platform);
126
308
  console.error(`\u26A0\uFE0F \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD50C\uB7AB\uD3FC\uC785\uB2C8\uB2E4: ${platform}`);
127
309
  }
128
310
  function getDiffArgs() {
129
311
  const args4 = process.argv.slice(2);
130
312
  const commitIndex = args4.indexOf("--commit");
131
313
  const { includeParams, excludeParams } = getGitDiffFilter();
314
+ helperTrace("diff-args:resolve:start", `args=${JSON.stringify(args4)}`);
132
315
  let diffArgs = "";
133
316
  if (commitIndex !== -1) {
134
317
  const commitHash = args4[commitIndex + 1];
135
318
  if (!commitHash) {
136
- console.error("\u274C \uCEE4\uBC0B \uD574\uC2DC\uAC00 \uC81C\uACF5\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
137
- 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
+ });
138
324
  }
139
325
  const nextArg = args4[commitIndex + 2];
140
326
  let n = 0;
@@ -144,21 +330,37 @@ function getDiffArgs() {
144
330
  n = 0;
145
331
  }
146
332
  }
333
+ helperTrace("diff-args:commit-mode", `${commitHash}~${n + 1} ${commitHash}`);
147
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...`);
148
335
  diffArgs = `${commitHash}~${n + 1} ${commitHash}`;
149
336
  } else {
150
337
  try {
338
+ helperTrace("diff-args:unstaged-check:start");
151
339
  const check = execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();
152
340
  if (!check.trim()) {
341
+ helperTrace("diff-args:unstaged-check:empty", "use HEAD~1 HEAD");
153
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...");
154
343
  diffArgs = "HEAD~1 HEAD";
344
+ } else {
345
+ helperTrace("diff-args:unstaged-check:has-changes", `length=${check.length}`);
155
346
  }
156
- } catch {
347
+ } catch (error) {
348
+ helperTrace("diff-args:unstaged-check:failed", getErrorSummary(error));
157
349
  }
158
350
  }
351
+ helperTrace("diff-args:resolve:done", diffArgs || "(default)");
159
352
  return diffArgs;
160
353
  }
161
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");
162
364
  let selectedIndex = 0;
163
365
  const rl = readline.createInterface({
164
366
  input: process.stdin,
@@ -172,6 +374,7 @@ async function showSelectionAIService() {
172
374
  readline.moveCursor(process.stdout, 0, -(AIServices.length + 1));
173
375
  }
174
376
  firstRender = false;
377
+ helperTrace("show-selection:interactive:render", AIServices[selectedIndex] || "unknown");
175
378
  readline.clearScreenDown(process.stdout);
176
379
  process.stdout.write(
177
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"
@@ -191,6 +394,7 @@ async function showSelectionAIService() {
191
394
  const onData = (data) => {
192
395
  const key = data.toString();
193
396
  if (key === "") {
397
+ helperTrace("show-selection:interactive:ctrl-c");
194
398
  process.stdout.write("\x1B[?25h");
195
399
  process.exit(0);
196
400
  }
@@ -211,6 +415,7 @@ async function showSelectionAIService() {
211
415
  `);
212
416
  const result = AIServices[selectedIndex];
213
417
  if (result) {
418
+ helperTrace("show-selection:interactive:confirmed", result);
214
419
  resolve(result);
215
420
  }
216
421
  }
@@ -222,40 +427,150 @@ async function showSelectionAIService() {
222
427
  }
223
428
  var args = process.argv.slice(2);
224
429
  var trace = createTraceLogger("claude-commander", args);
225
- var createClaudeCommand = (tempDiffPath2, reviewFormPath2) => {
226
- trace("createClaudeCommand:start", `tempDiffPath=${tempDiffPath2}, reviewFormPath=${reviewFormPath2}`);
227
- let modelOption = "";
228
- if (args.includes("--review")) {
229
- modelOption = "--model opus";
230
- trace("model:review", modelOption);
231
- } else if (args.includes("--flash")) {
232
- modelOption = "--model haiku";
233
- trace("model:flash", modelOption);
234
- } else {
235
- const modelIndex = args.indexOf("--model");
236
- if (modelIndex !== -1 && args[modelIndex + 1]) {
237
- 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.");
238
- modelOption = `--model ${args[modelIndex + 1]}`;
239
- trace("model:custom", modelOption);
240
- } else {
241
- 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.");
242
- modelOption = "--model sonnet";
243
- 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;
446
+ }
447
+ seen.add(value);
448
+ return true;
449
+ });
450
+ }
451
+ function normalizeEffort(level) {
452
+ if (level === "minimal") {
453
+ return "low";
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;
244
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"];
245
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));
246
527
  const rules = [
247
528
  { path: rulesPath, display: "\uB8F0\uC14B" },
248
529
  { path: namingRulesPath, display: "\uB124\uC774\uBC0D \uADDC\uCE59" },
249
530
  { path: codingConventionRulesPath, display: "\uCF54\uB529 \uCEE8\uBCA4\uC158" }
250
531
  ];
251
- const systemPromptFiles = rules.filter((rule) => fs5.existsSync(rule.path)).map((rule) => `--append-system-prompt-file ${rule.path}`).join(" ");
252
- trace(
253
- "rules:loaded",
254
- `count=${systemPromptFiles ? systemPromptFiles.split("--append-system-prompt-file").length - 1 : 0}`
255
- );
256
- const reviewFormOption = fs5.existsSync(reviewFormPath2) ? `--append-system-prompt-file ${reviewFormPath2}` : "";
257
- trace("reviewForm:status", reviewFormOption ? "exists" : "missing");
258
- 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(" || ");
259
574
  trace("command:created");
260
575
  if (args.includes("--test")) {
261
576
  const safeCommand = command.replace(/"/g, '\\"');
@@ -265,6 +580,7 @@ var createClaudeCommand = (tempDiffPath2, reviewFormPath2) => {
265
580
  \uC0DD\uC131\uB420 \uBA85\uB839\uC5B4 \uBBF8\uB9AC\uBCF4\uAE30:
266
581
  ${safeCommand}"`;
267
582
  }
583
+ trace("command:mode", "execute");
268
584
  trace("createClaudeCommand:end");
269
585
  return command;
270
586
  };
@@ -275,54 +591,79 @@ function checkClaudeCliInstalled() {
275
591
  trace2("version-check:run", "claude --version");
276
592
  execSync("claude --version", { stdio: "ignore" });
277
593
  trace2("version-check:ok");
278
- } catch {
279
- trace2("version-check:failed", "install-start");
594
+ } catch (error) {
595
+ trace2("version-check:failed", getErrorSummary(error));
596
+ trace2("install:start", "@anthropic-ai/claude-code");
280
597
  console.log(
281
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"
282
599
  );
283
600
  try {
284
601
  execSync("npm install -g @anthropic-ai/claude-code", { stdio: "inherit" });
285
- trace2("install:ok", "exit(1) for login");
602
+ trace2("install:ok", "login-required");
286
603
  console.log("\u2705 claude-cli \uC124\uCE58\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
287
604
  console.log("\u26A0\uFE0F claude-cli \uC0AC\uC6A9\uC744 \uC704\uD574 \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
288
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.');
289
606
  process.exit(1);
290
607
  } catch (installError) {
291
- trace2("install:failed");
292
- 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).");
293
- console.error(installError);
294
- 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
+ });
295
613
  }
296
614
  }
297
615
  trace2("checkClaudeCliInstalled:end");
298
616
  }
299
617
  var args2 = process.argv.slice(2);
300
618
  var trace3 = createTraceLogger("codex-commander", args2);
301
- var createCodexCommand = (tempDiffPath2, reviewFormPath2) => {
302
- trace3("createCodexCommand:start", `tempDiffPath=${tempDiffPath2}, reviewFormPath=${reviewFormPath2}`);
303
- let modelOption = "";
304
- if (args2.includes("--review")) {
305
- modelOption = "--model gpt-5";
306
- trace3("model:review", modelOption);
307
- } else if (args2.includes("--flash")) {
308
- modelOption = "--model gpt-5-mini";
309
- trace3("model:flash", modelOption);
310
- } else {
311
- const modelIndex = args2.indexOf("--model");
312
- if (modelIndex !== -1 && args2[modelIndex + 1]) {
313
- 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.");
314
- modelOption = `--model ${args2[modelIndex + 1]}`;
315
- trace3("model:custom", modelOption);
316
- } else {
317
- 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.");
318
- modelOption = "--model gpt-5-mini";
319
- 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;
320
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
+ );
321
642
  }
322
- const rules = [rulesPath, namingRulesPath, codingConventionRulesPath].filter((filePath) => fs5.existsSync(filePath)).map((filePath) => `- ${filePath}`).join("\n");
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();
663
+ const rules = [rulesPath, namingRulesPath, codingConventionRulesPath].filter((filePath) => fs.existsSync(filePath)).map((filePath) => `- ${filePath}`).join("\n");
323
664
  const rulesCount = rules ? rules.split("\n").length : 0;
324
665
  trace3("rules:loaded", `count=${rulesCount}`);
325
- const hasReviewForm = fs5.existsSync(reviewFormPath2);
666
+ const hasReviewForm = fs.existsSync(reviewFormPath2);
326
667
  const reviewFormLine = hasReviewForm ? `- ${reviewFormPath2}` : "";
327
668
  trace3("reviewForm:status", reviewFormLine ? "exists" : "missing");
328
669
  const prompt = `\uC544\uB798 \uD30C\uC77C\uB4E4\uC744 \uCC38\uACE0\uD574\uC11C \uCF54\uB4DC \uB9AC\uBDF0\uB97C \uC9C4\uD589\uD574\uC918.
@@ -334,7 +675,23 @@ ${reviewFormLine || "- (\uC5C6\uC74C)"}
334
675
  - ${tempDiffPath2}
335
676
 
336
677
  \uBC18\uB4DC\uC2DC \uB9AC\uBDF0 \uC591\uC2DD\uC5D0 \uB9DE\uCDB0 \uC791\uC131\uD574\uC918.`;
337
- 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
+ }
338
695
  trace3("command:created");
339
696
  if (args2.includes("--test")) {
340
697
  const safeCommand = command.replace(/"/g, '\\"');
@@ -344,6 +701,7 @@ ${reviewFormLine || "- (\uC5C6\uC74C)"}
344
701
  \uC0DD\uC131\uB420 \uBA85\uB839\uC5B4 \uBBF8\uB9AC\uBCF4\uAE30:
345
702
  ${safeCommand}"`;
346
703
  }
704
+ trace3("command:mode", "execute");
347
705
  trace3("createCodexCommand:end");
348
706
  return command;
349
707
  };
@@ -354,57 +712,156 @@ function checkCodexCliInstalled() {
354
712
  trace4("version-check:run", "codex --version");
355
713
  execSync("codex --version", { stdio: "ignore" });
356
714
  trace4("version-check:ok");
357
- } catch {
358
- trace4("version-check:failed", "install-start");
715
+ } catch (error) {
716
+ trace4("version-check:failed", getErrorSummary(error));
717
+ trace4("install:start", "@openai/codex");
359
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");
360
719
  try {
361
720
  execSync("npm install -g @openai/codex", { stdio: "inherit" });
362
- trace4("install:ok", "exit(1) for login");
721
+ trace4("install:ok", "login-required");
363
722
  console.log("\u2705 codex-cli \uC124\uCE58\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
364
723
  console.log("\u26A0\uFE0F codex-cli \uC0AC\uC6A9\uC744 \uC704\uD574 \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
365
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.');
366
725
  process.exit(1);
367
726
  } catch (installError) {
368
- trace4("install:failed");
369
- 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).");
370
- console.error(installError);
371
- 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
+ });
372
732
  }
373
733
  }
374
734
  trace4("checkCodexCliInstalled:end");
375
735
  }
376
736
  var args3 = process.argv.slice(2);
377
737
  var trace5 = createTraceLogger("gemini-commander", args3);
378
- var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
379
- trace5("createGeminiCommand:start", `tempDiffPath=${tempDiffPath2}, reviewFormPath=${reviewFormPath2}`);
380
- let modelOption = "";
381
- if (args3.includes("--review")) {
382
- modelOption = "--model pro";
383
- trace5("model:review", modelOption);
384
- } else if (args3.includes("--flash")) {
385
- modelOption = "--model flash";
386
- trace5("model:flash", modelOption);
387
- } else {
388
- const modelIndex = args3.indexOf("--model");
389
- if (modelIndex !== -1 && args3[modelIndex + 1]) {
390
- 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.");
391
- modelOption = `--model ${args3[modelIndex + 1]}`;
392
- trace5("model:custom", modelOption);
393
- } else {
394
- 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.");
395
- modelOption = "--model flash";
396
- 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;
397
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";
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";
398
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));
399
834
  const rules = [
400
835
  { path: rulesPath, display: "\uB8F0\uC14B" },
401
836
  { path: namingRulesPath, display: "\uB124\uC774\uBC0D \uADDC\uCE59" },
402
837
  { path: codingConventionRulesPath, display: "\uCF54\uB529 \uCEE8\uBCA4\uC158" }
403
838
  ];
404
- 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(", ");
405
840
  const rulesCount = validRules ? validRules.split(",").length : 0;
406
841
  trace5("rules:loaded", `count=${rulesCount}`);
407
- 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(" || ");
408
865
  trace5("command:created");
409
866
  if (args3.includes("--test")) {
410
867
  const safeCommand = command.replace(/"/g, '\\"');
@@ -414,6 +871,7 @@ var createGeminiCommand = (tempDiffPath2, reviewFormPath2) => {
414
871
  \uC0DD\uC131\uB420 \uBA85\uB839\uC5B4 \uBBF8\uB9AC\uBCF4\uAE30:
415
872
  ${safeCommand}"`;
416
873
  }
874
+ trace5("command:mode", "execute");
417
875
  trace5("createGeminiCommand:end");
418
876
  return command;
419
877
  };
@@ -424,21 +882,23 @@ function checkGeminiCliInstalled() {
424
882
  trace6("version-check:run", "gemini --version");
425
883
  execSync("gemini --version", { stdio: "ignore" });
426
884
  trace6("version-check:ok");
427
- } catch {
428
- trace6("version-check:failed", "install-start");
885
+ } catch (error) {
886
+ trace6("version-check:failed", getErrorSummary(error));
887
+ trace6("install:start", "@google/gemini-cli");
429
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");
430
889
  try {
431
890
  execSync("npm install -g @google/gemini-cli", { stdio: "inherit" });
432
- trace6("install:ok", "exit(1) for login");
891
+ trace6("install:ok", "login-required");
433
892
  console.log("\u2705 gemini-cli \uC124\uCE58\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
434
893
  console.log("\u26A0\uFE0F Gemini API \uC0AC\uC6A9\uC744 \uC704\uD574 \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
435
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.');
436
895
  process.exit(1);
437
896
  } catch (installError) {
438
- trace6("install:failed");
439
- 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).");
440
- console.error(installError);
441
- 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
+ });
442
902
  }
443
903
  }
444
904
  trace6("checkGeminiCliInstalled:end");
@@ -448,30 +908,34 @@ function checkGeminiCliInstalled() {
448
908
  var execAsync = util.promisify(exec);
449
909
  async function main() {
450
910
  const args4 = process.argv.slice(2);
911
+ clearTraceMessages();
451
912
  const isTest = isTestMode(args4);
452
913
  const trace7 = createTraceLogger("review-one-by-one", args4);
453
914
  trace7("main:start", `args=${JSON.stringify(args4)}`);
454
- trace7("service-selection:start");
455
- const service = await showSelectionAIService();
456
- trace7("service-selection:done", `service=${service}`);
457
- switch (service) {
458
- case "gemini":
459
- trace7("install-check:start", "service=gemini");
460
- checkGeminiCliInstalled();
461
- trace7("install-check:done", "service=gemini");
462
- break;
463
- case "claude":
464
- trace7("install-check:start", "service=claude");
465
- checkClaudeCliInstalled();
466
- trace7("install-check:done", "service=claude");
467
- break;
468
- case "codex":
469
- trace7("install-check:start", "service=codex");
470
- checkCodexCliInstalled();
471
- trace7("install-check:done", "service=codex");
472
- break;
473
- }
915
+ let service = "";
916
+ let savedDiffPath = "";
917
+ let savedReportPath = "";
474
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
+ }
475
939
  trace7("review-flow:start");
476
940
  console.log("\u{1F680} AI Code Review\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...");
477
941
  trace7("report-dir:create:start");
@@ -479,6 +943,7 @@ async function main() {
479
943
  trace7("report-dir:create:done");
480
944
  const { includeParams, excludeParams } = getGitDiffFilter();
481
945
  trace7("diff-filter:loaded", `include=${includeParams} | exclude=${excludeParams}`);
946
+ trace7("diff-args:build:start");
482
947
  const diffArgs = getDiffArgs();
483
948
  trace7("diff-args:build:done", `diffArgs=${diffArgs || "(default)"}`);
484
949
  const filesCommand = `git diff --name-only ${diffArgs} -- ${includeParams} ${excludeParams}`;
@@ -498,26 +963,26 @@ async function main() {
498
963
  trace7("full-diff:done", `length=${fullDiff.length}`);
499
964
  trace7("timestamp:created", nowStr);
500
965
  trace7("temp-diff:write:start", tempDiffPath);
501
- fs5.writeFileSync(tempDiffPath, fullDiff);
966
+ fs.writeFileSync(tempDiffPath, fullDiff);
502
967
  trace7("temp-diff:write:done");
503
968
  trace7("saved-diff:copy:start");
504
- const savedDiffPath = getNextFilePath(REPORT_DIR, `${nowStr}-diff`, ".txt");
505
- fs5.copyFileSync(tempDiffPath, savedDiffPath);
969
+ savedDiffPath = getNextFilePath(REPORT_DIR, `${nowStr}-diff`, ".txt");
970
+ fs.copyFileSync(tempDiffPath, savedDiffPath);
506
971
  trace7("saved-diff:copy:done", savedDiffPath);
507
- const savedReportPath = getNextFilePath(REPORT_DIR, nowStr, ".md");
972
+ savedReportPath = getNextFilePath(REPORT_DIR, nowStr, ".md");
508
973
  trace7("saved-report:path", savedReportPath);
509
974
  const promises = fileList.map(async (file) => {
975
+ const tempOneFileDiffPath = `temp_diff_${file.replace(/\//g, "_")}.txt`;
976
+ let command = "";
510
977
  try {
511
978
  trace7("file-review:start", file);
512
979
  console.log(`\u{1F50D} Reviewing: ${file}...`);
513
980
  trace7("file-diff:run", file);
514
981
  const fileDiff = execSync(`git diff ${diffArgs} -- "${file}"`).toString();
515
982
  trace7("file-diff:done", `${file} | length=${fileDiff.length}`);
516
- const tempOneFileDiffPath = `temp_diff_${file.replace(/\//g, "_")}.txt`;
517
983
  trace7("file-temp-diff:write:start", tempOneFileDiffPath);
518
- fs5.writeFileSync(tempOneFileDiffPath, fileDiff);
984
+ fs.writeFileSync(tempOneFileDiffPath, fileDiff);
519
985
  trace7("file-temp-diff:write:done", tempOneFileDiffPath);
520
- let command = "";
521
986
  trace7("file-command:create:start", file);
522
987
  switch (service) {
523
988
  case "gemini":
@@ -530,7 +995,7 @@ async function main() {
530
995
  command = createCodexCommand(tempOneFileDiffPath, reviewFormOneByOnePath);
531
996
  break;
532
997
  }
533
- trace7("file-command:create:done", file);
998
+ trace7("file-command:create:done", `${file} | commandLength=${command.length}`);
534
999
  trace7("file-command:exec:start", file);
535
1000
  const { stdout } = await execAsync(command, { maxBuffer: 1024 * 1024 * 20 });
536
1001
  const result = stdout.toString();
@@ -544,11 +1009,11 @@ ${result}
544
1009
  `;
545
1010
  console.log(tempReport);
546
1011
  trace7("file-report:append:start", file);
547
- fs5.appendFileSync(savedReportPath, tempReport);
1012
+ fs.appendFileSync(savedReportPath, tempReport);
548
1013
  trace7("file-report:append:done", file);
549
1014
  if (isTest) {
550
1015
  trace7("file-test-command:append:start", file);
551
- fs5.appendFileSync(savedReportPath, `
1016
+ fs.appendFileSync(savedReportPath, `
552
1017
 
553
1018
  ## \uC0AC\uC6A9\uB41C \uBA85\uB839\uC5B4
554
1019
 
@@ -557,26 +1022,51 @@ ${command}`);
557
1022
  }
558
1023
  trace7("file-review:end", file);
559
1024
  } catch (err) {
560
- 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
+ });
561
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
+ }
562
1053
  const errorReport = `### File: ${file}
563
1054
  \u274C Review Failed
564
1055
  \`\`\`
565
- ${err}
1056
+ ${getErrorSummary(err)}
566
1057
  \`\`\`
567
1058
 
568
1059
  `;
569
- fs5.appendFileSync(savedReportPath, errorReport);
1060
+ fs.appendFileSync(savedReportPath, errorReport);
570
1061
  try {
571
- const tempOneFileDiffPath = `temp_diff_${file.replace(/\//g, "_")}.txt`;
572
- if (fs5.existsSync(tempOneFileDiffPath)) {
1062
+ if (fs.existsSync(tempOneFileDiffPath)) {
573
1063
  trace7("file-temp-diff:delete:start(catch)", tempOneFileDiffPath);
574
1064
  deleteFile(tempOneFileDiffPath);
575
1065
  trace7("file-temp-diff:delete:done(catch)", tempOneFileDiffPath);
576
1066
  }
577
- } catch (e) {
578
- trace7("file-temp-diff:delete:failed(catch)", file);
579
- 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);
580
1070
  }
581
1071
  }
582
1072
  });
@@ -595,12 +1085,44 @@ ${err}
595
1085
  trace7("cleanup-temp-diff:done");
596
1086
  trace7("review-flow:end");
597
1087
  } catch (error) {
598
- 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
+ });
599
1121
  console.error("\u274C \uB9AC\uBDF0 \uB3C4\uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.");
600
1122
  console.error(error);
601
- trace7("cleanup-temp-diff:start(catch)");
602
- deleteTempDiff();
603
- trace7("cleanup-temp-diff:done(catch)");
1123
+ if (errorReportPath) {
1124
+ console.error(`\u{1F4C4} \uC5D0\uB7EC \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${errorReportPath}`);
1125
+ }
604
1126
  process.exit(1);
605
1127
  }
606
1128
  }