smart-review 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Smart Review
2
2
 
3
+ > 语言 / Language: [中文](README.md) | [English](README.en-US.md)
4
+
3
5
  🚀 **AI智能代码审查工具** - 结合静态规则检测和AI智能分析,为你的代码提供全方位的安全和质量审查。
4
6
 
5
7
  ## ✨ 特性
@@ -441,36 +443,45 @@ Smart Reviewer 采用智能分批处理技术,能够高效处理超大文件
441
443
  ```json
442
444
  {
443
445
  "ai": {
444
- "maxRequestTokens": 8000, // 单次请求最大token数
445
- "maxChunkSizeKB": 400, // 单个分段最大大小
446
- "chunkOverlapLines": 20 // 分段重叠行数
446
+ "maxRequestTokens": 8000, // 单次请求最大 token
447
+ "chunkOverlapLines": 5, // 分段重叠行数
448
+ "minFilesPerBatch": 1, // 每批次最少文件数
449
+ "maxFilesPerBatch": 10 // 每批次最多文件数
447
450
  }
448
451
  }
449
452
  ```
450
453
 
451
454
  #### 性能优化建议
452
455
 
453
- 1. **合理设置分段大小**
456
+ 1. **合理设置批处理参数**
454
457
  ```json
455
458
  {
456
- "maxChunkSizeKB": 300, // 较小的分段,更快的响应
457
- "chunkOverlapLines": 15 // 适中的重叠,平衡上下文和性能
459
+ "ai": {
460
+ "maxRequestTokens": 6000,
461
+ "chunkOverlapLines": 10,
462
+ "minFilesPerBatch": 1,
463
+ "maxFilesPerBatch": 5
464
+ }
458
465
  }
459
466
  ```
460
467
 
461
468
  2. **启用并发处理**
462
469
  ```json
463
470
  {
464
- "concurrency": 3, // 并发AI请求数量,提升处理速度
465
- "maxFilesPerBatch": 5 // 控制批次文件数
471
+ "ai": {
472
+ "concurrency": 3, // 并发 AI 请求数量
473
+ "maxFilesPerBatch": 5 // 控制批次文件数
474
+ }
466
475
  }
467
476
  ```
468
477
 
469
- 3. **启用智能分批**
478
+ 3. **调整智能分批参数**
470
479
  ```json
471
480
  {
472
- "enableSmartBatching": true,
473
- "maxFilesPerBatch": 5 // 控制并发文件数
481
+ "ai": {
482
+ "minFilesPerBatch": 1,
483
+ "maxFilesPerBatch": 5
484
+ }
474
485
  }
475
486
  ```
476
487
 
@@ -551,45 +562,71 @@ const reviewer = new CodeReviewer(customConfig, defaultRules);
551
562
  ```bash
552
563
  # OpenAI API配置
553
564
  export OPENAI_API_KEY="your-api-key"
554
- export OPENAI_BASE_URL="https://api.openai.com/v1"
555
565
 
556
566
  # 调试模式
557
567
  export DEBUG_SMART_REVIEW=true
568
+
569
+ # 国际化(i18n)
570
+ # Windows PowerShell(当前会话)
571
+ $env:SMART_REVIEW_LOCALE='zh-CN' # 或 'en-US'
572
+
573
+ # macOS/Linux bash
574
+ export SMART_REVIEW_LOCALE=zh-CN # 或 en-US
575
+ ```
576
+
577
+ 如果需要使用自定义的 OpenAI 兼容服务,请在项目的配置文件中设置 `ai.baseURL`:
578
+
579
+ ```json
580
+ {
581
+ "ai": { "baseURL": "https://api.openai.com/v1" }
582
+ }
558
583
  ```
559
584
 
585
+ ## 🌍 国际化 (i18n)
586
+
587
+ - 配置项 `locale`:在 `.smart-review/smart-review.json` 顶层设置输出与模板语言,支持 `zh-CN`、`en-US`。示例:`{"locale": "en-US"}`。
588
+ - 环境变量 `SMART_REVIEW_LOCALE`:优先级最高,安装和复制模板时将按该值选择语言目录。
589
+ - 选择优先级:环境变量 > 项目配置 `.smart-review/smart-review.json` > 模板默认配置 `templates/smart-review.json` > `zh-CN`。
590
+ - 模板目录结构:`templates/rules/<locale>/security.js|performance.js|best-practices.js`;当指定语言缺失某文件时自动回退到 `zh-CN`。
591
+ - 控制台与 Git 钩子提示会随 `locale` 切换语言,无需额外配置。
592
+ - 切换示例:
593
+ - PowerShell:`$env:SMART_REVIEW_LOCALE='en-US'; node bin/install.js`
594
+ - Bash:`export SMART_REVIEW_LOCALE=en-US && node bin/install.js`
595
+
596
+ 如需新增语言(例如 `ja-JP`),在 `templates/rules/ja-JP/` 下添加三类规则模板文件,并在配置中设置 `"locale": "ja-JP"` 或通过环境变量切换即可。
597
+
560
598
  ## 🔧 命令行参数
561
599
 
562
600
  ```bash
563
601
  smart-review [options]
564
602
 
565
603
  选项:
566
- --staged 审查Git暂存区文件
567
- --files <files> 审查指定文件 (逗号分隔)
568
- --ai 强制启用AI分析
569
- --no-ai 禁用AI分析
570
- --diff-only 强制启用Git Diff增量审查模式
571
- --full-file 禁用增量审查,审查完整文件内容
572
- --config <path> 指定配置文件路径
573
- --rules <path> 指定规则目录路径
574
- --output <format> 输出格式 (text|json)
575
- --verbose 详细输出
576
- --help 显示帮助信息
577
- ```
578
-
579
- **增量审查相关参数说明:**
580
- - `--diff-only`: 强制启用Git Diff增量审查模式,覆盖配置文件中的`reviewOnlyChanges`设置
581
- - `--full-file`: 禁用增量审查,审查完整文件内容,适用于需要全面审查的场景
604
+ --staged 审查 Git 暂存区文件
605
+ --files <files> 审查指定文件(逗号分隔)
606
+ --ai 强制启用 AI 分析
607
+ --no-ai 禁用 AI 分析
608
+ --diff-only 仅审查变更行(Git Diff 模式)
609
+ --debug 输出调试日志
610
+ ```
611
+
612
+ **增量审查相关说明:**
613
+ - `--diff-only`:仅审查变更行(Git Diff 模式),覆盖配置项 `ai.reviewOnlyChanges`
614
+ - 禁用增量审查:在 `.smart-review/smart-review.json` 将 `ai.reviewOnlyChanges` 设为 `false`,适用于需要全面审查的场景
582
615
 
583
616
  **使用示例:**
584
617
  ```bash
585
618
  # 强制使用增量审查模式
586
619
  smart-review --staged --diff-only
587
620
 
588
- # 强制审查完整文件内容
589
- smart-review --files src/important.js --full-file
621
+ # 审查完整文件内容(在配置中关闭增量)
622
+ # .smart-review/smart-review.json
623
+ {
624
+ "ai": { "reviewOnlyChanges": false }
625
+ }
626
+ smart-review --files src/important.js
590
627
 
591
628
  # 结合其他参数使用
592
- smart-review --staged --diff-only --verbose
629
+ smart-review --staged --diff-only --debug
593
630
  ```
594
631
 
595
632
  ## 🚀 CI/CD 集成
@@ -649,13 +686,13 @@ code_review:
649
686
  **解决方案**: 检查 `OPENAI_API_KEY` 环境变量或配置文件中的密钥
650
687
 
651
688
  4. **大文件处理超时**
652
- ```
653
- ❌ 文件分析超时
654
- ```
655
- **解决方案**:
656
- - 减小 `maxChunkSizeKB` 配置值
657
- - 增加 `chunkOverlapLines` 以减少分段数量
658
- - 检查网络连接稳定性
689
+ ```
690
+ ❌ 文件分析超时
691
+ ```
692
+ **解决方案**:
693
+ - 降低 `ai.maxRequestTokens` 或减少批次文件数(`maxFilesPerBatch`),并适当降低 `chunkOverlapLines`
694
+ - 增加 `chunkOverlapLines` 以减少分段数量
695
+ - 检查网络连接稳定性
659
696
 
660
697
  5. **分段分析结果不完整**
661
698
  ```
@@ -667,28 +704,30 @@ code_review:
667
704
  - 调整 `maxRequestTokens` 配置
668
705
 
669
706
  6. **内存占用过高**
670
- ```
671
- ❌ 内存不足错误
672
- ```
673
- **解决方案**:
674
- - 减少 `maxFilesPerBatch` 配置值
675
- - 启用 `enableSmartBatching` 智能分批
676
- - 添加更多文件到 `ignoreFiles` 列表
707
+ ```
708
+ ❌ 内存不足错误
709
+ ```
710
+ **解决方案**:
711
+ - 减少 `maxFilesPerBatch` 配置值
712
+ - 调整 `minFilesPerBatch`/`maxFilesPerBatch` 控制每批文件数量
713
+ - 添加更多文件到 `ignoreFiles` 列表
677
714
 
678
715
  7. **Token 限制错误**
679
- ```
680
- ❌ Request too large
681
- ```
682
- **解决方案**:
683
- - 降低 `maxRequestTokens` 配置值
684
- - 减小 `maxChunkSizeKB` 分段大小
685
- - 检查是否有超大的单行代码
716
+ ```
717
+ ❌ Request too large
718
+ ```
719
+ **解决方案**:
720
+ - 降低 `maxRequestTokens` 配置值
721
+ - 减少每批文件数量或启用增量审查 `--diff-only`
722
+ - 检查是否有超大的单行代码
686
723
 
687
724
  ### 调试模式
688
725
 
689
- 启用详细日志:
726
+ 启用调试日志:
690
727
  ```bash
691
- DEBUG_SMART_REVIEW=true smart-review --staged --verbose
728
+ DEBUG_SMART_REVIEW=true smart-review --staged
729
+ # 或
730
+ smart-review --staged --debug
692
731
  ```
693
732
 
694
733
  ## 🤝 贡献
package/bin/install.js CHANGED
@@ -6,6 +6,7 @@ import { fileURLToPath } from 'url';
6
6
  import { execSync } from 'child_process';
7
7
  import { logger } from '../lib/utils/logger.js';
8
8
  import { FILE_PERMISSIONS, BATCH_CONSTANTS } from '../lib/utils/constants.js';
9
+ import { t } from '../lib/utils/i18n.js';
9
10
 
10
11
  const __filename = fileURLToPath(import.meta.url);
11
12
  const __dirname = path.dirname(__filename);
