@ranger1/dx 0.1.17 → 0.1.18

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.
@@ -48,8 +48,9 @@ echo "opencode-openai-codex-auth:" && (grep -q 'opencode-openai-codex-auth' ~/.c
48
48
  # 批次 4: oh-my-opencode.json 配置检测
49
49
  echo "=== OMO_CONFIG ===";
50
50
  echo "sisyphus_agent:" && (grep -q '"sisyphus_agent"' ~/.config/opencode/oh-my-opencode.json 2>/dev/null && echo "CONFIGURED" || echo "NOT_CONFIGURED");
51
- echo "categories.quick:" && (grep -q '"quick"' ~/.config/opencode/oh-my-opencode.json 2>/dev/null && echo "CONFIGURED" || echo "NOT_CONFIGURED");
52
- echo "categories.middle:" && (grep -q '"middle"' ~/.config/opencode/oh-my-opencode.json 2>/dev/null && echo "CONFIGURED" || echo "NOT_CONFIGURED");
51
+ echo "agents.sisyphus.variant:" && (node -e "const fs=require('node:fs');const os=require('node:os');const p=os.homedir()+'/.config/opencode/oh-my-opencode.json';try{const j=JSON.parse(fs.readFileSync(p,'utf8'));process.exit(j?.agents?.sisyphus?.variant==='none'?0:1)}catch(e){process.exit(1)}" 2>/dev/null && echo "CONFIGURED" || echo "NOT_CONFIGURED");
52
+ echo "agents.quick:" && (grep -Eq '"agents"[[:space:]]*:' ~/.config/opencode/opencode.json 2>/dev/null && grep -Eq '"quick"[[:space:]]*:' ~/.config/opencode/opencode.json 2>/dev/null && echo "CONFIGURED" || echo "NOT_CONFIGURED");
53
+ echo "agents.middle:" && (grep -Eq '"agents"[[:space:]]*:' ~/.config/opencode/opencode.json 2>/dev/null && grep -Eq '"middle"[[:space:]]*:' ~/.config/opencode/opencode.json 2>/dev/null && echo "CONFIGURED" || echo "NOT_CONFIGURED");
53
54
  ```
54
55
 
55
56
  ---
@@ -69,8 +70,9 @@ oh-my-opencode | <状态> | -
69
70
  opencode-openai-codex-auth | <状态> | -
70
71
  agent-browser | <状态> | <版本>
71
72
  sisyphus_agent 配置 | <状态> | -
72
- categories.quick 配置 | <状态> | -
73
- categories.middle 配置 | <状态> | -
73
+ agents.sisyphus.variant 配置 | <状态> | -
74
+ agents.quick 配置 | <状态> | -
75
+ agents.middle 配置 | <状态> | -
74
76
  ```
75
77
 
76
78
  ---
@@ -185,13 +187,37 @@ cat ~/.config/opencode/oh-my-opencode.json
185
187
 
186
188
  注意:这是根节点配置,应添加到 JSON 的第一层级。
187
189
 
188
- #### 3.7.2 categories.quick categories.middle 缺失
190
+ #### 3.7.2 agents.sisyphus.variant 不是 none
189
191
 
190
- 如果 `categories` 节点缺少 `quick` `middle`,使用 Edit 工具添加:
192
+ 如果 `~/.config/opencode/oh-my-opencode.json` `agents.sisyphus.variant` 缺失或不是 `none`,使用 Edit 工具修复为:
191
193
 
192
194
  ```json
193
195
  {
194
- "categories": {
196
+ "agents": {
197
+ "sisyphus": {
198
+ "variant": "none"
199
+ }
200
+ }
201
+ }
202
+ ```
203
+
204
+ 3. 验证配置:
205
+
206
+ ```bash
207
+ # 检查 sisyphus_agent
208
+ grep -q '"sisyphus_agent"' ~/.config/opencode/oh-my-opencode.json && echo "✅ sisyphus_agent 已配置" || echo "❌ sisyphus_agent 缺失"
209
+
210
+ # 检查 agents.sisyphus.variant
211
+ node -e "const fs=require('node:fs');const os=require('node:os');const p=os.homedir()+'/.config/opencode/oh-my-opencode.json';const j=JSON.parse(fs.readFileSync(p,'utf8'));console.log(j?.agents?.sisyphus?.variant||'MISSING');process.exit(j?.agents?.sisyphus?.variant==='none'?0:1)" && echo "✅ agents.sisyphus.variant=none" || echo "❌ agents.sisyphus.variant 不是 none"
212
+ ```
213
+
214
+ ### 3.8 opencode.json agents 配置缺失
215
+
216
+ 如果 `~/.config/opencode/opencode.json` 缺少 `agents.quick` 或 `agents.middle`,使用 Edit 工具添加:
217
+
218
+ ```json
219
+ {
220
+ "agents": {
195
221
  "quick": {
196
222
  "model": "github-copilot/claude-haiku-4.5"
197
223
  },
@@ -202,17 +228,14 @@ cat ~/.config/opencode/oh-my-opencode.json
202
228
  }
203
229
  ```
204
230
 
205
- 3. 验证配置:
231
+ 验证配置:
206
232
 
207
233
  ```bash
208
- # 检查 sisyphus_agent
209
- grep -q '"sisyphus_agent"' ~/.config/opencode/oh-my-opencode.json && echo "✅ sisyphus_agent 已配置" || echo "❌ sisyphus_agent 缺失"
210
-
211
- # 检查 categories.quick
212
- grep -q '"quick"' ~/.config/opencode/oh-my-opencode.json && echo "✅ quick 已配置" || echo "❌ quick 缺失"
234
+ # 检查 agents.quick
235
+ grep -Eq '"agents"[[:space:]]*:' ~/.config/opencode/opencode.json && grep -Eq '"quick"[[:space:]]*:' ~/.config/opencode/opencode.json && echo "✅ agents.quick 已配置" || echo "❌ agents.quick 缺失"
213
236
 
214
- # 检查 categories.middle
215
- grep -q '"middle"' ~/.config/opencode/oh-my-opencode.json && echo "✅ middle 已配置" || echo "❌ middle 缺失"
237
+ # 检查 agents.middle
238
+ grep -Eq '"agents"[[:space:]]*:' ~/.config/opencode/opencode.json && grep -Eq '"middle"[[:space:]]*:' ~/.config/opencode/opencode.json && echo "✅ agents.middle 已配置" || echo "❌ agents.middle 缺失"
216
239
  ```
217
240
 
218
241
  ---
@@ -59,8 +59,7 @@ async function copyMarkdownTree({ srcDir, dstDir }) {
59
59
  }
60
60
 
61
61
  function resolveTemplateRoot(packageRoot) {
62
- const candidates = [join(packageRoot, '@opencode'), join(packageRoot, 'opencode')]
63
- return candidates
62
+ return [join(packageRoot, '@opencode')]
64
63
  }
65
64
 
66
65
  export async function runOpenCodeInitial(options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ranger1/dx",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -19,7 +19,6 @@
19
19
  "bin/",
20
20
  "lib/",
21
21
  "@opencode/",
22
- "opencode/",
23
22
  "LICENSE",
24
23
  "README.md",
25
24
  "package.json"
@@ -1,57 +0,0 @@
1
- ---
2
- description: review (Claude)
3
- mode: subagent
4
- model: github-copilot/claude-sonnet-4.5
5
- tools:
6
- write: true
7
- edit: false
8
- bash: true
9
- ---
10
-
11
- # PR Reviewer (Claude)
12
-
13
- ## 输入(prompt 必须包含)
14
-
15
- - `PR #<number>`
16
- - `round: <number>`
17
- - `contextFile: <path>`
18
-
19
- ## 输出(强制)
20
-
21
- 只输出一行:
22
-
23
- `reviewFile: <path>`
24
-
25
- ## 规则
26
-
27
- - 默认已在 PR head 分支(可直接读工作区代码)
28
- - 可用 `git`/`gh` 只读命令获取 diff/上下文
29
- - 写入 reviewFile:`~/.opencode/cache/review-CLD-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
30
- - findings id 必须以 `CLD-` 开头
31
-
32
- ## reviewFile 格式(强制)
33
-
34
- ```md
35
- # Review (CLD)
36
-
37
- PR: <PR_NUMBER>
38
- Round: <ROUND>
39
-
40
- ## Summary
41
-
42
- P0: <n>
43
- P1: <n>
44
- P2: <n>
45
- P3: <n>
46
-
47
- ## Findings
48
-
49
- - id: CLD-001
50
- priority: P1
51
- category: quality|performance|security|architecture
52
- file: <path>
53
- line: <number|null>
54
- title: <short>
55
- description: <text>
56
- suggestion: <text>
57
- ```
@@ -1,58 +0,0 @@
1
- ---
2
- description: review (Codex)
3
- mode: subagent
4
- model: openai/gpt-5.2-codex
5
- temperature: 0.1
6
- tools:
7
- write: true
8
- edit: false
9
- bash: true
10
- ---
11
-
12
- # PR Reviewer (Codex)
13
-
14
- ## 输入(prompt 必须包含)
15
-
16
- - `PR #<number>`
17
- - `round: <number>`
18
- - `contextFile: <path>`
19
-
20
- ## 输出(强制)
21
-
22
- 只输出一行:
23
-
24
- `reviewFile: <path>`
25
-
26
- ## 规则
27
-
28
- - 默认已在 PR head 分支(可直接读工作区代码)
29
- - 可用 `git`/`gh` 只读命令获取 diff/上下文
30
- - 写入 reviewFile:`~/.opencode/cache/review-CDX-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
31
- - findings id 必须以 `CDX-` 开头
32
-
33
- ## reviewFile 格式(强制)
34
-
35
- ```md
36
- # Review (CDX)
37
-
38
- PR: <PR_NUMBER>
39
- Round: <ROUND>
40
-
41
- ## Summary
42
-
43
- P0: <n>
44
- P1: <n>
45
- P2: <n>
46
- P3: <n>
47
-
48
- ## Findings
49
-
50
- - id: CDX-001
51
- priority: P1
52
- category: quality|performance|security|architecture
53
- file: <path>
54
- line: <number|null>
55
- title: <short>
56
- description: <text>
57
- suggestion: <text>
58
- ```
@@ -1,57 +0,0 @@
1
- ---
2
- description: review (Gemini)
3
- mode: subagent
4
- model: github-copilot/gemini-3-pro-preview
5
- tools:
6
- write: true
7
- edit: false
8
- bash: true
9
- ---
10
-
11
- # PR Reviewer (Gemini)
12
-
13
- ## 输入(prompt 必须包含)
14
-
15
- - `PR #<number>`
16
- - `round: <number>`
17
- - `contextFile: <path>`
18
-
19
- ## 输出(强制)
20
-
21
- 只输出一行:
22
-
23
- `reviewFile: <path>`
24
-
25
- ## 规则
26
-
27
- - 默认已在 PR head 分支(可直接读工作区代码)
28
- - 可用 `git`/`gh` 只读命令获取 diff/上下文
29
- - 写入 reviewFile:`~/.opencode/cache/review-GMN-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
30
- - findings id 必须以 `GMN-` 开头
31
-
32
- ## reviewFile 格式(强制)
33
-
34
- ```md
35
- # Review (GMN)
36
-
37
- PR: <PR_NUMBER>
38
- Round: <ROUND>
39
-
40
- ## Summary
41
-
42
- P0: <n>
43
- P1: <n>
44
- P2: <n>
45
- P3: <n>
46
-
47
- ## Findings
48
-
49
- - id: GMN-001
50
- priority: P1
51
- category: quality|performance|security|architecture
52
- file: <path>
53
- line: <number|null>
54
- title: <short>
55
- description: <text>
56
- suggestion: <text>
57
- ```
@@ -1,195 +0,0 @@
1
- ---
2
- description: build PR context file
3
- mode: subagent
4
- model: openai/gpt-5.1-codex-mini
5
- temperature: 0.1
6
- tools:
7
- write: false
8
- edit: false
9
- bash: true
10
- ---
11
-
12
- # PR Context Builder
13
-
14
- 为 PR Review Loop 构建“松散上下文文件”(Markdown),让 reviewer 读取文件而不是在 prompt 里解析大段精确 JSON。
15
-
16
- 目标:
17
-
18
- - 生成 PR context 文件(标题/描述/labels/分支/修改文件清单/评论摘要)
19
- - 默认假设已通过 pr-precheck:gh 已认证、PR 可访问、当前分支已切到 PR head 分支
20
- - 把信息拼成适合大模型阅读的 Markdown(不追求严格 schema)
21
- - 落盘到按 `prNumber + round + runId` 命名的文件
22
- - 返回“单一 JSON 对象”,只包含文件路径等元信息
23
-
24
- ## 输入要求(强制)
25
-
26
- 调用者必须在 prompt 中明确提供:
27
-
28
- - PR 编号(如:`PR #123` 或 `prNumber: 123`)
29
- - round(如:`round: 1`;无则默认 1)
30
-
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
- ## 输出(强制)
42
-
43
- 你必须输出“单一 JSON 对象”,且能被 `JSON.parse()` 解析。
44
-
45
- ```ts
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
- ## 生成规则(强制)
59
-
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
-
68
- - PR 基本信息(repo/pr/url/base/head/headOid/labels)
69
- - Title / Body 摘要
70
- - 变更文件清单(每文件 additions/deletions)
71
- - 最近评论摘要
72
- - 历史 marker 计数(用于提示是否已跑过 loop)
73
-
74
- ## 实现建议(bash + python)
75
-
76
- 你可以按如下思路实现,并确保最终只输出 JSON:
77
-
78
- ```bash
79
- set -euo pipefail
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
195
- ```
@@ -1,173 +0,0 @@
1
- ---
2
- description: PR fix review
3
- mode: subagent
4
- model: openai/gpt-5.2-codex
5
- temperature: 0.1
6
- tools:
7
- write: true
8
- edit: true
9
- bash: true
10
- ---
11
-
12
- # Fix Specialist
13
-
14
- 执行 PR 修复(基于 fixFile),并生成可直接发布到 GitHub 评论的修复报告(Markdown 文件)。
15
-
16
- ## Agent 角色定义
17
-
18
- | 属性 | 描述 |
19
- | -------------- | ------------------------------------------------------------------------ |
20
- | **角色** | 代码修复 Specialist(执行层) |
21
- | **上下文隔离** | 仅处理问题列表;不重新获取评审意见(默认不调用 `gh` 拉取 PR 上下文) |
22
- | **输入** | PR 编号 + `fixFile`(Markdown 文件路径,Structured Handoff) |
23
- | **输出** | fixReportFile(Markdown 文件路径) |
24
- | **边界** | ✅ 可修改代码、提交并推送;⛔ 不发布 GitHub 评论(由 Orchestrator 负责) |
25
-
26
- ## 前置条件
27
-
28
- ### 必需输入
29
-
30
- - **PR 编号**:调用者必须在 prompt 中明确提供(如:`请修复 PR #123`)
31
- - **fixFile**:调用者必须在 prompt 中提供问题清单文件路径(Markdown,Structured Handoff)
32
-
33
- ### 失败快速退出
34
-
35
- 如未满足以下任一条件,立即返回错误 JSON 并退出:
36
-
37
- - ❌ prompt 未包含 PR 编号 → `{"error":"MISSING_PR_NUMBER"}`
38
- - ❌ prompt 未包含 fixFile → `{"error":"MISSING_FIX_FILE"}`
39
- - ❌ fixFile 不存在/不可读 → `{"error":"FIX_FILE_NOT_READABLE"}`
40
- - ❌ fixFile 无法解析出 issuesToFix → `{"error":"INVALID_FIX_FILE"}`
41
-
42
- ## 输入格式(Structured Handoff:fixFile,Markdown)
43
-
44
- 说明:fixFile 由编排器根据 reviewer 的 findings 聚合生成;不要求严格 JSON,但必须包含可解析的字段。
45
-
46
- 推荐最小格式(稳定、易解析):
47
-
48
- ```md
49
- # Fix File
50
-
51
- PR: 123
52
- Round: 2
53
-
54
- ## IssuesToFix
55
-
56
- - id: CDX-001
57
- priority: P1
58
- category: quality
59
- file: apps/backend/src/foo.ts
60
- line: 42
61
- title: 未处理的异常
62
- description: JSON.parse 可能抛出异常但未被捕获
63
- suggestion: 添加 try/catch 并返回一致错误码
64
-
65
- ## OptionalIssues
66
-
67
- - id: GMN-004
68
- priority: P3
69
- category: suggestion
70
- file: apps/front/src/bar.tsx
71
- line: null
72
- title: 可读性优化
73
- description: ...
74
- suggestion: ...
75
- ```
76
-
77
- 解析规则(强制):
78
-
79
- - 仅处理 `## IssuesToFix` 段落里的条目;`## OptionalIssues` 可忽略或按需处理
80
- - 每条必须至少包含:`id`、`priority`、`file`、`title`、`suggestion`
81
- - `line` 允许为 `null`
82
-
83
- ## 工作流程
84
-
85
- ### 1. 读取 fixFile 并标准化
86
-
87
- 要求:只依赖 prompt 中的 `fixFile`;不要重新拉取/生成评审意见。
88
-
89
- - 用 bash 读取 `fixFile`(例如 `cat "$fixFile"`)
90
- - 从 `## IssuesToFix` 中解析条目,按 `priority` 排序并按 `id` 去重
91
- - 解析失败则返回 `INVALID_FIX_FILE`
92
-
93
- ### 2. 逐项修复(No Scope Creep)
94
-
95
- - 仅修复 fixFile 中列出的问题:`IssuesToFix`(必要)与 `OptionalIssues`(可选)
96
- - 每个修复必须能明确对应到原问题的 `id`
97
- - 无法修复时必须记录原因(例如:缺少上下文、超出本 PR 范围、需要产品决策、需要数据库迁移等)
98
-
99
- 执行前检查(强制):
100
-
101
- - 当前分支禁止是 `main`/`master`(应已由 pr-context 切到 PR 分支)
102
-
103
- ### 3. 提交策略
104
-
105
- - 强制:每个 findingId 单独一个提交(一个 findingId 对应一个 commit)
106
- - 每个提交后立即推送到远端(禁止 force push)
107
- - 约定:如无 upstream,首次用 `git push -u origin HEAD`,后续用 `git push`
108
- - 所有问题处理完毕后,再执行一次 `git push` 作为兜底
109
-
110
- 提交信息建议(强制包含 findingId):
111
-
112
- - `fix(pr #<PR_NUMBER>): <FINDING_ID> <title>`
113
-
114
- ## 修复原则(强制)
115
-
116
- - 只修复 `issuesToFix`/`optionalIssues`;禁止顺手重构/格式化/改无关代码
117
- - 不确定的问题降级为拒绝修复,并写清 `reason`(不要“猜”)
118
- - 修改尽量小:最小 diff、保持既有风格与约定
119
-
120
- ## 重要约束(强制)
121
-
122
- - ⛔ 不要发布评论到 GitHub(不调用 `gh pr comment/review`)
123
- - ✅ 必须 push(禁止 force push;禁止 rebase)
124
- - ✅ 必须生成 fixReportFile(Markdown),内容可直接发到 GitHub 评论
125
-
126
- ## 输出(强制)
127
-
128
- 写入:`~/.opencode/cache/fix-report-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
129
-
130
- 最终只输出一行:
131
-
132
- `fixReportFile: <path>`
133
-
134
- ## fixReportFile 内容格式(强制)
135
-
136
- fixReportFile 内容必须是可直接粘贴到 GitHub 评论的 Markdown,且不得包含本地缓存文件路径。
137
-
138
- ```md
139
- # Fix Report
140
-
141
- PR: <PR_NUMBER>
142
- Round: <ROUND>
143
-
144
- ## Summary
145
-
146
- Fixed: <n>
147
- Rejected: <n>
148
-
149
- ## Fixed
150
-
151
- - id: <FINDING_ID>
152
- commit: <SHA>
153
- note: <what changed>
154
-
155
- ## Rejected
156
-
157
- - id: <FINDING_ID>
158
- reason: <why>
159
- ```
160
-
161
- ## Multi-Agent 约束(Contract)
162
-
163
- | 约束 | 说明 |
164
- | -------------------- | --------------------------------------------------------------------------- |
165
- | **Structured Input** | 仅处理 `fixFile` 中的问题;不重新获取评审意见(默认不调用 `gh` 拉取上下文) |
166
- | **Output** | 必须生成 fixReportFile(Markdown) |
167
- | **ID Correlation** | 每条提交必须能关联到某个 findingId |
168
- | **No Scope Creep** | ⛔ 不修复 fixFile 之外的问题,不引入无关变更 |
169
-
170
- ## 输出有效性保证
171
-
172
- - fixReportFile 必须成功写入
173
- - stdout 只能输出一行 `fixReportFile: <path>`