@deepstorm/cli 0.2.4 → 0.3.1
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/dist/agents/reef-review-security.md +106 -0
- package/dist/cli.js +8 -6
- package/dist/hooks/reef-hooks.json +6 -6
- package/dist/hooks/sweep-hooks.json +1 -1
- package/dist/hooks/tide-hooks.json +1 -1
- package/dist/skills/reef-commit/SKILL.md +83 -8
- package/dist/skills/reef-review/SKILL.md.tmpl +74 -14
- package/package.json +1 -1
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: reef-review-security
|
|
3
|
+
description: 对安全敏感变更执行专项安全审查,覆盖多租户隔离、认证授权、注入防护、敏感数据保护
|
|
4
|
+
tools: Bash(git:*), Read
|
|
5
|
+
permissionMode: plan
|
|
6
|
+
model: sonnet
|
|
7
|
+
color: red
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
你是一名安全代码审查员,负责审查变更中的安全风险,覆盖多租户隔离、认证授权、注入防护、敏感数据保护等维度。
|
|
11
|
+
|
|
12
|
+
## Review Checklist
|
|
13
|
+
|
|
14
|
+
按优先级从高到低逐项检查。
|
|
15
|
+
|
|
16
|
+
### P0 — 多租户/数据隔离(数据安全事故)
|
|
17
|
+
- 多租户数据隔离是否被绕过(如缺少 `tenant_id` 过滤条件)
|
|
18
|
+
- 硬编码租户 ID 或跨租户数据泄露路径
|
|
19
|
+
- 操作日志是否完整记录租户上下文
|
|
20
|
+
|
|
21
|
+
### P1 — 认证与会话
|
|
22
|
+
- 认证 bypass(未受保护的路由、缺少 auth 拦截器/中间件)
|
|
23
|
+
- 会话管理问题(Token 未设置过期、刷新 Token 未做轮换)
|
|
24
|
+
- 密码存储:是否为明文或弱哈希(必须 bcrypt / argon2)
|
|
25
|
+
- OAuth/OIDC 流程中的 CSRF / redirect_uri 未校验
|
|
26
|
+
|
|
27
|
+
### P2 — 授权与越权
|
|
28
|
+
- IDOR(通过参数篡改访问他人数据)
|
|
29
|
+
- 垂直越权(普通用户访问管理员接口)
|
|
30
|
+
- 水平越权(用户 A 访问用户 B 的数据)
|
|
31
|
+
- 接口缺少 `@PreAuthorize` 或等效权限校验
|
|
32
|
+
|
|
33
|
+
### P3 — 输入验证与注入
|
|
34
|
+
- 原始 SQL 拼接(必须使用 ORM 参数化查询)
|
|
35
|
+
- 命令注入(`Runtime.exec()` / `subprocess` / `os.system`)
|
|
36
|
+
- NoSQL 注入(MongoDB `$where`)
|
|
37
|
+
- XSS:用户输入未经转义直接渲染(`innerHTML` / `v-html` / dangerouslySetInnerHTML)
|
|
38
|
+
- SSRF:用户可控 URL 被服务端直接请求
|
|
39
|
+
- 文件上传:路径穿越、类型校验缺失
|
|
40
|
+
- 序列化漏洞(Java 反序列化 / pickle.loads)
|
|
41
|
+
|
|
42
|
+
### P4 — 敏感数据保护
|
|
43
|
+
- 密钥/证书/Token 硬编码(环境变量替代)
|
|
44
|
+
- 日志输出中泄露 PII / 密钥 / Token
|
|
45
|
+
- 接口响应中返回了敏感字段(密码 hash、内部 IP、数据库连接串)
|
|
46
|
+
- 传输层:内部 API 未启用 TLS(mTLS)
|
|
47
|
+
- 前端存储:Token / 密钥存储在 localStorage 而非 httpOnly cookie
|
|
48
|
+
|
|
49
|
+
### P5 — 依赖与配置安全
|
|
50
|
+
- 引入已知 CVE 的依赖版本
|
|
51
|
+
- CORS 配置过于宽泛(`Access-Control-Allow-Origin: *` + 含认证凭证)
|
|
52
|
+
- CSP / HSTS / X-Frame-Options 等安全头缺失
|
|
53
|
+
- 调试接口 / Swagger 暴露到生产环境
|
|
54
|
+
- Docker 容器以 root 用户运行
|
|
55
|
+
|
|
56
|
+
### 🔴 禁止(Block)— 新增维度
|
|
57
|
+
- CLAUDE.md 明确规定的安全红线被变更违反(如"禁止手动拼 WHERE tenant_id = ?")
|
|
58
|
+
- 变更触及了 `// SECURITY:` / `// @audit` 标注的安全敏感区域但未做安全处理
|
|
59
|
+
|
|
60
|
+
### 🟡 必须(Request Changes)
|
|
61
|
+
- 变更区域在 git 历史中曾因安全漏洞修复过(有 `security`/`CVE`/`vuln`/`fix` 标记的 commit),当前变更可能回归
|
|
62
|
+
- 代码注释中有 `// FIXME` / `// HACK` 安全标注但变更未处理
|
|
63
|
+
|
|
64
|
+
## Workflow
|
|
65
|
+
|
|
66
|
+
1. 阅读 prompt 中提供的 CLAUDE.md → 提取安全相关规范条款
|
|
67
|
+
2. 阅读 prompt 中提供的代码注释标注上下文 → 查找变更附近的安全相关注释(`// SECURITY:` / `// @audit` / `// WARNING:`)
|
|
68
|
+
3. 获取安全敏感文件的 diff:`git diff "<fork_point>"..HEAD --name-only`
|
|
69
|
+
4. 阅读关键行 + git history 安全追踪:
|
|
70
|
+
- `git log --oneline -20 -- <file> | grep -i 'security\|CVE\|vuln\|fix\|audit\|CVE'` 查看安全相关历史
|
|
71
|
+
- 标记曾因安全原因修改过的区域
|
|
72
|
+
5. 逐项通过 Checklist(P0 → P1 → P2 → P3 → P4 → P5 → 🔴 → 🟡)
|
|
73
|
+
6. 额外维度:检查 CLAUDE.md 中明确禁止的安全模式是否在当前变更中出现
|
|
74
|
+
7. 输出结构化报告(含证据链)
|
|
75
|
+
|
|
76
|
+
## Output Format
|
|
77
|
+
|
|
78
|
+
仅输出以下格式的审查报告(每个 issue 后附加证据来源):
|
|
79
|
+
|
|
80
|
+
## 安全审查报告
|
|
81
|
+
|
|
82
|
+
### P0 — 数据隔离(数据安全事故)
|
|
83
|
+
1. **[文件:行号]** 问题描述 -> 修复建议
|
|
84
|
+
**证据**:🧾 CLAUDE.md → `CLAUDE.md`#L行号 "规范条款原文"
|
|
85
|
+
|
|
86
|
+
### P1 — 认证与会话
|
|
87
|
+
1. **[文件:行号]** 问题描述 -> 修复建议
|
|
88
|
+
**证据**:📜 git log → `commit_hash`: 该区域曾因安全问题修复过
|
|
89
|
+
|
|
90
|
+
### P2 — 授权与越权
|
|
91
|
+
1. **[文件:行号]** 问题描述 -> 修复建议
|
|
92
|
+
**证据**:📝 `// SECURITY:` 注释原文 at `文件:行号`
|
|
93
|
+
|
|
94
|
+
### 🔴 禁止(Block)
|
|
95
|
+
1. **[文件:行号]** 问题描述 -> 修复建议
|
|
96
|
+
**证据**:🧾 CLAUDE.md → `CLAUDE.md`#L行号 "规范条款原文"
|
|
97
|
+
|
|
98
|
+
### 🟡 必须(Request Changes)
|
|
99
|
+
1. **[文件:行号]** 问题描述 -> 修复建议
|
|
100
|
+
|
|
101
|
+
**证据类型符号**:
|
|
102
|
+
- 🧾 CLAUDE.md 规范条款
|
|
103
|
+
- 📜 git log 安全修复历史上下文
|
|
104
|
+
- 📝 代码注释(SECURITY / @audit / WARNING)
|
|
105
|
+
|
|
106
|
+
评分:P0 存在(必须修复)| 存在 🔴/🟡(Request Changes)| 全通过(Approve)
|
package/dist/cli.js
CHANGED
|
@@ -6757,6 +6757,10 @@ function cleanInstalled(targetDir) {
|
|
|
6757
6757
|
}
|
|
6758
6758
|
}
|
|
6759
6759
|
}
|
|
6760
|
+
const hooksJson = path2.join(targetDir, ".claude", "hooks.json");
|
|
6761
|
+
if (fs6.existsSync(hooksJson)) {
|
|
6762
|
+
fs6.rmSync(hooksJson, { force: true });
|
|
6763
|
+
}
|
|
6760
6764
|
const hooksDir = path2.join(targetDir, ".claude", "hooks");
|
|
6761
6765
|
if (fs6.existsSync(hooksDir)) {
|
|
6762
6766
|
fs6.rmSync(hooksDir, { recursive: true, force: true });
|
|
@@ -7003,12 +7007,11 @@ async function runSetup(reader, targetDir, cliDir2, options, registry2) {
|
|
|
7003
7007
|
}
|
|
7004
7008
|
if (shouldInstallGlobalHooks(tools, reader)) {
|
|
7005
7009
|
const hooksSrcDir = path6.join(cliDir2, "hooks");
|
|
7006
|
-
const
|
|
7007
|
-
const destHooksJson = path6.join(targetHooksDir, "hooks.json");
|
|
7010
|
+
const destHooksJson = path6.join(targetDir, ".claude", "hooks.json");
|
|
7008
7011
|
for (const tool of tools) {
|
|
7009
7012
|
const toolHooksJsonPath = path6.join(hooksSrcDir, `${tool}-hooks.json`);
|
|
7010
7013
|
if (fs10.existsSync(toolHooksJsonPath)) {
|
|
7011
|
-
ensureDir(
|
|
7014
|
+
ensureDir(path6.join(targetDir, ".claude"));
|
|
7012
7015
|
const incoming = JSON.parse(fs10.readFileSync(toolHooksJsonPath, "utf-8"));
|
|
7013
7016
|
mergeHooks(destHooksJson, incoming);
|
|
7014
7017
|
}
|
|
@@ -8338,12 +8341,11 @@ function loadRegistryFromCliDir(cliDir2) {
|
|
|
8338
8341
|
}
|
|
8339
8342
|
function mergeToolHooksJson(toolNames, cliDir2, targetDir) {
|
|
8340
8343
|
const hooksSrcDir = path18.join(cliDir2, "hooks");
|
|
8341
|
-
const
|
|
8342
|
-
const destHooksJson = path18.join(targetHooksDir, "hooks.json");
|
|
8344
|
+
const destHooksJson = path18.join(targetDir, ".claude", "hooks.json");
|
|
8343
8345
|
for (const tool of toolNames) {
|
|
8344
8346
|
const toolHooksJsonPath = path18.join(hooksSrcDir, `${tool}-hooks.json`);
|
|
8345
8347
|
if (fs20.existsSync(toolHooksJsonPath)) {
|
|
8346
|
-
ensureDir(
|
|
8348
|
+
ensureDir(path18.join(targetDir, ".claude"));
|
|
8347
8349
|
const incoming = JSON.parse(fs20.readFileSync(toolHooksJsonPath, "utf-8"));
|
|
8348
8350
|
mergeHooks(destHooksJson, incoming);
|
|
8349
8351
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"hooks": [
|
|
6
6
|
{
|
|
7
7
|
"type": "command",
|
|
8
|
-
"command": "bash
|
|
8
|
+
"command": "bash .claude/hooks/reef-intent-detect.sh"
|
|
9
9
|
}
|
|
10
10
|
]
|
|
11
11
|
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"hooks": [
|
|
17
17
|
{
|
|
18
18
|
"type": "command",
|
|
19
|
-
"command": "bash
|
|
19
|
+
"command": "bash .claude/hooks/reef-block-dangerous.sh"
|
|
20
20
|
}
|
|
21
21
|
]
|
|
22
22
|
},
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"hooks": [
|
|
26
26
|
{
|
|
27
27
|
"type": "command",
|
|
28
|
-
"command": "bash
|
|
28
|
+
"command": "bash .claude/hooks/reef-protect-files.sh"
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
"type": "prompt",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"hooks": [
|
|
42
42
|
{
|
|
43
43
|
"type": "command",
|
|
44
|
-
"command": "bash
|
|
44
|
+
"command": "bash .claude/hooks/reef-auto-format.sh"
|
|
45
45
|
}
|
|
46
46
|
]
|
|
47
47
|
},
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"hooks": [
|
|
51
51
|
{
|
|
52
52
|
"type": "command",
|
|
53
|
-
"command": "bash
|
|
53
|
+
"command": "bash .claude/hooks/reef-run-tests.sh",
|
|
54
54
|
"if": "Edit(*.ts)|Write(*.ts)",
|
|
55
55
|
"async": true,
|
|
56
56
|
"statusMessage": "Running tests..."
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"hooks": [
|
|
64
64
|
{
|
|
65
65
|
"type": "command",
|
|
66
|
-
"command": "bash
|
|
66
|
+
"command": "bash .claude/hooks/reef-run-tests.sh"
|
|
67
67
|
}
|
|
68
68
|
]
|
|
69
69
|
}
|
|
@@ -39,7 +39,82 @@ git status --short
|
|
|
39
39
|
|
|
40
40
|
若无任何变更则提示用户并退出。
|
|
41
41
|
|
|
42
|
-
### 2.
|
|
42
|
+
### 2. 分支名与任务相关性检查
|
|
43
|
+
|
|
44
|
+
在审查待提交文件之前,先检查当前分支是否与待提交的任务相关。如果当前在 `main`/`master` 分支上则**必须**创建新分支;如果分支名与任务内容明显不匹配(如命名随意、与 OpenSpec 任务不符),建议创建新分支。
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
BRANCH=$(git branch --show-current)
|
|
48
|
+
echo "当前分支: $BRANCH"
|
|
49
|
+
|
|
50
|
+
# 检测是否需要创建新分支
|
|
51
|
+
MUST_NEW_BRANCH=false
|
|
52
|
+
|
|
53
|
+
# 条件一:在 main 或 master 上必须创建新分支
|
|
54
|
+
if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
|
|
55
|
+
echo "⚠️ 当前在 $BRANCH 分支上,不允许直接提交,必须创建新分支"
|
|
56
|
+
MUST_NEW_BRANCH=true
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# 条件二:检查分支名是否包含 temp、wip、test、tmp、dev 等临时名称
|
|
60
|
+
TEMP_PATTERN='^(temp|wip|test|tmp|dev)(/.*)?$'
|
|
61
|
+
if echo "$BRANCH" | grep -qE "$TEMP_PATTERN"; then
|
|
62
|
+
echo "⚠️ 当前分支名 ($BRANCH) 看起来是临时分支,建议创建有意义的新分支"
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# 收集 OpenSpec 任务上下文
|
|
66
|
+
for dir in openspec/changes/*/; do
|
|
67
|
+
if [ -f "$dir/proposal.md" ]; then
|
|
68
|
+
TASK_NAME=$(basename "$dir")
|
|
69
|
+
echo "发现 OpenSpec 任务: $TASK_NAME"
|
|
70
|
+
fi
|
|
71
|
+
done
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**判断规则(LLM 自行推理执行):**
|
|
75
|
+
|
|
76
|
+
1. **如果 MUST_NEW_BRANCH=true**(当前在 main/master):
|
|
77
|
+
- 直接进入步骤 3「创建新分支」,无需询问用户
|
|
78
|
+
2. **如果分支名明显不相关**(如 `temp-xxx`、`test-foo`、或随意命名与当前变更毫无关联):
|
|
79
|
+
- 向用户说明:「当前分支 $BRANCH 与待提交的变更内容似乎不匹配,是否创建一个新分支?」
|
|
80
|
+
- 用户同意 → 进入步骤 3「创建新分支」
|
|
81
|
+
- 用户不同意 → 继续当前分支
|
|
82
|
+
3. **如果分支名合理**(如与 OpenSpec 任务同名,或包含功能描述的 kebab-case):
|
|
83
|
+
- 直接继续
|
|
84
|
+
|
|
85
|
+
### 3. 创建新分支
|
|
86
|
+
|
|
87
|
+
当需要创建新分支时,按以下逻辑确定分支名:
|
|
88
|
+
|
|
89
|
+
**分支名生成规则(优先级从高到低):**
|
|
90
|
+
1. **OpenSpec 任务名**:如果检测到 `openspec/changes/<task>/proposal.md`,使用 `<task>` 作为分支名
|
|
91
|
+
2. **用户输入**:询问用户想要的分支名
|
|
92
|
+
3. **AI 推导**:根据变更内容总结生成 kebab-case 分支名(例如 `feat/add-user-auth`、`fix/login-timeout`)
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# 暂存当前未提交变更
|
|
96
|
+
STASHED=false
|
|
97
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
98
|
+
git stash push -m "reef-commit-auto-stash"
|
|
99
|
+
STASHED=true
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# 创建并切换到新分支(基于 main)
|
|
103
|
+
git checkout main
|
|
104
|
+
git pull origin main 2>/dev/null || true
|
|
105
|
+
git checkout -b <new-branch-name>
|
|
106
|
+
|
|
107
|
+
# 恢复暂存的变更
|
|
108
|
+
if [ "$STASHED" = true ]; then
|
|
109
|
+
git stash pop
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
echo "✅ 已切换到新分支: <new-branch-name>"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
> **注意**:创建新分支后,后续步骤(审查文件、范围检查、测试等)在新分支上继续执行。
|
|
116
|
+
|
|
117
|
+
### 4. 审查待提交文件
|
|
43
118
|
|
|
44
119
|
展示变更清单,检查敏感文件:
|
|
45
120
|
|
|
@@ -49,7 +124,7 @@ git diff --stat
|
|
|
49
124
|
|
|
50
125
|
如包含 `.env`、凭据、证书或大型二进制文件,让用户确认或排除后再继续。
|
|
51
126
|
|
|
52
|
-
###
|
|
127
|
+
### 5. 分支范围检查
|
|
53
128
|
|
|
54
129
|
检查当前分支是否涉及多个业务领域。如果跨领域,给出拆分建议并中止提交。
|
|
55
130
|
|
|
@@ -68,7 +143,7 @@ if [ -f "$SCOPE_HOOK" ]; then
|
|
|
68
143
|
fi
|
|
69
144
|
```
|
|
70
145
|
|
|
71
|
-
###
|
|
146
|
+
### 6. 运行单元测试
|
|
72
147
|
|
|
73
148
|
```bash
|
|
74
149
|
git status --short
|
|
@@ -84,7 +159,7 @@ git status --short
|
|
|
84
159
|
- 测试全部通过 → 继续
|
|
85
160
|
- 任一测试失败 → 提示用户修复后再提交
|
|
86
161
|
|
|
87
|
-
###
|
|
162
|
+
### 7. 收集上下文
|
|
88
163
|
|
|
89
164
|
```bash
|
|
90
165
|
BRANCH=$(git branch --show-current)
|
|
@@ -97,24 +172,24 @@ grep -iE '(issue|jira|lc-|proj-)' "openspec/changes/$BRANCH/proposal.md" 2>/dev/
|
|
|
97
172
|
git log "$FORK_POINT"..HEAD --format="%B" 2>/dev/null | grep -ioP '[A-Z]+-\d+' | head -1
|
|
98
173
|
```
|
|
99
174
|
|
|
100
|
-
###
|
|
175
|
+
### 8. 生成提交信息
|
|
101
176
|
|
|
102
177
|
**标题:** 优先 OpenSpec proposal 标题 → 分支名(kebab-case 转中文)→ 变更总结。依赖更新用 `更新 {依赖} 至 {版本}`,问题修复用 `解决{问题描述}的问题`。
|
|
103
178
|
|
|
104
179
|
**正文:** 中/复杂变更用 `本次提交改动如下:` + 3-6 条要点。尾部隔空行加 `JIRA: {完整 URL}`(优先从 proposal.md/jira-start 元数据中获取 JIRA 链接),如有 PRD 链接加 `Ref: {URL}`。
|
|
105
180
|
|
|
106
|
-
###
|
|
181
|
+
### 9. 展示并确认
|
|
107
182
|
|
|
108
183
|
展示完整提交信息,用户确认即可提交;支持 `--amend` 合并;用户可要求修改后重新生成。
|
|
109
184
|
|
|
110
|
-
###
|
|
185
|
+
### 10. 执行提交
|
|
111
186
|
|
|
112
187
|
```bash
|
|
113
188
|
git add -A && git commit -m "<完整提交信息>"
|
|
114
189
|
# 用户要求 amend 时: git commit --amend -m "<完整提交信息>"
|
|
115
190
|
```
|
|
116
191
|
|
|
117
|
-
###
|
|
192
|
+
### 11. 推送(仅在用户要求时)
|
|
118
193
|
|
|
119
194
|
```bash
|
|
120
195
|
# 新提交(有远程跟踪):git push
|
|
@@ -12,7 +12,7 @@ deepstorm:
|
|
|
12
12
|
|
|
13
13
|
## 工作流
|
|
14
14
|
|
|
15
|
-
### Step 1: 检测变更范围
|
|
15
|
+
### Step 1: 检测变更范围 + Eligibility 预检
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
# 获取 fork-point(检测当前分支基于哪个分支创建)
|
|
@@ -28,6 +28,14 @@ BACKEND_FILES=$(git diff "$FORK_POINT"..HEAD --name-only -- {{reef.backend.langu
|
|
|
28
28
|
FRONTEND_FILES=$(git diff "$FORK_POINT"..HEAD --name-only -- {{reef.frontend.framework.sourcePath}})
|
|
29
29
|
INFRA_FILES=$(git diff "$FORK_POINT"..HEAD --name-only | grep -v '^{{reef.backend.language.sourcePath}}' | grep -v '^{{reef.frontend.framework.sourcePath}}')
|
|
30
30
|
SECURITY_FILES=$(git diff "$FORK_POINT"..HEAD --name-only -- {{reef.backend.language.sourcePath}} {{reef.frontend.framework.sourcePath}} | grep -iE 'auth|tenant|security|oauth|token|password|permission' || true)
|
|
31
|
+
|
|
32
|
+
# Eligibility 预检:明显不需要审查的变更跳过派发
|
|
33
|
+
LOCK_ONLY=$(echo "$FILE_LIST" | grep -cE 'package-lock\.json|yarn\.lock|pnpm-lock\.yaml' || true)
|
|
34
|
+
DOCS_ONLY=$(echo "$FILE_LIST" | grep -vE 'package-lock\.json|yarn\.lock|pnpm-lock\.yaml' | grep -cE '\.md$|\.txt$|^docs/' || true)
|
|
35
|
+
if [ "$LOCK_ONLY" -gt 0 ] && [ "$(echo "$FILE_LIST" | wc -l | tr -d ' ')" -eq "$LOCK_ONLY" ]; then
|
|
36
|
+
echo "ELIGIBLE=false"
|
|
37
|
+
echo "REASON=仅 lock 文件变更,跳过代码审查"
|
|
38
|
+
fi
|
|
31
39
|
```
|
|
32
40
|
|
|
33
41
|
判断哪些类别有变更:
|
|
@@ -40,7 +48,7 @@ SECURITY_FILES=$(git diff "$FORK_POINT"..HEAD --name-only -- {{reef.backend.lang
|
|
|
40
48
|
| 安全敏感 | `SECURITY_FILES` 非空 |
|
|
41
49
|
| 无匹配 | 全部为空 |
|
|
42
50
|
|
|
43
|
-
据此决定派发哪些 agent
|
|
51
|
+
据此决定派发哪些 agent(如果 ELIGIBLE=false 则不派发):
|
|
44
52
|
|
|
45
53
|
| 有变更的类别 | 派发 agent |
|
|
46
54
|
|-------------|-----------|
|
|
@@ -56,24 +64,71 @@ SECURITY_FILES=$(git diff "$FORK_POINT"..HEAD --name-only -- {{reef.backend.lang
|
|
|
56
64
|
|
|
57
65
|
> 安全敏感变更判定:SECURITY_FILES 非空 或 变更涉及权限/认证逻辑(由调用的 fork-point 范围决定)。
|
|
58
66
|
|
|
59
|
-
### Step 2: 派发 Sub‑Agent
|
|
60
|
-
|
|
61
|
-
每个 agent 按各自类别的文件清单构造 prompt。后端 agent 的清单含后端源文件,前端和 infra agent 只含自己的文件。
|
|
67
|
+
### Step 2: 收集上下文 + 派发 Sub‑Agent
|
|
62
68
|
|
|
63
|
-
|
|
64
|
-
|-------|-------------------|
|
|
65
|
-
| `backend-code-audit` | 后端文件 |
|
|
66
|
-
| `frontend-code-audit` | 前端文件 |
|
|
67
|
-
| `infra-code-audit` | 基础配置文件 |
|
|
68
|
-
| `security-code-audit` | 安全敏感文件 + 所有变更文件中有安全风险的 diff |
|
|
69
|
+
在派发前,收集 CLAUDE.md 和 git 上下文,透传给每个 agent:
|
|
69
70
|
|
|
71
|
+
```bash
|
|
72
|
+
# 收集根目录 CLAUDE.md 和变更文件夹下的 CLAUDE.md
|
|
73
|
+
CLAUDE_MD_FILES=$(echo "$FILE_LIST" | xargs -I{} dirname {} | sort -u | xargs -I{} sh -c '
|
|
74
|
+
if [ -f "{}/CLAUDE.md" ]; then echo "{}/CLAUDE.md"; fi
|
|
75
|
+
' 2>/dev/null)
|
|
76
|
+
[ -f "CLAUDE.md" ] && CLAUDE_MD_FILES="CLAUDE.md $CLAUDE_MD_FILES"
|
|
77
|
+
|
|
78
|
+
# 收集 git blame 摘要(取前 5 个变更文件的 git log 历史)
|
|
79
|
+
GIT_HISTORY_SUMMARY=$(echo "$BACKEND_FILES $FRONTEND_FILES" | tr ' ' '\n' | sort -u | grep -v '^$' | head -5 | while IFS= read -r f; do
|
|
80
|
+
echo "--- $f ---"
|
|
81
|
+
git log --oneline -10 -- "$f" 2>/dev/null | head -5
|
|
82
|
+
done)
|
|
83
|
+
|
|
84
|
+
# 收集代码注释上下文(FIXME / HACK / WARNING 标注)
|
|
85
|
+
COMMENT_CONTEXT=$(echo "$BACKEND_FILES $FRONTEND_FILES" | tr ' ' '\n' | sort -u | grep -v '^$' | head -10 | while IFS= read -r f; do
|
|
86
|
+
if [ -f "$f" ]; then
|
|
87
|
+
matches=$(git diff "$FORK_POINT"..HEAD -- "$f" | grep -E '^\+' | grep -iE 'FIXME|HACK|WARNING|SECURITY|@audit|TODO' || true)
|
|
88
|
+
[ -n "$matches" ] && echo "--- $f ---" && echo "$matches"
|
|
89
|
+
fi
|
|
90
|
+
done)
|
|
70
91
|
```
|
|
92
|
+
|
|
93
|
+
每个 agent 的 prompt 构造为标准前缀 + 上下文数据 + false positive 规则:
|
|
94
|
+
|
|
95
|
+
```markdown
|
|
96
|
+
## 变更上下文
|
|
97
|
+
|
|
71
98
|
Fork point: {FORK_POINT}
|
|
72
99
|
变更文件数: {count}
|
|
73
100
|
变更文件清单:
|
|
74
101
|
{file_list}
|
|
102
|
+
|
|
103
|
+
## 相关规范文件(CLAUDE.md)
|
|
104
|
+
|
|
105
|
+
{CLAUDE_MD_FILES_content}
|
|
106
|
+
|
|
107
|
+
## Git 历史上下文
|
|
108
|
+
|
|
109
|
+
{GIT_HISTORY_SUMMARY}
|
|
110
|
+
|
|
111
|
+
## 代码注释标注
|
|
112
|
+
|
|
113
|
+
{COMMENT_CONTEXT}
|
|
114
|
+
|
|
115
|
+
## 不算 issues 的情况(不要误报)
|
|
116
|
+
|
|
117
|
+
- 已经有 lint/typecheck/CI 保障的问题(import 错误、类型错误、格式问题)
|
|
118
|
+
- 新增功能的测试覆盖率不足(非本变更范围)
|
|
119
|
+
- 变更前的已有问题(pre-existing issue),除非变更使之更严重
|
|
120
|
+
- NPM/Gradle 版本更新中的上游 breaking change
|
|
121
|
+
- 与模块内已有实现一致的新增代码(一致性值得保留,除非原实现就有 bug)
|
|
122
|
+
- 纯格式/空白/注释变更
|
|
75
123
|
```
|
|
76
124
|
|
|
125
|
+
| Agent | prompt 中的文件清单 |
|
|
126
|
+
|-------|-------------------|
|
|
127
|
+
| `backend-code-audit` | 后端文件 |
|
|
128
|
+
| `frontend-code-audit` | 前端文件 |
|
|
129
|
+
| `infra-code-audit` | 基础配置文件 |
|
|
130
|
+
| `security-code-audit` | 安全敏感文件 |
|
|
131
|
+
|
|
77
132
|
Agent 的 system prompt(定义在 `.claude/agents/` 目录中)包含完整的 Checklist + Rules + 输出格式。
|
|
78
133
|
|
|
79
134
|
| Agent | 定义文件 |
|
|
@@ -85,10 +140,15 @@ Agent 的 system prompt(定义在 `.claude/agents/` 目录中)包含完整
|
|
|
85
140
|
|
|
86
141
|
多 agent 场景全部使用 `run_in_background: true` 并行执行。每个 agent 设置超时 300 秒(5 分钟),超时未返回则标记为超时,继续等待其他 agent。
|
|
87
142
|
|
|
88
|
-
### Step 3:
|
|
143
|
+
### Step 3: 汇总报告(含证据链评分)
|
|
89
144
|
|
|
90
145
|
1. 等待所有已派发的 agent 返回(收到全部 task-notification 后才汇总)。任一 agent 超时 300 秒未返回则标记为超时,继续等待其他 agent
|
|
91
|
-
2.
|
|
146
|
+
2. 聚合评分规则:
|
|
147
|
+
- 若 Block 项附有证据链(🧾 `.md` / 📜 `git log` / 📝 `// comment` / 📚 `context7` / 🛠 `style-*`),保留原评级
|
|
148
|
+
- 若 Block 项**无**证据链,降级为 Request Changes
|
|
149
|
+
- 若 Request Changes 项无证据链,降级为 Suggestion
|
|
150
|
+
- 确保报告不膨胀,每个 issue 一句话 + 一个链接
|
|
151
|
+
3. 分章节输出各 agent 的审查报告:
|
|
92
152
|
|
|
93
153
|
```
|
|
94
154
|
## 后端代码审查报告
|
|
@@ -104,4 +164,4 @@ Agent 的 system prompt(定义在 `.claude/agents/` 目录中)包含完整
|
|
|
104
164
|
{security agent 输出(仅安全敏感变更时)}
|
|
105
165
|
```
|
|
106
166
|
|
|
107
|
-
|
|
167
|
+
4. 最终结论取最低评分。若某 agent 失败(API Error / 超时等),标注失败原因,忽略其评分。仅有派发过的 agent 输出对应章节。
|