feat(ai-code-review): 改用 AI 助理 CLI 執行審查

This commit is contained in:
2026-06-27 13:30:40 +00:00
parent 93be261b90
commit 08a72a8c7f
4 changed files with 177 additions and 223 deletions
+47 -15
View File
@@ -1,4 +1,5 @@
import https from 'https';
import { execFileSync } from 'child_process';
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
@@ -30,25 +31,56 @@ export function getInsecureHttpsAgent() {
export const getOpenCodeHttpsAgent = getInsecureHttpsAgent;
const CLI_CANDIDATES = [
{
provider: 'codex',
command: 'codex',
defaultModel: 'gpt-5',
},
{
provider: 'claude',
command: 'claude',
defaultModel: 'sonnet',
},
{
provider: 'opencode',
command: 'opencode',
defaultModel: 'google/gemini-2.5-flash',
},
];
function commandExists(command) {
try {
execFileSync('/bin/sh', ['-lc', `command -v ${command}`], { stdio: 'ignore' });
return true;
} catch {
return false;
}
}
/**
* 依環境變數解析並回傳 LLM 提供者設定。
*
* 當設定了 `OPENCODE_BASE_URL` 時回傳 OpenCode 提供者設定
* model 取自 `OPENCODE_MODEL`預設為 `gemini-2.5-flash`);
* 否則回傳各欄位皆為空/null 的「無提供者」設定,由呼叫端據此判斷是否略過 LLM 流程。
* 優先使用 `AI_ASSISTANT_CLI` 指定的 CLI;未指定時依序偵測 codex、claude、opencode
* model 優先取 `MODEL`,再相容舊的 `OPENCODE_MODEL`最後使用各 CLI 預設值。
*
* @remarks 每次呼叫都會即時讀取 `process.env`。`apiKeys` 在 OpenCode 模式下為固定佔位值 `['opencode']`,並非真實金鑰
* @returns {{ provider: ('opencode'|null), apiKeys: string[], baseURL: (string|null), model: (string|null) }}
* @param {{ commandExistsFn?: (command: string) => boolean }} [deps] - 可注入的 CLI 偵測函式,供測試使用
* @returns {{ provider: ('codex'|'claude'|'opencode'|null), apiKeys: string[], baseURL: null, model: (string|null), command: (string|null) }}
* LLM 設定物件;`provider` 為 `null` 表示沒有可用的提供者。
*/
export function getLLMConfig() {
if (process.env.OPENCODE_BASE_URL) {
return {
provider: 'opencode',
apiKeys: ['opencode'],
baseURL: process.env.OPENCODE_BASE_URL,
model: process.env.OPENCODE_MODEL || 'gemini-2.5-flash',
};
}
return { provider: null, apiKeys: [], baseURL: null, model: null };
export function getLLMConfig({ commandExistsFn = commandExists } = {}) {
const requested = process.env.AI_ASSISTANT_CLI;
const candidates = requested
? CLI_CANDIDATES.filter(c => c.provider === requested || c.command === requested)
: CLI_CANDIDATES;
const cli = candidates.find(c => commandExistsFn(c.command));
if (!cli) return { provider: null, apiKeys: [], baseURL: null, model: null, command: null };
return {
provider: cli.provider,
apiKeys: [cli.provider],
baseURL: null,
model: process.env.MODEL || process.env.OPENCODE_MODEL || cli.defaultModel,
command: cli.command,
};
}