sales-frontend-gemini-cli 0.4.1 → 0.4.3
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 +674 -68
- package/dist/common/helper.cjs.map +1 -1
- package/dist/common/helper.d.cts +106 -4
- package/dist/common/helper.d.ts +106 -4
- package/dist/common/helper.js +659 -70
- package/dist/common/helper.js.map +1 -1
- package/dist/common/types.d.cts +24 -1
- package/dist/common/types.d.ts +24 -1
- package/dist/pr-review/claude/claude-commander.cjs +58 -10
- package/dist/pr-review/claude/claude-commander.cjs.map +1 -1
- package/dist/pr-review/claude/claude-commander.js +58 -10
- package/dist/pr-review/claude/claude-commander.js.map +1 -1
- package/dist/pr-review/claude/installation-claude.cjs +219 -13
- package/dist/pr-review/claude/installation-claude.cjs.map +1 -1
- package/dist/pr-review/claude/installation-claude.js +218 -13
- package/dist/pr-review/claude/installation-claude.js.map +1 -1
- package/dist/pr-review/codex/codex-commander.cjs +55 -9
- package/dist/pr-review/codex/codex-commander.cjs.map +1 -1
- package/dist/pr-review/codex/codex-commander.js +55 -9
- package/dist/pr-review/codex/codex-commander.js.map +1 -1
- package/dist/pr-review/codex/installation-codex.cjs +219 -13
- package/dist/pr-review/codex/installation-codex.cjs.map +1 -1
- package/dist/pr-review/codex/installation-codex.js +218 -13
- package/dist/pr-review/codex/installation-codex.js.map +1 -1
- package/dist/pr-review/gemini/gemini-commander.cjs +82 -16
- package/dist/pr-review/gemini/gemini-commander.cjs.map +1 -1
- package/dist/pr-review/gemini/gemini-commander.js +82 -16
- package/dist/pr-review/gemini/gemini-commander.js.map +1 -1
- package/dist/pr-review/gemini/installation-gemini.cjs +219 -13
- package/dist/pr-review/gemini/installation-gemini.cjs.map +1 -1
- package/dist/pr-review/gemini/installation-gemini.js +218 -13
- package/dist/pr-review/gemini/installation-gemini.js.map +1 -1
- package/dist/pr-review/review-one-by-one.cjs +838 -184
- package/dist/pr-review/review-one-by-one.cjs.map +1 -1
- package/dist/pr-review/review-one-by-one.js +839 -185
- package/dist/pr-review/review-one-by-one.js.map +1 -1
- package/dist/pr-review/review.cjs +815 -156
- package/dist/pr-review/review.cjs.map +1 -1
- package/dist/pr-review/review.js +815 -156
- package/dist/pr-review/review.js.map +1 -1
- package/package.json +4 -7
- package/src/common/rules/coding-convention.md +393 -0
- package/src/common/rules/coding-convention.pdf +0 -0
- package/src/common/rules/naming-rule.md +347 -0
- package/src/common/rules/naming-rule.pdf +0 -0
package/dist/common/helper.js
CHANGED
|
@@ -1,19 +1,59 @@
|
|
|
1
|
-
import { execSync } from 'child_process';
|
|
1
|
+
import { execSync, execFileSync } from 'child_process';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import readline from 'readline';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
|
+
import { inspect } from 'util';
|
|
6
7
|
|
|
7
8
|
// src/common/helper.ts
|
|
8
9
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
var
|
|
10
|
-
var
|
|
11
|
-
var
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
var traceMessages = [];
|
|
11
|
+
var GEMINI_CLI_PACKAGE_NAME = "sales-frontend-gemini-cli";
|
|
12
|
+
var cachedPackageRootPath = "";
|
|
13
|
+
function isGeminiCliPackageRoot(directory) {
|
|
14
|
+
const packageJsonPath = path.join(directory, "package.json");
|
|
15
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
20
|
+
return packageJson.name === GEMINI_CLI_PACKAGE_NAME;
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function resolveGeminiCliPackageRoot(startDirectory = __dirname) {
|
|
26
|
+
if (cachedPackageRootPath) {
|
|
27
|
+
return cachedPackageRootPath;
|
|
28
|
+
}
|
|
29
|
+
let currentDirectory = startDirectory;
|
|
30
|
+
while (true) {
|
|
31
|
+
if (isGeminiCliPackageRoot(currentDirectory)) {
|
|
32
|
+
cachedPackageRootPath = currentDirectory;
|
|
33
|
+
return cachedPackageRootPath;
|
|
34
|
+
}
|
|
35
|
+
const parentDirectory = path.dirname(currentDirectory);
|
|
36
|
+
if (parentDirectory === currentDirectory) {
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
currentDirectory = parentDirectory;
|
|
40
|
+
}
|
|
41
|
+
cachedPackageRootPath = path.resolve(startDirectory, "../..");
|
|
42
|
+
return cachedPackageRootPath;
|
|
43
|
+
}
|
|
44
|
+
function resolvePackageAssetPath(relativeFilePath) {
|
|
45
|
+
return path.resolve(resolveGeminiCliPackageRoot(), relativeFilePath);
|
|
46
|
+
}
|
|
47
|
+
var rulesPath = resolvePackageAssetPath("src/common/rules/review-rules.md");
|
|
48
|
+
var namingRulesPath = resolvePackageAssetPath("src/common/rules/naming-rule.md");
|
|
49
|
+
var codingConventionRulesPath = resolvePackageAssetPath("src/common/rules/coding-convention.md");
|
|
50
|
+
var reviewFormPath = resolvePackageAssetPath("src/common/form/review-form.md");
|
|
51
|
+
var reviewFormOneByOnePath = resolvePackageAssetPath("src/common/form/review-form-one-by-one.md");
|
|
14
52
|
var REPORT_DIR = ".review-report";
|
|
15
53
|
var tempDiffPath = "temp_diff.txt";
|
|
16
54
|
var AIServices = ["gemini", "claude", "codex"];
|
|
55
|
+
var COMMIT_FETCH_LIMIT = 20;
|
|
56
|
+
var COMMIT_SELECTION_WINDOW = 8;
|
|
17
57
|
var ignoreList = [
|
|
18
58
|
"package.json",
|
|
19
59
|
"*.yml",
|
|
@@ -28,32 +68,275 @@ var ignoreList = [
|
|
|
28
68
|
".review-report/"
|
|
29
69
|
// 생성되는 리포트 폴더도 제외
|
|
30
70
|
];
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
71
|
+
function isTestMode(args = process.argv.slice(2)) {
|
|
72
|
+
return args.includes("--test");
|
|
73
|
+
}
|
|
74
|
+
function clearTraceMessages() {
|
|
75
|
+
traceMessages.length = 0;
|
|
76
|
+
}
|
|
77
|
+
function getTraceMessages() {
|
|
78
|
+
return [...traceMessages];
|
|
79
|
+
}
|
|
80
|
+
var ANSI = {
|
|
81
|
+
bold: "\x1B[1m",
|
|
82
|
+
cyan: "\x1B[36m",
|
|
83
|
+
dim: "\x1B[2m",
|
|
84
|
+
green: "\x1B[32m",
|
|
85
|
+
reset: "\x1B[0m",
|
|
86
|
+
yellow: "\x1B[33m"
|
|
87
|
+
};
|
|
88
|
+
var ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
89
|
+
var COMBINING_MARK_PATTERN = /\p{Mark}/u;
|
|
90
|
+
var GRAPHEME_SEGMENTER = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter("ko", { granularity: "grapheme" }) : null;
|
|
91
|
+
function getGitDiffPathspecs() {
|
|
92
|
+
return {
|
|
93
|
+
excludePatterns: ignoreList.map((item) => `:(exclude)${item}`),
|
|
94
|
+
includePatterns: ["*.ts", "*.tsx", "*.js", "*.jsx"]
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function segmentGraphemes(value) {
|
|
98
|
+
if (!GRAPHEME_SEGMENTER) {
|
|
99
|
+
return [...value];
|
|
100
|
+
}
|
|
101
|
+
return [...GRAPHEME_SEGMENTER.segment(value)].map(({ segment }) => segment);
|
|
102
|
+
}
|
|
103
|
+
function isWideCodePoint(codePoint) {
|
|
104
|
+
return codePoint >= 4352 && (codePoint <= 4447 || codePoint === 9001 || codePoint === 9002 || codePoint >= 11904 && codePoint <= 12871 && codePoint !== 12351 || codePoint >= 12880 && codePoint <= 19903 || codePoint >= 19968 && codePoint <= 42182 || codePoint >= 43360 && codePoint <= 43388 || codePoint >= 44032 && codePoint <= 55203 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 65040 && codePoint <= 65049 || codePoint >= 65072 && codePoint <= 65131 || codePoint >= 65281 && codePoint <= 65376 || codePoint >= 65504 && codePoint <= 65510 || codePoint >= 127488 && codePoint <= 127569 || codePoint >= 131072 && codePoint <= 262141);
|
|
105
|
+
}
|
|
106
|
+
function isEmojiCodePoint(codePoint) {
|
|
107
|
+
return codePoint >= 127462 && codePoint <= 127487 || codePoint >= 127744 && codePoint <= 129791 || codePoint >= 9728 && codePoint <= 10175;
|
|
108
|
+
}
|
|
109
|
+
function getGraphemeWidth(grapheme) {
|
|
110
|
+
let width = 0;
|
|
111
|
+
for (const character of grapheme) {
|
|
112
|
+
const codePoint = character.codePointAt(0);
|
|
113
|
+
if (!codePoint || COMBINING_MARK_PATTERN.test(character) || codePoint === 8205) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (codePoint >= 65024 && codePoint <= 65039 || codePoint >= 917760 && codePoint <= 917999) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (isWideCodePoint(codePoint) || isEmojiCodePoint(codePoint)) {
|
|
120
|
+
width = Math.max(width, 2);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
width = Math.max(width, 1);
|
|
124
|
+
}
|
|
125
|
+
return width;
|
|
126
|
+
}
|
|
127
|
+
function tokenizePlainText(value) {
|
|
128
|
+
return segmentGraphemes(value).map((segment) => ({
|
|
129
|
+
value: segment,
|
|
130
|
+
visibleWidth: getGraphemeWidth(segment)
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
function tokenizeVisibleText(value) {
|
|
134
|
+
const tokens = [];
|
|
135
|
+
let lastIndex = 0;
|
|
136
|
+
for (const match of value.matchAll(ANSI_PATTERN)) {
|
|
137
|
+
const index = match.index ?? 0;
|
|
138
|
+
if (index > lastIndex) {
|
|
139
|
+
tokens.push(...tokenizePlainText(value.slice(lastIndex, index)));
|
|
140
|
+
}
|
|
141
|
+
tokens.push({
|
|
142
|
+
value: match[0],
|
|
143
|
+
visibleWidth: 0
|
|
144
|
+
});
|
|
145
|
+
lastIndex = index + match[0].length;
|
|
146
|
+
}
|
|
147
|
+
if (lastIndex < value.length) {
|
|
148
|
+
tokens.push(...tokenizePlainText(value.slice(lastIndex)));
|
|
149
|
+
}
|
|
150
|
+
return tokens;
|
|
151
|
+
}
|
|
152
|
+
function truncateLineForTerminal(value, maxWidth) {
|
|
153
|
+
if (maxWidth <= 0) {
|
|
35
154
|
return "";
|
|
36
155
|
}
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
156
|
+
const tokens = tokenizeVisibleText(value);
|
|
157
|
+
const totalWidth = tokens.reduce((sum, token) => sum + token.visibleWidth, 0);
|
|
158
|
+
if (totalWidth <= maxWidth) {
|
|
159
|
+
return value;
|
|
40
160
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
);
|
|
44
|
-
|
|
161
|
+
const ellipsis = "...";
|
|
162
|
+
const ellipsisWidth = 3;
|
|
163
|
+
const targetWidth = Math.max(0, maxWidth - ellipsisWidth);
|
|
164
|
+
let usedWidth = 0;
|
|
165
|
+
let result = "";
|
|
166
|
+
for (const token of tokens) {
|
|
167
|
+
if (token.visibleWidth === 0) {
|
|
168
|
+
result += token.value;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (usedWidth + token.visibleWidth > targetWidth) {
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
result += token.value;
|
|
175
|
+
usedWidth += token.visibleWidth;
|
|
176
|
+
}
|
|
177
|
+
return `${result}${ellipsis}${ANSI.reset}`;
|
|
45
178
|
}
|
|
46
|
-
function
|
|
47
|
-
|
|
179
|
+
function fitLinesToTerminal(lines) {
|
|
180
|
+
const maxWidth = Math.max(20, (process.stdout.columns || 120) - 1);
|
|
181
|
+
return lines.map((line) => truncateLineForTerminal(line, maxWidth));
|
|
182
|
+
}
|
|
183
|
+
function runGitCommand(args, options = {}) {
|
|
184
|
+
const { allowFailure = false, trimOutput = true } = options;
|
|
185
|
+
try {
|
|
186
|
+
const output = execFileSync("git", args, {
|
|
187
|
+
encoding: "utf8",
|
|
188
|
+
maxBuffer: 1024 * 1024 * 20,
|
|
189
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
190
|
+
});
|
|
191
|
+
return trimOutput ? output.trim() : output;
|
|
192
|
+
} catch (error) {
|
|
193
|
+
helperTrace("git-command:failed", `${args.join(" ")} | ${getErrorSummary(error)}`);
|
|
194
|
+
if (allowFailure) {
|
|
195
|
+
return "";
|
|
196
|
+
}
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
async function executeShellCommandWithProgress(command, options = {}) {
|
|
201
|
+
const { progressIntervalMs = 1e4, progressMessage = "\u23F3 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uB294 \uC911\uC785\uB2C8\uB2E4...", streamOutput = false } = options;
|
|
202
|
+
const { spawn } = await import('child_process');
|
|
203
|
+
return new Promise((resolve, reject) => {
|
|
204
|
+
let stdout = "";
|
|
205
|
+
let stderr = "";
|
|
206
|
+
const startedAt = Date.now();
|
|
207
|
+
console.log(progressMessage);
|
|
208
|
+
const child = spawn("/bin/zsh", ["-lc", command], {
|
|
209
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
210
|
+
});
|
|
211
|
+
const progressTimer = setInterval(() => {
|
|
212
|
+
const elapsedSeconds = Math.max(1, Math.floor((Date.now() - startedAt) / 1e3));
|
|
213
|
+
console.log(`${progressMessage} (${elapsedSeconds}s \uACBD\uACFC)`);
|
|
214
|
+
}, progressIntervalMs);
|
|
215
|
+
child.stdout.on("data", (chunk) => {
|
|
216
|
+
const text = chunk.toString();
|
|
217
|
+
stdout += text;
|
|
218
|
+
if (streamOutput) {
|
|
219
|
+
process.stdout.write(text);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
child.stderr.on("data", (chunk) => {
|
|
223
|
+
const text = chunk.toString();
|
|
224
|
+
stderr += text;
|
|
225
|
+
if (streamOutput) {
|
|
226
|
+
process.stderr.write(text);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
child.on("error", (error) => {
|
|
230
|
+
clearInterval(progressTimer);
|
|
231
|
+
reject(error);
|
|
232
|
+
});
|
|
233
|
+
child.on("close", (code, signal) => {
|
|
234
|
+
clearInterval(progressTimer);
|
|
235
|
+
if (code === 0) {
|
|
236
|
+
resolve({
|
|
237
|
+
stderr,
|
|
238
|
+
stdout
|
|
239
|
+
});
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const exitSummary = signal ? `signal=${signal}` : `code=${String(code ?? "unknown")}`;
|
|
243
|
+
reject(new Error(`\uC258 \uBA85\uB839 \uC2E4\uD589 \uC2E4\uD328 (${exitSummary})${stderr.trim() ? `
|
|
244
|
+
${stderr.trim()}` : ""}`));
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
function formatReviewTargetFiles(files, visibleCount = 5) {
|
|
249
|
+
if (files.length === 0) {
|
|
250
|
+
return "(\uC5C6\uC74C)";
|
|
251
|
+
}
|
|
252
|
+
const visibleFiles = files.slice(0, visibleCount);
|
|
253
|
+
const hiddenCount = Math.max(0, files.length - visibleFiles.length);
|
|
254
|
+
if (hiddenCount === 0) {
|
|
255
|
+
return visibleFiles.join(", ");
|
|
256
|
+
}
|
|
257
|
+
return `${visibleFiles.join(", ")} \uC678 ${hiddenCount}\uAC1C`;
|
|
48
258
|
}
|
|
49
259
|
function createTraceLogger(scope, args = process.argv.slice(2)) {
|
|
50
260
|
const enabled = isTestMode(args);
|
|
51
261
|
return (step, detail) => {
|
|
262
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
263
|
+
const message = `[${timestamp}][TRACE][${scope}] ${step}${detail ? ` | ${detail}` : ""}`;
|
|
264
|
+
traceMessages.push(message);
|
|
52
265
|
if (!enabled) {
|
|
53
266
|
return;
|
|
54
267
|
}
|
|
55
|
-
console.log(
|
|
268
|
+
console.log(message);
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
var helperTrace = createTraceLogger("helper");
|
|
272
|
+
function getTimestampParts(now = /* @__PURE__ */ new Date()) {
|
|
273
|
+
return {
|
|
274
|
+
YYYY: now.getFullYear(),
|
|
275
|
+
MM: String(now.getMonth() + 1).padStart(2, "0"),
|
|
276
|
+
DD: String(now.getDate()).padStart(2, "0"),
|
|
277
|
+
HH: String(now.getHours()).padStart(2, "0"),
|
|
278
|
+
mm: String(now.getMinutes()).padStart(2, "0"),
|
|
279
|
+
ss: String(now.getSeconds()).padStart(2, "0")
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function getHumanReadableNowString(now = /* @__PURE__ */ new Date()) {
|
|
283
|
+
const { YYYY, MM, DD, HH, mm, ss } = getTimestampParts(now);
|
|
284
|
+
return `${YYYY}-${MM}-${DD} ${HH}:${mm}:${ss}`;
|
|
285
|
+
}
|
|
286
|
+
function stringifyUnknown(value) {
|
|
287
|
+
if (value === void 0 || value === null) {
|
|
288
|
+
return "";
|
|
289
|
+
}
|
|
290
|
+
if (typeof value === "string") {
|
|
291
|
+
return value;
|
|
292
|
+
}
|
|
293
|
+
if (Buffer.isBuffer(value)) {
|
|
294
|
+
return value.toString();
|
|
295
|
+
}
|
|
296
|
+
if (value instanceof Error) {
|
|
297
|
+
return value.stack || value.message;
|
|
298
|
+
}
|
|
299
|
+
return inspect(value, { depth: 5, breakLength: 120 });
|
|
300
|
+
}
|
|
301
|
+
function getErrorSummary(error) {
|
|
302
|
+
if (error instanceof Error) {
|
|
303
|
+
return `${error.name}: ${error.message}`;
|
|
304
|
+
}
|
|
305
|
+
return stringifyUnknown(error) || "Unknown error";
|
|
306
|
+
}
|
|
307
|
+
function serializeError(error) {
|
|
308
|
+
const serialized = {
|
|
309
|
+
summary: getErrorSummary(error)
|
|
56
310
|
};
|
|
311
|
+
if (error instanceof Error) {
|
|
312
|
+
serialized.name = error.name;
|
|
313
|
+
serialized.message = error.message;
|
|
314
|
+
serialized.stack = error.stack;
|
|
315
|
+
} else {
|
|
316
|
+
serialized.value = stringifyUnknown(error);
|
|
317
|
+
}
|
|
318
|
+
if (error && typeof error === "object") {
|
|
319
|
+
const errorLike = error;
|
|
320
|
+
const extraKeys = ["code", "errno", "syscall", "path", "cmd", "status", "signal", "spawnargs"];
|
|
321
|
+
extraKeys.forEach((key) => {
|
|
322
|
+
if (errorLike[key] !== void 0) {
|
|
323
|
+
serialized[key] = errorLike[key];
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
const stdout = stringifyUnknown(errorLike.stdout);
|
|
327
|
+
if (stdout) {
|
|
328
|
+
serialized.stdout = stdout;
|
|
329
|
+
}
|
|
330
|
+
const stderr = stringifyUnknown(errorLike.stderr);
|
|
331
|
+
if (stderr) {
|
|
332
|
+
serialized.stderr = stderr;
|
|
333
|
+
}
|
|
334
|
+
const cause = stringifyUnknown(errorLike.cause);
|
|
335
|
+
if (cause) {
|
|
336
|
+
serialized.cause = cause;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return serialized;
|
|
57
340
|
}
|
|
58
341
|
function getNextFilePath(dir, baseName, extension) {
|
|
59
342
|
let counter = 1;
|
|
@@ -65,6 +348,13 @@ function getNextFilePath(dir, baseName, extension) {
|
|
|
65
348
|
counter++;
|
|
66
349
|
}
|
|
67
350
|
}
|
|
351
|
+
function getAvailableFilePath(dir, baseName, extension) {
|
|
352
|
+
const firstFilePath = path.join(dir, `${baseName}${extension}`);
|
|
353
|
+
if (!fs.existsSync(firstFilePath)) {
|
|
354
|
+
return firstFilePath;
|
|
355
|
+
}
|
|
356
|
+
return getNextFilePath(dir, baseName, extension);
|
|
357
|
+
}
|
|
68
358
|
function deleteFile(filePath) {
|
|
69
359
|
if (fs.existsSync(filePath)) {
|
|
70
360
|
fs.unlinkSync(filePath);
|
|
@@ -78,27 +368,194 @@ function createReportDirectory() {
|
|
|
78
368
|
fs.mkdirSync(REPORT_DIR, { recursive: true });
|
|
79
369
|
}
|
|
80
370
|
}
|
|
81
|
-
function getNowString() {
|
|
82
|
-
const
|
|
83
|
-
const YYYY = now.getFullYear();
|
|
84
|
-
const MM = String(now.getMonth() + 1).padStart(2, "0");
|
|
85
|
-
const DD = String(now.getDate()).padStart(2, "0");
|
|
86
|
-
const HH = String(now.getHours()).padStart(2, "0");
|
|
87
|
-
const mm = String(now.getMinutes()).padStart(2, "0");
|
|
88
|
-
const ss = String(now.getSeconds()).padStart(2, "0");
|
|
371
|
+
function getNowString(now = /* @__PURE__ */ new Date()) {
|
|
372
|
+
const { YYYY, MM, DD, HH, mm, ss } = getTimestampParts(now);
|
|
89
373
|
return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;
|
|
90
374
|
}
|
|
375
|
+
function getErrorLogTimestamp(now = /* @__PURE__ */ new Date()) {
|
|
376
|
+
const { YYYY, MM, DD, HH, mm, ss } = getTimestampParts(now);
|
|
377
|
+
return `${YYYY}-${MM}-${DD}-${HH}\uC2DC-${mm}\uBD84-${ss}\uCD08`;
|
|
378
|
+
}
|
|
379
|
+
function writeErrorReport(error, options = {}) {
|
|
380
|
+
try {
|
|
381
|
+
const now = /* @__PURE__ */ new Date();
|
|
382
|
+
helperTrace("error-report:write:start", options.scope || "unknown");
|
|
383
|
+
createReportDirectory();
|
|
384
|
+
const reportPath = getAvailableFilePath(REPORT_DIR, `error-log-${getErrorLogTimestamp(now)}`, ".md");
|
|
385
|
+
const serializedError = serializeError(error);
|
|
386
|
+
const traceSnapshot = options.traceMessages ?? getTraceMessages();
|
|
387
|
+
const extraSections = options.extraSections || [];
|
|
388
|
+
const report = `# Error Log
|
|
389
|
+
|
|
390
|
+
- \uBC1C\uC0DD \uC2DC\uAC01: ${getHumanReadableNowString(now)}
|
|
391
|
+
- Scope: \`${options.scope || "unknown"}\`
|
|
392
|
+
- \uC791\uC5C5 \uACBD\uB85C: \`${process.cwd()}\`
|
|
393
|
+
- \uC2E4\uD589 \uC778\uC790: \`${JSON.stringify(options.args ?? process.argv.slice(2))}\`
|
|
394
|
+
- \uC2E4\uD589 \uD658\uACBD: \`${process.platform} ${process.arch} / Node ${process.version}\`
|
|
395
|
+
|
|
396
|
+
## Summary
|
|
397
|
+
|
|
398
|
+
${options.title || serializedError.summary || "Unknown error"}
|
|
399
|
+
|
|
400
|
+
## Error
|
|
401
|
+
|
|
402
|
+
\`\`\`json
|
|
403
|
+
${JSON.stringify(serializedError, null, 2)}
|
|
404
|
+
\`\`\`
|
|
405
|
+
|
|
406
|
+
## Trace
|
|
407
|
+
|
|
408
|
+
\`\`\`json
|
|
409
|
+
${JSON.stringify(traceSnapshot, null, 2)}
|
|
410
|
+
\`\`\`${extraSections.length ? `
|
|
411
|
+
${extraSections.map((section) => `
|
|
412
|
+
## ${section.heading}
|
|
413
|
+
|
|
414
|
+
${section.markdown}`).join("\n")}
|
|
415
|
+
` : "\n"}
|
|
416
|
+
`;
|
|
417
|
+
fs.writeFileSync(reportPath, report);
|
|
418
|
+
helperTrace("error-report:write:done", reportPath);
|
|
419
|
+
return reportPath;
|
|
420
|
+
} catch (writeError) {
|
|
421
|
+
console.error("\u26A0\uFE0F \uC5D0\uB7EC \uB85C\uADF8 \uD30C\uC77C \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
|
|
422
|
+
console.error(writeError);
|
|
423
|
+
return "";
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
function exitWithError(message, options = {}) {
|
|
427
|
+
const reportPath = writeErrorReport(options.error || new Error(message), {
|
|
428
|
+
...options,
|
|
429
|
+
title: message
|
|
430
|
+
});
|
|
431
|
+
console.error(message);
|
|
432
|
+
if (options.error) {
|
|
433
|
+
console.error(options.error);
|
|
434
|
+
}
|
|
435
|
+
if (reportPath) {
|
|
436
|
+
console.error(`\u{1F4C4} \uC5D0\uB7EC \uB85C\uADF8 \uC800\uC7A5 \uC704\uCE58: ${reportPath}`);
|
|
437
|
+
}
|
|
438
|
+
process.exit(1);
|
|
439
|
+
}
|
|
440
|
+
function parseServiceFromArgs(args = process.argv.slice(2)) {
|
|
441
|
+
helperTrace("parse-service:start", `args=${JSON.stringify(args)}`);
|
|
442
|
+
const serviceIndex = args.indexOf("--service");
|
|
443
|
+
const rawService = serviceIndex !== -1 ? args[serviceIndex + 1] : "";
|
|
444
|
+
if (!rawService) {
|
|
445
|
+
helperTrace("parse-service:empty");
|
|
446
|
+
return "";
|
|
447
|
+
}
|
|
448
|
+
const normalizedService = rawService.toLowerCase();
|
|
449
|
+
if (AIServices.includes(normalizedService)) {
|
|
450
|
+
helperTrace("parse-service:resolved", normalizedService);
|
|
451
|
+
return normalizedService;
|
|
452
|
+
}
|
|
453
|
+
helperTrace("parse-service:invalid", rawService);
|
|
454
|
+
exitWithError(
|
|
455
|
+
`\u274C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC11C\uBE44\uC2A4\uC785\uB2C8\uB2E4: ${rawService}. \uC0AC\uC6A9 \uAC00\uB2A5 \uAC12: ${AIServices.join(", ")} (\uC608: --service codex)`,
|
|
456
|
+
{
|
|
457
|
+
scope: "helper:parseServiceFromArgs",
|
|
458
|
+
args,
|
|
459
|
+
extraSections: [
|
|
460
|
+
{
|
|
461
|
+
heading: "Allowed Services",
|
|
462
|
+
markdown: `\`\`\`json
|
|
463
|
+
${JSON.stringify(AIServices, null, 2)}
|
|
464
|
+
\`\`\``
|
|
465
|
+
}
|
|
466
|
+
]
|
|
467
|
+
}
|
|
468
|
+
);
|
|
469
|
+
}
|
|
91
470
|
function getGitDiffFilter() {
|
|
92
|
-
const
|
|
93
|
-
const excludePatterns = ignoreList.map((item) => `:(exclude)${item}`);
|
|
471
|
+
const { includePatterns, excludePatterns } = getGitDiffPathspecs();
|
|
94
472
|
const quote = (pattern) => `"${pattern}"`;
|
|
95
|
-
const includeParams =
|
|
473
|
+
const includeParams = includePatterns.map(quote).join(" ");
|
|
96
474
|
const excludeParams = excludePatterns.map(quote).join(" ");
|
|
97
475
|
return { includeParams, excludeParams };
|
|
98
476
|
}
|
|
477
|
+
function truncateCommitSubject(subject) {
|
|
478
|
+
if (subject.length <= 72) {
|
|
479
|
+
return subject;
|
|
480
|
+
}
|
|
481
|
+
return `${subject.slice(0, 69)}...`;
|
|
482
|
+
}
|
|
483
|
+
function getRecentCommitOptions() {
|
|
484
|
+
const output = runGitCommand(
|
|
485
|
+
["log", `-${COMMIT_FETCH_LIMIT}`, "--date=relative", "--pretty=format:%h%x09%an%x09%ar%x09%s"],
|
|
486
|
+
{ allowFailure: true }
|
|
487
|
+
);
|
|
488
|
+
if (!output) {
|
|
489
|
+
return [];
|
|
490
|
+
}
|
|
491
|
+
return output.split("\n").map((line) => {
|
|
492
|
+
const [hash = "", author = "", relativeDate = "", ...subjectParts] = line.split(" ");
|
|
493
|
+
const subject = subjectParts.join(" ").trim();
|
|
494
|
+
return {
|
|
495
|
+
author,
|
|
496
|
+
description: `${author} | ${relativeDate}`,
|
|
497
|
+
hash,
|
|
498
|
+
label: `${hash} | ${truncateCommitSubject(subject)}`,
|
|
499
|
+
relativeDate,
|
|
500
|
+
subject
|
|
501
|
+
};
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
function buildSelectedCommitSummary(commits) {
|
|
505
|
+
return commits.map((commit) => `- ${commit.hash} | ${commit.subject} | ${commit.author} | ${commit.relativeDate}`).join("\n");
|
|
506
|
+
}
|
|
507
|
+
function getReviewPathspecArgs() {
|
|
508
|
+
const { includePatterns, excludePatterns } = getGitDiffPathspecs();
|
|
509
|
+
return [...includePatterns, ...excludePatterns];
|
|
510
|
+
}
|
|
511
|
+
function buildSelectedCommitDiff(commits) {
|
|
512
|
+
const reviewPathspecArgs = getReviewPathspecArgs();
|
|
513
|
+
const sections = commits.map((commit) => {
|
|
514
|
+
const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", ...reviewPathspecArgs], {
|
|
515
|
+
allowFailure: true,
|
|
516
|
+
trimOutput: false
|
|
517
|
+
}).trim();
|
|
518
|
+
if (!diff) {
|
|
519
|
+
return "";
|
|
520
|
+
}
|
|
521
|
+
return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
|
|
522
|
+
}).filter(Boolean).join("\n\n");
|
|
523
|
+
if (!sections) {
|
|
524
|
+
return "";
|
|
525
|
+
}
|
|
526
|
+
return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", "# \uB9AC\uBDF0 \uB300\uC0C1 diff", sections].join("\n");
|
|
527
|
+
}
|
|
528
|
+
function getSelectedCommitFiles(commits) {
|
|
529
|
+
const reviewPathspecArgs = getReviewPathspecArgs();
|
|
530
|
+
const files = /* @__PURE__ */ new Set();
|
|
531
|
+
commits.forEach((commit) => {
|
|
532
|
+
const output = runGitCommand(["show", "--pretty=format:", "--name-only", commit.hash, "--", ...reviewPathspecArgs], {
|
|
533
|
+
allowFailure: true
|
|
534
|
+
});
|
|
535
|
+
output.split("\n").map((line) => line.trim()).filter(Boolean).forEach((filePath) => files.add(filePath));
|
|
536
|
+
});
|
|
537
|
+
return [...files];
|
|
538
|
+
}
|
|
539
|
+
function buildSelectedFileDiff(commits, filePath) {
|
|
540
|
+
const sections = commits.map((commit) => {
|
|
541
|
+
const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", filePath], {
|
|
542
|
+
allowFailure: true,
|
|
543
|
+
trimOutput: false
|
|
544
|
+
}).trim();
|
|
545
|
+
if (!diff) {
|
|
546
|
+
return "";
|
|
547
|
+
}
|
|
548
|
+
return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
|
|
549
|
+
}).filter(Boolean).join("\n\n");
|
|
550
|
+
if (!sections) {
|
|
551
|
+
return "";
|
|
552
|
+
}
|
|
553
|
+
return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", `# \uD30C\uC77C: ${filePath}`, sections].join("\n\n");
|
|
554
|
+
}
|
|
99
555
|
function openReport(reportPath) {
|
|
100
556
|
const resolvedPath = path.resolve(reportPath);
|
|
101
557
|
const { platform } = process;
|
|
558
|
+
helperTrace("open-report:start", resolvedPath);
|
|
102
559
|
const openWithChrome = () => {
|
|
103
560
|
if (platform === "darwin") {
|
|
104
561
|
execSync(`open -a "Google Chrome" "${resolvedPath}"`, { stdio: "ignore" });
|
|
@@ -123,71 +580,199 @@ function openReport(reportPath) {
|
|
|
123
580
|
};
|
|
124
581
|
try {
|
|
125
582
|
if (openWithChrome()) {
|
|
583
|
+
helperTrace("open-report:chrome:success", platform);
|
|
126
584
|
console.log("\u{1F680} Google Chrome\uC5D0\uC11C \uB9AC\uD3EC\uD2B8\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
127
585
|
return;
|
|
128
586
|
}
|
|
129
|
-
} catch {
|
|
587
|
+
} catch (error) {
|
|
588
|
+
helperTrace("open-report:chrome:failed", getErrorSummary(error));
|
|
130
589
|
}
|
|
131
590
|
try {
|
|
132
591
|
if (openWithDefaultBrowser()) {
|
|
592
|
+
helperTrace("open-report:default-browser:success", platform);
|
|
133
593
|
console.log("\u{1F680} \uAE30\uBCF8 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB9AC\uD3EC\uD2B8\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
134
594
|
return;
|
|
135
595
|
}
|
|
136
|
-
} catch (
|
|
137
|
-
|
|
596
|
+
} catch (error) {
|
|
597
|
+
helperTrace("open-report:default-browser:failed", getErrorSummary(error));
|
|
598
|
+
console.error("\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800 \uC5F4\uAE30 \uC2E4\uD328:", error);
|
|
138
599
|
return;
|
|
139
600
|
}
|
|
601
|
+
helperTrace("open-report:unsupported-platform", platform);
|
|
140
602
|
console.error(`\u26A0\uFE0F \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD50C\uB7AB\uD3FC\uC785\uB2C8\uB2E4: ${platform}`);
|
|
141
603
|
}
|
|
142
|
-
function
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
604
|
+
function ensureInteractiveSelectionAvailable(scope, message) {
|
|
605
|
+
if (process.stdin.isTTY && process.stdout.isTTY && typeof process.stdin.setRawMode === "function") {
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
helperTrace(`${scope}:tty-missing`);
|
|
609
|
+
exitWithError(message, {
|
|
610
|
+
scope: `helper:${scope}`
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
function renderSelectionBlock(lines, previousLineCount) {
|
|
614
|
+
if (previousLineCount > 0) {
|
|
615
|
+
readline.moveCursor(process.stdout, 0, -previousLineCount);
|
|
616
|
+
readline.clearScreenDown(process.stdout);
|
|
617
|
+
}
|
|
618
|
+
const fittedLines = fitLinesToTerminal(lines);
|
|
619
|
+
process.stdout.write(`${fittedLines.join("\n")}
|
|
620
|
+
`);
|
|
621
|
+
return fittedLines.length;
|
|
622
|
+
}
|
|
623
|
+
function getSelectionWindowRange(optionCount, selectedIndex, windowSize) {
|
|
624
|
+
if (optionCount <= windowSize) {
|
|
625
|
+
return {
|
|
626
|
+
end: optionCount,
|
|
627
|
+
start: 0
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
const halfWindow = Math.floor(windowSize / 2);
|
|
631
|
+
const maxStart = optionCount - windowSize;
|
|
632
|
+
const start = Math.max(0, Math.min(selectedIndex - halfWindow, maxStart));
|
|
633
|
+
return {
|
|
634
|
+
end: Math.min(optionCount, start + windowSize),
|
|
635
|
+
start
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
function buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize) {
|
|
639
|
+
const { start, end } = getSelectionWindowRange(options.length, selectedIndex, windowSize);
|
|
640
|
+
const lines = [
|
|
641
|
+
`${ANSI.bold}${question}${ANSI.reset}`,
|
|
642
|
+
`${ANSI.dim}\u2191\u2193 \uC774\uB3D9 | Space \uC120\uD0DD/\uD574\uC81C | Enter \uC644\uB8CC | Esc \uCDE8\uC18C${ANSI.reset}`,
|
|
643
|
+
`${ANSI.dim}\uC120\uD0DD\uB428: ${toggled.size}\uAC1C / \uC804\uCCB4: ${options.length}\uAC1C${ANSI.reset}`
|
|
644
|
+
];
|
|
645
|
+
for (let index = start; index < end; index += 1) {
|
|
646
|
+
const option = options[index];
|
|
647
|
+
if (!option) {
|
|
648
|
+
continue;
|
|
160
649
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
650
|
+
const cursor = index === selectedIndex ? `${ANSI.cyan}>${ANSI.reset}` : " ";
|
|
651
|
+
const checked = toggled.has(index) ? `${ANSI.green}\u2611${ANSI.reset}` : "\u2610";
|
|
652
|
+
const description = option.description ? ` ${ANSI.dim}${option.description}${ANSI.reset}` : "";
|
|
653
|
+
lines.push(`${cursor} ${checked} ${option.label}${description}`);
|
|
654
|
+
}
|
|
655
|
+
if (options.length > windowSize) {
|
|
656
|
+
lines.push(`${ANSI.dim}\uD45C\uC2DC \uBC94\uC704: ${start + 1}-${end} / ${options.length}${ANSI.reset}`);
|
|
657
|
+
}
|
|
658
|
+
return lines;
|
|
659
|
+
}
|
|
660
|
+
async function showMultiSelect(question, options, windowSize = COMMIT_SELECTION_WINDOW) {
|
|
661
|
+
ensureInteractiveSelectionAvailable("showMultiSelect", "\u274C \uCEE4\uBC0B \uC120\uD0DD \uBAA8\uB2EC\uC740 TTY \uD658\uACBD\uC5D0\uC11C\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
662
|
+
let selectedIndex = 0;
|
|
663
|
+
let renderedLineCount = 0;
|
|
664
|
+
const toggled = /* @__PURE__ */ new Set();
|
|
665
|
+
const rl = readline.createInterface({
|
|
666
|
+
input: process.stdin,
|
|
667
|
+
output: process.stdout,
|
|
668
|
+
terminal: true
|
|
669
|
+
});
|
|
670
|
+
process.stdout.write("\x1B[?25l");
|
|
671
|
+
const cleanup = () => {
|
|
672
|
+
if (renderedLineCount > 0) {
|
|
673
|
+
readline.moveCursor(process.stdout, 0, -renderedLineCount);
|
|
674
|
+
readline.clearScreenDown(process.stdout);
|
|
675
|
+
renderedLineCount = 0;
|
|
171
676
|
}
|
|
677
|
+
process.stdin.removeListener("data", onData);
|
|
678
|
+
process.stdin.setRawMode(false);
|
|
679
|
+
process.stdin.pause();
|
|
680
|
+
rl.close();
|
|
681
|
+
process.stdout.write("\x1B[?25h");
|
|
682
|
+
};
|
|
683
|
+
const render = () => {
|
|
684
|
+
const lines = buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize);
|
|
685
|
+
renderedLineCount = renderSelectionBlock(lines, renderedLineCount);
|
|
686
|
+
};
|
|
687
|
+
const confirmSelection = (resolve) => {
|
|
688
|
+
const values = [...toggled].sort((left, right) => left - right).map((index) => options[index]?.value).filter((value) => value !== void 0);
|
|
689
|
+
cleanup();
|
|
690
|
+
resolve(values);
|
|
691
|
+
};
|
|
692
|
+
const cancelSelection = (resolve) => {
|
|
693
|
+
cleanup();
|
|
694
|
+
resolve([]);
|
|
695
|
+
};
|
|
696
|
+
let onData = (_data) => {
|
|
697
|
+
};
|
|
698
|
+
render();
|
|
699
|
+
return new Promise((resolve) => {
|
|
700
|
+
onData = (data) => {
|
|
701
|
+
const key = data.toString();
|
|
702
|
+
if (key === "") {
|
|
703
|
+
cleanup();
|
|
704
|
+
process.exit(0);
|
|
705
|
+
}
|
|
706
|
+
if (key === "\x1B") {
|
|
707
|
+
cancelSelection(resolve);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
if (key === "\x1B[A") {
|
|
711
|
+
selectedIndex = (selectedIndex - 1 + options.length) % options.length;
|
|
712
|
+
render();
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
if (key === "\x1B[B") {
|
|
716
|
+
selectedIndex = (selectedIndex + 1) % options.length;
|
|
717
|
+
render();
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
if (key === " ") {
|
|
721
|
+
if (toggled.has(selectedIndex)) {
|
|
722
|
+
toggled.delete(selectedIndex);
|
|
723
|
+
} else {
|
|
724
|
+
toggled.add(selectedIndex);
|
|
725
|
+
}
|
|
726
|
+
render();
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
if (key === "\r" || key === "\n") {
|
|
730
|
+
confirmSelection(resolve);
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
process.stdin.setRawMode(true);
|
|
734
|
+
process.stdin.resume();
|
|
735
|
+
process.stdin.on("data", onData);
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
async function selectReviewCommits() {
|
|
739
|
+
const commits = getRecentCommitOptions();
|
|
740
|
+
if (commits.length === 0) {
|
|
741
|
+
console.log("\u2139\uFE0F \uB9AC\uBDF0\uD560 \uCD5C\uADFC \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
742
|
+
return [];
|
|
172
743
|
}
|
|
173
|
-
return
|
|
744
|
+
return showMultiSelect(
|
|
745
|
+
"\uB9AC\uBDF0\uD560 \uCEE4\uBC0B\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.",
|
|
746
|
+
commits.map((commit) => ({
|
|
747
|
+
description: commit.description,
|
|
748
|
+
label: commit.label,
|
|
749
|
+
value: commit
|
|
750
|
+
})),
|
|
751
|
+
COMMIT_SELECTION_WINDOW
|
|
752
|
+
);
|
|
174
753
|
}
|
|
175
754
|
function selectAIService() {
|
|
176
755
|
const service = parseServiceFromArgs();
|
|
177
756
|
if (!service) {
|
|
178
|
-
|
|
179
|
-
|
|
757
|
+
helperTrace("select-service:missing");
|
|
758
|
+
exitWithError("\u274C \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.", {
|
|
759
|
+
scope: "helper:selectAIService"
|
|
760
|
+
});
|
|
180
761
|
}
|
|
762
|
+
helperTrace("select-service:done", service);
|
|
181
763
|
return service;
|
|
182
764
|
}
|
|
183
765
|
async function showSelectionAIService() {
|
|
184
766
|
const selectedServiceFromArgs = parseServiceFromArgs();
|
|
185
767
|
if (selectedServiceFromArgs) {
|
|
768
|
+
helperTrace("show-selection:from-args", selectedServiceFromArgs);
|
|
186
769
|
console.log(`
|
|
187
|
-
\u2705
|
|
770
|
+
\u2705 ${ANSI.green}${selectedServiceFromArgs}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (--service)
|
|
188
771
|
`);
|
|
189
772
|
return selectedServiceFromArgs;
|
|
190
773
|
}
|
|
774
|
+
ensureInteractiveSelectionAvailable("showSelectionAIService", "\u274C AI \uC11C\uBE44\uC2A4 \uC120\uD0DD UI\uB294 TTY \uD658\uACBD\uC5D0\uC11C\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
775
|
+
helperTrace("show-selection:interactive:start");
|
|
191
776
|
let selectedIndex = 0;
|
|
192
777
|
const rl = readline.createInterface({
|
|
193
778
|
input: process.stdin,
|
|
@@ -201,13 +786,15 @@ async function showSelectionAIService() {
|
|
|
201
786
|
readline.moveCursor(process.stdout, 0, -(AIServices.length + 1));
|
|
202
787
|
}
|
|
203
788
|
firstRender = false;
|
|
789
|
+
helperTrace("show-selection:interactive:render", AIServices[selectedIndex] || "unknown");
|
|
204
790
|
readline.clearScreenDown(process.stdout);
|
|
205
791
|
process.stdout.write(
|
|
206
|
-
|
|
792
|
+
`\u{1F916} AI \uC11C\uBE44\uC2A4\uB97C \uC120\uD0DD\uD574\uC8FC\uC138\uC694 (${ANSI.yellow}\u2191\u2193 \uBC29\uD5A5\uD0A4${ANSI.reset} \uC774\uB3D9, ${ANSI.yellow}Enter${ANSI.reset} \uC120\uD0DD):
|
|
793
|
+
`
|
|
207
794
|
);
|
|
208
795
|
AIServices.forEach((service, index) => {
|
|
209
796
|
if (index === selectedIndex) {
|
|
210
|
-
process.stdout.write(`
|
|
797
|
+
process.stdout.write(` ${ANSI.cyan}>${ANSI.reset} ${ANSI.cyan}\u25C9${ANSI.reset} ${ANSI.bold}${service}${ANSI.reset}
|
|
211
798
|
`);
|
|
212
799
|
} else {
|
|
213
800
|
process.stdout.write(` \u25EF ${service}
|
|
@@ -220,6 +807,7 @@ async function showSelectionAIService() {
|
|
|
220
807
|
const onData = (data) => {
|
|
221
808
|
const key = data.toString();
|
|
222
809
|
if (key === "") {
|
|
810
|
+
helperTrace("show-selection:interactive:ctrl-c");
|
|
223
811
|
process.stdout.write("\x1B[?25h");
|
|
224
812
|
process.exit(0);
|
|
225
813
|
}
|
|
@@ -236,10 +824,11 @@ async function showSelectionAIService() {
|
|
|
236
824
|
rl.close();
|
|
237
825
|
process.stdout.write("\x1B[?25h");
|
|
238
826
|
console.log(`
|
|
239
|
-
\u2705
|
|
827
|
+
\u2705 ${ANSI.green}${AIServices[selectedIndex]}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
|
|
240
828
|
`);
|
|
241
829
|
const result = AIServices[selectedIndex];
|
|
242
830
|
if (result) {
|
|
831
|
+
helperTrace("show-selection:interactive:confirmed", result);
|
|
243
832
|
resolve(result);
|
|
244
833
|
}
|
|
245
834
|
}
|
|
@@ -250,6 +839,6 @@ async function showSelectionAIService() {
|
|
|
250
839
|
});
|
|
251
840
|
}
|
|
252
841
|
|
|
253
|
-
export { AIServices, REPORT_DIR, codingConventionRulesPath, createReportDirectory, createTraceLogger, deleteFile, deleteTempDiff,
|
|
842
|
+
export { AIServices, COMMIT_FETCH_LIMIT, COMMIT_SELECTION_WINDOW, REPORT_DIR, buildSelectedCommitDiff, buildSelectedCommitSummary, buildSelectedFileDiff, clearTraceMessages, codingConventionRulesPath, createReportDirectory, createTraceLogger, deleteFile, deleteTempDiff, executeShellCommandWithProgress, exitWithError, formatReviewTargetFiles, getAvailableFilePath, getErrorLogTimestamp, getErrorSummary, getGitDiffFilter, getNextFilePath, getNowString, getRecentCommitOptions, getSelectedCommitFiles, getTraceMessages, ignoreList, isTestMode, namingRulesPath, openReport, reviewFormOneByOnePath, reviewFormPath, rulesPath, selectAIService, selectReviewCommits, showMultiSelect, showSelectionAIService, tempDiffPath, writeErrorReport };
|
|
254
843
|
//# sourceMappingURL=helper.js.map
|
|
255
844
|
//# sourceMappingURL=helper.js.map
|