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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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.
|
|
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
|
-
|
|
65
|
-
|
|
82
|
+
3. 留言与评论:
|
|
83
|
+
如果用户想给 MR 直接留言,例如“给 101 说这个写的很棒”
|
|
84
|
+
请在底层执行命令:`hong-review comment 101 "这个写的很棒"`
|
|
66
85
|
|
|
67
|
-
|
|
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
package/src/commands/mr.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
28
|
-
const
|
|
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.
|
|
31
|
-
configCmd.set('hooks.
|
|
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
|
};
|
package/src/utils/hooks.js
CHANGED
|
@@ -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) {
|