@ranger1/dx 0.1.91 → 0.1.93
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/README.md +2 -2
- package/lib/cli/help.js +1 -1
- package/lib/codex-initial.js +24 -211
- package/package.json +2 -2
- package/skills/backend-layering-audit-fixer/SKILL.md +180 -0
- package/{codex/skills → skills}/doctor/SKILL.md +2 -9
- package/{codex/skills → skills}/doctor/scripts/doctor.sh +2 -253
- package/skills/git-pr-ship/SKILL.md +481 -0
- package/skills/naming-audit-fixer/SKILL.md +149 -0
- package/skills/naming-audit-fixer/references/fix-guide.md +93 -0
- package/skills/naming-audit-fixer/scripts/audit_naming.py +534 -0
- package/codex/agents/fixer.toml +0 -37
- package/codex/agents/orchestrator.toml +0 -11
- package/codex/agents/reviewer.toml +0 -52
- package/codex/agents/spark.toml +0 -18
- package/codex/skills/pr-review-loop/SKILL.md +0 -209
- package/codex/skills/pr-review-loop/agents/openai.yaml +0 -4
- package/codex/skills/pr-review-loop/references/agents/pr-context.md +0 -73
- package/codex/skills/pr-review-loop/references/agents/pr-precheck.md +0 -161
- package/codex/skills/pr-review-loop/references/agents/pr-review-aggregate.md +0 -188
- package/codex/skills/pr-review-loop/references/skill-layout.md +0 -25
- package/codex/skills/pr-review-loop/scripts/gh_review_harvest.py +0 -292
- package/codex/skills/pr-review-loop/scripts/pr_context.py +0 -351
- package/codex/skills/pr-review-loop/scripts/pr_review_aggregate.py +0 -951
- package/codex/skills/pr-review-loop/scripts/test_pr_review_aggregate.py +0 -876
- package/codex/skills/pr-review-loop/scripts/test_validate_reviewer_prompts.py +0 -92
- package/codex/skills/pr-review-loop/scripts/validate_reviewer_prompts.py +0 -87
- /package/{codex/skills → skills}/doctor/agents/openai.yaml +0 -0
- /package/{codex/skills → skills}/e2e-audit-fixer/SKILL.md +0 -0
- /package/{codex/skills → skills}/e2e-audit-fixer/agents/openai.yaml +0 -0
- /package/{codex/skills → skills}/e2e-audit-fixer/scripts/e2e_e2e_audit.py +0 -0
- /package/{codex/skills → skills}/env-accessor-audit-fixer/SKILL.md +0 -0
- /package/{codex/skills → skills}/env-accessor-audit-fixer/agents/openai.yaml +0 -0
- /package/{codex/skills → skills}/env-accessor-audit-fixer/references/bootstrap-env-foundation.md +0 -0
- /package/{codex/skills → skills}/env-accessor-audit-fixer/scripts/env_accessor_audit.py +0 -0
- /package/{codex/skills → skills}/error-handling-audit-fixer/SKILL.md +0 -0
- /package/{codex/skills → skills}/error-handling-audit-fixer/agents/openai.yaml +0 -0
- /package/{codex/skills → skills}/error-handling-audit-fixer/references/error-handling-standard.md +0 -0
- /package/{codex/skills → skills}/error-handling-audit-fixer/references/foundation-bootstrap.md +0 -0
- /package/{codex/skills → skills}/error-handling-audit-fixer/scripts/error_handling_audit.py +0 -0
- /package/{codex/skills → skills}/gh-dependabot-cleanup/SKILL.md +0 -0
- /package/{codex/skills → skills}/gh-dependabot-cleanup/agents/openai.yaml +0 -0
- /package/{codex/skills → skills}/git-commit-and-pr/SKILL.md +0 -0
- /package/{codex/skills → skills}/git-commit-and-pr/agents/openai.yaml +0 -0
- /package/{codex/skills → skills}/git-release/SKILL.md +0 -0
- /package/{codex/skills → skills}/git-release/agents/openai.yaml +0 -0
- /package/{codex/skills → skills}/online-debug-guard/SKILL.md +0 -0
- /package/{codex/skills → skills}/online-debug-guard/agents/openai.yaml +0 -0
- /package/{codex/skills → skills}/pagination-dto-audit-fixer/SKILL.md +0 -0
- /package/{codex/skills → skills}/pagination-dto-audit-fixer/agents/openai.yaml +0 -0
- /package/{codex/skills → skills}/pagination-dto-audit-fixer/references/pagination-standard.md +0 -0
- /package/{codex/skills → skills}/pagination-dto-audit-fixer/scripts/pagination_dto_audit.py +0 -0
|
@@ -1,351 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# PR context builder (deterministic).
|
|
3
|
-
# - Reads PR metadata + recent comments via gh
|
|
4
|
-
# - Reads changed files via git diff (no patch)
|
|
5
|
-
# - Writes Markdown context file to project cache: ./.cache/
|
|
6
|
-
# - Prints exactly one JSON object to stdout
|
|
7
|
-
|
|
8
|
-
import argparse
|
|
9
|
-
import json
|
|
10
|
-
import os
|
|
11
|
-
import re
|
|
12
|
-
import subprocess
|
|
13
|
-
import sys
|
|
14
|
-
from urllib.parse import urlparse
|
|
15
|
-
from pathlib import Path
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def _repo_root():
|
|
19
|
-
# Prefer git top-level so the cache follows the current repo.
|
|
20
|
-
try:
|
|
21
|
-
p = subprocess.run(
|
|
22
|
-
["git", "rev-parse", "--show-toplevel"],
|
|
23
|
-
stdout=subprocess.PIPE,
|
|
24
|
-
stderr=subprocess.DEVNULL,
|
|
25
|
-
text=True,
|
|
26
|
-
)
|
|
27
|
-
out = (p.stdout or "").strip()
|
|
28
|
-
if p.returncode == 0 and out:
|
|
29
|
-
return Path(out)
|
|
30
|
-
except Exception:
|
|
31
|
-
pass
|
|
32
|
-
return Path.cwd()
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def _cache_dir(repo_root):
|
|
36
|
-
return (repo_root / ".cache").resolve()
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def _repo_relpath(repo_root, p):
|
|
40
|
-
try:
|
|
41
|
-
rel = p.resolve().relative_to(repo_root.resolve())
|
|
42
|
-
return "./" + rel.as_posix()
|
|
43
|
-
except Exception:
|
|
44
|
-
# Fallback to basename-only.
|
|
45
|
-
return os.path.basename(str(p))
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
REPO_ROOT = _repo_root()
|
|
49
|
-
CACHE_DIR = _cache_dir(REPO_ROOT)
|
|
50
|
-
MARKER_SUBSTR = "<!-- pr-review-loop-marker"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def _json_out(obj):
|
|
54
|
-
sys.stdout.write(json.dumps(obj, ensure_ascii=True))
|
|
55
|
-
sys.stdout.write("\n")
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def _run_capture(cmd):
|
|
59
|
-
try:
|
|
60
|
-
p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
61
|
-
return p.returncode, p.stdout, p.stderr
|
|
62
|
-
except FileNotFoundError as e:
|
|
63
|
-
return 127, "", str(e)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def _detect_git_remote_host():
|
|
67
|
-
# Best-effort parse from origin remote.
|
|
68
|
-
rc, origin_url, _ = _run_capture(["git", "remote", "get-url", "origin"])
|
|
69
|
-
if rc != 0:
|
|
70
|
-
rc, origin_url, _ = _run_capture(["git", "config", "--get", "remote.origin.url"])
|
|
71
|
-
if rc != 0:
|
|
72
|
-
return None
|
|
73
|
-
|
|
74
|
-
url = (origin_url or "").strip()
|
|
75
|
-
if not url:
|
|
76
|
-
return None
|
|
77
|
-
|
|
78
|
-
# Examples:
|
|
79
|
-
# - git@github.com:owner/repo.git
|
|
80
|
-
# - ssh://git@github.company.com/owner/repo.git
|
|
81
|
-
# - https://github.com/owner/repo.git
|
|
82
|
-
if url.startswith("git@"): # SCP-like syntax
|
|
83
|
-
m = re.match(r"^git@([^:]+):", url)
|
|
84
|
-
return m.group(1) if m else None
|
|
85
|
-
|
|
86
|
-
if url.startswith("ssh://") or url.startswith("https://") or url.startswith("http://"):
|
|
87
|
-
try:
|
|
88
|
-
parsed = urlparse(url)
|
|
89
|
-
return parsed.hostname
|
|
90
|
-
except Exception:
|
|
91
|
-
return None
|
|
92
|
-
|
|
93
|
-
return None
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def _clip(s, n):
|
|
97
|
-
if s is None:
|
|
98
|
-
return ""
|
|
99
|
-
s = str(s)
|
|
100
|
-
return s if len(s) <= n else (s[:n] + "...")
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
def _safe_basename(name):
|
|
104
|
-
if not name:
|
|
105
|
-
return None
|
|
106
|
-
base = os.path.basename(name.strip())
|
|
107
|
-
if base != name.strip():
|
|
108
|
-
return None
|
|
109
|
-
if base in (".", ".."):
|
|
110
|
-
return None
|
|
111
|
-
return base
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
def _git_fetch_origin(ref):
|
|
115
|
-
subprocess.run(["git", "fetch", "origin", ref], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def _gh_default_branch(owner_repo):
|
|
119
|
-
if not owner_repo:
|
|
120
|
-
return ""
|
|
121
|
-
rc, out, _ = _run_capture(
|
|
122
|
-
[
|
|
123
|
-
"gh",
|
|
124
|
-
"repo",
|
|
125
|
-
"view",
|
|
126
|
-
"--repo",
|
|
127
|
-
owner_repo,
|
|
128
|
-
"--json",
|
|
129
|
-
"defaultBranchRef",
|
|
130
|
-
"--jq",
|
|
131
|
-
".defaultBranchRef.name",
|
|
132
|
-
]
|
|
133
|
-
)
|
|
134
|
-
if rc != 0:
|
|
135
|
-
return ""
|
|
136
|
-
return (out or "").strip()
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def _git_numstat(base_ref, base_oid):
|
|
140
|
-
# Prefer commit-oid diff when available; it's unambiguous for stacked PRs.
|
|
141
|
-
candidates = []
|
|
142
|
-
if base_oid:
|
|
143
|
-
candidates.append(f"{base_oid}...HEAD")
|
|
144
|
-
if base_ref:
|
|
145
|
-
candidates.append(f"origin/{base_ref}...HEAD")
|
|
146
|
-
candidates.append(f"{base_ref}...HEAD")
|
|
147
|
-
|
|
148
|
-
for lhs in candidates:
|
|
149
|
-
rc, out, _ = _run_capture(["git", "diff", "--numstat", lhs])
|
|
150
|
-
if rc == 0:
|
|
151
|
-
return out
|
|
152
|
-
return ""
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
def _parse_numstat(numstat_text):
|
|
156
|
-
rows = []
|
|
157
|
-
for line in (numstat_text or "").splitlines():
|
|
158
|
-
parts = line.split("\t")
|
|
159
|
-
if len(parts) < 3:
|
|
160
|
-
continue
|
|
161
|
-
add_s, del_s, path = parts[0].strip(), parts[1].strip(), parts[2].strip()
|
|
162
|
-
if not path:
|
|
163
|
-
continue
|
|
164
|
-
rows.append((add_s, del_s, path))
|
|
165
|
-
return rows
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
def main(argv):
|
|
169
|
-
class _ArgParser(argparse.ArgumentParser):
|
|
170
|
-
def error(self, message):
|
|
171
|
-
raise ValueError(message)
|
|
172
|
-
|
|
173
|
-
parser = _ArgParser(add_help=False)
|
|
174
|
-
parser.add_argument("--pr", type=int, required=True)
|
|
175
|
-
parser.add_argument("--round", type=int, default=1)
|
|
176
|
-
try:
|
|
177
|
-
args = parser.parse_args(argv)
|
|
178
|
-
except ValueError:
|
|
179
|
-
_json_out({"error": "INVALID_ARGS"})
|
|
180
|
-
return 2
|
|
181
|
-
|
|
182
|
-
pr_number = int(args.pr)
|
|
183
|
-
round_num = int(args.round)
|
|
184
|
-
|
|
185
|
-
def _json_err(error_code, extra=None):
|
|
186
|
-
obj = {"error": error_code, "prNumber": pr_number, "round": round_num}
|
|
187
|
-
if isinstance(extra, dict) and extra:
|
|
188
|
-
obj.update(extra)
|
|
189
|
-
_json_out(obj)
|
|
190
|
-
|
|
191
|
-
# Preconditions: be in a git repo and gh is authenticated.
|
|
192
|
-
rc, out, _ = _run_capture(["git", "rev-parse", "--is-inside-work-tree"])
|
|
193
|
-
if rc != 0 or out.strip() != "true":
|
|
194
|
-
_json_err("NOT_A_GIT_REPO")
|
|
195
|
-
return 1
|
|
196
|
-
|
|
197
|
-
host = _detect_git_remote_host() or "github.com"
|
|
198
|
-
rc, gh_out, gh_err = _run_capture(["gh", "auth", "status", "--hostname", host])
|
|
199
|
-
if rc == 127:
|
|
200
|
-
_json_err(
|
|
201
|
-
"GH_CLI_NOT_FOUND",
|
|
202
|
-
{
|
|
203
|
-
"detail": "gh not found in PATH",
|
|
204
|
-
"suggestion": "Install GitHub CLI: https://cli.github.com/",
|
|
205
|
-
},
|
|
206
|
-
)
|
|
207
|
-
return 1
|
|
208
|
-
if rc != 0:
|
|
209
|
-
# If host detection is wrong, a global check might still succeed.
|
|
210
|
-
rc2, gh_out2, gh_err2 = _run_capture(["gh", "auth", "status"])
|
|
211
|
-
if rc2 == 0:
|
|
212
|
-
pass
|
|
213
|
-
else:
|
|
214
|
-
detail = (gh_err or gh_out or "").strip()
|
|
215
|
-
if len(detail) > 4000:
|
|
216
|
-
detail = detail[-4000:]
|
|
217
|
-
_json_err(
|
|
218
|
-
"GH_NOT_AUTHENTICATED",
|
|
219
|
-
{
|
|
220
|
-
"host": host,
|
|
221
|
-
"detail": detail,
|
|
222
|
-
"suggestion": f"Run: gh auth login --hostname {host}",
|
|
223
|
-
},
|
|
224
|
-
)
|
|
225
|
-
return 1
|
|
226
|
-
|
|
227
|
-
rc, owner_repo, _ = _run_capture(["gh", "repo", "view", "--json", "nameWithOwner", "--jq", ".nameWithOwner"])
|
|
228
|
-
owner_repo = owner_repo.strip() if rc == 0 else ""
|
|
229
|
-
if not owner_repo:
|
|
230
|
-
_json_err("REPO_NOT_FOUND")
|
|
231
|
-
return 1
|
|
232
|
-
|
|
233
|
-
fields = "number,url,title,body,isDraft,labels,baseRefName,headRefName,baseRefOid,headRefOid,comments"
|
|
234
|
-
rc, pr_json, _ = _run_capture(["gh", "pr", "view", str(pr_number), "--repo", owner_repo, "--json", fields])
|
|
235
|
-
if rc != 0:
|
|
236
|
-
_json_err("PR_NOT_FOUND_OR_NO_ACCESS")
|
|
237
|
-
return 1
|
|
238
|
-
try:
|
|
239
|
-
pr = json.loads(pr_json)
|
|
240
|
-
except Exception:
|
|
241
|
-
_json_err("PR_NOT_FOUND_OR_NO_ACCESS")
|
|
242
|
-
return 1
|
|
243
|
-
|
|
244
|
-
head_oid = (pr.get("headRefOid") or "").strip()
|
|
245
|
-
base_oid = (pr.get("baseRefOid") or "").strip()
|
|
246
|
-
base_ref = (pr.get("baseRefName") or "").strip()
|
|
247
|
-
|
|
248
|
-
if not head_oid:
|
|
249
|
-
_json_err("PR_HEAD_OID_NOT_FOUND")
|
|
250
|
-
return 1
|
|
251
|
-
|
|
252
|
-
head_short = head_oid[:7]
|
|
253
|
-
if not base_ref:
|
|
254
|
-
base_ref = _gh_default_branch(owner_repo)
|
|
255
|
-
if not base_ref and not base_oid:
|
|
256
|
-
_json_err("PR_BASE_REF_NOT_FOUND")
|
|
257
|
-
return 1
|
|
258
|
-
head_ref = (pr.get("headRefName") or "").strip()
|
|
259
|
-
url = (pr.get("url") or "").strip()
|
|
260
|
-
|
|
261
|
-
run_id = f"{pr_number}-{round_num}-{head_short}"
|
|
262
|
-
|
|
263
|
-
if base_ref:
|
|
264
|
-
_git_fetch_origin(base_ref)
|
|
265
|
-
file_rows = _parse_numstat(_git_numstat(base_ref, base_oid))
|
|
266
|
-
|
|
267
|
-
labels = []
|
|
268
|
-
for l in (pr.get("labels") or []):
|
|
269
|
-
if isinstance(l, dict) and l.get("name"):
|
|
270
|
-
labels.append(str(l.get("name")))
|
|
271
|
-
|
|
272
|
-
comments = pr.get("comments") or []
|
|
273
|
-
recent = comments[-10:] if isinstance(comments, list) else []
|
|
274
|
-
marker_count = 0
|
|
275
|
-
for c in recent:
|
|
276
|
-
if not isinstance(c, dict):
|
|
277
|
-
continue
|
|
278
|
-
body = c.get("body") or ""
|
|
279
|
-
if isinstance(body, str) and MARKER_SUBSTR in body:
|
|
280
|
-
marker_count += 1
|
|
281
|
-
|
|
282
|
-
CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
|
283
|
-
context_file = f"pr-context-pr{pr_number}-r{round_num}-{run_id}.md"
|
|
284
|
-
context_path = CACHE_DIR / context_file
|
|
285
|
-
|
|
286
|
-
with open(context_path, "w", encoding="utf-8", newline="\n") as fp:
|
|
287
|
-
fp.write("# PR Context\n\n")
|
|
288
|
-
fp.write(f"- Repo: {owner_repo}\n")
|
|
289
|
-
fp.write(f"- PR: #{pr_number} {url}\n")
|
|
290
|
-
fp.write(f"- Round: {round_num}\n")
|
|
291
|
-
fp.write(f"- RunId: {run_id}\n")
|
|
292
|
-
fp.write(f"- Base: {base_ref}\n")
|
|
293
|
-
fp.write(f"- Head: {head_ref}\n")
|
|
294
|
-
fp.write(f"- HeadShort: {head_short}\n")
|
|
295
|
-
fp.write(f"- HeadOid: {head_oid}\n")
|
|
296
|
-
fp.write(f"- Draft: {pr.get('isDraft')}\n")
|
|
297
|
-
fp.write(f"- Labels: {', '.join(labels) if labels else '(none)'}\n")
|
|
298
|
-
fp.write(f"- ExistingLoopMarkers: {marker_count}\n\n")
|
|
299
|
-
|
|
300
|
-
fp.write("## Title\n\n")
|
|
301
|
-
fp.write(_clip(pr.get("title") or "", 200) + "\n\n")
|
|
302
|
-
|
|
303
|
-
fp.write("## Body (excerpt)\n\n")
|
|
304
|
-
fp.write(_clip(pr.get("body") or "", 2000) or "(empty)")
|
|
305
|
-
fp.write("\n\n")
|
|
306
|
-
|
|
307
|
-
fp.write(f"## Changed Files ({len(file_rows)})\n\n")
|
|
308
|
-
if file_rows:
|
|
309
|
-
for add_s, del_s, path in file_rows:
|
|
310
|
-
fp.write(f"- +{add_s} -{del_s} {path}\n")
|
|
311
|
-
else:
|
|
312
|
-
fp.write("(none)\n")
|
|
313
|
-
fp.write("\n")
|
|
314
|
-
|
|
315
|
-
fp.write("## Recent Comments (excerpt)\n\n")
|
|
316
|
-
if recent:
|
|
317
|
-
for c in recent:
|
|
318
|
-
if not isinstance(c, dict):
|
|
319
|
-
continue
|
|
320
|
-
author = None
|
|
321
|
-
if isinstance(c.get("author"), dict):
|
|
322
|
-
author = (c.get("author") or {}).get("login")
|
|
323
|
-
fp.write(f"- {author or 'unknown'}: {_clip(c.get('body') or '', 300)}\n")
|
|
324
|
-
else:
|
|
325
|
-
fp.write("(none)\n")
|
|
326
|
-
|
|
327
|
-
_json_out(
|
|
328
|
-
{
|
|
329
|
-
"agent": "pr-context",
|
|
330
|
-
"prNumber": pr_number,
|
|
331
|
-
"round": round_num,
|
|
332
|
-
"runId": run_id,
|
|
333
|
-
"repo": {"nameWithOwner": owner_repo},
|
|
334
|
-
"headOid": head_oid,
|
|
335
|
-
"headShort": head_short,
|
|
336
|
-
"existingMarkerCount": marker_count,
|
|
337
|
-
# Handoff should be repo-relative path so downstream agents can read it directly.
|
|
338
|
-
"contextFile": _repo_relpath(REPO_ROOT, context_path),
|
|
339
|
-
}
|
|
340
|
-
)
|
|
341
|
-
return 0
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
if __name__ == "__main__":
|
|
345
|
-
try:
|
|
346
|
-
raise SystemExit(main(sys.argv[1:]))
|
|
347
|
-
except SystemExit:
|
|
348
|
-
raise
|
|
349
|
-
except Exception:
|
|
350
|
-
_json_out({"error": "PR_CONTEXT_SCRIPT_FAILED"})
|
|
351
|
-
raise SystemExit(1)
|