@ranger1/dx 0.1.33 → 0.1.35

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,25 +14,26 @@ tools:
14
14
 
15
15
  - `PR #<number>`
16
16
  - `round: <number>`
17
+ - `runId: <string>`(必须透传,禁止自行生成)
17
18
  - `contextFile: <filename>`
18
19
 
19
20
  ## 输出(强制)
20
21
 
21
22
  只输出一行:
22
23
 
23
- `reviewFile: <filename>`
24
+ `reviewFile: ./.cache/<file>.md`
24
25
 
25
26
 
26
27
  ## 规则
27
28
 
28
29
  - 默认已在 PR head 分支(可直接读工作区代码)
29
30
  - 可用 `git`/`gh` 只读命令获取 diff/上下文
30
- - 写入 reviewFile:`review-CLD-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
31
+ - 写入 reviewFile:`./.cache/review-CLD-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
31
32
  - findings id 必须以 `CLD-` 开头
32
33
 
33
34
  ## Cache 约定(强制)
34
- - 本流程所有中间文件都存放在 `~/.opencode/cache/`
35
- - agent/命令之间仅传递文件名(basename),不传目录
35
+
36
+ - 缓存目录固定为 `./.cache/`;交接一律传 `./.cache/<file>`(repo 相对路径),禁止 basename-only(如 `foo.md`)。
36
37
 
37
38
  ## reviewFile 格式(强制)
38
39
 
@@ -15,26 +15,26 @@ tools:
15
15
 
16
16
  - `PR #<number>`
17
17
  - `round: <number>`
18
+ - `runId: <string>`(必须透传,禁止自行生成)
18
19
  - `contextFile: <filename>`
19
20
 
20
21
  ## 输出(强制)
21
22
 
22
23
  只输出一行:
23
24
 
24
- `reviewFile: <filename>`
25
+ `reviewFile: ./.cache/<file>.md`
25
26
 
26
27
 
27
28
  ## 规则
28
29
 
29
30
  - 默认已在 PR head 分支(可直接读工作区代码)
30
31
  - 可用 `git`/`gh` 只读命令获取 diff/上下文
31
- - 写入 reviewFile:`review-CDX-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
32
+ - 写入 reviewFile:`./.cache/review-CDX-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
32
33
  - findings id 必须以 `CDX-` 开头
33
34
 
34
35
  ## Cache 约定(强制)
35
- - 本流程所有中间文件都存放在 `~/.opencode/cache/`
36
- - agent/命令之间仅传递文件名(basename),不传目录
37
36
 
37
+ - 缓存目录固定为 `./.cache/`;交接一律传 `./.cache/<file>`(repo 相对路径),禁止 basename-only(如 `foo.md`)。
38
38
 
39
39
  ## reviewFile 格式(强制)
40
40
 
@@ -14,26 +14,26 @@ tools:
14
14
 
15
15
  - `PR #<number>`
16
16
  - `round: <number>`
17
+ - `runId: <string>`(必须透传,禁止自行生成)
17
18
  - `contextFile: <filename>`
18
19
 
19
20
  ## 输出(强制)
20
21
 
21
22
  只输出一行:
22
23
 
23
- `reviewFile: <filename>`
24
+ `reviewFile: ./.cache/<file>.md`
24
25
 
25
26
 
26
27
  ## 规则
27
28
 
28
29
  - 默认已在 PR head 分支(可直接读工作区代码)
29
30
  - 可用 `git`/`gh` 只读命令获取 diff/上下文
30
- - 写入 reviewFile:`review-GMN-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
31
+ - 写入 reviewFile:`./.cache/review-GMN-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
31
32
  - findings id 必须以 `GMN-` 开头
32
33
 
33
34
  ## Cache 约定(强制)
34
- - 本流程所有中间文件都存放在 `~/.opencode/cache/`
35
- - agent/命令之间仅传递文件名(basename),不传目录
36
35
 
36
+ - 缓存目录固定为 `./.cache/`;交接一律传 `./.cache/<file>`(repo 相对路径),禁止 basename-only(如 `foo.md`)。
37
37
 
