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.
- package/dist/common/helper.cjs +234 -20
- package/dist/common/helper.cjs.map +1 -1
- package/dist/common/helper.d.cts +23 -3
- package/dist/common/helper.d.ts +23 -3
- package/dist/common/helper.js +228 -21
- package/dist/common/helper.js.map +1 -1
- package/dist/pr-review/claude/claude-commander.cjs +142 -28
- package/dist/pr-review/claude/claude-commander.cjs.map +1 -1
- package/dist/pr-review/claude/claude-commander.d.cts +7 -8
- package/dist/pr-review/claude/claude-commander.d.ts +7 -8
- package/dist/pr-review/claude/claude-commander.js +142 -28
- package/dist/pr-review/claude/claude-commander.js.map +1 -1
- package/dist/pr-review/claude/installation-claude.cjs +178 -8
- package/dist/pr-review/claude/installation-claude.cjs.map +1 -1
- package/dist/pr-review/claude/installation-claude.js +177 -8
- package/dist/pr-review/claude/installation-claude.js.map +1 -1
- package/dist/pr-review/codex/codex-commander.cjs +64 -21
- package/dist/pr-review/codex/codex-commander.cjs.map +1 -1
- package/dist/pr-review/codex/codex-commander.d.cts +8 -3
- package/dist/pr-review/codex/codex-commander.d.ts +8 -3
- package/dist/pr-review/codex/codex-commander.js +64 -21
- package/dist/pr-review/codex/codex-commander.js.map +1 -1
- package/dist/pr-review/codex/installation-codex.cjs +178 -8
- package/dist/pr-review/codex/installation-codex.cjs.map +1 -1
- package/dist/pr-review/codex/installation-codex.js +177 -8
- package/dist/pr-review/codex/installation-codex.js.map +1 -1
- package/dist/pr-review/gemini/gemini-commander.cjs +122 -21
- package/dist/pr-review/gemini/gemini-commander.cjs.map +1 -1
- package/dist/pr-review/gemini/gemini-commander.d.cts +10 -13
- package/dist/pr-review/gemini/gemini-commander.d.ts +10 -13
- package/dist/pr-review/gemini/gemini-commander.js +122 -21
- package/dist/pr-review/gemini/gemini-commander.js.map +1 -1
- package/dist/pr-review/gemini/installation-gemini.cjs +178 -8
- package/dist/pr-review/gemini/installation-gemini.cjs.map +1 -1
- package/dist/pr-review/gemini/installation-gemini.js +177 -8
- package/dist/pr-review/gemini/installation-gemini.js.map +1 -1
- package/dist/pr-review/review-one-by-one.cjs +679 -157
- package/dist/pr-review/review-one-by-one.cjs.map +1 -1
- package/dist/pr-review/review-one-by-one.js +679 -157
- package/dist/pr-review/review-one-by-one.js.map +1 -1
- package/dist/pr-review/review.cjs +630 -132
- package/dist/pr-review/review.cjs.map +1 -1
- package/dist/pr-review/review.js +630 -132
- package/dist/pr-review/review.js.map +1 -1
- package/package.json +1 -1
package/dist/pr-review/review.js
CHANGED
|
@@ -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(
|
|
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
|
|
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 (
|
|
122
|
-
|
|
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
|
-
|
|
136
|
-
|
|
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
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
|
251
|
-
trace(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const
|
|
256
|
-
trace("
|
|
257
|
-
|
|
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",
|
|
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", "
|
|
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
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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
|
-
|
|
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",
|
|
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", "
|
|
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
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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
|
|
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",
|
|
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", "
|
|
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
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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",
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
553
|
-
|
|
554
|
-
|
|
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
|
}
|