code-gate 0.1.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 (48) hide show
  1. package/README.md +118 -0
  2. package/bin/code-gate.js +29 -0
  3. package/dist/__tests__/config.test.d.ts +1 -0
  4. package/dist/__tests__/config.test.js +28 -0
  5. package/dist/__tests__/config.test.js.map +1 -0
  6. package/dist/__tests__/render.test.d.ts +1 -0
  7. package/dist/__tests__/render.test.js +16 -0
  8. package/dist/__tests__/render.test.js.map +1 -0
  9. package/dist/cli.d.ts +1 -0
  10. package/dist/cli.js +32 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/commands/hook.d.ts +1 -0
  13. package/dist/commands/hook.js +71 -0
  14. package/dist/commands/hook.js.map +1 -0
  15. package/dist/commands/init.d.ts +1 -0
  16. package/dist/commands/init.js +129 -0
  17. package/dist/commands/init.js.map +1 -0
  18. package/dist/commands/setup.d.ts +1 -0
  19. package/dist/commands/setup.js +19 -0
  20. package/dist/commands/setup.js.map +1 -0
  21. package/dist/config.d.ts +40 -0
  22. package/dist/config.js +86 -0
  23. package/dist/config.js.map +1 -0
  24. package/dist/core/review-flow.d.ts +1 -0
  25. package/dist/core/review-flow.js +109 -0
  26. package/dist/core/review-flow.js.map +1 -0
  27. package/dist/git.d.ts +4 -0
  28. package/dist/git.js +32 -0
  29. package/dist/git.js.map +1 -0
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.js +2 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/llm/deepseek.d.ts +6 -0
  34. package/dist/llm/deepseek.js +24 -0
  35. package/dist/llm/deepseek.js.map +1 -0
  36. package/dist/llm/ollama.d.ts +6 -0
  37. package/dist/llm/ollama.js +81 -0
  38. package/dist/llm/ollama.js.map +1 -0
  39. package/dist/log.d.ts +3 -0
  40. package/dist/log.js +10 -0
  41. package/dist/log.js.map +1 -0
  42. package/dist/ui/render.d.ts +18 -0
  43. package/dist/ui/render.js +138 -0
  44. package/dist/ui/render.js.map +1 -0
  45. package/dist/ui/server.d.ts +2 -0
  46. package/dist/ui/server.js +49 -0
  47. package/dist/ui/server.js.map +1 -0
  48. package/package.json +52 -0