38
38
  ## reviewFile 格式(强制)
39
39
 
@@ -20,11 +20,11 @@ tools:
20
20
 
21
21
  ## 输出(强制)
22
22
 
23
- 脚本会写入 `~/.opencode/cache/`,stdout 只输出单一 JSON(可 `JSON.parse()`)。
23
+ 脚本会写入项目内 `./.cache/`,stdout 只输出单一 JSON(可 `JSON.parse()`)。
24
24
 
25
25
  ## Cache 约定(强制)
26
26
 
27
-
27
+ - 缓存目录固定为 `./.cache/`;交接一律传 `./.cache/<file>`(repo 相对路径),禁止 basename-only(如 `foo.md`)。
28
28
 
29
29
  ## 调用脚本(强制)
30
30
 
@@ -26,13 +26,14 @@ tools:
26
26
  ## 前置条件
27
27
 
28
28
  ### Cache 约定(强制)
29
- - 本流程所有中间文件都存放在 `~/.opencode/cache/`
30
- - agent/命令之间仅传递文件名(basename),不传目录
29
+
30
+ - 缓存目录固定为 `./.cache/`;交接一律传 `./.cache/<file>`(repo 相对路径),禁止 basename-only(如 `foo.md`)。
31
31
 
32
32
  ### 必需输入
33
33
 
34
34
  - **PR 编号**:调用者必须在 prompt 中明确提供(如:`请修复 PR #123`)
35
- - **fixFile**:调用者必须在 prompt 中提供问题清单文件名(basename)(Structured Handoff)
35
+ - **runId**:调用者必须在 prompt 中提供(必须透传,禁止自行生成)
36
+ - **fixFile**:调用者必须在 prompt 中提供问题清单文件路径(repo 相对路径,例:`./.cache/fix-...md`)(Structured Handoff)
36
37
 
37
38
  ### 失败快速退出
38
39
 
@@ -126,11 +127,11 @@ Round: 2
126
127
 
127
128
  ## 输出(强制)
128
129
 
129
- 写入:`fix-report-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
130
+ 写入:`./.cache/fix-report-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
130
131
 
131
132
  最终只输出一行:
132
133
 
133
- `fixReportFile: <filename>`
134
+ `fixReportFile: ./.cache/<file>.md`
134
135
 
135
136
 
136
137
  ## fixReportFile 内容格式(强制)
@@ -172,4 +173,4 @@ Rejected: <n>
172
173
  ## 输出有效性保证
173
174
 
174
175
  - fixReportFile 必须成功写入
175
- - stdout 只能输出一行 `fixReportFile: <filename>`
176
+ - stdout 只能输出一行 `fixReportFile: ./.cache/<file>.md`
@@ -10,9 +10,8 @@ tools:
10
10
  # PR Precheck
11
11
 
12
12
  ## Cache 约定(强制)
13
- - 本流程所有中间文件都存放在 `~/.opencode/cache/`
14
- - agent/命令之间仅传递文件名(basename),不传目录
15
13
 
14
+ - 缓存目录固定为 `./.cache/`;交接一律传 `./.cache/<file>`(repo 相对路径),禁止 basename-only(如 `foo.md`)。
16
15
 
17
16
  ## 输入(prompt 必须包含)
18
17
 
@@ -10,8 +10,8 @@ tools:
10
10
  # PR Review Aggregator
11
11
 
12
12
  ## Cache 约定(强制)
13
- - 本流程所有中间文件都存放在 `~/.opencode/cache/`
14
- - agent/命令之间仅传递文件名(basename),不传目录
13
+
14
+ - 缓存目录固定为 `./.cache/`;交接一律传 `./.cache/<file>`(repo 相对路径),禁止 basename-only(如 `foo.md`)。
15
15
 
16
16
  ## 输入(两种模式)
17
17
 
@@ -20,15 +20,15 @@ tools:
20
20
  - `PR #<number>`
21
21
  - `round: <number>`
22
22
  - `runId: <string>`
23
- - `contextFile: <filename>`
24
- - `reviewFile: <filename>`(三行,分别对应 CDX/CLD/GMN
23
+ - `contextFile: <path>`(例如:`./.cache/pr-context-...md`)
24
+ - `reviewFile: <path>`(三行,分别对应 CDX/CLD/GMN,例如:`./.cache/review-...md`)
25
25
 
26
26
  ### 模式 B:发布修复评论(基于 fixReportFile)
27
27
 
28
28
  - `PR #<number>`
29
29
  - `round: <number>`
30
30
  - `runId: <string>`
31
- - `fixReportFile: <filename>`
31
+ - `fixReportFile: <path>`(例如:`./.cache/fix-report-...md`)
32
32
 
33
33
  示例:
34
34
 
@@ -36,10 +36,10 @@ tools:
36
36
  PR #123
37
37
  round: 1
38
38
  runId: abcdef123456
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
39
+ contextFile: ./.cache/pr-context-pr123-r1-abcdef123456.md
40
+ reviewFile: ./.cache/review-CDX-pr123-r1-abcdef123456.md
41
+ reviewFile: ./.cache/review-CLD-pr123-r1-abcdef123456.md
42
+ reviewFile: ./.cache/review-GMN-pr123-r1-abcdef123456.md
43
43
  ```
44
44
 
45
45
  ## 执行方式(强制)
@@ -2,7 +2,7 @@
2
2
  # PR context builder (deterministic).
3
3
  # - Reads PR metadata + recent comments via gh
4
4
  # - Reads changed files via git diff (no patch)
5
- # - Writes Markdown context file to ~/.opencode/cache/
5
+ # - Writes Markdown context file to project cache: ./.cache/
6
6
  # - Prints exactly one JSON object to stdout
7
7
 
8
8
  import argparse
@@ -16,7 +16,38 @@ from urllib.parse import urlparse
16
16
  from pathlib import Path
17
17
 
18
18
 
19
- CACHE_DIR = Path.home() / ".opencode" / "cache"
19
+ def _repo_root():
20
+ # Prefer git top-level so the cache follows the current repo.
21
+ try:
22
+ p = subprocess.run(
23
+ ["git", "rev-parse", "--show-toplevel"],
24
+ stdout=subprocess.PIPE,
25
+ stderr=subprocess.DEVNULL,
26
+ text=True,
27
+ )
28
+ out = (p.stdout or "").strip()
29
+ if p.returncode == 0 and out:
30
+ return Path(out)
31
+ except Exception:
32
+ pass
33
+ return Path.cwd()
34
+
35
+
36
+ def _cache_dir(repo_root):
37
+ return (repo_root / ".cache").resolve()
38
+
39
+
40
+ def _repo_relpath(repo_root, p):
41
+ try:
42
+ rel = p.resolve().relative_to(repo_root.resolve())
43
+ return "./" + rel.as_posix()
44
+ except Exception:
45
+ # Fallback to basename-only.
46
+ return os.path.basename(str(p))
47
+
48
+
49
+ REPO_ROOT = _repo_root()
50
+ CACHE_DIR = _cache_dir(REPO_ROOT)
20
51
  MARKER_SUBSTR = "<!-- pr-review-loop-marker"
21
52
 
22
53
 
@@ -252,7 +283,8 @@ def main(argv):
252
283
  "repo": {"nameWithOwner": owner_repo},
253
284
  "headOid": head_oid,
254
285
  "existingMarkerCount": marker_count,
255
- "contextFile": context_file,
286
+ # Handoff should be repo-relative path so downstream agents can read it directly.
287
+ "contextFile": _repo_relpath(REPO_ROOT, context_path),
256
288
  }
257
289
  )
258
290
  return 0
@@ -8,12 +8,14 @@
8
8
  # - If mergeable == CONFLICTING: return {"error":"PR_MERGE_CONFLICTS_UNRESOLVED"}
9
9
  # - Run dx cache clear
10
10
  # - Run dx lint and dx build all concurrently
11
- # - On failure, write fixFile to ~/.opencode/cache/ and return {"ok":false,"fixFile":"..."}
11
+ # - On failure, write fixFile to project cache: ./.cache/
12
+ # and return {"ok":false,"fixFile":"./.cache/..."}
12
13
  # - On success, return {"ok":true}
13
14
  #
14
15
  # Stdout contract: print exactly one JSON object and nothing else.
15
16
 
16
17
  import json
18
+ import os
17
19
  import re
18
20
  import secrets
19
21
  import subprocess
@@ -92,6 +94,34 @@ def _detect_git_remote_host():
92
94
  return None
93
95
 
94
96
 
97
+ def repo_root():
98
+ try:
99
+ p = subprocess.run(
100
+ ["git", "rev-parse", "--show-toplevel"],
101
+ stdout=subprocess.PIPE,
102
+ stderr=subprocess.DEVNULL,
103
+ text=True,
104
+ )
105
+ out = (p.stdout or "").strip()
106
+ if p.returncode == 0 and out:
107
+ return Path(out)
108
+ except Exception:
109
+ pass
110
+ return Path.cwd()
111
+
112
+
113
+ def cache_dir(repo_root_path):
114
+ return (repo_root_path / ".cache").resolve()
115
+
116
+
117
+ def repo_relpath(repo_root_path, p):
118
+ try:
119
+ rel = p.resolve().relative_to(repo_root_path.resolve())
120
+ return "./" + rel.as_posix()
121
+ except Exception:
122
+ return str(p)
123
+
124
+
95
125
  def tail_text(path, max_lines=200, max_chars=12000):
96
126
  try:
97
127
  data = Path(path).read_text(errors="replace")
@@ -214,7 +244,8 @@ def main():
214
244
  return 1
215
245
 
216
246
  run_id = secrets.token_hex(4)
217
- cache = Path.home() / ".opencode" / "cache"
247
+ root = repo_root()
248
+ cache = cache_dir(root)
218
249
  cache.mkdir(parents=True, exist_ok=True)
219
250
 
220
251
  cache_clear_log = cache / f"precheck-pr{pr}-{run_id}-cache-clear.log"
@@ -227,9 +258,9 @@ def main():
227
258
  "headRefName": head,
228
259
  "baseRefName": base,
229
260
  "mergeable": mergeable,
230
- "cacheClearLog": str(cache_clear_log),
231
- "lintLog": str(lint_log),
232
- "buildLog": str(build_log),
261
+ "cacheClearLog": repo_relpath(root, cache_clear_log),
262
+ "lintLog": repo_relpath(root, lint_log),
263
+ "buildLog": repo_relpath(root, build_log),
233
264
  }, indent=2) + "\n")
234
265
 
235
266
  cache_rc = run(["dx", "cache", "clear"], stdout_path=str(cache_clear_log), stderr_path=str(cache_clear_log))
@@ -245,10 +276,10 @@ def main():
245
276
  "line": None,
246
277
  "title": "dx cache clear failed",
247
278
  "description": log_tail,
248
- "suggestion": f"Open log: {cache_clear_log}",
279
+ "suggestion": f"Open log: {repo_relpath(root, cache_clear_log)}",
249
280
  }]
250
281
  write_fixfile(str(fix_path), issues)
251
- print(json.dumps({"ok": False, "fixFile": fix_file}))
282
+ print(json.dumps({"ok": False, "fixFile": repo_relpath(root, fix_path)}))
252
283
  return 1
253
284
 
254
285
  import threading
@@ -285,7 +316,7 @@ def main():
285
316
  "line": line,
286
317
  "title": "dx lint failed",
287
318
  "description": log_tail,
288
- "suggestion": f"Open log: {lint_log}",
319
+ "suggestion": f"Open log: {repo_relpath(root, lint_log)}",
289
320
  })
290
321
  i += 1
291
322
  if results.get("build", 1) != 0:
@@ -299,11 +330,11 @@ def main():
299
330
  "line": line,
300
331
  "title": "dx build all failed",
301
332
  "description": log_tail,
302
- "suggestion": f"Open log: {build_log}",
333
+ "suggestion": f"Open log: {repo_relpath(root, build_log)}",
303
334
  })
304
335
 
305
336
  write_fixfile(str(fix_path), issues)
306
- print(json.dumps({"ok": False, "fixFile": fix_file}))
337
+ print(json.dumps({"ok": False, "fixFile": repo_relpath(root, fix_path)}))
307
338
  return 1
308
339
 
309
340
 
@@ -2,12 +2,12 @@
2
2
  # Deterministic PR review aggregation (script owns all rules).
3
3
  #
4
4
  # Workflow:
5
- # - Mode A: read contextFile + reviewFile(s) from ~/.opencode/cache/, parse findings, merge duplicates,
5
+ # - Mode A: read contextFile + reviewFile(s) from project cache: ./.cache/, parse findings, merge duplicates,
6
6
  # post a single PR comment, and optionally generate a fixFile for pr-fix.
7
7
  # - Mode B: read fixReportFile from cache and post it as a PR comment.
8
8
  #
9
9
  # Input rules:
10
- # - Callers pass only basenames (no paths). This script reads/writes under ~/.opencode/cache/.
10
+ # - Callers should pass repo-relative paths (e.g. ./.cache/foo.md). For backward-compat, basenames are also accepted.
11
11
  # - Duplicate groups come from LLM but are passed as an argument (NOT written to disk).
12
12
  # - Prefer: --duplicate-groups-b64 <base64(json)>
13
13
  # - Also supported: --duplicate-groups-json '<json>'
@@ -20,7 +20,7 @@
20
20
  #
21
21
  # PR comment rules:
22
22
  # - Every comment must include marker: <!-- pr-review-loop-marker -->
23
- # - Comment body must NOT contain local filesystem paths (this script scrubs ~/.opencode/cache and $HOME).
23
+ # - Comment body must NOT contain local filesystem paths (this script scrubs cache paths, $HOME, and repo absolute paths).
24
24
  #
25
25
  # fixFile rules:
26
26
  # - fixFile includes ONLY P0/P1/P2 findings.
@@ -38,7 +38,76 @@ from pathlib import Path
38
38
 
39
39
 
40
40
  MARKER = "<!-- pr-review-loop-marker -->"
41
- CACHE_DIR = Path.home() / ".opencode" / "cache"
41
+
42
+
43
+ def _repo_root():
44
+ try:
45
+ p = subprocess.run(
46
+ ["git", "rev-parse", "--show-toplevel"],
47
+ stdout=subprocess.PIPE,
48
+ stderr=subprocess.DEVNULL,
49
+ text=True,
50
+ )
51
+ out = (p.stdout or "").strip()
52
+ if p.returncode == 0 and out:
53
+ return Path(out)
54
+ except Exception:
55
+ pass
56
+ return Path.cwd()
57
+
58
+
59
+ def _cache_dir(repo_root):
60
+ return (repo_root / ".cache").resolve()
61
+
62
+
63
+ def _is_safe_relpath(p):
64
+ if p.is_absolute():
65
+ return False
66
+ if any(part in ("..",) for part in p.parts):
67
+ return False
68
+ return True
69
+
70
+
71
+ def _resolve_ref(repo_root, cache_dir, ref):
72
+ if not ref:
73
+ return None
74
+ s = str(ref).strip()
75
+ if not s:
76
+ return None
77
+
78
+ # If caller already passes a repo-relative path like ./.cache/foo.md
79
+ looks_like_path = ("/" in s) or ("\\" in s) or s.startswith(".")
80
+ if looks_like_path:
81
+ p = Path(s)
82
+ if p.is_absolute():
83
+ # Only allow absolute paths under cache_dir.
84
+ try:
85
+ p2 = p.resolve()
86
+ p2.relative_to(cache_dir.resolve())
87
+ return p2
88
+ except Exception:
89
+ return None
90
+ if not _is_safe_relpath(p):
91
+ return None
92
+ return (repo_root / p).resolve()
93
+
94
+ # Backward-compat: accept basename-only.
95
+ b = _safe_basename(s)
96
+ if not b:
97
+ return None
98
+ return (cache_dir / b).resolve()
99
+
100
+
101
+ def _repo_relpath(repo_root, p):
102
+ try:
103
+ rel = p.resolve().relative_to(repo_root.resolve())
104
+ return "./" + rel.as_posix()
105
+ except Exception:
106
+ return os.path.basename(str(p))
107
+
108
+
109
+ REPO_ROOT = _repo_root()
110
+ CACHE_DIR = _cache_dir(REPO_ROOT)
42
111
 
43
112
 
44
113
  def _json_out(obj):
@@ -57,14 +126,19 @@ def _safe_basename(name):
57
126
  return base
58
127
 
59
128
 
60
- def _read_cache_text(basename):
61
- p = CACHE_DIR / basename
129
+ def _read_cache_text(ref):
130
+ p = _resolve_ref(REPO_ROOT, CACHE_DIR, ref)
131
+ if not p:
132
+ raise FileNotFoundError("INVALID_CACHE_REF")
62
133
  return p.read_text(encoding="utf-8", errors="replace")
63
134
 
64
135
 
65
- def _write_cache_text(basename, content):
136
+ def _write_cache_text(ref, content):
137
+ p = _resolve_ref(REPO_ROOT, CACHE_DIR, ref)
138
+ if not p:
139
+ raise ValueError("INVALID_CACHE_REF")
66
140
  CACHE_DIR.mkdir(parents=True, exist_ok=True)
67
- p = CACHE_DIR / basename
141
+ p.parent.mkdir(parents=True, exist_ok=True)
68
142
  p.write_text(content, encoding="utf-8", newline="\n")
69
143
 
70
144
 
@@ -88,13 +162,21 @@ def _sanitize_for_comment(text):
88
162
  text = str(text)
89
163
 
90
164
  home = str(Path.home())
91
- cache_abs = str(CACHE_DIR)
165
+ cache_abs = str(CACHE_DIR.resolve())
166
+ repo_abs = str(REPO_ROOT.resolve())
92
167
 
168
+ # Backward-compat scrub.
93
169
  text = text.replace("~/.opencode/cache/", "[cache]/")
94
- text = text.replace(cache_abs + "/", "[cache]/")
95
170
  if home:
96
171
  text = text.replace(home + "/.opencode/cache/", "[cache]/")
97
172
 
173
+ # New cache scrub.
174
+ text = text.replace(cache_abs + "/", "[cache]/")
175
+
176
+ # Avoid leaking absolute local repo paths.
177
+ if repo_abs:
178
+ text = text.replace(repo_abs + "/", "")
179
+
98
180
  return text
99
181
 
100
182
 
@@ -236,8 +318,14 @@ def _counts(findings):
236
318
  return c
237
319
 
238
320
 
239
- def _post_pr_comment(pr_number, body_basename):
240
- body_path = str(CACHE_DIR / body_basename)
321
+ def _post_pr_comment(pr_number, body_ref):
322
+ if isinstance(body_ref, Path):
323
+ p = body_ref
324
+ else:
325
+ p = _resolve_ref(REPO_ROOT, CACHE_DIR, body_ref)
326
+ if not p:
327
+ return False
328
+ body_path = str(p)
241
329
  rc = subprocess.run(
242
330
  ["gh", "pr", "comment", str(pr_number), "--body-file", body_path],
243
331
  stdout=subprocess.DEVNULL,
@@ -334,20 +422,25 @@ def main(argv):
334
422
  round_num = args.round
335
423
  run_id = str(args.run_id)
336
424
 
337
- fix_report_file = _safe_basename(args.fix_report_file) if args.fix_report_file else None
338
- context_file = _safe_basename(args.context_file) if args.context_file else None
425
+ fix_report_file = (args.fix_report_file or "").strip() or None
426
+ context_file = (args.context_file or "").strip() or None
339
427
  review_files = []
340
428
  for rf in args.review_file or []:
341
- b = _safe_basename(rf)
342
- if b:
343
- review_files.append(b)
429
+ s = (rf or "").strip()
430
+ if s:
431
+ review_files.append(s)
344
432
 
345
433
  if fix_report_file:
434
+ fix_p = _resolve_ref(REPO_ROOT, CACHE_DIR, fix_report_file)
435
+ if not fix_p or not fix_p.exists():
436
+ _json_out({"error": "FIX_REPORT_FILE_NOT_FOUND"})
437
+ return 1
346
438
  fix_md = _read_cache_text(fix_report_file)
347
439
  body = _render_mode_b_comment(pr_number, round_num, run_id, fix_md)
348
- body_file = f"review-aggregate-fix-comment-pr{pr_number}-r{round_num}-{run_id}.md"
349
- _write_cache_text(body_file, body)
350
- if not _post_pr_comment(pr_number, body_file):
440
+ body_basename = f"review-aggregate-fix-comment-pr{pr_number}-r{round_num}-{run_id}.md"
441
+ body_ref = _repo_relpath(REPO_ROOT, CACHE_DIR / body_basename)
442
+ _write_cache_text(body_ref, body)
443
+ if not _post_pr_comment(pr_number, body_ref):
351
444
  _json_out({"error": "GH_PR_COMMENT_FAILED"})
352
445
  return 1
353
446
  _json_out({"ok": True})
@@ -360,6 +453,21 @@ def main(argv):
360
453
  _json_out({"error": "MISSING_REVIEW_FILES"})
361
454
  return 1
362
455
 
456
+ ctx_p = _resolve_ref(REPO_ROOT, CACHE_DIR, context_file)
457
+ if not ctx_p or not ctx_p.exists():
458
+ _json_out({"error": "CONTEXT_FILE_NOT_FOUND"})
459
+ return 1
460
+
461
+ valid_review_files = []
462
+ for rf in review_files:
463
+ p = _resolve_ref(REPO_ROOT, CACHE_DIR, rf)
464
+ if p and p.exists():
465
+ valid_review_files.append(rf)
466
+ review_files = valid_review_files
467
+ if not review_files:
468
+ _json_out({"error": "REVIEW_FILES_NOT_FOUND"})
469
+ return 1
470
+
363
471
  raw_reviews = []
364
472
  all_findings = []
365
473
  for rf in review_files:
@@ -377,9 +485,10 @@ def main(argv):
377
485
  stop = len(must_fix) == 0
378
486
 
379
487
  body = _render_mode_a_comment(pr_number, round_num, run_id, counts, must_fix, merged_map, raw_reviews)
380
- body_file = f"review-aggregate-comment-pr{pr_number}-r{round_num}-{run_id}.md"
381
- _write_cache_text(body_file, body)
382
- if not _post_pr_comment(pr_number, body_file):
488
+ body_basename = f"review-aggregate-comment-pr{pr_number}-r{round_num}-{run_id}.md"
489
+ body_ref = _repo_relpath(REPO_ROOT, CACHE_DIR / body_basename)
490
+ _write_cache_text(body_ref, body)
491
+ if not _post_pr_comment(pr_number, body_ref):
383
492
  _json_out({"error": "GH_PR_COMMENT_FAILED"})
384
493
  return 1
385
494
 
@@ -415,8 +524,9 @@ def main(argv):
415
524
  lines.append(f" description: {desc}")
416
525
  lines.append(f" suggestion: {sugg}")
417
526
 
418
- _write_cache_text(fix_file, "\n".join(lines) + "\n")
419
- _json_out({"stop": False, "fixFile": fix_file})
527
+ fix_ref = _repo_relpath(REPO_ROOT, CACHE_DIR / fix_file)
528
+ _write_cache_text(fix_ref, "\n".join(lines) + "\n")
529
+ _json_out({"stop": False, "fixFile": fix_ref})
420
530
  return 0
421
531
 
422
532
 
@@ -12,8 +12,8 @@ agent: sisyphus
12
12
 
13
13
  ## Cache 约定(强制)
14
14
 
15
- - 本流程所有中间文件都存放在 `~/.opencode/cache/`
16
- - agent/命令之间仅传递文件名(basename),不传目录
15
+ - 本流程所有中间文件都存放在项目内:`./.cache/`
16
+ - agent/命令之间传递**repo 相对路径**(例如:`./.cache/pr-context-...md`),不要只传 basename
17
17
 
18
18
  ## 固定 subagent_type(直接用 Task 调用,不要反复确认)
19
19
 
@@ -56,17 +56,18 @@ agent: sisyphus
56
56
  - 每个 reviewer prompt 必须包含:
57
57
  - `PR #{{PR_NUMBER}}`
58
58
  - `round: <ROUND>`
59
- - `contextFile: <filename>`(来自 Step 1 的输出)
59
+ - `runId: <RUN_ID>`(来自 Step 1 的输出,必须透传,禁止自行生成)
60
+ - `contextFile: ./.cache/<file>.md`(来自 Step 1 的输出)
60
61
  - reviewer 默认读 `contextFile`;必要时允许用 `git/gh` 只读命令拿 diff
61
62
  - 忽略问题:1.格式化代码引起的噪音 2.已经lint检查以外的格式问题
62
63
  - 特别关注: 逻辑、安全、性能、可维护性
63
64
  - 同时要注意 pr 前面轮次的 修复和讨论,对于已经拒绝、已修复的问题不要反复的提出
64
65
  - 同时也要注意fix的过程中有没有引入新的问题。
65
- - 每个 reviewer 输出:`reviewFile: <filename>`(Markdown)
66
+ - 每个 reviewer 输出:`reviewFile: ./.cache/<file>.md`(Markdown)
66
67
 
67
68
  3. Task: `pr-review-aggregate`
68
69
 
69
- - prompt 必须包含:`PR #{{PR_NUMBER}}`、`round: <ROUND>`、`runId: <RUN_ID>`、`contextFile: <filename>`、三条 `reviewFile: <filename>`
70
+ - prompt 必须包含:`PR #{{PR_NUMBER}}`、`round: <ROUND>`、`runId: <RUN_ID>`、`contextFile: ./.cache/<file>.md`、三条 `reviewFile: ./.cache/<file>.md`
70
71
  - 输出:`{"stop":true}` 或 `{"stop":false,"fixFile":"..."}`
71
72
  - 若 `stop=true`:本轮结束并退出循环
72
73
 
@@ -75,14 +76,15 @@ agent: sisyphus
75
76
  - prompt 必须包含:
76
77
  - `PR #{{PR_NUMBER}}`
77
78
  - `round: <ROUND>`
78
- - `fixFile: <filename>`
79
+ - `runId: <RUN_ID>`(来自 Step 1 的输出,必须透传,禁止自行生成)
80
+ - `fixFile: ./.cache/<file>.md`
79
81
  - 约定:`pr-fix` 对每个 findingId 单独 commit + push(一个 findingId 一个 commit),结束后再 `git push` 兜底
80
82
 
81
- - pr-fix 输出:`fixReportFile: <filename>`(Markdown)
83
+ - pr-fix 输出:`fixReportFile: ./.cache/<file>.md`(Markdown)
82
84
 
83
85
  5. Task: `pr-review-aggregate`(发布修复评论)
84
86
 
85
- - prompt 必须包含:`PR #{{PR_NUMBER}}`、`round: <ROUND>`、`runId: <RUN_ID>`、`fixReportFile: <filename>`
87
+ - prompt 必须包含:`PR #{{PR_NUMBER}}`、`round: <ROUND>`、`runId: <RUN_ID>`、`fixReportFile: ./.cache/<file>.md`
86
88
  - 输出:`{"ok":true}`
87
89
 
88
90
  6. 下一轮
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ranger1/dx",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {