@ranger1/dx 0.1.29 → 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.
@@ -14,21 +14,26 @@ tools:
14
14
 
15
15
  - `PR #<number>`
16
16
  - `round: <number>`
17
- - `contextFile: <path>`
17
+ - `contextFile: <filename>`
18
18
 
19
19
  ## 输出(强制)
20
20
 
21
21
  只输出一行:
22
22
 
23
- `reviewFile: <path>`
23
+ `reviewFile: <filename>`
24
+
24
25
 
25
26
  ## 规则
26
27
 
27
28
  - 默认已在 PR head 分支(可直接读工作区代码)
28
29
  - 可用 `git`/`gh` 只读命令获取 diff/上下文
29
- - 写入 reviewFile:`~/.opencode/cache/review-CLD-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
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: <path>`
18
+ - `contextFile: <filename>`
19
19
 
20
20
  ## 输出(强制)
21
21
 
22
22
  只输出一行:
23
23
 
24
- `reviewFile: <path>`
24
+ `reviewFile: <filename>`
25
+
25
26
 
26
27
  ## 规则
27
28
 
28
29
  - 默认已在 PR head 分支(可直接读工作区代码)
29
30
  - 可用 `git`/`gh` 只读命令获取 diff/上下文
30
- - 写入 reviewFile:`~/.opencode/cache/review-CDX-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
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: <path>`
17
+ - `contextFile: <filename>`
18
18
 
19
19
  ## 输出(强制)
20
20
 
21
21
  只输出一行:
22
22
 
23
- `reviewFile: <path>`
23
+ `reviewFile: <filename>`
24
+
24
25
 
25
26
  ## 规则
26
27
 
27
28
  - 默认已在 PR head 分支(可直接读工作区代码)
28
29
  - 可用 `git`/`gh` 只读命令获取 diff/上下文
29
- - 写入 reviewFile:`~/.opencode/cache/review-GMN-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
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 构建“松散上下文文件”(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 对象”,只包含文件路径等元信息
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
- 你必须输出“单一 JSON 对象”,且能被 `JSON.parse()` 解析。
23
+ 脚本会写入 `~/.opencode/cache/`,stdout 只输出单一 JSON(可 `JSON.parse()`)。
44
24
 
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
- ## 生成规则(强制)
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
- ## 实现建议(bash + python)
29
+ ## 调用脚本(强制)
75
30
 
76
- 你可以按如下思路实现,并确保最终只输出 JSON:
31
+ 脚本位置:`~/.opencode/agents/pr_context.py`
77
32
 
78
33
  ```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
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 文件路径,Structured Handoff) |
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 中提供问题清单文件路径(Markdown,Structured Handoff)
35
+ - **fixFile**:调用者必须在 prompt 中提供问题清单文件名(basename)(Structured Handoff)
32
36
 
33
37
  ### 失败快速退出
34
38
 
@@ -122,11 +126,12 @@ Round: 2
122
126
 
123
127
  ## 输出(强制)
124
128
 
125
- 写入:`~/.opencode/cache/fix-report-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
129
+ 写入:`fix-report-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
126
130
 
127
131
  最终只输出一行:
128
132
 
129
- `fixReportFile: <path>`
133
+ `fixReportFile: <filename>`
134
+
130
135
 
131
136
  ## fixReportFile 内容格式(强制)
132
137
 
@@ -167,4 +172,4 @@ Rejected: <n>
167
172
  ## 输出有效性保证
168
173
 
169
174
  - fixReportFile 必须成功写入
170
- - stdout 只能输出一行 `fixReportFile: <path>`
175
+ - stdout 只能输出一行 `fixReportFile: <filename>`