sales-frontend-gemini-cli 0.4.2 → 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 +464 -52
- package/dist/common/helper.cjs.map +1 -1
- package/dist/common/helper.d.cts +85 -3
- package/dist/common/helper.d.ts +85 -3
- package/dist/common/helper.js +455 -53
- 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 +50 -9
- package/dist/pr-review/claude/claude-commander.cjs.map +1 -1
- package/dist/pr-review/claude/claude-commander.js +50 -9
- package/dist/pr-review/claude/claude-commander.js.map +1 -1
- package/dist/pr-review/claude/installation-claude.cjs +41 -5
- package/dist/pr-review/claude/installation-claude.cjs.map +1 -1
- package/dist/pr-review/claude/installation-claude.js +41 -5
- package/dist/pr-review/claude/installation-claude.js.map +1 -1
- package/dist/pr-review/codex/codex-commander.cjs +49 -8
- package/dist/pr-review/codex/codex-commander.cjs.map +1 -1
- package/dist/pr-review/codex/codex-commander.js +49 -8
- package/dist/pr-review/codex/codex-commander.js.map +1 -1
- package/dist/pr-review/codex/installation-codex.cjs +41 -5
- package/dist/pr-review/codex/installation-codex.cjs.map +1 -1
- package/dist/pr-review/codex/installation-codex.js +41 -5
- package/dist/pr-review/codex/installation-codex.js.map +1 -1
- package/dist/pr-review/gemini/gemini-commander.cjs +75 -15
- package/dist/pr-review/gemini/gemini-commander.cjs.map +1 -1
- package/dist/pr-review/gemini/gemini-commander.js +75 -15
- package/dist/pr-review/gemini/gemini-commander.js.map +1 -1
- package/dist/pr-review/gemini/installation-gemini.cjs +41 -5
- package/dist/pr-review/gemini/installation-gemini.cjs.map +1 -1
- package/dist/pr-review/gemini/installation-gemini.js +41 -5
- package/dist/pr-review/gemini/installation-gemini.js.map +1 -1
- package/dist/pr-review/review-one-by-one.cjs +489 -95
- package/dist/pr-review/review-one-by-one.cjs.map +1 -1
- package/dist/pr-review/review-one-by-one.js +490 -96
- package/dist/pr-review/review-one-by-one.js.map +1 -1
- package/dist/pr-review/review.cjs +517 -94
- package/dist/pr-review/review.cjs.map +1 -1
- package/dist/pr-review/review.js +517 -94
- 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.cjs
CHANGED
|
@@ -17,14 +17,52 @@ var readline__default = /*#__PURE__*/_interopDefault(readline);
|
|
|
17
17
|
// src/common/helper.ts
|
|
18
18
|
var __dirname$1 = path__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('helper.cjs', document.baseURI).href))));
|
|
19
19
|
var traceMessages = [];
|
|
20
|
-
var
|
|
21
|
-
var
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
var GEMINI_CLI_PACKAGE_NAME = "sales-frontend-gemini-cli";
|
|
21
|
+
var cachedPackageRootPath = "";
|
|
22
|
+
function isGeminiCliPackageRoot(directory) {
|
|
23
|
+
const packageJsonPath = path__default.default.join(directory, "package.json");
|
|
24
|
+
if (!fs__default.default.existsSync(packageJsonPath)) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const packageJson = JSON.parse(fs__default.default.readFileSync(packageJsonPath, "utf8"));
|
|
29
|
+
return packageJson.name === GEMINI_CLI_PACKAGE_NAME;
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function resolveGeminiCliPackageRoot(startDirectory = __dirname$1) {
|
|
35
|
+
if (cachedPackageRootPath) {
|
|
36
|
+
return cachedPackageRootPath;
|
|
37
|
+
}
|
|
38
|
+
let currentDirectory = startDirectory;
|
|
39
|
+
while (true) {
|
|
40
|
+
if (isGeminiCliPackageRoot(currentDirectory)) {
|
|
41
|
+
cachedPackageRootPath = currentDirectory;
|
|
42
|
+
return cachedPackageRootPath;
|
|
43
|
+
}
|
|
44
|
+
const parentDirectory = path__default.default.dirname(currentDirectory);
|
|
45
|
+
if (parentDirectory === currentDirectory) {
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
currentDirectory = parentDirectory;
|
|
49
|
+
}
|
|
50
|
+
cachedPackageRootPath = path__default.default.resolve(startDirectory, "../..");
|
|
51
|
+
return cachedPackageRootPath;
|
|
52
|
+
}
|
|
53
|
+
function resolvePackageAssetPath(relativeFilePath) {
|
|
54
|
+
return path__default.default.resolve(resolveGeminiCliPackageRoot(), relativeFilePath);
|
|
55
|
+
}
|
|
56
|
+
var rulesPath = resolvePackageAssetPath("src/common/rules/review-rules.md");
|
|
57
|
+
var namingRulesPath = resolvePackageAssetPath("src/common/rules/naming-rule.md");
|
|
58
|
+
var codingConventionRulesPath = resolvePackageAssetPath("src/common/rules/coding-convention.md");
|
|
59
|
+
var reviewFormPath = resolvePackageAssetPath("src/common/form/review-form.md");
|
|
60
|
+
var reviewFormOneByOnePath = resolvePackageAssetPath("src/common/form/review-form-one-by-one.md");
|
|
25
61
|
var REPORT_DIR = ".review-report";
|
|
26
62
|
var tempDiffPath = "temp_diff.txt";
|
|
27
63
|
var AIServices = ["gemini", "claude", "codex"];
|
|
64
|
+
var COMMIT_FETCH_LIMIT = 20;
|
|
65
|
+
var COMMIT_SELECTION_WINDOW = 8;
|
|
28
66
|
var ignoreList = [
|
|
29
67
|
"package.json",
|
|
30
68
|
"*.yml",
|
|
@@ -48,6 +86,185 @@ function clearTraceMessages() {
|
|
|
48
86
|
function getTraceMessages() {
|
|
49
87
|
return [...traceMessages];
|
|
50
88
|
}
|
|
89
|
+
var ANSI = {
|
|
90
|
+
bold: "\x1B[1m",
|
|
91
|
+
cyan: "\x1B[36m",
|
|
92
|
+
dim: "\x1B[2m",
|
|
93
|
+
green: "\x1B[32m",
|
|
94
|
+
reset: "\x1B[0m",
|
|
95
|
+
yellow: "\x1B[33m"
|
|
96
|
+
};
|
|
97
|
+
var ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
98
|
+
var COMBINING_MARK_PATTERN = /\p{Mark}/u;
|
|
99
|
+
var GRAPHEME_SEGMENTER = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter("ko", { granularity: "grapheme" }) : null;
|
|
100
|
+
function getGitDiffPathspecs() {
|
|
101
|
+
return {
|
|
102
|
+
excludePatterns: ignoreList.map((item) => `:(exclude)${item}`),
|
|
103
|
+
includePatterns: ["*.ts", "*.tsx", "*.js", "*.jsx"]
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function segmentGraphemes(value) {
|
|
107
|
+
if (!GRAPHEME_SEGMENTER) {
|
|
108
|
+
return [...value];
|
|
109
|
+
}
|
|
110
|
+
return [...GRAPHEME_SEGMENTER.segment(value)].map(({ segment }) => segment);
|
|
111
|
+
}
|
|
112
|
+
function isWideCodePoint(codePoint) {
|
|
113
|
+
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);
|
|
114
|
+
}
|
|
115
|
+
function isEmojiCodePoint(codePoint) {
|
|
116
|
+
return codePoint >= 127462 && codePoint <= 127487 || codePoint >= 127744 && codePoint <= 129791 || codePoint >= 9728 && codePoint <= 10175;
|
|
117
|
+
}
|
|
118
|
+
function getGraphemeWidth(grapheme) {
|
|
119
|
+
let width = 0;
|
|
120
|
+
for (const character of grapheme) {
|
|
121
|
+
const codePoint = character.codePointAt(0);
|
|
122
|
+
if (!codePoint || COMBINING_MARK_PATTERN.test(character) || codePoint === 8205) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (codePoint >= 65024 && codePoint <= 65039 || codePoint >= 917760 && codePoint <= 917999) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (isWideCodePoint(codePoint) || isEmojiCodePoint(codePoint)) {
|
|
129
|
+
width = Math.max(width, 2);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
width = Math.max(width, 1);
|
|
133
|
+
}
|
|
134
|
+
return width;
|
|
135
|
+
}
|
|
136
|
+
function tokenizePlainText(value) {
|
|
137
|
+
return segmentGraphemes(value).map((segment) => ({
|
|
138
|
+
value: segment,
|
|
139
|
+
visibleWidth: getGraphemeWidth(segment)
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
function tokenizeVisibleText(value) {
|
|
143
|
+
const tokens = [];
|
|
144
|
+
let lastIndex = 0;
|
|
145
|
+
for (const match of value.matchAll(ANSI_PATTERN)) {
|
|
146
|
+
const index = match.index ?? 0;
|
|
147
|
+
if (index > lastIndex) {
|
|
148
|
+
tokens.push(...tokenizePlainText(value.slice(lastIndex, index)));
|
|
149
|
+
}
|
|
150
|
+
tokens.push({
|
|
151
|
+
value: match[0],
|
|
152
|
+
visibleWidth: 0
|
|
153
|
+
});
|
|
154
|
+
lastIndex = index + match[0].length;
|
|
155
|
+
}
|
|
156
|
+
if (lastIndex < value.length) {
|
|
157
|
+
tokens.push(...tokenizePlainText(value.slice(lastIndex)));
|
|
158
|
+
}
|
|
159
|
+
return tokens;
|
|
160
|
+
}
|
|
161
|
+
function truncateLineForTerminal(value, maxWidth) {
|
|
162
|
+
if (maxWidth <= 0) {
|
|
163
|
+
return "";
|
|
164
|
+
}
|
|
165
|
+
const tokens = tokenizeVisibleText(value);
|
|
166
|
+
const totalWidth = tokens.reduce((sum, token) => sum + token.visibleWidth, 0);
|
|
167
|
+
if (totalWidth <= maxWidth) {
|
|
168
|
+
return value;
|
|
169
|
+
}
|
|
170
|
+
const ellipsis = "...";
|
|
171
|
+
const ellipsisWidth = 3;
|
|
172
|
+
const targetWidth = Math.max(0, maxWidth - ellipsisWidth);
|
|
173
|
+
let usedWidth = 0;
|
|
174
|
+
let result = "";
|
|
175
|
+
for (const token of tokens) {
|
|
176
|
+
if (token.visibleWidth === 0) {
|
|
177
|
+
result += token.value;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (usedWidth + token.visibleWidth > targetWidth) {
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
result += token.value;
|
|
184
|
+
usedWidth += token.visibleWidth;
|
|
185
|
+
}
|
|
186
|
+
return `${result}${ellipsis}${ANSI.reset}`;
|
|
187
|
+
}
|
|
188
|
+
function fitLinesToTerminal(lines) {
|
|
189
|
+
const maxWidth = Math.max(20, (process.stdout.columns || 120) - 1);
|
|
190
|
+
return lines.map((line) => truncateLineForTerminal(line, maxWidth));
|
|
191
|
+
}
|
|
192
|
+
function runGitCommand(args, options = {}) {
|
|
193
|
+
const { allowFailure = false, trimOutput = true } = options;
|
|
194
|
+
try {
|
|
195
|
+
const output = child_process.execFileSync("git", args, {
|
|
196
|
+
encoding: "utf8",
|
|
197
|
+
maxBuffer: 1024 * 1024 * 20,
|
|
198
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
199
|
+
});
|
|
200
|
+
return trimOutput ? output.trim() : output;
|
|
201
|
+
} catch (error) {
|
|
202
|
+
helperTrace("git-command:failed", `${args.join(" ")} | ${getErrorSummary(error)}`);
|
|
203
|
+
if (allowFailure) {
|
|
204
|
+
return "";
|
|
205
|
+
}
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async function executeShellCommandWithProgress(command, options = {}) {
|
|
210
|
+
const { progressIntervalMs = 1e4, progressMessage = "\u23F3 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uB294 \uC911\uC785\uB2C8\uB2E4...", streamOutput = false } = options;
|
|
211
|
+
const { spawn } = await import('child_process');
|
|
212
|
+
return new Promise((resolve, reject) => {
|
|
213
|
+
let stdout = "";
|
|
214
|
+
let stderr = "";
|
|
215
|
+
const startedAt = Date.now();
|
|
216
|
+
console.log(progressMessage);
|
|
217
|
+
const child = spawn("/bin/zsh", ["-lc", command], {
|
|
218
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
219
|
+
});
|
|
220
|
+
const progressTimer = setInterval(() => {
|
|
221
|
+
const elapsedSeconds = Math.max(1, Math.floor((Date.now() - startedAt) / 1e3));
|
|
222
|
+
console.log(`${progressMessage} (${elapsedSeconds}s \uACBD\uACFC)`);
|
|
223
|
+
}, progressIntervalMs);
|
|
224
|
+
child.stdout.on("data", (chunk) => {
|
|
225
|
+
const text = chunk.toString();
|
|
226
|
+
stdout += text;
|
|
227
|
+
if (streamOutput) {
|
|
228
|
+
process.stdout.write(text);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
child.stderr.on("data", (chunk) => {
|
|
232
|
+
const text = chunk.toString();
|
|
233
|
+
stderr += text;
|
|
234
|
+
if (streamOutput) {
|
|
235
|
+
process.stderr.write(text);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
child.on("error", (error) => {
|
|
239
|
+
clearInterval(progressTimer);
|
|
240
|
+
reject(error);
|
|
241
|
+
});
|
|
242
|
+
child.on("close", (code, signal) => {
|
|
243
|
+
clearInterval(progressTimer);
|
|
244
|
+
if (code === 0) {
|
|
245
|
+
resolve({
|
|
246
|
+
stderr,
|
|
247
|
+
stdout
|
|
248
|
+
});
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const exitSummary = signal ? `signal=${signal}` : `code=${String(code ?? "unknown")}`;
|
|
252
|
+
reject(new Error(`\uC258 \uBA85\uB839 \uC2E4\uD589 \uC2E4\uD328 (${exitSummary})${stderr.trim() ? `
|
|
253
|
+
${stderr.trim()}` : ""}`));
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
function formatReviewTargetFiles(files, visibleCount = 5) {
|
|
258
|
+
if (files.length === 0) {
|
|
259
|
+
return "(\uC5C6\uC74C)";
|
|
260
|
+
}
|
|
261
|
+
const visibleFiles = files.slice(0, visibleCount);
|
|
262
|
+
const hiddenCount = Math.max(0, files.length - visibleFiles.length);
|
|
263
|
+
if (hiddenCount === 0) {
|
|
264
|
+
return visibleFiles.join(", ");
|
|
265
|
+
}
|
|
266
|
+
return `${visibleFiles.join(", ")} \uC678 ${hiddenCount}\uAC1C`;
|
|
267
|
+
}
|
|
51
268
|
function createTraceLogger(scope, args = process.argv.slice(2)) {
|
|
52
269
|
const enabled = isTestMode(args);
|
|
53
270
|
return (step, detail) => {
|
|
@@ -260,13 +477,90 @@ ${JSON.stringify(AIServices, null, 2)}
|
|
|
260
477
|
);
|
|
261
478
|
}
|
|
262
479
|
function getGitDiffFilter() {
|
|
263
|
-
const
|
|
264
|
-
const excludePatterns = ignoreList.map((item) => `:(exclude)${item}`);
|
|
480
|
+
const { includePatterns, excludePatterns } = getGitDiffPathspecs();
|
|
265
481
|
const quote = (pattern) => `"${pattern}"`;
|
|
266
|
-
const includeParams =
|
|
482
|
+
const includeParams = includePatterns.map(quote).join(" ");
|
|
267
483
|
const excludeParams = excludePatterns.map(quote).join(" ");
|
|
268
484
|
return { includeParams, excludeParams };
|
|
269
485
|
}
|
|
486
|
+
function truncateCommitSubject(subject) {
|
|
487
|
+
if (subject.length <= 72) {
|
|
488
|
+
return subject;
|
|
489
|
+
}
|
|
490
|
+
return `${subject.slice(0, 69)}...`;
|
|
491
|
+
}
|
|
492
|
+
function getRecentCommitOptions() {
|
|
493
|
+
const output = runGitCommand(
|
|
494
|
+
["log", `-${COMMIT_FETCH_LIMIT}`, "--date=relative", "--pretty=format:%h%x09%an%x09%ar%x09%s"],
|
|
495
|
+
{ allowFailure: true }
|
|
496
|
+
);
|
|
497
|
+
if (!output) {
|
|
498
|
+
return [];
|
|
499
|
+
}
|
|
500
|
+
return output.split("\n").map((line) => {
|
|
501
|
+
const [hash = "", author = "", relativeDate = "", ...subjectParts] = line.split(" ");
|
|
502
|
+
const subject = subjectParts.join(" ").trim();
|
|
503
|
+
return {
|
|
504
|
+
author,
|
|
505
|
+
description: `${author} | ${relativeDate}`,
|
|
506
|
+
hash,
|
|
507
|
+
label: `${hash} | ${truncateCommitSubject(subject)}`,
|
|
508
|
+
relativeDate,
|
|
509
|
+
subject
|
|
510
|
+
};
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
function buildSelectedCommitSummary(commits) {
|
|
514
|
+
return commits.map((commit) => `- ${commit.hash} | ${commit.subject} | ${commit.author} | ${commit.relativeDate}`).join("\n");
|
|
515
|
+
}
|
|
516
|
+
function getReviewPathspecArgs() {
|
|
517
|
+
const { includePatterns, excludePatterns } = getGitDiffPathspecs();
|
|
518
|
+
return [...includePatterns, ...excludePatterns];
|
|
519
|
+
}
|
|
520
|
+
function buildSelectedCommitDiff(commits) {
|
|
521
|
+
const reviewPathspecArgs = getReviewPathspecArgs();
|
|
522
|
+
const sections = commits.map((commit) => {
|
|
523
|
+
const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", ...reviewPathspecArgs], {
|
|
524
|
+
allowFailure: true,
|
|
525
|
+
trimOutput: false
|
|
526
|
+
}).trim();
|
|
527
|
+
if (!diff) {
|
|
528
|
+
return "";
|
|
529
|
+
}
|
|
530
|
+
return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
|
|
531
|
+
}).filter(Boolean).join("\n\n");
|
|
532
|
+
if (!sections) {
|
|
533
|
+
return "";
|
|
534
|
+
}
|
|
535
|
+
return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", "# \uB9AC\uBDF0 \uB300\uC0C1 diff", sections].join("\n");
|
|
536
|
+
}
|
|
537
|
+
function getSelectedCommitFiles(commits) {
|
|
538
|
+
const reviewPathspecArgs = getReviewPathspecArgs();
|
|
539
|
+
const files = /* @__PURE__ */ new Set();
|
|
540
|
+
commits.forEach((commit) => {
|
|
541
|
+
const output = runGitCommand(["show", "--pretty=format:", "--name-only", commit.hash, "--", ...reviewPathspecArgs], {
|
|
542
|
+
allowFailure: true
|
|
543
|
+
});
|
|
544
|
+
output.split("\n").map((line) => line.trim()).filter(Boolean).forEach((filePath) => files.add(filePath));
|
|
545
|
+
});
|
|
546
|
+
return [...files];
|
|
547
|
+
}
|
|
548
|
+
function buildSelectedFileDiff(commits, filePath) {
|
|
549
|
+
const sections = commits.map((commit) => {
|
|
550
|
+
const diff = runGitCommand(["show", "--stat", "--patch", "--format=", commit.hash, "--", filePath], {
|
|
551
|
+
allowFailure: true,
|
|
552
|
+
trimOutput: false
|
|
553
|
+
}).trim();
|
|
554
|
+
if (!diff) {
|
|
555
|
+
return "";
|
|
556
|
+
}
|
|
557
|
+
return [`## ${commit.hash} ${commit.subject}`, diff].join("\n\n");
|
|
558
|
+
}).filter(Boolean).join("\n\n");
|
|
559
|
+
if (!sections) {
|
|
560
|
+
return "";
|
|
561
|
+
}
|
|
562
|
+
return ["# \uC120\uD0DD\uD55C \uCEE4\uBC0B", buildSelectedCommitSummary(commits), "", `# \uD30C\uC77C: ${filePath}`, sections].join("\n\n");
|
|
563
|
+
}
|
|
270
564
|
function openReport(reportPath) {
|
|
271
565
|
const resolvedPath = path__default.default.resolve(reportPath);
|
|
272
566
|
const { platform } = process;
|
|
@@ -316,49 +610,155 @@ function openReport(reportPath) {
|
|
|
316
610
|
helperTrace("open-report:unsupported-platform", platform);
|
|
317
611
|
console.error(`\u26A0\uFE0F \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD50C\uB7AB\uD3FC\uC785\uB2C8\uB2E4: ${platform}`);
|
|
318
612
|
}
|
|
319
|
-
function
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
helperTrace(
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
613
|
+
function ensureInteractiveSelectionAvailable(scope, message) {
|
|
614
|
+
if (process.stdin.isTTY && process.stdout.isTTY && typeof process.stdin.setRawMode === "function") {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
helperTrace(`${scope}:tty-missing`);
|
|
618
|
+
exitWithError(message, {
|
|
619
|
+
scope: `helper:${scope}`
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
function renderSelectionBlock(lines, previousLineCount) {
|
|
623
|
+
if (previousLineCount > 0) {
|
|
624
|
+
readline__default.default.moveCursor(process.stdout, 0, -previousLineCount);
|
|
625
|
+
readline__default.default.clearScreenDown(process.stdout);
|
|
626
|
+
}
|
|
627
|
+
const fittedLines = fitLinesToTerminal(lines);
|
|
628
|
+
process.stdout.write(`${fittedLines.join("\n")}
|
|
629
|
+
`);
|
|
630
|
+
return fittedLines.length;
|
|
631
|
+
}
|
|
632
|
+
function getSelectionWindowRange(optionCount, selectedIndex, windowSize) {
|
|
633
|
+
if (optionCount <= windowSize) {
|
|
634
|
+
return {
|
|
635
|
+
end: optionCount,
|
|
636
|
+
start: 0
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
const halfWindow = Math.floor(windowSize / 2);
|
|
640
|
+
const maxStart = optionCount - windowSize;
|
|
641
|
+
const start = Math.max(0, Math.min(selectedIndex - halfWindow, maxStart));
|
|
642
|
+
return {
|
|
643
|
+
end: Math.min(optionCount, start + windowSize),
|
|
644
|
+
start
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
function buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize) {
|
|
648
|
+
const { start, end } = getSelectionWindowRange(options.length, selectedIndex, windowSize);
|
|
649
|
+
const lines = [
|
|
650
|
+
`${ANSI.bold}${question}${ANSI.reset}`,
|
|
651
|
+
`${ANSI.dim}\u2191\u2193 \uC774\uB3D9 | Space \uC120\uD0DD/\uD574\uC81C | Enter \uC644\uB8CC | Esc \uCDE8\uC18C${ANSI.reset}`,
|
|
652
|
+
`${ANSI.dim}\uC120\uD0DD\uB428: ${toggled.size}\uAC1C / \uC804\uCCB4: ${options.length}\uAC1C${ANSI.reset}`
|
|
653
|
+
];
|
|
654
|
+
for (let index = start; index < end; index += 1) {
|
|
655
|
+
const option = options[index];
|
|
656
|
+
if (!option) {
|
|
657
|
+
continue;
|
|
333
658
|
}
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
659
|
+
const cursor = index === selectedIndex ? `${ANSI.cyan}>${ANSI.reset}` : " ";
|
|
660
|
+
const checked = toggled.has(index) ? `${ANSI.green}\u2611${ANSI.reset}` : "\u2610";
|
|
661
|
+
const description = option.description ? ` ${ANSI.dim}${option.description}${ANSI.reset}` : "";
|
|
662
|
+
lines.push(`${cursor} ${checked} ${option.label}${description}`);
|
|
663
|
+
}
|
|
664
|
+
if (options.length > windowSize) {
|
|
665
|
+
lines.push(`${ANSI.dim}\uD45C\uC2DC \uBC94\uC704: ${start + 1}-${end} / ${options.length}${ANSI.reset}`);
|
|
666
|
+
}
|
|
667
|
+
return lines;
|
|
668
|
+
}
|
|
669
|
+
async function showMultiSelect(question, options, windowSize = COMMIT_SELECTION_WINDOW) {
|
|
670
|
+
ensureInteractiveSelectionAvailable("showMultiSelect", "\u274C \uCEE4\uBC0B \uC120\uD0DD \uBAA8\uB2EC\uC740 TTY \uD658\uACBD\uC5D0\uC11C\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
671
|
+
let selectedIndex = 0;
|
|
672
|
+
let renderedLineCount = 0;
|
|
673
|
+
const toggled = /* @__PURE__ */ new Set();
|
|
674
|
+
const rl = readline__default.default.createInterface({
|
|
675
|
+
input: process.stdin,
|
|
676
|
+
output: process.stdout,
|
|
677
|
+
terminal: true
|
|
678
|
+
});
|
|
679
|
+
process.stdout.write("\x1B[?25l");
|
|
680
|
+
const cleanup = () => {
|
|
681
|
+
if (renderedLineCount > 0) {
|
|
682
|
+
readline__default.default.moveCursor(process.stdout, 0, -renderedLineCount);
|
|
683
|
+
readline__default.default.clearScreenDown(process.stdout);
|
|
684
|
+
renderedLineCount = 0;
|
|
341
685
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
686
|
+
process.stdin.removeListener("data", onData);
|
|
687
|
+
process.stdin.setRawMode(false);
|
|
688
|
+
process.stdin.pause();
|
|
689
|
+
rl.close();
|
|
690
|
+
process.stdout.write("\x1B[?25h");
|
|
691
|
+
};
|
|
692
|
+
const render = () => {
|
|
693
|
+
const lines = buildMultiSelectLines(question, options, selectedIndex, toggled, windowSize);
|
|
694
|
+
renderedLineCount = renderSelectionBlock(lines, renderedLineCount);
|
|
695
|
+
};
|
|
696
|
+
const confirmSelection = (resolve) => {
|
|
697
|
+
const values = [...toggled].sort((left, right) => left - right).map((index) => options[index]?.value).filter((value) => value !== void 0);
|
|
698
|
+
cleanup();
|
|
699
|
+
resolve(values);
|
|
700
|
+
};
|
|
701
|
+
const cancelSelection = (resolve) => {
|
|
702
|
+
cleanup();
|
|
703
|
+
resolve([]);
|
|
704
|
+
};
|
|
705
|
+
let onData = (_data) => {
|
|
706
|
+
};
|
|
707
|
+
render();
|
|
708
|
+
return new Promise((resolve) => {
|
|
709
|
+
onData = (data) => {
|
|
710
|
+
const key = data.toString();
|
|
711
|
+
if (key === "") {
|
|
712
|
+
cleanup();
|
|
713
|
+
process.exit(0);
|
|
355
714
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
715
|
+
if (key === "\x1B") {
|
|
716
|
+
cancelSelection(resolve);
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
if (key === "\x1B[A") {
|
|
720
|
+
selectedIndex = (selectedIndex - 1 + options.length) % options.length;
|
|
721
|
+
render();
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
if (key === "\x1B[B") {
|
|
725
|
+
selectedIndex = (selectedIndex + 1) % options.length;
|
|
726
|
+
render();
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
if (key === " ") {
|
|
730
|
+
if (toggled.has(selectedIndex)) {
|
|
731
|
+
toggled.delete(selectedIndex);
|
|
732
|
+
} else {
|
|
733
|
+
toggled.add(selectedIndex);
|
|
734
|
+
}
|
|
735
|
+
render();
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
if (key === "\r" || key === "\n") {
|
|
739
|
+
confirmSelection(resolve);
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
process.stdin.setRawMode(true);
|
|
743
|
+
process.stdin.resume();
|
|
744
|
+
process.stdin.on("data", onData);
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
async function selectReviewCommits() {
|
|
748
|
+
const commits = getRecentCommitOptions();
|
|
749
|
+
if (commits.length === 0) {
|
|
750
|
+
console.log("\u2139\uFE0F \uB9AC\uBDF0\uD560 \uCD5C\uADFC \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
751
|
+
return [];
|
|
359
752
|
}
|
|
360
|
-
|
|
361
|
-
|
|
753
|
+
return showMultiSelect(
|
|
754
|
+
"\uB9AC\uBDF0\uD560 \uCEE4\uBC0B\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.",
|
|
755
|
+
commits.map((commit) => ({
|
|
756
|
+
description: commit.description,
|
|
757
|
+
label: commit.label,
|
|
758
|
+
value: commit
|
|
759
|
+
})),
|
|
760
|
+
COMMIT_SELECTION_WINDOW
|
|
761
|
+
);
|
|
362
762
|
}
|
|
363
763
|
function selectAIService() {
|
|
364
764
|
const service = parseServiceFromArgs();
|
|
@@ -376,10 +776,11 @@ async function showSelectionAIService() {
|
|
|
376
776
|
if (selectedServiceFromArgs) {
|
|
377
777
|
helperTrace("show-selection:from-args", selectedServiceFromArgs);
|
|
378
778
|
console.log(`
|
|
379
|
-
\u2705
|
|
779
|
+
\u2705 ${ANSI.green}${selectedServiceFromArgs}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (--service)
|
|
380
780
|
`);
|
|
381
781
|
return selectedServiceFromArgs;
|
|
382
782
|
}
|
|
783
|
+
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.");
|
|
383
784
|
helperTrace("show-selection:interactive:start");
|
|
384
785
|
let selectedIndex = 0;
|
|
385
786
|
const rl = readline__default.default.createInterface({
|
|
@@ -397,11 +798,12 @@ async function showSelectionAIService() {
|
|
|
397
798
|
helperTrace("show-selection:interactive:render", AIServices[selectedIndex] || "unknown");
|
|
398
799
|
readline__default.default.clearScreenDown(process.stdout);
|
|
399
800
|
process.stdout.write(
|
|
400
|
-
|
|
801
|
+
`\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):
|
|
802
|
+
`
|
|
401
803
|
);
|
|
402
804
|
AIServices.forEach((service, index) => {
|
|
403
805
|
if (index === selectedIndex) {
|
|
404
|
-
process.stdout.write(`
|
|
806
|
+
process.stdout.write(` ${ANSI.cyan}>${ANSI.reset} ${ANSI.cyan}\u25C9${ANSI.reset} ${ANSI.bold}${service}${ANSI.reset}
|
|
405
807
|
`);
|
|
406
808
|
} else {
|
|
407
809
|
process.stdout.write(` \u25EF ${service}
|
|
@@ -431,7 +833,7 @@ async function showSelectionAIService() {
|
|
|
431
833
|
rl.close();
|
|
432
834
|
process.stdout.write("\x1B[?25h");
|
|
433
835
|
console.log(`
|
|
434
|
-
\u2705
|
|
836
|
+
\u2705 ${ANSI.green}${AIServices[selectedIndex]}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
|
|
435
837
|
`);
|
|
436
838
|
const result = AIServices[selectedIndex];
|
|
437
839
|
if (result) {
|
|
@@ -447,21 +849,29 @@ async function showSelectionAIService() {
|
|
|
447
849
|
}
|
|
448
850
|
|
|
449
851
|
exports.AIServices = AIServices;
|
|
852
|
+
exports.COMMIT_FETCH_LIMIT = COMMIT_FETCH_LIMIT;
|
|
853
|
+
exports.COMMIT_SELECTION_WINDOW = COMMIT_SELECTION_WINDOW;
|
|
450
854
|
exports.REPORT_DIR = REPORT_DIR;
|
|
855
|
+
exports.buildSelectedCommitDiff = buildSelectedCommitDiff;
|
|
856
|
+
exports.buildSelectedCommitSummary = buildSelectedCommitSummary;
|
|
857
|
+
exports.buildSelectedFileDiff = buildSelectedFileDiff;
|
|
451
858
|
exports.clearTraceMessages = clearTraceMessages;
|
|
452
859
|
exports.codingConventionRulesPath = codingConventionRulesPath;
|
|
453
860
|
exports.createReportDirectory = createReportDirectory;
|
|
454
861
|
exports.createTraceLogger = createTraceLogger;
|
|
455
862
|
exports.deleteFile = deleteFile;
|
|
456
863
|
exports.deleteTempDiff = deleteTempDiff;
|
|
864
|
+
exports.executeShellCommandWithProgress = executeShellCommandWithProgress;
|
|
457
865
|
exports.exitWithError = exitWithError;
|
|
866
|
+
exports.formatReviewTargetFiles = formatReviewTargetFiles;
|
|
458
867
|
exports.getAvailableFilePath = getAvailableFilePath;
|
|
459
|
-
exports.getDiffArgs = getDiffArgs;
|
|
460
868
|
exports.getErrorLogTimestamp = getErrorLogTimestamp;
|
|
461
869
|
exports.getErrorSummary = getErrorSummary;
|
|
462
870
|
exports.getGitDiffFilter = getGitDiffFilter;
|
|
463
871
|
exports.getNextFilePath = getNextFilePath;
|
|
464
872
|
exports.getNowString = getNowString;
|
|
873
|
+
exports.getRecentCommitOptions = getRecentCommitOptions;
|
|
874
|
+
exports.getSelectedCommitFiles = getSelectedCommitFiles;
|
|
465
875
|
exports.getTraceMessages = getTraceMessages;
|
|
466
876
|
exports.ignoreList = ignoreList;
|
|
467
877
|
exports.isTestMode = isTestMode;
|
|
@@ -471,6 +881,8 @@ exports.reviewFormOneByOnePath = reviewFormOneByOnePath;
|
|
|
471
881
|
exports.reviewFormPath = reviewFormPath;
|
|
472
882
|
exports.rulesPath = rulesPath;
|
|
473
883
|
exports.selectAIService = selectAIService;
|
|
884
|
+
exports.selectReviewCommits = selectReviewCommits;
|
|
885
|
+
exports.showMultiSelect = showMultiSelect;
|
|
474
886
|
exports.showSelectionAIService = showSelectionAIService;
|
|
475
887
|
exports.tempDiffPath = tempDiffPath;
|
|
476
888
|
exports.writeErrorReport = writeErrorReport;
|