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.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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';
|
|
@@ -8,14 +8,52 @@ import { inspect } from 'util';
|
|
|
8
8
|
// src/common/helper.ts
|
|
9
9
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
var traceMessages = [];
|
|
11
|
-
var
|
|
12
|
-
var
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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");
|
|
16
52
|
var REPORT_DIR = ".review-report";
|
|
17
53
|
var tempDiffPath = "temp_diff.txt";
|
|
18
54
|
var AIServices = ["gemini", "claude", "codex"];
|
|
55
|
+
var COMMIT_FETCH_LIMIT = 20;
|
|
56
|
+
var COMMIT_SELECTION_WINDOW = 8;
|
|
19
57
|
var ignoreList = [
|
|
20
58
|
"package.json",
|
|
21
59
|
"*.yml",
|
|
@@ -39,6 +77,185 @@ function clearTraceMessages() {
|
|
|
39
77
|
function getTraceMessages() {
|
|
40
78
|
return [...traceMessages];
|
|
41
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) {
|
|
154
|
+
return "";
|
|
155
|
+
}
|
|
156
|
+
const tokens = tokenizeVisibleText(value);
|
|
157
|
+
const totalWidth = tokens.reduce((sum, token) => sum + token.visibleWidth, 0);
|
|
158
|
+
if (totalWidth <= maxWidth) {
|
|
159
|
+
return value;
|
|
160
|
+
}
|
|
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}`;
|
|
178
|
+
}
|
|
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`;
|
|
258
|
+
}
|
|
42
259
|
function createTraceLogger(scope, args = process.argv.slice(2)) {
|
|
43
260
|
const enabled = isTestMode(args);
|
|
44
261
|
return (step, detail) => {
|
|
@@ -251,13 +468,90 @@ ${JSON.stringify(AIServices, null, 2)}
|
|
|
251
468
|
);
|
|
252
469
|
}
|
|
253
470
|
function getGitDiffFilter() {
|
|
254
|
-
const
|
|
255
|
-
const excludePatterns = ignoreList.map((item) => `:(exclude)${item}`);
|
|
471
|
+
const { includePatterns, excludePatterns } = getGitDiffPathspecs();
|
|
256
472
|
const quote = (pattern) => `"${pattern}"`;
|
|
257
|
-
const includeParams =
|
|
473
|
+
const includeParams = includePatterns.map(quote).join(" ");
|
|
258
474
|
const excludeParams = excludePatterns.map(quote).join(" ");
|
|
259
475
|
return { includeParams, excludeParams };
|
|
260
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
|
+
}
|
|
261
555
|
function openReport(reportPath) {
|
|
262
556
|
const resolvedPath = path.resolve(reportPath);
|
|
263
557
|
const { platform } = process;
|
|
@@ -307,49 +601,155 @@ function openReport(reportPath) {
|
|
|
307
601
|
helperTrace("open-report:unsupported-platform", platform);
|
|
308
602
|
console.error(`\u26A0\uFE0F \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD50C\uB7AB\uD3FC\uC785\uB2C8\uB2E4: ${platform}`);
|
|
309
603
|
}
|
|
310
|
-
function
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
helperTrace(
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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;
|
|
324
649
|
}
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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;
|
|
332
676
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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);
|
|
346
705
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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 [];
|
|
350
743
|
}
|
|
351
|
-
|
|
352
|
-
|
|
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
|
+
);
|
|
353
753
|
}
|
|
354
754
|
function selectAIService() {
|
|
355
755
|
const service = parseServiceFromArgs();
|
|
@@ -367,10 +767,11 @@ async function showSelectionAIService() {
|
|
|
367
767
|
if (selectedServiceFromArgs) {
|
|
368
768
|
helperTrace("show-selection:from-args", selectedServiceFromArgs);
|
|
369
769
|
console.log(`
|
|
370
|
-
\u2705
|
|
770
|
+
\u2705 ${ANSI.green}${selectedServiceFromArgs}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (--service)
|
|
371
771
|
`);
|
|
372
772
|
return selectedServiceFromArgs;
|
|
373
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.");
|
|
374
775
|
helperTrace("show-selection:interactive:start");
|
|
375
776
|
let selectedIndex = 0;
|
|
376
777
|
const rl = readline.createInterface({
|
|
@@ -388,11 +789,12 @@ async function showSelectionAIService() {
|
|
|
388
789
|
helperTrace("show-selection:interactive:render", AIServices[selectedIndex] || "unknown");
|
|
389
790
|
readline.clearScreenDown(process.stdout);
|
|
390
791
|
process.stdout.write(
|
|
391
|
-
|
|
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
|
+
`
|
|
392
794
|
);
|
|
393
795
|
AIServices.forEach((service, index) => {
|
|
394
796
|
if (index === selectedIndex) {
|
|
395
|
-
process.stdout.write(`
|
|
797
|
+
process.stdout.write(` ${ANSI.cyan}>${ANSI.reset} ${ANSI.cyan}\u25C9${ANSI.reset} ${ANSI.bold}${service}${ANSI.reset}
|
|
396
798
|
`);
|
|
397
799
|
} else {
|
|
398
800
|
process.stdout.write(` \u25EF ${service}
|
|
@@ -422,7 +824,7 @@ async function showSelectionAIService() {
|
|
|
422
824
|
rl.close();
|
|
423
825
|
process.stdout.write("\x1B[?25h");
|
|
424
826
|
console.log(`
|
|
425
|
-
\u2705
|
|
827
|
+
\u2705 ${ANSI.green}${AIServices[selectedIndex]}${ANSI.reset} \uC11C\uBE44\uC2A4\uAC00 \uC120\uD0DD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
|
|
426
828
|
`);
|
|
427
829
|
const result = AIServices[selectedIndex];
|
|
428
830
|
if (result) {
|
|
@@ -437,6 +839,6 @@ async function showSelectionAIService() {
|
|
|
437
839
|
});
|
|
438
840
|
}
|
|
439
841
|
|
|
440
|
-
export { AIServices, REPORT_DIR, clearTraceMessages, codingConventionRulesPath, createReportDirectory, createTraceLogger, deleteFile, deleteTempDiff, exitWithError,
|
|
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 };
|
|
441
843
|
//# sourceMappingURL=helper.js.map
|
|
442
844
|
//# sourceMappingURL=helper.js.map
|