package/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # code-gate
2
+
3
+ AI 助力的提交时代码 Review 工具,支持本地 Ollama 或 DeepSeek API,审查 `git commit` 的 `staged diff`,并以 GitHub Diff 风格在本地页面展示结果。
4
+
5
+ ## 安装
6
+ - `npm i -D code-gate`
7
+
8
+ ## 初始化与集成
9
+ - 推荐使用 `init` 命令,选择初始化方式并自动生成配置文件:
10
+ - 原生 Git Hooks:`npx code-gate init -m git`
11
+ - Husky:`npx code-gate init -m husky`
12
+ - simple-git-hooks:`npx code-gate init -m simple`
13
+ - 跳过配置文件生成:`npx code-gate init --no-config`
14
+ - 仍支持旧方式:
15
+ - 原生 Git Hooks 快速安装:`npx code-gate setup`
16
+ - 该命令会创建 `.githooks/pre-commit` 并设置 `core.hooksPath`
17
+
18
+ ## 手动初始化(不覆盖现有钩子)
19
+ - Husky:
20
+ - 编辑 `.husky/pre-commit`,在原有内容后追加一行:
21
+ - `npx code-gate hook`
22
+ - 确认 `git config core.hooksPath` 输出 `.husky`
23
+ - 原生 Git Hooks:
24
+ - 在项目根创建 `.githooks/pre-commit`,内容示例:
25
+ - `#!/usr/bin/env sh`
26
+ - `npx code-gate hook`
27
+ - 设置 `core.hooksPath`:
28
+ - `git config core.hooksPath .githooks`
29
+ - 配置文件生成:
30
+ - 在项目根新建 `code-gate.config.json`(可从下文示例复制并按需调整)
31
+ - 如使用 DeepSeek,请在环境变量设置 `DEEPSEEK_API_KEY`
32
+
33
+ ## 命令
34
+ - `npx code-gate init` 交互式初始化(可选择 git/husky/simple,并生成配置文件)
35
+ - `npx code-gate setup` 快速安装原生 Git Hook
36
+ - `npx code-gate hook` 在 Hook 中执行交互式 Review
37
+
38
+ ## 本地开发(link 调试)
39
+ - 在 `code-gate` 仓库:
40
+ - `npm install`
41
+ - `npm run build:watch`
42
+ - `npm link`
43
+ - 在目标项目:
44
+ - `npm link code-gate`
45
+ - `npx code-gate init -m git`(或 `husky`/`simple`)
46
+ - `git add` + `git commit` 时触发审查流程
47
+
48
+ ## 配置文件
49
+ - 推荐使用 `.codegate.js`(支持注释与更灵活的写法),兼容旧的 `code-gate.config.json/.yaml`、`.code-gaterc.{json,yaml}`。
50
+ - 示例(`.codegate.js`):
51
+ ```js
52
+ // provider: 选择使用的 AI 审查引擎,可选值: 'ollama' | 'deepseek'
53
+ // review: 审查相关设置
54
+ // - enabled: 是否启用审查
55
+ // - provider: 覆盖顶层 provider,支持按需切换,可选值: 'ollama' | 'deepseek'
56
+ // - mode: 审查展示模式,可选值: 'aggregate'(整合输出)| 'per_file'(按文件 Tab 展示)
57
+ // ollama: 本地 Ollama 设置
58
+ // - baseURL: Ollama 服务地址,默认 'http://localhost:11434'
59
+ // - model: 模型名称,例如 'qwen3:8b'、'deepseek-r1:14b'
60
+ // deepseek: DeepSeek 云端设置
61
+ // - baseURL: API 地址,默认 'https://api.deepseek.com'
62
+ // - apiKeyEnv: 存放密钥的环境变量名,默认 'DEEPSEEK_API_KEY'
63
+ // - model: 模型名称,默认 'deepseek-chat'
64
+ // fileTypes: 需要审查的文件类型扩展名列表
65
+ // scope: 审查范围,可选值: 'staged' | 'allChanged' | { include?: string[], exclude?: string[] }
66
+ // ui: 页面与交互设置
67
+ // - openBrowser: 是否自动打开浏览器
68
+ // - theme: 主题,目前仅 'github'
69
+ // - port: 预览服务端口
70
+ // limits: 限制项
71
+ // - maxDiffLines: 最大 diff 行数
72
+ // - maxFiles: 最大审查文件数
73
+ // rules: 审查关注点,可选值: 'security' | 'performance' | 'style' | 'tests'
74
+ // prompt: 通用提示词
75
+ // output: 输出目录配置
76
+ // - dir: 本地输出目录
77
+ export default {
78
+ provider: 'deepseek',
79
+ review: { enabled: true, mode: 'aggregate' },
80
+ deepseek: { baseURL: 'https://api.deepseek.com', apiKeyEnv: 'DEEPSEEK_API_KEY', model: 'deepseek-chat' },
81
+ ollama: { baseURL: 'http://localhost:11434', model: 'qwen3:8b' },
82
+ fileTypes: ['ts', 'tsx', 'js', 'jsx', 'json', 'md', 'py', 'go', 'rs'],
83
+ scope: 'staged',
84
+ ui: { openBrowser: true, theme: 'github', port: 5175 },
85
+ limits: { maxDiffLines: 10000, maxFiles: 100 },
86
+ rules: { focus: ['security', 'performance', 'style', 'tests'] },
87
+ prompt: '作为资深代码审查工程师,从安全、性能、代码风格与测试覆盖角度审查本次变更,指出问题与改进建议,并给出必要的示例补丁。',
88
+ output: { dir: '.code-gate' }
89
+ }
90
+ ```
91
+
92
+ ## 使用流程
93
+ - 运行 `git commit` 时会询问是否进行 Review:
94
+ - 选择否:正常提交。
95
+ - 选择是:抓取 `staged diff` 调用 AI 审查,生成本地页面并打印预览 URL,再询问是否继续提交。
96
+ - 非交互环境会自动跳过。
97
+ - 如需在非交互环境强制执行,可在 `.husky/pre-commit` 中使用:`npx code-gate hook -f`
98
+ - 页面顶部会显示 AI 状态(是否参与、Provider、Model、错误信息)
99
+ - 支持按文件 Tab 展示:在配置中设置 `"review": { "mode": "per_file" }`,每个文件一个 Tab,Tabs 超出视野时横向滚动,每个文件顶部展示该文件的审查内容
100
+
101
+ ## 故障排查
102
+ - 页面只有 diff、没有 AI 审查内容:
103
+ - DeepSeek:确保设置了环境变量 `DEEPSEEK_API_KEY`,并且 `provider` 为 `deepseek`;可在 shell 中 `export DEEPSEEK_API_KEY="your_key"`
104
+ - Ollama:确保本地 Ollama 正在运行(默认 `http://localhost:11434`),并且模型已安装;例如 `ollama list` 查看,`ollama pull qwen2.5-coder`
105
+ - 可在 `code-gate.config.json` 中切换 `provider`,调整 `prompt` 与 `ui.port`
106
+ - 出错时页面顶部会显示原因与解决建议
107
+
108
+ ## DeepSeek 集成
109
+ - 使用 OpenAI 兼容接口 `https://api.deepseek.com`,需在环境变量设置密钥:`DEEPSEEK_API_KEY`。
110
+ - 参考文档:https://api-docs.deepseek.com/
111
+
112
+ ## Ollama 集成
113
+ - 通过本地 HTTP 接口调用,不内置安装;需用户自行安装与启动 Ollama。
114
+ - 默认地址:`http://localhost:11434`
115
+
116
+ ## 注意
117
+ - 不会将密钥写入仓库;配置建议走环境变量。
118
+ - 大 Diff 会消耗模型 token,可通过 `limits` 控制。
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ import('../dist/cli.js')
3
+ .then(async (mod) => {
4
+ if (mod && typeof mod.run === 'function') {
5
+ await mod.run()
6
+ } else {
7
+ console.error('code-gate: CLI not found in dist/cli.js')
8
+ process.exit(1)
9
+ }
10
+ })
11
+ .catch(async () => {
12
+ const { createRequire } = await import('module')
13
+ const require = createRequire(import.meta.url)
14
+ const path = require('path')
15
+ const url = require('url')
16
+ const fs = require('fs')
17
+ const dist = path.join(path.dirname(url.fileURLToPath(import.meta.url)), '..', 'dist', 'cli.js')
18
+ if (fs.existsSync(dist)) {
19
+ const mod = await import(url.pathToFileURL(dist).href)
20
+ if (mod && typeof mod.run === 'function') await mod.run()
21
+ else {
22
+ console.error('code-gate: CLI not found in dist/cli.js')
23
+ process.exit(1)
24
+ }
25
+ } else {
26
+ console.error('code-gate is not built yet. Run `npm run build`.')
27
+ process.exit(1)
28
+ }
29
+ })
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { loadConfig } from '../config.js';
5
+ function withTempConfig(content, ext = '.json') {
6
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'code-gate-test-'));
7
+ const file = path.join(dir, `code-gate.config${ext}`);
8
+ fs.writeFileSync(file, ext === '.json' ? JSON.stringify(content) : content, 'utf8');
9
+ return { dir, file };
10
+ }
11
+ async function run() {
12
+ const { dir } = withTempConfig({
13
+ provider: 'deepseek',
14
+ ui: { port: 6000, openBrowser: false },
15
+ prompt: '通用提示词'
16
+ });
17
+ const cfg = await loadConfig(dir);
18
+ if (cfg.ui?.port !== 6000)
19
+ throw new Error('ui.port merge failed');
20
+ if (cfg.prompt !== '通用提示词')
21
+ throw new Error('prompt merge failed');
22
+ process.stdout.write('config.test passed\n');
23
+ }
24
+ run().catch((e) => {
25
+ process.stderr.write(String(e) + '\n');
26
+ process.exit(1);
27
+ });
28
+ //# sourceMappingURL=config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../src/__tests__/config.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzC,SAAS,cAAc,CAAC,OAAY,EAAE,GAAG,GAAG,OAAO;IACjD,MAAM,GAAG,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAA;IACrE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,mBAAmB,GAAG,EAAE,CAAC,CAAA;IACrD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IACnF,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAA;AACtB,CAAC;AAED,KAAK,UAAU,GAAG;IAChB,MAAM,EAAE,GAAG,EAAE,GAAG,cAAc,CAAC;QAC7B,QAAQ,EAAE,UAAU;QACpB,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE;QACtC,MAAM,EAAE,OAAO;KAChB,CAAC,CAAA;IACF,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,CAAA;IACjC,IAAI,GAAG,CAAC,EAAE,EAAE,IAAI,KAAK,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAA;IAClE,IAAI,GAAG,CAAC,MAAM,KAAK,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAA;IAClE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAA;AAC9C,CAAC;AAED,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;IAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;IACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
1
+ import { renderHTML } from '../ui/render.js';
2
+ const sampleDiff = `diff --git a/a.txt b/a.txt
3
+ index e69de29..4b825dc 100644
4
+ --- a/a.txt
5
+ +++ b/a.txt
6
+ @@ -0,0 +1,1 @@
7
+ +hello
8
+ `;
9
+ function run() {
10
+ const html = renderHTML(sampleDiff, 'review');
11
+ if (!html.includes('Code Review'))
12
+ throw new Error('render failed');
13
+ process.stdout.write('render.test passed\n');
14
+ }
15
+ run();
16
+ //# sourceMappingURL=render.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.test.js","sourceRoot":"","sources":["../../src/__tests__/render.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE5C,MAAM,UAAU,GAAG;;;;;;CAMlB,CAAA;AAED,SAAS,GAAG;IACV,MAAM,IAAI,GAAG,UAAU,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;IAC7C,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAA;IACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAA;AAC9C,CAAC;AAED,GAAG,EAAE,CAAA"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function run(): Promise<void>;
package/dist/cli.js ADDED
@@ -0,0 +1,32 @@
1
+ import { Command } from 'commander';
2
+ import { runSetup } from './commands/setup.js';
3
+ import { runHook } from './commands/hook.js';
4
+ import { runInit } from './commands/init.js';
5
+ export async function run() {
6
+ const program = new Command();
7
+ program.name('code-gate').description('AI commit review tool').version('0.1.0');
8
+ program
9
+ .command('init')
10
+ .description('Initialize integration and generate config')
11
+ .option('-m, --method <method>', 'init method: git|husky|simple', 'git')
12
+ .option('-f, --force', 'force overwrite/append')
13
+ .option('--no-config', 'do not generate config file')
14
+ .action(async (opts) => {
15
+ await runInit(opts.method, !!opts.config, !!opts.force);
16
+ });
17
+ program
18
+ .command('setup')
19
+ .description('Install git hook integration')
20
+ .action(async () => {
21
+ await runSetup();
22
+ });
23
+ program
24
+ .command('hook')
25
+ .description('Run interactive pre-commit review')
26
+ .option('-f, --force', 'force review even when non-interactive', false)
27
+ .action(async (opts) => {
28
+ await runHook(!!opts.force);
29
+ });
30
+ program.parseAsync(process.argv);
31
+ }
32
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAE5C,MAAM,CAAC,KAAK,UAAU,GAAG;IACvB,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;IAC7B,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,WAAW,CAAC,uBAAuB,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAE/E,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,4CAA4C,CAAC;SACzD,MAAM,CAAC,uBAAuB,EAAE,+BAA+B,EAAE,KAAK,CAAC;SACvE,MAAM,CAAC,aAAa,EAAE,wBAAwB,CAAC;SAC/C,MAAM,CAAC,aAAa,EAAE,6BAA6B,CAAC;SACpD,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,MAAM,OAAO,CAAC,IAAI,CAAC,MAAgB,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACnE,CAAC,CAAC,CAAA;IAEJ,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,8BAA8B,CAAC;SAC3C,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,QAAQ,EAAE,CAAA;IAClB,CAAC,CAAC,CAAA;IAEJ,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,mCAAmC,CAAC;SAChD,MAAM,CAAC,aAAa,EAAE,wCAAwC,EAAE,KAAK,CAAC;SACtE,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,MAAM,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC7B,CAAC,CAAC,CAAA;IAEJ,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;AAClC,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function runHook(force?: boolean): Promise<void>;
@@ -0,0 +1,71 @@
1
+ import readline from 'node:readline';
2
+ import fs from 'node:fs';
3
+ import { runReviewFlow } from '../core/review-flow.js';
4
+ function isInteractive() {
5
+ return process.stdin.isTTY && process.stdout.isTTY;
6
+ }
7
+ function askYesNo(question) {
8
+ const input = process.stdin.isTTY ? process.stdin : (fs.existsSync('/dev/tty') ? fs.createReadStream('/dev/tty') : process.stdin);
9
+ return new Promise((resolve) => {
10
+ const rl = readline.createInterface({ input, output: process.stdout });
11
+ rl.question(question, (answer) => {
12
+ rl.close();
13
+ resolve(answer.trim().toLowerCase() === 'y');
14
+ });
15
+ });
16
+ }
17
+ function startProgress(title) {
18
+ let i = 0;
19
+ const marks = ['.', '..', '...', '....'];
20
+ process.stdout.write(`${title}`);
21
+ const timer = setInterval(() => {
22
+ i = (i + 1) % marks.length;
23
+ process.stdout.write(`${marks[i]}`);
24
+ }, 500);
25
+ return () => {
26
+ clearInterval(timer);
27
+ process.stdout.write('\n');
28
+ };
29
+ }
30
+ export async function runHook(force = false) {
31
+ const canPrompt = isInteractive() || fs.existsSync('/dev/tty');
32
+ if (!canPrompt && !force) {
33
+ process.stdout.write('code-gate: non-interactive environment, skipping review\n');
34
+ process.exit(0);
35
+ return;
36
+ }
37
+ const yes = await askYesNo('\n========================================\n需要进行本次提交的代码 Review 吗?(🟢Y/🔴N)\n========================================\n');
38
+ if (!yes) {
39
+ process.stdin.pause();
40
+ setImmediate(() => process.exit(0));
41
+ return;
42
+ }
43
+ const stop = startProgress('正在进行 AI 审查');
44
+ const ok = await runReviewFlow();
45
+ stop();
46
+ if (!ok) {
47
+ const cont = await askYesNo('\n========================================\nReview 已完成,是否继续提交?(🟢Y/🔴N)\n========================================\n');
48
+ if (cont) {
49
+ process.stdin.pause();
50
+ setImmediate(() => process.exit(0));
51
+ }
52
+ else {
53
+ process.stdout.write('已取消提交\n');
54
+ process.stdin.pause();
55
+ setImmediate(() => process.exit(1));
56
+ }
57
+ }
58
+ else {
59
+ const cont = await askYesNo('\n========================================\nReview 已完成,是否继续提交?(🟢Y/🔴N)\n========================================\n');
60
+ if (cont) {
61
+ process.stdin.pause();
62
+ setImmediate(() => process.exit(0));
63
+ }
64
+ else {
65
+ process.stdout.write('已取消提交\n');
66
+ process.stdin.pause();
67
+ setImmediate(() => process.exit(1));
68
+ }
69
+ }
70
+ }
71
+ //# sourceMappingURL=hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.js","sourceRoot":"","sources":["../../src/commands/hook.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function runInit(method: string, genConfig: boolean, force?: boolean): Promise<void>;
@@ -0,0 +1,129 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { spawnSync } from 'node:child_process';
4
+ function writeFileSafe(p, content) {
5
+ fs.mkdirSync(path.dirname(p), { recursive: true });
6
+ fs.writeFileSync(p, content, 'utf8');
7
+ }
8
+ function isGitRepo(cwd) {
9
+ const res = spawnSync('git', ['rev-parse', '--is-inside-work-tree'], { cwd });
10
+ return res.status === 0;
11
+ }
12
+ function initGitHooks(cwd) {
13
+ const hooksDir = path.join(cwd, '.githooks');
14
+ const preCommit = path.join(hooksDir, 'pre-commit');
15
+ const hookLine = `npx code-gate hook`;
16
+ ensurePreCommitContains(preCommit, hookLine, 'git');
17
+ spawnSync('git', ['config', 'core.hooksPath', '.githooks'], { cwd, stdio: 'inherit' });
18
+ process.stdout.write('code-gate: initialized with native git hooks (.githooks)\n');
19
+ }
20
+ function ensurePreCommitContains(p, line, type) {
21
+ if (fs.existsSync(p)) {
22
+ const content = fs.readFileSync(p, 'utf8');
23
+ if (!content.includes(line)) {
24
+ fs.writeFileSync(p, content.trimEnd() + '\n' + line + '\n', 'utf8');
25
+ }
26
+ return;
27
+ }
28
+ if (type === 'husky') {
29
+ const content = [
30
+ '#!/usr/bin/env sh',
31
+ '. "$(dirname -- "$0")/_/husky.sh"',
32
+ line
33
+ ].join('\n') + '\n';
34
+ writeFileSafe(p, content);
35
+ fs.chmodSync(p, 0o755);
36
+ return;
37
+ }
38
+ const content = ['#!/usr/bin/env sh', line].join('\n') + '\n';
39
+ writeFileSafe(p, content);
40
+ fs.chmodSync(p, 0o755);
41
+ }
42
+ function initHusky(cwd) {
43
+ const huskyDir = path.join(cwd, '.husky');
44
+ if (!fs.existsSync(huskyDir)) {
45
+ spawnSync('npx', ['husky', 'init'], { cwd, stdio: 'inherit' });
46
+ }
47
+ const preCommit = path.join(huskyDir, 'pre-commit');
48
+ const hookLine = `npx code-gate hook`;
49
+ ensurePreCommitContains(preCommit, hookLine, 'husky');
50
+ spawnSync('git', ['config', 'core.hooksPath', '.husky'], { cwd, stdio: 'inherit' });
51
+ process.stdout.write('code-gate: initialized with husky (.husky)\n');
52
+ }
53
+ function initSimpleGitHooks(cwd) {
54
+ const pkgPath = path.join(cwd, 'package.json');
55
+ if (!fs.existsSync(pkgPath)) {
56
+ process.stderr.write('code-gate: package.json not found for simple-git-hooks\n');
57
+ return;
58
+ }
59
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
60
+ pkg['simple-git-hooks'] = { ...(pkg['simple-git-hooks'] || {}), 'pre-commit': 'npx code-gate hook' };
61
+ pkg.scripts = { ...(pkg.scripts || {}), postinstall: 'simple-git-hooks' };
62
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2), 'utf8');
63
+ process.stdout.write('code-gate: initialized simple-git-hooks config in package.json\n');
64
+ }
65
+ function generateConfig(cwd, force = false) {
66
+ const configPath = path.join(cwd, '.codegate.js');
67
+ if (fs.existsSync(configPath) && !force)
68
+ return;
69
+ const content = `// code-gate 配置文件
70
+ // provider: 选择使用的 AI 审查引擎,可选值: 'ollama' | 'deepseek'
71
+ // review: 审查相关设置
72
+ // - enabled: 是否启用审查
73
+ // - provider: 覆盖顶层 provider,支持按需切换,可选值: 'ollama' | 'deepseek'
74
+ // - mode: 审查展示模式,可选值: 'aggregate'(整合输出)| 'per_file'(按文件 Tab 展示)
75
+ // ollama: 本地 Ollama 设置
76
+ // - baseURL: Ollama 服务地址,默认 'http://localhost:11434'
77
+ // - model: 模型名称,例如 'qwen3:8b'、'deepseek-r1:14b'
78
+ // deepseek: DeepSeek 云端设置
79
+ // - baseURL: API 地址,默认 'https://api.deepseek.com'
80
+ // - apiKeyEnv: 存放密钥的环境变量名,默认 'DEEPSEEK_API_KEY'
81
+ // - model: 模型名称,默认 'deepseek-chat'
82
+ // fileTypes: 需要审查的文件类型扩展名列表
83
+ // scope: 审查范围,可选值: 'staged' | 'allChanged' | { include?: string[], exclude?: string[] }
84
+ // ui: 页面与交互设置
85
+ // - openBrowser: 是否自动打开浏览器
86
+ // - theme: 主题,目前仅 'github'
87
+ // - port: 预览服务端口
88
+ // limits: 限制项
89
+ // - maxDiffLines: 最大 diff 行数
90
+ // - maxFiles: 最大审查文件数
91
+ // rules: 审查关注点,可选值: 'security' | 'performance' | 'style' | 'tests'
92
+ // prompt: 通用提示词
93
+ // output: 输出目录配置
94
+ // - dir: 本地输出目录
95
+
96
+ export default {
97
+ provider: 'deepseek',
98
+ review: { enabled: true, mode: 'per_file' },
99
+ deepseek: { baseURL: 'https://api.deepseek.com', apiKeyEnv: 'DEEPSEEK_API_KEY', model: 'deepseek-chat' },
100
+ ollama: { baseURL: 'http://localhost:11434', model: 'qwen3:8b' },
101
+ fileTypes: ['ts', 'tsx', 'js', 'jsx', 'json', 'md', 'py', 'go', 'rs'],
102
+ scope: 'staged',
103
+ ui: { openBrowser: true, theme: 'github', port: 5175 },
104
+ limits: { maxDiffLines: 10000, maxFiles: 100 },
105
+ rules: { focus: ['security', 'performance', 'style', 'tests'] },
106
+ prompt: '作为资深代码审查工程师,从安全、性能、代码风格与测试覆盖角度审查本次变更,指出问题与改进建议,并给出必要的示例补丁。',
107
+ output: { dir: '.code-gate' }
108
+ }
109
+ `;
110
+ fs.writeFileSync(configPath, content, 'utf8');
111
+ process.stdout.write('code-gate: generated .codegate.js\n');
112
+ }
113
+ export async function runInit(method, genConfig, force = false) {
114
+ const cwd = process.cwd();
115
+ if (!isGitRepo(cwd)) {
116
+ process.stderr.write('code-gate: not a git repository\n');
117
+ process.exit(1);
118
+ return;
119
+ }
120
+ if (method === 'husky')
121
+ initHusky(cwd);
122
+ else if (method === 'simple')
123
+ initSimpleGitHooks(cwd);
124
+ else
125
+ initGitHooks(cwd);
126
+ if (genConfig)
127
+ generateConfig(cwd, force);
128
+ }
129
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function runSetup(): Promise<void>;
@@ -0,0 +1,19 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { spawnSync } from 'node:child_process';
4
+ function writeFileSafe(p, content) {
5
+ fs.mkdirSync(path.dirname(p), { recursive: true });
6
+ fs.writeFileSync(p, content, 'utf8');
7
+ }
8
+ export async function runSetup() {
9
+ const cwd = process.cwd();
10
+ const hooksDir = path.join(cwd, '.githooks');
11
+ const preCommit = path.join(hooksDir, 'pre-commit');
12
+ const binPath = 'npx code-gate hook';
13
+ const script = `#!/usr/bin/env sh\n${binPath}\n`;
14
+ writeFileSafe(preCommit, script);
15
+ fs.chmodSync(preCommit, 0o755);
16
+ spawnSync('git', ['config', 'core.hooksPath', '.githooks'], { stdio: 'inherit' });
17
+ process.stdout.write('code-gate installed: .githooks/pre-commit\n');
18
+ }
19
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAE9C,SAAS,aAAa,CAAC,CAAS,EAAE,OAAe;IAC/C,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAClD,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;AACtC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ;IAC5B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAA;IACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAA;IAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAA;IACnD,MAAM,OAAO,GAAG,oBAAoB,CAAA;IACpC,MAAM,MAAM,GAAG,sBAAsB,OAAO,IAAI,CAAA;IAChD,aAAa,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;IAChC,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;IAC9B,SAAS,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,gBAAgB,EAAE,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAA;IACjF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAA;AACrE,CAAC"}
@@ -0,0 +1,40 @@
1
+ export type Provider = 'ollama' | 'deepseek';
2
+ export interface Config {
3
+ provider: Provider;
4
+ review?: {
5
+ provider?: Provider;
6
+ enabled?: boolean;
7
+ mode?: 'aggregate' | 'per_file';
8
+ };
9
+ ollama?: {
10
+ baseURL?: string;
11
+ model?: string;
12
+ };
13
+ deepseek?: {
14
+ baseURL?: string;
15
+ apiKeyEnv?: string;
16
+ model?: string;
17
+ };
18
+ fileTypes?: string[];
19
+ scope?: 'staged' | 'allChanged' | {
20
+ include?: string[];
21
+ exclude?: string[];
22
+ };
23
+ ui?: {
24
+ openBrowser?: boolean;
25
+ theme?: 'github';
26
+ port: number;
27
+ };
28
+ limits?: {
29
+ maxDiffLines?: number;
30
+ maxFiles?: number;
31
+ };
32
+ rules?: {
33
+ focus?: Array<'security' | 'performance' | 'style' | 'tests'>;
34
+ };
35
+ prompt?: string;
36
+ output?: {
37
+ dir?: string;
38
+ };
39
+ }
40
+ export declare function loadConfig(cwd?: string): Promise<Config>;
package/dist/config.js ADDED
@@ -0,0 +1,86 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import yaml from 'yaml';
4
+ const defaultConfig = {
5
+ provider: 'deepseek',
6
+ review: {
7
+ enabled: true,
8
+ mode: 'per_file'
9
+ },
10
+ deepseek: {
11
+ baseURL: 'https://api.deepseek.com',
12
+ apiKeyEnv: 'DEEPSEEK_API_KEY',
13
+ model: 'deepseek-chat'
14
+ },
15
+ ollama: {
16
+ baseURL: 'http://localhost:11434',
17
+ model: 'qwen2.5-coder'
18
+ },
19
+ fileTypes: ['ts', 'tsx', 'js', 'jsx', 'json', 'md', 'py', 'go', 'rs'],
20
+ scope: 'staged',
21
+ ui: {
22
+ openBrowser: true,
23
+ theme: 'github',
24
+ port: 5175
25
+ },
26
+ limits: {
27
+ maxDiffLines: 10000,
28
+ maxFiles: 100
29
+ },
30
+ rules: {
31
+ focus: ['security', 'performance', 'style', 'tests']
32
+ },
33
+ prompt: '作为资深代码审查工程师,从安全、性能、代码风格与测试覆盖角度审查本次变更,指出问题与改进建议,并给出必要的示例补丁。',
34
+ output: {
35
+ dir: '.code-gate'
36
+ }
37
+ };
38
+ function findConfigFile(cwd) {
39
+ const candidates = [
40
+ path.join(cwd, '.codegate.js'),
41
+ path.join(cwd, '.codegate.cjs'),
42
+ path.join(cwd, 'code-gate.config.json'),
43
+ path.join(cwd, 'code-gate.config.yaml'),
44
+ path.join(cwd, 'code-gate.config.yml'),
45
+ path.join(cwd, '.code-gaterc'),
46
+ path.join(cwd, '.code-gaterc.json'),
47
+ path.join(cwd, '.code-gaterc.yaml'),
48
+ path.join(cwd, '.code-gaterc.yml')
49
+ ];
50
+ for (const p of candidates) {
51
+ if (fs.existsSync(p))
52
+ return p;
53
+ }
54
+ return undefined;
55
+ }
56
+ async function readFile(p) {
57
+ const ext = path.extname(p).toLowerCase();
58
+ if (ext === '.js' || ext === '.cjs') {
59
+ const { pathToFileURL } = await import('node:url');
60
+ const mod = await import(pathToFileURL(p).href);
61
+ return mod?.default ?? mod;
62
+ }
63
+ const raw = fs.readFileSync(p, 'utf8');
64
+ if (ext === '.yaml' || ext === '.yml')
65
+ return yaml.parse(raw);
66
+ return JSON.parse(raw);
67
+ }
68
+ export async function loadConfig(cwd = process.cwd()) {
69
+ const p = findConfigFile(cwd);
70
+ if (!p)
71
+ return { ...defaultConfig };
72
+ const user = (await readFile(p)) || {};
73
+ const merged = {
74
+ ...defaultConfig,
75
+ ...user,
76
+ review: { ...defaultConfig.review, ...(user.review || {}) },
77
+ deepseek: { ...defaultConfig.deepseek, ...(user.deepseek || {}) },
78
+ ollama: { ...defaultConfig.ollama, ...(user.ollama || {}) },
79
+ ui: { ...defaultConfig.ui, ...(user.ui || {}) },
80
+ limits: { ...defaultConfig.limits, ...(user.limits || {}) },
81
+ rules: { ...defaultConfig.rules, ...(user.rules || {}) },
82
+ output: { ...defaultConfig.output, ...(user.output || {}) }
83
+ };
84
+ return merged;
85
+ }
86
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function runReviewFlow(): Promise<boolean>;