@@ -19,12 +20,12 @@ class Installer {
19
20
 
20
21
  findGitRoot() {
21
22
  let currentDir = process.cwd();
22
- logger.debug(`查找Git根目录,从 ${currentDir} 开始...`);
23
+ logger.debug(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_search_git_root_dbg', { dir: currentDir }));
23
24
 
24
25
  for (let i = 0; i < BATCH_CONSTANTS.MAX_DIRECTORY_SEARCH_DEPTH; i++) {
25
26
  const gitDir = path.join(currentDir, '.git');
26
27
  if (fs.existsSync(gitDir)) {
27
- logger.success(`找到Git根目录: ${currentDir}`);
28
+ logger.success(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_found_git_root_success', { dir: currentDir }));
28
29
  return currentDir;
29
30
  }
30
31
 
@@ -35,25 +36,25 @@ class Installer {
35
36
  currentDir = parentDir;
36
37
  }
37
38
 
38
- logger.info('ℹ️ 未找到.git目录,使用当前目录作为项目根目录');
39
+ logger.info(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_no_git_use_current'));
39
40
  return process.cwd();
40
41
  }
41
42
 
42
- install() {
43
- logger.info('🚀 开始安装智能代码审查系统...');
43
+ async install() {
44
+ logger.info(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_start'));
44
45
 
45
46
  try {
46
47
  this.createReviewDirectory();
47
- this.copyTemplateFiles();
48
+ await this.copyTemplateFiles();
48
49
  this.installGitHooks();
49
50
  this.showNextSteps();
50
51
 
51
- logger.success('\n🎉 智能代码审查系统安装完成!');
52
- logger.info('💡 系统已内置默认配置和规则,无需额外配置即可使用');
53
- logger.info('📝 如需自定义,请编辑 .smart-review/ 目录下的配置文件');
52
+ logger.success('\n' + t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_done_success'));
53
+ logger.info(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_bundled_info'));
54
+ logger.info(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_customize_tip'));
54
55
 
55
56
  } catch (error) {
56
- logger.error('安装失败:', error);
57
+ logger.error(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_failed', { error: error.message }));
57
58
  process.exit(1);
58
59
  }
59
60
  }
@@ -61,25 +62,25 @@ class Installer {
61
62
  createReviewDirectory() {
62
63
  if (!fs.existsSync(this.reviewDir)) {
63
64
  fs.mkdirSync(this.reviewDir, { recursive: true });
64
- logger.success('创建 .smart-review 目录');
65
+ logger.success(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_create_review_dir'));
65
66
  }
66
67
 
67
68
  // 创建AI提示词子目录(用于AI自定义提示)
68
69
  const aiPromptsDir = path.join(this.reviewDir, 'ai-rules');
69
70
  if (!fs.existsSync(aiPromptsDir)) {
70
71
  fs.mkdirSync(aiPromptsDir, { recursive: true });
71
- logger.success('创建 ai-rules 子目录(AI提示词)');
72
+ logger.success(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_create_ai_rules_dir'));
72
73
  }
73
74
 
74
75
  // 创建本地静态规则目录
75
76
  const localRulesDir = path.join(this.reviewDir, 'local-rules');
76
77
  if (!fs.existsSync(localRulesDir)) {
77
78
  fs.mkdirSync(localRulesDir, { recursive: true });
78
- logger.success('创建 local-rules 子目录(静态规则)');
79
+ logger.success(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_create_local_rules_dir'));
79
80
  }
80
81
  }
81
82
 
82
- copyTemplateFiles() {
83
+ async copyTemplateFiles() {
83
84
  // 将模板源路径(templates 下)映射到目标路径(.smart-review 下)
84
85
  const templatesMap = [
85
86
  { src: 'smart-review.json', dest: 'smart-review.json', description: '主配置文件' },
@@ -87,9 +88,25 @@ class Installer {
87
88
  { src: 'rules/performance.js', dest: 'local-rules/performance.js', description: '性能规则' },
88
89
  { src: 'rules/best-practices.js', dest: 'local-rules/best-practices.js', description: '最佳实践规则' }
89
90
  ];
91
+
92
+ // 根据 locale 选择模板目录(优先 rules/<locale>/,否则回退到 rules/zh-CN/)
93
+ const loc = await this.resolveLocale();
90
94
 
91
95
  for (const { src, dest, description } of templatesMap) {
92
- const templatePath = path.join(this.templatesDir, src);
96
+ let effectiveSrc = src;
97
+ if (src.startsWith('rules/')) {
98
+ const fileName = path.basename(src);
99
+ const candidateRel = path.join('rules', loc, fileName);
100
+ const candidateAbs = path.join(this.templatesDir, candidateRel);
101
+ const fallbackRel = path.join('rules', 'zh-CN', fileName);
102
+ const fallbackAbs = path.join(this.templatesDir, fallbackRel);
103
+ if (fs.existsSync(candidateAbs)) {
104
+ effectiveSrc = candidateRel;
105
+ } else if (fs.existsSync(fallbackAbs)) {
106
+ effectiveSrc = fallbackRel;
107
+ }
108
+ }
109
+ const templatePath = path.join(this.templatesDir, effectiveSrc);
93
110
  const targetPath = path.join(this.reviewDir, dest);
94
111
 
95
112
  if (fs.existsSync(templatePath) && !fs.existsSync(targetPath)) {
@@ -98,11 +115,108 @@ class Installer {
98
115
  if (!fs.existsSync(targetDir)) {
99
116
  fs.mkdirSync(targetDir, { recursive: true });
100
117
  }
118
+ // smart-review.json 原样复制;规则文件按 locale 生成本地化版本
119
+ if (src === 'smart-review.json') {
120
+ fs.copyFileSync(templatePath, targetPath);
121
+ } else {
122
+ try {
123
+ const content = await this.buildLocalizedRuleModule(templatePath);
124
+ fs.writeFileSync(targetPath, content, 'utf8');
125
+ } catch (e) {
126
+ // 失败时退回直接复制原模板
127
+ fs.copyFileSync(templatePath, targetPath);
128
+ }
129
+ }
130
+ logger.success(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_create_template_success', { desc: description }));
131
+ }
132
+ }
133
+ }
134
+
135
+ async resolveLocale() {
136
+ // 解析语言优先级:环境变量 > 已复制的项目配置 > 模板默认配置 > zh-CN
137
+ let loc = process.env.SMART_REVIEW_LOCALE || '';
138
+ if (!loc) {
139
+ try {
140
+ const projectCfg = path.join(this.reviewDir, 'smart-review.json');
141
+ if (fs.existsSync(projectCfg)) {
142
+ const cfg = JSON.parse(fs.readFileSync(projectCfg, 'utf8'));
143
+ if (cfg && typeof cfg.locale === 'string' && cfg.locale.trim()) {
144
+ loc = cfg.locale.trim();
145
+ }
146
+ }
147
+ } catch (e) {
148
+ // 忽略读取失败,继续尝试模板配置
149
+ }
150
+ }
151
+ if (!loc) {
152
+ try {
153
+ const templateCfg = path.join(this.templatesDir, 'smart-review.json');
154
+ if (fs.existsSync(templateCfg)) {
155
+ const cfg = JSON.parse(fs.readFileSync(templateCfg, 'utf8'));
156
+ if (cfg && typeof cfg.locale === 'string' && cfg.locale.trim()) {
157
+ loc = cfg.locale.trim();
158
+ }
159
+ }
160
+ } catch (e) {
161
+ // 忽略读取失败
162
+ }
163
+ }
164
+ if (!loc) loc = 'zh-CN';
165
+ return loc;
166
+ }
101
167
 
102
- fs.copyFileSync(templatePath, targetPath);
103
- logger.success(`创建 ${description}`);
168
+ async buildLocalizedRuleModule(templatePath) {
169
+ // 解析语言优先级:环境变量 > 已复制的项目配置 > 模板默认配置 > zh-CN
170
+ let loc = process.env.SMART_REVIEW_LOCALE || '';
171
+ if (!loc) {
172
+ try {
173
+ const projectCfg = path.join(this.reviewDir, 'smart-review.json');
174
+ if (fs.existsSync(projectCfg)) {
175
+ const cfg = JSON.parse(fs.readFileSync(projectCfg, 'utf8'));
176
+ if (cfg && typeof cfg.locale === 'string' && cfg.locale.trim()) {
177
+ loc = cfg.locale.trim();
178
+ }
179
+ }
180
+ } catch (e) {
181
+ // 忽略读取失败,继续尝试模板配置
104
182
  }
105
183
  }
184
+ if (!loc) {
185
+ try {
186
+ const templateCfg = path.join(this.templatesDir, 'smart-review.json');
187
+ if (fs.existsSync(templateCfg)) {
188
+ const cfg = JSON.parse(fs.readFileSync(templateCfg, 'utf8'));
189
+ if (cfg && typeof cfg.locale === 'string' && cfg.locale.trim()) {
190
+ loc = cfg.locale.trim();
191
+ }
192
+ }
193
+ } catch (e) {
194
+ // 忽略读取失败
195
+ }
196
+ }
197
+ if (!loc) loc = 'zh-CN';
198
+ const fileUrl = `file://${templatePath.replace(/\\/g, '/')}`;
199
+ const mod = await import(fileUrl);
200
+ const rules = Array.isArray(mod?.default) ? mod.default : (Array.isArray(mod?.rules) ? mod.rules : []);
201
+ const localized = rules.map((r) => {
202
+ const id = r?.id;
203
+ if (!id) return r;
204
+ const nameKey = `rule_${id}_name`;
205
+ const msgKey = `rule_${id}_message`;
206
+ const sugKey = `rule_${id}_suggestion`;
207
+ const name = t(loc, nameKey);
208
+ const message = t(loc, msgKey);
209
+ const suggestion = t(loc, sugKey);
210
+ return {
211
+ ...r,
212
+ name: (typeof name === 'string' && name !== nameKey) ? name : r.name,
213
+ message: (typeof message === 'string' && message !== msgKey) ? message : r.message,
214
+ suggestion: (typeof suggestion === 'string' && suggestion !== sugKey) ? suggestion : r.suggestion,
215
+ };
216
+ });
217
+ // 生成ESM模块内容
218
+ const json = JSON.stringify(localized, null, 2);
219
+ return `// Generated by smart-review install (locale: ${loc})\nexport default ${json};\n`;
106
220
  }
107
221
 
108
222
  installGitHooks() {
@@ -116,7 +230,7 @@ class Installer {
116
230
  }
117
231
 
118
232
  if (!gitAvailable) {
119
- logger.error('未检测到 Git,请先安装后重试: https://git-scm.com/downloads');
233
+ logger.error(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_git_missing', { url: 'https://git-scm.com/downloads' }));
120
234
  process.exit(1);
121
235
  }
122
236
 
@@ -124,11 +238,11 @@ class Installer {
124
238
  const gitDir = path.join(this.projectRoot, '.git');
125
239
  // review-disable-start
126
240
  if (!fs.existsSync(gitDir)) {
127
- logger.warn('未检测到 .git 目录,正在初始化 Git 仓库...');
241
+ logger.warn(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_init_git_warn'));
128
242
  try {
129
243
  execSync('git init', { cwd: this.projectRoot, stdio: 'ignore' });
130
244
  } catch (e) {
131
- logger.error('Git 仓库初始化失败,请手动执行 `git init` 后重试');
245
+ logger.error(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_init_git_failed'));
132
246
  process.exit(1);
133
247
  }
134
248
  }
@@ -144,27 +258,27 @@ class Installer {
144
258
 
145
259
  const preCommitHook = path.join(gitHooksDir, 'pre-commit');
146
260
 
147
- const hookContent = `#!/bin/bash
148
- # 智能代码审查 - pre-commit钩子(子项目兼容,基于暂存文件逐层定位)
261
+ const loc = process.env.SMART_REVIEW_LOCALE || 'zh-CN';
262
+ const hookContent = `#!/usr/bin/env bash
263
+ # ${t(loc, 'hook_header_comment')}
149
264
 
150
- echo "🔍 启动代码审查..."
265
+ echo "${t(loc, 'hook_start_review')}"
151
266
 
152
267
  # 获取暂存区文件
153
268
  STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
154
269
 
155
270
  if [ -z "$STAGED_FILES" ]; then
156
- echo "📭 没有暂存的文件需要审查"
271
+ echo "${t(loc, 'hook_no_staged')}"
157
272
  exit 0
158
273
  fi
159
274
 
160
- echo "📁 发现暂存文件:"
275
+ echo "${t(loc, 'hook_found_staged_header')}"
161
276
  echo "$STAGED_FILES"
162
277
 
163
278
  # 运行代码审查(定位到仓库根目录)
164
279
  REPO_ROOT=$(git rev-parse --show-toplevel)
165
- cd "$REPO_ROOT" || { echo " 无法进入仓库根目录"; exit 1; }
280
+ cd "$REPO_ROOT" || { echo "${t(loc, 'hook_cd_repo_fail')}"; exit 1; }
166
281
 
167
- # 优先使用仓库根目录安装的 CLI
168
282
  ROOT_BIN="$REPO_ROOT/node_modules/.bin/smart-review"
169
283
 
170
284
  FOUND_CMD=""
@@ -173,8 +287,6 @@ FOUND_IS_ENTRY=0
173
287
  if [ -f "$ROOT_BIN" ]; then
174
288
  FOUND_CMD="$ROOT_BIN"
175
289
  else
176
- # 基于暂存文件的路径,逐层向上查找其子项目的 node_modules
177
- # 限制最大向上层级,避免卡住
178
290
  MAX_ASCEND=6
179
291
  while IFS= read -r file; do
180
292
  [ -z "$file" ] && continue
@@ -194,18 +306,17 @@ else
194
306
  done <<< "$STAGED_FILES"
195
307
  fi
196
308
 
197
- # 额外兜底:PATH 中的全局 smart-review
198
309
  if [ -z "$FOUND_CMD" ] && command -v smart-review >/dev/null 2>&1; then
199
310
  FOUND_CMD="smart-review"; FOUND_IS_ENTRY=0
200
311
  fi
201
312
 
202
313
  if [ -z "$FOUND_CMD" ]; then
203
- echo " 未找到 smart-review。请在对应子项目安装:npm i -D smart-review"
204
- echo " 或在仓库根安装供统一使用:npm i -D smart-review"
314
+ echo "${t(loc, 'hook_cmd_not_found1')}"
315
+ echo "${t(loc, 'hook_cmd_not_found2')}"
205
316
  exit 1
206
317
  fi
207
318
 
208
- echo "⚙️ 使用命令: $FOUND_CMD --staged"
319
+ echo "${t(loc, 'hook_use_command_prefix')} $FOUND_CMD --staged"
209
320
  if [ $FOUND_IS_ENTRY -eq 1 ]; then
210
321
  node "$FOUND_CMD" --staged
211
322
  else
@@ -214,21 +325,40 @@ fi
214
325
 
215
326
  EXIT_CODE=$?
216
327
  if [ $EXIT_CODE -ne 0 ]; then
217
- echo " 代码审查未通过,请修复问题后重新提交"
328
+ echo "${t(loc, 'hook_review_fail')}"
218
329
  exit 1
219
330
  else
220
- echo " 代码审查通过,继续提交"
331
+ echo "${t(loc, 'hook_review_pass')}"
221
332
  exit 0
222
333
  fi
223
334
  `;
335
+
224
336
  fs.writeFileSync(preCommitHook, hookContent);
337
+ // Windows 兼容:提供 CMD 包装器,调用 bash 执行同名脚本
338
+ try {
339
+ const preCommitCmd = path.join(gitHooksDir, 'pre-commit.cmd');
340
+ const cmdContent = [
341
+ '@echo off',
342
+ 'SETLOCAL',
343
+ 'set HOOK=%~dp0pre-commit',
344
+ 'if not exist "%HOOK%" (',
345
+ ' echo [smart-review] pre-commit hook missing.',
346
+ ' exit /b 1',
347
+ ')',
348
+ 'bash "%HOOK%"',
349
+ 'exit /b %ERRORLEVEL%\r\n'
350
+ ].join('\r\n');
351
+ fs.writeFileSync(preCommitCmd, cmdContent);
352
+ } catch (e) {
353
+ // 忽略 CMD 包装器写入失败
354
+ }
225
355
 
226
356
  // 设置执行权限
227
357
  try {
228
358
  fs.chmodSync(preCommitHook, FILE_PERMISSIONS.EXECUTABLE);
229
- logger.success('安装 pre-commit Git钩子');
359
+ logger.success(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_precommit_installed_success'));
230
360
  } catch (error) {
231
- logger.warn('无法设置执行权限,但钩子文件已创建');
361
+ logger.warn(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_precommit_perm_warn'));
232
362
  }
233
363
 
234
364
  // 测试钩子是否能正常执行
@@ -236,45 +366,52 @@ fi
236
366
  }
237
367
 
238
368
  testHook(hookPath) {
239
- logger.info('🧪 测试Git钩子...');
369
+ logger.info(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_test_hook'));
240
370
 
241
371
  // 检查文件是否存在且可执行
242
372
  if (!fs.existsSync(hookPath)) {
243
- logger.error('钩子文件不存在');
373
+ logger.error(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_hook_missing'));
244
374
  return;
245
375
  }
246
376
 
247
377
  try {
378
+ // 在 Windows 上无需检查可执行位,直接提示成功
379
+ if (process.platform === 'win32') {
380
+ logger.success(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_test_hook_success'));
381
+ return;
382
+ }
248
383
  const stats = fs.statSync(hookPath);
249
384
  const isExecutable = !!(stats.mode & 0o111);
250
- logger.debug(`钩子文件权限: ${stats.mode.toString(8)}, 可执行: ${isExecutable}`);
251
-
385
+ logger.debug(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_hook_perm_dbg', { mode: stats.mode.toString(8), exec: isExecutable }));
252
386
  if (!isExecutable) {
253
- logger.warn('钩子文件不可执行,尝试重新设置权限...');
387
+ logger.warn(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_hook_perm_fix_warn'));
254
388
  fs.chmodSync(hookPath, FILE_PERMISSIONS.EXECUTABLE);
255
389
  }
390
+ // POSIX 环境下权限检查完成,提示成功
391
+ logger.success(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_test_hook_success'));
256
392
  } catch (error) {
257
- logger.warn('无法检查钩子文件权限:', error.message);
393
+ logger.warn(t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_hook_perm_check_failed', { error: error.message }));
258
394
  }
259
395
  }
260
396
 
261
397
  showNextSteps() {
262
- logger.info('\n📝 可选配置:');
263
- logger.info(' 1. 编辑 .smart-review/smart-review.json 配置AI参数和风险等级');
264
- logger.info(' 2. .smart-review/local-rules/ 目录添加静态规则文件');
265
- logger.info(' 3. .smart-review/ai-rules/ 目录添加AI提示词文件');
266
- logger.info(' 4. 设置 OPENAI_API_KEY 环境变量启用AI审查');
267
- logger.info('\n⚙️ 配置文件位置:');
398
+ logger.info('\n' + t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_optional_header'));
399
+ logger.info(' ' + t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_optional_item1'));
400
+ logger.info(' ' + t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_optional_item2'));
401
+ logger.info(' ' + t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_optional_item3'));
402
+ logger.info(' ' + t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_optional_item4'));
403
+
404
+ logger.info('\n' + t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_paths_header'));
268
405
  logger.info(` ${path.join(this.reviewDir, 'smart-review.json')}`);
269
- logger.info(` 静态规则: ${path.join(this.reviewDir, 'local-rules/')}`);
270
- logger.info(` AI提示词: ${path.join(this.reviewDir, 'ai-rules/')}`);
406
+ logger.info(' ' + t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_local_rules_path', { path: path.join(this.reviewDir, 'local-rules/') }));
407
+ logger.info(' ' + t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_ai_rules_path', { path: path.join(this.reviewDir, 'ai-rules/') }));
271
408
 
272
- logger.info('\n🔧 测试命令:');
273
- logger.info(' git add . && git commit -m "test" # 测试提交触发审查');
274
- logger.info(' npx smart-review --files test/src/test-file.js # 手动测试审查(使用项目内CLI)');
409
+ logger.info('\n' + t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_test_header'));
410
+ logger.info(' ' + t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_test_git_commit'));
411
+ logger.info(' ' + t(process.env.SMART_REVIEW_LOCALE || 'zh-CN', 'install_test_cli'));
275
412
  }
276
413
  }
277
414
 
278
415
  // 运行安装
279
416
  const installer = new Installer();
280
- installer.install();
417
+ (async () => { await installer.install(); })();