review-mark 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -225,6 +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
229
  var appId = "cli_a93822da7238dbb5";
229
230
  var appSecret = "ZQdcpLUHFb4gFa8cGfrlJfVfSSyGtyzF";
230
231
  var receiveId = "oc_482b6a04f95f4206c4fa9bc61829fd17";
@@ -455,11 +456,13 @@ var BeLinkReview = class _BeLinkReview {
455
456
  #agentPath;
456
457
  #ignorePatterns;
457
458
  #enableFeishu;
458
- constructor(apiKey, agentPath, ignorePatterns, enableFeishu = true) {
459
+ #model;
460
+ constructor(apiKey, agentPath, ignorePatterns, enableFeishu = true, model) {
459
461
  this.#apiKey = apiKey;
460
462
  this.#agentPath = agentPath;
461
463
  this.#ignorePatterns = ignorePatterns;
462
464
  this.#enableFeishu = enableFeishu;
465
+ this.#model = model;
463
466
  }
464
467
  /**
465
468
  * 初始化单例并注入参数,在项目入口调用一次即可。
@@ -472,13 +475,15 @@ var BeLinkReview = class _BeLinkReview {
472
475
  options.apiKey,
473
476
  options.agentPath,
474
477
  options.ignore,
475
- options.enableFeishu ?? true
478
+ options.enableFeishu ?? true,
479
+ options.model
476
480
  );
477
481
  } else {
478
482
  _BeLinkReview.#instance.#apiKey = options.apiKey;
479
483
  _BeLinkReview.#instance.#agentPath = options.agentPath;
480
484
  _BeLinkReview.#instance.#ignorePatterns = options.ignore;
481
485
  _BeLinkReview.#instance.#enableFeishu = options.enableFeishu ?? true;
486
+ _BeLinkReview.#instance.#model = options.model;
482
487
  }
483
488
  _BeLinkReview.#instance.#setupProjectScript();
484
489
  return _BeLinkReview.#instance;
@@ -490,12 +495,13 @@ var BeLinkReview = class _BeLinkReview {
490
495
  * @param cliIgnorePatterns 可选的命令行传入的 ignorePatterns
491
496
  * @param cliEnableFeishu 可选的命令行传入的飞书开关
492
497
  */
493
- static getInstance(cliApiKey, cliAgentPath, cliIgnorePatterns, cliEnableFeishu) {
498
+ static getInstance(cliApiKey, cliAgentPath, cliIgnorePatterns, cliEnableFeishu, cliModel) {
494
499
  if (_BeLinkReview.#instance === null) {
495
500
  const apiKey = cliApiKey || process.env.CURSOR_API_KEY;
496
501
  const agentPath = cliAgentPath || process.env.CURSOR_AGENT_PATH;
497
502
  const ignorePatterns = cliIgnorePatterns || (process.env.BE_LINK_REVIEW_IGNORE ? process.env.BE_LINK_REVIEW_IGNORE.split(",") : void 0);
498
503
  const enableFeishu = cliEnableFeishu ?? process.env.FEISHU_ENABLED !== "false";
504
+ const model = cliModel || process.env.CURSOR_MODEL;
499
505
  if (!apiKey) {
500
506
  throw new Error(
501
507
  '[review-mark] \u8BF7\u5148\u8C03\u7528 BeLinkReview.init({ apiKey: "..." }) \u521D\u59CB\u5316\uFF0C\u6216\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF CURSOR_API_KEY\uFF0C\u6216\u901A\u8FC7\u547D\u4EE4\u884C\u53C2\u6570 --apiKey \u4F20\u5165\u3002'
@@ -505,7 +511,8 @@ var BeLinkReview = class _BeLinkReview {
505
511
  apiKey,
506
512
  agentPath,
507
513
  ignorePatterns,
508
- enableFeishu
514
+ enableFeishu,
515
+ model
509
516
  );
510
517
  } else {
511
518
  if (cliApiKey && !_BeLinkReview.#instance.#apiKey) {
@@ -520,6 +527,9 @@ var BeLinkReview = class _BeLinkReview {
520
527
  if (cliEnableFeishu !== void 0) {
521
528
  _BeLinkReview.#instance.#enableFeishu = cliEnableFeishu;
522
529
  }
530
+ if (cliModel) {
531
+ _BeLinkReview.#instance.#model = cliModel;
532
+ }
523
533
  }
524
534
  return _BeLinkReview.#instance;
525
535
  }
@@ -542,6 +552,9 @@ var BeLinkReview = class _BeLinkReview {
542
552
  #isFeishuEnabled() {
543
553
  return this.#enableFeishu;
544
554
  }
555
+ #getModel() {
556
+ return this.#model || process.env.CURSOR_MODEL || DEFAULT_MODEL;
557
+ }
545
558
  /**
546
559
  * 自动往用户项目 package.json 写入 script
547
560
  */
@@ -588,8 +601,10 @@ var BeLinkReview = class _BeLinkReview {
588
601
  console.log("[review-mark] Sending to AI...");
589
602
  const response = await this.chat(prompt, {
590
603
  agentPath: ensureResult.actualAgentPath,
591
- force: true
604
+ force: true,
605
+ model: this.#getModel()
592
606
  });
607
+ console.log(this.#getModel(), "model");
593
608
  console.log("===== AI Review =====");
594
609
  console.log(response);
595
610
  if (this.#isFeishuEnabled()) {
@@ -618,7 +633,9 @@ var BeLinkReview = class _BeLinkReview {
618
633
  */
619
634
  async chat(prompt, options = {}) {
620
635
  const actualAgentPath = options.agentPath || this.#getAgentPath() || "agent";
636
+ const model = options.model || this.#getModel();
621
637
  const args = ["--yolo", "-p", prompt];
638
+ if (model) args.unshift("--model", model);
622
639
  if (options.outputFormat === "json") args.push("--output-format", "json");
623
640
  return new Promise((resolve, reject) => {
624
641
  const env = { ...process.env, CURSOR_API_KEY: this.#getApiKey() };
@@ -660,89 +677,40 @@ var BeLinkReview = class _BeLinkReview {
660
677
 
661
678
  // src/cli/review.ts
662
679
  var program = new import_commander.Command();
663
- program.name("review-mark").description("AI-powered code review tool").version("1.0.0").option(
680
+ var sharedOptions = (cmd) => cmd.option(
664
681
  "--apiKey <key>",
665
682
  "Cursor API Key (overrides CURSOR_API_KEY environment variable)"
666
683
  ).option(
667
684
  "--agentPath <path>",
668
685
  "Path to the Cursor CLI agent executable (overrides CURSOR_AGENT_PATH environment variable)"
669
686
  ).option(
670
- "--ignore <patterns>",
671
- "Comma-separated list of glob patterns to ignore (e.g., *.lock,dist/**)"
672
- ).option(
673
- "--feishu-app-id <appId>",
674
- "Feishu App ID (overrides FEISHU_APP_ID environment variable)"
675
- ).option(
676
- "--feishu-app-secret <appSecret>",
677
- "Feishu App Secret (overrides FEISHU_APP_SECRET environment variable)"
678
- ).option(
679
- "--feishu-receive-id <receiveId>",
680
- "Feishu Receive ID - user open_id or chat_id (overrides FEISHU_RECEIVE_ID environment variable)"
681
- ).option(
682
- "--feishu-receive-id-type <type>",
683
- "Feishu Receive ID Type: open_id, user_id, chat_id, email, union_id (default: chat_id)"
684
- ).option(
685
- "--feishu-type <type>",
686
- "Feishu message type: text, post, or interactive (default: interactive)"
687
- ).option(
688
- "--feishu-title <title>",
689
- "Feishu message title (default: \u{1F50D} Code Review \u7ED3\u679C)"
690
- ).option("--no-feishu", "Disable Feishu notification").action(async (options) => {
691
- try {
692
- const ignorePatterns = options.ignore ? options.ignore.split(",") : void 0;
693
- const instance = BeLinkReview.getInstance(
694
- options.apiKey,
695
- options.agentPath,
696
- ignorePatterns,
697
- options.feishu !== false
698
- );
699
- await instance.goCli();
700
- } catch (error) {
701
- console.error(`[review-mark] Error: ${error.message}`);
702
- process.exit(1);
703
- }
704
- });
705
- program.command("review").description("Perform an AI code review on git diff").option(
706
- "--apiKey <key>",
707
- "Cursor API Key (overrides CURSOR_API_KEY environment variable)"
708
- ).option(
709
- "--agentPath <path>",
710
- "Path to the Cursor CLI agent executable (overrides CURSOR_AGENT_PATH environment variable)"
687
+ "--model <model>",
688
+ "AI model to use, e.g. claude-3-5-sonnet (overrides CURSOR_MODEL environment variable)"
711
689
  ).option(
712
690
  "--ignore <patterns>",
713
691
  "Comma-separated list of glob patterns to ignore (e.g., *.lock,dist/**)"
714
- ).option(
715
- "--feishu-app-id <appId>",
716
- "Feishu App ID (overrides FEISHU_APP_ID environment variable)"
717
- ).option(
718
- "--feishu-app-secret <appSecret>",
719
- "Feishu App Secret (overrides FEISHU_APP_SECRET environment variable)"
720
- ).option(
721
- "--feishu-receive-id <receiveId>",
722
- "Feishu Receive ID - user open_id or chat_id (overrides FEISHU_RECEIVE_ID environment variable)"
723
- ).option(
724
- "--feishu-receive-id-type <type>",
725
- "Feishu Receive ID Type: open_id, user_id, chat_id, email, union_id (default: chat_id)"
726
- ).option(
727
- "--feishu-type <type>",
728
- "Feishu message type: text, post, or interactive (default: interactive)"
729
- ).option(
730
- "--feishu-title <title>",
731
- "Feishu message title (default: \u{1F50D} Code Review \u7ED3\u679C)"
732
- ).option("--no-feishu", "Disable Feishu notification").action(async (options) => {
692
+ ).option("--no-feishu", "Disable Feishu notification");
693
+ var runAction = async (options) => {
733
694
  try {
734
695
  const ignorePatterns = options.ignore ? options.ignore.split(",") : void 0;
735
696
  const instance = BeLinkReview.getInstance(
736
697
  options.apiKey,
737
698
  options.agentPath,
738
699
  ignorePatterns,
739
- options.feishu !== false
700
+ options.feishu !== false,
701
+ options.model
740
702
  );
741
703
  await instance.goCli();
742
704
  } catch (error) {
743
705
  console.error(`[review-mark] Error: ${error.message}`);
744
706
  process.exit(1);
745
707
  }
746
- });
708
+ };
709
+ sharedOptions(
710
+ program.name("review-mark").description("AI-powered code review tool").version("1.0.0")
711
+ ).action(runAction);
712
+ sharedOptions(
713
+ program.command("review").description("Perform an AI code review on git diff")
714
+ ).action(runAction);
747
715
  program.parse(process.argv);
748
716
  //# sourceMappingURL=review.cjs.map
@@ -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\nprogram\n .name(\"review-mark\")\n .description(\"AI-powered code review tool\")\n .version(\"1.0.0\")\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 \"--ignore <patterns>\",\n \"Comma-separated list of glob patterns to ignore (e.g., *.lock,dist/**)\"\n )\n .option(\n \"--feishu-app-id <appId>\",\n \"Feishu App ID (overrides FEISHU_APP_ID environment variable)\"\n )\n .option(\n \"--feishu-app-secret <appSecret>\",\n \"Feishu App Secret (overrides FEISHU_APP_SECRET environment variable)\"\n )\n .option(\n \"--feishu-receive-id <receiveId>\",\n \"Feishu Receive ID - user open_id or chat_id (overrides FEISHU_RECEIVE_ID environment variable)\"\n )\n .option(\n \"--feishu-receive-id-type <type>\",\n \"Feishu Receive ID Type: open_id, user_id, chat_id, email, union_id (default: chat_id)\"\n )\n .option(\n \"--feishu-type <type>\",\n \"Feishu message type: text, post, or interactive (default: interactive)\"\n )\n .option(\n \"--feishu-title <title>\",\n \"Feishu message title (default: 🔍 Code Review 结果)\"\n )\n .option(\"--no-feishu\", \"Disable Feishu notification\")\n .action(async (options) => {\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 );\n await instance.goCli();\n } catch (error: any) {\n console.error(`[review-mark] Error: ${error.message}`);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"review\")\n .description(\"Perform an AI code review on git diff\")\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 \"--ignore <patterns>\",\n \"Comma-separated list of glob patterns to ignore (e.g., *.lock,dist/**)\"\n )\n .option(\n \"--feishu-app-id <appId>\",\n \"Feishu App ID (overrides FEISHU_APP_ID environment variable)\"\n )\n .option(\n \"--feishu-app-secret <appSecret>\",\n \"Feishu App Secret (overrides FEISHU_APP_SECRET environment variable)\"\n )\n .option(\n \"--feishu-receive-id <receiveId>\",\n \"Feishu Receive ID - user open_id or chat_id (overrides FEISHU_RECEIVE_ID environment variable)\"\n )\n .option(\n \"--feishu-receive-id-type <type>\",\n \"Feishu Receive ID Type: open_id, user_id, chat_id, email, union_id (default: chat_id)\"\n )\n .option(\n \"--feishu-type <type>\",\n \"Feishu message type: text, post, or interactive (default: interactive)\"\n )\n .option(\n \"--feishu-title <title>\",\n \"Feishu message title (default: 🔍 Code Review 结果)\"\n )\n .option(\"--no-feishu\", \"Disable Feishu notification\")\n .action(async (options) => {\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 );\n await instance.goCli();\n } catch (error: any) {\n console.error(`[review-mark] Error: ${error.message}`);\n process.exit(1);\n }\n });\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\";\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\n private constructor(\n apiKey?: string,\n agentPath?: string,\n ignorePatterns?: string[],\n enableFeishu: boolean = true\n ) {\n this.#apiKey = apiKey;\n this.#agentPath = agentPath;\n this.#ignorePatterns = ignorePatterns;\n this.#enableFeishu = enableFeishu;\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 );\n } else {\n BeLinkReview.#instance.#apiKey = options.apiKey; // 允许重新初始化时更新 apiKey\n BeLinkReview.#instance.#agentPath = options.agentPath; // 允许重新初始化时更新 agentPath\n BeLinkReview.#instance.#ignorePatterns = options.ignore; // 允许重新初始化时更新 ignorePatterns\n BeLinkReview.#instance.#enableFeishu = options.enableFeishu ?? true; // 允许重新初始化时更新飞书开关\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 ): 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\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 );\n } else {\n // 如果单例已存在,但参数未设置,且命令行传入了,则更新\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 }\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 /**\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 });\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 args: string[] = [\"--yolo\", \"-p\", prompt];\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","// 飞书机器人配置(内置,不需要外部配置)\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\";\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"]}
@@ -208,6 +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
212
  var appId = "cli_a93822da7238dbb5";
212
213
  var appSecret = "ZQdcpLUHFb4gFa8cGfrlJfVfSSyGtyzF";
213
214
  var receiveId = "oc_482b6a04f95f4206c4fa9bc61829fd17";
@@ -438,11 +439,13 @@ var BeLinkReview = class _BeLinkReview {
438
439
  #agentPath;
439
440
  #ignorePatterns;
440
441
  #enableFeishu;
441
- constructor(apiKey, agentPath, ignorePatterns, enableFeishu = true) {
442
+ #model;
443
+ constructor(apiKey, agentPath, ignorePatterns, enableFeishu = true, model) {
442
444
  this.#apiKey = apiKey;
443
445
  this.#agentPath = agentPath;
444
446
  this.#ignorePatterns = ignorePatterns;
445
447
  this.#enableFeishu = enableFeishu;
448
+ this.#model = model;
446
449
  }
447
450
  /**
448
451
  * 初始化单例并注入参数,在项目入口调用一次即可。
@@ -455,13 +458,15 @@ var BeLinkReview = class _BeLinkReview {
455
458
  options.apiKey,
456
459
  options.agentPath,
457
460
  options.ignore,
458
- options.enableFeishu ?? true
461
+ options.enableFeishu ?? true,
462
+ options.model
459
463
  );
460
464
  } else {
461
465
  _BeLinkReview.#instance.#apiKey = options.apiKey;
462
466
  _BeLinkReview.#instance.#agentPath = options.agentPath;
463
467
  _BeLinkReview.#instance.#ignorePatterns = options.ignore;
464
468
  _BeLinkReview.#instance.#enableFeishu = options.enableFeishu ?? true;
469
+ _BeLinkReview.#instance.#model = options.model;
465
470
  }
466
471
  _BeLinkReview.#instance.#setupProjectScript();
467
472
  return _BeLinkReview.#instance;
@@ -473,12 +478,13 @@ var BeLinkReview = class _BeLinkReview {
473
478
  * @param cliIgnorePatterns 可选的命令行传入的 ignorePatterns
474
479
  * @param cliEnableFeishu 可选的命令行传入的飞书开关
475
480
  */
476
- static getInstance(cliApiKey, cliAgentPath, cliIgnorePatterns, cliEnableFeishu) {
481
+ static getInstance(cliApiKey, cliAgentPath, cliIgnorePatterns, cliEnableFeishu, cliModel) {
477
482
  if (_BeLinkReview.#instance === null) {
478
483
  const apiKey = cliApiKey || process.env.CURSOR_API_KEY;
479
484
  const agentPath = cliAgentPath || process.env.CURSOR_AGENT_PATH;
480
485
  const ignorePatterns = cliIgnorePatterns || (process.env.BE_LINK_REVIEW_IGNORE ? process.env.BE_LINK_REVIEW_IGNORE.split(",") : void 0);
481
486
  const enableFeishu = cliEnableFeishu ?? process.env.FEISHU_ENABLED !== "false";
487
+ const model = cliModel || process.env.CURSOR_MODEL;
482
488
  if (!apiKey) {
483
489
  throw new Error(
484
490
  '[review-mark] \u8BF7\u5148\u8C03\u7528 BeLinkReview.init({ apiKey: "..." }) \u521D\u59CB\u5316\uFF0C\u6216\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF CURSOR_API_KEY\uFF0C\u6216\u901A\u8FC7\u547D\u4EE4\u884C\u53C2\u6570 --apiKey \u4F20\u5165\u3002'
@@ -488,7 +494,8 @@ var BeLinkReview = class _BeLinkReview {
488
494
  apiKey,
489
495
  agentPath,
490
496
  ignorePatterns,
491
- enableFeishu
497
+ enableFeishu,
498
+ model
492
499
  );
493
500
  } else {
494
501
  if (cliApiKey && !_BeLinkReview.#instance.#apiKey) {
@@ -503,6 +510,9 @@ var BeLinkReview = class _BeLinkReview {
503
510
  if (cliEnableFeishu !== void 0) {
504
511
  _BeLinkReview.#instance.#enableFeishu = cliEnableFeishu;
505
512
  }
513
+ if (cliModel) {
514
+ _BeLinkReview.#instance.#model = cliModel;
515
+ }
506
516
  }
507
517
  return _BeLinkReview.#instance;
508
518
  }
@@ -525,6 +535,9 @@ var BeLinkReview = class _BeLinkReview {
525
535
  #isFeishuEnabled() {
526
536
  return this.#enableFeishu;
527
537
  }
538
+ #getModel() {
539
+ return this.#model || process.env.CURSOR_MODEL || DEFAULT_MODEL;
540
+ }
528
541
  /**
529
542
  * 自动往用户项目 package.json 写入 script
530
543
  */
@@ -571,8 +584,10 @@ var BeLinkReview = class _BeLinkReview {
571
584
  console.log("[review-mark] Sending to AI...");
572
585
  const response = await this.chat(prompt, {
573
586
  agentPath: ensureResult.actualAgentPath,
574
- force: true
587
+ force: true,
588
+ model: this.#getModel()
575
589
  });
590
+ console.log(this.#getModel(), "model");
576
591
  console.log("===== AI Review =====");
577
592
  console.log(response);
578
593
  if (this.#isFeishuEnabled()) {
@@ -601,7 +616,9 @@ var BeLinkReview = class _BeLinkReview {
601
616
  */
602
617
  async chat(prompt, options = {}) {
603
618
  const actualAgentPath = options.agentPath || this.#getAgentPath() || "agent";
619
+ const model = options.model || this.#getModel();
604
620
  const args = ["--yolo", "-p", prompt];
621
+ if (model) args.unshift("--model", model);
605
622
  if (options.outputFormat === "json") args.push("--output-format", "json");
606
623
  return new Promise((resolve, reject) => {
607
624
  const env = { ...process.env, CURSOR_API_KEY: this.#getApiKey() };
@@ -643,89 +660,40 @@ var BeLinkReview = class _BeLinkReview {
643
660
 
644
661
  // src/cli/review.ts
645
662
  var program = new Command();
646
- program.name("review-mark").description("AI-powered code review tool").version("1.0.0").option(
663
+ var sharedOptions = (cmd) => cmd.option(
647
664
  "--apiKey <key>",
648
665
  "Cursor API Key (overrides CURSOR_API_KEY environment variable)"
649
666
  ).option(
650
667
  "--agentPath <path>",
651
668
  "Path to the Cursor CLI agent executable (overrides CURSOR_AGENT_PATH environment variable)"
652
669
  ).option(
653
- "--ignore <patterns>",
654
- "Comma-separated list of glob patterns to ignore (e.g., *.lock,dist/**)"
655
- ).option(
656
- "--feishu-app-id <appId>",
657
- "Feishu App ID (overrides FEISHU_APP_ID environment variable)"
658
- ).option(
659
- "--feishu-app-secret <appSecret>",
660
- "Feishu App Secret (overrides FEISHU_APP_SECRET environment variable)"
661
- ).option(
662
- "--feishu-receive-id <receiveId>",
663
- "Feishu Receive ID - user open_id or chat_id (overrides FEISHU_RECEIVE_ID environment variable)"
664
- ).option(
665
- "--feishu-receive-id-type <type>",
666
- "Feishu Receive ID Type: open_id, user_id, chat_id, email, union_id (default: chat_id)"
667
- ).option(
668
- "--feishu-type <type>",
669
- "Feishu message type: text, post, or interactive (default: interactive)"
670
- ).option(
671
- "--feishu-title <title>",
672
- "Feishu message title (default: \u{1F50D} Code Review \u7ED3\u679C)"
673
- ).option("--no-feishu", "Disable Feishu notification").action(async (options) => {
674
- try {
675
- const ignorePatterns = options.ignore ? options.ignore.split(",") : void 0;
676
- const instance = BeLinkReview.getInstance(
677
- options.apiKey,
678
- options.agentPath,
679
- ignorePatterns,
680
- options.feishu !== false
681
- );
682
- await instance.goCli();
683
- } catch (error) {
684
- console.error(`[review-mark] Error: ${error.message}`);
685
- process.exit(1);
686
- }
687
- });
688
- program.command("review").description("Perform an AI code review on git diff").option(
689
- "--apiKey <key>",
690
- "Cursor API Key (overrides CURSOR_API_KEY environment variable)"
691
- ).option(
692
- "--agentPath <path>",
693
- "Path to the Cursor CLI agent executable (overrides CURSOR_AGENT_PATH environment variable)"
670
+ "--model <model>",
671
+ "AI model to use, e.g. claude-3-5-sonnet (overrides CURSOR_MODEL environment variable)"
694
672
  ).option(
695
673
  "--ignore <patterns>",
696
674
  "Comma-separated list of glob patterns to ignore (e.g., *.lock,dist/**)"
697
- ).option(
698
- "--feishu-app-id <appId>",
699
- "Feishu App ID (overrides FEISHU_APP_ID environment variable)"
700
- ).option(
701
- "--feishu-app-secret <appSecret>",
702
- "Feishu App Secret (overrides FEISHU_APP_SECRET environment variable)"
703
- ).option(
704
- "--feishu-receive-id <receiveId>",
705
- "Feishu Receive ID - user open_id or chat_id (overrides FEISHU_RECEIVE_ID environment variable)"
706
- ).option(
707
- "--feishu-receive-id-type <type>",
708
- "Feishu Receive ID Type: open_id, user_id, chat_id, email, union_id (default: chat_id)"
709
- ).option(
710
- "--feishu-type <type>",
711
- "Feishu message type: text, post, or interactive (default: interactive)"
712
- ).option(
713
- "--feishu-title <title>",
714
- "Feishu message title (default: \u{1F50D} Code Review \u7ED3\u679C)"
715
- ).option("--no-feishu", "Disable Feishu notification").action(async (options) => {
675
+ ).option("--no-feishu", "Disable Feishu notification");
676
+ var runAction = async (options) => {
716
677
  try {
717
678
  const ignorePatterns = options.ignore ? options.ignore.split(",") : void 0;
718
679
  const instance = BeLinkReview.getInstance(
719
680
  options.apiKey,
720
681
  options.agentPath,
721
682
  ignorePatterns,
722
- options.feishu !== false
683
+ options.feishu !== false,
684
+ options.model
723
685
  );
724
686
  await instance.goCli();
725
687
  } catch (error) {
726
688
  console.error(`[review-mark] Error: ${error.message}`);
727
689
  process.exit(1);
728
690
  }
729
- });
691
+ };
692
+ sharedOptions(
693
+ program.name("review-mark").description("AI-powered code review tool").version("1.0.0")
694
+ ).action(runAction);
695
+ sharedOptions(
696
+ program.command("review").description("Perform an AI code review on git diff")
697
+ ).action(runAction);
730
698
  program.parse(process.argv);
731
699
  //# sourceMappingURL=review.js.map