@routerhub/agent-rules 1.5.153 → 1.5.155
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/AGENTS.base.md +8 -0
- package/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/rules/global.md +8 -0
- package/skills/github-pr-review/SKILL.md +30 -0
- package/skills/loop-review/SKILL.md +44 -1
- package/skills/loop-review/scripts/query-review-decision.sh +60 -0
- package/skills/loop-review/scripts/save-review-decision.sh +65 -0
package/AGENTS.base.md
CHANGED
|
@@ -368,6 +368,14 @@
|
|
|
368
368
|
|
|
369
369
|
- 新建文档使用 `/create-doc` skill(HTML 格式、中文文件名、base64 内嵌、分步记录三要素等规范见该 skill)。
|
|
370
370
|
|
|
371
|
+
## 文档/文件链接交付
|
|
372
|
+
|
|
373
|
+
- ⚠️ 给用户交付 docs/ 等文件链接时,禁止使用相对路径(如 `docs/模型xxx.html`)或 `file:///` 形式:相对路径含中文/空格时 VSCode 无法点击,`file:///` 被 VSCode webview 安全策略拦截,用户都打不开。
|
|
374
|
+
- ⚠️ 必须先启动本地 HTTP 服务提供 docs 目录(后台运行):`python3 -m http.server 8777 --bind 127.0.0.1`,启动后必须 `curl -s -o /dev/null -w "%{http_code}"` 验证返回 200。
|
|
375
|
+
- ⚠️ 然后交付 `http://127.0.0.1:8777/<相对路径>` 形式的链接,保证用户点击即可在默认浏览器打开。中文路径建议做 URL 编码,未编码也能打开(浏览器自动处理)。
|
|
376
|
+
- ⚠️ 端口固定使用 8777;若被占用,递增 +1 并告知用户实际端口。服务只绑定 `127.0.0.1`,仅本机可访问,不对外暴露。服务保持后台运行,用户不需要时再停止。
|
|
377
|
+
- 交付时同时给出可点击链接 + 简要内容说明,方便用户确认。
|
|
378
|
+
|
|
371
379
|
## Figma 还原
|
|
372
380
|
|
|
373
381
|
- Figma 设计还原使用 `/figma-to-code` skill,按 MCP 三步验证法执行。
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
所有对 @routerhub/agent-rules 的重大更改都会记录在这个文件中。
|
|
4
4
|
|
|
5
|
+
## [1.5.154] - 2026-08-27
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **loop-review 循环收敛状态持久化(跨会话防回跳)**:新增 `skills/loop-review/scripts/save-review-decision.sh`(判断表落盘)+ `query-review-decision.sh`(跨轮去重查询),把每轮「值得修 / Won't fix / 误报」的判断落盘到 `.tmp-loop-review/decisions/pr-<PR>.decisions.json`。会话中断 / 上下文压缩后,下轮用 `query-review-decision.sh` 命中已处理建议直接复用结论,从「靠对话记忆」升级为「靠文件记忆」,根治循环回跳。
|
|
10
|
+
- **外部事实核查命令化**:新增 `scripts/check-facts.sh`,把「审查方法④」从「依赖 reviewer 自觉 dig/curl」变成一条可复跑命令——对 PR 里的域名 / 子域 / 邮箱 / URL / 环境归属批量核查(`dig A` / `dig MX` / `curl -IL`),输出 PASS/FAIL 汇总,FAIL 项按「必须报」处理。
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **release.sh 规则漂移检查**:发版前若 `AGENTS.base.md` / `AGENTS.private.md` / `skills` / `rules` 有未提交改动,强制先 `node merge.js sync`,并用派生产物指纹(AGENTS.md / CLAUDE.md / .github 指令文件 / .claude/skills 目录)校验确实同步,否则中止发布——防止「规则改了但下游没吃到」的静默漂移。
|
|
15
|
+
- **github-pr-review 嵌入审稿边界**:处理每条评论前先用审稿边界判断(该不该改),而非凭 bot 严重度标签定夺;处理完必须给落点(修复 / Won't fix / 误报)并(loop-review 场景下)落盘 decisions。新增「Review boundary」Rules 章节。
|
|
16
|
+
|
|
5
17
|
## [1.5.152] - 2026-08-26
|
|
6
18
|
|
|
7
19
|
### Added
|
package/package.json
CHANGED
package/rules/global.md
CHANGED
|
@@ -368,6 +368,14 @@ name: "通用规则"
|
|
|
368
368
|
|
|
369
369
|
- 新建文档使用 `/create-doc` skill(HTML 格式、中文文件名、base64 内嵌、分步记录三要素等规范见该 skill)。
|
|
370
370
|
|
|
371
|
+
## 文档/文件链接交付
|
|
372
|
+
|
|
373
|
+
- ⚠️ 给用户交付 docs/ 等文件链接时,禁止使用相对路径(如 `docs/模型xxx.html`)或 `file:///` 形式:相对路径含中文/空格时 VSCode 无法点击,`file:///` 被 VSCode webview 安全策略拦截,用户都打不开。
|
|
374
|
+
- ⚠️ 必须先启动本地 HTTP 服务提供 docs 目录(后台运行):`python3 -m http.server 8777 --bind 127.0.0.1`,启动后必须 `curl -s -o /dev/null -w "%{http_code}"` 验证返回 200。
|
|
375
|
+
- ⚠️ 然后交付 `http://127.0.0.1:8777/<相对路径>` 形式的链接,保证用户点击即可在默认浏览器打开。中文路径建议做 URL 编码,未编码也能打开(浏览器自动处理)。
|
|
376
|
+
- ⚠️ 端口固定使用 8777;若被占用,递增 +1 并告知用户实际端口。服务只绑定 `127.0.0.1`,仅本机可访问,不对外暴露。服务保持后台运行,用户不需要时再停止。
|
|
377
|
+
- 交付时同时给出可点击链接 + 简要内容说明,方便用户确认。
|
|
378
|
+
|
|
371
379
|
## Figma 还原
|
|
372
380
|
|
|
373
381
|
- Figma 设计还原使用 `/figma-to-code` skill,按 MCP 三步验证法执行。
|
|
@@ -123,6 +123,27 @@ For each comment, in severity order:
|
|
|
123
123
|
7. **Apply fix** if approved
|
|
124
124
|
8. **Verify ALL issues** in the comment are addressed (multi-issue comments are common)
|
|
125
125
|
|
|
126
|
+
### 3.1 Apply the review boundary (审稿边界判断)
|
|
127
|
+
|
|
128
|
+
**severity 只是 bot 的标注,不是「该不该改」的依据。** 每一条评论在提案修改之前,先用项目的审稿边界(`AGENTS.base.md`「审稿边界 / Review Boundary」章节,源文件 `rules/review-boundary.md`)过一遍:
|
|
129
|
+
|
|
130
|
+
| 判断 | 处理 |
|
|
131
|
+
|------|------|
|
|
132
|
+
| **会导致实际 bug / 数据丢失 / 数据错误 / 安全漏洞**(有明确触发路径,读 diff 能确认) | ✅ 必须修 |
|
|
133
|
+
| **违反 AGENTS.base.md / AGENTS.private.md 的 ⚠️ 铁律**(DELETE 铁律、部署自包含、数据链路核对、环境配置禁止推断等) | ✅ 必须修 |
|
|
134
|
+
| **口味 / 风格偏好 / 重构建议**(换个命名、建议用某设计模式、提取公共函数) | ⛔ Won't fix |
|
|
135
|
+
| **纯防御性建议**(「如果未来 / 万一 / 边界情况下」开头,当前代码无实际触发路径) | ⛔ Won't fix |
|
|
136
|
+
| **无法确认真假的问题**(读 diff 不能确认会导致 bug) | ⛔ 不报 / Won't fix |
|
|
137
|
+
| **已处理过的问题**(已 Won't fix 或「已修复于 <hash>」,含历史 decisions 命中) | ⛔ 回复「已修复于 <hash>」或复用历史结论 |
|
|
138
|
+
| **重复评论**(同一问题被多视角/多轮重复提出) | 视为更高优先级(验证者重复 = 可能真问题),但仍按边界判定 |
|
|
139
|
+
|
|
140
|
+
- **判定一句话**:「这条不修,功能会不会错 / 数据会不会坏?」会 → 修;不会 → Won't fix。
|
|
141
|
+
- 每条该修的必须**引用具体 diff 行 + 触发路径**做证据,禁止只给结论。
|
|
142
|
+
- 宁可漏报一条真问题,不用十条口味问题去刷屏。
|
|
143
|
+
|
|
144
|
+
**处理完的每条都必须有落点**:修复 commit / Won't fix 回复 / 误报说明。不回复 bot 下轮必重提。
|
|
145
|
+
**同步落盘**:配合 `loop-review` 的 `save-review-decision.sh`,把判断结果写入 `.tmp-loop-review/decisions/`,跨会话去重(与 loop-review 共用同一收敛状态)。
|
|
146
|
+
|
|
126
147
|
### 4. Commit changes
|
|
127
148
|
|
|
128
149
|
Use git-commit skill format. Functional fixes get separate commits, cosmetic fixes are batched:
|
|
@@ -225,6 +246,15 @@ Bots (Gemini, Codex, etc.) review every push. **Each push should go through revi
|
|
|
225
246
|
- **Duplicate comments** -> treat as higher priority than their label (issue was already flagged before)
|
|
226
247
|
- **Related comments** -> group and fix together when they share root cause or file context
|
|
227
248
|
|
|
249
|
+
## Review boundary (edge, must follow)
|
|
250
|
+
|
|
251
|
+
- **ALWAYS** judge every comment by the project's review boundary (`rules/review-boundary.md` / AGENTS.base.md「审稿边界」章节), NEVER by the bot's severity label alone.
|
|
252
|
+
- **ALWAYS** fix comments that cause actual bug / data loss / security vulnerability with a reproducible trigger path, or that violate AGENTS.base.md / AGENTS.private.md ⚠️ rules.
|
|
253
|
+
- **ALWAYS** Won't fix style/refactor/defensive suggestions with no trigger path — do NOT fix them just because a bot flagged them.
|
|
254
|
+
- **ALWAYS** give every processed comment a resolution: fix commit, Won't fix, or false-alarm reply. A silent skip means the bot re-asks next round.
|
|
255
|
+
- **ALWAYS** record decisions to `.tmp-loop-review/decisions/` via `save-review-decision.sh` when doing loop-review; cross-session dedup depends on it.
|
|
256
|
+
- **NEVER** report an issue you cannot confirm from the diff. One real bug beats ten style suggestions.
|
|
257
|
+
|
|
228
258
|
## References
|
|
229
259
|
|
|
230
260
|
- `references/severity_guide.md` - Severity detection patterns (Gemini badges, CodeRabbit emoji, Cursor comments, keyword fallback, related comments heuristics)
|
|
@@ -36,6 +36,15 @@ AI review bot 在**每次 push 到 PR 时自动触发新一轮 review**。因此
|
|
|
36
36
|
→ 若本轮没有任何值得修的新问题 → 结束;否则进入下一轮
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
### 双模型交叉验证(第二视角)
|
|
40
|
+
|
|
41
|
+
循环 review 的「交叉验证」是两个模型互相纠偏——Claude Opus 与 GPT-5.5 由同一个 `github-actions[bot]` 发布,各自独立审查同一份 diff,再用判断表逐条收敛。
|
|
42
|
+
|
|
43
|
+
| 视角 | 谁在审 | 特点 | 捕捉盲区 |
|
|
44
|
+
|---|---|---|---|
|
|
45
|
+
| 第一视角 | 🤖 Claude Opus(`github-actions[bot]` 发布) | 深度语义理解,长链路追踪 | —— |
|
|
46
|
+
| 第二视角 | 🤖 GPT-5.5(同 bot 交叉验证) | 不同训练分布,捕捉 Opus 的误报/漏报 | 单一模型自证式漏报 |
|
|
47
|
+
|
|
39
48
|
## 当前 PR
|
|
40
49
|
|
|
41
50
|
!`gh pr view --json number,title,state -q '"PR #\(.number): \(.title) (\(.state))"' 2>/dev/null`
|
|
@@ -81,6 +90,37 @@ if [ -n "$LATEST" ] && [ "$LATEST" = "$CUR" ]; then echo "YES"; else echo "NO";
|
|
|
81
90
|
|
|
82
91
|
### 2. 汇总建议并逐条判断(核心)
|
|
83
92
|
|
|
93
|
+
#### 2.0 先查历史判断,跨轮去重(防止循环回跳的关键)
|
|
94
|
+
|
|
95
|
+
**收敛的记忆在 `decisions/` 目录,不在对话里。** 每次会话中断 / 上下文压缩后,上一轮的「已 Won't fix / 已修复」判断会丢,bot 下轮原样重提,循环就回跳了。因此**每轮开始,先把 bot 的建议与历史决策比对一遍**,命中「已处理过」的锚定到历史记录,不重复判断:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# 先建立建议到「去重键」的映射,再逐个查历史
|
|
99
|
+
# 例:某条建议摘要是「并行部署失败被静默忽略」
|
|
100
|
+
bash "$(git rev-parse --show-toplevel)/.claude/skills/loop-review/scripts/query-review-decision.sh" 26 summary "并行部署失败"
|
|
101
|
+
# 命中 → 输出历史决策 JSON(decision=fixed/wontfix/false_alarm),直接复用结论,不再重复判断
|
|
102
|
+
# 未命中(exit 1)→ 这条是新的,进入下方判断框架
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**查询策略**:建议有稳定 ID(如 review id + 序号)用 `id` 精确查;没有稳定 ID 就按摘要子串 `summary` 模糊查。**判断历史里已有结论的建议,直接复用「理由 + 落点」**,禁止重新判断一遍(那正是循环回跳的根源)。
|
|
106
|
+
|
|
107
|
+
**每轮结束时,把判断结果落盘**(写入搭配脚本 `save-review-decision.sh`):
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
bash "$(git rev-parse --show-toplevel)/.claude/skills/loop-review/scripts/save-review-decision.sh" '{
|
|
111
|
+
"pr": "26", "id": "opus-3fix", "severity": "warning",
|
|
112
|
+
"summary": "并行部署失败被静默忽略",
|
|
113
|
+
"decision": "fixed", "reason": "后台进程不受 set -e 管控,属真实风险",
|
|
114
|
+
"commit": "3fa21c0"
|
|
115
|
+
}'
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
决策 JSON 落盘到 `.tmp-loop-review/decisions/pr-<PR>.decisions.json`,跨会话、跨上下文压缩都能复用。**判断表既要展示给人看(终端表格),也要落盘给机器看(decisions/ JSON)**,两者都做。
|
|
119
|
+
|
|
120
|
+
⚠️ **落盘的原则**:每条建议都必须有一个 decision(fixed / wontfix / false_alarm / deferred)落盘,没有落盘的建议 = 没有收敛。
|
|
121
|
+
|
|
122
|
+
#### 2.1 逐条判断(核心)
|
|
123
|
+
|
|
84
124
|
解析两个模型的所有问题,**两模型报同一问题时合并为一条**(不要重复改两遍)。
|
|
85
125
|
|
|
86
126
|
⚠️ **严重度只是 bot 的标注,不是「该不该修」的依据**。真正决定改不改的是你读完代码后的判断。逐条按下面的决策框架:
|
|
@@ -95,10 +135,11 @@ if [ -n "$LATEST" ] && [ "$LATEST" = "$CUR" ]; then echo "YES"; else echo "NO";
|
|
|
95
135
|
**判断依据(每条必须读真实代码,禁止凭 review 文字盲改)**:
|
|
96
136
|
|
|
97
137
|
- 这是否是真实 bug / 真实风险?(读代码验证,不轻信 review 描述,也不轻信严重度标签)
|
|
98
|
-
-
|
|
138
|
+
- 是否违反本项目已固化规则(AGENTS.base.md / AGENTS.private.md 的 ⚠️ 铁律)?
|
|
99
139
|
- 改动风险多大?会不会超出该建议本身的范围?(为了修一条问题去大改共享逻辑,本身可能引入新风险)
|
|
100
140
|
- 是"必须修"还是"有则更好"?后者更接近口味,可拒绝。
|
|
101
141
|
- 两模型是否一致指出?一致 → 优先级更高、基本可信。
|
|
142
|
+
- **涉及部署脚本 / 配置 / 品牌 / 环境归属类改动时,先跑外部事实核查**(「审查方法④」命令化):`bash "$(git rev-parse --show-toplevel)/scripts/check-facts.sh" <核查列表>`,把 PR 里的域名 / 子域 / 邮箱 / URL / 环境归属提取成核查列表跑一遍,FAIL 项按「必须报」处理,WARN 项人工确认。
|
|
102
143
|
|
|
103
144
|
**每轮必须追问一句**:这条问题是不是上一轮已经判断过/处理过的?是 → 直接 Won't fix / 已修复,不重复改。**这一步是循环能收敛的关键。**
|
|
104
145
|
|
|
@@ -196,6 +237,8 @@ done
|
|
|
196
237
|
|
|
197
238
|
- ⚠️ **每条建议必须读真实代码再判断,禁止盲从 review 文字、也禁止盲从严重度标签**。review 是 LLM 生成的,可能误报,严重度只是它的标注。
|
|
198
239
|
- ⚠️ **循环收敛靠「值得修的都修完 + 不值得修的 Won't fix 切断」**,不是靠「严重度清零」。禁止用「无 Critical/Warning」当结束标准。
|
|
240
|
+
- ⚠️ **落点 = 落盘到 `.tmp-loop-review/decisions/`(`save-review-decision.sh`)+ 本轮 PR 回复(`gh pr comment` / inline reply),缺一不可。** 落盘让「会话中断后下轮还能识别已处理」,PR 回复让「bot 不再重提」。缺落盘:pickup 时历史丢失,等价于没收敛;缺回复:bot 下轮原样重提,循环无限。
|
|
241
|
+
- ⚠️ **每轮先查 `query-review-decision.sh`,命中「已处理过」的建议直接复用历史结论**,禁止重新判断一遍——那是循环回跳的根源。
|
|
199
242
|
- ⚠️ **每条 review 建议必须有落点**(修复 commit / Won't fix / 误报回复),禁止静默跳过——不回复 bot 下轮必重提,循环无限。
|
|
200
243
|
- ⚠️ **每轮先确认哪些是上一轮已处理过的**,一律不重复改,直接回复「已修复于 <hash>」。
|
|
201
244
|
- ⚠️ **禁止使用 `[skip ci]` / `[skip review]`**——循环依赖「每次 push 触发 review」,跳过就断了。
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# query-review-decision.sh —— 查询某条 review 建议是否已处理过(跨轮去重核心)。
|
|
3
|
+
#
|
|
4
|
+
# 用法:
|
|
5
|
+
# bash query-review-decision.sh <PR> <查询字段> [值]
|
|
6
|
+
#
|
|
7
|
+
# 查询字段:
|
|
8
|
+
# id 按建议唯一 ID 精确匹配(最可靠,下游写入时就要带上稳定 id)
|
|
9
|
+
# summary 按建议摘要子串匹配(模糊,用于没有稳定 id 的场景)
|
|
10
|
+
# count 输出该 PR 已记录的决策总数
|
|
11
|
+
#
|
|
12
|
+
# 返回值:
|
|
13
|
+
# 0 = 命中(已处理过),打印匹配的决策 JSON
|
|
14
|
+
# 1 = 未命中(新建议)
|
|
15
|
+
# 2 = 用法错误
|
|
16
|
+
#
|
|
17
|
+
# 配合 save-review-decision.sh 使用:
|
|
18
|
+
# 下轮启动时,对 bot 的每条建议先查一次;命中「已 fixed / wontfix / false_alarm」的
|
|
19
|
+
# 直接跳过,不再重复判断。这是「收敛靠文件记忆」的读取侧。
|
|
20
|
+
|
|
21
|
+
set -euo pipefail
|
|
22
|
+
|
|
23
|
+
DECISIONS_DIR="$(git rev-parse --show-toplevel)/.tmp-loop-review/decisions"
|
|
24
|
+
|
|
25
|
+
PR="${1:-}"
|
|
26
|
+
FIELD="${2:-}"
|
|
27
|
+
VALUE="${3:-}"
|
|
28
|
+
|
|
29
|
+
if [ -z "$PR" ] || [ -z "$FIELD" ]; then
|
|
30
|
+
echo "用法: bash query-review-decision.sh <PR> <id|summary|count> [值]" >&2
|
|
31
|
+
exit 2
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
DECISION_FILE="$DECISIONS_DIR/pr-$PR.decisions.json"
|
|
35
|
+
if [ ! -f "$DECISION_FILE" ]; then
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
case "$FIELD" in
|
|
40
|
+
count)
|
|
41
|
+
jq -r '.decisions | length' "$DECISION_FILE"
|
|
42
|
+
exit 0
|
|
43
|
+
;;
|
|
44
|
+
id)
|
|
45
|
+
HIT=$(jq --arg id "$VALUE" '.decisions[] | select(.id == $id)' "$DECISION_FILE" 2>/dev/null || true)
|
|
46
|
+
;;
|
|
47
|
+
summary)
|
|
48
|
+
HIT=$(jq --arg s "$VALUE" '.decisions[] | select(.summary | contains($s))' "$DECISION_FILE" 2>/dev/null || true)
|
|
49
|
+
;;
|
|
50
|
+
*)
|
|
51
|
+
echo "错误: 未知查询字段 $FIELD(支持 id / summary / count)" >&2
|
|
52
|
+
exit 2
|
|
53
|
+
;;
|
|
54
|
+
esac
|
|
55
|
+
|
|
56
|
+
if [ -n "$HIT" ]; then
|
|
57
|
+
echo "$HIT"
|
|
58
|
+
exit 0
|
|
59
|
+
fi
|
|
60
|
+
exit 1
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# save-review-decision.sh —— 把「循环 review 每一轮的判断表」落盘为机器可读状态。
|
|
3
|
+
#
|
|
4
|
+
# 为什么需要它:
|
|
5
|
+
# loop-review 的收敛靠「每条建议都有落点」(修复 commit / Won't fix / 误报)。
|
|
6
|
+
# 但判断表只存在于对话上下文里——会话一中断、上下文一压缩,上一轮的
|
|
7
|
+
# 「这条已 Won't fix / 这条已修复于 <hash>」记忆就丢了。bot 下轮原样重提,
|
|
8
|
+
# 于是又进入「改了又错、错了再改」的循环。
|
|
9
|
+
# 本脚本把判断表落盘到 .tmp-loop-review/decisions/,下次启动先读历史判断
|
|
10
|
+
# 去重,让「收敛」从「靠对话记忆」升级为「靠文件记忆」。
|
|
11
|
+
#
|
|
12
|
+
# 用法:
|
|
13
|
+
# bash save-review-decision.sh <决策JSON> # 写入一条决策
|
|
14
|
+
#
|
|
15
|
+
# 决策 JSON 格式(每条建议一个对象):
|
|
16
|
+
# {
|
|
17
|
+
# "pr": "26", // PR 号
|
|
18
|
+
# "id": "opus-1", // 建议的唯一 ID(用于去重)
|
|
19
|
+
# "severity": "critical", // bot 标注,仅参考
|
|
20
|
+
# "summary": "建议摘要",
|
|
21
|
+
# "decision": "fixed|wontfix|false_alarm|deferred",
|
|
22
|
+
# "reason": "判断理由",
|
|
23
|
+
# "commit": "修复 commit hash(decision=fixed 时)"
|
|
24
|
+
# }
|
|
25
|
+
#
|
|
26
|
+
# 查询(配对脚本 query-review-decision.sh):
|
|
27
|
+
# 下轮启动时用 review_id / summary 查询是否已处理过,命中即跳过。
|
|
28
|
+
|
|
29
|
+
set -euo pipefail
|
|
30
|
+
|
|
31
|
+
DECISIONS_DIR="$(git rev-parse --show-toplevel)/.tmp-loop-review/decisions"
|
|
32
|
+
mkdir -p "$DECISIONS_DIR"
|
|
33
|
+
|
|
34
|
+
INPUT="${1:-}"
|
|
35
|
+
if [ -z "$INPUT" ]; then
|
|
36
|
+
echo "用法: bash save-review-decision.sh '<决策JSON>'" >&2
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
if ! jq -e . <<<"$INPUT" >/dev/null 2>&1; then
|
|
41
|
+
echo "错误: 输入不是合法 JSON" >&2
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
PR="$(jq -r '.pr // empty' <<<"$INPUT")"
|
|
46
|
+
if [ -z "$PR" ]; then
|
|
47
|
+
echo "错误: JSON 缺少 pr 字段" >&2
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
DECISION_FILE="$DECISIONS_DIR/pr-$PR.decisions.json"
|
|
52
|
+
|
|
53
|
+
# 追加/合并:已存在该 id 则覆盖,否则追加
|
|
54
|
+
if [ -f "$DECISION_FILE" ]; then
|
|
55
|
+
MERGED="$(jq --argjson new "$INPUT" '
|
|
56
|
+
.decisions as $old |
|
|
57
|
+
([$old[] | select(.id != $new.id)] + [$new]) |
|
|
58
|
+
{ pr: $new.pr, decisions: . }
|
|
59
|
+
' "$DECISION_FILE")"
|
|
60
|
+
else
|
|
61
|
+
MERGED="$(jq -n --argjson new "$INPUT" '{ pr: $new.pr, decisions: [$new] }')"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
printf '%s\n' "$MERGED" > "$DECISION_FILE"
|
|
65
|
+
echo "✅ 已写入 $DECISION_FILE"
|