hong-review-cli 1.0.3 → 1.0.5

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.
@@ -18,26 +18,39 @@ hong-review setup-openclaw
18
18
 
19
19
  在您的 OpenClaw 配置文件 (`openclaw.yaml` 或 [.json](file:///Users/hong/Desktop/cli/package.json)) 中,找到 `hooks` 配置块。你需要新增一条映射规则,让 OpenClaw 知道收到 `code-review` 路径的请求时,发送什么消息给 Telegram。
20
20
 
21
- **示例配置(YAML 风格):**
22
-
23
- ```yaml
24
- hooks:
25
- token: "e4e2136cb731cfb14f1ad190833738c5bdadfbf79cf7c0f5" # 这必须与第一步 CURL 里的 Token 一致
26
- basePath: "/hooks" # 这也对应第一步的路径
27
- mappings:
28
- - id: "hong-review-notification"
29
- match:
30
- path: "code-review" # 匹配 /hooks/code-review
31
- action: "agent" # 以 Agent 代理的口吻发送
32
- agentId: "telegram-bot" # 你的 Telegram Bot 的 Agent 标识
33
- name: "AI Code Reviewer"
34
- channel: "telegram" # 明确发往 Telegram 通道
35
- to: "@your_telegram_username" # 你的真实触达账号
36
- messageTemplate: |
37
- 代码审查完成 🔔
38
- - **MR ID**: {{payload.mrId}}
39
- - **状态**: {{payload.status}}
40
- *(日志及报告已由系统分析进程执行完毕)*
21
+ **针对您当前 JSON 配置的示例配置:**
22
+
23
+ 您可以直接将以下 `hooks` 代码片段复制替换掉您当前配置文件里的 `hooks` 部分。已经为您配置好了正确的 `tech-group` 发送代理目标和 Telegram 群组 ID:
24
+
25
+ ```json
26
+ "hooks": {
27
+ "token": "e4e2136cb731cfb14f1ad190833738c5bdadfbf79cf7c0f5",
28
+ "internal": {
29
+ "enabled": true,
30
+ "entries": {
31
+ "command-logger": {
32
+ "enabled": true
33
+ },
34
+ "session-memory": {
35
+ "enabled": false
36
+ }
37
+ }
38
+ },
39
+ "mappings": [
40
+ {
41
+ "id": "hong-review-notification",
42
+ "match": {
43
+ "path": "code-review"
44
+ },
45
+ "action": "agent",
46
+ "agentId": "tech-group",
47
+ "name": "AI Code Reviewer",
48
+ "channel": "telegram",
49
+ "to": "-5063603196",
50
+ "messageTemplate": "代码审查完成 🔔\n- **MR ID**: {{payload.mrId}}\n- **状态**: {{payload.status}}\n*(日志及报告已由系统分析进程执行完毕)*"
51
+ }
52
+ ]
53
+ }
41
54
  ```
42
55
  *(这段配置使用了 OpenClaw 原生的 [src/gateway/hooks-mapping.ts](file:///tmp/openclaw/src/gateway/hooks-mapping.ts) 解析能力,它允许插值获取 Payload 里的变量并组合成文本推出去)*
43
56
 
@@ -56,16 +69,34 @@ hooks:
56
69
  因为你处于非人机交互环境,你每次调用该程序时,**必须加上 `-y` 或 `--yes` 参数**,这代表启用静默安全模式。
57
70
  程序在执行结束或出错后会自动处理,**你永远不要尝试去读取它的标准输出日志来提取长篇大论的报告**,程序的内建 Hooks 系统会自己将带有排版的最终结果推送到 Telegram,你要做的这只是触发它即可。
58
71
 
59
- **你可以使用它来回复用户的三种核心意图:**
72
+ **你可以使用它来回复用户的各种核心意图(请务必携带对应的命令和必要参数):**
60
73
 
61
- 1. 如果用户问“我的待办列表里有什么 MR?”或“列出待审查代码”:
74
+ 1. 审查和分析具体合并请求:
75
+ 如果用户说“去帮我分析一下 101 这个 MR” 或 “查下 101”
76
+ 请在底层执行命令:`hong-review mr 101 -y`
77
+
78
+ 2. 查看待办列表:
79
+ 如果用户问“我的待办列表里有什么 MR?”或“列出待审查代码”
62
80
  请在底层执行命令:`hong-review ls -y`
63
81
 
64
- 2. 如果用户说“去帮我分析一下 101 这个 MR” 或 “查下 101”:
65
- 请在底层执行命令:`hong-review mr 101 -y`
82
+ 3. 留言与评论:
83
+ 如果用户想给 MR 直接留言,例如“给 101 说这个写的很棒”
84
+ 请在底层执行命令:`hong-review comment 101 "这个写的很棒"`
66
85
 
67
- 3. 如果用户经过阅读报告后说“确认合并 101 吧”:
86
+ 4. 自动代码合并:
87
+ 如果用户在代码分析并确保安全后说“确认合并 101 吧”
68
88
  请在底层执行命令:`hong-review merge 101 -y`
89
+
90
+ 5. 快速查看代码的原始变更 (Diff):
91
+ 如果用户问“我想直接看看 101 改了啥代码”
92
+ 请在底层执行命令:`hong-review view 101`
93
+
94
+ **附录: 额外的底层管理员命令集**
95
+ *(在绝大数情况下不需要触发,仅供全能 AI 掌握系统全貌,切勿对普通用户暴露)*
96
+ - 程序功能登录与初始化配置:`hong-review login` (或 `init`)
97
+ - 查看配置信息:`hong-review config list`
98
+ - 修改配置:`hong-review config set <key> <value>`
99
+ - 路由挂载初始化:`hong-review setup-openclaw`
69
100
  ```
70
101
 
71
102
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hong-review-cli",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "main": "index.js",
5
5
  "bin": {
6
6
  "hong-review": "bin/hong-review.js"
@@ -57,6 +57,7 @@ module.exports = async function(mrId, options) {
57
57
 
58
58
  try {
59
59
  logger.startSpinner('fetching', '正在从 GitLab 拉取合并请求详情与代码变更...');
60
+ await hooks.emit('onReviewProgress', { projectId, mrId: mrIid, status: '拉取合并请求代码中' });
60
61
  const detail = await gitlab.getMergeRequestDetail(projectId, mrIid);
61
62
  const changes = await gitlab.getMergeRequestChanges(projectId, mrIid);
62
63
  logger.stopSpinner('fetching', true, `成功拉取 MR [${detail.title}] 分析所需的 ${changes.length} 个文件变更。`);
@@ -68,6 +69,7 @@ module.exports = async function(mrId, options) {
68
69
  }
69
70
 
70
71
  if (!options.yes) logger.startSpinner('ai-thinking', `🤖 AI (${config.aiModel}) 正在深度分析中,请稍候...`);
72
+ await hooks.emit('onReviewProgress', { projectId, mrId: mrIid, status: 'AI 深度分析代码中' });
71
73
 
72
74
  let reviewResult = null;
73
75
  let round = 1;
@@ -78,6 +80,7 @@ module.exports = async function(mrId, options) {
78
80
  // 开始 Agent 轮询
79
81
  while (true) {
80
82
  if (!options.yes) logger.startSpinner('ai-thinking', `🤖 AI 思考中 (第 ${round} 轮)...`);
83
+ await hooks.emit('onReviewProgress', { projectId, mrId: mrIid, status: `AI 思考中 (第 ${round} 轮)` });
81
84
  const aiResponse = await ai.chatWithJsonMode(AGENT_SYSTEM_PROMPT, conversationHistory);
82
85
  const action = parseAgentResponse(aiResponse);
83
86
 
@@ -88,10 +91,13 @@ module.exports = async function(mrId, options) {
88
91
  conversationHistory.push({ role: 'assistant', content: aiResponse });
89
92
 
90
93
  if (action.action === 'generate_report') {
94
+ await hooks.emit('onReviewProgress', { projectId, mrId: mrIid, status: 'AI 审查完成,正在生成报告' });
91
95
  reviewResult = action.result;
92
96
  break;
93
97
  } else if (action.action === 'request_files') {
94
- if (!options.yes) logger.stopSpinner('ai-thinking', true, `AI 请求查看额外文件: ${action.files.join(', ')}`);
98
+ const reqFilesStr = action.files.join(', ');
99
+ if (!options.yes) logger.stopSpinner('ai-thinking', true, `AI 请求查看额外文件: ${reqFilesStr}`);
100
+ await hooks.emit('onReviewProgress', { projectId, mrId: mrIid, status: `AI 正在拉取依赖文件: ${reqFilesStr}` });
95
101
 
96
102
  const contents = await fetchFileContents(gitlab, projectId, detail.source_branch, changes, action.files, action.contentTypes);
97
103
  const fileContentMessage = formatFileContents(contents);
@@ -24,11 +24,13 @@ module.exports = async function setupOpenClaw() {
24
24
 
25
25
  const spinner = ora('正在写入 hooks 配置...').start();
26
26
 
27
- const successCurl = `curl -s -X POST ${answers.webhookUrl} -H 'Authorization: Bearer ${answers.token}' -H 'Content-Type: application/json' -d '{"mrId": "$MR_ID", "status": "Success"}' > /dev/null`;
28
- const failedCurl = `curl -s -X POST ${answers.webhookUrl} -H 'Authorization: Bearer ${answers.token}' -H 'Content-Type: application/json' -d '{"mrId": "$MR_ID", "status": "Failed"}' > /dev/null`;
27
+ // 使用 \" 保证 JSON 结构合法,同时允许 bash 解析其中的 $MR_ID $REVIEW_STATUS
28
+ const generateCurl = (statusValue) => `curl -s -X POST ${answers.webhookUrl} -H "Authorization: Bearer ${answers.token}" -H "Content-Type: application/json" -d "{\\"mrId\\": \\"$MR_ID\\", \\"status\\": \\"${statusValue}\\"}" > /dev/null`;
29
29
 
30
- configCmd.set('hooks.onReviewSuccess', successCurl);
31
- configCmd.set('hooks.onReviewFailed', failedCurl);
30
+ configCmd.set('hooks.onReviewStart', generateCurl('Started'));
31
+ configCmd.set('hooks.onReviewProgress', generateCurl('$REVIEW_STATUS'));
32
+ configCmd.set('hooks.onReviewSuccess', generateCurl('Success'));
33
+ configCmd.set('hooks.onReviewFailed', generateCurl('Failed'));
32
34
 
33
- spinner.succeed('配置完成!您已成功接入 OpenClaw 路由,审查结果将自动进行广播推送。');
35
+ spinner.succeed('配置完成!您已成功接入 OpenClaw 路由,审查过程的所有动态都将自动进行广播推送。');
34
36
  };
@@ -12,6 +12,9 @@ class Hooks {
12
12
  * 执行指定生命周期的 Hook
13
13
  */
14
14
  async emit(eventName, payload = {}) {
15
+ // 每次执行时都重新实时拉取 config,以便读取到最新通过命令设置的全局 hook 配置
16
+ this.hooksConfig = storage.get('hooks') || {};
17
+
15
18
  const hookAction = this.hooksConfig[eventName];
16
19
  if (!hookAction) return;
17
20
 
@@ -45,6 +48,7 @@ class Hooks {
45
48
  // 也可以把特定的字段展开
46
49
  if (payload.mrId) env['MR_ID'] = payload.mrId;
47
50
  if (payload.status) env['REVIEW_STATUS'] = payload.status;
51
+ if (payload.error) env['REVIEW_ERROR'] = payload.error;
48
52
 
49
53
  exec(command, { env }, (error, stdout, stderr) => {
50
54
  if (error) {