@ranger1/dx 0.1.76 → 0.1.78

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.
Files changed (36) hide show
  1. package/README.md +92 -31
  2. package/bin/dx.js +3 -3
  3. package/lib/cli/commands/deploy.js +2 -1
  4. package/lib/cli/commands/stack.js +198 -237
  5. package/lib/cli/commands/start.js +0 -6
  6. package/lib/cli/dx-cli.js +10 -1
  7. package/lib/cli/help.js +8 -7
  8. package/lib/{opencode-initial.js → codex-initial.js} +3 -82
  9. package/lib/vercel-deploy.js +14 -27
  10. package/package.json +1 -2
  11. package/@opencode/agents/__pycache__/gh_review_harvest.cpython-314.pyc +0 -0
  12. package/@opencode/agents/__pycache__/pr_context.cpython-314.pyc +0 -0
  13. package/@opencode/agents/__pycache__/pr_precheck.cpython-314.pyc +0 -0
  14. package/@opencode/agents/__pycache__/pr_review_aggregate.cpython-314.pyc +0 -0
  15. package/@opencode/agents/__pycache__/test_pr_review_aggregate.cpython-314-pytest-9.0.2.pyc +0 -0
  16. package/@opencode/agents/__pycache__/test_pr_review_aggregate.cpython-314.pyc +0 -0
  17. package/@opencode/agents/claude-reviewer.md +0 -82
  18. package/@opencode/agents/codex-reviewer.md +0 -83
  19. package/@opencode/agents/gemini-reviewer.md +0 -82
  20. package/@opencode/agents/gh-thread-reviewer.md +0 -122
  21. package/@opencode/agents/gh_review_harvest.py +0 -292
  22. package/@opencode/agents/pr-context.md +0 -82
  23. package/@opencode/agents/pr-fix.md +0 -243
  24. package/@opencode/agents/pr-precheck.md +0 -89
  25. package/@opencode/agents/pr-review-aggregate.md +0 -151
  26. package/@opencode/agents/pr_context.py +0 -351
  27. package/@opencode/agents/pr_precheck.py +0 -505
  28. package/@opencode/agents/pr_review_aggregate.py +0 -868
  29. package/@opencode/agents/test_pr_review_aggregate.py +0 -701
  30. package/@opencode/commands/doctor.md +0 -271
  31. package/@opencode/commands/git-commit-and-pr.md +0 -282
  32. package/@opencode/commands/git-release.md +0 -642
  33. package/@opencode/commands/oh_attach.json +0 -92
  34. package/@opencode/commands/opencode_attach.json +0 -29
  35. package/@opencode/commands/opencode_attach.py +0 -142
  36. package/@opencode/commands/pr-review-loop.md +0 -211
@@ -1,92 +0,0 @@
1
- {
2
- "sisyphus_agent": {
3
- "disabled": false,
4
- "default_builder_enabled": true,
5
- "planner_enabled": true,
6
- "replace_plan": false
7
- },
8
- "agents": {
9
- "sisyphus": {
10
- "model": "openai/gpt-5.3-codex",
11
- "variant": "none"
12
- },
13
- "oracle": {
14
- "model": "openai/gpt-5.3-codex",
15
- "variant": "high"
16
- },
17
- "librarian": {
18
- "model": "openai/gpt-5.3-codex"
19
- },
20
- "explore": {
21
- "model": "openai/gpt-5.3-codex"
22
- },
23
- "multimodal-looker": {
24
- "model": "openai/gpt-5.3-codex"
25
- },
26
- "prometheus": {
27
- "model": "gpt-5.3-codex",
28
- "variant": "max"
29
- },
30
- "metis": {
31
- "model": "gpt-5.3-codex",
32
- "variant": "max"
33
- },
34
- "momus": {
35
- "model": "openai/gpt-5.2",
36
- "variant": "medium"
37
- },
38
- "atlas": {
39
- "model": "openai/gpt-5.3-codex"
40
- },
41
- "codex-reviewer": {
42
- "model": "openai/gpt-5.3-codex",
43
- "variant": "xhigh",
44
- "temperature": 0.1
45
- },
46
- "gemini-reviewer": {
47
- "model": "openai/gpt-5.3-codex",
48
- "variant": "max"
49
- },
50
- "claude-reviewer": {
51
- "model": "openai/gpt-5.3-codex",
52
- "variant": "high"
53
- },
54
- "pr-fixer": {
55
- "model": "openai/gpt-5.3-codex",
56
- "variant": "xhigh",
57
- "temperature": 0.1
58
- }
59
- },
60
- "concurrency": 5,
61
- "categories": {
62
- "visual-engineering": {
63
- "model": "openai/gpt-5.3-codex",
64
- "variant": "high"
65
- },
66
- "ultrabrain": {
67
- "model": "openai/gpt-5.3-codex",
68
- "variant": "xhigh"
69
- },
70
- "artistry": {
71
- "model": "openai/gpt-5.3-codex",
72
- "variant": "max"
73
- },
74
- "quick": {
75
- "model": "openai/gpt-5.1-codex-min"
76
- },
77
- "middle": {
78
- "model": "openai/gpt-5.3-codex"
79
- },
80
- "unspecified-low": {
81
- "model": "openai/gpt-5.1-codex-min",
82
- "variant": "medium"
83
- },
84
- "unspecified-high": {
85
- "model": "openai/gpt-5.3-codex",
86
- "variant": "medium"
87
- },
88
- "writing": {
89
- "model": "openai/gpt-5.3-codex"
90
- }
91
- }
92
- }
@@ -1,29 +0,0 @@
1
- {
2
- "instructions": [
3
- "AGENTS.md",
4
- "ruler/**/*.md"
5
- ],
6
- "plugin": [
7
- "opencode-antigravity-auth",
8
- "oh-my-opencode",
9
- "opencode-openai-codex-auth"
10
- ],
11
- "agent": {
12
- "quick": {
13
- "model": "openai/gpt-5.1-codex-min"
14
- },
15
- "middle": {
16
- "model": "openai/gpt-5.3-codex"
17
- },
18
- "documenter": {
19
- "model": "openai/gpt-5.3-codex"
20
- }
21
- },
22
- "permission": {
23
- "read": {
24
- "*.env": "allow",
25
- "*.env.*": "allow"
26
- }
27
- },
28
- "model": "openai/gpt-5.3-codex"
29
- }
@@ -1,142 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- import argparse
4
- import json
5
- import os
6
- import shutil
7
- import sys
8
- import time
9
- from pathlib import Path
10
-
11
-
12
- def _is_plain_object(value):
13
- return isinstance(value, dict)
14
-
15
-
16
- def deep_merge_in_place(target, source):
17
- if not _is_plain_object(target) or not _is_plain_object(source):
18
- raise TypeError("deep_merge_in_place expects dicts")
19
-
20
- for key, s_val in source.items():
21
- if _is_plain_object(s_val):
22
- t_val = target.get(key)
23
- if not _is_plain_object(t_val):
24
- t_val = {}
25
- target[key] = t_val
26
- deep_merge_in_place(t_val, s_val)
27
- else:
28
- target[key] = s_val
29
-
30
- return target
31
-
32
-
33
- def load_json_file(path):
34
- raw = path.read_text(encoding="utf-8")
35
- return json.loads(raw)
36
-
37
-
38
- def atomic_write_json(path, data):
39
- path.parent.mkdir(parents=True, exist_ok=True)
40
- tmp_path = path.with_name(f"{path.name}.tmp.{os.getpid()}")
41
- serialized = json.dumps(data, ensure_ascii=False, indent=2) + "\n"
42
- tmp_path.write_text(serialized, encoding="utf-8")
43
- os.replace(tmp_path, path)
44
-
45
-
46
- def backup_file(path):
47
- if not path.exists():
48
- return None
49
- ts = time.strftime("%Y%m%d%H%M%S")
50
- bak = path.with_name(f"{path.name}.bak.{ts}")
51
- shutil.copy2(path, bak)
52
- return bak
53
-
54
-
55
- def attach(source_path, target_path, *, make_backup=True, dry_run=False):
56
- source = load_json_file(source_path)
57
- if not _is_plain_object(source):
58
- raise ValueError(f"Source JSON root must be an object: {source_path}")
59
-
60
- if target_path.exists():
61
- target = load_json_file(target_path)
62
- if not _is_plain_object(target):
63
- raise ValueError(f"Target JSON root must be an object: {target_path}")
64
- else:
65
- target = {}
66
-
67
- merged = deep_merge_in_place(target, source)
68
-
69
- bak = None
70
- if make_backup and target_path.exists() and not dry_run:
71
- bak = backup_file(target_path)
72
-
73
- if not dry_run:
74
- atomic_write_json(target_path, merged)
75
-
76
- return bak
77
-
78
-
79
- def main(argv):
80
- parser = argparse.ArgumentParser(
81
- description=(
82
- "Attach JSON fragments into OpenCode global config files. "
83
- "Rule: deep-merge objects; replace arrays/primitives; preserve other keys."
84
- )
85
- )
86
- parser.add_argument(
87
- "--oh-source",
88
- default=str(Path(__file__).resolve().parents[1] / "commands" / "oh_attach.json"),
89
- help="Path to oh_attach.json",
90
- )
91
- parser.add_argument(
92
- "--opencode-source",
93
- default=str(Path(__file__).resolve().parents[1] / "commands" / "opencode_attach.json"),
94
- help="Path to opencode_attach.json",
95
- )
96
- parser.add_argument(
97
- "--config-dir",
98
- default=str(Path.home() / ".config" / "opencode"),
99
- help="Config directory (default: ~/.config/opencode)",
100
- )
101
- parser.add_argument("--no-backup", action="store_true", help="Do not create .bak files")
102
- parser.add_argument("--dry-run", action="store_true", help="Do not write files")
103
-
104
- args = parser.parse_args(argv)
105
-
106
- config_dir = Path(os.path.expanduser(args.config_dir)).resolve()
107
- oh_target = config_dir / "oh-my-opencode.json"
108
- opencode_target = config_dir / "opencode.json"
109
-
110
- oh_source = Path(args.oh_source).resolve()
111
- opencode_source = Path(args.opencode_source).resolve()
112
-
113
- if not oh_source.exists():
114
- raise FileNotFoundError(f"Missing source file: {oh_source}")
115
- if not opencode_source.exists():
116
- raise FileNotFoundError(f"Missing source file: {opencode_source}")
117
-
118
- make_backup = not args.no_backup
119
- dry_run = args.dry_run
120
-
121
- bak1 = attach(oh_source, oh_target, make_backup=make_backup, dry_run=dry_run)
122
- bak2 = attach(opencode_source, opencode_target, make_backup=make_backup, dry_run=dry_run)
123
-
124
- if dry_run:
125
- print("DRY_RUN: no files written")
126
- return 0
127
-
128
- if bak1:
129
- print(f"backup: {bak1}")
130
- if bak2:
131
- print(f"backup: {bak2}")
132
- print(f"updated: {oh_target}")
133
- print(f"updated: {opencode_target}")
134
- return 0
135
-
136
-
137
- if __name__ == "__main__":
138
- try:
139
- raise SystemExit(main(sys.argv[1:]))
140
- except Exception as e:
141
- print(f"ERROR: {e}", file=sys.stderr)
142
- raise SystemExit(1)
@@ -1,211 +0,0 @@
1
- ---
2
- allowed-tools: [Bash, Read, Glob, TodoWrite, Edit, Grep, Task]
3
- description: '循环审核修复'
4
- ---
5
-
6
- # PR Review Loop
7
-
8
- ## Stacked PR / PR -> PR(重要)
9
-
10
- - 本流程的 diff 基准来自 GitHub PR 元数据的 `baseRefName`(不是硬编码 main/master),因此天然支持“PR 合并到另一个 PR 分支”的 stacked PR。
11
- - 当 `baseRefName` 缺失时:会回退到仓库默认分支(`defaultBranchRef.name`)。
12
- - 当 base 分支 fetch 失败时:会直接报错终止(不再静默回退到 main/master),避免 review/changed-files 基准悄悄跑偏。
13
-
14
- ## 输入
15
-
16
- - `{{PR_NUMBER}}`
17
- - `round`(默认 1,由调用者/循环控制)
18
-
19
- ## 唯一标识 runId(强制)
20
-
21
- - 全局唯一标识 `runId` 格式:`<PR>-<ROUND>-<HEAD_SHORT>`
22
- - 其中:
23
- - `<PR>`:PR 编号
24
- - `<ROUND>`:当前轮次
25
- - `<HEAD_SHORT>`:`headOid` 的前 7 位(git rev-parse --short HEAD)
26
- - 生成者:
27
- - 第 1 步 `pr-context` 负责计算并返回 `runId`(基于当前 checkout 的 headOid)
28
- - 后续所有步骤(reviewers, aggregate, fix)必须透传并使用该 `runId`
29
- - 禁止任何下游步骤自行生成或篡改 `runId`
30
-
31
- ## Cache 约定(强制)
32
-
33
- - 本流程所有中间文件都存放在项目内:`./.cache/`
34
- - agent/命令之间传递**repo 相对路径**(例如:`./.cache/pr-context-...md`),不要只传 basename
35
-
36
- ## 固定 subagent_type(直接用 Task 调用,不要反复确认)
37
-
38
- - `pr-precheck`
39
- - `pr-context`
40
- - `codex-reviewer`
41
-
42
- - `claude-reviewer`
43
- - `gemini-reviewer`
44
- - `gh-thread-reviewer`
45
- - `pr-review-aggregate`
46
- - `pr-fix`
47
-
48
-
49
- ## 预检
50
-
51
- 0. Task: `pr-precheck`(强制 gate:编译/预检必须先通过)
52
-
53
- - prompt 必须包含:`PR #{{PR_NUMBER}}`、`round: <ROUND>`(precheck 需计算并返回 runId,格式同 context)
54
- - 若返回 `{"error":"..."}`:立即终止本轮并回传错误
55
- - 若返回 `{"ok":false,"fixFile":"..."}`:
56
- - 预检阶段 runId 同样基于 `headOid` 生成(`<PR>-<ROUND>-<HEAD_SHORT>`),可直接传给 fix。
57
- - 最多修复 2 次(防止无限循环):
58
- - 第 1 次:Task `pr-fix`(传入 `fixFile`, `runId`, `round`)→ 再 Task `pr-precheck`
59
- - 若仍返回 `{"ok":false,"fixFile":"..."}`:第 2 次 Task `pr-fix` → 再 Task `pr-precheck`
60
- - 若仍不是 `{"ok":true}`:终止并回传错误(建议:`{"error":"PRECHECK_NOT_CLEAN_AFTER_FIX"}`)
61
- - 注意:预检失败产生的修复也应记录在 Decision Log 中(essence: `__precheck__` 或具体错误信息,file: `__precheck__`),以便后续追踪。
62
-
63
- ## 循环(最多 3 轮)
64
-
65
- **⚠️ 严格串行执行要求(Critical)**:
66
-
67
- - 每个 Step 必须完成(收到返回值)后才能开始下一个 Step
68
- - **禁止任何步骤并行执行**(除了 Step 2 的三个 reviewer 可并行)
69
- - 如果任何步骤失败或超时,必须立即终止当前轮次,不能跳过或重试
70
- - 每个步骤的 Task 调用必须 await 返回结果,不能 fire-and-forget
71
-
72
- 每轮按顺序执行:
73
-
74
- 1. Task: `pr-context` **(必须先完成,不可与 Step 2 并行)**
75
-
76
- - prompt 必须包含:`PR #{{PR_NUMBER}}`、`round: <ROUND>`
77
- - 若返回 `{"error":"..."}`:立即终止本轮并回传错误(不再调用 reviewers)
78
- - 取出:`contextFile`、`runId`、`headOid`
79
- - **runId 校验**:确认返回的 `runId` 符合 `<PR>-<ROUND>-<HEAD_SHORT>` 格式
80
- - **CRITICAL**: 必须等待此 Task 成功完成并获取到 `contextFile` 后,才能进入 Step 2
81
-
82
- **检查 Decision Log**:
83
- - 检查是否存在 `./.cache/decision-log-pr{{PR_NUMBER}}.md`
84
- - 如存在,将路径记录为 `decisionLogFile`(用于后续步骤)
85
- - 如不存在,`decisionLogFile` 为空或不传递
86
-
87
- 2. Task(并行): `codex-reviewer` + `claude-reviewer` + `gemini-reviewer` + `gh-thread-reviewer` **(依赖 Step 1 的 contextFile 和 decisionLogFile)**
88
-
89
- - **DEPENDENCY**: 这些 reviewers 依赖 Step 1 返回的 `contextFile` 和 `decisionLogFile`(如存在),因此**必须等 Step 1 完成后才能并行启动**
90
- - 每个 reviewer prompt 必须包含:
91
- - `PR #{{PR_NUMBER}}`
92
- - `round: <ROUND>`
93
- - `runId: <RUN_ID>`(来自 Step 1 的输出,必须透传,禁止自行生成)
94
- - `contextFile: ./.cache/<file>.md`(来自 Step 1 的输出)
95
- - `decisionLogFile: ./.cache/decision-log-pr{{PR_NUMBER}}.md`(如存在)
96
- - reviewer 默认读 `contextFile`;如果 `decisionLogFile` 存在,reviewer 应在 prompt 中提供该文件路径以参考前轮决策;必要时允许用 `git/gh` 只读命令拿 diff
97
- - 忽略问题:1.格式化代码引起的噪音 2.已经lint检查以外的格式问题 3.忽略单元测试不足的问题
98
- - 对于重构代码,不必考虑和以前版本数据的兼容性问题,直接切换就行
99
- - 特别关注: 逻辑、安全、性能、可维护性
100
- - 遵守 Decision Log:
101
- - 已修复(Fixed):不再提
102
- - 已拒绝(Rejected):除非优先级升级(P_new - P_old >= 2),否则不再提
103
- - 任何新发现必须基于当前 `runId` 对应的代码状态
104
- - 每个 reviewer 输出:`reviewFile: ./.cache/<file>.md`(Markdown)
105
-
106
- 3. Task: `pr-review-aggregate`
107
-
108
- - prompt 必须包含:`PR #{{PR_NUMBER}}`、`round: <ROUND>`、`runId: <RUN_ID>`、`contextFile: ./.cache/<file>.md`、以及 1+ 条 `reviewFile: ./.cache/<file>.md`、以及 `decisionLogFile: ./.cache/decision-log-pr{{PR_NUMBER}}.md`(如存在)
109
- - 输出:`{"stop":true}` 或 `{"stop":false,"fixFile":"..."}`
110
- - 若 `stop=true`:本轮结束并退出循环
111
- - **唯一性约束**: 每轮只能发布一次 Review Summary;脚本内置幂等检查,重复调用不会重复发布
112
- - 智能聚合:
113
- - 使用 LLM 对比 decision-log 中的 `essence` 与新 finding
114
- - 仅当问题本质相同且优先级 delta < 2 时,自动归为 Repeated/Ignored
115
- - 否则视为 New Issue 或 Escalation
116
-
117
- 4. Task: `pr-fix`
118
-
119
- - prompt 必须包含:
120
- - `PR #{{PR_NUMBER}}`
121
- - `round: <ROUND>`
122
- - `runId: <RUN_ID>`(来自 Step 1 的输出,必须透传,禁止自行生成)
123
- - `fixFile: ./.cache/<file>.md`
124
- - 约定:`pr-fix` 对每个 findingId 单独 commit + push(一个 findingId 一个 commit),结束后再 `git push` 兜底
125
- - 决策记录:
126
- - 修复成功:追加 Fixed 记录(含 `essence`)到 Decision Log
127
- - 拒绝/无法修复:追加 Rejected 记录(含 `reason`, `essence`)到 Decision Log
128
- - 范围限制:essence 匹配必须在 **同一个文件** 内(不支持跨文件/重命名追踪)
129
- - pr-fix 输出:`fixReportFile: ./.cache/<file>.md`(Markdown)
130
-
131
-
132
- 5. Task: `pr-review-aggregate`(发布修复评论)
133
-
134
- - prompt 必须包含:`PR #{{PR_NUMBER}}`、`round: <ROUND>`、`runId: <RUN_ID>`、`fixReportFile: ./.cache/<file>.md`
135
- - 输出:`{"ok":true}`
136
- - **唯一性约束**: 每轮只能发布一次 Fix Report;脚本内置幂等检查,重复调用不会重复发布
137
-
138
- **Decision Log 更新**:
139
- - `pr-fix` agent 在修复过程中会在 `./.cache/decision-log-pr{{PR_NUMBER}}.md` 中追加本轮决策(Fixed/Rejected)
140
- - 下一轮 review 将自动使用更新后的 decision-log,避免重复提出已决策问题
141
-
142
- 6. 下一轮
143
-
144
- - 回到 1(进入下一轮 reviewers)
145
-
146
- ## 本地验证(脚本直跑)
147
-
148
- ```bash
149
- # 0) 先确保 gh 已认证(host 从 git remote origin 推断;必要时用 --hostname)
150
- gh auth status
151
-
152
- # 1) precheck(round 1)
153
- python3 "~/.opencode/agents/pr_precheck.py" --pr <PR_NUMBER> --round 1
154
-
155
- # 2) context(round 1)
156
- python3 "~/.opencode/agents/pr_context.py" --pr <PR_NUMBER> --round 1
157
-
158
- # 3) 校验:两者都必须输出单行 JSON,且 runId 必须一致
159
- python3 "~/.opencode/agents/pr_precheck.py" --pr <PR_NUMBER> --round 1 > ./.cache/_precheck.json
160
- python3 "~/.opencode/agents/pr_context.py" --pr <PR_NUMBER> --round 1 > ./.cache/_context.json
161
- python3 - <<'PY'
162
- import json
163
- p=json.load(open('./.cache/_precheck.json'))
164
- c=json.load(open('./.cache/_context.json'))
165
- assert p.get('runId') == c.get('runId'), (p.get('runId'), c.get('runId'))
166
- print('OK', p.get('runId'))
167
- PY
168
-
169
- # 4) 运行脚本相关测试(注意:pytest 把 @ 当作 argfile;必须加 ./ 并加引号)
170
- python3 -m pytest -q "~/.opencode/agents/test_pr_review_aggregate.py"
171
- ```
172
-
173
- ## 终止与收尾(强制)
174
-
175
- 循环结束时,必须发布一个最终评论到 PR,格式如下:
176
-
177
- ### 情况 A: 所有问题已解决(stop=true)
178
-
179
- 当 Step 3 返回 `{"stop":true}` 时,调用 `pr-review-aggregate` 发布收尾评论:
180
-
181
- - prompt 必须包含:
182
- - `PR #{{PR_NUMBER}}`
183
- - `round: <ROUND>`
184
- - `runId: <RUN_ID>`
185
- - `--final-report "RESOLVED"`(新增参数,表示所有问题已解决)
186
-
187
- ### 情况 B: 达到最大轮次(3 轮后仍有问题)
188
-
189
- 当循环完成 3 轮后仍未 stop,调用 `pr-review-aggregate` 发布收尾评论:
190
-
191
- - prompt 必须包含:
192
- - `PR #{{PR_NUMBER}}`
193
- - `round: 3`
194
- - `runId: <RUN_ID>`
195
- - `--final-report "MAX_ROUNDS_REACHED"`(新增参数,表示达到最大轮次)
196
-
197
- ### 最终评论格式(由脚本生成)
198
-
199
- ```markdown
200
- <!-- pr-review-loop-marker -->
201
-
202
- ## Final Report
203
-
204
- - PR: #<PR_NUMBER>
205
- - Total Rounds: <N>
206
- - Status: ✅ All issues resolved / ⚠️ Max rounds reached (some issues may remain)
207
-
208
- ### Summary
209
-
210
- [自动生成的总结]
211
- ```