gstack/bin/gstack-github-codex-review

125 lines
4.1 KiB
Plaintext
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bun
import {
createRealDependencies,
GithubCodexReviewError,
runGithubCodexReview,
type ReviewCommand,
type ReviewMode,
type ReviewOptions,
type ReviewResult,
} from "../lib/github-codex-review";
function usage(): string {
return `gstack-github-codex-review — 有界 GitHub Codex Review 辅助检查
Usage:
gstack-github-codex-review assess [options]
gstack-github-codex-review status [options]
gstack-github-codex-review run [options]
Options:
--mode off|risk|always 覆盖本次模式;默认读取 gstack-config
--pr <number> 指定 PR默认从当前分支发现
--ack-timeout <seconds> 等待机器人 ACK默认 120
--completion-timeout <sec> ACK 后等待完成;默认 600
--poll-interval <seconds> 轮询间隔;默认 5
--no-wait 创建或复用请求后立即返回
--help 显示帮助
最快启用:
gstack-config set github_codex_review risk
gstack-github-codex-review assess
该功能默认关闭。risk/always 可能消耗 GitHub Codex Review 额度。
stdout 始终输出单个 JSON运行进度写入 stderr。
`;
}
function parsePositive(raw: string | undefined, flag: string): number {
const value = Number(raw);
if (!raw || !Number.isFinite(value) || value <= 0) {
throw new GithubCodexReviewError("invalid_argument", `${flag} 需要正数。`);
}
return value;
}
function parseArgs(argv: string[]): ReviewOptions | "help" {
if (argv.includes("--help") || argv.includes("-h")) return "help";
const command = argv.shift() as ReviewCommand | undefined;
if (command !== "assess" && command !== "status" && command !== "run") {
throw new GithubCodexReviewError("invalid_argument", "首个参数必须是 assess、status 或 run。 ");
}
const options: ReviewOptions = { command };
while (argv.length > 0) {
const flag = argv.shift();
switch (flag) {
case "--mode": {
const mode = argv.shift() as ReviewMode | undefined;
if (mode !== "off" && mode !== "risk" && mode !== "always") {
throw new GithubCodexReviewError("invalid_argument", "--mode 允许值off、risk、always。 ");
}
options.mode = mode;
break;
}
case "--pr": {
const value = parsePositive(argv.shift(), "--pr");
if (!Number.isInteger(value)) throw new GithubCodexReviewError("invalid_argument", "--pr 需要正整数。 ");
options.prNumber = value;
break;
}
case "--ack-timeout":
options.ackTimeoutSeconds = parsePositive(argv.shift(), "--ack-timeout");
break;
case "--completion-timeout":
options.completionTimeoutSeconds = parsePositive(argv.shift(), "--completion-timeout");
break;
case "--poll-interval":
options.pollIntervalSeconds = parsePositive(argv.shift(), "--poll-interval");
break;
case "--no-wait":
options.noWait = true;
break;
default:
throw new GithubCodexReviewError("invalid_argument", `未知参数:${flag}`);
}
}
return options;
}
function argumentError(error: unknown): ReviewResult {
const message = error instanceof Error ? error.message : String(error);
return {
schema_version: 1,
status: "ERROR",
blocking: false,
reason: error instanceof GithubCodexReviewError ? error.reasonCode : "invalid_argument",
message,
mode: null,
config_source: "argument",
pr: null,
request: { comment_id: null, comment_url: null, created: false },
timeout_stage: null,
unresolved_threads: [],
warnings: [message],
elapsed_ms: 0,
exit_code: 64,
};
}
try {
const parsed = parseArgs(process.argv.slice(2));
if (parsed === "help") {
process.stdout.write(usage());
} else {
const result = await runGithubCodexReview(parsed, createRealDependencies());
process.stdout.write(`${JSON.stringify(result)}\n`);
process.exitCode = result.exit_code;
}
} catch (error) {
const result = argumentError(error);
process.stdout.write(`${JSON.stringify(result)}\n`);
console.error(`${result.message}\n\n${usage()}`);
process.exitCode = result.exit_code;
}