@ranger1/dx 0.1.28 → 0.1.30
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/@opencode/agents/__pycache__/pr_context.cpython-314.pyc +0 -0
- package/@opencode/agents/__pycache__/pr_precheck.cpython-314.pyc +0 -0
- package/@opencode/agents/__pycache__/pr_review_aggregate.cpython-314.pyc +0 -0
- package/@opencode/agents/claude-reviewer.md +8 -3
- package/@opencode/agents/codex-reviewer.md +9 -3
- package/@opencode/agents/gemini-reviewer.md +9 -3
- package/@opencode/agents/pr-context.md +6 -166
- package/@opencode/agents/pr-fix.md +12 -11
- package/@opencode/agents/pr-precheck.md +27 -84
- package/@opencode/agents/pr-review-aggregate.md +36 -72
- package/@opencode/agents/pr_context.py +211 -0
- package/@opencode/agents/pr_precheck.py +256 -0
- package/@opencode/agents/pr_review_aggregate.py +428 -0
- package/@opencode/commands/doctor.md +0 -1
- package/@opencode/commands/git-commit-and-pr.md +4 -0
- package/@opencode/commands/pr-review-loop.md +11 -6
- package/package.json +6 -4
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -14,21 +14,26 @@ tools:
|
|
|
14
14
|
|
|
15
15
|
- `PR #<number>`
|
|
16
16
|
- `round: <number>`
|
|
17
|
-
- `contextFile: <
|
|
17
|
+
- `contextFile: <filename>`
|
|
18
18
|
|
|
19
19
|
## 输出(强制)
|
|
20
20
|
|
|
21
21
|
只输出一行:
|
|
22
22
|
|
|
23
|
-
`reviewFile: <
|
|
23
|
+
`reviewFile: <filename>`
|
|
24
|
+
|
|
24
25
|
|
|
25
26
|
## 规则
|
|
26
27
|
|
|
27
28
|
- 默认已在 PR head 分支(可直接读工作区代码)
|
|
28
29
|
- 可用 `git`/`gh` 只读命令获取 diff/上下文
|
|
29
|
-
- 写入 reviewFile
|
|
30
|
+
- 写入 reviewFile:`review-CLD-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
|
|
30
31
|
- findings id 必须以 `CLD-` 开头
|
|
31
32
|
|
|
33
|
+
## Cache 约定(强制)
|
|
34
|
+
- 本流程所有中间文件都存放在 `~/.opencode/cache/`
|
|
35
|
+
- agent/命令之间仅传递文件名(basename),不传目录
|
|
36
|
+
|
|
32
37
|
## reviewFile 格式(强制)
|
|
33
38
|
|
|
34
39
|
```md
|
|
@@ -15,21 +15,27 @@ tools:
|
|
|
15
15
|
|
|
16
16
|
- `PR #<number>`
|
|
17
17
|
- `round: <number>`
|
|
18
|
-
- `contextFile: <
|
|
18
|
+
- `contextFile: <filename>`
|
|
19
19
|
|
|
20
20
|
## 输出(强制)
|
|
21
21
|
|
|
22
22
|
只输出一行:
|
|
23
23
|
|
|
24
|
-
`reviewFile: <
|
|
24
|
+
`reviewFile: <filename>`
|
|
25
|
+
|
|
25
26
|
|
|
26
27
|
## 规则
|
|
27
28
|
|
|
28
29
|
- 默认已在 PR head 分支(可直接读工作区代码)
|
|
29
30
|
- 可用 `git`/`gh` 只读命令获取 diff/上下文
|
|
30
|
-
- 写入 reviewFile
|
|
31
|
+
- 写入 reviewFile:`review-CDX-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
|
|
31
32
|
- findings id 必须以 `CDX-` 开头
|
|
32
33
|
|
|
34
|
+
## Cache 约定(强制)
|
|
35
|
+
- 本流程所有中间文件都存放在 `~/.opencode/cache/`
|
|
36
|
+
- agent/命令之间仅传递文件名(basename),不传目录
|
|
37
|
+
|
|
38
|
+
|
|
33
39
|
## reviewFile 格式(强制)
|
|
34
40
|
|
|
35
41
|
```md
|
|
@@ -14,21 +14,27 @@ tools:
|
|
|
14
14
|
|
|
15
15
|
- `PR #<number>`
|
|
16
16
|
- `round: <number>`
|
|
17
|
-
- `contextFile: <
|
|
17
|
+
- `contextFile: <filename>`
|
|
18
18
|
|
|
19
19
|
## 输出(强制)
|
|
20
20
|
|
|
21
21
|
只输出一行:
|
|
22
22
|
|
|
23
|
-
`reviewFile: <
|
|
23
|
+
`reviewFile: <filename>`
|
|
24
|
+
|
|
24
25
|
|
|
25
26
|
## 规则
|
|
26
27
|
|
|
27
28
|
- 默认已在 PR head 分支(可直接读工作区代码)
|
|
28
29
|
- 可用 `git`/`gh` 只读命令获取 diff/上下文
|
|
29
|
-
- 写入 reviewFile
|
|
30
|
+
- 写入 reviewFile:`review-GMN-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
|
|
30
31
|
- findings id 必须以 `GMN-` 开头
|
|
31
32
|
|
|
33
|
+
## Cache 约定(强制)
|
|
34
|
+
- 本流程所有中间文件都存放在 `~/.opencode/cache/`
|
|
35
|
+
- agent/命令之间仅传递文件名(basename),不传目录
|
|
36
|
+
|
|
37
|
+
|
|
32
38
|
## reviewFile 格式(强制)
|
|
33
39
|
|
|
34
40
|
```md
|
|
@@ -4,22 +4,12 @@ mode: subagent
|
|
|
4
4
|
model: openai/gpt-5.1-codex-mini
|
|
5
5
|
temperature: 0.1
|
|
6
6
|
tools:
|
|
7
|
-
write: false
|
|
8
|
-
edit: false
|
|
9
7
|
bash: true
|
|
10
8
|
---
|
|
11
9
|
|
|
12
10
|
# PR Context Builder
|
|
13
11
|
|
|
14
|
-
为 PR Review Loop
|
|
15
|
-
|
|
16
|
-
目标:
|
|
17
|
-
|
|
18
|
-
- 生成 PR context 文件(标题/描述/labels/分支/修改文件清单/评论摘要)
|
|
19
|
-
- 默认假设已通过 pr-precheck:gh 已认证、PR 可访问、当前分支已切到 PR head 分支
|
|
20
|
-
- 把信息拼成适合大模型阅读的 Markdown(不追求严格 schema)
|
|
21
|
-
- 落盘到按 `prNumber + round + runId` 命名的文件
|
|
22
|
-
- 返回“单一 JSON 对象”,只包含文件路径等元信息
|
|
12
|
+
为 PR Review Loop 构建上下文文件(Markdown)。确定性工作由脚本完成。
|
|
23
13
|
|
|
24
14
|
## 输入要求(强制)
|
|
25
15
|
|
|
@@ -28,168 +18,18 @@ tools:
|
|
|
28
18
|
- PR 编号(如:`PR #123` 或 `prNumber: 123`)
|
|
29
19
|
- round(如:`round: 1`;无则默认 1)
|
|
30
20
|
|
|
31
|
-
## 允许/禁止
|
|
32
|
-
|
|
33
|
-
- ✅ 允许使用 `gh` 只读获取 PR 信息与评论
|
|
34
|
-
- ✅ 允许使用 `git` 获取修改文件清单(推荐)
|
|
35
|
-
- ✅ 允许写入缓存文件到 `~/.opencode/cache/`
|
|
36
|
-
- ✅ 允许使用本地脚本(python)拼接/裁剪内容
|
|
37
|
-
- ⛔ 禁止修改业务代码(只允许写入 `~/.opencode/cache/`)
|
|
38
|
-
- ⛔ 禁止发布 GitHub 评论(不调用 `gh pr comment/review`)
|
|
39
|
-
- ⛔ 禁止 push/force push/rebase
|
|
40
|
-
|
|
41
21
|
## 输出(强制)
|
|
42
22
|
|
|
43
|
-
|
|
23
|
+
脚本会写入 `~/.opencode/cache/`,stdout 只输出单一 JSON(可 `JSON.parse()`)。
|
|
44
24
|
|
|
45
|
-
|
|
46
|
-
type PRContextBuildResult = {
|
|
47
|
-
agent: 'pr-context'
|
|
48
|
-
prNumber: number
|
|
49
|
-
round: number
|
|
50
|
-
runId: string
|
|
51
|
-
repo: { nameWithOwner: string }
|
|
52
|
-
headOid: string
|
|
53
|
-
existingMarkerCount?: number
|
|
54
|
-
contextFile: string
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## 生成规则(强制)
|
|
25
|
+
## Cache 约定(强制)
|
|
59
26
|
|
|
60
|
-
1. 假设已完成 pr-precheck(gh 已认证、PR 可访问、当前分支已切到 PR head 分支、工作区可用)
|
|
61
|
-
2. 获取 PR 元信息(title/body/labels/base/head oid/ref/url/isDraft)
|
|
62
|
-
3. 获取修改文件清单(优先用 git diff 与 baseRefName 对比;不包含 patch)
|
|
63
|
-
4. 获取评论:最多最近 10 条,正文截断 300 字符
|
|
64
|
-
5. 生成 runId:必须包含 prNumber 与 round;使用 `sha1(prNumber:round:headOid)` 的前 12 位(不使用时间)
|
|
65
|
-
6. 写入 `~/.opencode/cache/pr-context-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
|
|
66
|
-
7. 上下文文件内容必须包含:
|
|
67
27
|
|
|
68
|
-
- PR 基本信息(repo/pr/url/base/head/headOid/labels)
|
|
69
|
-
- Title / Body 摘要
|
|
70
|
-
- 变更文件清单(每文件 additions/deletions)
|
|
71
|
-
- 最近评论摘要
|
|
72
|
-
- 历史 marker 计数(用于提示是否已跑过 loop)
|
|
73
28
|
|
|
74
|
-
##
|
|
29
|
+
## 调用脚本(强制)
|
|
75
30
|
|
|
76
|
-
|
|
31
|
+
脚本位置:`~/.opencode/agents/pr_context.py`
|
|
77
32
|
|
|
78
33
|
```bash
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
mkdir -p "$HOME/.opencode/cache"
|
|
82
|
-
|
|
83
|
-
OWNER_REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
|
|
84
|
-
|
|
85
|
-
PR_JSON=$(gh pr view "$PR_NUMBER" --repo "$OWNER_REPO" \
|
|
86
|
-
--json number,url,title,body,isDraft,labels,baseRefName,headRefName,baseRefOid,headRefOid,comments)
|
|
87
|
-
|
|
88
|
-
BASE_REF=$(echo "$PR_JSON" | python3 -c 'import json,sys;print(json.load(sys.stdin).get("baseRefName") or "")')
|
|
89
|
-
test -n "$BASE_REF" || BASE_REF="main"
|
|
90
|
-
|
|
91
|
-
git fetch origin "$BASE_REF" > /dev/null 2>&1 || true
|
|
92
|
-
FILES_TXT=$(git diff --name-status "origin/$BASE_REF...HEAD" 2>/dev/null || git diff --name-status "$BASE_REF...HEAD" 2>/dev/null || true)
|
|
93
|
-
|
|
94
|
-
OWNER_REPO="$OWNER_REPO" PR_NUMBER="$PR_NUMBER" ROUND="$ROUND" PR_JSON="$PR_JSON" FILES_TXT="$FILES_TXT" \
|
|
95
|
-
python3 - <<'PY'
|
|
96
|
-
import json, os, hashlib
|
|
97
|
-
|
|
98
|
-
pr_number = int(os.environ['PR_NUMBER'])
|
|
99
|
-
round_num = int(os.environ.get('ROUND') or '1')
|
|
100
|
-
owner_repo = os.environ['OWNER_REPO']
|
|
101
|
-
pr = json.loads(os.environ['PR_JSON'])
|
|
102
|
-
files_txt = os.environ.get('FILES_TXT') or ''
|
|
103
|
-
|
|
104
|
-
head_oid = pr.get('headRefOid') or ''
|
|
105
|
-
run_seed = f"{pr_number}:{round_num}:{head_oid}".encode('utf-8')
|
|
106
|
-
run_id = hashlib.sha1(run_seed).hexdigest()[:12]
|
|
107
|
-
|
|
108
|
-
def clip(s, n):
|
|
109
|
-
if s is None:
|
|
110
|
-
return ''
|
|
111
|
-
s = str(s)
|
|
112
|
-
return s if len(s) <= n else (s[:n] + '...')
|
|
113
|
-
|
|
114
|
-
labels = [l.get('name') for l in (pr.get('labels') or []) if isinstance(l, dict) and l.get('name')]
|
|
115
|
-
|
|
116
|
-
dir_path = os.path.join(os.path.expanduser('~'), '.opencode', 'cache')
|
|
117
|
-
os.makedirs(dir_path, exist_ok=True)
|
|
118
|
-
context_file = os.path.join(dir_path, f"pr-context-pr{pr_number}-r{round_num}-{run_id}.md")
|
|
119
|
-
|
|
120
|
-
base_ref = pr.get('baseRefName') or ''
|
|
121
|
-
head_ref = pr.get('headRefName') or ''
|
|
122
|
-
url = pr.get('url') or ''
|
|
123
|
-
|
|
124
|
-
file_rows = []
|
|
125
|
-
for line in files_txt.splitlines():
|
|
126
|
-
parts = line.split('\t')
|
|
127
|
-
if not parts:
|
|
128
|
-
continue
|
|
129
|
-
status = parts[0].strip()
|
|
130
|
-
path = parts[-1].strip() if len(parts) >= 2 else ''
|
|
131
|
-
if not path:
|
|
132
|
-
continue
|
|
133
|
-
file_rows.append((status, path))
|
|
134
|
-
|
|
135
|
-
comments = pr.get('comments') or []
|
|
136
|
-
recent = comments[-10:] if isinstance(comments, list) else []
|
|
137
|
-
|
|
138
|
-
with open(context_file, 'w', encoding='utf-8', newline='\n') as fp:
|
|
139
|
-
fp.write('# PR Context\n\n')
|
|
140
|
-
fp.write(f"- Repo: {owner_repo}\n")
|
|
141
|
-
fp.write(f"- PR: #{pr_number} {url}\n")
|
|
142
|
-
fp.write(f"- Round: {round_num}\n")
|
|
143
|
-
fp.write(f"- RunId: {run_id}\n")
|
|
144
|
-
fp.write(f"- Base: {base_ref}\n")
|
|
145
|
-
fp.write(f"- Head: {head_ref}\n")
|
|
146
|
-
fp.write(f"- HeadOid: {head_oid}\n")
|
|
147
|
-
fp.write(f"- Draft: {pr.get('isDraft')}\n")
|
|
148
|
-
marker = '<!-- pr-review-loop-marker'
|
|
149
|
-
marker_count = 0
|
|
150
|
-
for c in recent:
|
|
151
|
-
if not isinstance(c, dict):
|
|
152
|
-
continue
|
|
153
|
-
body = c.get('body') or ''
|
|
154
|
-
if isinstance(body, str) and marker in body:
|
|
155
|
-
marker_count += 1
|
|
156
|
-
|
|
157
|
-
fp.write(f"- Labels: {', '.join(labels) if labels else '(none)'}\n")
|
|
158
|
-
fp.write(f"- ExistingLoopMarkers: {marker_count}\n\n")
|
|
159
|
-
|
|
160
|
-
fp.write('## Title\n\n')
|
|
161
|
-
fp.write(clip(pr.get('title') or '', 200) + '\n\n')
|
|
162
|
-
|
|
163
|
-
fp.write('## Body (excerpt)\n\n')
|
|
164
|
-
fp.write(clip(pr.get('body') or '', 2000) or '(empty)')
|
|
165
|
-
fp.write('\n\n')
|
|
166
|
-
|
|
167
|
-
fp.write(f"## Changed Files ({len(file_rows)})\n\n")
|
|
168
|
-
for (status, path) in file_rows:
|
|
169
|
-
fp.write(f"- {status} {path}\n")
|
|
170
|
-
fp.write('\n')
|
|
171
|
-
|
|
172
|
-
fp.write('## Recent Comments (excerpt)\n\n')
|
|
173
|
-
if recent:
|
|
174
|
-
for c in recent:
|
|
175
|
-
if not isinstance(c, dict):
|
|
176
|
-
continue
|
|
177
|
-
author = (c.get('author') or {}).get('login') if isinstance(c.get('author'), dict) else None
|
|
178
|
-
fp.write(f"- {author or 'unknown'}: {clip(c.get('body') or '', 300)}\n")
|
|
179
|
-
else:
|
|
180
|
-
fp.write('(none)\n')
|
|
181
|
-
|
|
182
|
-
result = {
|
|
183
|
-
'agent': 'pr-context',
|
|
184
|
-
'prNumber': pr_number,
|
|
185
|
-
'round': round_num,
|
|
186
|
-
'runId': run_id,
|
|
187
|
-
'repo': {'nameWithOwner': owner_repo},
|
|
188
|
-
'headOid': head_oid,
|
|
189
|
-
'existingMarkerCount': marker_count,
|
|
190
|
-
'contextFile': context_file,
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
print(json.dumps(result, ensure_ascii=True))
|
|
194
|
-
PY
|
|
34
|
+
python3 ~/.opencode/agents/pr_context.py --pr <PR_NUMBER> --round <ROUND>
|
|
195
35
|
```
|
|
@@ -19,16 +19,20 @@ tools:
|
|
|
19
19
|
| -------------- | ------------------------------------------------------------------------ |
|
|
20
20
|
| **角色** | 代码修复 Specialist(执行层) |
|
|
21
21
|
| **上下文隔离** | 仅处理问题列表;不重新获取评审意见(默认不调用 `gh` 拉取 PR 上下文) |
|
|
22
|
-
| **输入** | PR 编号 + `fixFile`(Markdown
|
|
23
|
-
| **输出** | fixReportFile(Markdown
|
|
22
|
+
| **输入** | PR 编号 + `fixFile`(Markdown 文件名,Structured Handoff) |
|
|
23
|
+
| **输出** | fixReportFile(Markdown 文件名) |
|
|
24
24
|
| **边界** | ✅ 可修改代码、提交并推送;⛔ 不发布 GitHub 评论(由 Orchestrator 负责) |
|
|
25
25
|
|
|
26
26
|
## 前置条件
|
|
27
27
|
|
|
28
|
+
### Cache 约定(强制)
|
|
29
|
+
- 本流程所有中间文件都存放在 `~/.opencode/cache/`
|
|
30
|
+
- agent/命令之间仅传递文件名(basename),不传目录
|
|
31
|
+
|
|
28
32
|
### 必需输入
|
|
29
33
|
|
|
30
34
|
- **PR 编号**:调用者必须在 prompt 中明确提供(如:`请修复 PR #123`)
|
|
31
|
-
- **fixFile**:调用者必须在 prompt
|
|
35
|
+
- **fixFile**:调用者必须在 prompt 中提供问题清单文件名(basename)(Structured Handoff)
|
|
32
36
|
|
|
33
37
|
### 失败快速退出
|
|
34
38
|
|
|
@@ -87,7 +91,6 @@ Round: 2
|
|
|
87
91
|
要求:只依赖 prompt 中的 `fixFile`;不要重新拉取/生成评审意见。
|
|
88
92
|
|
|
89
93
|
- 用 bash 读取 `fixFile`(例如 `cat "$fixFile"`)
|
|
90
|
-
- 从 `## IssuesToFix` 中解析条目,按 `priority` 排序并按 `id` 去重
|
|
91
94
|
- 解析失败则返回 `INVALID_FIX_FILE`
|
|
92
95
|
|
|
93
96
|
### 2. 逐项修复(No Scope Creep)
|
|
@@ -96,10 +99,6 @@ Round: 2
|
|
|
96
99
|
- 每个修复必须能明确对应到原问题的 `id`
|
|
97
100
|
- 无法修复时必须记录原因(例如:缺少上下文、超出本 PR 范围、需要产品决策、需要数据库迁移等)
|
|
98
101
|
|
|
99
|
-
执行前检查(强制):
|
|
100
|
-
|
|
101
|
-
- 当前分支禁止是 `main`/`master`(应已由 pr-context 切到 PR 分支)
|
|
102
|
-
|
|
103
102
|
### 3. 提交策略
|
|
104
103
|
|
|
105
104
|
- 强制:每个 findingId 单独一个提交(一个 findingId 对应一个 commit)
|
|
@@ -117,6 +116,7 @@ Round: 2
|
|
|
117
116
|
- 不确定的问题降级为拒绝修复,并写清 `reason`(不要“猜”)
|
|
118
117
|
- 修改尽量小:最小 diff、保持既有风格与约定
|
|
119
118
|
- 修改项目里的json/jsonc文件的时候,使用python脚本进行修改,禁止手动拼接字符串,防止格式错误
|
|
119
|
+
- 修复完成之后,调用 dx lint 和 dx build all 确保编译通过
|
|
120
120
|
|
|
121
121
|
## 重要约束(强制)
|
|
122
122
|
|
|
@@ -126,11 +126,12 @@ Round: 2
|
|
|
126
126
|
|
|
127
127
|
## 输出(强制)
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
写入:`fix-report-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
|
|
130
130
|
|
|
131
131
|
最终只输出一行:
|
|
132
132
|
|
|
133
|
-
`fixReportFile: <
|
|
133
|
+
`fixReportFile: <filename>`
|
|
134
|
+
|
|
134
135
|
|
|
135
136
|
## fixReportFile 内容格式(强制)
|
|
136
137
|
|
|
@@ -171,4 +172,4 @@ Rejected: <n>
|
|
|
171
172
|
## 输出有效性保证
|
|
172
173
|
|
|
173
174
|
- fixReportFile 必须成功写入
|
|
174
|
-
- stdout 只能输出一行 `fixReportFile: <
|
|
175
|
+
- stdout 只能输出一行 `fixReportFile: <filename>`
|
|
@@ -4,106 +4,49 @@ mode: subagent
|
|
|
4
4
|
model: openai/gpt-5.2-codex
|
|
5
5
|
temperature: 0.1
|
|
6
6
|
tools:
|
|
7
|
-
write: true
|
|
8
|
-
edit: true
|
|
9
7
|
bash: true
|
|
10
8
|
---
|
|
11
9
|
|
|
12
10
|
# PR Precheck
|
|
13
11
|
|
|
14
|
-
##
|
|
15
|
-
|
|
16
|
-
-
|
|
12
|
+
## Cache 约定(强制)
|
|
13
|
+
- 本流程所有中间文件都存放在 `~/.opencode/cache/`
|
|
14
|
+
- agent/命令之间仅传递文件名(basename),不传目录
|
|
17
15
|
|
|
18
|
-
## 要做的事(按顺序)
|
|
19
16
|
|
|
20
|
-
|
|
17
|
+
## 输入(prompt 必须包含)
|
|
21
18
|
|
|
22
|
-
-
|
|
23
|
-
- `gh auth status` 必须通过,否则输出 `{"error":"GH_NOT_AUTHENTICATED"}`
|
|
24
|
-
- PR 必须存在且可访问,否则输出 `{"error":"PR_NOT_FOUND_OR_NO_ACCESS"}`
|
|
19
|
+
- `PR #<number>`
|
|
25
20
|
|
|
26
|
-
|
|
21
|
+
## 一键脚本
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
- 如果当前分支不是 headRefName:执行 `gh pr checkout <PR_NUMBER>`
|
|
30
|
-
- 切换失败输出 `{"error":"PR_CHECKOUT_FAILED"}`
|
|
23
|
+
脚本位置:`~/.opencode/agents/pr_precheck.py`
|
|
31
24
|
|
|
32
|
-
|
|
25
|
+
```bash
|
|
26
|
+
python3 ~/.opencode/agents/pr_precheck.py <PR_NUMBER>
|
|
27
|
+
```
|
|
33
28
|
|
|
34
|
-
|
|
35
|
-
- base 分支名必须兼容 `main`/`master`:
|
|
36
|
-
- 优先使用 PR 返回的 `baseRefName`
|
|
37
|
-
- 若 `baseRefName` 为空:用 `gh repo view --json defaultBranchRef -q .defaultBranchRef.name` 获取仓库默认分支
|
|
38
|
-
- 若仍取不到:输出 `{"error":"PR_BASE_REF_NOT_FOUND"}`
|
|
39
|
-
- 拉取 base 分支(后续 merge/affected build 都依赖):
|
|
40
|
-
- `git fetch origin <baseRefName>`(若失败则按 `main/master` fallback 重试)
|
|
41
|
-
- 仍失败:输出 `{"error":"PR_BASE_REF_FETCH_FAILED"}`
|
|
42
|
-
- 若 `mergeable=CONFLICTING`(存在合并冲突):
|
|
43
|
-
- 尝试把 base 合入当前 PR 分支(不 rebase、不 force push):
|
|
44
|
-
- `git merge --no-ff --no-commit origin/<baseRefName>`
|
|
45
|
-
- 若 merge 产生冲突文件(`git diff --name-only --diff-filter=U` 非空):
|
|
46
|
-
- 先按文件类型做“低风险确定性策略”,再对剩余文件做“基于内容的智能合并”
|
|
47
|
-
- 低风险确定性策略(示例,按仓库实际补充):
|
|
48
|
-
- lockfiles(如 `pnpm-lock.yaml`/`package-lock.json`/`yarn.lock`):优先 `--theirs`(以 base 为准,减少依赖漂移)
|
|
49
|
-
- 其余生成物/构建产物:能识别则同上(优先 base),识别不了不要瞎选
|
|
50
|
-
- 对剩余冲突文件:
|
|
51
|
-
- 读取包含冲突标记(`<<<<<<<`/`=======`/`>>>>>>>`)的文件内容
|
|
52
|
-
- 基于代码语义进行合并:
|
|
53
|
-
- 保证语法正确(JS/TS/JSON/YAML 等)
|
|
54
|
-
- 变更尽量小
|
|
55
|
-
- 若两边都合理:优先保留 PR 的业务逻辑,同时把 base 的必要改动(接口/字段/类型)合进去
|
|
56
|
-
- 写回文件,确保冲突标记完全消除
|
|
57
|
-
- 合并完成后必须验证:
|
|
58
|
-
- `git diff --name-only --diff-filter=U` 为空
|
|
59
|
-
- 不再存在冲突标记(允许用 `git grep -n '<<<<<<< ' -- <files>` 复核)
|
|
60
|
-
- 若仍有未解决冲突:
|
|
61
|
-
- `git merge --abort`
|
|
62
|
-
- 输出 `{"error":"PR_MERGE_CONFLICTS_UNRESOLVED"}`
|
|
63
|
-
- 全部解决后:
|
|
64
|
-
- `git add -A` 后 `git commit`(建议 message:`chore(pr #<PR_NUMBER>): resolve merge conflicts`)
|
|
65
|
-
- `git push`(如无 upstream:`git push -u origin HEAD`)
|
|
66
|
-
- 任一步失败则输出 `{"error":"PR_CONFLICT_AUTO_RESOLVE_FAILED"}`
|
|
67
|
-
- 推送失败输出 `{"error":"PR_CONFLICT_PUSH_FAILED"}`
|
|
29
|
+
## 仅当出现 merge 冲突时怎么处理
|
|
68
30
|
|
|
69
|
-
|
|
31
|
+
当脚本输出 `{"error":"PR_MERGE_CONFLICTS_UNRESOLVED"}` 时:
|
|
70
32
|
|
|
71
|
-
|
|
72
|
-
|
|
33
|
+
```bash
|
|
34
|
+
# 1) 获取 base 分支名
|
|
35
|
+
gh pr view <PR_NUMBER> --json baseRefName --jq .baseRefName
|
|
73
36
|
|
|
74
|
-
|
|
37
|
+
# 2) 拉取 base 并合并到当前 PR 分支(不 rebase、不 force push)
|
|
38
|
+
git fetch origin <baseRefName>
|
|
39
|
+
git merge --no-ff --no-commit origin/<baseRefName>
|
|
75
40
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
-
|
|
79
|
-
- fixFile 格式(Markdown,最小字段集,供 `pr-fix` 解析):
|
|
41
|
+
# 3) 解决冲突后确认无未解决文件
|
|
42
|
+
git diff --name-only --diff-filter=U
|
|
43
|
+
git grep -n '<<<<<<< ' -- .
|
|
80
44
|
|
|
81
|
-
|
|
82
|
-
|
|
45
|
+
# 4) 提交并推送
|
|
46
|
+
git add -A
|
|
47
|
+
git commit -m "chore(pr #<PR_NUMBER>): resolve merge conflicts"
|
|
48
|
+
git push
|
|
83
49
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
category: lint|build|quality
|
|
87
|
-
file: <path>
|
|
88
|
-
line: <number|null>
|
|
89
|
-
title: <short>
|
|
90
|
-
description: <error message>
|
|
91
|
-
suggestion: <how to fix>
|
|
50
|
+
# 5) 重新运行预检脚本
|
|
51
|
+
python3 ~/.opencode/agents/pr_precheck.py <PR_NUMBER>
|
|
92
52
|
```
|
|
93
|
-
- 每条 issue 的 `id` 必须以 `PRE-` 开头(例如 `PRE-001`)
|
|
94
|
-
- 尽量从输出中提取 file/line;取不到则 `line: null`
|
|
95
|
-
|
|
96
|
-
## 输出(强制)
|
|
97
|
-
|
|
98
|
-
只输出一个 JSON 对象:
|
|
99
|
-
|
|
100
|
-
- 通过:`{"ok":true}`
|
|
101
|
-
- 需要修复:`{"ok":false,"fixFile":"~/.opencode/cache/precheck-fix-pr123-<RUN_ID>.md"}`
|
|
102
|
-
- 环境/权限/分支问题:`{"error":"..."}`
|
|
103
|
-
|
|
104
|
-
## 规则
|
|
105
|
-
|
|
106
|
-
- 不要输出任何时间字段
|
|
107
|
-
- 不要在 stdout 输出 lint/build 的长日志(写入 fixFile 的 description 即可)
|
|
108
|
-
- stdout 只能输出最终的单一 JSON 对象(其余命令输出请重定向到文件或丢弃)
|
|
109
|
-
- 允许使用 bash 生成 runId(例如 8-12 位随机/sha1 截断均可)
|
|
@@ -4,13 +4,15 @@ mode: subagent
|
|
|
4
4
|
model: openai/gpt-5.1-codex-mini
|
|
5
5
|
temperature: 0.1
|
|
6
6
|
tools:
|
|
7
|
-
write: true
|
|
8
|
-
edit: false
|
|
9
7
|
bash: true
|
|
10
8
|
---
|
|
11
9
|
|
|
12
10
|
# PR Review Aggregator
|
|
13
11
|
|
|
12
|
+
## Cache 约定(强制)
|
|
13
|
+
- 本流程所有中间文件都存放在 `~/.opencode/cache/`
|
|
14
|
+
- agent/命令之间仅传递文件名(basename),不传目录
|
|
15
|
+
|
|
14
16
|
## 输入(两种模式)
|
|
15
17
|
|
|
16
18
|
### 模式 A:评审聚合 + 生成 fixFile + 发布评审评论
|
|
@@ -18,15 +20,15 @@ tools:
|
|
|
18
20
|
- `PR #<number>`
|
|
19
21
|
- `round: <number>`
|
|
20
22
|
- `runId: <string>`
|
|
21
|
-
- `contextFile: <
|
|
22
|
-
- `reviewFile: <
|
|
23
|
+
- `contextFile: <filename>`
|
|
24
|
+
- `reviewFile: <filename>`(三行,分别对应 CDX/CLD/GMN)
|
|
23
25
|
|
|
24
26
|
### 模式 B:发布修复评论(基于 fixReportFile)
|
|
25
27
|
|
|
26
28
|
- `PR #<number>`
|
|
27
29
|
- `round: <number>`
|
|
28
30
|
- `runId: <string>`
|
|
29
|
-
- `fixReportFile: <
|
|
31
|
+
- `fixReportFile: <filename>`
|
|
30
32
|
|
|
31
33
|
示例:
|
|
32
34
|
|
|
@@ -34,86 +36,48 @@ tools:
|
|
|
34
36
|
PR #123
|
|
35
37
|
round: 1
|
|
36
38
|
runId: abcdef123456
|
|
37
|
-
contextFile:
|
|
38
|
-
reviewFile:
|
|
39
|
-
reviewFile:
|
|
40
|
-
reviewFile:
|
|
39
|
+
contextFile: pr-context-pr123-r1-abcdef123456.md
|
|
40
|
+
reviewFile: review-CDX-pr123-r1-abcdef123456.md
|
|
41
|
+
reviewFile: review-CLD-pr123-r1-abcdef123456.md
|
|
42
|
+
reviewFile: review-GMN-pr123-r1-abcdef123456.md
|
|
41
43
|
```
|
|
42
44
|
|
|
43
|
-
##
|
|
44
|
-
|
|
45
|
-
模式 A:
|
|
45
|
+
## 执行方式(强制)
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
2. 计算 needsFix(P0/P1/P2 任意 > 0)
|
|
49
|
-
3. 合并重复的问题为一个
|
|
50
|
-
4. 发布评审评论到 GitHub(gh pr comment),必须带 marker,评论正文必须内联包含:
|
|
51
|
-
- Summary(P0/P1/P2/P3 统计)
|
|
52
|
-
- P0/P1/P2 问题列表(至少 id/title/file:line/suggestion)
|
|
53
|
-
- 三个 reviewer 的 reviewFile 原文(建议放到 <details>)
|
|
54
|
-
5. 若 needsFix:生成 `fixFile`(Markdown)并返回;否则发布“完成”评论并返回 stop
|
|
47
|
+
所有确定性工作(解析/聚合/发评论/生成 fixFile/输出 JSON)都由 `~/.opencode/agents/pr_review_aggregate.py` 完成。
|
|
55
48
|
|
|
56
|
-
|
|
49
|
+
你只做一件事:在模式 A 里用大模型判断哪些 finding 是重复的,并把重复分组作为参数传给脚本(不落盘)。
|
|
57
50
|
|
|
58
|
-
|
|
59
|
-
2. 发布修复评论到 GitHub(gh pr comment),必须带 marker,评论正文必须内联 fixReportFile 内容
|
|
60
|
-
3. 输出 `{"ok":true}`
|
|
51
|
+
## 重复分组(给大模型输出)
|
|
61
52
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
模式 A:只输出一个 JSON 对象(很小):
|
|
53
|
+
大模型只输出一行 JSON(不要代码块、不要解释文字、不要换行):
|
|
65
54
|
|
|
66
55
|
```json
|
|
67
|
-
{
|
|
68
|
-
"stop": false,
|
|
69
|
-
"fixFile": "~/.opencode/cache/fix-pr123-r1-abcdef123456.md"
|
|
70
|
-
}
|
|
56
|
+
{"duplicateGroups":[["CDX-001","CLD-003"],["GMN-002","CLD-005","CDX-004"]]}
|
|
71
57
|
```
|
|
72
58
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
- `stop`: boolean
|
|
76
|
-
- `fixFile`: string(仅 stop=false 时必须提供)
|
|
59
|
+
## 调用脚本(强制)
|
|
77
60
|
|
|
78
|
-
模式
|
|
61
|
+
模式 A(带 reviewFile + 重复分组):
|
|
79
62
|
|
|
80
|
-
```
|
|
81
|
-
|
|
63
|
+
```bash
|
|
64
|
+
python3 ~/.opencode/agents/pr_review_aggregate.py \
|
|
65
|
+
--pr <PR_NUMBER> \
|
|
66
|
+
--round <ROUND> \
|
|
67
|
+
--run-id <RUN_ID> \
|
|
68
|
+
--context-file <CONTEXT_FILE> \
|
|
69
|
+
--review-file <REVIEW_FILE_1> \
|
|
70
|
+
--review-file <REVIEW_FILE_2> \
|
|
71
|
+
--review-file <REVIEW_FILE_3> \
|
|
72
|
+
--duplicate-groups-b64 <BASE64_JSON>
|
|
82
73
|
```
|
|
83
74
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
- 不要输出 ReviewResult JSON
|
|
87
|
-
- 不要校验/要求 reviewer 的 JSON
|
|
88
|
-
- 不要生成/输出任何时间字段
|
|
89
|
-
- `fixFile` 只包含 P0/P1/P2
|
|
90
|
-
- `id` 必须使用 reviewer 给出的 findingId(例如 `CDX-001`),不要再改前缀
|
|
91
|
-
|
|
92
|
-
## 评论要求
|
|
93
|
-
|
|
94
|
-
- 每条评论必须包含:`<!-- pr-review-loop-marker -->`
|
|
95
|
-
- body 必须是最终字符串(用 `--body-file` 读取文件),不要依赖 heredoc 变量展开
|
|
96
|
-
- 禁止在评论里出现本地缓存文件路径(例如 `~/.opencode/cache/...`)
|
|
97
|
-
|
|
98
|
-
## fixFile 输出路径与格式
|
|
99
|
-
|
|
100
|
-
- 路径:`~/.opencode/cache/fix-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
|
|
101
|
-
- 格式:
|
|
102
|
-
|
|
103
|
-
```md
|
|
104
|
-
# Fix File
|
|
105
|
-
|
|
106
|
-
PR: <PR_NUMBER>
|
|
107
|
-
Round: <ROUND>
|
|
108
|
-
|
|
109
|
-
## IssuesToFix
|
|
75
|
+
模式 B(带 fixReportFile):
|
|
110
76
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
description: <text>
|
|
118
|
-
suggestion: <text>
|
|
77
|
+
```bash
|
|
78
|
+
python3 ~/.opencode/agents/pr_review_aggregate.py \
|
|
79
|
+
--pr <PR_NUMBER> \
|
|
80
|
+
--round <ROUND> \
|
|
81
|
+
--run-id <RUN_ID> \
|
|
82
|
+
--fix-report-file <FIX_REPORT_FILE>
|
|
119
83
|
```
|