hong-review-cli 1.0.7 → 1.0.8

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.
@@ -136,8 +136,15 @@ hong-review setup-openclaw
136
136
  4. **查看待办池**
137
137
  - 触发话术样例:“目前积压了什么 MR ?”或“看下待审查代码”
138
138
  - 执行底包名:`hong-review ls -y`
139
+
140
+ 5. **查询执行日志 (排错/打点记录)**
141
+ - 触发话术样例:“查一下 CLI 的最新日志,刚才 webhook 没发出来”
142
+ - 工具行为准则:直接读取本地日志文件。日志文件夹路径获取方式:先执行 `node -e "console.log(require('/path/to/cli/src/utils/storage.js').get('logDir'))"` 获取配置目录(若为空则默认是在 `~/.hong-review-logs/`)。找到最新的 `hong-review-YYYY-MM-DD.log`,并 `tail -n 100` 或者 `cat` 读取并提炼关键错误信息回复给用户。
139
143
  ```
140
144
 
145
+ > **进阶设置:修改默认日志输出目录**
146
+ > 默认情况下,每日轮转的追踪日志(包含 Webhook 与生命周期全过程)存储在 `~/.hong-review-logs` 文件夹下。您可以在机器上使用 `hong-review config set logDir "/您的/自定义/绝对路径"` 来集中管理追踪内容。
147
+
141
148
  ---
142
149
 
143
150
  ## 验证与测试联调
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hong-review-cli",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "main": "index.js",
5
5
  "bin": {
6
6
  "hong-review": "bin/hong-review.js"
@@ -25,6 +25,8 @@
25
25
  "commander": "^14.0.3",
26
26
  "figlet": "^1.10.0",
27
27
  "inquirer": "^8.2.7",
28
- "ora": "^5.4.1"
28
+ "ora": "^5.4.1",
29
+ "winston": "^3.19.0",
30
+ "winston-daily-rotate-file": "^5.0.0"
29
31
  }
30
32
  }
@@ -30,7 +30,9 @@ class Hooks {
30
30
  ...payload
31
31
  };
32
32
 
33
- await this.executeWebhook(this.hookConfig, hookPayload, this.hookToken);
33
+ logger.info(`开始推送 Webhook [${eventName}] 到 ${this.hookConfig}`);
34
+ const response = await this.executeWebhook(this.hookConfig, hookPayload, this.hookToken);
35
+ logger.success(`Webhook 推送完成 [${eventName}] HTTP状态: ${response.status}`);
34
36
  } catch (err) {
35
37
  // 如果是用户配置的静态 URL,失败不中断主流程,仅打个 Warn
36
38
  logger.warn(`Webhook 推送失败 [${eventName}]: ${err.message}`);
@@ -1,51 +1,102 @@
1
1
  const chalk = require('chalk');
2
2
  const ora = require('ora');
3
+ const winston = require('winston');
4
+ const DailyRotateFile = require('winston-daily-rotate-file');
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const os = require('os');
8
+ const storage = require('./storage');
3
9
 
4
10
  const spinners = new Map();
5
11
 
6
12
  class Logger {
13
+ constructor() {
14
+ this.initWinston();
15
+ }
16
+
17
+ initWinston() {
18
+ // Find log directory setting or default to ~/.hong-review-logs
19
+ const configLogDir = storage.get('logDir');
20
+ let defaultLogDir = '';
21
+ if (configLogDir) {
22
+ defaultLogDir = configLogDir;
23
+ } else {
24
+ defaultLogDir = path.join(os.homedir(), '.hong-review-logs');
25
+ }
26
+
27
+ if (!fs.existsSync(defaultLogDir)) {
28
+ fs.mkdirSync(defaultLogDir, { recursive: true });
29
+ }
30
+
31
+ this.fileLogger = winston.createLogger({
32
+ level: 'info',
33
+ format: winston.format.combine(
34
+ winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
35
+ winston.format.printf(info => `[${info.timestamp}] ${info.level.toUpperCase()}: ${info.message}`)
36
+ ),
37
+ transports: [
38
+ new DailyRotateFile({
39
+ dirname: defaultLogDir,
40
+ filename: 'hong-review-%DATE%.log',
41
+ datePattern: 'YYYY-MM-DD',
42
+ maxFiles: '14d'
43
+ })
44
+ ]
45
+ });
46
+ }
47
+
7
48
  info(msg) {
8
49
  console.log(chalk.blue('ℹ ' + msg));
50
+ this.fileLogger.info(msg);
9
51
  }
10
-
52
+
11
53
  success(msg) {
12
54
  console.log(chalk.green('✔ ' + msg));
55
+ this.fileLogger.info(msg);
13
56
  }
14
-
57
+
15
58
  warn(msg) {
16
59
  console.log(chalk.yellow('⚠ ' + msg));
60
+ this.fileLogger.warn(msg);
17
61
  }
18
-
62
+
19
63
  error(msg) {
20
64
  console.log(chalk.red('✖ ' + msg));
65
+ this.fileLogger.error(msg);
21
66
  }
22
-
67
+
23
68
  startSpinner(id, text) {
24
69
  if (spinners.has(id)) {
25
70
  spinners.get(id).stop();
26
71
  }
27
72
  const spinner = ora(text).start();
28
73
  spinners.set(id, spinner);
74
+ this.fileLogger.info(`(Started) ${text}`);
29
75
  }
30
-
76
+
31
77
  stopSpinner(id, success = true, text = '') {
32
78
  const spinner = spinners.get(id);
33
79
  if (!spinner) return;
34
-
80
+
81
+ const finalMsg = text || spinner.text;
35
82
  if (success) {
36
- spinner.succeed(text || spinner.text);
83
+ spinner.succeed(finalMsg);
84
+ this.fileLogger.info(`(Completed) ${finalMsg}`);
37
85
  } else {
38
- spinner.fail(text || spinner.text);
86
+ spinner.fail(finalMsg);
87
+ this.fileLogger.error(`(Failed) ${finalMsg}`);
39
88
  }
40
89
  spinners.delete(id);
41
90
  }
42
91
 
43
92
  logRaw(msg) {
44
93
  console.log(msg);
94
+ this.fileLogger.info(msg);
45
95
  }
46
-
96
+
47
97
  divider() {
48
98
  console.log(chalk.gray('-'.repeat(50)));
99
+ this.fileLogger.info('-'.repeat(50));
49
100
  }
50
101
  }
51
102