@wuyanbin/ai-code-review 1.0.0

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.
Files changed (46) hide show
  1. package/bin/ai-code-review.js +12 -0
  2. package/dist/config.d.ts +14 -0
  3. package/dist/config.d.ts.map +1 -0
  4. package/dist/config.js +52 -0
  5. package/dist/config.js.map +1 -0
  6. package/dist/formatters/index.d.ts +5 -0
  7. package/dist/formatters/index.d.ts.map +1 -0
  8. package/dist/formatters/index.js +21 -0
  9. package/dist/formatters/index.js.map +1 -0
  10. package/dist/formatters/rdjson.d.ts +17 -0
  11. package/dist/formatters/rdjson.d.ts.map +1 -0
  12. package/dist/formatters/rdjson.js +65 -0
  13. package/dist/formatters/rdjson.js.map +1 -0
  14. package/dist/index.d.ts +8 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +59 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/reviewer.d.ts +9 -0
  19. package/dist/reviewer.d.ts.map +1 -0
  20. package/dist/reviewer.js +160 -0
  21. package/dist/reviewer.js.map +1 -0
  22. package/dist/rules/index.d.ts +13 -0
  23. package/dist/rules/index.d.ts.map +1 -0
  24. package/dist/rules/index.js +50 -0
  25. package/dist/rules/index.js.map +1 -0
  26. package/dist/rules/react.d.ts +6 -0
  27. package/dist/rules/react.d.ts.map +1 -0
  28. package/dist/rules/react.js +17 -0
  29. package/dist/rules/react.js.map +1 -0
  30. package/dist/rules/typescript.d.ts +6 -0
  31. package/dist/rules/typescript.d.ts.map +1 -0
  32. package/dist/rules/typescript.js +16 -0
  33. package/dist/rules/typescript.js.map +1 -0
  34. package/dist/rules/vue.d.ts +6 -0
  35. package/dist/rules/vue.d.ts.map +1 -0
  36. package/dist/rules/vue.js +16 -0
  37. package/dist/rules/vue.js.map +1 -0
  38. package/dist/types.d.ts +78 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +6 -0
  41. package/dist/types.js.map +1 -0
  42. package/dist/utils.d.ts +17 -0
  43. package/dist/utils.d.ts.map +1 -0
  44. package/dist/utils.js +84 -0
  45. package/dist/utils.js.map +1 -0
  46. package/package.json +41 -0
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * AI Code Review CLI 入口
5
+ */
6
+
7
+ const { main } = require("../dist/index");
8
+
9
+ main().catch((error) => {
10
+ console.error("[ai-code-review] CLI 执行错误:", error);
11
+ process.exit(1);
12
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 配置加载器
3
+ * 现在仅支持:环境变量 + 本地配置文件,移除远程配置服务以保持简单
4
+ */
5
+ import { CodeReviewConfig } from "./types";
6
+ /**
7
+ * 加载配置
8
+ */
9
+ export declare const loadConfig: () => Promise<CodeReviewConfig>;
10
+ /**
11
+ * 验证配置完整性
12
+ */
13
+ export declare const validateConfig: (config: CodeReviewConfig) => boolean;
14
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAI3C;;GAEG;AACH,eAAO,MAAM,UAAU,QAAa,OAAO,CAAC,gBAAgB,CAyB3D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,gBAAgB,KAAG,OAczD,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ /**
3
+ * 配置加载器
4
+ * 现在仅支持:环境变量 + 本地配置文件,移除远程配置服务以保持简单
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.validateConfig = exports.loadConfig = void 0;
8
+ const cosmiconfig_1 = require("cosmiconfig");
9
+ const explorer = (0, cosmiconfig_1.cosmiconfigSync)("aicodereviews");
10
+ /**
11
+ * 加载配置
12
+ */
13
+ const loadConfig = async () => {
14
+ // 1. 加载配置文件
15
+ const result = explorer.search();
16
+ const fileConfig = result?.config || {};
17
+ // 2. 合并配置(环境变量 > 配置文件 > 默认值)
18
+ const config = {
19
+ ai: {
20
+ provider: fileConfig.ai?.provider || "deepseek",
21
+ model: fileConfig.ai?.model || "deepseek-chat",
22
+ endpoint: fileConfig.ai?.endpoint || "https://api.deepseek.com/chat/completions",
23
+ apiKey: process.env.MODEL_API_KEY || fileConfig.ai?.apiKey,
24
+ },
25
+ rules: {
26
+ custom: fileConfig.rules?.custom || [],
27
+ },
28
+ review: {
29
+ severity: fileConfig.review?.severity || "warning",
30
+ ignore: fileConfig.review?.ignore || [],
31
+ maxComments: fileConfig.review?.maxComments || 100,
32
+ },
33
+ };
34
+ return config;
35
+ };
36
+ exports.loadConfig = loadConfig;
37
+ /**
38
+ * 验证配置完整性
39
+ */
40
+ const validateConfig = (config) => {
41
+ if (!config.ai.apiKey) {
42
+ console.error("[config] 缺少 API Key,请通过环境变量、配置文件或配置服务提供");
43
+ return false;
44
+ }
45
+ if (!config.ai.endpoint) {
46
+ console.error("[config] 缺少 API Endpoint");
47
+ return false;
48
+ }
49
+ return true;
50
+ };
51
+ exports.validateConfig = validateConfig;
52
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,6CAA8C;AAG9C,MAAM,QAAQ,GAAG,IAAA,6BAAe,EAAC,eAAe,CAAC,CAAC;AAElD;;GAEG;AACI,MAAM,UAAU,GAAG,KAAK,IAA+B,EAAE;IAC9D,YAAY;IACZ,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC;IACjC,MAAM,UAAU,GAA8B,MAAM,EAAE,MAAM,IAAI,EAAE,CAAC;IAEnE,6BAA6B;IAC7B,MAAM,MAAM,GAAqB;QAC/B,EAAE,EAAE;YACF,QAAQ,EAAE,UAAU,CAAC,EAAE,EAAE,QAAQ,IAAI,UAAU;YAC/C,KAAK,EAAE,UAAU,CAAC,EAAE,EAAE,KAAK,IAAI,eAAe;YAC9C,QAAQ,EACN,UAAU,CAAC,EAAE,EAAE,QAAQ,IAAI,2CAA2C;YACxE,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,UAAU,CAAC,EAAE,EAAE,MAAM;SAC3D;QACD,KAAK,EAAE;YACL,MAAM,EAAE,UAAU,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE;SACvC;QACD,MAAM,EAAE;YACN,QAAQ,EAAG,UAAU,CAAC,MAAM,EAAE,QAAgB,IAAI,SAAS;YAC3D,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,MAAM,IAAI,EAAE;YACvC,WAAW,EAAE,UAAU,CAAC,MAAM,EAAE,WAAW,IAAI,GAAG;SACnD;KACF,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAzBW,QAAA,UAAU,cAyBrB;AAEF;;GAEG;AACI,MAAM,cAAc,GAAG,CAAC,MAAwB,EAAW,EAAE;IAClE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,KAAK,CACX,yCAAyC,CAC1C,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC;QACxB,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC1C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAdW,QAAA,cAAc,kBAczB"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 格式化器索引
3
+ */
4
+ export * from './rdjson';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/formatters/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,UAAU,CAAC"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ /**
3
+ * 格式化器索引
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
17
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ __exportStar(require("./rdjson"), exports);
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/formatters/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;;;;;;;;;;;;;AAEH,2CAAyB"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Reviewdog JSON 格式化器
3
+ */
4
+ import { AIComment, RdjsonDiagnostic, RdjsonOutput } from "../types";
5
+ /**
6
+ * 构建空的 Rdjson 输出
7
+ */
8
+ export declare const buildEmptyRdjson: () => RdjsonOutput;
9
+ /**
10
+ * 将 AI 评论转换为 Reviewdog 诊断信息
11
+ */
12
+ export declare const buildDiagnosticsFromComments: (comments: AIComment[]) => RdjsonDiagnostic[];
13
+ /**
14
+ * 构建完整的 Rdjson 输出
15
+ */
16
+ export declare const buildRdjson: (comments: AIComment[]) => RdjsonOutput;
17
+ //# sourceMappingURL=rdjson.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rdjson.d.ts","sourceRoot":"","sources":["../../src/formatters/rdjson.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,YAAY,EAAY,MAAM,UAAU,CAAC;AAc/E;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAAO,YAGlC,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,4BAA4B,GACvC,UAAU,SAAS,EAAE,KACpB,gBAAgB,EAyBlB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,UAAU,SAAS,EAAE,KAAG,YAMnD,CAAC"}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ /**
3
+ * Reviewdog JSON 格式化器
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.buildRdjson = exports.buildDiagnosticsFromComments = exports.buildEmptyRdjson = void 0;
7
+ /**
8
+ * 转换 severity 到 Reviewdog 格式
9
+ */
10
+ const toRdjsonSeverity = (severity) => {
11
+ const map = {
12
+ error: "ERROR",
13
+ warning: "WARNING",
14
+ info: "INFO",
15
+ };
16
+ return map[severity] || "WARNING";
17
+ };
18
+ /**
19
+ * 构建空的 Rdjson 输出
20
+ */
21
+ const buildEmptyRdjson = () => ({
22
+ source: { name: "ai-code-review" },
23
+ diagnostics: [],
24
+ });
25
+ exports.buildEmptyRdjson = buildEmptyRdjson;
26
+ /**
27
+ * 将 AI 评论转换为 Reviewdog 诊断信息
28
+ */
29
+ const buildDiagnosticsFromComments = (comments) => {
30
+ if (!Array.isArray(comments)) {
31
+ return [];
32
+ }
33
+ return comments
34
+ .filter((item) => item && item.file && item.line)
35
+ .map((item) => {
36
+ const baseMessage = item.message || "AI Code Review 提示";
37
+ const suggestion = item.suggestion ? `\n建议:${item.suggestion}` : "";
38
+ return {
39
+ message: `${baseMessage}${suggestion}`,
40
+ location: {
41
+ path: item.file,
42
+ range: {
43
+ start: {
44
+ line: Number(item.line) || 1,
45
+ column: 1,
46
+ },
47
+ },
48
+ },
49
+ severity: toRdjsonSeverity(item.severity),
50
+ };
51
+ });
52
+ };
53
+ exports.buildDiagnosticsFromComments = buildDiagnosticsFromComments;
54
+ /**
55
+ * 构建完整的 Rdjson 输出
56
+ */
57
+ const buildRdjson = (comments) => {
58
+ const diagnostics = (0, exports.buildDiagnosticsFromComments)(comments);
59
+ return {
60
+ source: { name: "ai-code-review" },
61
+ diagnostics,
62
+ };
63
+ };
64
+ exports.buildRdjson = buildRdjson;
65
+ //# sourceMappingURL=rdjson.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rdjson.js","sourceRoot":"","sources":["../../src/formatters/rdjson.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAIH;;GAEG;AACH,MAAM,gBAAgB,GAAG,CAAC,QAAkB,EAAgC,EAAE;IAC5E,MAAM,GAAG,GAAmD;QAC1D,KAAK,EAAE,OAAO;QACd,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE,MAAM;KACb,CAAC;IACF,OAAO,GAAG,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC;AACpC,CAAC,CAAC;AAEF;;GAEG;AACI,MAAM,gBAAgB,GAAG,GAAiB,EAAE,CAAC,CAAC;IACnD,MAAM,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE;IAClC,WAAW,EAAE,EAAE;CAChB,CAAC,CAAC;AAHU,QAAA,gBAAgB,oBAG1B;AAEH;;GAEG;AACI,MAAM,4BAA4B,GAAG,CAC1C,QAAqB,EACD,EAAE;IACtB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,QAAQ;SACZ,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC;SAChD,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,IAAI,mBAAmB,CAAC;QACxD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAEpE,OAAO;YACL,OAAO,EAAE,GAAG,WAAW,GAAG,UAAU,EAAE;YACtC,QAAQ,EAAE;gBACR,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE;oBACL,KAAK,EAAE;wBACL,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;wBAC5B,MAAM,EAAE,CAAC;qBACV;iBACF;aACF;YACD,QAAQ,EAAE,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC;SAC1C,CAAC;IACJ,CAAC,CAAC,CAAC;AACP,CAAC,CAAC;AA3BW,QAAA,4BAA4B,gCA2BvC;AAEF;;GAEG;AACI,MAAM,WAAW,GAAG,CAAC,QAAqB,EAAgB,EAAE;IACjE,MAAM,WAAW,GAAG,IAAA,oCAA4B,EAAC,QAAQ,CAAC,CAAC;IAC3D,OAAO;QACL,MAAM,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE;QAClC,WAAW;KACZ,CAAC;AACJ,CAAC,CAAC;AANW,QAAA,WAAW,eAMtB"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * AI Code Review 主入口
3
+ */
4
+ /**
5
+ * 主函数
6
+ */
7
+ export declare const main: () => Promise<void>;
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH;;GAEG;AACH,eAAO,MAAM,IAAI,QAAa,OAAO,CAAC,IAAI,CAoCzC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ /**
3
+ * AI Code Review 主入口
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.main = void 0;
7
+ const config_1 = require("./config");
8
+ const formatters_1 = require("./formatters");
9
+ const reviewer_1 = require("./reviewer");
10
+ const utils_1 = require("./utils");
11
+ /**
12
+ * 主函数
13
+ */
14
+ const main = async () => {
15
+ try {
16
+ // 1. 加载配置
17
+ const config = await (0, config_1.loadConfig)();
18
+ // 2. 验证配置
19
+ if (!(0, config_1.validateConfig)(config)) {
20
+ console.log(JSON.stringify((0, formatters_1.buildEmptyRdjson)()));
21
+ process.exit(0);
22
+ }
23
+ // 3. 读取 diff
24
+ const diff = await (0, utils_1.readStdin)();
25
+ if (!diff || !diff.trim()) {
26
+ console.error("[ai-code-review] 未读取到 diff,输出空诊断结果");
27
+ console.log(JSON.stringify((0, formatters_1.buildEmptyRdjson)()));
28
+ process.exit(0);
29
+ }
30
+ // 4. 执行 AI Review
31
+ const result = await (0, reviewer_1.reviewCode)(diff, config);
32
+ if (!result) {
33
+ console.log(JSON.stringify((0, formatters_1.buildEmptyRdjson)()));
34
+ process.exit(0);
35
+ }
36
+ // 5. 格式化输出
37
+ const output = (0, formatters_1.buildRdjson)(result.comments);
38
+ console.log(JSON.stringify(output));
39
+ }
40
+ catch (error) {
41
+ console.error("[ai-code-review] 未预期的错误:", error);
42
+ console.log(JSON.stringify((0, formatters_1.buildEmptyRdjson)()));
43
+ process.exit(1);
44
+ }
45
+ };
46
+ exports.main = main;
47
+ /**
48
+ * CLI 入口
49
+ * 兼容直接执行 dist/index.js 的场景:
50
+ * git diff ... | node dist/index.js | reviewdog ...
51
+ */
52
+ if (require.main === module) {
53
+ (0, exports.main)().catch((error) => {
54
+ console.error("[ai-code-review] CLI 执行错误:", error);
55
+ console.log(JSON.stringify((0, formatters_1.buildEmptyRdjson)()));
56
+ process.exit(1);
57
+ });
58
+ }
59
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,qCAAsD;AACtD,6CAA6D;AAC7D,yCAAwC;AACxC,mCAAoC;AACpC;;GAEG;AACI,MAAM,IAAI,GAAG,KAAK,IAAmB,EAAE;IAC5C,IAAI,CAAC;QACH,UAAU;QACV,MAAM,MAAM,GAAG,MAAM,IAAA,mBAAU,GAAE,CAAC;QAElC,UAAU;QACV,IAAI,CAAC,IAAA,uBAAc,EAAC,MAAM,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAA,6BAAgB,GAAE,CAAC,CAAC,CAAC;YAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,aAAa;QACb,MAAM,IAAI,GAAG,MAAM,IAAA,iBAAS,GAAE,CAAC;QAE/B,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAC1B,OAAO,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;YACpD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAA,6BAAgB,GAAE,CAAC,CAAC,CAAC;YAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,kBAAkB;QAClB,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAU,EAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAE9C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAA,6BAAgB,GAAE,CAAC,CAAC,CAAC;YAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,WAAW;QACX,MAAM,MAAM,GAAG,IAAA,wBAAW,EAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAA,6BAAgB,GAAE,CAAC,CAAC,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC;AApCW,QAAA,IAAI,QAoCf;AAEF;;;;GAIG;AACH,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IAC5B,IAAA,YAAI,GAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACrB,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAA,6BAAgB,GAAE,CAAC,CAAC,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * AI Code Reviewer 核心逻辑
3
+ */
4
+ import { AIResponse, CodeReviewConfig } from "./types";
5
+ /**
6
+ * 执行 AI Code Review
7
+ */
8
+ export declare const reviewCode: (diff: string, config: CodeReviewConfig) => Promise<AIResponse | null>;
9
+ //# sourceMappingURL=reviewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviewer.d.ts","sourceRoot":"","sources":["../src/reviewer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAmIvD;;GAEG;AACH,eAAO,MAAM,UAAU,GACrB,MAAM,MAAM,EACZ,QAAQ,gBAAgB,KACvB,OAAO,CAAC,UAAU,GAAG,IAAI,CA+D3B,CAAC"}
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ /**
3
+ * AI Code Reviewer 核心逻辑
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.reviewCode = void 0;
7
+ const utils_1 = require("./utils");
8
+ /**
9
+ * 通用代码质量规则(始终启用)
10
+ */
11
+ const commonRules = [
12
+ "重点关注潜在 bug、性能问题、可读性与安全性(如 XSS、注入、敏感信息泄露)",
13
+ "代码要有适当的错误处理",
14
+ "避免代码重复,注意抽离复用逻辑",
15
+ "函数和变量命名要语义化,见名知意",
16
+ "复杂逻辑要添加注释说明",
17
+ ];
18
+ /**
19
+ * 构建系统 Prompt
20
+ */
21
+ const buildSystemPrompt = () => {
22
+ return "你是一个资深的代码审查助手,需要根据给定的 Git diff 和项目规范,对改动的代码进行严格的 Code Review。";
23
+ };
24
+ /**
25
+ * 构建用户 Prompt
26
+ */
27
+ const buildUserPrompt = (diff, rules) => {
28
+ const rulesBrief = rules.join("\n");
29
+ return [
30
+ "请你只针对 diff 中【新增或修改】的代码行给出审查意见。",
31
+ "输出格式要求:必须返回严格的 JSON,形如:",
32
+ "{",
33
+ ' "comments": [',
34
+ " {",
35
+ ' "file": "src/xxx.tsx",',
36
+ ' "line": 123,',
37
+ ' "severity": "info" | "warning" | "error",',
38
+ ' "message": "问题描述(中文,简洁具体)",',
39
+ ' "suggestion": "可选的修复建议或更佳写法(中文)"',
40
+ " }",
41
+ " ]",
42
+ "}",
43
+ "注意:",
44
+ "1. 一定要保证输出是合法的 JSON,不能包含注释或多余文本;",
45
+ '2. 如果没有任何问题,也请返回 {"comments": []};',
46
+ "3. 请根据 diff 中的路径和行号填写 file 与 line 字段。",
47
+ "",
48
+ "项目规范(摘要):",
49
+ rulesBrief,
50
+ "",
51
+ "下面是 Git diff:",
52
+ diff,
53
+ ].join("\n");
54
+ };
55
+ /**
56
+ * 调用 DeepSeek/OpenAI API
57
+ */
58
+ const callChatAPI = async (endpoint, apiKey, model, systemPrompt, userPrompt) => {
59
+ try {
60
+ const payload = {
61
+ model,
62
+ messages: [
63
+ { role: "system", content: systemPrompt },
64
+ { role: "user", content: userPrompt },
65
+ ],
66
+ stream: false,
67
+ };
68
+ const response = await fetch(endpoint, {
69
+ method: "POST",
70
+ headers: {
71
+ "Content-Type": "application/json",
72
+ Authorization: `Bearer ${apiKey}`,
73
+ },
74
+ body: JSON.stringify(payload),
75
+ });
76
+ if (!response.ok) {
77
+ console.error(`[reviewer] AI API 调用失败,status=${response.status}`);
78
+ const errorText = await response.text();
79
+ console.error(`[reviewer] 错误详情: ${errorText}`);
80
+ return null;
81
+ }
82
+ const resJson = (await response.json());
83
+ const rawContent = resJson.choices?.[0]?.message?.content;
84
+ if (!rawContent) {
85
+ console.error("[reviewer] AI 返回内容为空");
86
+ return null;
87
+ }
88
+ return String(rawContent).trim();
89
+ }
90
+ catch (error) {
91
+ console.error("[reviewer] AI API 调用异常:", error);
92
+ return null;
93
+ }
94
+ };
95
+ /**
96
+ * 解析 AI 返回的 JSON(兼容代码块包裹)
97
+ */
98
+ const parseAIResponse = (content) => {
99
+ // 兼容 ```json ... ``` 代码块的情况
100
+ const contentText = content
101
+ .replace(/^```(?:json)?\s*/i, "")
102
+ .replace(/```$/i, "")
103
+ .trim();
104
+ try {
105
+ return JSON.parse(contentText);
106
+ }
107
+ catch (parseError) {
108
+ console.error("[reviewer] AI 返回内容 JSON 解析失败");
109
+ console.error("[reviewer] 原始内容:", content);
110
+ console.error(parseError);
111
+ return null;
112
+ }
113
+ };
114
+ /**
115
+ * 执行 AI Code Review
116
+ */
117
+ const reviewCode = async (diff, config) => {
118
+ // 先根据 ignore 规则过滤掉不需要审查的文件 diff
119
+ const effectiveDiff = (0, utils_1.filterDiffByIgnore)(diff, config.review?.ignore);
120
+ if (!effectiveDiff || !effectiveDiff.trim()) {
121
+ console.error("[reviewer] diff 为空,跳过审查");
122
+ return { comments: [] };
123
+ }
124
+ if (!config.ai.apiKey || !config.ai.endpoint) {
125
+ console.error("[reviewer] 缺少 API 配置");
126
+ return null;
127
+ }
128
+ // 获取规则:仅使用通用规则 + 自定义规则(不再支持内置 extends)
129
+ const rules = [...commonRules, ...(config.rules?.custom || [])];
130
+ // 构建 prompt
131
+ const systemPrompt = buildSystemPrompt();
132
+ const userPrompt = buildUserPrompt(effectiveDiff, rules);
133
+ // 调用 AI API
134
+ const rawContent = await callChatAPI(config.ai.endpoint, config.ai.apiKey, config.ai.model, systemPrompt, userPrompt);
135
+ if (!rawContent) {
136
+ return null;
137
+ }
138
+ // 解析响应
139
+ const result = parseAIResponse(rawContent);
140
+ if (!result) {
141
+ return null;
142
+ }
143
+ // 应用过滤和限制
144
+ let comments = result.comments || [];
145
+ // 过滤 severity
146
+ if (config.review?.severity) {
147
+ const severityOrder = { info: 0, warning: 1, error: 2 };
148
+ const minLevel = severityOrder[config.review.severity];
149
+ comments = comments.filter((c) => severityOrder[c.severity] >= minLevel);
150
+ }
151
+ // 限制评论数量
152
+ if (config.review?.maxComments &&
153
+ comments.length > config.review.maxComments) {
154
+ comments = comments.slice(0, config.review.maxComments);
155
+ console.warn(`[reviewer] 评论数量超过限制,仅保留前 ${config.review.maxComments} 条`);
156
+ }
157
+ return { comments };
158
+ };
159
+ exports.reviewCode = reviewCode;
160
+ //# sourceMappingURL=reviewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviewer.js","sourceRoot":"","sources":["../src/reviewer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * 规则库索引
3
+ */
4
+ export declare const builtinRules: Record<string, string[]>;
5
+ /**
6
+ * 通用代码质量规则
7
+ */
8
+ export declare const commonRules: string[];
9
+ /**
10
+ * 根据配置获取合并后的规则
11
+ */
12
+ export declare const getRules: (extends_?: string[], custom?: string[]) => string[];
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAIjD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,UAMvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,QAAQ,GAAI,WAAU,MAAM,EAAO,EAAE,SAAQ,MAAM,EAAO,KAAG,MAAM,EAmB/E,CAAC"}
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ /**
3
+ * 规则库索引
4
+ */
5
+ var __importDefault = (this && this.__importDefault) || function (mod) {
6
+ return (mod && mod.__esModule) ? mod : { "default": mod };
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.getRules = exports.commonRules = exports.builtinRules = void 0;
10
+ const react_1 = __importDefault(require("./react"));
11
+ const typescript_1 = __importDefault(require("./typescript"));
12
+ const vue_1 = __importDefault(require("./vue"));
13
+ exports.builtinRules = {
14
+ typescript: typescript_1.default,
15
+ react: react_1.default,
16
+ vue: vue_1.default,
17
+ };
18
+ /**
19
+ * 通用代码质量规则
20
+ */
21
+ exports.commonRules = [
22
+ '重点关注潜在 bug、性能问题、可读性与安全性(如 XSS、注入、敏感信息泄露)',
23
+ '代码要有适当的错误处理',
24
+ '避免代码重复,注意抽离复用逻辑',
25
+ '函数和变量命名要语义化,见名知意',
26
+ '复杂逻辑要添加注释说明',
27
+ ];
28
+ /**
29
+ * 根据配置获取合并后的规则
30
+ */
31
+ const getRules = (extends_ = [], custom = []) => {
32
+ const rules = [...exports.commonRules];
33
+ // 加载内置规则
34
+ for (const ruleName of extends_) {
35
+ const builtinRule = exports.builtinRules[ruleName];
36
+ if (builtinRule) {
37
+ rules.push(...builtinRule);
38
+ }
39
+ else {
40
+ console.warn(`[rules] 未找到内置规则: ${ruleName}`);
41
+ }
42
+ }
43
+ // 加载自定义规则
44
+ if (custom.length > 0) {
45
+ rules.push(...custom);
46
+ }
47
+ return rules;
48
+ };
49
+ exports.getRules = getRules;
50
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;;;AAEH,oDAAiC;AACjC,8DAA2C;AAC3C,gDAA6B;AAEhB,QAAA,YAAY,GAA6B;IACpD,UAAU,EAAE,oBAAe;IAC3B,KAAK,EAAE,eAAU;IACjB,GAAG,EAAE,aAAQ;CACd,CAAC;AAEF;;GAEG;AACU,QAAA,WAAW,GAAG;IACzB,0CAA0C;IAC1C,aAAa;IACb,iBAAiB;IACjB,kBAAkB;IAClB,aAAa;CACd,CAAC;AAEF;;GAEG;AACI,MAAM,QAAQ,GAAG,CAAC,WAAqB,EAAE,EAAE,SAAmB,EAAE,EAAY,EAAE;IACnF,MAAM,KAAK,GAAa,CAAC,GAAG,mBAAW,CAAC,CAAC;IAEzC,SAAS;IACT,KAAK,MAAM,QAAQ,IAAI,QAAQ,EAAE,CAAC;QAChC,MAAM,WAAW,GAAG,oBAAY,CAAC,QAAQ,CAAC,CAAC;QAC3C,IAAI,WAAW,EAAE,CAAC;YAChB,KAAK,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,oBAAoB,QAAQ,EAAE,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,UAAU;IACV,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;IACxB,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAnBW,QAAA,QAAQ,YAmBnB"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * React 代码规范规则
3
+ */
4
+ export declare const reactRules: string[];
5
+ export default reactRules;
6
+ //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/rules/react.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,UAAU,UAQtB,CAAC;AAEF,eAAe,UAAU,CAAC"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ /**
3
+ * React 代码规范规则
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.reactRules = void 0;
7
+ exports.reactRules = [
8
+ '使用函数组件和 Hooks,避免使用 class 组件',
9
+ '组件使用 PascalCase 命名,函数和变量使用 camelCase 命名',
10
+ '合理使用 useMemo 和 useCallback 优化性能,避免过度优化',
11
+ 'useEffect 的依赖项数组要完整准确',
12
+ '避免在循环中使用数组索引作为 key',
13
+ '组件职责单一,复杂组件要拆分',
14
+ 'Props 要有明确的 TypeScript 类型定义',
15
+ ];
16
+ exports.default = exports.reactRules;
17
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.js","sourceRoot":"","sources":["../../src/rules/react.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEU,QAAA,UAAU,GAAG;IACxB,6BAA6B;IAC7B,yCAAyC;IACzC,wCAAwC;IACxC,uBAAuB;IACvB,oBAAoB;IACpB,gBAAgB;IAChB,6BAA6B;CAC9B,CAAC;AAEF,kBAAe,kBAAU,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * TypeScript 代码规范规则
3
+ */
4
+ export declare const typescriptRules: string[];
5
+ export default typescriptRules;
6
+ //# sourceMappingURL=typescript.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript.d.ts","sourceRoot":"","sources":["../../src/rules/typescript.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,eAAe,UAO3B,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ /**
3
+ * TypeScript 代码规范规则
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.typescriptRules = void 0;
7
+ exports.typescriptRules = [
8
+ '使用 TypeScript 进行类型检查,确保代码健壮性',
9
+ '优先使用 interface 而非 type 来定义对象类型',
10
+ '避免使用 any 类型,尽可能使用具体类型或泛型',
11
+ '使用箭头函数声明函数,不要用 function 声明',
12
+ '合理使用可选链操作符(?.)和空值合并操作符(??)',
13
+ '避免使用魔法数字和魔法字符串,定义为常量',
14
+ ];
15
+ exports.default = exports.typescriptRules;
16
+ //# sourceMappingURL=typescript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript.js","sourceRoot":"","sources":["../../src/rules/typescript.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEU,QAAA,eAAe,GAAG;IAC7B,8BAA8B;IAC9B,gCAAgC;IAChC,0BAA0B;IAC1B,4BAA4B;IAC5B,4BAA4B;IAC5B,sBAAsB;CACvB,CAAC;AAEF,kBAAe,uBAAe,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Vue 代码规范规则
3
+ */
4
+ export declare const vueRules: string[];
5
+ export default vueRules;
6
+ //# sourceMappingURL=vue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vue.d.ts","sourceRoot":"","sources":["../../src/rules/vue.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,QAAQ,UAOpB,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ /**
3
+ * Vue 代码规范规则
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.vueRules = void 0;
7
+ exports.vueRules = [
8
+ '使用 Composition API (setup) 而非 Options API',
9
+ '组件使用 PascalCase 命名',
10
+ 'Props 要有明确的类型定义和默认值',
11
+ '合理使用 computed 和 watch,避免在 template 中写复杂逻辑',
12
+ '避免直接修改 props',
13
+ '组件职责单一,复杂组件要拆分',
14
+ ];
15
+ exports.default = exports.vueRules;
16
+ //# sourceMappingURL=vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vue.js","sourceRoot":"","sources":["../../src/rules/vue.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEU,QAAA,QAAQ,GAAG;IACtB,2CAA2C;IAC3C,oBAAoB;IACpB,qBAAqB;IACrB,2CAA2C;IAC3C,cAAc;IACd,gBAAgB;CACjB,CAAC;AAEF,kBAAe,gBAAQ,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * AI Code Review 类型定义
3
+ */
4
+ export type Severity = "info" | "warning" | "error";
5
+ export type AIProvider = "deepseek" | "openai" | "claude";
6
+ /**
7
+ * AI 模型配置
8
+ */
9
+ export interface AIConfig {
10
+ provider: AIProvider;
11
+ model: string;
12
+ endpoint?: string;
13
+ apiKey?: string;
14
+ }
15
+ /**
16
+ * 规则配置
17
+ */
18
+ export interface RulesConfig {
19
+ custom?: string[];
20
+ }
21
+ /**
22
+ * 审查策略配置
23
+ */
24
+ export interface ReviewConfig {
25
+ severity?: Severity;
26
+ ignore?: string[];
27
+ maxComments?: number;
28
+ }
29
+ /**
30
+ * 完整配置接口
31
+ */
32
+ export interface CodeReviewConfig {
33
+ ai: AIConfig;
34
+ rules?: RulesConfig;
35
+ review?: ReviewConfig;
36
+ }
37
+ /**
38
+ * AI 返回的评论
39
+ */
40
+ export interface AIComment {
41
+ file: string;
42
+ line: number;
43
+ severity: Severity;
44
+ message: string;
45
+ suggestion?: string;
46
+ }
47
+ /**
48
+ * AI 响应
49
+ */
50
+ export interface AIResponse {
51
+ comments: AIComment[];
52
+ }
53
+ /**
54
+ * Reviewdog JSON 格式 - 诊断信息
55
+ */
56
+ export interface RdjsonDiagnostic {
57
+ message: string;
58
+ location: {
59
+ path: string;
60
+ range: {
61
+ start: {
62
+ line: number;
63
+ column: number;
64
+ };
65
+ };
66
+ };
67
+ severity: "ERROR" | "WARNING" | "INFO";
68
+ }
69
+ /**
70
+ * Reviewdog JSON 格式 - 完整输出
71
+ */
72
+ export interface RdjsonOutput {
73
+ source: {
74
+ name: string;
75
+ };
76
+ diagnostics: RdjsonDiagnostic[];
77
+ }
78
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAEpD,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,UAAU,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,QAAQ,CAAC;IACb,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,SAAS,EAAE,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE;YACL,KAAK,EAAE;gBACL,IAAI,EAAE,MAAM,CAAC;gBACb,MAAM,EAAE,MAAM,CAAC;aAChB,CAAC;SACH,CAAC;KACH,CAAC;IACF,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;CACxC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,WAAW,EAAE,gBAAgB,EAAE,CAAC;CACjC"}
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ /**
3
+ * AI Code Review 类型定义
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;GAEG"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 简单的 glob -> RegExp 转换,只覆盖当前项目常用模式:
3
+ */
4
+ export declare const globToRegExp: (pattern: string) => RegExp;
5
+ export declare const matchGlob: (filePath: string, pattern: string) => boolean;
6
+ export declare const shouldIgnoreFile: (filePath: string, ignorePatterns: string[] | undefined) => boolean;
7
+ /**
8
+ * 根据 ignore 规则过滤 diff 中的文件:
9
+ * - 通过 `diff --git a/xxx b/xxx` 识别每个文件的 diff 块
10
+ * - 命中 ignore 的文件整块跳过
11
+ */
12
+ export declare const filterDiffByIgnore: (diff: string, ignorePatterns: string[] | undefined) => string;
13
+ /**
14
+ * 读取 stdin
15
+ */
16
+ export declare const readStdin: () => Promise<string>;
17
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,YAAY,GAAI,SAAS,MAAM,KAAG,MAY9C,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,UAAU,MAAM,EAAE,SAAS,MAAM,KAAG,OAG7D,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC3B,UAAU,MAAM,EAChB,gBAAgB,MAAM,EAAE,GAAG,SAAS,KACnC,OAGF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAC7B,MAAM,MAAM,EACZ,gBAAgB,MAAM,EAAE,GAAG,SAAS,KACnC,MAuCF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS,QAAO,OAAO,CAAC,MAAM,CAavC,CAAC"}
package/dist/utils.js ADDED
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readStdin = exports.filterDiffByIgnore = exports.shouldIgnoreFile = exports.matchGlob = exports.globToRegExp = void 0;
4
+ /**
5
+ * 简单的 glob -> RegExp 转换,只覆盖当前项目常用模式:
6
+ */
7
+ const globToRegExp = (pattern) => {
8
+ // 先对正则关键字符转义
9
+ let regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
10
+ // 处理 **/ 和 /** 形式,允许跨目录
11
+ regex = regex.replace(/\\\*\\\*\\\//g, "(.*/)?"); // **/foo => (任何目录)/foo
12
+ regex = regex.replace(/\/\\\*\\\*$/g, "/.*"); // foo/** => foo/后面任意
13
+ // 剩余的 * 匹配单路径段中的任意字符
14
+ regex = regex.replace(/\\\*/g, "[^/]*");
15
+ return new RegExp(`^${regex}$`);
16
+ };
17
+ exports.globToRegExp = globToRegExp;
18
+ const matchGlob = (filePath, pattern) => {
19
+ const re = (0, exports.globToRegExp)(pattern);
20
+ return re.test(filePath);
21
+ };
22
+ exports.matchGlob = matchGlob;
23
+ const shouldIgnoreFile = (filePath, ignorePatterns) => {
24
+ if (!ignorePatterns || ignorePatterns.length === 0)
25
+ return false;
26
+ return ignorePatterns.some((p) => (0, exports.matchGlob)(filePath, p));
27
+ };
28
+ exports.shouldIgnoreFile = shouldIgnoreFile;
29
+ /**
30
+ * 根据 ignore 规则过滤 diff 中的文件:
31
+ * - 通过 `diff --git a/xxx b/xxx` 识别每个文件的 diff 块
32
+ * - 命中 ignore 的文件整块跳过
33
+ */
34
+ const filterDiffByIgnore = (diff, ignorePatterns) => {
35
+ if (!ignorePatterns || ignorePatterns.length === 0 || !diff.trim()) {
36
+ return diff;
37
+ }
38
+ const lines = diff.split("\n");
39
+ const kept = [];
40
+ let currentFile = null;
41
+ let ignoreCurrent = false;
42
+ for (const line of lines) {
43
+ if (line.startsWith("diff --git ")) {
44
+ // 新文件块开始,先重置状态
45
+ currentFile = null;
46
+ ignoreCurrent = false;
47
+ const match = /^diff --git a\/(.+?) b\/(.+)$/.exec(line);
48
+ if (match) {
49
+ // 通常 a/ 和 b/ 路径相同,使用 b/ 路径
50
+ currentFile = match[2];
51
+ ignoreCurrent = (0, exports.shouldIgnoreFile)(currentFile, ignorePatterns);
52
+ }
53
+ if (!ignoreCurrent) {
54
+ kept.push(line);
55
+ }
56
+ continue;
57
+ }
58
+ if (ignoreCurrent) {
59
+ // 当前文件被 ignore,整块 diff 跳过直到下一个 diff --git
60
+ continue;
61
+ }
62
+ kept.push(line);
63
+ }
64
+ return kept.join("\n");
65
+ };
66
+ exports.filterDiffByIgnore = filterDiffByIgnore;
67
+ /**
68
+ * 读取 stdin
69
+ */
70
+ const readStdin = () => new Promise((resolve, reject) => {
71
+ let data = "";
72
+ process.stdin.setEncoding("utf8");
73
+ process.stdin.on("data", (chunk) => {
74
+ data += chunk;
75
+ });
76
+ process.stdin.on("end", () => {
77
+ resolve(data);
78
+ });
79
+ process.stdin.on("error", (err) => {
80
+ reject(err);
81
+ });
82
+ });
83
+ exports.readStdin = readStdin;
84
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACI,MAAM,YAAY,GAAG,CAAC,OAAe,EAAU,EAAE;IACtD,aAAa;IACb,IAAI,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;IAEzD,wBAAwB;IACxB,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC,uBAAuB;IACzE,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC,CAAC,qBAAqB;IAEnE,qBAAqB;IACrB,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAExC,OAAO,IAAI,MAAM,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC;AAClC,CAAC,CAAC;AAZW,QAAA,YAAY,gBAYvB;AAEK,MAAM,SAAS,GAAG,CAAC,QAAgB,EAAE,OAAe,EAAW,EAAE;IACtE,MAAM,EAAE,GAAG,IAAA,oBAAY,EAAC,OAAO,CAAC,CAAC;IACjC,OAAO,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC3B,CAAC,CAAC;AAHW,QAAA,SAAS,aAGpB;AAEK,MAAM,gBAAgB,GAAG,CAC9B,QAAgB,EAChB,cAAoC,EAC3B,EAAE;IACX,IAAI,CAAC,cAAc,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACjE,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,iBAAS,EAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;AAC5D,CAAC,CAAC;AANW,QAAA,gBAAgB,oBAM3B;AAEF;;;;GAIG;AACI,MAAM,kBAAkB,GAAG,CAChC,IAAY,EACZ,cAAoC,EAC5B,EAAE;IACV,IAAI,CAAC,cAAc,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QACnE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAa,EAAE,CAAC;IAE1B,IAAI,WAAW,GAAkB,IAAI,CAAC;IACtC,IAAI,aAAa,GAAG,KAAK,CAAC;IAE1B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YACnC,eAAe;YACf,WAAW,GAAG,IAAI,CAAC;YACnB,aAAa,GAAG,KAAK,CAAC;YAEtB,MAAM,KAAK,GAAG,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzD,IAAI,KAAK,EAAE,CAAC;gBACV,2BAA2B;gBAC3B,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBACvB,aAAa,GAAG,IAAA,wBAAgB,EAAC,WAAW,EAAE,cAAc,CAAC,CAAC;YAChE,CAAC;YAED,IAAI,CAAC,aAAa,EAAE,CAAC;gBACnB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClB,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,aAAa,EAAE,CAAC;YAClB,0CAA0C;YAC1C,SAAS;QACX,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC,CAAC;AA1CW,QAAA,kBAAkB,sBA0C7B;AAEF;;GAEG;AACI,MAAM,SAAS,GAAG,GAAoB,EAAE,CAC7C,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IAC9B,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;QACjC,IAAI,IAAI,KAAK,CAAC;IAChB,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;QAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QAChC,MAAM,CAAC,GAAG,CAAC,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAbQ,QAAA,SAAS,aAajB"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@wuyanbin/ai-code-review",
3
+ "version": "1.0.0",
4
+ "description": "AI-powered code review tool for GitLab CI/CD",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "bin": {
11
+ "ai-code-review": "bin/ai-code-review.js"
12
+ },
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "dev": "tsc --watch",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "keywords": [
19
+ "ai",
20
+ "code-review",
21
+ "gitlab",
22
+ "ci-cd",
23
+ "reviewdog"
24
+ ],
25
+ "author": "binn <binn@micous.com>",
26
+ "license": "MIT",
27
+ "files": ["dist", "bin", "README.md"],
28
+ "dependencies": {
29
+ "cosmiconfig": "^9.0.0",
30
+ "minimist": "^1.2.8"
31
+ },
32
+ "devDependencies": {
33
+ "@types/minimist": "^1.2.5",
34
+ "@types/node": "^20.0.0",
35
+ "typescript": "^5.6.3"
36
+ },
37
+ "engines": {
38
+ "node": ">=18.0.0"
39
+ }
40
+ }
41
+