review-mark 1.0.3 → 1.0.4
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/cli/review.cjs +1 -1
- package/dist/cli/review.cjs.map +1 -1
- package/dist/cli/review.js +1 -1
- package/dist/cli/review.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +1 -1
package/dist/cli/review.cjs
CHANGED
|
@@ -225,7 +225,7 @@ ${diff}
|
|
|
225
225
|
var lark = __toESM(require("@larksuiteoapi/node-sdk"), 1);
|
|
226
226
|
|
|
227
227
|
// src/constants.ts
|
|
228
|
-
var DEFAULT_MODEL = "composer-1";
|
|
228
|
+
var DEFAULT_MODEL = "composer-1.5";
|
|
229
229
|
var appId = "cli_a93822da7238dbb5";
|
|
230
230
|
var appSecret = "ZQdcpLUHFb4gFa8cGfrlJfVfSSyGtyzF";
|
|
231
231
|
var receiveId = "oc_482b6a04f95f4206c4fa9bc61829fd17";
|
package/dist/cli/review.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/review.ts","../../src/core/BeLinkReview.ts","../../src/utils/checkCli.ts","../../src/core/git.ts","../../src/core/prompt.ts","../../src/core/feishu.ts","../../src/constants.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { BeLinkReview } from \"../core/BeLinkReview\";\n\nconst program = new Command();\n\nconst sharedOptions = (cmd: Command) =>\n cmd\n .option(\n \"--apiKey <key>\",\n \"Cursor API Key (overrides CURSOR_API_KEY environment variable)\"\n )\n .option(\n \"--agentPath <path>\",\n \"Path to the Cursor CLI agent executable (overrides CURSOR_AGENT_PATH environment variable)\"\n )\n .option(\n \"--model <model>\",\n \"AI model to use, e.g. claude-3-5-sonnet (overrides CURSOR_MODEL environment variable)\"\n )\n .option(\n \"--ignore <patterns>\",\n \"Comma-separated list of glob patterns to ignore (e.g., *.lock,dist/**)\"\n )\n .option(\"--no-feishu\", \"Disable Feishu notification\");\n\nconst runAction = async (options: any) => {\n try {\n const ignorePatterns = options.ignore\n ? options.ignore.split(\",\")\n : undefined;\n\n const instance = BeLinkReview.getInstance(\n options.apiKey,\n options.agentPath,\n ignorePatterns,\n options.feishu !== false,\n options.model\n );\n await instance.goCli();\n } catch (error: any) {\n console.error(`[review-mark] Error: ${error.message}`);\n process.exit(1);\n }\n};\n\nsharedOptions(\n program\n .name(\"review-mark\")\n .description(\"AI-powered code review tool\")\n .version(\"1.0.0\")\n).action(runAction);\n\nsharedOptions(\n program.command(\"review\").description(\"Perform an AI code review on git diff\")\n).action(runAction);\n\nprogram.parse(process.argv);\n","import { spawn } from \"node:child_process\";\nimport { readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type {\n BeLinkReviewOptions,\n BeLinkReviewChatOptions,\n BeLinkReviewEnsureResult,\n} from \"../types\";\nimport { isCheckCliInstall } from \"../utils/checkCli\";\nimport { getGitDiff } from \"./git\";\nimport { generateAIPrompt } from \"./prompt\";\nimport { sendReviewToFeishu } from \"./feishu\";\nimport { DEFAULT_MODEL } from \"../constants\";\n\nexport class BeLinkReview {\n static #instance: BeLinkReview | null = null;\n #apiKey: string | undefined;\n #agentPath: string | undefined;\n #ignorePatterns: string[] | undefined;\n #enableFeishu: boolean;\n #model: string | undefined;\n\n private constructor(\n apiKey?: string,\n agentPath?: string,\n ignorePatterns?: string[],\n enableFeishu: boolean = true,\n model?: string\n ) {\n this.#apiKey = apiKey;\n this.#agentPath = agentPath;\n this.#ignorePatterns = ignorePatterns;\n this.#enableFeishu = enableFeishu;\n this.#model = model;\n }\n\n /**\n * 初始化单例并注入参数,在项目入口调用一次即可。\n * 负责保存 apiKey 和自动写入 package.json 脚本。\n * 此方法主要用于在用户项目中设置 apiKey 和脚本,CLI 运行时会优先从环境变量或命令行参数获取。\n */\n static init(options: BeLinkReviewOptions = {}): BeLinkReview {\n if (BeLinkReview.#instance === null) {\n BeLinkReview.#instance = new BeLinkReview(\n options.apiKey,\n options.agentPath,\n options.ignore,\n options.enableFeishu ?? true,\n options.model\n );\n } else {\n BeLinkReview.#instance.#apiKey = options.apiKey;\n BeLinkReview.#instance.#agentPath = options.agentPath;\n BeLinkReview.#instance.#ignorePatterns = options.ignore;\n BeLinkReview.#instance.#enableFeishu = options.enableFeishu ?? true;\n BeLinkReview.#instance.#model = options.model;\n }\n BeLinkReview.#instance.#setupProjectScript();\n return BeLinkReview.#instance;\n }\n\n /**\n * 获取单例实例。如果未通过 init() 初始化,则尝试从环境变量 CURSOR_API_KEY 获取。\n * @param cliApiKey 可选的命令行传入的 apiKey\n * @param cliAgentPath 可选的命令行传入的 agentPath\n * @param cliIgnorePatterns 可选的命令行传入的 ignorePatterns\n * @param cliEnableFeishu 可选的命令行传入的飞书开关\n */\n static getInstance(\n cliApiKey?: string,\n cliAgentPath?: string,\n cliIgnorePatterns?: string[],\n cliEnableFeishu?: boolean,\n cliModel?: string\n ): BeLinkReview {\n if (BeLinkReview.#instance === null) {\n const apiKey = cliApiKey || process.env.CURSOR_API_KEY;\n const agentPath = cliAgentPath || process.env.CURSOR_AGENT_PATH;\n const ignorePatterns =\n cliIgnorePatterns ||\n (process.env.BE_LINK_REVIEW_IGNORE\n ? process.env.BE_LINK_REVIEW_IGNORE.split(\",\")\n : undefined);\n const enableFeishu =\n cliEnableFeishu ?? process.env.FEISHU_ENABLED !== \"false\";\n const model = cliModel || process.env.CURSOR_MODEL;\n\n if (!apiKey) {\n throw new Error(\n '[review-mark] 请先调用 BeLinkReview.init({ apiKey: \"...\" }) 初始化,或设置环境变量 CURSOR_API_KEY,或通过命令行参数 --apiKey 传入。'\n );\n }\n BeLinkReview.#instance = new BeLinkReview(\n apiKey,\n agentPath,\n ignorePatterns,\n enableFeishu,\n model\n );\n } else {\n if (cliApiKey && !BeLinkReview.#instance.#apiKey) {\n BeLinkReview.#instance.#apiKey = cliApiKey;\n }\n if (cliAgentPath && !BeLinkReview.#instance.#agentPath) {\n BeLinkReview.#instance.#agentPath = cliAgentPath;\n }\n if (cliIgnorePatterns && !BeLinkReview.#instance.#ignorePatterns) {\n BeLinkReview.#instance.#ignorePatterns = cliIgnorePatterns;\n }\n if (cliEnableFeishu !== undefined) {\n BeLinkReview.#instance.#enableFeishu = cliEnableFeishu;\n }\n if (cliModel) {\n BeLinkReview.#instance.#model = cliModel;\n }\n }\n return BeLinkReview.#instance;\n }\n\n #getApiKey(): string {\n const apiKey = this.#apiKey || process.env.CURSOR_API_KEY;\n if (!apiKey) {\n throw new Error(\n '[review-mark] 请先在 init({ apiKey: \"...\" }) 中传入 apiKey,或设置环境变量 CURSOR_API_KEY,或通过命令行参数 --apiKey 传入。'\n );\n }\n return apiKey;\n }\n\n #getAgentPath(): string | undefined {\n return this.#agentPath || process.env.CURSOR_AGENT_PATH;\n }\n\n #getIgnorePatterns(): string[] {\n const envIgnore = process.env.BE_LINK_REVIEW_IGNORE\n ? process.env.BE_LINK_REVIEW_IGNORE.split(\",\")\n : [];\n return this.#ignorePatterns || envIgnore;\n }\n\n #isFeishuEnabled(): boolean {\n return this.#enableFeishu;\n }\n\n #getModel(): string {\n return this.#model || process.env.CURSOR_MODEL || DEFAULT_MODEL;\n }\n\n /**\n * 自动往用户项目 package.json 写入 script\n */\n async #setupProjectScript() {\n try {\n const packageJsonPath = join(process.cwd(), \"package.json\");\n const packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf-8\"));\n\n if (!packageJson.scripts) {\n packageJson.scripts = {};\n }\n\n if (!packageJson.scripts[\"review-mark\"]) {\n packageJson.scripts[\"review-mark\"] = \"review-mark\";\n writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));\n console.log(\n \"[review-mark] 已在 package.json 中添加 'review-mark' 脚本。\"\n );\n } else {\n console.log(\"[review-mark] 'review-mark' 脚本已存在,跳过添加。\");\n }\n } catch (error) {\n console.error(\"[review-mark] 无法更新 package.json: \", error);\n }\n }\n\n /**\n * 执行一次完整 CLI 流程:获取 git diff → 生成 prompt → 检查/安装 CLI → 与 Cursor 对话并打印结果。\n */\n async goCli(): Promise<string> {\n const apiKey = this.#getApiKey();\n const agentPath = this.#getAgentPath();\n const ignorePatterns = this.#getIgnorePatterns();\n\n const ensureResult = await this.ensureAgentInstalled(false, agentPath);\n\n if (!ensureResult.isInstalled) {\n throw new Error(\n \"[review-mark] Cursor CLI 未安装且自动安装失败,请手动安装。\"\n );\n }\n\n console.log(\"[review-mark] Getting git diff...\");\n const diff = await getGitDiff(ignorePatterns, process.cwd());\n\n if (!diff) {\n console.log(\"[review-mark] No code changes detected\");\n return \"No code changes detected\";\n }\n\n const prompt = generateAIPrompt(diff);\n console.log(\"[review-mark] Sending to AI...\");\n const response = await this.chat(prompt, {\n agentPath: ensureResult.actualAgentPath,\n force: true,\n model: this.#getModel(),\n });\n console.log(this.#getModel(), \"model\");\n console.log(\"===== AI Review =====\");\n console.log(response);\n\n // 如果启用了飞书,发送到飞书\n if (this.#isFeishuEnabled()) {\n try {\n await sendReviewToFeishu(response);\n } catch (error: any) {\n console.error(\n `[review-mark] 飞书通知发送失败,但不影响 review 结果: ${error.message}`\n );\n }\n }\n\n return response;\n }\n\n /**\n * 检查 Cursor CLI 是否已安装;未安装则自动安装(需网络)。\n * @param silent 为 true 时不打印「已安装」等提示\n * @param agentPath 可选的 agent 可执行文件路径\n */\n async ensureAgentInstalled(\n silent = false,\n agentPath?: string\n ): Promise<BeLinkReviewEnsureResult> {\n return isCheckCliInstall({ apiKey: this.#getApiKey(), silent, agentPath });\n }\n\n /**\n * 通过 Cursor Headless CLI 与 Cursor 对话(发送 prompt,拿到回复)。\n * 默认会先执行 ensureAgentInstalled(检查/安装 CLI),再启动 agent 执行提问。\n * 参考:https://cursor.com/cn/docs/cli/headless\n */\n async chat(\n prompt: string,\n options: BeLinkReviewChatOptions = {}\n ): Promise<string> {\n const actualAgentPath =\n options.agentPath || this.#getAgentPath() || \"agent\";\n const model = options.model || this.#getModel();\n const args: string[] = [\"--yolo\", \"-p\", prompt];\n if (model) args.unshift(\"--model\", model);\n if (options.outputFormat === \"json\") args.push(\"--output-format\", \"json\");\n\n return new Promise((resolve, reject) => {\n const env = { ...process.env, CURSOR_API_KEY: this.#getApiKey() };\n const proc = spawn(actualAgentPath, args, {\n env,\n cwd: process.cwd(),\n shell: false,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n proc.stdout?.on(\"data\", (chunk) => {\n stdout += chunk.toString();\n });\n proc.stderr?.on(\"data\", (chunk) => {\n stderr += chunk.toString();\n });\n\n proc.on(\"error\", (err) => {\n reject(\n new Error(\n `[review-mark] 无法启动 Cursor CLI (${actualAgentPath}),请先安装:curl https://cursor.com/install -fsS | bash。原始错误: ${err.message}`\n )\n );\n });\n\n proc.on(\"close\", (code) => {\n if (code !== 0) {\n reject(\n new Error(\n `[review-mark] agent 退出码 ${code}${\n stderr ? `: ${stderr.trim()}` : \"\"\n }`\n )\n );\n return;\n }\n resolve(stdout.trim());\n });\n });\n }\n}\n","import { spawn, exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { existsSync, appendFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { BeLinkReviewEnsureResult } from \"@/types\";\n\nconst execAsync = promisify(exec);\n\ninterface CheckCliInstallOptions {\n apiKey: string;\n silent?: boolean;\n agentPath?: string;\n}\n\n// Cursor 官方安装脚本默认将 agent 放在 ~/.local/bin\nconst LOCAL_BIN = join(homedir(), \".local\", \"bin\");\n\n// 常见的 Cursor CLI 安装路径\nconst COMMON_AGENT_PATHS = [\n join(LOCAL_BIN, \"agent\"), // Cursor 官方安装脚本默认路径\n join(homedir(), \".cursor\", \"agent\"),\n \"/usr/local/bin/agent\",\n \"/opt/homebrew/bin/agent\",\n];\n\n/**\n * 将 ~/.local/bin 写入用户 shell 配置文件(.zshrc / .bashrc),\n * 并立即注入当前进程的 PATH,确保安装后无需重启终端即可使用。\n */\nfunction setupLocalBinPath(silent: boolean): void {\n // 立即让当前进程能找到 agent\n if (!process.env.PATH?.includes(LOCAL_BIN)) {\n process.env.PATH = `${LOCAL_BIN}:${process.env.PATH}`;\n }\n\n const exportLine = `\\nexport PATH=\"$HOME/.local/bin:$PATH\"`;\n const shell = process.env.SHELL ?? \"\";\n const rcFiles: string[] = [];\n\n if (shell.includes(\"zsh\")) {\n rcFiles.push(join(homedir(), \".zshrc\"));\n } else if (shell.includes(\"bash\")) {\n rcFiles.push(join(homedir(), \".bashrc\"));\n } else {\n // 两个都写,保证覆盖\n rcFiles.push(join(homedir(), \".zshrc\"), join(homedir(), \".bashrc\"));\n }\n\n for (const rc of rcFiles) {\n try {\n const content = existsSync(rc)\n ? require(\"node:fs\").readFileSync(rc, \"utf-8\")\n : \"\";\n if (!content.includes(\".local/bin\")) {\n appendFileSync(rc, exportLine);\n if (!silent) {\n console.log(`[review-mark] 已将 ~/.local/bin 写入 ${rc}`);\n }\n }\n } catch {\n // 写入失败不阻断流程\n }\n }\n}\n\nasync function findAgentExecutable(\n userAgentPath?: string\n): Promise<string | null> {\n // 1. 优先使用用户指定的路径\n if (userAgentPath && existsSync(userAgentPath)) {\n return userAgentPath;\n }\n\n // 2. 尝试通过 which 命令在 PATH 中查找\n try {\n const { stdout } = await execAsync(\"which agent\");\n const pathFromWhich = stdout.trim();\n if (pathFromWhich && existsSync(pathFromWhich)) {\n return pathFromWhich;\n }\n } catch (error) {\n // ignore error, continue to next method\n }\n\n // 3. 检查常见安装路径\n for (const path of COMMON_AGENT_PATHS) {\n if (existsSync(path)) {\n return path;\n }\n }\n\n return null;\n}\n\nexport async function isCheckCliInstall(\n options: CheckCliInstallOptions\n): Promise<BeLinkReviewEnsureResult> {\n const { silent = false, agentPath: userAgentPath } = options;\n\n let actualAgentPath = await findAgentExecutable(userAgentPath);\n\n if (actualAgentPath) {\n if (!silent) {\n console.log(\n `[review-mark] Cursor CLI (agent) 已在 ${actualAgentPath} 找到。`\n );\n }\n return { isInstalled: true, message: \"Cursor CLI 已安装\", actualAgentPath };\n }\n\n if (!silent) {\n console.log(\"[review-mark] Cursor CLI (agent) 未找到,正在尝试安装...\");\n console.log(\n \"[review-mark] 执行安装命令: curl https://cursor.com/install -fsS | bash\"\n );\n }\n\n return new Promise((resolve, reject) => {\n const installProcess = spawn(\n \"bash\",\n [\"-c\", \"curl https://cursor.com/install -fsS | bash\"],\n {\n stdio: \"inherit\", // 将安装过程的输出直接显示给用户\n }\n );\n\n installProcess.on(\"close\", async (code) => {\n if (code === 0) {\n // 安装完成后自动配置 PATH(写入 rc 文件 + 注入当前进程)\n setupLocalBinPath(silent);\n\n // 再次检查 agent 命令是否可用\n actualAgentPath = await findAgentExecutable(userAgentPath);\n if (actualAgentPath) {\n if (!silent) {\n console.log(\"[review-mark] Cursor CLI 安装成功。\");\n }\n resolve({\n isInstalled: true,\n message: \"Cursor CLI 安装成功\",\n actualAgentPath,\n });\n } else {\n reject(\n new Error(\n `[review-mark] Cursor CLI 安装命令执行成功,但未找到 agent 可执行文件。请手动检查安装:curl https://cursor.com/install -fsS | bash。`\n )\n );\n }\n } else {\n reject(\n new Error(\n `[review-mark] Cursor CLI 安装失败,退出码 ${code}。请手动安装:curl https://cursor.com/install -fsS | bash。`\n )\n );\n }\n });\n\n installProcess.on(\"error\", (err) => {\n reject(\n new Error(\n `[review-mark] 无法启动安装进程:${err.message}。请手动安装:curl https://cursor.com/install -fsS | bash`\n )\n );\n });\n });\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\n// 固定的忽略文件模式,这些文件将永远不会被审查\nconst FIXED_IGNORE_PATTERNS = [\"package.json\"];\n\nasync function runGit(args: string[], cwd: string): Promise<string> {\n const { stdout } = await execFileAsync(\"git\", args, { cwd });\n return stdout.trim();\n}\n\nexport async function getGitDiff(\n userIgnorePatterns: string[] = [],\n cwd: string = process.cwd()\n): Promise<string> {\n // 检查是否在 git 仓库中\n try {\n await runGit([\"rev-parse\", \"--git-dir\"], cwd);\n } catch (error) {\n console.error(`[review-mark] 当前目录不是 git 仓库: ${cwd}`);\n return \"\";\n }\n\n // 合并固定忽略模式和用户提供的忽略模式\n const allIgnorePatterns = [\n ...new Set([...FIXED_IGNORE_PATTERNS, ...userIgnorePatterns]),\n ];\n // 使用 execFile 直接传参,完全绕过 shell,:(exclude) 不会被解析\n // git 参数顺序:git diff [options] [commit] -- [pathspec]\n // options/commit 必须在 -- 和 pathspec 之前\n const excludeArgs = allIgnorePatterns.map((p) => `:(exclude)${p}`);\n\n let diff = \"\";\n\n try {\n // 优先使用 git diff --cached 获取暂存区的改动\n diff = await runGit(\n [\"diff\", \"--no-color\", \"--relative\", \"--cached\", \"--\", ...excludeArgs],\n cwd\n );\n if (diff) {\n console.log(\"[review-mark] 检测到暂存区改动\");\n }\n } catch (error: any) {\n console.warn(`[review-mark] 获取暂存区 diff 失败: ${error.message}`);\n }\n\n if (!diff) {\n try {\n // 如果暂存区没有改动,则获取工作区和 HEAD 的改动\n diff = await runGit(\n [\"diff\", \"--no-color\", \"--relative\", \"HEAD\", \"--\", ...excludeArgs],\n cwd\n );\n if (diff) {\n console.log(\"[review-mark] 检测到工作区改动(相对于 HEAD)\");\n }\n } catch (error: any) {\n console.warn(`[review-mark] 获取工作区 diff 失败: ${error.message}`);\n }\n }\n\n if (!diff) {\n console.log(\"[review-mark] 未检测到代码改动(已检查暂存区和工作区)\");\n }\n\n return diff;\n}\n","\nexport function generateAIPrompt(diff: string): string {\n return `你是一名资深软件工程师,请 review 以下代码变更。\nGit diff:\n${diff}\n请分析:\n是否存在 bug\n是否存在潜在逻辑问题\n是否存在性能问题\n是否存在代码风格问题\n给出优化建议`;\n}\n","import * as lark from \"@larksuiteoapi/node-sdk\";\nimport {\n appId,\n appSecret,\n receiveId,\n receiveIdType,\n messageType,\n messageTitle,\n} from \"../constants\";\n\n/**\n * 发送 Code Review 结果到飞书\n * 使用飞书官方 Node.js SDK,所有配置从 constants.ts 读取\n */\nexport async function sendReviewToFeishu(reviewContent: string): Promise<void> {\n console.log(\"[review-mark] 正在发送消息到飞书...\");\n console.log(`[review-mark] 消息类型: ${messageType}`);\n\n try {\n // 初始化飞书客户端\n const client = new lark.Client({\n appId,\n appSecret,\n domain: lark.Domain.Feishu,\n });\n\n // 根据消息类型构造消息内容\n let msgContent: string;\n let msgType: string;\n\n if (messageType === \"interactive\") {\n // Interactive 卡片消息(支持 Markdown 渲染)\n msgContent = JSON.stringify({\n config: {\n wide_screen_mode: true,\n },\n header: {\n title: {\n tag: \"plain_text\",\n content: messageTitle,\n },\n template: \"blue\",\n },\n elements: [\n {\n tag: \"div\",\n text: {\n tag: \"lark_md\",\n content: reviewContent,\n },\n },\n {\n tag: \"hr\",\n },\n {\n tag: \"note\",\n elements: [\n {\n tag: \"plain_text\",\n content: `生成时间: ${new Date().toLocaleString(\"zh-CN\", {\n timeZone: \"Asia/Shanghai\",\n })}`,\n },\n ],\n },\n ],\n });\n msgType = \"interactive\";\n } else if (messageType === \"post\") {\n // Post 富文本消息\n msgContent = JSON.stringify({\n zh_cn: {\n title: messageTitle,\n content: convertMarkdownToFeishuPost(reviewContent),\n },\n });\n msgType = \"post\";\n } else if (messageType === \"text\") {\n // Text 纯文本消息\n msgContent = JSON.stringify({\n text: `${messageTitle}\\n\\n${reviewContent}`,\n });\n msgType = \"text\";\n } else {\n throw new Error(`[review-mark] 不支持的消息类型: ${messageType}`);\n }\n\n // 调试日志\n console.log(`[review-mark] 发送参数:`);\n console.log(` - receive_id_type: ${receiveIdType}`);\n console.log(` - receive_id: ${receiveId}`);\n console.log(` - msg_type: ${msgType}`);\n\n // 调用发送消息接口\n const response = await client.im.message.create({\n params: {\n receive_id_type: receiveIdType,\n },\n data: {\n receive_id: receiveId,\n msg_type: msgType,\n content: msgContent,\n },\n });\n\n if (response.code !== 0) {\n throw new Error(\n `[review-mark] 飞书 API 返回错误: ${response.msg || \"未知错误\"}`\n );\n }\n\n console.log(\"[review-mark] ✅ 飞书消息发送成功\");\n console.log(\n `[review-mark] 消息 ID: ${response.data?.message_id || \"未知\"}`\n );\n } catch (error: any) {\n console.error(`[review-mark] ❌ 飞书消息发送失败: ${error.message}`);\n throw error;\n }\n}\n\n/**\n * 将 Markdown 格式转换为飞书 post 格式\n * 飞书 post 格式支持富文本样式\n */\nfunction convertMarkdownToFeishuPost(markdown: string): any[][] {\n const lines = markdown.split(\"\\n\");\n const result: any[][] = [];\n\n let inCodeBlock = false;\n let codeBlockContent: string[] = [];\n\n for (const line of lines) {\n // 处理代码块\n if (line.startsWith(\"```\")) {\n if (inCodeBlock) {\n // 代码块结束\n if (codeBlockContent.length > 0) {\n result.push([\n {\n tag: \"text\",\n text: codeBlockContent.join(\"\\n\"),\n style: [\"code\"],\n },\n ]);\n codeBlockContent = [];\n }\n inCodeBlock = false;\n } else {\n // 代码块开始\n inCodeBlock = true;\n }\n continue;\n }\n\n if (inCodeBlock) {\n codeBlockContent.push(line);\n continue;\n }\n\n // 空行\n if (!line.trim()) {\n result.push([{ tag: \"text\", text: \"\" }]);\n continue;\n }\n\n // 标题(加粗 + 加大)\n if (line.startsWith(\"#\")) {\n const level = line.match(/^#+/)?.[0].length || 1;\n const text = line.replace(/^#+\\s*/, \"\");\n result.push([\n {\n tag: \"text\",\n text: text,\n style: level <= 2 ? [\"bold\", \"underline\"] : [\"bold\"],\n },\n ]);\n continue;\n }\n\n // 解析行内样式(加粗、链接等)\n const parsedLine = parseLineStyles(line);\n result.push(parsedLine);\n }\n\n // 如果还在代码块中,添加剩余内容\n if (codeBlockContent.length > 0) {\n result.push([\n {\n tag: \"text\",\n text: codeBlockContent.join(\"\\n\"),\n style: [\"code\"],\n },\n ]);\n }\n\n return result;\n}\n\n/**\n * 解析行内样式(加粗、链接、代码等)\n */\nfunction parseLineStyles(line: string): any[] {\n const elements: any[] = [];\n let currentText = \"\";\n let i = 0;\n\n while (i < line.length) {\n // 处理加粗 **text**\n if (line[i] === \"*\" && line[i + 1] === \"*\") {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const endIndex = line.indexOf(\"**\", i + 2);\n if (endIndex !== -1) {\n const boldText = line.substring(i + 2, endIndex);\n elements.push({ tag: \"text\", text: boldText, style: [\"bold\"] });\n i = endIndex + 2;\n continue;\n }\n }\n\n // 处理行内代码 `code`\n if (line[i] === \"`\" && line[i + 1] !== \"`\") {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const endIndex = line.indexOf(\"`\", i + 1);\n if (endIndex !== -1) {\n const codeText = line.substring(i + 1, endIndex);\n elements.push({\n tag: \"text\",\n text: codeText,\n style: [\"code\"],\n });\n i = endIndex + 1;\n continue;\n }\n }\n\n // 处理链接 [text](url)\n if (line[i] === \"[\") {\n const textEnd = line.indexOf(\"](\", i);\n const urlEnd = line.indexOf(\")\", textEnd + 2);\n if (textEnd !== -1 && urlEnd !== -1) {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const linkText = line.substring(i + 1, textEnd);\n const url = line.substring(textEnd + 2, urlEnd);\n elements.push({\n tag: \"a\",\n text: linkText,\n href: url,\n });\n i = urlEnd + 1;\n continue;\n }\n }\n\n currentText += line[i];\n i++;\n }\n\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n }\n\n return elements.length > 0 ? elements : [{ tag: \"text\", text: line }];\n}\n","// 默认使用的 AI 模型\nexport const DEFAULT_MODEL = \"composer-1\";\n\n// 飞书机器人配置(内置,不需要外部配置)\nexport const appId = \"cli_a93822da7238dbb5\";\nexport const appSecret = \"ZQdcpLUHFb4gFa8cGfrlJfVfSSyGtyzF\";\nexport const receiveId = \"oc_482b6a04f95f4206c4fa9bc61829fd17\";\nexport const receiveIdType = \"chat_id\";\nexport const messageType = \"post\"; // text | post | interactive\nexport const messageTitle = \"🔍 Code Review 结果\";\n"],"mappings":"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","names":["import_node_child_process","import_node_fs","import_node_path","import_node_child_process","import_node_util"]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/review.ts","../../src/core/BeLinkReview.ts","../../src/utils/checkCli.ts","../../src/core/git.ts","../../src/core/prompt.ts","../../src/core/feishu.ts","../../src/constants.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { BeLinkReview } from \"../core/BeLinkReview\";\n\nconst program = new Command();\n\nconst sharedOptions = (cmd: Command) =>\n cmd\n .option(\n \"--apiKey <key>\",\n \"Cursor API Key (overrides CURSOR_API_KEY environment variable)\"\n )\n .option(\n \"--agentPath <path>\",\n \"Path to the Cursor CLI agent executable (overrides CURSOR_AGENT_PATH environment variable)\"\n )\n .option(\n \"--model <model>\",\n \"AI model to use, e.g. claude-3-5-sonnet (overrides CURSOR_MODEL environment variable)\"\n )\n .option(\n \"--ignore <patterns>\",\n \"Comma-separated list of glob patterns to ignore (e.g., *.lock,dist/**)\"\n )\n .option(\"--no-feishu\", \"Disable Feishu notification\");\n\nconst runAction = async (options: any) => {\n try {\n const ignorePatterns = options.ignore\n ? options.ignore.split(\",\")\n : undefined;\n\n const instance = BeLinkReview.getInstance(\n options.apiKey,\n options.agentPath,\n ignorePatterns,\n options.feishu !== false,\n options.model\n );\n await instance.goCli();\n } catch (error: any) {\n console.error(`[review-mark] Error: ${error.message}`);\n process.exit(1);\n }\n};\n\nsharedOptions(\n program\n .name(\"review-mark\")\n .description(\"AI-powered code review tool\")\n .version(\"1.0.0\")\n).action(runAction);\n\nsharedOptions(\n program.command(\"review\").description(\"Perform an AI code review on git diff\")\n).action(runAction);\n\nprogram.parse(process.argv);\n","import { spawn } from \"node:child_process\";\nimport { readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type {\n BeLinkReviewOptions,\n BeLinkReviewChatOptions,\n BeLinkReviewEnsureResult,\n} from \"../types\";\nimport { isCheckCliInstall } from \"../utils/checkCli\";\nimport { getGitDiff } from \"./git\";\nimport { generateAIPrompt } from \"./prompt\";\nimport { sendReviewToFeishu } from \"./feishu\";\nimport { DEFAULT_MODEL } from \"../constants\";\n\nexport class BeLinkReview {\n static #instance: BeLinkReview | null = null;\n #apiKey: string | undefined;\n #agentPath: string | undefined;\n #ignorePatterns: string[] | undefined;\n #enableFeishu: boolean;\n #model: string | undefined;\n\n private constructor(\n apiKey?: string,\n agentPath?: string,\n ignorePatterns?: string[],\n enableFeishu: boolean = true,\n model?: string\n ) {\n this.#apiKey = apiKey;\n this.#agentPath = agentPath;\n this.#ignorePatterns = ignorePatterns;\n this.#enableFeishu = enableFeishu;\n this.#model = model;\n }\n\n /**\n * 初始化单例并注入参数,在项目入口调用一次即可。\n * 负责保存 apiKey 和自动写入 package.json 脚本。\n * 此方法主要用于在用户项目中设置 apiKey 和脚本,CLI 运行时会优先从环境变量或命令行参数获取。\n */\n static init(options: BeLinkReviewOptions = {}): BeLinkReview {\n if (BeLinkReview.#instance === null) {\n BeLinkReview.#instance = new BeLinkReview(\n options.apiKey,\n options.agentPath,\n options.ignore,\n options.enableFeishu ?? true,\n options.model\n );\n } else {\n BeLinkReview.#instance.#apiKey = options.apiKey;\n BeLinkReview.#instance.#agentPath = options.agentPath;\n BeLinkReview.#instance.#ignorePatterns = options.ignore;\n BeLinkReview.#instance.#enableFeishu = options.enableFeishu ?? true;\n BeLinkReview.#instance.#model = options.model;\n }\n BeLinkReview.#instance.#setupProjectScript();\n return BeLinkReview.#instance;\n }\n\n /**\n * 获取单例实例。如果未通过 init() 初始化,则尝试从环境变量 CURSOR_API_KEY 获取。\n * @param cliApiKey 可选的命令行传入的 apiKey\n * @param cliAgentPath 可选的命令行传入的 agentPath\n * @param cliIgnorePatterns 可选的命令行传入的 ignorePatterns\n * @param cliEnableFeishu 可选的命令行传入的飞书开关\n */\n static getInstance(\n cliApiKey?: string,\n cliAgentPath?: string,\n cliIgnorePatterns?: string[],\n cliEnableFeishu?: boolean,\n cliModel?: string\n ): BeLinkReview {\n if (BeLinkReview.#instance === null) {\n const apiKey = cliApiKey || process.env.CURSOR_API_KEY;\n const agentPath = cliAgentPath || process.env.CURSOR_AGENT_PATH;\n const ignorePatterns =\n cliIgnorePatterns ||\n (process.env.BE_LINK_REVIEW_IGNORE\n ? process.env.BE_LINK_REVIEW_IGNORE.split(\",\")\n : undefined);\n const enableFeishu =\n cliEnableFeishu ?? process.env.FEISHU_ENABLED !== \"false\";\n const model = cliModel || process.env.CURSOR_MODEL;\n\n if (!apiKey) {\n throw new Error(\n '[review-mark] 请先调用 BeLinkReview.init({ apiKey: \"...\" }) 初始化,或设置环境变量 CURSOR_API_KEY,或通过命令行参数 --apiKey 传入。'\n );\n }\n BeLinkReview.#instance = new BeLinkReview(\n apiKey,\n agentPath,\n ignorePatterns,\n enableFeishu,\n model\n );\n } else {\n if (cliApiKey && !BeLinkReview.#instance.#apiKey) {\n BeLinkReview.#instance.#apiKey = cliApiKey;\n }\n if (cliAgentPath && !BeLinkReview.#instance.#agentPath) {\n BeLinkReview.#instance.#agentPath = cliAgentPath;\n }\n if (cliIgnorePatterns && !BeLinkReview.#instance.#ignorePatterns) {\n BeLinkReview.#instance.#ignorePatterns = cliIgnorePatterns;\n }\n if (cliEnableFeishu !== undefined) {\n BeLinkReview.#instance.#enableFeishu = cliEnableFeishu;\n }\n if (cliModel) {\n BeLinkReview.#instance.#model = cliModel;\n }\n }\n return BeLinkReview.#instance;\n }\n\n #getApiKey(): string {\n const apiKey = this.#apiKey || process.env.CURSOR_API_KEY;\n if (!apiKey) {\n throw new Error(\n '[review-mark] 请先在 init({ apiKey: \"...\" }) 中传入 apiKey,或设置环境变量 CURSOR_API_KEY,或通过命令行参数 --apiKey 传入。'\n );\n }\n return apiKey;\n }\n\n #getAgentPath(): string | undefined {\n return this.#agentPath || process.env.CURSOR_AGENT_PATH;\n }\n\n #getIgnorePatterns(): string[] {\n const envIgnore = process.env.BE_LINK_REVIEW_IGNORE\n ? process.env.BE_LINK_REVIEW_IGNORE.split(\",\")\n : [];\n return this.#ignorePatterns || envIgnore;\n }\n\n #isFeishuEnabled(): boolean {\n return this.#enableFeishu;\n }\n\n #getModel(): string {\n return this.#model || process.env.CURSOR_MODEL || DEFAULT_MODEL;\n }\n\n /**\n * 自动往用户项目 package.json 写入 script\n */\n async #setupProjectScript() {\n try {\n const packageJsonPath = join(process.cwd(), \"package.json\");\n const packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf-8\"));\n\n if (!packageJson.scripts) {\n packageJson.scripts = {};\n }\n\n if (!packageJson.scripts[\"review-mark\"]) {\n packageJson.scripts[\"review-mark\"] = \"review-mark\";\n writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));\n console.log(\n \"[review-mark] 已在 package.json 中添加 'review-mark' 脚本。\"\n );\n } else {\n console.log(\"[review-mark] 'review-mark' 脚本已存在,跳过添加。\");\n }\n } catch (error) {\n console.error(\"[review-mark] 无法更新 package.json: \", error);\n }\n }\n\n /**\n * 执行一次完整 CLI 流程:获取 git diff → 生成 prompt → 检查/安装 CLI → 与 Cursor 对话并打印结果。\n */\n async goCli(): Promise<string> {\n const apiKey = this.#getApiKey();\n const agentPath = this.#getAgentPath();\n const ignorePatterns = this.#getIgnorePatterns();\n\n const ensureResult = await this.ensureAgentInstalled(false, agentPath);\n\n if (!ensureResult.isInstalled) {\n throw new Error(\n \"[review-mark] Cursor CLI 未安装且自动安装失败,请手动安装。\"\n );\n }\n\n console.log(\"[review-mark] Getting git diff...\");\n const diff = await getGitDiff(ignorePatterns, process.cwd());\n\n if (!diff) {\n console.log(\"[review-mark] No code changes detected\");\n return \"No code changes detected\";\n }\n\n const prompt = generateAIPrompt(diff);\n console.log(\"[review-mark] Sending to AI...\");\n const response = await this.chat(prompt, {\n agentPath: ensureResult.actualAgentPath,\n force: true,\n model: this.#getModel(),\n });\n console.log(this.#getModel(), \"model\");\n console.log(\"===== AI Review =====\");\n console.log(response);\n\n // 如果启用了飞书,发送到飞书\n if (this.#isFeishuEnabled()) {\n try {\n await sendReviewToFeishu(response);\n } catch (error: any) {\n console.error(\n `[review-mark] 飞书通知发送失败,但不影响 review 结果: ${error.message}`\n );\n }\n }\n\n return response;\n }\n\n /**\n * 检查 Cursor CLI 是否已安装;未安装则自动安装(需网络)。\n * @param silent 为 true 时不打印「已安装」等提示\n * @param agentPath 可选的 agent 可执行文件路径\n */\n async ensureAgentInstalled(\n silent = false,\n agentPath?: string\n ): Promise<BeLinkReviewEnsureResult> {\n return isCheckCliInstall({ apiKey: this.#getApiKey(), silent, agentPath });\n }\n\n /**\n * 通过 Cursor Headless CLI 与 Cursor 对话(发送 prompt,拿到回复)。\n * 默认会先执行 ensureAgentInstalled(检查/安装 CLI),再启动 agent 执行提问。\n * 参考:https://cursor.com/cn/docs/cli/headless\n */\n async chat(\n prompt: string,\n options: BeLinkReviewChatOptions = {}\n ): Promise<string> {\n const actualAgentPath =\n options.agentPath || this.#getAgentPath() || \"agent\";\n const model = options.model || this.#getModel();\n const args: string[] = [\"--yolo\", \"-p\", prompt];\n if (model) args.unshift(\"--model\", model);\n if (options.outputFormat === \"json\") args.push(\"--output-format\", \"json\");\n\n return new Promise((resolve, reject) => {\n const env = { ...process.env, CURSOR_API_KEY: this.#getApiKey() };\n const proc = spawn(actualAgentPath, args, {\n env,\n cwd: process.cwd(),\n shell: false,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n proc.stdout?.on(\"data\", (chunk) => {\n stdout += chunk.toString();\n });\n proc.stderr?.on(\"data\", (chunk) => {\n stderr += chunk.toString();\n });\n\n proc.on(\"error\", (err) => {\n reject(\n new Error(\n `[review-mark] 无法启动 Cursor CLI (${actualAgentPath}),请先安装:curl https://cursor.com/install -fsS | bash。原始错误: ${err.message}`\n )\n );\n });\n\n proc.on(\"close\", (code) => {\n if (code !== 0) {\n reject(\n new Error(\n `[review-mark] agent 退出码 ${code}${\n stderr ? `: ${stderr.trim()}` : \"\"\n }`\n )\n );\n return;\n }\n resolve(stdout.trim());\n });\n });\n }\n}\n","import { spawn, exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { existsSync, appendFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { BeLinkReviewEnsureResult } from \"@/types\";\n\nconst execAsync = promisify(exec);\n\ninterface CheckCliInstallOptions {\n apiKey: string;\n silent?: boolean;\n agentPath?: string;\n}\n\n// Cursor 官方安装脚本默认将 agent 放在 ~/.local/bin\nconst LOCAL_BIN = join(homedir(), \".local\", \"bin\");\n\n// 常见的 Cursor CLI 安装路径\nconst COMMON_AGENT_PATHS = [\n join(LOCAL_BIN, \"agent\"), // Cursor 官方安装脚本默认路径\n join(homedir(), \".cursor\", \"agent\"),\n \"/usr/local/bin/agent\",\n \"/opt/homebrew/bin/agent\",\n];\n\n/**\n * 将 ~/.local/bin 写入用户 shell 配置文件(.zshrc / .bashrc),\n * 并立即注入当前进程的 PATH,确保安装后无需重启终端即可使用。\n */\nfunction setupLocalBinPath(silent: boolean): void {\n // 立即让当前进程能找到 agent\n if (!process.env.PATH?.includes(LOCAL_BIN)) {\n process.env.PATH = `${LOCAL_BIN}:${process.env.PATH}`;\n }\n\n const exportLine = `\\nexport PATH=\"$HOME/.local/bin:$PATH\"`;\n const shell = process.env.SHELL ?? \"\";\n const rcFiles: string[] = [];\n\n if (shell.includes(\"zsh\")) {\n rcFiles.push(join(homedir(), \".zshrc\"));\n } else if (shell.includes(\"bash\")) {\n rcFiles.push(join(homedir(), \".bashrc\"));\n } else {\n // 两个都写,保证覆盖\n rcFiles.push(join(homedir(), \".zshrc\"), join(homedir(), \".bashrc\"));\n }\n\n for (const rc of rcFiles) {\n try {\n const content = existsSync(rc)\n ? require(\"node:fs\").readFileSync(rc, \"utf-8\")\n : \"\";\n if (!content.includes(\".local/bin\")) {\n appendFileSync(rc, exportLine);\n if (!silent) {\n console.log(`[review-mark] 已将 ~/.local/bin 写入 ${rc}`);\n }\n }\n } catch {\n // 写入失败不阻断流程\n }\n }\n}\n\nasync function findAgentExecutable(\n userAgentPath?: string\n): Promise<string | null> {\n // 1. 优先使用用户指定的路径\n if (userAgentPath && existsSync(userAgentPath)) {\n return userAgentPath;\n }\n\n // 2. 尝试通过 which 命令在 PATH 中查找\n try {\n const { stdout } = await execAsync(\"which agent\");\n const pathFromWhich = stdout.trim();\n if (pathFromWhich && existsSync(pathFromWhich)) {\n return pathFromWhich;\n }\n } catch (error) {\n // ignore error, continue to next method\n }\n\n // 3. 检查常见安装路径\n for (const path of COMMON_AGENT_PATHS) {\n if (existsSync(path)) {\n return path;\n }\n }\n\n return null;\n}\n\nexport async function isCheckCliInstall(\n options: CheckCliInstallOptions\n): Promise<BeLinkReviewEnsureResult> {\n const { silent = false, agentPath: userAgentPath } = options;\n\n let actualAgentPath = await findAgentExecutable(userAgentPath);\n\n if (actualAgentPath) {\n if (!silent) {\n console.log(\n `[review-mark] Cursor CLI (agent) 已在 ${actualAgentPath} 找到。`\n );\n }\n return { isInstalled: true, message: \"Cursor CLI 已安装\", actualAgentPath };\n }\n\n if (!silent) {\n console.log(\"[review-mark] Cursor CLI (agent) 未找到,正在尝试安装...\");\n console.log(\n \"[review-mark] 执行安装命令: curl https://cursor.com/install -fsS | bash\"\n );\n }\n\n return new Promise((resolve, reject) => {\n const installProcess = spawn(\n \"bash\",\n [\"-c\", \"curl https://cursor.com/install -fsS | bash\"],\n {\n stdio: \"inherit\", // 将安装过程的输出直接显示给用户\n }\n );\n\n installProcess.on(\"close\", async (code) => {\n if (code === 0) {\n // 安装完成后自动配置 PATH(写入 rc 文件 + 注入当前进程)\n setupLocalBinPath(silent);\n\n // 再次检查 agent 命令是否可用\n actualAgentPath = await findAgentExecutable(userAgentPath);\n if (actualAgentPath) {\n if (!silent) {\n console.log(\"[review-mark] Cursor CLI 安装成功。\");\n }\n resolve({\n isInstalled: true,\n message: \"Cursor CLI 安装成功\",\n actualAgentPath,\n });\n } else {\n reject(\n new Error(\n `[review-mark] Cursor CLI 安装命令执行成功,但未找到 agent 可执行文件。请手动检查安装:curl https://cursor.com/install -fsS | bash。`\n )\n );\n }\n } else {\n reject(\n new Error(\n `[review-mark] Cursor CLI 安装失败,退出码 ${code}。请手动安装:curl https://cursor.com/install -fsS | bash。`\n )\n );\n }\n });\n\n installProcess.on(\"error\", (err) => {\n reject(\n new Error(\n `[review-mark] 无法启动安装进程:${err.message}。请手动安装:curl https://cursor.com/install -fsS | bash`\n )\n );\n });\n });\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\n// 固定的忽略文件模式,这些文件将永远不会被审查\nconst FIXED_IGNORE_PATTERNS = [\"package.json\"];\n\nasync function runGit(args: string[], cwd: string): Promise<string> {\n const { stdout } = await execFileAsync(\"git\", args, { cwd });\n return stdout.trim();\n}\n\nexport async function getGitDiff(\n userIgnorePatterns: string[] = [],\n cwd: string = process.cwd()\n): Promise<string> {\n // 检查是否在 git 仓库中\n try {\n await runGit([\"rev-parse\", \"--git-dir\"], cwd);\n } catch (error) {\n console.error(`[review-mark] 当前目录不是 git 仓库: ${cwd}`);\n return \"\";\n }\n\n // 合并固定忽略模式和用户提供的忽略模式\n const allIgnorePatterns = [\n ...new Set([...FIXED_IGNORE_PATTERNS, ...userIgnorePatterns]),\n ];\n // 使用 execFile 直接传参,完全绕过 shell,:(exclude) 不会被解析\n // git 参数顺序:git diff [options] [commit] -- [pathspec]\n // options/commit 必须在 -- 和 pathspec 之前\n const excludeArgs = allIgnorePatterns.map((p) => `:(exclude)${p}`);\n\n let diff = \"\";\n\n try {\n // 优先使用 git diff --cached 获取暂存区的改动\n diff = await runGit(\n [\"diff\", \"--no-color\", \"--relative\", \"--cached\", \"--\", ...excludeArgs],\n cwd\n );\n if (diff) {\n console.log(\"[review-mark] 检测到暂存区改动\");\n }\n } catch (error: any) {\n console.warn(`[review-mark] 获取暂存区 diff 失败: ${error.message}`);\n }\n\n if (!diff) {\n try {\n // 如果暂存区没有改动,则获取工作区和 HEAD 的改动\n diff = await runGit(\n [\"diff\", \"--no-color\", \"--relative\", \"HEAD\", \"--\", ...excludeArgs],\n cwd\n );\n if (diff) {\n console.log(\"[review-mark] 检测到工作区改动(相对于 HEAD)\");\n }\n } catch (error: any) {\n console.warn(`[review-mark] 获取工作区 diff 失败: ${error.message}`);\n }\n }\n\n if (!diff) {\n console.log(\"[review-mark] 未检测到代码改动(已检查暂存区和工作区)\");\n }\n\n return diff;\n}\n","\nexport function generateAIPrompt(diff: string): string {\n return `你是一名资深软件工程师,请 review 以下代码变更。\nGit diff:\n${diff}\n请分析:\n是否存在 bug\n是否存在潜在逻辑问题\n是否存在性能问题\n是否存在代码风格问题\n给出优化建议`;\n}\n","import * as lark from \"@larksuiteoapi/node-sdk\";\nimport {\n appId,\n appSecret,\n receiveId,\n receiveIdType,\n messageType,\n messageTitle,\n} from \"../constants\";\n\n/**\n * 发送 Code Review 结果到飞书\n * 使用飞书官方 Node.js SDK,所有配置从 constants.ts 读取\n */\nexport async function sendReviewToFeishu(reviewContent: string): Promise<void> {\n console.log(\"[review-mark] 正在发送消息到飞书...\");\n console.log(`[review-mark] 消息类型: ${messageType}`);\n\n try {\n // 初始化飞书客户端\n const client = new lark.Client({\n appId,\n appSecret,\n domain: lark.Domain.Feishu,\n });\n\n // 根据消息类型构造消息内容\n let msgContent: string;\n let msgType: string;\n\n if (messageType === \"interactive\") {\n // Interactive 卡片消息(支持 Markdown 渲染)\n msgContent = JSON.stringify({\n config: {\n wide_screen_mode: true,\n },\n header: {\n title: {\n tag: \"plain_text\",\n content: messageTitle,\n },\n template: \"blue\",\n },\n elements: [\n {\n tag: \"div\",\n text: {\n tag: \"lark_md\",\n content: reviewContent,\n },\n },\n {\n tag: \"hr\",\n },\n {\n tag: \"note\",\n elements: [\n {\n tag: \"plain_text\",\n content: `生成时间: ${new Date().toLocaleString(\"zh-CN\", {\n timeZone: \"Asia/Shanghai\",\n })}`,\n },\n ],\n },\n ],\n });\n msgType = \"interactive\";\n } else if (messageType === \"post\") {\n // Post 富文本消息\n msgContent = JSON.stringify({\n zh_cn: {\n title: messageTitle,\n content: convertMarkdownToFeishuPost(reviewContent),\n },\n });\n msgType = \"post\";\n } else if (messageType === \"text\") {\n // Text 纯文本消息\n msgContent = JSON.stringify({\n text: `${messageTitle}\\n\\n${reviewContent}`,\n });\n msgType = \"text\";\n } else {\n throw new Error(`[review-mark] 不支持的消息类型: ${messageType}`);\n }\n\n // 调试日志\n console.log(`[review-mark] 发送参数:`);\n console.log(` - receive_id_type: ${receiveIdType}`);\n console.log(` - receive_id: ${receiveId}`);\n console.log(` - msg_type: ${msgType}`);\n\n // 调用发送消息接口\n const response = await client.im.message.create({\n params: {\n receive_id_type: receiveIdType,\n },\n data: {\n receive_id: receiveId,\n msg_type: msgType,\n content: msgContent,\n },\n });\n\n if (response.code !== 0) {\n throw new Error(\n `[review-mark] 飞书 API 返回错误: ${response.msg || \"未知错误\"}`\n );\n }\n\n console.log(\"[review-mark] ✅ 飞书消息发送成功\");\n console.log(\n `[review-mark] 消息 ID: ${response.data?.message_id || \"未知\"}`\n );\n } catch (error: any) {\n console.error(`[review-mark] ❌ 飞书消息发送失败: ${error.message}`);\n throw error;\n }\n}\n\n/**\n * 将 Markdown 格式转换为飞书 post 格式\n * 飞书 post 格式支持富文本样式\n */\nfunction convertMarkdownToFeishuPost(markdown: string): any[][] {\n const lines = markdown.split(\"\\n\");\n const result: any[][] = [];\n\n let inCodeBlock = false;\n let codeBlockContent: string[] = [];\n\n for (const line of lines) {\n // 处理代码块\n if (line.startsWith(\"```\")) {\n if (inCodeBlock) {\n // 代码块结束\n if (codeBlockContent.length > 0) {\n result.push([\n {\n tag: \"text\",\n text: codeBlockContent.join(\"\\n\"),\n style: [\"code\"],\n },\n ]);\n codeBlockContent = [];\n }\n inCodeBlock = false;\n } else {\n // 代码块开始\n inCodeBlock = true;\n }\n continue;\n }\n\n if (inCodeBlock) {\n codeBlockContent.push(line);\n continue;\n }\n\n // 空行\n if (!line.trim()) {\n result.push([{ tag: \"text\", text: \"\" }]);\n continue;\n }\n\n // 标题(加粗 + 加大)\n if (line.startsWith(\"#\")) {\n const level = line.match(/^#+/)?.[0].length || 1;\n const text = line.replace(/^#+\\s*/, \"\");\n result.push([\n {\n tag: \"text\",\n text: text,\n style: level <= 2 ? [\"bold\", \"underline\"] : [\"bold\"],\n },\n ]);\n continue;\n }\n\n // 解析行内样式(加粗、链接等)\n const parsedLine = parseLineStyles(line);\n result.push(parsedLine);\n }\n\n // 如果还在代码块中,添加剩余内容\n if (codeBlockContent.length > 0) {\n result.push([\n {\n tag: \"text\",\n text: codeBlockContent.join(\"\\n\"),\n style: [\"code\"],\n },\n ]);\n }\n\n return result;\n}\n\n/**\n * 解析行内样式(加粗、链接、代码等)\n */\nfunction parseLineStyles(line: string): any[] {\n const elements: any[] = [];\n let currentText = \"\";\n let i = 0;\n\n while (i < line.length) {\n // 处理加粗 **text**\n if (line[i] === \"*\" && line[i + 1] === \"*\") {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const endIndex = line.indexOf(\"**\", i + 2);\n if (endIndex !== -1) {\n const boldText = line.substring(i + 2, endIndex);\n elements.push({ tag: \"text\", text: boldText, style: [\"bold\"] });\n i = endIndex + 2;\n continue;\n }\n }\n\n // 处理行内代码 `code`\n if (line[i] === \"`\" && line[i + 1] !== \"`\") {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const endIndex = line.indexOf(\"`\", i + 1);\n if (endIndex !== -1) {\n const codeText = line.substring(i + 1, endIndex);\n elements.push({\n tag: \"text\",\n text: codeText,\n style: [\"code\"],\n });\n i = endIndex + 1;\n continue;\n }\n }\n\n // 处理链接 [text](url)\n if (line[i] === \"[\") {\n const textEnd = line.indexOf(\"](\", i);\n const urlEnd = line.indexOf(\")\", textEnd + 2);\n if (textEnd !== -1 && urlEnd !== -1) {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const linkText = line.substring(i + 1, textEnd);\n const url = line.substring(textEnd + 2, urlEnd);\n elements.push({\n tag: \"a\",\n text: linkText,\n href: url,\n });\n i = urlEnd + 1;\n continue;\n }\n }\n\n currentText += line[i];\n i++;\n }\n\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n }\n\n return elements.length > 0 ? elements : [{ tag: \"text\", text: line }];\n}\n","// 默认使用的 AI 模型\nexport const DEFAULT_MODEL = \"composer-1.5\";\n\n// 飞书机器人配置(内置,不需要外部配置)\nexport const appId = \"cli_a93822da7238dbb5\";\nexport const appSecret = \"ZQdcpLUHFb4gFa8cGfrlJfVfSSyGtyzF\";\nexport const receiveId = \"oc_482b6a04f95f4206c4fa9bc61829fd17\";\nexport const receiveIdType = \"chat_id\";\nexport const messageType = \"post\"; // text | post | interactive\nexport const messageTitle = \"🔍 Code Review 结果\";\n"],"mappings":"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","names":["import_node_child_process","import_node_fs","import_node_path","import_node_child_process","import_node_util"]}
|
package/dist/cli/review.js
CHANGED
|
@@ -208,7 +208,7 @@ ${diff}
|
|
|
208
208
|
import * as lark from "@larksuiteoapi/node-sdk";
|
|
209
209
|
|
|
210
210
|
// src/constants.ts
|
|
211
|
-
var DEFAULT_MODEL = "composer-1";
|
|
211
|
+
var DEFAULT_MODEL = "composer-1.5";
|
|
212
212
|
var appId = "cli_a93822da7238dbb5";
|
|
213
213
|
var appSecret = "ZQdcpLUHFb4gFa8cGfrlJfVfSSyGtyzF";
|
|
214
214
|
var receiveId = "oc_482b6a04f95f4206c4fa9bc61829fd17";
|
package/dist/cli/review.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/review.ts","../../src/core/BeLinkReview.ts","../../src/utils/checkCli.ts","../../src/core/git.ts","../../src/core/prompt.ts","../../src/core/feishu.ts","../../src/constants.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { BeLinkReview } from \"../core/BeLinkReview\";\n\nconst program = new Command();\n\nconst sharedOptions = (cmd: Command) =>\n cmd\n .option(\n \"--apiKey <key>\",\n \"Cursor API Key (overrides CURSOR_API_KEY environment variable)\"\n )\n .option(\n \"--agentPath <path>\",\n \"Path to the Cursor CLI agent executable (overrides CURSOR_AGENT_PATH environment variable)\"\n )\n .option(\n \"--model <model>\",\n \"AI model to use, e.g. claude-3-5-sonnet (overrides CURSOR_MODEL environment variable)\"\n )\n .option(\n \"--ignore <patterns>\",\n \"Comma-separated list of glob patterns to ignore (e.g., *.lock,dist/**)\"\n )\n .option(\"--no-feishu\", \"Disable Feishu notification\");\n\nconst runAction = async (options: any) => {\n try {\n const ignorePatterns = options.ignore\n ? options.ignore.split(\",\")\n : undefined;\n\n const instance = BeLinkReview.getInstance(\n options.apiKey,\n options.agentPath,\n ignorePatterns,\n options.feishu !== false,\n options.model\n );\n await instance.goCli();\n } catch (error: any) {\n console.error(`[review-mark] Error: ${error.message}`);\n process.exit(1);\n }\n};\n\nsharedOptions(\n program\n .name(\"review-mark\")\n .description(\"AI-powered code review tool\")\n .version(\"1.0.0\")\n).action(runAction);\n\nsharedOptions(\n program.command(\"review\").description(\"Perform an AI code review on git diff\")\n).action(runAction);\n\nprogram.parse(process.argv);\n","import { spawn } from \"node:child_process\";\nimport { readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type {\n BeLinkReviewOptions,\n BeLinkReviewChatOptions,\n BeLinkReviewEnsureResult,\n} from \"../types\";\nimport { isCheckCliInstall } from \"../utils/checkCli\";\nimport { getGitDiff } from \"./git\";\nimport { generateAIPrompt } from \"./prompt\";\nimport { sendReviewToFeishu } from \"./feishu\";\nimport { DEFAULT_MODEL } from \"../constants\";\n\nexport class BeLinkReview {\n static #instance: BeLinkReview | null = null;\n #apiKey: string | undefined;\n #agentPath: string | undefined;\n #ignorePatterns: string[] | undefined;\n #enableFeishu: boolean;\n #model: string | undefined;\n\n private constructor(\n apiKey?: string,\n agentPath?: string,\n ignorePatterns?: string[],\n enableFeishu: boolean = true,\n model?: string\n ) {\n this.#apiKey = apiKey;\n this.#agentPath = agentPath;\n this.#ignorePatterns = ignorePatterns;\n this.#enableFeishu = enableFeishu;\n this.#model = model;\n }\n\n /**\n * 初始化单例并注入参数,在项目入口调用一次即可。\n * 负责保存 apiKey 和自动写入 package.json 脚本。\n * 此方法主要用于在用户项目中设置 apiKey 和脚本,CLI 运行时会优先从环境变量或命令行参数获取。\n */\n static init(options: BeLinkReviewOptions = {}): BeLinkReview {\n if (BeLinkReview.#instance === null) {\n BeLinkReview.#instance = new BeLinkReview(\n options.apiKey,\n options.agentPath,\n options.ignore,\n options.enableFeishu ?? true,\n options.model\n );\n } else {\n BeLinkReview.#instance.#apiKey = options.apiKey;\n BeLinkReview.#instance.#agentPath = options.agentPath;\n BeLinkReview.#instance.#ignorePatterns = options.ignore;\n BeLinkReview.#instance.#enableFeishu = options.enableFeishu ?? true;\n BeLinkReview.#instance.#model = options.model;\n }\n BeLinkReview.#instance.#setupProjectScript();\n return BeLinkReview.#instance;\n }\n\n /**\n * 获取单例实例。如果未通过 init() 初始化,则尝试从环境变量 CURSOR_API_KEY 获取。\n * @param cliApiKey 可选的命令行传入的 apiKey\n * @param cliAgentPath 可选的命令行传入的 agentPath\n * @param cliIgnorePatterns 可选的命令行传入的 ignorePatterns\n * @param cliEnableFeishu 可选的命令行传入的飞书开关\n */\n static getInstance(\n cliApiKey?: string,\n cliAgentPath?: string,\n cliIgnorePatterns?: string[],\n cliEnableFeishu?: boolean,\n cliModel?: string\n ): BeLinkReview {\n if (BeLinkReview.#instance === null) {\n const apiKey = cliApiKey || process.env.CURSOR_API_KEY;\n const agentPath = cliAgentPath || process.env.CURSOR_AGENT_PATH;\n const ignorePatterns =\n cliIgnorePatterns ||\n (process.env.BE_LINK_REVIEW_IGNORE\n ? process.env.BE_LINK_REVIEW_IGNORE.split(\",\")\n : undefined);\n const enableFeishu =\n cliEnableFeishu ?? process.env.FEISHU_ENABLED !== \"false\";\n const model = cliModel || process.env.CURSOR_MODEL;\n\n if (!apiKey) {\n throw new Error(\n '[review-mark] 请先调用 BeLinkReview.init({ apiKey: \"...\" }) 初始化,或设置环境变量 CURSOR_API_KEY,或通过命令行参数 --apiKey 传入。'\n );\n }\n BeLinkReview.#instance = new BeLinkReview(\n apiKey,\n agentPath,\n ignorePatterns,\n enableFeishu,\n model\n );\n } else {\n if (cliApiKey && !BeLinkReview.#instance.#apiKey) {\n BeLinkReview.#instance.#apiKey = cliApiKey;\n }\n if (cliAgentPath && !BeLinkReview.#instance.#agentPath) {\n BeLinkReview.#instance.#agentPath = cliAgentPath;\n }\n if (cliIgnorePatterns && !BeLinkReview.#instance.#ignorePatterns) {\n BeLinkReview.#instance.#ignorePatterns = cliIgnorePatterns;\n }\n if (cliEnableFeishu !== undefined) {\n BeLinkReview.#instance.#enableFeishu = cliEnableFeishu;\n }\n if (cliModel) {\n BeLinkReview.#instance.#model = cliModel;\n }\n }\n return BeLinkReview.#instance;\n }\n\n #getApiKey(): string {\n const apiKey = this.#apiKey || process.env.CURSOR_API_KEY;\n if (!apiKey) {\n throw new Error(\n '[review-mark] 请先在 init({ apiKey: \"...\" }) 中传入 apiKey,或设置环境变量 CURSOR_API_KEY,或通过命令行参数 --apiKey 传入。'\n );\n }\n return apiKey;\n }\n\n #getAgentPath(): string | undefined {\n return this.#agentPath || process.env.CURSOR_AGENT_PATH;\n }\n\n #getIgnorePatterns(): string[] {\n const envIgnore = process.env.BE_LINK_REVIEW_IGNORE\n ? process.env.BE_LINK_REVIEW_IGNORE.split(\",\")\n : [];\n return this.#ignorePatterns || envIgnore;\n }\n\n #isFeishuEnabled(): boolean {\n return this.#enableFeishu;\n }\n\n #getModel(): string {\n return this.#model || process.env.CURSOR_MODEL || DEFAULT_MODEL;\n }\n\n /**\n * 自动往用户项目 package.json 写入 script\n */\n async #setupProjectScript() {\n try {\n const packageJsonPath = join(process.cwd(), \"package.json\");\n const packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf-8\"));\n\n if (!packageJson.scripts) {\n packageJson.scripts = {};\n }\n\n if (!packageJson.scripts[\"review-mark\"]) {\n packageJson.scripts[\"review-mark\"] = \"review-mark\";\n writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));\n console.log(\n \"[review-mark] 已在 package.json 中添加 'review-mark' 脚本。\"\n );\n } else {\n console.log(\"[review-mark] 'review-mark' 脚本已存在,跳过添加。\");\n }\n } catch (error) {\n console.error(\"[review-mark] 无法更新 package.json: \", error);\n }\n }\n\n /**\n * 执行一次完整 CLI 流程:获取 git diff → 生成 prompt → 检查/安装 CLI → 与 Cursor 对话并打印结果。\n */\n async goCli(): Promise<string> {\n const apiKey = this.#getApiKey();\n const agentPath = this.#getAgentPath();\n const ignorePatterns = this.#getIgnorePatterns();\n\n const ensureResult = await this.ensureAgentInstalled(false, agentPath);\n\n if (!ensureResult.isInstalled) {\n throw new Error(\n \"[review-mark] Cursor CLI 未安装且自动安装失败,请手动安装。\"\n );\n }\n\n console.log(\"[review-mark] Getting git diff...\");\n const diff = await getGitDiff(ignorePatterns, process.cwd());\n\n if (!diff) {\n console.log(\"[review-mark] No code changes detected\");\n return \"No code changes detected\";\n }\n\n const prompt = generateAIPrompt(diff);\n console.log(\"[review-mark] Sending to AI...\");\n const response = await this.chat(prompt, {\n agentPath: ensureResult.actualAgentPath,\n force: true,\n model: this.#getModel(),\n });\n console.log(this.#getModel(), \"model\");\n console.log(\"===== AI Review =====\");\n console.log(response);\n\n // 如果启用了飞书,发送到飞书\n if (this.#isFeishuEnabled()) {\n try {\n await sendReviewToFeishu(response);\n } catch (error: any) {\n console.error(\n `[review-mark] 飞书通知发送失败,但不影响 review 结果: ${error.message}`\n );\n }\n }\n\n return response;\n }\n\n /**\n * 检查 Cursor CLI 是否已安装;未安装则自动安装(需网络)。\n * @param silent 为 true 时不打印「已安装」等提示\n * @param agentPath 可选的 agent 可执行文件路径\n */\n async ensureAgentInstalled(\n silent = false,\n agentPath?: string\n ): Promise<BeLinkReviewEnsureResult> {\n return isCheckCliInstall({ apiKey: this.#getApiKey(), silent, agentPath });\n }\n\n /**\n * 通过 Cursor Headless CLI 与 Cursor 对话(发送 prompt,拿到回复)。\n * 默认会先执行 ensureAgentInstalled(检查/安装 CLI),再启动 agent 执行提问。\n * 参考:https://cursor.com/cn/docs/cli/headless\n */\n async chat(\n prompt: string,\n options: BeLinkReviewChatOptions = {}\n ): Promise<string> {\n const actualAgentPath =\n options.agentPath || this.#getAgentPath() || \"agent\";\n const model = options.model || this.#getModel();\n const args: string[] = [\"--yolo\", \"-p\", prompt];\n if (model) args.unshift(\"--model\", model);\n if (options.outputFormat === \"json\") args.push(\"--output-format\", \"json\");\n\n return new Promise((resolve, reject) => {\n const env = { ...process.env, CURSOR_API_KEY: this.#getApiKey() };\n const proc = spawn(actualAgentPath, args, {\n env,\n cwd: process.cwd(),\n shell: false,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n proc.stdout?.on(\"data\", (chunk) => {\n stdout += chunk.toString();\n });\n proc.stderr?.on(\"data\", (chunk) => {\n stderr += chunk.toString();\n });\n\n proc.on(\"error\", (err) => {\n reject(\n new Error(\n `[review-mark] 无法启动 Cursor CLI (${actualAgentPath}),请先安装:curl https://cursor.com/install -fsS | bash。原始错误: ${err.message}`\n )\n );\n });\n\n proc.on(\"close\", (code) => {\n if (code !== 0) {\n reject(\n new Error(\n `[review-mark] agent 退出码 ${code}${\n stderr ? `: ${stderr.trim()}` : \"\"\n }`\n )\n );\n return;\n }\n resolve(stdout.trim());\n });\n });\n }\n}\n","import { spawn, exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { existsSync, appendFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { BeLinkReviewEnsureResult } from \"@/types\";\n\nconst execAsync = promisify(exec);\n\ninterface CheckCliInstallOptions {\n apiKey: string;\n silent?: boolean;\n agentPath?: string;\n}\n\n// Cursor 官方安装脚本默认将 agent 放在 ~/.local/bin\nconst LOCAL_BIN = join(homedir(), \".local\", \"bin\");\n\n// 常见的 Cursor CLI 安装路径\nconst COMMON_AGENT_PATHS = [\n join(LOCAL_BIN, \"agent\"), // Cursor 官方安装脚本默认路径\n join(homedir(), \".cursor\", \"agent\"),\n \"/usr/local/bin/agent\",\n \"/opt/homebrew/bin/agent\",\n];\n\n/**\n * 将 ~/.local/bin 写入用户 shell 配置文件(.zshrc / .bashrc),\n * 并立即注入当前进程的 PATH,确保安装后无需重启终端即可使用。\n */\nfunction setupLocalBinPath(silent: boolean): void {\n // 立即让当前进程能找到 agent\n if (!process.env.PATH?.includes(LOCAL_BIN)) {\n process.env.PATH = `${LOCAL_BIN}:${process.env.PATH}`;\n }\n\n const exportLine = `\\nexport PATH=\"$HOME/.local/bin:$PATH\"`;\n const shell = process.env.SHELL ?? \"\";\n const rcFiles: string[] = [];\n\n if (shell.includes(\"zsh\")) {\n rcFiles.push(join(homedir(), \".zshrc\"));\n } else if (shell.includes(\"bash\")) {\n rcFiles.push(join(homedir(), \".bashrc\"));\n } else {\n // 两个都写,保证覆盖\n rcFiles.push(join(homedir(), \".zshrc\"), join(homedir(), \".bashrc\"));\n }\n\n for (const rc of rcFiles) {\n try {\n const content = existsSync(rc)\n ? require(\"node:fs\").readFileSync(rc, \"utf-8\")\n : \"\";\n if (!content.includes(\".local/bin\")) {\n appendFileSync(rc, exportLine);\n if (!silent) {\n console.log(`[review-mark] 已将 ~/.local/bin 写入 ${rc}`);\n }\n }\n } catch {\n // 写入失败不阻断流程\n }\n }\n}\n\nasync function findAgentExecutable(\n userAgentPath?: string\n): Promise<string | null> {\n // 1. 优先使用用户指定的路径\n if (userAgentPath && existsSync(userAgentPath)) {\n return userAgentPath;\n }\n\n // 2. 尝试通过 which 命令在 PATH 中查找\n try {\n const { stdout } = await execAsync(\"which agent\");\n const pathFromWhich = stdout.trim();\n if (pathFromWhich && existsSync(pathFromWhich)) {\n return pathFromWhich;\n }\n } catch (error) {\n // ignore error, continue to next method\n }\n\n // 3. 检查常见安装路径\n for (const path of COMMON_AGENT_PATHS) {\n if (existsSync(path)) {\n return path;\n }\n }\n\n return null;\n}\n\nexport async function isCheckCliInstall(\n options: CheckCliInstallOptions\n): Promise<BeLinkReviewEnsureResult> {\n const { silent = false, agentPath: userAgentPath } = options;\n\n let actualAgentPath = await findAgentExecutable(userAgentPath);\n\n if (actualAgentPath) {\n if (!silent) {\n console.log(\n `[review-mark] Cursor CLI (agent) 已在 ${actualAgentPath} 找到。`\n );\n }\n return { isInstalled: true, message: \"Cursor CLI 已安装\", actualAgentPath };\n }\n\n if (!silent) {\n console.log(\"[review-mark] Cursor CLI (agent) 未找到,正在尝试安装...\");\n console.log(\n \"[review-mark] 执行安装命令: curl https://cursor.com/install -fsS | bash\"\n );\n }\n\n return new Promise((resolve, reject) => {\n const installProcess = spawn(\n \"bash\",\n [\"-c\", \"curl https://cursor.com/install -fsS | bash\"],\n {\n stdio: \"inherit\", // 将安装过程的输出直接显示给用户\n }\n );\n\n installProcess.on(\"close\", async (code) => {\n if (code === 0) {\n // 安装完成后自动配置 PATH(写入 rc 文件 + 注入当前进程)\n setupLocalBinPath(silent);\n\n // 再次检查 agent 命令是否可用\n actualAgentPath = await findAgentExecutable(userAgentPath);\n if (actualAgentPath) {\n if (!silent) {\n console.log(\"[review-mark] Cursor CLI 安装成功。\");\n }\n resolve({\n isInstalled: true,\n message: \"Cursor CLI 安装成功\",\n actualAgentPath,\n });\n } else {\n reject(\n new Error(\n `[review-mark] Cursor CLI 安装命令执行成功,但未找到 agent 可执行文件。请手动检查安装:curl https://cursor.com/install -fsS | bash。`\n )\n );\n }\n } else {\n reject(\n new Error(\n `[review-mark] Cursor CLI 安装失败,退出码 ${code}。请手动安装:curl https://cursor.com/install -fsS | bash。`\n )\n );\n }\n });\n\n installProcess.on(\"error\", (err) => {\n reject(\n new Error(\n `[review-mark] 无法启动安装进程:${err.message}。请手动安装:curl https://cursor.com/install -fsS | bash`\n )\n );\n });\n });\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\n// 固定的忽略文件模式,这些文件将永远不会被审查\nconst FIXED_IGNORE_PATTERNS = [\"package.json\"];\n\nasync function runGit(args: string[], cwd: string): Promise<string> {\n const { stdout } = await execFileAsync(\"git\", args, { cwd });\n return stdout.trim();\n}\n\nexport async function getGitDiff(\n userIgnorePatterns: string[] = [],\n cwd: string = process.cwd()\n): Promise<string> {\n // 检查是否在 git 仓库中\n try {\n await runGit([\"rev-parse\", \"--git-dir\"], cwd);\n } catch (error) {\n console.error(`[review-mark] 当前目录不是 git 仓库: ${cwd}`);\n return \"\";\n }\n\n // 合并固定忽略模式和用户提供的忽略模式\n const allIgnorePatterns = [\n ...new Set([...FIXED_IGNORE_PATTERNS, ...userIgnorePatterns]),\n ];\n // 使用 execFile 直接传参,完全绕过 shell,:(exclude) 不会被解析\n // git 参数顺序:git diff [options] [commit] -- [pathspec]\n // options/commit 必须在 -- 和 pathspec 之前\n const excludeArgs = allIgnorePatterns.map((p) => `:(exclude)${p}`);\n\n let diff = \"\";\n\n try {\n // 优先使用 git diff --cached 获取暂存区的改动\n diff = await runGit(\n [\"diff\", \"--no-color\", \"--relative\", \"--cached\", \"--\", ...excludeArgs],\n cwd\n );\n if (diff) {\n console.log(\"[review-mark] 检测到暂存区改动\");\n }\n } catch (error: any) {\n console.warn(`[review-mark] 获取暂存区 diff 失败: ${error.message}`);\n }\n\n if (!diff) {\n try {\n // 如果暂存区没有改动,则获取工作区和 HEAD 的改动\n diff = await runGit(\n [\"diff\", \"--no-color\", \"--relative\", \"HEAD\", \"--\", ...excludeArgs],\n cwd\n );\n if (diff) {\n console.log(\"[review-mark] 检测到工作区改动(相对于 HEAD)\");\n }\n } catch (error: any) {\n console.warn(`[review-mark] 获取工作区 diff 失败: ${error.message}`);\n }\n }\n\n if (!diff) {\n console.log(\"[review-mark] 未检测到代码改动(已检查暂存区和工作区)\");\n }\n\n return diff;\n}\n","\nexport function generateAIPrompt(diff: string): string {\n return `你是一名资深软件工程师,请 review 以下代码变更。\nGit diff:\n${diff}\n请分析:\n是否存在 bug\n是否存在潜在逻辑问题\n是否存在性能问题\n是否存在代码风格问题\n给出优化建议`;\n}\n","import * as lark from \"@larksuiteoapi/node-sdk\";\nimport {\n appId,\n appSecret,\n receiveId,\n receiveIdType,\n messageType,\n messageTitle,\n} from \"../constants\";\n\n/**\n * 发送 Code Review 结果到飞书\n * 使用飞书官方 Node.js SDK,所有配置从 constants.ts 读取\n */\nexport async function sendReviewToFeishu(reviewContent: string): Promise<void> {\n console.log(\"[review-mark] 正在发送消息到飞书...\");\n console.log(`[review-mark] 消息类型: ${messageType}`);\n\n try {\n // 初始化飞书客户端\n const client = new lark.Client({\n appId,\n appSecret,\n domain: lark.Domain.Feishu,\n });\n\n // 根据消息类型构造消息内容\n let msgContent: string;\n let msgType: string;\n\n if (messageType === \"interactive\") {\n // Interactive 卡片消息(支持 Markdown 渲染)\n msgContent = JSON.stringify({\n config: {\n wide_screen_mode: true,\n },\n header: {\n title: {\n tag: \"plain_text\",\n content: messageTitle,\n },\n template: \"blue\",\n },\n elements: [\n {\n tag: \"div\",\n text: {\n tag: \"lark_md\",\n content: reviewContent,\n },\n },\n {\n tag: \"hr\",\n },\n {\n tag: \"note\",\n elements: [\n {\n tag: \"plain_text\",\n content: `生成时间: ${new Date().toLocaleString(\"zh-CN\", {\n timeZone: \"Asia/Shanghai\",\n })}`,\n },\n ],\n },\n ],\n });\n msgType = \"interactive\";\n } else if (messageType === \"post\") {\n // Post 富文本消息\n msgContent = JSON.stringify({\n zh_cn: {\n title: messageTitle,\n content: convertMarkdownToFeishuPost(reviewContent),\n },\n });\n msgType = \"post\";\n } else if (messageType === \"text\") {\n // Text 纯文本消息\n msgContent = JSON.stringify({\n text: `${messageTitle}\\n\\n${reviewContent}`,\n });\n msgType = \"text\";\n } else {\n throw new Error(`[review-mark] 不支持的消息类型: ${messageType}`);\n }\n\n // 调试日志\n console.log(`[review-mark] 发送参数:`);\n console.log(` - receive_id_type: ${receiveIdType}`);\n console.log(` - receive_id: ${receiveId}`);\n console.log(` - msg_type: ${msgType}`);\n\n // 调用发送消息接口\n const response = await client.im.message.create({\n params: {\n receive_id_type: receiveIdType,\n },\n data: {\n receive_id: receiveId,\n msg_type: msgType,\n content: msgContent,\n },\n });\n\n if (response.code !== 0) {\n throw new Error(\n `[review-mark] 飞书 API 返回错误: ${response.msg || \"未知错误\"}`\n );\n }\n\n console.log(\"[review-mark] ✅ 飞书消息发送成功\");\n console.log(\n `[review-mark] 消息 ID: ${response.data?.message_id || \"未知\"}`\n );\n } catch (error: any) {\n console.error(`[review-mark] ❌ 飞书消息发送失败: ${error.message}`);\n throw error;\n }\n}\n\n/**\n * 将 Markdown 格式转换为飞书 post 格式\n * 飞书 post 格式支持富文本样式\n */\nfunction convertMarkdownToFeishuPost(markdown: string): any[][] {\n const lines = markdown.split(\"\\n\");\n const result: any[][] = [];\n\n let inCodeBlock = false;\n let codeBlockContent: string[] = [];\n\n for (const line of lines) {\n // 处理代码块\n if (line.startsWith(\"```\")) {\n if (inCodeBlock) {\n // 代码块结束\n if (codeBlockContent.length > 0) {\n result.push([\n {\n tag: \"text\",\n text: codeBlockContent.join(\"\\n\"),\n style: [\"code\"],\n },\n ]);\n codeBlockContent = [];\n }\n inCodeBlock = false;\n } else {\n // 代码块开始\n inCodeBlock = true;\n }\n continue;\n }\n\n if (inCodeBlock) {\n codeBlockContent.push(line);\n continue;\n }\n\n // 空行\n if (!line.trim()) {\n result.push([{ tag: \"text\", text: \"\" }]);\n continue;\n }\n\n // 标题(加粗 + 加大)\n if (line.startsWith(\"#\")) {\n const level = line.match(/^#+/)?.[0].length || 1;\n const text = line.replace(/^#+\\s*/, \"\");\n result.push([\n {\n tag: \"text\",\n text: text,\n style: level <= 2 ? [\"bold\", \"underline\"] : [\"bold\"],\n },\n ]);\n continue;\n }\n\n // 解析行内样式(加粗、链接等)\n const parsedLine = parseLineStyles(line);\n result.push(parsedLine);\n }\n\n // 如果还在代码块中,添加剩余内容\n if (codeBlockContent.length > 0) {\n result.push([\n {\n tag: \"text\",\n text: codeBlockContent.join(\"\\n\"),\n style: [\"code\"],\n },\n ]);\n }\n\n return result;\n}\n\n/**\n * 解析行内样式(加粗、链接、代码等)\n */\nfunction parseLineStyles(line: string): any[] {\n const elements: any[] = [];\n let currentText = \"\";\n let i = 0;\n\n while (i < line.length) {\n // 处理加粗 **text**\n if (line[i] === \"*\" && line[i + 1] === \"*\") {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const endIndex = line.indexOf(\"**\", i + 2);\n if (endIndex !== -1) {\n const boldText = line.substring(i + 2, endIndex);\n elements.push({ tag: \"text\", text: boldText, style: [\"bold\"] });\n i = endIndex + 2;\n continue;\n }\n }\n\n // 处理行内代码 `code`\n if (line[i] === \"`\" && line[i + 1] !== \"`\") {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const endIndex = line.indexOf(\"`\", i + 1);\n if (endIndex !== -1) {\n const codeText = line.substring(i + 1, endIndex);\n elements.push({\n tag: \"text\",\n text: codeText,\n style: [\"code\"],\n });\n i = endIndex + 1;\n continue;\n }\n }\n\n // 处理链接 [text](url)\n if (line[i] === \"[\") {\n const textEnd = line.indexOf(\"](\", i);\n const urlEnd = line.indexOf(\")\", textEnd + 2);\n if (textEnd !== -1 && urlEnd !== -1) {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const linkText = line.substring(i + 1, textEnd);\n const url = line.substring(textEnd + 2, urlEnd);\n elements.push({\n tag: \"a\",\n text: linkText,\n href: url,\n });\n i = urlEnd + 1;\n continue;\n }\n }\n\n currentText += line[i];\n i++;\n }\n\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n }\n\n return elements.length > 0 ? elements : [{ tag: \"text\", text: line }];\n}\n","// 默认使用的 AI 模型\nexport const DEFAULT_MODEL = \"composer-1\";\n\n// 飞书机器人配置(内置,不需要外部配置)\nexport const appId = \"cli_a93822da7238dbb5\";\nexport const appSecret = \"ZQdcpLUHFb4gFa8cGfrlJfVfSSyGtyzF\";\nexport const receiveId = \"oc_482b6a04f95f4206c4fa9bc61829fd17\";\nexport const receiveIdType = \"chat_id\";\nexport const messageType = \"post\"; // text | post | interactive\nexport const messageTitle = \"🔍 Code Review 结果\";\n"],"mappings":"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","names":["spawn","join","promisify","join","spawn"]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/review.ts","../../src/core/BeLinkReview.ts","../../src/utils/checkCli.ts","../../src/core/git.ts","../../src/core/prompt.ts","../../src/core/feishu.ts","../../src/constants.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { BeLinkReview } from \"../core/BeLinkReview\";\n\nconst program = new Command();\n\nconst sharedOptions = (cmd: Command) =>\n cmd\n .option(\n \"--apiKey <key>\",\n \"Cursor API Key (overrides CURSOR_API_KEY environment variable)\"\n )\n .option(\n \"--agentPath <path>\",\n \"Path to the Cursor CLI agent executable (overrides CURSOR_AGENT_PATH environment variable)\"\n )\n .option(\n \"--model <model>\",\n \"AI model to use, e.g. claude-3-5-sonnet (overrides CURSOR_MODEL environment variable)\"\n )\n .option(\n \"--ignore <patterns>\",\n \"Comma-separated list of glob patterns to ignore (e.g., *.lock,dist/**)\"\n )\n .option(\"--no-feishu\", \"Disable Feishu notification\");\n\nconst runAction = async (options: any) => {\n try {\n const ignorePatterns = options.ignore\n ? options.ignore.split(\",\")\n : undefined;\n\n const instance = BeLinkReview.getInstance(\n options.apiKey,\n options.agentPath,\n ignorePatterns,\n options.feishu !== false,\n options.model\n );\n await instance.goCli();\n } catch (error: any) {\n console.error(`[review-mark] Error: ${error.message}`);\n process.exit(1);\n }\n};\n\nsharedOptions(\n program\n .name(\"review-mark\")\n .description(\"AI-powered code review tool\")\n .version(\"1.0.0\")\n).action(runAction);\n\nsharedOptions(\n program.command(\"review\").description(\"Perform an AI code review on git diff\")\n).action(runAction);\n\nprogram.parse(process.argv);\n","import { spawn } from \"node:child_process\";\nimport { readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type {\n BeLinkReviewOptions,\n BeLinkReviewChatOptions,\n BeLinkReviewEnsureResult,\n} from \"../types\";\nimport { isCheckCliInstall } from \"../utils/checkCli\";\nimport { getGitDiff } from \"./git\";\nimport { generateAIPrompt } from \"./prompt\";\nimport { sendReviewToFeishu } from \"./feishu\";\nimport { DEFAULT_MODEL } from \"../constants\";\n\nexport class BeLinkReview {\n static #instance: BeLinkReview | null = null;\n #apiKey: string | undefined;\n #agentPath: string | undefined;\n #ignorePatterns: string[] | undefined;\n #enableFeishu: boolean;\n #model: string | undefined;\n\n private constructor(\n apiKey?: string,\n agentPath?: string,\n ignorePatterns?: string[],\n enableFeishu: boolean = true,\n model?: string\n ) {\n this.#apiKey = apiKey;\n this.#agentPath = agentPath;\n this.#ignorePatterns = ignorePatterns;\n this.#enableFeishu = enableFeishu;\n this.#model = model;\n }\n\n /**\n * 初始化单例并注入参数,在项目入口调用一次即可。\n * 负责保存 apiKey 和自动写入 package.json 脚本。\n * 此方法主要用于在用户项目中设置 apiKey 和脚本,CLI 运行时会优先从环境变量或命令行参数获取。\n */\n static init(options: BeLinkReviewOptions = {}): BeLinkReview {\n if (BeLinkReview.#instance === null) {\n BeLinkReview.#instance = new BeLinkReview(\n options.apiKey,\n options.agentPath,\n options.ignore,\n options.enableFeishu ?? true,\n options.model\n );\n } else {\n BeLinkReview.#instance.#apiKey = options.apiKey;\n BeLinkReview.#instance.#agentPath = options.agentPath;\n BeLinkReview.#instance.#ignorePatterns = options.ignore;\n BeLinkReview.#instance.#enableFeishu = options.enableFeishu ?? true;\n BeLinkReview.#instance.#model = options.model;\n }\n BeLinkReview.#instance.#setupProjectScript();\n return BeLinkReview.#instance;\n }\n\n /**\n * 获取单例实例。如果未通过 init() 初始化,则尝试从环境变量 CURSOR_API_KEY 获取。\n * @param cliApiKey 可选的命令行传入的 apiKey\n * @param cliAgentPath 可选的命令行传入的 agentPath\n * @param cliIgnorePatterns 可选的命令行传入的 ignorePatterns\n * @param cliEnableFeishu 可选的命令行传入的飞书开关\n */\n static getInstance(\n cliApiKey?: string,\n cliAgentPath?: string,\n cliIgnorePatterns?: string[],\n cliEnableFeishu?: boolean,\n cliModel?: string\n ): BeLinkReview {\n if (BeLinkReview.#instance === null) {\n const apiKey = cliApiKey || process.env.CURSOR_API_KEY;\n const agentPath = cliAgentPath || process.env.CURSOR_AGENT_PATH;\n const ignorePatterns =\n cliIgnorePatterns ||\n (process.env.BE_LINK_REVIEW_IGNORE\n ? process.env.BE_LINK_REVIEW_IGNORE.split(\",\")\n : undefined);\n const enableFeishu =\n cliEnableFeishu ?? process.env.FEISHU_ENABLED !== \"false\";\n const model = cliModel || process.env.CURSOR_MODEL;\n\n if (!apiKey) {\n throw new Error(\n '[review-mark] 请先调用 BeLinkReview.init({ apiKey: \"...\" }) 初始化,或设置环境变量 CURSOR_API_KEY,或通过命令行参数 --apiKey 传入。'\n );\n }\n BeLinkReview.#instance = new BeLinkReview(\n apiKey,\n agentPath,\n ignorePatterns,\n enableFeishu,\n model\n );\n } else {\n if (cliApiKey && !BeLinkReview.#instance.#apiKey) {\n BeLinkReview.#instance.#apiKey = cliApiKey;\n }\n if (cliAgentPath && !BeLinkReview.#instance.#agentPath) {\n BeLinkReview.#instance.#agentPath = cliAgentPath;\n }\n if (cliIgnorePatterns && !BeLinkReview.#instance.#ignorePatterns) {\n BeLinkReview.#instance.#ignorePatterns = cliIgnorePatterns;\n }\n if (cliEnableFeishu !== undefined) {\n BeLinkReview.#instance.#enableFeishu = cliEnableFeishu;\n }\n if (cliModel) {\n BeLinkReview.#instance.#model = cliModel;\n }\n }\n return BeLinkReview.#instance;\n }\n\n #getApiKey(): string {\n const apiKey = this.#apiKey || process.env.CURSOR_API_KEY;\n if (!apiKey) {\n throw new Error(\n '[review-mark] 请先在 init({ apiKey: \"...\" }) 中传入 apiKey,或设置环境变量 CURSOR_API_KEY,或通过命令行参数 --apiKey 传入。'\n );\n }\n return apiKey;\n }\n\n #getAgentPath(): string | undefined {\n return this.#agentPath || process.env.CURSOR_AGENT_PATH;\n }\n\n #getIgnorePatterns(): string[] {\n const envIgnore = process.env.BE_LINK_REVIEW_IGNORE\n ? process.env.BE_LINK_REVIEW_IGNORE.split(\",\")\n : [];\n return this.#ignorePatterns || envIgnore;\n }\n\n #isFeishuEnabled(): boolean {\n return this.#enableFeishu;\n }\n\n #getModel(): string {\n return this.#model || process.env.CURSOR_MODEL || DEFAULT_MODEL;\n }\n\n /**\n * 自动往用户项目 package.json 写入 script\n */\n async #setupProjectScript() {\n try {\n const packageJsonPath = join(process.cwd(), \"package.json\");\n const packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf-8\"));\n\n if (!packageJson.scripts) {\n packageJson.scripts = {};\n }\n\n if (!packageJson.scripts[\"review-mark\"]) {\n packageJson.scripts[\"review-mark\"] = \"review-mark\";\n writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));\n console.log(\n \"[review-mark] 已在 package.json 中添加 'review-mark' 脚本。\"\n );\n } else {\n console.log(\"[review-mark] 'review-mark' 脚本已存在,跳过添加。\");\n }\n } catch (error) {\n console.error(\"[review-mark] 无法更新 package.json: \", error);\n }\n }\n\n /**\n * 执行一次完整 CLI 流程:获取 git diff → 生成 prompt → 检查/安装 CLI → 与 Cursor 对话并打印结果。\n */\n async goCli(): Promise<string> {\n const apiKey = this.#getApiKey();\n const agentPath = this.#getAgentPath();\n const ignorePatterns = this.#getIgnorePatterns();\n\n const ensureResult = await this.ensureAgentInstalled(false, agentPath);\n\n if (!ensureResult.isInstalled) {\n throw new Error(\n \"[review-mark] Cursor CLI 未安装且自动安装失败,请手动安装。\"\n );\n }\n\n console.log(\"[review-mark] Getting git diff...\");\n const diff = await getGitDiff(ignorePatterns, process.cwd());\n\n if (!diff) {\n console.log(\"[review-mark] No code changes detected\");\n return \"No code changes detected\";\n }\n\n const prompt = generateAIPrompt(diff);\n console.log(\"[review-mark] Sending to AI...\");\n const response = await this.chat(prompt, {\n agentPath: ensureResult.actualAgentPath,\n force: true,\n model: this.#getModel(),\n });\n console.log(this.#getModel(), \"model\");\n console.log(\"===== AI Review =====\");\n console.log(response);\n\n // 如果启用了飞书,发送到飞书\n if (this.#isFeishuEnabled()) {\n try {\n await sendReviewToFeishu(response);\n } catch (error: any) {\n console.error(\n `[review-mark] 飞书通知发送失败,但不影响 review 结果: ${error.message}`\n );\n }\n }\n\n return response;\n }\n\n /**\n * 检查 Cursor CLI 是否已安装;未安装则自动安装(需网络)。\n * @param silent 为 true 时不打印「已安装」等提示\n * @param agentPath 可选的 agent 可执行文件路径\n */\n async ensureAgentInstalled(\n silent = false,\n agentPath?: string\n ): Promise<BeLinkReviewEnsureResult> {\n return isCheckCliInstall({ apiKey: this.#getApiKey(), silent, agentPath });\n }\n\n /**\n * 通过 Cursor Headless CLI 与 Cursor 对话(发送 prompt,拿到回复)。\n * 默认会先执行 ensureAgentInstalled(检查/安装 CLI),再启动 agent 执行提问。\n * 参考:https://cursor.com/cn/docs/cli/headless\n */\n async chat(\n prompt: string,\n options: BeLinkReviewChatOptions = {}\n ): Promise<string> {\n const actualAgentPath =\n options.agentPath || this.#getAgentPath() || \"agent\";\n const model = options.model || this.#getModel();\n const args: string[] = [\"--yolo\", \"-p\", prompt];\n if (model) args.unshift(\"--model\", model);\n if (options.outputFormat === \"json\") args.push(\"--output-format\", \"json\");\n\n return new Promise((resolve, reject) => {\n const env = { ...process.env, CURSOR_API_KEY: this.#getApiKey() };\n const proc = spawn(actualAgentPath, args, {\n env,\n cwd: process.cwd(),\n shell: false,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n proc.stdout?.on(\"data\", (chunk) => {\n stdout += chunk.toString();\n });\n proc.stderr?.on(\"data\", (chunk) => {\n stderr += chunk.toString();\n });\n\n proc.on(\"error\", (err) => {\n reject(\n new Error(\n `[review-mark] 无法启动 Cursor CLI (${actualAgentPath}),请先安装:curl https://cursor.com/install -fsS | bash。原始错误: ${err.message}`\n )\n );\n });\n\n proc.on(\"close\", (code) => {\n if (code !== 0) {\n reject(\n new Error(\n `[review-mark] agent 退出码 ${code}${\n stderr ? `: ${stderr.trim()}` : \"\"\n }`\n )\n );\n return;\n }\n resolve(stdout.trim());\n });\n });\n }\n}\n","import { spawn, exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { existsSync, appendFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { BeLinkReviewEnsureResult } from \"@/types\";\n\nconst execAsync = promisify(exec);\n\ninterface CheckCliInstallOptions {\n apiKey: string;\n silent?: boolean;\n agentPath?: string;\n}\n\n// Cursor 官方安装脚本默认将 agent 放在 ~/.local/bin\nconst LOCAL_BIN = join(homedir(), \".local\", \"bin\");\n\n// 常见的 Cursor CLI 安装路径\nconst COMMON_AGENT_PATHS = [\n join(LOCAL_BIN, \"agent\"), // Cursor 官方安装脚本默认路径\n join(homedir(), \".cursor\", \"agent\"),\n \"/usr/local/bin/agent\",\n \"/opt/homebrew/bin/agent\",\n];\n\n/**\n * 将 ~/.local/bin 写入用户 shell 配置文件(.zshrc / .bashrc),\n * 并立即注入当前进程的 PATH,确保安装后无需重启终端即可使用。\n */\nfunction setupLocalBinPath(silent: boolean): void {\n // 立即让当前进程能找到 agent\n if (!process.env.PATH?.includes(LOCAL_BIN)) {\n process.env.PATH = `${LOCAL_BIN}:${process.env.PATH}`;\n }\n\n const exportLine = `\\nexport PATH=\"$HOME/.local/bin:$PATH\"`;\n const shell = process.env.SHELL ?? \"\";\n const rcFiles: string[] = [];\n\n if (shell.includes(\"zsh\")) {\n rcFiles.push(join(homedir(), \".zshrc\"));\n } else if (shell.includes(\"bash\")) {\n rcFiles.push(join(homedir(), \".bashrc\"));\n } else {\n // 两个都写,保证覆盖\n rcFiles.push(join(homedir(), \".zshrc\"), join(homedir(), \".bashrc\"));\n }\n\n for (const rc of rcFiles) {\n try {\n const content = existsSync(rc)\n ? require(\"node:fs\").readFileSync(rc, \"utf-8\")\n : \"\";\n if (!content.includes(\".local/bin\")) {\n appendFileSync(rc, exportLine);\n if (!silent) {\n console.log(`[review-mark] 已将 ~/.local/bin 写入 ${rc}`);\n }\n }\n } catch {\n // 写入失败不阻断流程\n }\n }\n}\n\nasync function findAgentExecutable(\n userAgentPath?: string\n): Promise<string | null> {\n // 1. 优先使用用户指定的路径\n if (userAgentPath && existsSync(userAgentPath)) {\n return userAgentPath;\n }\n\n // 2. 尝试通过 which 命令在 PATH 中查找\n try {\n const { stdout } = await execAsync(\"which agent\");\n const pathFromWhich = stdout.trim();\n if (pathFromWhich && existsSync(pathFromWhich)) {\n return pathFromWhich;\n }\n } catch (error) {\n // ignore error, continue to next method\n }\n\n // 3. 检查常见安装路径\n for (const path of COMMON_AGENT_PATHS) {\n if (existsSync(path)) {\n return path;\n }\n }\n\n return null;\n}\n\nexport async function isCheckCliInstall(\n options: CheckCliInstallOptions\n): Promise<BeLinkReviewEnsureResult> {\n const { silent = false, agentPath: userAgentPath } = options;\n\n let actualAgentPath = await findAgentExecutable(userAgentPath);\n\n if (actualAgentPath) {\n if (!silent) {\n console.log(\n `[review-mark] Cursor CLI (agent) 已在 ${actualAgentPath} 找到。`\n );\n }\n return { isInstalled: true, message: \"Cursor CLI 已安装\", actualAgentPath };\n }\n\n if (!silent) {\n console.log(\"[review-mark] Cursor CLI (agent) 未找到,正在尝试安装...\");\n console.log(\n \"[review-mark] 执行安装命令: curl https://cursor.com/install -fsS | bash\"\n );\n }\n\n return new Promise((resolve, reject) => {\n const installProcess = spawn(\n \"bash\",\n [\"-c\", \"curl https://cursor.com/install -fsS | bash\"],\n {\n stdio: \"inherit\", // 将安装过程的输出直接显示给用户\n }\n );\n\n installProcess.on(\"close\", async (code) => {\n if (code === 0) {\n // 安装完成后自动配置 PATH(写入 rc 文件 + 注入当前进程)\n setupLocalBinPath(silent);\n\n // 再次检查 agent 命令是否可用\n actualAgentPath = await findAgentExecutable(userAgentPath);\n if (actualAgentPath) {\n if (!silent) {\n console.log(\"[review-mark] Cursor CLI 安装成功。\");\n }\n resolve({\n isInstalled: true,\n message: \"Cursor CLI 安装成功\",\n actualAgentPath,\n });\n } else {\n reject(\n new Error(\n `[review-mark] Cursor CLI 安装命令执行成功,但未找到 agent 可执行文件。请手动检查安装:curl https://cursor.com/install -fsS | bash。`\n )\n );\n }\n } else {\n reject(\n new Error(\n `[review-mark] Cursor CLI 安装失败,退出码 ${code}。请手动安装:curl https://cursor.com/install -fsS | bash。`\n )\n );\n }\n });\n\n installProcess.on(\"error\", (err) => {\n reject(\n new Error(\n `[review-mark] 无法启动安装进程:${err.message}。请手动安装:curl https://cursor.com/install -fsS | bash`\n )\n );\n });\n });\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\n// 固定的忽略文件模式,这些文件将永远不会被审查\nconst FIXED_IGNORE_PATTERNS = [\"package.json\"];\n\nasync function runGit(args: string[], cwd: string): Promise<string> {\n const { stdout } = await execFileAsync(\"git\", args, { cwd });\n return stdout.trim();\n}\n\nexport async function getGitDiff(\n userIgnorePatterns: string[] = [],\n cwd: string = process.cwd()\n): Promise<string> {\n // 检查是否在 git 仓库中\n try {\n await runGit([\"rev-parse\", \"--git-dir\"], cwd);\n } catch (error) {\n console.error(`[review-mark] 当前目录不是 git 仓库: ${cwd}`);\n return \"\";\n }\n\n // 合并固定忽略模式和用户提供的忽略模式\n const allIgnorePatterns = [\n ...new Set([...FIXED_IGNORE_PATTERNS, ...userIgnorePatterns]),\n ];\n // 使用 execFile 直接传参,完全绕过 shell,:(exclude) 不会被解析\n // git 参数顺序:git diff [options] [commit] -- [pathspec]\n // options/commit 必须在 -- 和 pathspec 之前\n const excludeArgs = allIgnorePatterns.map((p) => `:(exclude)${p}`);\n\n let diff = \"\";\n\n try {\n // 优先使用 git diff --cached 获取暂存区的改动\n diff = await runGit(\n [\"diff\", \"--no-color\", \"--relative\", \"--cached\", \"--\", ...excludeArgs],\n cwd\n );\n if (diff) {\n console.log(\"[review-mark] 检测到暂存区改动\");\n }\n } catch (error: any) {\n console.warn(`[review-mark] 获取暂存区 diff 失败: ${error.message}`);\n }\n\n if (!diff) {\n try {\n // 如果暂存区没有改动,则获取工作区和 HEAD 的改动\n diff = await runGit(\n [\"diff\", \"--no-color\", \"--relative\", \"HEAD\", \"--\", ...excludeArgs],\n cwd\n );\n if (diff) {\n console.log(\"[review-mark] 检测到工作区改动(相对于 HEAD)\");\n }\n } catch (error: any) {\n console.warn(`[review-mark] 获取工作区 diff 失败: ${error.message}`);\n }\n }\n\n if (!diff) {\n console.log(\"[review-mark] 未检测到代码改动(已检查暂存区和工作区)\");\n }\n\n return diff;\n}\n","\nexport function generateAIPrompt(diff: string): string {\n return `你是一名资深软件工程师,请 review 以下代码变更。\nGit diff:\n${diff}\n请分析:\n是否存在 bug\n是否存在潜在逻辑问题\n是否存在性能问题\n是否存在代码风格问题\n给出优化建议`;\n}\n","import * as lark from \"@larksuiteoapi/node-sdk\";\nimport {\n appId,\n appSecret,\n receiveId,\n receiveIdType,\n messageType,\n messageTitle,\n} from \"../constants\";\n\n/**\n * 发送 Code Review 结果到飞书\n * 使用飞书官方 Node.js SDK,所有配置从 constants.ts 读取\n */\nexport async function sendReviewToFeishu(reviewContent: string): Promise<void> {\n console.log(\"[review-mark] 正在发送消息到飞书...\");\n console.log(`[review-mark] 消息类型: ${messageType}`);\n\n try {\n // 初始化飞书客户端\n const client = new lark.Client({\n appId,\n appSecret,\n domain: lark.Domain.Feishu,\n });\n\n // 根据消息类型构造消息内容\n let msgContent: string;\n let msgType: string;\n\n if (messageType === \"interactive\") {\n // Interactive 卡片消息(支持 Markdown 渲染)\n msgContent = JSON.stringify({\n config: {\n wide_screen_mode: true,\n },\n header: {\n title: {\n tag: \"plain_text\",\n content: messageTitle,\n },\n template: \"blue\",\n },\n elements: [\n {\n tag: \"div\",\n text: {\n tag: \"lark_md\",\n content: reviewContent,\n },\n },\n {\n tag: \"hr\",\n },\n {\n tag: \"note\",\n elements: [\n {\n tag: \"plain_text\",\n content: `生成时间: ${new Date().toLocaleString(\"zh-CN\", {\n timeZone: \"Asia/Shanghai\",\n })}`,\n },\n ],\n },\n ],\n });\n msgType = \"interactive\";\n } else if (messageType === \"post\") {\n // Post 富文本消息\n msgContent = JSON.stringify({\n zh_cn: {\n title: messageTitle,\n content: convertMarkdownToFeishuPost(reviewContent),\n },\n });\n msgType = \"post\";\n } else if (messageType === \"text\") {\n // Text 纯文本消息\n msgContent = JSON.stringify({\n text: `${messageTitle}\\n\\n${reviewContent}`,\n });\n msgType = \"text\";\n } else {\n throw new Error(`[review-mark] 不支持的消息类型: ${messageType}`);\n }\n\n // 调试日志\n console.log(`[review-mark] 发送参数:`);\n console.log(` - receive_id_type: ${receiveIdType}`);\n console.log(` - receive_id: ${receiveId}`);\n console.log(` - msg_type: ${msgType}`);\n\n // 调用发送消息接口\n const response = await client.im.message.create({\n params: {\n receive_id_type: receiveIdType,\n },\n data: {\n receive_id: receiveId,\n msg_type: msgType,\n content: msgContent,\n },\n });\n\n if (response.code !== 0) {\n throw new Error(\n `[review-mark] 飞书 API 返回错误: ${response.msg || \"未知错误\"}`\n );\n }\n\n console.log(\"[review-mark] ✅ 飞书消息发送成功\");\n console.log(\n `[review-mark] 消息 ID: ${response.data?.message_id || \"未知\"}`\n );\n } catch (error: any) {\n console.error(`[review-mark] ❌ 飞书消息发送失败: ${error.message}`);\n throw error;\n }\n}\n\n/**\n * 将 Markdown 格式转换为飞书 post 格式\n * 飞书 post 格式支持富文本样式\n */\nfunction convertMarkdownToFeishuPost(markdown: string): any[][] {\n const lines = markdown.split(\"\\n\");\n const result: any[][] = [];\n\n let inCodeBlock = false;\n let codeBlockContent: string[] = [];\n\n for (const line of lines) {\n // 处理代码块\n if (line.startsWith(\"```\")) {\n if (inCodeBlock) {\n // 代码块结束\n if (codeBlockContent.length > 0) {\n result.push([\n {\n tag: \"text\",\n text: codeBlockContent.join(\"\\n\"),\n style: [\"code\"],\n },\n ]);\n codeBlockContent = [];\n }\n inCodeBlock = false;\n } else {\n // 代码块开始\n inCodeBlock = true;\n }\n continue;\n }\n\n if (inCodeBlock) {\n codeBlockContent.push(line);\n continue;\n }\n\n // 空行\n if (!line.trim()) {\n result.push([{ tag: \"text\", text: \"\" }]);\n continue;\n }\n\n // 标题(加粗 + 加大)\n if (line.startsWith(\"#\")) {\n const level = line.match(/^#+/)?.[0].length || 1;\n const text = line.replace(/^#+\\s*/, \"\");\n result.push([\n {\n tag: \"text\",\n text: text,\n style: level <= 2 ? [\"bold\", \"underline\"] : [\"bold\"],\n },\n ]);\n continue;\n }\n\n // 解析行内样式(加粗、链接等)\n const parsedLine = parseLineStyles(line);\n result.push(parsedLine);\n }\n\n // 如果还在代码块中,添加剩余内容\n if (codeBlockContent.length > 0) {\n result.push([\n {\n tag: \"text\",\n text: codeBlockContent.join(\"\\n\"),\n style: [\"code\"],\n },\n ]);\n }\n\n return result;\n}\n\n/**\n * 解析行内样式(加粗、链接、代码等)\n */\nfunction parseLineStyles(line: string): any[] {\n const elements: any[] = [];\n let currentText = \"\";\n let i = 0;\n\n while (i < line.length) {\n // 处理加粗 **text**\n if (line[i] === \"*\" && line[i + 1] === \"*\") {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const endIndex = line.indexOf(\"**\", i + 2);\n if (endIndex !== -1) {\n const boldText = line.substring(i + 2, endIndex);\n elements.push({ tag: \"text\", text: boldText, style: [\"bold\"] });\n i = endIndex + 2;\n continue;\n }\n }\n\n // 处理行内代码 `code`\n if (line[i] === \"`\" && line[i + 1] !== \"`\") {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const endIndex = line.indexOf(\"`\", i + 1);\n if (endIndex !== -1) {\n const codeText = line.substring(i + 1, endIndex);\n elements.push({\n tag: \"text\",\n text: codeText,\n style: [\"code\"],\n });\n i = endIndex + 1;\n continue;\n }\n }\n\n // 处理链接 [text](url)\n if (line[i] === \"[\") {\n const textEnd = line.indexOf(\"](\", i);\n const urlEnd = line.indexOf(\")\", textEnd + 2);\n if (textEnd !== -1 && urlEnd !== -1) {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const linkText = line.substring(i + 1, textEnd);\n const url = line.substring(textEnd + 2, urlEnd);\n elements.push({\n tag: \"a\",\n text: linkText,\n href: url,\n });\n i = urlEnd + 1;\n continue;\n }\n }\n\n currentText += line[i];\n i++;\n }\n\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n }\n\n return elements.length > 0 ? elements : [{ tag: \"text\", text: line }];\n}\n","// 默认使用的 AI 模型\nexport const DEFAULT_MODEL = \"composer-1.5\";\n\n// 飞书机器人配置(内置,不需要外部配置)\nexport const appId = \"cli_a93822da7238dbb5\";\nexport const appSecret = \"ZQdcpLUHFb4gFa8cGfrlJfVfSSyGtyzF\";\nexport const receiveId = \"oc_482b6a04f95f4206c4fa9bc61829fd17\";\nexport const receiveIdType = \"chat_id\";\nexport const messageType = \"post\"; // text | post | interactive\nexport const messageTitle = \"🔍 Code Review 结果\";\n"],"mappings":"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","names":["spawn","join","promisify","join","spawn"]}
|
package/dist/index.cjs
CHANGED
|
@@ -233,7 +233,7 @@ ${diff}
|
|
|
233
233
|
var lark = __toESM(require("@larksuiteoapi/node-sdk"), 1);
|
|
234
234
|
|
|
235
235
|
// src/constants.ts
|
|
236
|
-
var DEFAULT_MODEL = "composer-1";
|
|
236
|
+
var DEFAULT_MODEL = "composer-1.5";
|
|
237
237
|
var appId = "cli_a93822da7238dbb5";
|
|
238
238
|
var appSecret = "ZQdcpLUHFb4gFa8cGfrlJfVfSSyGtyzF";
|
|
239
239
|
var receiveId = "oc_482b6a04f95f4206c4fa9bc61829fd17";
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/core/BeLinkReview.ts","../src/utils/checkCli.ts","../src/core/git.ts","../src/core/prompt.ts","../src/core/feishu.ts","../src/constants.ts"],"sourcesContent":["export { BeLinkReview } from \"./core/BeLinkReview\";\nexport type { BeLinkReviewOptions } from \"./types\";\n","import { spawn } from \"node:child_process\";\nimport { readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type {\n BeLinkReviewOptions,\n BeLinkReviewChatOptions,\n BeLinkReviewEnsureResult,\n} from \"../types\";\nimport { isCheckCliInstall } from \"../utils/checkCli\";\nimport { getGitDiff } from \"./git\";\nimport { generateAIPrompt } from \"./prompt\";\nimport { sendReviewToFeishu } from \"./feishu\";\nimport { DEFAULT_MODEL } from \"../constants\";\n\nexport class BeLinkReview {\n static #instance: BeLinkReview | null = null;\n #apiKey: string | undefined;\n #agentPath: string | undefined;\n #ignorePatterns: string[] | undefined;\n #enableFeishu: boolean;\n #model: string | undefined;\n\n private constructor(\n apiKey?: string,\n agentPath?: string,\n ignorePatterns?: string[],\n enableFeishu: boolean = true,\n model?: string\n ) {\n this.#apiKey = apiKey;\n this.#agentPath = agentPath;\n this.#ignorePatterns = ignorePatterns;\n this.#enableFeishu = enableFeishu;\n this.#model = model;\n }\n\n /**\n * 初始化单例并注入参数,在项目入口调用一次即可。\n * 负责保存 apiKey 和自动写入 package.json 脚本。\n * 此方法主要用于在用户项目中设置 apiKey 和脚本,CLI 运行时会优先从环境变量或命令行参数获取。\n */\n static init(options: BeLinkReviewOptions = {}): BeLinkReview {\n if (BeLinkReview.#instance === null) {\n BeLinkReview.#instance = new BeLinkReview(\n options.apiKey,\n options.agentPath,\n options.ignore,\n options.enableFeishu ?? true,\n options.model\n );\n } else {\n BeLinkReview.#instance.#apiKey = options.apiKey;\n BeLinkReview.#instance.#agentPath = options.agentPath;\n BeLinkReview.#instance.#ignorePatterns = options.ignore;\n BeLinkReview.#instance.#enableFeishu = options.enableFeishu ?? true;\n BeLinkReview.#instance.#model = options.model;\n }\n BeLinkReview.#instance.#setupProjectScript();\n return BeLinkReview.#instance;\n }\n\n /**\n * 获取单例实例。如果未通过 init() 初始化,则尝试从环境变量 CURSOR_API_KEY 获取。\n * @param cliApiKey 可选的命令行传入的 apiKey\n * @param cliAgentPath 可选的命令行传入的 agentPath\n * @param cliIgnorePatterns 可选的命令行传入的 ignorePatterns\n * @param cliEnableFeishu 可选的命令行传入的飞书开关\n */\n static getInstance(\n cliApiKey?: string,\n cliAgentPath?: string,\n cliIgnorePatterns?: string[],\n cliEnableFeishu?: boolean,\n cliModel?: string\n ): BeLinkReview {\n if (BeLinkReview.#instance === null) {\n const apiKey = cliApiKey || process.env.CURSOR_API_KEY;\n const agentPath = cliAgentPath || process.env.CURSOR_AGENT_PATH;\n const ignorePatterns =\n cliIgnorePatterns ||\n (process.env.BE_LINK_REVIEW_IGNORE\n ? process.env.BE_LINK_REVIEW_IGNORE.split(\",\")\n : undefined);\n const enableFeishu =\n cliEnableFeishu ?? process.env.FEISHU_ENABLED !== \"false\";\n const model = cliModel || process.env.CURSOR_MODEL;\n\n if (!apiKey) {\n throw new Error(\n '[review-mark] 请先调用 BeLinkReview.init({ apiKey: \"...\" }) 初始化,或设置环境变量 CURSOR_API_KEY,或通过命令行参数 --apiKey 传入。'\n );\n }\n BeLinkReview.#instance = new BeLinkReview(\n apiKey,\n agentPath,\n ignorePatterns,\n enableFeishu,\n model\n );\n } else {\n if (cliApiKey && !BeLinkReview.#instance.#apiKey) {\n BeLinkReview.#instance.#apiKey = cliApiKey;\n }\n if (cliAgentPath && !BeLinkReview.#instance.#agentPath) {\n BeLinkReview.#instance.#agentPath = cliAgentPath;\n }\n if (cliIgnorePatterns && !BeLinkReview.#instance.#ignorePatterns) {\n BeLinkReview.#instance.#ignorePatterns = cliIgnorePatterns;\n }\n if (cliEnableFeishu !== undefined) {\n BeLinkReview.#instance.#enableFeishu = cliEnableFeishu;\n }\n if (cliModel) {\n BeLinkReview.#instance.#model = cliModel;\n }\n }\n return BeLinkReview.#instance;\n }\n\n #getApiKey(): string {\n const apiKey = this.#apiKey || process.env.CURSOR_API_KEY;\n if (!apiKey) {\n throw new Error(\n '[review-mark] 请先在 init({ apiKey: \"...\" }) 中传入 apiKey,或设置环境变量 CURSOR_API_KEY,或通过命令行参数 --apiKey 传入。'\n );\n }\n return apiKey;\n }\n\n #getAgentPath(): string | undefined {\n return this.#agentPath || process.env.CURSOR_AGENT_PATH;\n }\n\n #getIgnorePatterns(): string[] {\n const envIgnore = process.env.BE_LINK_REVIEW_IGNORE\n ? process.env.BE_LINK_REVIEW_IGNORE.split(\",\")\n : [];\n return this.#ignorePatterns || envIgnore;\n }\n\n #isFeishuEnabled(): boolean {\n return this.#enableFeishu;\n }\n\n #getModel(): string {\n return this.#model || process.env.CURSOR_MODEL || DEFAULT_MODEL;\n }\n\n /**\n * 自动往用户项目 package.json 写入 script\n */\n async #setupProjectScript() {\n try {\n const packageJsonPath = join(process.cwd(), \"package.json\");\n const packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf-8\"));\n\n if (!packageJson.scripts) {\n packageJson.scripts = {};\n }\n\n if (!packageJson.scripts[\"review-mark\"]) {\n packageJson.scripts[\"review-mark\"] = \"review-mark\";\n writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));\n console.log(\n \"[review-mark] 已在 package.json 中添加 'review-mark' 脚本。\"\n );\n } else {\n console.log(\"[review-mark] 'review-mark' 脚本已存在,跳过添加。\");\n }\n } catch (error) {\n console.error(\"[review-mark] 无法更新 package.json: \", error);\n }\n }\n\n /**\n * 执行一次完整 CLI 流程:获取 git diff → 生成 prompt → 检查/安装 CLI → 与 Cursor 对话并打印结果。\n */\n async goCli(): Promise<string> {\n const apiKey = this.#getApiKey();\n const agentPath = this.#getAgentPath();\n const ignorePatterns = this.#getIgnorePatterns();\n\n const ensureResult = await this.ensureAgentInstalled(false, agentPath);\n\n if (!ensureResult.isInstalled) {\n throw new Error(\n \"[review-mark] Cursor CLI 未安装且自动安装失败,请手动安装。\"\n );\n }\n\n console.log(\"[review-mark] Getting git diff...\");\n const diff = await getGitDiff(ignorePatterns, process.cwd());\n\n if (!diff) {\n console.log(\"[review-mark] No code changes detected\");\n return \"No code changes detected\";\n }\n\n const prompt = generateAIPrompt(diff);\n console.log(\"[review-mark] Sending to AI...\");\n const response = await this.chat(prompt, {\n agentPath: ensureResult.actualAgentPath,\n force: true,\n model: this.#getModel(),\n });\n console.log(this.#getModel(), \"model\");\n console.log(\"===== AI Review =====\");\n console.log(response);\n\n // 如果启用了飞书,发送到飞书\n if (this.#isFeishuEnabled()) {\n try {\n await sendReviewToFeishu(response);\n } catch (error: any) {\n console.error(\n `[review-mark] 飞书通知发送失败,但不影响 review 结果: ${error.message}`\n );\n }\n }\n\n return response;\n }\n\n /**\n * 检查 Cursor CLI 是否已安装;未安装则自动安装(需网络)。\n * @param silent 为 true 时不打印「已安装」等提示\n * @param agentPath 可选的 agent 可执行文件路径\n */\n async ensureAgentInstalled(\n silent = false,\n agentPath?: string\n ): Promise<BeLinkReviewEnsureResult> {\n return isCheckCliInstall({ apiKey: this.#getApiKey(), silent, agentPath });\n }\n\n /**\n * 通过 Cursor Headless CLI 与 Cursor 对话(发送 prompt,拿到回复)。\n * 默认会先执行 ensureAgentInstalled(检查/安装 CLI),再启动 agent 执行提问。\n * 参考:https://cursor.com/cn/docs/cli/headless\n */\n async chat(\n prompt: string,\n options: BeLinkReviewChatOptions = {}\n ): Promise<string> {\n const actualAgentPath =\n options.agentPath || this.#getAgentPath() || \"agent\";\n const model = options.model || this.#getModel();\n const args: string[] = [\"--yolo\", \"-p\", prompt];\n if (model) args.unshift(\"--model\", model);\n if (options.outputFormat === \"json\") args.push(\"--output-format\", \"json\");\n\n return new Promise((resolve, reject) => {\n const env = { ...process.env, CURSOR_API_KEY: this.#getApiKey() };\n const proc = spawn(actualAgentPath, args, {\n env,\n cwd: process.cwd(),\n shell: false,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n proc.stdout?.on(\"data\", (chunk) => {\n stdout += chunk.toString();\n });\n proc.stderr?.on(\"data\", (chunk) => {\n stderr += chunk.toString();\n });\n\n proc.on(\"error\", (err) => {\n reject(\n new Error(\n `[review-mark] 无法启动 Cursor CLI (${actualAgentPath}),请先安装:curl https://cursor.com/install -fsS | bash。原始错误: ${err.message}`\n )\n );\n });\n\n proc.on(\"close\", (code) => {\n if (code !== 0) {\n reject(\n new Error(\n `[review-mark] agent 退出码 ${code}${\n stderr ? `: ${stderr.trim()}` : \"\"\n }`\n )\n );\n return;\n }\n resolve(stdout.trim());\n });\n });\n }\n}\n","import { spawn, exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { existsSync, appendFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { BeLinkReviewEnsureResult } from \"@/types\";\n\nconst execAsync = promisify(exec);\n\ninterface CheckCliInstallOptions {\n apiKey: string;\n silent?: boolean;\n agentPath?: string;\n}\n\n// Cursor 官方安装脚本默认将 agent 放在 ~/.local/bin\nconst LOCAL_BIN = join(homedir(), \".local\", \"bin\");\n\n// 常见的 Cursor CLI 安装路径\nconst COMMON_AGENT_PATHS = [\n join(LOCAL_BIN, \"agent\"), // Cursor 官方安装脚本默认路径\n join(homedir(), \".cursor\", \"agent\"),\n \"/usr/local/bin/agent\",\n \"/opt/homebrew/bin/agent\",\n];\n\n/**\n * 将 ~/.local/bin 写入用户 shell 配置文件(.zshrc / .bashrc),\n * 并立即注入当前进程的 PATH,确保安装后无需重启终端即可使用。\n */\nfunction setupLocalBinPath(silent: boolean): void {\n // 立即让当前进程能找到 agent\n if (!process.env.PATH?.includes(LOCAL_BIN)) {\n process.env.PATH = `${LOCAL_BIN}:${process.env.PATH}`;\n }\n\n const exportLine = `\\nexport PATH=\"$HOME/.local/bin:$PATH\"`;\n const shell = process.env.SHELL ?? \"\";\n const rcFiles: string[] = [];\n\n if (shell.includes(\"zsh\")) {\n rcFiles.push(join(homedir(), \".zshrc\"));\n } else if (shell.includes(\"bash\")) {\n rcFiles.push(join(homedir(), \".bashrc\"));\n } else {\n // 两个都写,保证覆盖\n rcFiles.push(join(homedir(), \".zshrc\"), join(homedir(), \".bashrc\"));\n }\n\n for (const rc of rcFiles) {\n try {\n const content = existsSync(rc)\n ? require(\"node:fs\").readFileSync(rc, \"utf-8\")\n : \"\";\n if (!content.includes(\".local/bin\")) {\n appendFileSync(rc, exportLine);\n if (!silent) {\n console.log(`[review-mark] 已将 ~/.local/bin 写入 ${rc}`);\n }\n }\n } catch {\n // 写入失败不阻断流程\n }\n }\n}\n\nasync function findAgentExecutable(\n userAgentPath?: string\n): Promise<string | null> {\n // 1. 优先使用用户指定的路径\n if (userAgentPath && existsSync(userAgentPath)) {\n return userAgentPath;\n }\n\n // 2. 尝试通过 which 命令在 PATH 中查找\n try {\n const { stdout } = await execAsync(\"which agent\");\n const pathFromWhich = stdout.trim();\n if (pathFromWhich && existsSync(pathFromWhich)) {\n return pathFromWhich;\n }\n } catch (error) {\n // ignore error, continue to next method\n }\n\n // 3. 检查常见安装路径\n for (const path of COMMON_AGENT_PATHS) {\n if (existsSync(path)) {\n return path;\n }\n }\n\n return null;\n}\n\nexport async function isCheckCliInstall(\n options: CheckCliInstallOptions\n): Promise<BeLinkReviewEnsureResult> {\n const { silent = false, agentPath: userAgentPath } = options;\n\n let actualAgentPath = await findAgentExecutable(userAgentPath);\n\n if (actualAgentPath) {\n if (!silent) {\n console.log(\n `[review-mark] Cursor CLI (agent) 已在 ${actualAgentPath} 找到。`\n );\n }\n return { isInstalled: true, message: \"Cursor CLI 已安装\", actualAgentPath };\n }\n\n if (!silent) {\n console.log(\"[review-mark] Cursor CLI (agent) 未找到,正在尝试安装...\");\n console.log(\n \"[review-mark] 执行安装命令: curl https://cursor.com/install -fsS | bash\"\n );\n }\n\n return new Promise((resolve, reject) => {\n const installProcess = spawn(\n \"bash\",\n [\"-c\", \"curl https://cursor.com/install -fsS | bash\"],\n {\n stdio: \"inherit\", // 将安装过程的输出直接显示给用户\n }\n );\n\n installProcess.on(\"close\", async (code) => {\n if (code === 0) {\n // 安装完成后自动配置 PATH(写入 rc 文件 + 注入当前进程)\n setupLocalBinPath(silent);\n\n // 再次检查 agent 命令是否可用\n actualAgentPath = await findAgentExecutable(userAgentPath);\n if (actualAgentPath) {\n if (!silent) {\n console.log(\"[review-mark] Cursor CLI 安装成功。\");\n }\n resolve({\n isInstalled: true,\n message: \"Cursor CLI 安装成功\",\n actualAgentPath,\n });\n } else {\n reject(\n new Error(\n `[review-mark] Cursor CLI 安装命令执行成功,但未找到 agent 可执行文件。请手动检查安装:curl https://cursor.com/install -fsS | bash。`\n )\n );\n }\n } else {\n reject(\n new Error(\n `[review-mark] Cursor CLI 安装失败,退出码 ${code}。请手动安装:curl https://cursor.com/install -fsS | bash。`\n )\n );\n }\n });\n\n installProcess.on(\"error\", (err) => {\n reject(\n new Error(\n `[review-mark] 无法启动安装进程:${err.message}。请手动安装:curl https://cursor.com/install -fsS | bash`\n )\n );\n });\n });\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\n// 固定的忽略文件模式,这些文件将永远不会被审查\nconst FIXED_IGNORE_PATTERNS = [\"package.json\"];\n\nasync function runGit(args: string[], cwd: string): Promise<string> {\n const { stdout } = await execFileAsync(\"git\", args, { cwd });\n return stdout.trim();\n}\n\nexport async function getGitDiff(\n userIgnorePatterns: string[] = [],\n cwd: string = process.cwd()\n): Promise<string> {\n // 检查是否在 git 仓库中\n try {\n await runGit([\"rev-parse\", \"--git-dir\"], cwd);\n } catch (error) {\n console.error(`[review-mark] 当前目录不是 git 仓库: ${cwd}`);\n return \"\";\n }\n\n // 合并固定忽略模式和用户提供的忽略模式\n const allIgnorePatterns = [\n ...new Set([...FIXED_IGNORE_PATTERNS, ...userIgnorePatterns]),\n ];\n // 使用 execFile 直接传参,完全绕过 shell,:(exclude) 不会被解析\n // git 参数顺序:git diff [options] [commit] -- [pathspec]\n // options/commit 必须在 -- 和 pathspec 之前\n const excludeArgs = allIgnorePatterns.map((p) => `:(exclude)${p}`);\n\n let diff = \"\";\n\n try {\n // 优先使用 git diff --cached 获取暂存区的改动\n diff = await runGit(\n [\"diff\", \"--no-color\", \"--relative\", \"--cached\", \"--\", ...excludeArgs],\n cwd\n );\n if (diff) {\n console.log(\"[review-mark] 检测到暂存区改动\");\n }\n } catch (error: any) {\n console.warn(`[review-mark] 获取暂存区 diff 失败: ${error.message}`);\n }\n\n if (!diff) {\n try {\n // 如果暂存区没有改动,则获取工作区和 HEAD 的改动\n diff = await runGit(\n [\"diff\", \"--no-color\", \"--relative\", \"HEAD\", \"--\", ...excludeArgs],\n cwd\n );\n if (diff) {\n console.log(\"[review-mark] 检测到工作区改动(相对于 HEAD)\");\n }\n } catch (error: any) {\n console.warn(`[review-mark] 获取工作区 diff 失败: ${error.message}`);\n }\n }\n\n if (!diff) {\n console.log(\"[review-mark] 未检测到代码改动(已检查暂存区和工作区)\");\n }\n\n return diff;\n}\n","\nexport function generateAIPrompt(diff: string): string {\n return `你是一名资深软件工程师,请 review 以下代码变更。\nGit diff:\n${diff}\n请分析:\n是否存在 bug\n是否存在潜在逻辑问题\n是否存在性能问题\n是否存在代码风格问题\n给出优化建议`;\n}\n","import * as lark from \"@larksuiteoapi/node-sdk\";\nimport {\n appId,\n appSecret,\n receiveId,\n receiveIdType,\n messageType,\n messageTitle,\n} from \"../constants\";\n\n/**\n * 发送 Code Review 结果到飞书\n * 使用飞书官方 Node.js SDK,所有配置从 constants.ts 读取\n */\nexport async function sendReviewToFeishu(reviewContent: string): Promise<void> {\n console.log(\"[review-mark] 正在发送消息到飞书...\");\n console.log(`[review-mark] 消息类型: ${messageType}`);\n\n try {\n // 初始化飞书客户端\n const client = new lark.Client({\n appId,\n appSecret,\n domain: lark.Domain.Feishu,\n });\n\n // 根据消息类型构造消息内容\n let msgContent: string;\n let msgType: string;\n\n if (messageType === \"interactive\") {\n // Interactive 卡片消息(支持 Markdown 渲染)\n msgContent = JSON.stringify({\n config: {\n wide_screen_mode: true,\n },\n header: {\n title: {\n tag: \"plain_text\",\n content: messageTitle,\n },\n template: \"blue\",\n },\n elements: [\n {\n tag: \"div\",\n text: {\n tag: \"lark_md\",\n content: reviewContent,\n },\n },\n {\n tag: \"hr\",\n },\n {\n tag: \"note\",\n elements: [\n {\n tag: \"plain_text\",\n content: `生成时间: ${new Date().toLocaleString(\"zh-CN\", {\n timeZone: \"Asia/Shanghai\",\n })}`,\n },\n ],\n },\n ],\n });\n msgType = \"interactive\";\n } else if (messageType === \"post\") {\n // Post 富文本消息\n msgContent = JSON.stringify({\n zh_cn: {\n title: messageTitle,\n content: convertMarkdownToFeishuPost(reviewContent),\n },\n });\n msgType = \"post\";\n } else if (messageType === \"text\") {\n // Text 纯文本消息\n msgContent = JSON.stringify({\n text: `${messageTitle}\\n\\n${reviewContent}`,\n });\n msgType = \"text\";\n } else {\n throw new Error(`[review-mark] 不支持的消息类型: ${messageType}`);\n }\n\n // 调试日志\n console.log(`[review-mark] 发送参数:`);\n console.log(` - receive_id_type: ${receiveIdType}`);\n console.log(` - receive_id: ${receiveId}`);\n console.log(` - msg_type: ${msgType}`);\n\n // 调用发送消息接口\n const response = await client.im.message.create({\n params: {\n receive_id_type: receiveIdType,\n },\n data: {\n receive_id: receiveId,\n msg_type: msgType,\n content: msgContent,\n },\n });\n\n if (response.code !== 0) {\n throw new Error(\n `[review-mark] 飞书 API 返回错误: ${response.msg || \"未知错误\"}`\n );\n }\n\n console.log(\"[review-mark] ✅ 飞书消息发送成功\");\n console.log(\n `[review-mark] 消息 ID: ${response.data?.message_id || \"未知\"}`\n );\n } catch (error: any) {\n console.error(`[review-mark] ❌ 飞书消息发送失败: ${error.message}`);\n throw error;\n }\n}\n\n/**\n * 将 Markdown 格式转换为飞书 post 格式\n * 飞书 post 格式支持富文本样式\n */\nfunction convertMarkdownToFeishuPost(markdown: string): any[][] {\n const lines = markdown.split(\"\\n\");\n const result: any[][] = [];\n\n let inCodeBlock = false;\n let codeBlockContent: string[] = [];\n\n for (const line of lines) {\n // 处理代码块\n if (line.startsWith(\"```\")) {\n if (inCodeBlock) {\n // 代码块结束\n if (codeBlockContent.length > 0) {\n result.push([\n {\n tag: \"text\",\n text: codeBlockContent.join(\"\\n\"),\n style: [\"code\"],\n },\n ]);\n codeBlockContent = [];\n }\n inCodeBlock = false;\n } else {\n // 代码块开始\n inCodeBlock = true;\n }\n continue;\n }\n\n if (inCodeBlock) {\n codeBlockContent.push(line);\n continue;\n }\n\n // 空行\n if (!line.trim()) {\n result.push([{ tag: \"text\", text: \"\" }]);\n continue;\n }\n\n // 标题(加粗 + 加大)\n if (line.startsWith(\"#\")) {\n const level = line.match(/^#+/)?.[0].length || 1;\n const text = line.replace(/^#+\\s*/, \"\");\n result.push([\n {\n tag: \"text\",\n text: text,\n style: level <= 2 ? [\"bold\", \"underline\"] : [\"bold\"],\n },\n ]);\n continue;\n }\n\n // 解析行内样式(加粗、链接等)\n const parsedLine = parseLineStyles(line);\n result.push(parsedLine);\n }\n\n // 如果还在代码块中,添加剩余内容\n if (codeBlockContent.length > 0) {\n result.push([\n {\n tag: \"text\",\n text: codeBlockContent.join(\"\\n\"),\n style: [\"code\"],\n },\n ]);\n }\n\n return result;\n}\n\n/**\n * 解析行内样式(加粗、链接、代码等)\n */\nfunction parseLineStyles(line: string): any[] {\n const elements: any[] = [];\n let currentText = \"\";\n let i = 0;\n\n while (i < line.length) {\n // 处理加粗 **text**\n if (line[i] === \"*\" && line[i + 1] === \"*\") {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const endIndex = line.indexOf(\"**\", i + 2);\n if (endIndex !== -1) {\n const boldText = line.substring(i + 2, endIndex);\n elements.push({ tag: \"text\", text: boldText, style: [\"bold\"] });\n i = endIndex + 2;\n continue;\n }\n }\n\n // 处理行内代码 `code`\n if (line[i] === \"`\" && line[i + 1] !== \"`\") {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const endIndex = line.indexOf(\"`\", i + 1);\n if (endIndex !== -1) {\n const codeText = line.substring(i + 1, endIndex);\n elements.push({\n tag: \"text\",\n text: codeText,\n style: [\"code\"],\n });\n i = endIndex + 1;\n continue;\n }\n }\n\n // 处理链接 [text](url)\n if (line[i] === \"[\") {\n const textEnd = line.indexOf(\"](\", i);\n const urlEnd = line.indexOf(\")\", textEnd + 2);\n if (textEnd !== -1 && urlEnd !== -1) {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const linkText = line.substring(i + 1, textEnd);\n const url = line.substring(textEnd + 2, urlEnd);\n elements.push({\n tag: \"a\",\n text: linkText,\n href: url,\n });\n i = urlEnd + 1;\n continue;\n }\n }\n\n currentText += line[i];\n i++;\n }\n\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n }\n\n return elements.length > 0 ? elements : [{ tag: \"text\", text: line }];\n}\n","// 默认使用的 AI 模型\nexport const DEFAULT_MODEL = \"composer-1\";\n\n// 飞书机器人配置(内置,不需要外部配置)\nexport const appId = \"cli_a93822da7238dbb5\";\nexport const appSecret = \"ZQdcpLUHFb4gFa8cGfrlJfVfSSyGtyzF\";\nexport const receiveId = \"oc_482b6a04f95f4206c4fa9bc61829fd17\";\nexport const receiveIdType = \"chat_id\";\nexport const messageType = \"post\"; // text | post | interactive\nexport const messageTitle = \"🔍 Code Review 结果\";\n"],"mappings":"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","names":["import_node_child_process","import_node_fs","import_node_path","import_node_child_process","import_node_util"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/BeLinkReview.ts","../src/utils/checkCli.ts","../src/core/git.ts","../src/core/prompt.ts","../src/core/feishu.ts","../src/constants.ts"],"sourcesContent":["export { BeLinkReview } from \"./core/BeLinkReview\";\nexport type { BeLinkReviewOptions } from \"./types\";\n","import { spawn } from \"node:child_process\";\nimport { readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type {\n BeLinkReviewOptions,\n BeLinkReviewChatOptions,\n BeLinkReviewEnsureResult,\n} from \"../types\";\nimport { isCheckCliInstall } from \"../utils/checkCli\";\nimport { getGitDiff } from \"./git\";\nimport { generateAIPrompt } from \"./prompt\";\nimport { sendReviewToFeishu } from \"./feishu\";\nimport { DEFAULT_MODEL } from \"../constants\";\n\nexport class BeLinkReview {\n static #instance: BeLinkReview | null = null;\n #apiKey: string | undefined;\n #agentPath: string | undefined;\n #ignorePatterns: string[] | undefined;\n #enableFeishu: boolean;\n #model: string | undefined;\n\n private constructor(\n apiKey?: string,\n agentPath?: string,\n ignorePatterns?: string[],\n enableFeishu: boolean = true,\n model?: string\n ) {\n this.#apiKey = apiKey;\n this.#agentPath = agentPath;\n this.#ignorePatterns = ignorePatterns;\n this.#enableFeishu = enableFeishu;\n this.#model = model;\n }\n\n /**\n * 初始化单例并注入参数,在项目入口调用一次即可。\n * 负责保存 apiKey 和自动写入 package.json 脚本。\n * 此方法主要用于在用户项目中设置 apiKey 和脚本,CLI 运行时会优先从环境变量或命令行参数获取。\n */\n static init(options: BeLinkReviewOptions = {}): BeLinkReview {\n if (BeLinkReview.#instance === null) {\n BeLinkReview.#instance = new BeLinkReview(\n options.apiKey,\n options.agentPath,\n options.ignore,\n options.enableFeishu ?? true,\n options.model\n );\n } else {\n BeLinkReview.#instance.#apiKey = options.apiKey;\n BeLinkReview.#instance.#agentPath = options.agentPath;\n BeLinkReview.#instance.#ignorePatterns = options.ignore;\n BeLinkReview.#instance.#enableFeishu = options.enableFeishu ?? true;\n BeLinkReview.#instance.#model = options.model;\n }\n BeLinkReview.#instance.#setupProjectScript();\n return BeLinkReview.#instance;\n }\n\n /**\n * 获取单例实例。如果未通过 init() 初始化,则尝试从环境变量 CURSOR_API_KEY 获取。\n * @param cliApiKey 可选的命令行传入的 apiKey\n * @param cliAgentPath 可选的命令行传入的 agentPath\n * @param cliIgnorePatterns 可选的命令行传入的 ignorePatterns\n * @param cliEnableFeishu 可选的命令行传入的飞书开关\n */\n static getInstance(\n cliApiKey?: string,\n cliAgentPath?: string,\n cliIgnorePatterns?: string[],\n cliEnableFeishu?: boolean,\n cliModel?: string\n ): BeLinkReview {\n if (BeLinkReview.#instance === null) {\n const apiKey = cliApiKey || process.env.CURSOR_API_KEY;\n const agentPath = cliAgentPath || process.env.CURSOR_AGENT_PATH;\n const ignorePatterns =\n cliIgnorePatterns ||\n (process.env.BE_LINK_REVIEW_IGNORE\n ? process.env.BE_LINK_REVIEW_IGNORE.split(\",\")\n : undefined);\n const enableFeishu =\n cliEnableFeishu ?? process.env.FEISHU_ENABLED !== \"false\";\n const model = cliModel || process.env.CURSOR_MODEL;\n\n if (!apiKey) {\n throw new Error(\n '[review-mark] 请先调用 BeLinkReview.init({ apiKey: \"...\" }) 初始化,或设置环境变量 CURSOR_API_KEY,或通过命令行参数 --apiKey 传入。'\n );\n }\n BeLinkReview.#instance = new BeLinkReview(\n apiKey,\n agentPath,\n ignorePatterns,\n enableFeishu,\n model\n );\n } else {\n if (cliApiKey && !BeLinkReview.#instance.#apiKey) {\n BeLinkReview.#instance.#apiKey = cliApiKey;\n }\n if (cliAgentPath && !BeLinkReview.#instance.#agentPath) {\n BeLinkReview.#instance.#agentPath = cliAgentPath;\n }\n if (cliIgnorePatterns && !BeLinkReview.#instance.#ignorePatterns) {\n BeLinkReview.#instance.#ignorePatterns = cliIgnorePatterns;\n }\n if (cliEnableFeishu !== undefined) {\n BeLinkReview.#instance.#enableFeishu = cliEnableFeishu;\n }\n if (cliModel) {\n BeLinkReview.#instance.#model = cliModel;\n }\n }\n return BeLinkReview.#instance;\n }\n\n #getApiKey(): string {\n const apiKey = this.#apiKey || process.env.CURSOR_API_KEY;\n if (!apiKey) {\n throw new Error(\n '[review-mark] 请先在 init({ apiKey: \"...\" }) 中传入 apiKey,或设置环境变量 CURSOR_API_KEY,或通过命令行参数 --apiKey 传入。'\n );\n }\n return apiKey;\n }\n\n #getAgentPath(): string | undefined {\n return this.#agentPath || process.env.CURSOR_AGENT_PATH;\n }\n\n #getIgnorePatterns(): string[] {\n const envIgnore = process.env.BE_LINK_REVIEW_IGNORE\n ? process.env.BE_LINK_REVIEW_IGNORE.split(\",\")\n : [];\n return this.#ignorePatterns || envIgnore;\n }\n\n #isFeishuEnabled(): boolean {\n return this.#enableFeishu;\n }\n\n #getModel(): string {\n return this.#model || process.env.CURSOR_MODEL || DEFAULT_MODEL;\n }\n\n /**\n * 自动往用户项目 package.json 写入 script\n */\n async #setupProjectScript() {\n try {\n const packageJsonPath = join(process.cwd(), \"package.json\");\n const packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf-8\"));\n\n if (!packageJson.scripts) {\n packageJson.scripts = {};\n }\n\n if (!packageJson.scripts[\"review-mark\"]) {\n packageJson.scripts[\"review-mark\"] = \"review-mark\";\n writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));\n console.log(\n \"[review-mark] 已在 package.json 中添加 'review-mark' 脚本。\"\n );\n } else {\n console.log(\"[review-mark] 'review-mark' 脚本已存在,跳过添加。\");\n }\n } catch (error) {\n console.error(\"[review-mark] 无法更新 package.json: \", error);\n }\n }\n\n /**\n * 执行一次完整 CLI 流程:获取 git diff → 生成 prompt → 检查/安装 CLI → 与 Cursor 对话并打印结果。\n */\n async goCli(): Promise<string> {\n const apiKey = this.#getApiKey();\n const agentPath = this.#getAgentPath();\n const ignorePatterns = this.#getIgnorePatterns();\n\n const ensureResult = await this.ensureAgentInstalled(false, agentPath);\n\n if (!ensureResult.isInstalled) {\n throw new Error(\n \"[review-mark] Cursor CLI 未安装且自动安装失败,请手动安装。\"\n );\n }\n\n console.log(\"[review-mark] Getting git diff...\");\n const diff = await getGitDiff(ignorePatterns, process.cwd());\n\n if (!diff) {\n console.log(\"[review-mark] No code changes detected\");\n return \"No code changes detected\";\n }\n\n const prompt = generateAIPrompt(diff);\n console.log(\"[review-mark] Sending to AI...\");\n const response = await this.chat(prompt, {\n agentPath: ensureResult.actualAgentPath,\n force: true,\n model: this.#getModel(),\n });\n console.log(this.#getModel(), \"model\");\n console.log(\"===== AI Review =====\");\n console.log(response);\n\n // 如果启用了飞书,发送到飞书\n if (this.#isFeishuEnabled()) {\n try {\n await sendReviewToFeishu(response);\n } catch (error: any) {\n console.error(\n `[review-mark] 飞书通知发送失败,但不影响 review 结果: ${error.message}`\n );\n }\n }\n\n return response;\n }\n\n /**\n * 检查 Cursor CLI 是否已安装;未安装则自动安装(需网络)。\n * @param silent 为 true 时不打印「已安装」等提示\n * @param agentPath 可选的 agent 可执行文件路径\n */\n async ensureAgentInstalled(\n silent = false,\n agentPath?: string\n ): Promise<BeLinkReviewEnsureResult> {\n return isCheckCliInstall({ apiKey: this.#getApiKey(), silent, agentPath });\n }\n\n /**\n * 通过 Cursor Headless CLI 与 Cursor 对话(发送 prompt,拿到回复)。\n * 默认会先执行 ensureAgentInstalled(检查/安装 CLI),再启动 agent 执行提问。\n * 参考:https://cursor.com/cn/docs/cli/headless\n */\n async chat(\n prompt: string,\n options: BeLinkReviewChatOptions = {}\n ): Promise<string> {\n const actualAgentPath =\n options.agentPath || this.#getAgentPath() || \"agent\";\n const model = options.model || this.#getModel();\n const args: string[] = [\"--yolo\", \"-p\", prompt];\n if (model) args.unshift(\"--model\", model);\n if (options.outputFormat === \"json\") args.push(\"--output-format\", \"json\");\n\n return new Promise((resolve, reject) => {\n const env = { ...process.env, CURSOR_API_KEY: this.#getApiKey() };\n const proc = spawn(actualAgentPath, args, {\n env,\n cwd: process.cwd(),\n shell: false,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n proc.stdout?.on(\"data\", (chunk) => {\n stdout += chunk.toString();\n });\n proc.stderr?.on(\"data\", (chunk) => {\n stderr += chunk.toString();\n });\n\n proc.on(\"error\", (err) => {\n reject(\n new Error(\n `[review-mark] 无法启动 Cursor CLI (${actualAgentPath}),请先安装:curl https://cursor.com/install -fsS | bash。原始错误: ${err.message}`\n )\n );\n });\n\n proc.on(\"close\", (code) => {\n if (code !== 0) {\n reject(\n new Error(\n `[review-mark] agent 退出码 ${code}${\n stderr ? `: ${stderr.trim()}` : \"\"\n }`\n )\n );\n return;\n }\n resolve(stdout.trim());\n });\n });\n }\n}\n","import { spawn, exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { existsSync, appendFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { BeLinkReviewEnsureResult } from \"@/types\";\n\nconst execAsync = promisify(exec);\n\ninterface CheckCliInstallOptions {\n apiKey: string;\n silent?: boolean;\n agentPath?: string;\n}\n\n// Cursor 官方安装脚本默认将 agent 放在 ~/.local/bin\nconst LOCAL_BIN = join(homedir(), \".local\", \"bin\");\n\n// 常见的 Cursor CLI 安装路径\nconst COMMON_AGENT_PATHS = [\n join(LOCAL_BIN, \"agent\"), // Cursor 官方安装脚本默认路径\n join(homedir(), \".cursor\", \"agent\"),\n \"/usr/local/bin/agent\",\n \"/opt/homebrew/bin/agent\",\n];\n\n/**\n * 将 ~/.local/bin 写入用户 shell 配置文件(.zshrc / .bashrc),\n * 并立即注入当前进程的 PATH,确保安装后无需重启终端即可使用。\n */\nfunction setupLocalBinPath(silent: boolean): void {\n // 立即让当前进程能找到 agent\n if (!process.env.PATH?.includes(LOCAL_BIN)) {\n process.env.PATH = `${LOCAL_BIN}:${process.env.PATH}`;\n }\n\n const exportLine = `\\nexport PATH=\"$HOME/.local/bin:$PATH\"`;\n const shell = process.env.SHELL ?? \"\";\n const rcFiles: string[] = [];\n\n if (shell.includes(\"zsh\")) {\n rcFiles.push(join(homedir(), \".zshrc\"));\n } else if (shell.includes(\"bash\")) {\n rcFiles.push(join(homedir(), \".bashrc\"));\n } else {\n // 两个都写,保证覆盖\n rcFiles.push(join(homedir(), \".zshrc\"), join(homedir(), \".bashrc\"));\n }\n\n for (const rc of rcFiles) {\n try {\n const content = existsSync(rc)\n ? require(\"node:fs\").readFileSync(rc, \"utf-8\")\n : \"\";\n if (!content.includes(\".local/bin\")) {\n appendFileSync(rc, exportLine);\n if (!silent) {\n console.log(`[review-mark] 已将 ~/.local/bin 写入 ${rc}`);\n }\n }\n } catch {\n // 写入失败不阻断流程\n }\n }\n}\n\nasync function findAgentExecutable(\n userAgentPath?: string\n): Promise<string | null> {\n // 1. 优先使用用户指定的路径\n if (userAgentPath && existsSync(userAgentPath)) {\n return userAgentPath;\n }\n\n // 2. 尝试通过 which 命令在 PATH 中查找\n try {\n const { stdout } = await execAsync(\"which agent\");\n const pathFromWhich = stdout.trim();\n if (pathFromWhich && existsSync(pathFromWhich)) {\n return pathFromWhich;\n }\n } catch (error) {\n // ignore error, continue to next method\n }\n\n // 3. 检查常见安装路径\n for (const path of COMMON_AGENT_PATHS) {\n if (existsSync(path)) {\n return path;\n }\n }\n\n return null;\n}\n\nexport async function isCheckCliInstall(\n options: CheckCliInstallOptions\n): Promise<BeLinkReviewEnsureResult> {\n const { silent = false, agentPath: userAgentPath } = options;\n\n let actualAgentPath = await findAgentExecutable(userAgentPath);\n\n if (actualAgentPath) {\n if (!silent) {\n console.log(\n `[review-mark] Cursor CLI (agent) 已在 ${actualAgentPath} 找到。`\n );\n }\n return { isInstalled: true, message: \"Cursor CLI 已安装\", actualAgentPath };\n }\n\n if (!silent) {\n console.log(\"[review-mark] Cursor CLI (agent) 未找到,正在尝试安装...\");\n console.log(\n \"[review-mark] 执行安装命令: curl https://cursor.com/install -fsS | bash\"\n );\n }\n\n return new Promise((resolve, reject) => {\n const installProcess = spawn(\n \"bash\",\n [\"-c\", \"curl https://cursor.com/install -fsS | bash\"],\n {\n stdio: \"inherit\", // 将安装过程的输出直接显示给用户\n }\n );\n\n installProcess.on(\"close\", async (code) => {\n if (code === 0) {\n // 安装完成后自动配置 PATH(写入 rc 文件 + 注入当前进程)\n setupLocalBinPath(silent);\n\n // 再次检查 agent 命令是否可用\n actualAgentPath = await findAgentExecutable(userAgentPath);\n if (actualAgentPath) {\n if (!silent) {\n console.log(\"[review-mark] Cursor CLI 安装成功。\");\n }\n resolve({\n isInstalled: true,\n message: \"Cursor CLI 安装成功\",\n actualAgentPath,\n });\n } else {\n reject(\n new Error(\n `[review-mark] Cursor CLI 安装命令执行成功,但未找到 agent 可执行文件。请手动检查安装:curl https://cursor.com/install -fsS | bash。`\n )\n );\n }\n } else {\n reject(\n new Error(\n `[review-mark] Cursor CLI 安装失败,退出码 ${code}。请手动安装:curl https://cursor.com/install -fsS | bash。`\n )\n );\n }\n });\n\n installProcess.on(\"error\", (err) => {\n reject(\n new Error(\n `[review-mark] 无法启动安装进程:${err.message}。请手动安装:curl https://cursor.com/install -fsS | bash`\n )\n );\n });\n });\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\n// 固定的忽略文件模式,这些文件将永远不会被审查\nconst FIXED_IGNORE_PATTERNS = [\"package.json\"];\n\nasync function runGit(args: string[], cwd: string): Promise<string> {\n const { stdout } = await execFileAsync(\"git\", args, { cwd });\n return stdout.trim();\n}\n\nexport async function getGitDiff(\n userIgnorePatterns: string[] = [],\n cwd: string = process.cwd()\n): Promise<string> {\n // 检查是否在 git 仓库中\n try {\n await runGit([\"rev-parse\", \"--git-dir\"], cwd);\n } catch (error) {\n console.error(`[review-mark] 当前目录不是 git 仓库: ${cwd}`);\n return \"\";\n }\n\n // 合并固定忽略模式和用户提供的忽略模式\n const allIgnorePatterns = [\n ...new Set([...FIXED_IGNORE_PATTERNS, ...userIgnorePatterns]),\n ];\n // 使用 execFile 直接传参,完全绕过 shell,:(exclude) 不会被解析\n // git 参数顺序:git diff [options] [commit] -- [pathspec]\n // options/commit 必须在 -- 和 pathspec 之前\n const excludeArgs = allIgnorePatterns.map((p) => `:(exclude)${p}`);\n\n let diff = \"\";\n\n try {\n // 优先使用 git diff --cached 获取暂存区的改动\n diff = await runGit(\n [\"diff\", \"--no-color\", \"--relative\", \"--cached\", \"--\", ...excludeArgs],\n cwd\n );\n if (diff) {\n console.log(\"[review-mark] 检测到暂存区改动\");\n }\n } catch (error: any) {\n console.warn(`[review-mark] 获取暂存区 diff 失败: ${error.message}`);\n }\n\n if (!diff) {\n try {\n // 如果暂存区没有改动,则获取工作区和 HEAD 的改动\n diff = await runGit(\n [\"diff\", \"--no-color\", \"--relative\", \"HEAD\", \"--\", ...excludeArgs],\n cwd\n );\n if (diff) {\n console.log(\"[review-mark] 检测到工作区改动(相对于 HEAD)\");\n }\n } catch (error: any) {\n console.warn(`[review-mark] 获取工作区 diff 失败: ${error.message}`);\n }\n }\n\n if (!diff) {\n console.log(\"[review-mark] 未检测到代码改动(已检查暂存区和工作区)\");\n }\n\n return diff;\n}\n","\nexport function generateAIPrompt(diff: string): string {\n return `你是一名资深软件工程师,请 review 以下代码变更。\nGit diff:\n${diff}\n请分析:\n是否存在 bug\n是否存在潜在逻辑问题\n是否存在性能问题\n是否存在代码风格问题\n给出优化建议`;\n}\n","import * as lark from \"@larksuiteoapi/node-sdk\";\nimport {\n appId,\n appSecret,\n receiveId,\n receiveIdType,\n messageType,\n messageTitle,\n} from \"../constants\";\n\n/**\n * 发送 Code Review 结果到飞书\n * 使用飞书官方 Node.js SDK,所有配置从 constants.ts 读取\n */\nexport async function sendReviewToFeishu(reviewContent: string): Promise<void> {\n console.log(\"[review-mark] 正在发送消息到飞书...\");\n console.log(`[review-mark] 消息类型: ${messageType}`);\n\n try {\n // 初始化飞书客户端\n const client = new lark.Client({\n appId,\n appSecret,\n domain: lark.Domain.Feishu,\n });\n\n // 根据消息类型构造消息内容\n let msgContent: string;\n let msgType: string;\n\n if (messageType === \"interactive\") {\n // Interactive 卡片消息(支持 Markdown 渲染)\n msgContent = JSON.stringify({\n config: {\n wide_screen_mode: true,\n },\n header: {\n title: {\n tag: \"plain_text\",\n content: messageTitle,\n },\n template: \"blue\",\n },\n elements: [\n {\n tag: \"div\",\n text: {\n tag: \"lark_md\",\n content: reviewContent,\n },\n },\n {\n tag: \"hr\",\n },\n {\n tag: \"note\",\n elements: [\n {\n tag: \"plain_text\",\n content: `生成时间: ${new Date().toLocaleString(\"zh-CN\", {\n timeZone: \"Asia/Shanghai\",\n })}`,\n },\n ],\n },\n ],\n });\n msgType = \"interactive\";\n } else if (messageType === \"post\") {\n // Post 富文本消息\n msgContent = JSON.stringify({\n zh_cn: {\n title: messageTitle,\n content: convertMarkdownToFeishuPost(reviewContent),\n },\n });\n msgType = \"post\";\n } else if (messageType === \"text\") {\n // Text 纯文本消息\n msgContent = JSON.stringify({\n text: `${messageTitle}\\n\\n${reviewContent}`,\n });\n msgType = \"text\";\n } else {\n throw new Error(`[review-mark] 不支持的消息类型: ${messageType}`);\n }\n\n // 调试日志\n console.log(`[review-mark] 发送参数:`);\n console.log(` - receive_id_type: ${receiveIdType}`);\n console.log(` - receive_id: ${receiveId}`);\n console.log(` - msg_type: ${msgType}`);\n\n // 调用发送消息接口\n const response = await client.im.message.create({\n params: {\n receive_id_type: receiveIdType,\n },\n data: {\n receive_id: receiveId,\n msg_type: msgType,\n content: msgContent,\n },\n });\n\n if (response.code !== 0) {\n throw new Error(\n `[review-mark] 飞书 API 返回错误: ${response.msg || \"未知错误\"}`\n );\n }\n\n console.log(\"[review-mark] ✅ 飞书消息发送成功\");\n console.log(\n `[review-mark] 消息 ID: ${response.data?.message_id || \"未知\"}`\n );\n } catch (error: any) {\n console.error(`[review-mark] ❌ 飞书消息发送失败: ${error.message}`);\n throw error;\n }\n}\n\n/**\n * 将 Markdown 格式转换为飞书 post 格式\n * 飞书 post 格式支持富文本样式\n */\nfunction convertMarkdownToFeishuPost(markdown: string): any[][] {\n const lines = markdown.split(\"\\n\");\n const result: any[][] = [];\n\n let inCodeBlock = false;\n let codeBlockContent: string[] = [];\n\n for (const line of lines) {\n // 处理代码块\n if (line.startsWith(\"```\")) {\n if (inCodeBlock) {\n // 代码块结束\n if (codeBlockContent.length > 0) {\n result.push([\n {\n tag: \"text\",\n text: codeBlockContent.join(\"\\n\"),\n style: [\"code\"],\n },\n ]);\n codeBlockContent = [];\n }\n inCodeBlock = false;\n } else {\n // 代码块开始\n inCodeBlock = true;\n }\n continue;\n }\n\n if (inCodeBlock) {\n codeBlockContent.push(line);\n continue;\n }\n\n // 空行\n if (!line.trim()) {\n result.push([{ tag: \"text\", text: \"\" }]);\n continue;\n }\n\n // 标题(加粗 + 加大)\n if (line.startsWith(\"#\")) {\n const level = line.match(/^#+/)?.[0].length || 1;\n const text = line.replace(/^#+\\s*/, \"\");\n result.push([\n {\n tag: \"text\",\n text: text,\n style: level <= 2 ? [\"bold\", \"underline\"] : [\"bold\"],\n },\n ]);\n continue;\n }\n\n // 解析行内样式(加粗、链接等)\n const parsedLine = parseLineStyles(line);\n result.push(parsedLine);\n }\n\n // 如果还在代码块中,添加剩余内容\n if (codeBlockContent.length > 0) {\n result.push([\n {\n tag: \"text\",\n text: codeBlockContent.join(\"\\n\"),\n style: [\"code\"],\n },\n ]);\n }\n\n return result;\n}\n\n/**\n * 解析行内样式(加粗、链接、代码等)\n */\nfunction parseLineStyles(line: string): any[] {\n const elements: any[] = [];\n let currentText = \"\";\n let i = 0;\n\n while (i < line.length) {\n // 处理加粗 **text**\n if (line[i] === \"*\" && line[i + 1] === \"*\") {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const endIndex = line.indexOf(\"**\", i + 2);\n if (endIndex !== -1) {\n const boldText = line.substring(i + 2, endIndex);\n elements.push({ tag: \"text\", text: boldText, style: [\"bold\"] });\n i = endIndex + 2;\n continue;\n }\n }\n\n // 处理行内代码 `code`\n if (line[i] === \"`\" && line[i + 1] !== \"`\") {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const endIndex = line.indexOf(\"`\", i + 1);\n if (endIndex !== -1) {\n const codeText = line.substring(i + 1, endIndex);\n elements.push({\n tag: \"text\",\n text: codeText,\n style: [\"code\"],\n });\n i = endIndex + 1;\n continue;\n }\n }\n\n // 处理链接 [text](url)\n if (line[i] === \"[\") {\n const textEnd = line.indexOf(\"](\", i);\n const urlEnd = line.indexOf(\")\", textEnd + 2);\n if (textEnd !== -1 && urlEnd !== -1) {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const linkText = line.substring(i + 1, textEnd);\n const url = line.substring(textEnd + 2, urlEnd);\n elements.push({\n tag: \"a\",\n text: linkText,\n href: url,\n });\n i = urlEnd + 1;\n continue;\n }\n }\n\n currentText += line[i];\n i++;\n }\n\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n }\n\n return elements.length > 0 ? elements : [{ tag: \"text\", text: line }];\n}\n","// 默认使用的 AI 模型\nexport const DEFAULT_MODEL = \"composer-1.5\";\n\n// 飞书机器人配置(内置,不需要外部配置)\nexport const appId = \"cli_a93822da7238dbb5\";\nexport const appSecret = \"ZQdcpLUHFb4gFa8cGfrlJfVfSSyGtyzF\";\nexport const receiveId = \"oc_482b6a04f95f4206c4fa9bc61829fd17\";\nexport const receiveIdType = \"chat_id\";\nexport const messageType = \"post\"; // text | post | interactive\nexport const messageTitle = \"🔍 Code Review 结果\";\n"],"mappings":"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","names":["import_node_child_process","import_node_fs","import_node_path","import_node_child_process","import_node_util"]}
|
package/dist/index.js
CHANGED
|
@@ -204,7 +204,7 @@ ${diff}
|
|
|
204
204
|
import * as lark from "@larksuiteoapi/node-sdk";
|
|
205
205
|
|
|
206
206
|
// src/constants.ts
|
|
207
|
-
var DEFAULT_MODEL = "composer-1";
|
|
207
|
+
var DEFAULT_MODEL = "composer-1.5";
|
|
208
208
|
var appId = "cli_a93822da7238dbb5";
|
|
209
209
|
var appSecret = "ZQdcpLUHFb4gFa8cGfrlJfVfSSyGtyzF";
|
|
210
210
|
var receiveId = "oc_482b6a04f95f4206c4fa9bc61829fd17";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/BeLinkReview.ts","../src/utils/checkCli.ts","../src/core/git.ts","../src/core/prompt.ts","../src/core/feishu.ts","../src/constants.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type {\n BeLinkReviewOptions,\n BeLinkReviewChatOptions,\n BeLinkReviewEnsureResult,\n} from \"../types\";\nimport { isCheckCliInstall } from \"../utils/checkCli\";\nimport { getGitDiff } from \"./git\";\nimport { generateAIPrompt } from \"./prompt\";\nimport { sendReviewToFeishu } from \"./feishu\";\nimport { DEFAULT_MODEL } from \"../constants\";\n\nexport class BeLinkReview {\n static #instance: BeLinkReview | null = null;\n #apiKey: string | undefined;\n #agentPath: string | undefined;\n #ignorePatterns: string[] | undefined;\n #enableFeishu: boolean;\n #model: string | undefined;\n\n private constructor(\n apiKey?: string,\n agentPath?: string,\n ignorePatterns?: string[],\n enableFeishu: boolean = true,\n model?: string\n ) {\n this.#apiKey = apiKey;\n this.#agentPath = agentPath;\n this.#ignorePatterns = ignorePatterns;\n this.#enableFeishu = enableFeishu;\n this.#model = model;\n }\n\n /**\n * 初始化单例并注入参数,在项目入口调用一次即可。\n * 负责保存 apiKey 和自动写入 package.json 脚本。\n * 此方法主要用于在用户项目中设置 apiKey 和脚本,CLI 运行时会优先从环境变量或命令行参数获取。\n */\n static init(options: BeLinkReviewOptions = {}): BeLinkReview {\n if (BeLinkReview.#instance === null) {\n BeLinkReview.#instance = new BeLinkReview(\n options.apiKey,\n options.agentPath,\n options.ignore,\n options.enableFeishu ?? true,\n options.model\n );\n } else {\n BeLinkReview.#instance.#apiKey = options.apiKey;\n BeLinkReview.#instance.#agentPath = options.agentPath;\n BeLinkReview.#instance.#ignorePatterns = options.ignore;\n BeLinkReview.#instance.#enableFeishu = options.enableFeishu ?? true;\n BeLinkReview.#instance.#model = options.model;\n }\n BeLinkReview.#instance.#setupProjectScript();\n return BeLinkReview.#instance;\n }\n\n /**\n * 获取单例实例。如果未通过 init() 初始化,则尝试从环境变量 CURSOR_API_KEY 获取。\n * @param cliApiKey 可选的命令行传入的 apiKey\n * @param cliAgentPath 可选的命令行传入的 agentPath\n * @param cliIgnorePatterns 可选的命令行传入的 ignorePatterns\n * @param cliEnableFeishu 可选的命令行传入的飞书开关\n */\n static getInstance(\n cliApiKey?: string,\n cliAgentPath?: string,\n cliIgnorePatterns?: string[],\n cliEnableFeishu?: boolean,\n cliModel?: string\n ): BeLinkReview {\n if (BeLinkReview.#instance === null) {\n const apiKey = cliApiKey || process.env.CURSOR_API_KEY;\n const agentPath = cliAgentPath || process.env.CURSOR_AGENT_PATH;\n const ignorePatterns =\n cliIgnorePatterns ||\n (process.env.BE_LINK_REVIEW_IGNORE\n ? process.env.BE_LINK_REVIEW_IGNORE.split(\",\")\n : undefined);\n const enableFeishu =\n cliEnableFeishu ?? process.env.FEISHU_ENABLED !== \"false\";\n const model = cliModel || process.env.CURSOR_MODEL;\n\n if (!apiKey) {\n throw new Error(\n '[review-mark] 请先调用 BeLinkReview.init({ apiKey: \"...\" }) 初始化,或设置环境变量 CURSOR_API_KEY,或通过命令行参数 --apiKey 传入。'\n );\n }\n BeLinkReview.#instance = new BeLinkReview(\n apiKey,\n agentPath,\n ignorePatterns,\n enableFeishu,\n model\n );\n } else {\n if (cliApiKey && !BeLinkReview.#instance.#apiKey) {\n BeLinkReview.#instance.#apiKey = cliApiKey;\n }\n if (cliAgentPath && !BeLinkReview.#instance.#agentPath) {\n BeLinkReview.#instance.#agentPath = cliAgentPath;\n }\n if (cliIgnorePatterns && !BeLinkReview.#instance.#ignorePatterns) {\n BeLinkReview.#instance.#ignorePatterns = cliIgnorePatterns;\n }\n if (cliEnableFeishu !== undefined) {\n BeLinkReview.#instance.#enableFeishu = cliEnableFeishu;\n }\n if (cliModel) {\n BeLinkReview.#instance.#model = cliModel;\n }\n }\n return BeLinkReview.#instance;\n }\n\n #getApiKey(): string {\n const apiKey = this.#apiKey || process.env.CURSOR_API_KEY;\n if (!apiKey) {\n throw new Error(\n '[review-mark] 请先在 init({ apiKey: \"...\" }) 中传入 apiKey,或设置环境变量 CURSOR_API_KEY,或通过命令行参数 --apiKey 传入。'\n );\n }\n return apiKey;\n }\n\n #getAgentPath(): string | undefined {\n return this.#agentPath || process.env.CURSOR_AGENT_PATH;\n }\n\n #getIgnorePatterns(): string[] {\n const envIgnore = process.env.BE_LINK_REVIEW_IGNORE\n ? process.env.BE_LINK_REVIEW_IGNORE.split(\",\")\n : [];\n return this.#ignorePatterns || envIgnore;\n }\n\n #isFeishuEnabled(): boolean {\n return this.#enableFeishu;\n }\n\n #getModel(): string {\n return this.#model || process.env.CURSOR_MODEL || DEFAULT_MODEL;\n }\n\n /**\n * 自动往用户项目 package.json 写入 script\n */\n async #setupProjectScript() {\n try {\n const packageJsonPath = join(process.cwd(), \"package.json\");\n const packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf-8\"));\n\n if (!packageJson.scripts) {\n packageJson.scripts = {};\n }\n\n if (!packageJson.scripts[\"review-mark\"]) {\n packageJson.scripts[\"review-mark\"] = \"review-mark\";\n writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));\n console.log(\n \"[review-mark] 已在 package.json 中添加 'review-mark' 脚本。\"\n );\n } else {\n console.log(\"[review-mark] 'review-mark' 脚本已存在,跳过添加。\");\n }\n } catch (error) {\n console.error(\"[review-mark] 无法更新 package.json: \", error);\n }\n }\n\n /**\n * 执行一次完整 CLI 流程:获取 git diff → 生成 prompt → 检查/安装 CLI → 与 Cursor 对话并打印结果。\n */\n async goCli(): Promise<string> {\n const apiKey = this.#getApiKey();\n const agentPath = this.#getAgentPath();\n const ignorePatterns = this.#getIgnorePatterns();\n\n const ensureResult = await this.ensureAgentInstalled(false, agentPath);\n\n if (!ensureResult.isInstalled) {\n throw new Error(\n \"[review-mark] Cursor CLI 未安装且自动安装失败,请手动安装。\"\n );\n }\n\n console.log(\"[review-mark] Getting git diff...\");\n const diff = await getGitDiff(ignorePatterns, process.cwd());\n\n if (!diff) {\n console.log(\"[review-mark] No code changes detected\");\n return \"No code changes detected\";\n }\n\n const prompt = generateAIPrompt(diff);\n console.log(\"[review-mark] Sending to AI...\");\n const response = await this.chat(prompt, {\n agentPath: ensureResult.actualAgentPath,\n force: true,\n model: this.#getModel(),\n });\n console.log(this.#getModel(), \"model\");\n console.log(\"===== AI Review =====\");\n console.log(response);\n\n // 如果启用了飞书,发送到飞书\n if (this.#isFeishuEnabled()) {\n try {\n await sendReviewToFeishu(response);\n } catch (error: any) {\n console.error(\n `[review-mark] 飞书通知发送失败,但不影响 review 结果: ${error.message}`\n );\n }\n }\n\n return response;\n }\n\n /**\n * 检查 Cursor CLI 是否已安装;未安装则自动安装(需网络)。\n * @param silent 为 true 时不打印「已安装」等提示\n * @param agentPath 可选的 agent 可执行文件路径\n */\n async ensureAgentInstalled(\n silent = false,\n agentPath?: string\n ): Promise<BeLinkReviewEnsureResult> {\n return isCheckCliInstall({ apiKey: this.#getApiKey(), silent, agentPath });\n }\n\n /**\n * 通过 Cursor Headless CLI 与 Cursor 对话(发送 prompt,拿到回复)。\n * 默认会先执行 ensureAgentInstalled(检查/安装 CLI),再启动 agent 执行提问。\n * 参考:https://cursor.com/cn/docs/cli/headless\n */\n async chat(\n prompt: string,\n options: BeLinkReviewChatOptions = {}\n ): Promise<string> {\n const actualAgentPath =\n options.agentPath || this.#getAgentPath() || \"agent\";\n const model = options.model || this.#getModel();\n const args: string[] = [\"--yolo\", \"-p\", prompt];\n if (model) args.unshift(\"--model\", model);\n if (options.outputFormat === \"json\") args.push(\"--output-format\", \"json\");\n\n return new Promise((resolve, reject) => {\n const env = { ...process.env, CURSOR_API_KEY: this.#getApiKey() };\n const proc = spawn(actualAgentPath, args, {\n env,\n cwd: process.cwd(),\n shell: false,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n proc.stdout?.on(\"data\", (chunk) => {\n stdout += chunk.toString();\n });\n proc.stderr?.on(\"data\", (chunk) => {\n stderr += chunk.toString();\n });\n\n proc.on(\"error\", (err) => {\n reject(\n new Error(\n `[review-mark] 无法启动 Cursor CLI (${actualAgentPath}),请先安装:curl https://cursor.com/install -fsS | bash。原始错误: ${err.message}`\n )\n );\n });\n\n proc.on(\"close\", (code) => {\n if (code !== 0) {\n reject(\n new Error(\n `[review-mark] agent 退出码 ${code}${\n stderr ? `: ${stderr.trim()}` : \"\"\n }`\n )\n );\n return;\n }\n resolve(stdout.trim());\n });\n });\n }\n}\n","import { spawn, exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { existsSync, appendFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { BeLinkReviewEnsureResult } from \"@/types\";\n\nconst execAsync = promisify(exec);\n\ninterface CheckCliInstallOptions {\n apiKey: string;\n silent?: boolean;\n agentPath?: string;\n}\n\n// Cursor 官方安装脚本默认将 agent 放在 ~/.local/bin\nconst LOCAL_BIN = join(homedir(), \".local\", \"bin\");\n\n// 常见的 Cursor CLI 安装路径\nconst COMMON_AGENT_PATHS = [\n join(LOCAL_BIN, \"agent\"), // Cursor 官方安装脚本默认路径\n join(homedir(), \".cursor\", \"agent\"),\n \"/usr/local/bin/agent\",\n \"/opt/homebrew/bin/agent\",\n];\n\n/**\n * 将 ~/.local/bin 写入用户 shell 配置文件(.zshrc / .bashrc),\n * 并立即注入当前进程的 PATH,确保安装后无需重启终端即可使用。\n */\nfunction setupLocalBinPath(silent: boolean): void {\n // 立即让当前进程能找到 agent\n if (!process.env.PATH?.includes(LOCAL_BIN)) {\n process.env.PATH = `${LOCAL_BIN}:${process.env.PATH}`;\n }\n\n const exportLine = `\\nexport PATH=\"$HOME/.local/bin:$PATH\"`;\n const shell = process.env.SHELL ?? \"\";\n const rcFiles: string[] = [];\n\n if (shell.includes(\"zsh\")) {\n rcFiles.push(join(homedir(), \".zshrc\"));\n } else if (shell.includes(\"bash\")) {\n rcFiles.push(join(homedir(), \".bashrc\"));\n } else {\n // 两个都写,保证覆盖\n rcFiles.push(join(homedir(), \".zshrc\"), join(homedir(), \".bashrc\"));\n }\n\n for (const rc of rcFiles) {\n try {\n const content = existsSync(rc)\n ? require(\"node:fs\").readFileSync(rc, \"utf-8\")\n : \"\";\n if (!content.includes(\".local/bin\")) {\n appendFileSync(rc, exportLine);\n if (!silent) {\n console.log(`[review-mark] 已将 ~/.local/bin 写入 ${rc}`);\n }\n }\n } catch {\n // 写入失败不阻断流程\n }\n }\n}\n\nasync function findAgentExecutable(\n userAgentPath?: string\n): Promise<string | null> {\n // 1. 优先使用用户指定的路径\n if (userAgentPath && existsSync(userAgentPath)) {\n return userAgentPath;\n }\n\n // 2. 尝试通过 which 命令在 PATH 中查找\n try {\n const { stdout } = await execAsync(\"which agent\");\n const pathFromWhich = stdout.trim();\n if (pathFromWhich && existsSync(pathFromWhich)) {\n return pathFromWhich;\n }\n } catch (error) {\n // ignore error, continue to next method\n }\n\n // 3. 检查常见安装路径\n for (const path of COMMON_AGENT_PATHS) {\n if (existsSync(path)) {\n return path;\n }\n }\n\n return null;\n}\n\nexport async function isCheckCliInstall(\n options: CheckCliInstallOptions\n): Promise<BeLinkReviewEnsureResult> {\n const { silent = false, agentPath: userAgentPath } = options;\n\n let actualAgentPath = await findAgentExecutable(userAgentPath);\n\n if (actualAgentPath) {\n if (!silent) {\n console.log(\n `[review-mark] Cursor CLI (agent) 已在 ${actualAgentPath} 找到。`\n );\n }\n return { isInstalled: true, message: \"Cursor CLI 已安装\", actualAgentPath };\n }\n\n if (!silent) {\n console.log(\"[review-mark] Cursor CLI (agent) 未找到,正在尝试安装...\");\n console.log(\n \"[review-mark] 执行安装命令: curl https://cursor.com/install -fsS | bash\"\n );\n }\n\n return new Promise((resolve, reject) => {\n const installProcess = spawn(\n \"bash\",\n [\"-c\", \"curl https://cursor.com/install -fsS | bash\"],\n {\n stdio: \"inherit\", // 将安装过程的输出直接显示给用户\n }\n );\n\n installProcess.on(\"close\", async (code) => {\n if (code === 0) {\n // 安装完成后自动配置 PATH(写入 rc 文件 + 注入当前进程)\n setupLocalBinPath(silent);\n\n // 再次检查 agent 命令是否可用\n actualAgentPath = await findAgentExecutable(userAgentPath);\n if (actualAgentPath) {\n if (!silent) {\n console.log(\"[review-mark] Cursor CLI 安装成功。\");\n }\n resolve({\n isInstalled: true,\n message: \"Cursor CLI 安装成功\",\n actualAgentPath,\n });\n } else {\n reject(\n new Error(\n `[review-mark] Cursor CLI 安装命令执行成功,但未找到 agent 可执行文件。请手动检查安装:curl https://cursor.com/install -fsS | bash。`\n )\n );\n }\n } else {\n reject(\n new Error(\n `[review-mark] Cursor CLI 安装失败,退出码 ${code}。请手动安装:curl https://cursor.com/install -fsS | bash。`\n )\n );\n }\n });\n\n installProcess.on(\"error\", (err) => {\n reject(\n new Error(\n `[review-mark] 无法启动安装进程:${err.message}。请手动安装:curl https://cursor.com/install -fsS | bash`\n )\n );\n });\n });\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\n// 固定的忽略文件模式,这些文件将永远不会被审查\nconst FIXED_IGNORE_PATTERNS = [\"package.json\"];\n\nasync function runGit(args: string[], cwd: string): Promise<string> {\n const { stdout } = await execFileAsync(\"git\", args, { cwd });\n return stdout.trim();\n}\n\nexport async function getGitDiff(\n userIgnorePatterns: string[] = [],\n cwd: string = process.cwd()\n): Promise<string> {\n // 检查是否在 git 仓库中\n try {\n await runGit([\"rev-parse\", \"--git-dir\"], cwd);\n } catch (error) {\n console.error(`[review-mark] 当前目录不是 git 仓库: ${cwd}`);\n return \"\";\n }\n\n // 合并固定忽略模式和用户提供的忽略模式\n const allIgnorePatterns = [\n ...new Set([...FIXED_IGNORE_PATTERNS, ...userIgnorePatterns]),\n ];\n // 使用 execFile 直接传参,完全绕过 shell,:(exclude) 不会被解析\n // git 参数顺序:git diff [options] [commit] -- [pathspec]\n // options/commit 必须在 -- 和 pathspec 之前\n const excludeArgs = allIgnorePatterns.map((p) => `:(exclude)${p}`);\n\n let diff = \"\";\n\n try {\n // 优先使用 git diff --cached 获取暂存区的改动\n diff = await runGit(\n [\"diff\", \"--no-color\", \"--relative\", \"--cached\", \"--\", ...excludeArgs],\n cwd\n );\n if (diff) {\n console.log(\"[review-mark] 检测到暂存区改动\");\n }\n } catch (error: any) {\n console.warn(`[review-mark] 获取暂存区 diff 失败: ${error.message}`);\n }\n\n if (!diff) {\n try {\n // 如果暂存区没有改动,则获取工作区和 HEAD 的改动\n diff = await runGit(\n [\"diff\", \"--no-color\", \"--relative\", \"HEAD\", \"--\", ...excludeArgs],\n cwd\n );\n if (diff) {\n console.log(\"[review-mark] 检测到工作区改动(相对于 HEAD)\");\n }\n } catch (error: any) {\n console.warn(`[review-mark] 获取工作区 diff 失败: ${error.message}`);\n }\n }\n\n if (!diff) {\n console.log(\"[review-mark] 未检测到代码改动(已检查暂存区和工作区)\");\n }\n\n return diff;\n}\n","\nexport function generateAIPrompt(diff: string): string {\n return `你是一名资深软件工程师,请 review 以下代码变更。\nGit diff:\n${diff}\n请分析:\n是否存在 bug\n是否存在潜在逻辑问题\n是否存在性能问题\n是否存在代码风格问题\n给出优化建议`;\n}\n","import * as lark from \"@larksuiteoapi/node-sdk\";\nimport {\n appId,\n appSecret,\n receiveId,\n receiveIdType,\n messageType,\n messageTitle,\n} from \"../constants\";\n\n/**\n * 发送 Code Review 结果到飞书\n * 使用飞书官方 Node.js SDK,所有配置从 constants.ts 读取\n */\nexport async function sendReviewToFeishu(reviewContent: string): Promise<void> {\n console.log(\"[review-mark] 正在发送消息到飞书...\");\n console.log(`[review-mark] 消息类型: ${messageType}`);\n\n try {\n // 初始化飞书客户端\n const client = new lark.Client({\n appId,\n appSecret,\n domain: lark.Domain.Feishu,\n });\n\n // 根据消息类型构造消息内容\n let msgContent: string;\n let msgType: string;\n\n if (messageType === \"interactive\") {\n // Interactive 卡片消息(支持 Markdown 渲染)\n msgContent = JSON.stringify({\n config: {\n wide_screen_mode: true,\n },\n header: {\n title: {\n tag: \"plain_text\",\n content: messageTitle,\n },\n template: \"blue\",\n },\n elements: [\n {\n tag: \"div\",\n text: {\n tag: \"lark_md\",\n content: reviewContent,\n },\n },\n {\n tag: \"hr\",\n },\n {\n tag: \"note\",\n elements: [\n {\n tag: \"plain_text\",\n content: `生成时间: ${new Date().toLocaleString(\"zh-CN\", {\n timeZone: \"Asia/Shanghai\",\n })}`,\n },\n ],\n },\n ],\n });\n msgType = \"interactive\";\n } else if (messageType === \"post\") {\n // Post 富文本消息\n msgContent = JSON.stringify({\n zh_cn: {\n title: messageTitle,\n content: convertMarkdownToFeishuPost(reviewContent),\n },\n });\n msgType = \"post\";\n } else if (messageType === \"text\") {\n // Text 纯文本消息\n msgContent = JSON.stringify({\n text: `${messageTitle}\\n\\n${reviewContent}`,\n });\n msgType = \"text\";\n } else {\n throw new Error(`[review-mark] 不支持的消息类型: ${messageType}`);\n }\n\n // 调试日志\n console.log(`[review-mark] 发送参数:`);\n console.log(` - receive_id_type: ${receiveIdType}`);\n console.log(` - receive_id: ${receiveId}`);\n console.log(` - msg_type: ${msgType}`);\n\n // 调用发送消息接口\n const response = await client.im.message.create({\n params: {\n receive_id_type: receiveIdType,\n },\n data: {\n receive_id: receiveId,\n msg_type: msgType,\n content: msgContent,\n },\n });\n\n if (response.code !== 0) {\n throw new Error(\n `[review-mark] 飞书 API 返回错误: ${response.msg || \"未知错误\"}`\n );\n }\n\n console.log(\"[review-mark] ✅ 飞书消息发送成功\");\n console.log(\n `[review-mark] 消息 ID: ${response.data?.message_id || \"未知\"}`\n );\n } catch (error: any) {\n console.error(`[review-mark] ❌ 飞书消息发送失败: ${error.message}`);\n throw error;\n }\n}\n\n/**\n * 将 Markdown 格式转换为飞书 post 格式\n * 飞书 post 格式支持富文本样式\n */\nfunction convertMarkdownToFeishuPost(markdown: string): any[][] {\n const lines = markdown.split(\"\\n\");\n const result: any[][] = [];\n\n let inCodeBlock = false;\n let codeBlockContent: string[] = [];\n\n for (const line of lines) {\n // 处理代码块\n if (line.startsWith(\"```\")) {\n if (inCodeBlock) {\n // 代码块结束\n if (codeBlockContent.length > 0) {\n result.push([\n {\n tag: \"text\",\n text: codeBlockContent.join(\"\\n\"),\n style: [\"code\"],\n },\n ]);\n codeBlockContent = [];\n }\n inCodeBlock = false;\n } else {\n // 代码块开始\n inCodeBlock = true;\n }\n continue;\n }\n\n if (inCodeBlock) {\n codeBlockContent.push(line);\n continue;\n }\n\n // 空行\n if (!line.trim()) {\n result.push([{ tag: \"text\", text: \"\" }]);\n continue;\n }\n\n // 标题(加粗 + 加大)\n if (line.startsWith(\"#\")) {\n const level = line.match(/^#+/)?.[0].length || 1;\n const text = line.replace(/^#+\\s*/, \"\");\n result.push([\n {\n tag: \"text\",\n text: text,\n style: level <= 2 ? [\"bold\", \"underline\"] : [\"bold\"],\n },\n ]);\n continue;\n }\n\n // 解析行内样式(加粗、链接等)\n const parsedLine = parseLineStyles(line);\n result.push(parsedLine);\n }\n\n // 如果还在代码块中,添加剩余内容\n if (codeBlockContent.length > 0) {\n result.push([\n {\n tag: \"text\",\n text: codeBlockContent.join(\"\\n\"),\n style: [\"code\"],\n },\n ]);\n }\n\n return result;\n}\n\n/**\n * 解析行内样式(加粗、链接、代码等)\n */\nfunction parseLineStyles(line: string): any[] {\n const elements: any[] = [];\n let currentText = \"\";\n let i = 0;\n\n while (i < line.length) {\n // 处理加粗 **text**\n if (line[i] === \"*\" && line[i + 1] === \"*\") {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const endIndex = line.indexOf(\"**\", i + 2);\n if (endIndex !== -1) {\n const boldText = line.substring(i + 2, endIndex);\n elements.push({ tag: \"text\", text: boldText, style: [\"bold\"] });\n i = endIndex + 2;\n continue;\n }\n }\n\n // 处理行内代码 `code`\n if (line[i] === \"`\" && line[i + 1] !== \"`\") {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const endIndex = line.indexOf(\"`\", i + 1);\n if (endIndex !== -1) {\n const codeText = line.substring(i + 1, endIndex);\n elements.push({\n tag: \"text\",\n text: codeText,\n style: [\"code\"],\n });\n i = endIndex + 1;\n continue;\n }\n }\n\n // 处理链接 [text](url)\n if (line[i] === \"[\") {\n const textEnd = line.indexOf(\"](\", i);\n const urlEnd = line.indexOf(\")\", textEnd + 2);\n if (textEnd !== -1 && urlEnd !== -1) {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const linkText = line.substring(i + 1, textEnd);\n const url = line.substring(textEnd + 2, urlEnd);\n elements.push({\n tag: \"a\",\n text: linkText,\n href: url,\n });\n i = urlEnd + 1;\n continue;\n }\n }\n\n currentText += line[i];\n i++;\n }\n\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n }\n\n return elements.length > 0 ? elements : [{ tag: \"text\", text: line }];\n}\n","// 默认使用的 AI 模型\nexport const DEFAULT_MODEL = \"composer-1\";\n\n// 飞书机器人配置(内置,不需要外部配置)\nexport const appId = \"cli_a93822da7238dbb5\";\nexport const appSecret = \"ZQdcpLUHFb4gFa8cGfrlJfVfSSyGtyzF\";\nexport const receiveId = \"oc_482b6a04f95f4206c4fa9bc61829fd17\";\nexport const receiveIdType = \"chat_id\";\nexport const messageType = \"post\"; // text | post | interactive\nexport const messageTitle = \"🔍 Code Review 结果\";\n"],"mappings":"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","names":["spawn","join","promisify","join","spawn"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/BeLinkReview.ts","../src/utils/checkCli.ts","../src/core/git.ts","../src/core/prompt.ts","../src/core/feishu.ts","../src/constants.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type {\n BeLinkReviewOptions,\n BeLinkReviewChatOptions,\n BeLinkReviewEnsureResult,\n} from \"../types\";\nimport { isCheckCliInstall } from \"../utils/checkCli\";\nimport { getGitDiff } from \"./git\";\nimport { generateAIPrompt } from \"./prompt\";\nimport { sendReviewToFeishu } from \"./feishu\";\nimport { DEFAULT_MODEL } from \"../constants\";\n\nexport class BeLinkReview {\n static #instance: BeLinkReview | null = null;\n #apiKey: string | undefined;\n #agentPath: string | undefined;\n #ignorePatterns: string[] | undefined;\n #enableFeishu: boolean;\n #model: string | undefined;\n\n private constructor(\n apiKey?: string,\n agentPath?: string,\n ignorePatterns?: string[],\n enableFeishu: boolean = true,\n model?: string\n ) {\n this.#apiKey = apiKey;\n this.#agentPath = agentPath;\n this.#ignorePatterns = ignorePatterns;\n this.#enableFeishu = enableFeishu;\n this.#model = model;\n }\n\n /**\n * 初始化单例并注入参数,在项目入口调用一次即可。\n * 负责保存 apiKey 和自动写入 package.json 脚本。\n * 此方法主要用于在用户项目中设置 apiKey 和脚本,CLI 运行时会优先从环境变量或命令行参数获取。\n */\n static init(options: BeLinkReviewOptions = {}): BeLinkReview {\n if (BeLinkReview.#instance === null) {\n BeLinkReview.#instance = new BeLinkReview(\n options.apiKey,\n options.agentPath,\n options.ignore,\n options.enableFeishu ?? true,\n options.model\n );\n } else {\n BeLinkReview.#instance.#apiKey = options.apiKey;\n BeLinkReview.#instance.#agentPath = options.agentPath;\n BeLinkReview.#instance.#ignorePatterns = options.ignore;\n BeLinkReview.#instance.#enableFeishu = options.enableFeishu ?? true;\n BeLinkReview.#instance.#model = options.model;\n }\n BeLinkReview.#instance.#setupProjectScript();\n return BeLinkReview.#instance;\n }\n\n /**\n * 获取单例实例。如果未通过 init() 初始化,则尝试从环境变量 CURSOR_API_KEY 获取。\n * @param cliApiKey 可选的命令行传入的 apiKey\n * @param cliAgentPath 可选的命令行传入的 agentPath\n * @param cliIgnorePatterns 可选的命令行传入的 ignorePatterns\n * @param cliEnableFeishu 可选的命令行传入的飞书开关\n */\n static getInstance(\n cliApiKey?: string,\n cliAgentPath?: string,\n cliIgnorePatterns?: string[],\n cliEnableFeishu?: boolean,\n cliModel?: string\n ): BeLinkReview {\n if (BeLinkReview.#instance === null) {\n const apiKey = cliApiKey || process.env.CURSOR_API_KEY;\n const agentPath = cliAgentPath || process.env.CURSOR_AGENT_PATH;\n const ignorePatterns =\n cliIgnorePatterns ||\n (process.env.BE_LINK_REVIEW_IGNORE\n ? process.env.BE_LINK_REVIEW_IGNORE.split(\",\")\n : undefined);\n const enableFeishu =\n cliEnableFeishu ?? process.env.FEISHU_ENABLED !== \"false\";\n const model = cliModel || process.env.CURSOR_MODEL;\n\n if (!apiKey) {\n throw new Error(\n '[review-mark] 请先调用 BeLinkReview.init({ apiKey: \"...\" }) 初始化,或设置环境变量 CURSOR_API_KEY,或通过命令行参数 --apiKey 传入。'\n );\n }\n BeLinkReview.#instance = new BeLinkReview(\n apiKey,\n agentPath,\n ignorePatterns,\n enableFeishu,\n model\n );\n } else {\n if (cliApiKey && !BeLinkReview.#instance.#apiKey) {\n BeLinkReview.#instance.#apiKey = cliApiKey;\n }\n if (cliAgentPath && !BeLinkReview.#instance.#agentPath) {\n BeLinkReview.#instance.#agentPath = cliAgentPath;\n }\n if (cliIgnorePatterns && !BeLinkReview.#instance.#ignorePatterns) {\n BeLinkReview.#instance.#ignorePatterns = cliIgnorePatterns;\n }\n if (cliEnableFeishu !== undefined) {\n BeLinkReview.#instance.#enableFeishu = cliEnableFeishu;\n }\n if (cliModel) {\n BeLinkReview.#instance.#model = cliModel;\n }\n }\n return BeLinkReview.#instance;\n }\n\n #getApiKey(): string {\n const apiKey = this.#apiKey || process.env.CURSOR_API_KEY;\n if (!apiKey) {\n throw new Error(\n '[review-mark] 请先在 init({ apiKey: \"...\" }) 中传入 apiKey,或设置环境变量 CURSOR_API_KEY,或通过命令行参数 --apiKey 传入。'\n );\n }\n return apiKey;\n }\n\n #getAgentPath(): string | undefined {\n return this.#agentPath || process.env.CURSOR_AGENT_PATH;\n }\n\n #getIgnorePatterns(): string[] {\n const envIgnore = process.env.BE_LINK_REVIEW_IGNORE\n ? process.env.BE_LINK_REVIEW_IGNORE.split(\",\")\n : [];\n return this.#ignorePatterns || envIgnore;\n }\n\n #isFeishuEnabled(): boolean {\n return this.#enableFeishu;\n }\n\n #getModel(): string {\n return this.#model || process.env.CURSOR_MODEL || DEFAULT_MODEL;\n }\n\n /**\n * 自动往用户项目 package.json 写入 script\n */\n async #setupProjectScript() {\n try {\n const packageJsonPath = join(process.cwd(), \"package.json\");\n const packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf-8\"));\n\n if (!packageJson.scripts) {\n packageJson.scripts = {};\n }\n\n if (!packageJson.scripts[\"review-mark\"]) {\n packageJson.scripts[\"review-mark\"] = \"review-mark\";\n writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));\n console.log(\n \"[review-mark] 已在 package.json 中添加 'review-mark' 脚本。\"\n );\n } else {\n console.log(\"[review-mark] 'review-mark' 脚本已存在,跳过添加。\");\n }\n } catch (error) {\n console.error(\"[review-mark] 无法更新 package.json: \", error);\n }\n }\n\n /**\n * 执行一次完整 CLI 流程:获取 git diff → 生成 prompt → 检查/安装 CLI → 与 Cursor 对话并打印结果。\n */\n async goCli(): Promise<string> {\n const apiKey = this.#getApiKey();\n const agentPath = this.#getAgentPath();\n const ignorePatterns = this.#getIgnorePatterns();\n\n const ensureResult = await this.ensureAgentInstalled(false, agentPath);\n\n if (!ensureResult.isInstalled) {\n throw new Error(\n \"[review-mark] Cursor CLI 未安装且自动安装失败,请手动安装。\"\n );\n }\n\n console.log(\"[review-mark] Getting git diff...\");\n const diff = await getGitDiff(ignorePatterns, process.cwd());\n\n if (!diff) {\n console.log(\"[review-mark] No code changes detected\");\n return \"No code changes detected\";\n }\n\n const prompt = generateAIPrompt(diff);\n console.log(\"[review-mark] Sending to AI...\");\n const response = await this.chat(prompt, {\n agentPath: ensureResult.actualAgentPath,\n force: true,\n model: this.#getModel(),\n });\n console.log(this.#getModel(), \"model\");\n console.log(\"===== AI Review =====\");\n console.log(response);\n\n // 如果启用了飞书,发送到飞书\n if (this.#isFeishuEnabled()) {\n try {\n await sendReviewToFeishu(response);\n } catch (error: any) {\n console.error(\n `[review-mark] 飞书通知发送失败,但不影响 review 结果: ${error.message}`\n );\n }\n }\n\n return response;\n }\n\n /**\n * 检查 Cursor CLI 是否已安装;未安装则自动安装(需网络)。\n * @param silent 为 true 时不打印「已安装」等提示\n * @param agentPath 可选的 agent 可执行文件路径\n */\n async ensureAgentInstalled(\n silent = false,\n agentPath?: string\n ): Promise<BeLinkReviewEnsureResult> {\n return isCheckCliInstall({ apiKey: this.#getApiKey(), silent, agentPath });\n }\n\n /**\n * 通过 Cursor Headless CLI 与 Cursor 对话(发送 prompt,拿到回复)。\n * 默认会先执行 ensureAgentInstalled(检查/安装 CLI),再启动 agent 执行提问。\n * 参考:https://cursor.com/cn/docs/cli/headless\n */\n async chat(\n prompt: string,\n options: BeLinkReviewChatOptions = {}\n ): Promise<string> {\n const actualAgentPath =\n options.agentPath || this.#getAgentPath() || \"agent\";\n const model = options.model || this.#getModel();\n const args: string[] = [\"--yolo\", \"-p\", prompt];\n if (model) args.unshift(\"--model\", model);\n if (options.outputFormat === \"json\") args.push(\"--output-format\", \"json\");\n\n return new Promise((resolve, reject) => {\n const env = { ...process.env, CURSOR_API_KEY: this.#getApiKey() };\n const proc = spawn(actualAgentPath, args, {\n env,\n cwd: process.cwd(),\n shell: false,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n proc.stdout?.on(\"data\", (chunk) => {\n stdout += chunk.toString();\n });\n proc.stderr?.on(\"data\", (chunk) => {\n stderr += chunk.toString();\n });\n\n proc.on(\"error\", (err) => {\n reject(\n new Error(\n `[review-mark] 无法启动 Cursor CLI (${actualAgentPath}),请先安装:curl https://cursor.com/install -fsS | bash。原始错误: ${err.message}`\n )\n );\n });\n\n proc.on(\"close\", (code) => {\n if (code !== 0) {\n reject(\n new Error(\n `[review-mark] agent 退出码 ${code}${\n stderr ? `: ${stderr.trim()}` : \"\"\n }`\n )\n );\n return;\n }\n resolve(stdout.trim());\n });\n });\n }\n}\n","import { spawn, exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { existsSync, appendFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { BeLinkReviewEnsureResult } from \"@/types\";\n\nconst execAsync = promisify(exec);\n\ninterface CheckCliInstallOptions {\n apiKey: string;\n silent?: boolean;\n agentPath?: string;\n}\n\n// Cursor 官方安装脚本默认将 agent 放在 ~/.local/bin\nconst LOCAL_BIN = join(homedir(), \".local\", \"bin\");\n\n// 常见的 Cursor CLI 安装路径\nconst COMMON_AGENT_PATHS = [\n join(LOCAL_BIN, \"agent\"), // Cursor 官方安装脚本默认路径\n join(homedir(), \".cursor\", \"agent\"),\n \"/usr/local/bin/agent\",\n \"/opt/homebrew/bin/agent\",\n];\n\n/**\n * 将 ~/.local/bin 写入用户 shell 配置文件(.zshrc / .bashrc),\n * 并立即注入当前进程的 PATH,确保安装后无需重启终端即可使用。\n */\nfunction setupLocalBinPath(silent: boolean): void {\n // 立即让当前进程能找到 agent\n if (!process.env.PATH?.includes(LOCAL_BIN)) {\n process.env.PATH = `${LOCAL_BIN}:${process.env.PATH}`;\n }\n\n const exportLine = `\\nexport PATH=\"$HOME/.local/bin:$PATH\"`;\n const shell = process.env.SHELL ?? \"\";\n const rcFiles: string[] = [];\n\n if (shell.includes(\"zsh\")) {\n rcFiles.push(join(homedir(), \".zshrc\"));\n } else if (shell.includes(\"bash\")) {\n rcFiles.push(join(homedir(), \".bashrc\"));\n } else {\n // 两个都写,保证覆盖\n rcFiles.push(join(homedir(), \".zshrc\"), join(homedir(), \".bashrc\"));\n }\n\n for (const rc of rcFiles) {\n try {\n const content = existsSync(rc)\n ? require(\"node:fs\").readFileSync(rc, \"utf-8\")\n : \"\";\n if (!content.includes(\".local/bin\")) {\n appendFileSync(rc, exportLine);\n if (!silent) {\n console.log(`[review-mark] 已将 ~/.local/bin 写入 ${rc}`);\n }\n }\n } catch {\n // 写入失败不阻断流程\n }\n }\n}\n\nasync function findAgentExecutable(\n userAgentPath?: string\n): Promise<string | null> {\n // 1. 优先使用用户指定的路径\n if (userAgentPath && existsSync(userAgentPath)) {\n return userAgentPath;\n }\n\n // 2. 尝试通过 which 命令在 PATH 中查找\n try {\n const { stdout } = await execAsync(\"which agent\");\n const pathFromWhich = stdout.trim();\n if (pathFromWhich && existsSync(pathFromWhich)) {\n return pathFromWhich;\n }\n } catch (error) {\n // ignore error, continue to next method\n }\n\n // 3. 检查常见安装路径\n for (const path of COMMON_AGENT_PATHS) {\n if (existsSync(path)) {\n return path;\n }\n }\n\n return null;\n}\n\nexport async function isCheckCliInstall(\n options: CheckCliInstallOptions\n): Promise<BeLinkReviewEnsureResult> {\n const { silent = false, agentPath: userAgentPath } = options;\n\n let actualAgentPath = await findAgentExecutable(userAgentPath);\n\n if (actualAgentPath) {\n if (!silent) {\n console.log(\n `[review-mark] Cursor CLI (agent) 已在 ${actualAgentPath} 找到。`\n );\n }\n return { isInstalled: true, message: \"Cursor CLI 已安装\", actualAgentPath };\n }\n\n if (!silent) {\n console.log(\"[review-mark] Cursor CLI (agent) 未找到,正在尝试安装...\");\n console.log(\n \"[review-mark] 执行安装命令: curl https://cursor.com/install -fsS | bash\"\n );\n }\n\n return new Promise((resolve, reject) => {\n const installProcess = spawn(\n \"bash\",\n [\"-c\", \"curl https://cursor.com/install -fsS | bash\"],\n {\n stdio: \"inherit\", // 将安装过程的输出直接显示给用户\n }\n );\n\n installProcess.on(\"close\", async (code) => {\n if (code === 0) {\n // 安装完成后自动配置 PATH(写入 rc 文件 + 注入当前进程)\n setupLocalBinPath(silent);\n\n // 再次检查 agent 命令是否可用\n actualAgentPath = await findAgentExecutable(userAgentPath);\n if (actualAgentPath) {\n if (!silent) {\n console.log(\"[review-mark] Cursor CLI 安装成功。\");\n }\n resolve({\n isInstalled: true,\n message: \"Cursor CLI 安装成功\",\n actualAgentPath,\n });\n } else {\n reject(\n new Error(\n `[review-mark] Cursor CLI 安装命令执行成功,但未找到 agent 可执行文件。请手动检查安装:curl https://cursor.com/install -fsS | bash。`\n )\n );\n }\n } else {\n reject(\n new Error(\n `[review-mark] Cursor CLI 安装失败,退出码 ${code}。请手动安装:curl https://cursor.com/install -fsS | bash。`\n )\n );\n }\n });\n\n installProcess.on(\"error\", (err) => {\n reject(\n new Error(\n `[review-mark] 无法启动安装进程:${err.message}。请手动安装:curl https://cursor.com/install -fsS | bash`\n )\n );\n });\n });\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\n// 固定的忽略文件模式,这些文件将永远不会被审查\nconst FIXED_IGNORE_PATTERNS = [\"package.json\"];\n\nasync function runGit(args: string[], cwd: string): Promise<string> {\n const { stdout } = await execFileAsync(\"git\", args, { cwd });\n return stdout.trim();\n}\n\nexport async function getGitDiff(\n userIgnorePatterns: string[] = [],\n cwd: string = process.cwd()\n): Promise<string> {\n // 检查是否在 git 仓库中\n try {\n await runGit([\"rev-parse\", \"--git-dir\"], cwd);\n } catch (error) {\n console.error(`[review-mark] 当前目录不是 git 仓库: ${cwd}`);\n return \"\";\n }\n\n // 合并固定忽略模式和用户提供的忽略模式\n const allIgnorePatterns = [\n ...new Set([...FIXED_IGNORE_PATTERNS, ...userIgnorePatterns]),\n ];\n // 使用 execFile 直接传参,完全绕过 shell,:(exclude) 不会被解析\n // git 参数顺序:git diff [options] [commit] -- [pathspec]\n // options/commit 必须在 -- 和 pathspec 之前\n const excludeArgs = allIgnorePatterns.map((p) => `:(exclude)${p}`);\n\n let diff = \"\";\n\n try {\n // 优先使用 git diff --cached 获取暂存区的改动\n diff = await runGit(\n [\"diff\", \"--no-color\", \"--relative\", \"--cached\", \"--\", ...excludeArgs],\n cwd\n );\n if (diff) {\n console.log(\"[review-mark] 检测到暂存区改动\");\n }\n } catch (error: any) {\n console.warn(`[review-mark] 获取暂存区 diff 失败: ${error.message}`);\n }\n\n if (!diff) {\n try {\n // 如果暂存区没有改动,则获取工作区和 HEAD 的改动\n diff = await runGit(\n [\"diff\", \"--no-color\", \"--relative\", \"HEAD\", \"--\", ...excludeArgs],\n cwd\n );\n if (diff) {\n console.log(\"[review-mark] 检测到工作区改动(相对于 HEAD)\");\n }\n } catch (error: any) {\n console.warn(`[review-mark] 获取工作区 diff 失败: ${error.message}`);\n }\n }\n\n if (!diff) {\n console.log(\"[review-mark] 未检测到代码改动(已检查暂存区和工作区)\");\n }\n\n return diff;\n}\n","\nexport function generateAIPrompt(diff: string): string {\n return `你是一名资深软件工程师,请 review 以下代码变更。\nGit diff:\n${diff}\n请分析:\n是否存在 bug\n是否存在潜在逻辑问题\n是否存在性能问题\n是否存在代码风格问题\n给出优化建议`;\n}\n","import * as lark from \"@larksuiteoapi/node-sdk\";\nimport {\n appId,\n appSecret,\n receiveId,\n receiveIdType,\n messageType,\n messageTitle,\n} from \"../constants\";\n\n/**\n * 发送 Code Review 结果到飞书\n * 使用飞书官方 Node.js SDK,所有配置从 constants.ts 读取\n */\nexport async function sendReviewToFeishu(reviewContent: string): Promise<void> {\n console.log(\"[review-mark] 正在发送消息到飞书...\");\n console.log(`[review-mark] 消息类型: ${messageType}`);\n\n try {\n // 初始化飞书客户端\n const client = new lark.Client({\n appId,\n appSecret,\n domain: lark.Domain.Feishu,\n });\n\n // 根据消息类型构造消息内容\n let msgContent: string;\n let msgType: string;\n\n if (messageType === \"interactive\") {\n // Interactive 卡片消息(支持 Markdown 渲染)\n msgContent = JSON.stringify({\n config: {\n wide_screen_mode: true,\n },\n header: {\n title: {\n tag: \"plain_text\",\n content: messageTitle,\n },\n template: \"blue\",\n },\n elements: [\n {\n tag: \"div\",\n text: {\n tag: \"lark_md\",\n content: reviewContent,\n },\n },\n {\n tag: \"hr\",\n },\n {\n tag: \"note\",\n elements: [\n {\n tag: \"plain_text\",\n content: `生成时间: ${new Date().toLocaleString(\"zh-CN\", {\n timeZone: \"Asia/Shanghai\",\n })}`,\n },\n ],\n },\n ],\n });\n msgType = \"interactive\";\n } else if (messageType === \"post\") {\n // Post 富文本消息\n msgContent = JSON.stringify({\n zh_cn: {\n title: messageTitle,\n content: convertMarkdownToFeishuPost(reviewContent),\n },\n });\n msgType = \"post\";\n } else if (messageType === \"text\") {\n // Text 纯文本消息\n msgContent = JSON.stringify({\n text: `${messageTitle}\\n\\n${reviewContent}`,\n });\n msgType = \"text\";\n } else {\n throw new Error(`[review-mark] 不支持的消息类型: ${messageType}`);\n }\n\n // 调试日志\n console.log(`[review-mark] 发送参数:`);\n console.log(` - receive_id_type: ${receiveIdType}`);\n console.log(` - receive_id: ${receiveId}`);\n console.log(` - msg_type: ${msgType}`);\n\n // 调用发送消息接口\n const response = await client.im.message.create({\n params: {\n receive_id_type: receiveIdType,\n },\n data: {\n receive_id: receiveId,\n msg_type: msgType,\n content: msgContent,\n },\n });\n\n if (response.code !== 0) {\n throw new Error(\n `[review-mark] 飞书 API 返回错误: ${response.msg || \"未知错误\"}`\n );\n }\n\n console.log(\"[review-mark] ✅ 飞书消息发送成功\");\n console.log(\n `[review-mark] 消息 ID: ${response.data?.message_id || \"未知\"}`\n );\n } catch (error: any) {\n console.error(`[review-mark] ❌ 飞书消息发送失败: ${error.message}`);\n throw error;\n }\n}\n\n/**\n * 将 Markdown 格式转换为飞书 post 格式\n * 飞书 post 格式支持富文本样式\n */\nfunction convertMarkdownToFeishuPost(markdown: string): any[][] {\n const lines = markdown.split(\"\\n\");\n const result: any[][] = [];\n\n let inCodeBlock = false;\n let codeBlockContent: string[] = [];\n\n for (const line of lines) {\n // 处理代码块\n if (line.startsWith(\"```\")) {\n if (inCodeBlock) {\n // 代码块结束\n if (codeBlockContent.length > 0) {\n result.push([\n {\n tag: \"text\",\n text: codeBlockContent.join(\"\\n\"),\n style: [\"code\"],\n },\n ]);\n codeBlockContent = [];\n }\n inCodeBlock = false;\n } else {\n // 代码块开始\n inCodeBlock = true;\n }\n continue;\n }\n\n if (inCodeBlock) {\n codeBlockContent.push(line);\n continue;\n }\n\n // 空行\n if (!line.trim()) {\n result.push([{ tag: \"text\", text: \"\" }]);\n continue;\n }\n\n // 标题(加粗 + 加大)\n if (line.startsWith(\"#\")) {\n const level = line.match(/^#+/)?.[0].length || 1;\n const text = line.replace(/^#+\\s*/, \"\");\n result.push([\n {\n tag: \"text\",\n text: text,\n style: level <= 2 ? [\"bold\", \"underline\"] : [\"bold\"],\n },\n ]);\n continue;\n }\n\n // 解析行内样式(加粗、链接等)\n const parsedLine = parseLineStyles(line);\n result.push(parsedLine);\n }\n\n // 如果还在代码块中,添加剩余内容\n if (codeBlockContent.length > 0) {\n result.push([\n {\n tag: \"text\",\n text: codeBlockContent.join(\"\\n\"),\n style: [\"code\"],\n },\n ]);\n }\n\n return result;\n}\n\n/**\n * 解析行内样式(加粗、链接、代码等)\n */\nfunction parseLineStyles(line: string): any[] {\n const elements: any[] = [];\n let currentText = \"\";\n let i = 0;\n\n while (i < line.length) {\n // 处理加粗 **text**\n if (line[i] === \"*\" && line[i + 1] === \"*\") {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const endIndex = line.indexOf(\"**\", i + 2);\n if (endIndex !== -1) {\n const boldText = line.substring(i + 2, endIndex);\n elements.push({ tag: \"text\", text: boldText, style: [\"bold\"] });\n i = endIndex + 2;\n continue;\n }\n }\n\n // 处理行内代码 `code`\n if (line[i] === \"`\" && line[i + 1] !== \"`\") {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const endIndex = line.indexOf(\"`\", i + 1);\n if (endIndex !== -1) {\n const codeText = line.substring(i + 1, endIndex);\n elements.push({\n tag: \"text\",\n text: codeText,\n style: [\"code\"],\n });\n i = endIndex + 1;\n continue;\n }\n }\n\n // 处理链接 [text](url)\n if (line[i] === \"[\") {\n const textEnd = line.indexOf(\"](\", i);\n const urlEnd = line.indexOf(\")\", textEnd + 2);\n if (textEnd !== -1 && urlEnd !== -1) {\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n currentText = \"\";\n }\n const linkText = line.substring(i + 1, textEnd);\n const url = line.substring(textEnd + 2, urlEnd);\n elements.push({\n tag: \"a\",\n text: linkText,\n href: url,\n });\n i = urlEnd + 1;\n continue;\n }\n }\n\n currentText += line[i];\n i++;\n }\n\n if (currentText) {\n elements.push({ tag: \"text\", text: currentText });\n }\n\n return elements.length > 0 ? elements : [{ tag: \"text\", text: line }];\n}\n","// 默认使用的 AI 模型\nexport const DEFAULT_MODEL = \"composer-1.5\";\n\n// 飞书机器人配置(内置,不需要外部配置)\nexport const appId = \"cli_a93822da7238dbb5\";\nexport const appSecret = \"ZQdcpLUHFb4gFa8cGfrlJfVfSSyGtyzF\";\nexport const receiveId = \"oc_482b6a04f95f4206c4fa9bc61829fd17\";\nexport const receiveIdType = \"chat_id\";\nexport const messageType = \"post\"; // text | post | interactive\nexport const messageTitle = \"🔍 Code Review 结果\";\n"],"mappings":"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","names":["spawn","join","promisify","join","spawn"]}
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED