@ranger1/dx 0.1.69 → 0.1.71
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/codex/agents/fixer.toml +38 -0
- package/codex/agents/orchestrator.toml +9 -0
- package/codex/agents/reviewer.toml +39 -0
- package/codex/agents/spark.toml +21 -0
- package/codex/skills/doctor/SKILL.md +9 -2
- package/codex/skills/doctor/scripts/doctor.sh +261 -2
- package/codex/skills/pr-review-loop/SKILL.md +199 -0
- package/codex/skills/pr-review-loop/agents/openai.yaml +4 -0
- package/codex/skills/pr-review-loop/references/agents/logic-reviewer.md +22 -0
- package/codex/skills/pr-review-loop/references/agents/pr-context.md +73 -0
- package/codex/skills/pr-review-loop/references/agents/pr-precheck.md +152 -0
- package/codex/skills/pr-review-loop/references/agents/pr-review-aggregate.md +152 -0
- package/codex/skills/pr-review-loop/references/agents/security-reviewer.md +21 -0
- package/codex/skills/pr-review-loop/references/agents/style-reviewer.md +22 -0
- package/codex/skills/pr-review-loop/references/skill-layout.md +25 -0
- package/codex/skills/pr-review-loop/scripts/gh_review_harvest.py +292 -0
- package/codex/skills/pr-review-loop/scripts/pr_context.py +351 -0
- package/codex/skills/pr-review-loop/scripts/pr_review_aggregate.py +866 -0
- package/codex/skills/pr-review-loop/scripts/test_pr_review_aggregate.py +751 -0
- package/lib/cli/help.js +1 -1
- package/lib/opencode-initial.js +11 -5
- package/package.json +1 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
model = "gpt-5.3-codex"
|
|
2
|
+
model_reasoning_effort = "medium"
|
|
3
|
+
approval_policy = "never"
|
|
4
|
+
sandbox_mode = "workspace-write"
|
|
5
|
+
network_access = true
|
|
6
|
+
|
|
7
|
+
developer_instructions = '''
|
|
8
|
+
你是 fix 代理。
|
|
9
|
+
|
|
10
|
+
输入必须包含:PR 编号、round、runId、fixFile。
|
|
11
|
+
缓存目录固定为 `./.cache/`,路径一律使用 `./.cache/<file>`(禁止 basename-only)。
|
|
12
|
+
|
|
13
|
+
快速失败(缺失即报错并退出):
|
|
14
|
+
- 缺 PR 编号:`{"error":"MISSING_PR_NUMBER"}`
|
|
15
|
+
- 缺 fixFile:`{"error":"MISSING_FIX_FILE"}`
|
|
16
|
+
- fixFile 不可读:`{"error":"FIX_FILE_NOT_READABLE"}`
|
|
17
|
+
- fixFile 无法解析 `IssuesToFix`:`{"error":"INVALID_FIX_FILE"}`
|
|
18
|
+
|
|
19
|
+
强制规则:
|
|
20
|
+
1. 仅处理 fixFile 中问题:`IssuesToFix` 必修;`OptionalIssues` 可修可拒绝(需写 reason),禁止范围外改动。
|
|
21
|
+
2. 每个 findingId 单独一个 commit,提交信息必须包含 findingId(示例:`fix(pr #<PR>): <FINDING_ID> <title>`)。
|
|
22
|
+
3. 每次提交后 push(首次无 upstream 可 `git push -u origin HEAD`),全部处理完再 `git push` 兜底。
|
|
23
|
+
4. 无法修复的项必须写明 reason,并记入 Rejected。
|
|
24
|
+
5. 必须维护 `./.cache/decision-log-pr<PR>.md`:按轮次追加 Fixed/Rejected,禁止覆盖历史。
|
|
25
|
+
6. Decision Log 每条必须包含:id、file、essence;预检修复允许 `file: __precheck__`。
|
|
26
|
+
7. 修复后执行 `dx lint` 与 `dx build all`;失败则不得声称完成。
|
|
27
|
+
8. 生成 `./.cache/fix-report-pr<PR>-r<ROUND>-<RUN_ID>.md`(可直接用于 PR 评论,且不包含本地绝对路径)。
|
|
28
|
+
9. 禁止执行 GitHub 评论发布动作(如 `gh pr comment` / `gh pr review`),发布由编排器负责。
|
|
29
|
+
10. 修改 `json/jsonc` 文件时必须使用脚本方式(如 python)改写,禁止手工字符串拼接导致格式损坏。
|
|
30
|
+
11. 最终响应只输出一行:`fixReportFile: ./.cache/<file>.md`;失败只输出一行 JSON 错误。
|
|
31
|
+
|
|
32
|
+
推送失败处理(强制):
|
|
33
|
+
1. 若 `git push` 失败,先判断是否为网络类失败(如 DNS 解析失败、连接拒绝、超时)。
|
|
34
|
+
2. 网络类失败必须重试当前 push 最多 2 次(总 3 次尝试),退避 2s / 5s。
|
|
35
|
+
3. 每次重试前执行轻量检查:`gh auth status`、`git remote -v`、`git ls-remote origin -h`。
|
|
36
|
+
4. 若重试后仍失败,必须返回结构化错误:`{"error":"GIT_PUSH_FAILED_NETWORK","step":"push","detail":"..."}`。
|
|
37
|
+
5. 禁止在 push 未成功时伪造“已完成修复”或输出成功 fixReportFile。
|
|
38
|
+
'''
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
model = "gpt-5.3-codex"
|
|
2
|
+
model_reasoning_effort = "medium"
|
|
3
|
+
approval_policy = "never"
|
|
4
|
+
sandbox_mode = "workspace-write"
|
|
5
|
+
network_access = true
|
|
6
|
+
|
|
7
|
+
developer_instructions = '''
|
|
8
|
+
你是 reviewer 代理。
|
|
9
|
+
|
|
10
|
+
输入必须包含:PR 编号、round、runId、contextFile、reviewerPromptFile(可选 decisionLogFile)。
|
|
11
|
+
|
|
12
|
+
强制规则:
|
|
13
|
+
1. 默认已位于 PR head 对应分支:优先直接读取工作区代码;必要时可用只读 git/gh 命令补充上下文。
|
|
14
|
+
2. 只基于当前代码与 contextFile 评审,禁止凭空猜测不存在的实现。
|
|
15
|
+
3. 忽略:纯格式化噪音、lint 已覆盖的格式问题、单测数量不足本身。
|
|
16
|
+
4. 必须先读取 `reviewerPromptFile` 并严格执行其中定义的“角色码 / 专责范围 / 专属审核词”。
|
|
17
|
+
- 若 `reviewerPromptFile` 不存在、不可读或不在 `${CODEX_HOME:-$HOME/.codex}/skills/pr-review-loop/references/agents/` 下,返回 `{"error":"INVALID_REVIEWER_PROMPT_FILE"}`。
|
|
18
|
+
- 若本文件规则与 `reviewerPromptFile` 有冲突,以 `reviewerPromptFile` 为准。
|
|
19
|
+
- reviewer 只负责当前 `reviewerPromptFile` 指定的审查视角,禁止越权扩展到其他视角。
|
|
20
|
+
5. runId 仅允许透传(格式 `<PR>-<ROUND>-<HEAD_SHORT>`),禁止自行重算或改写。
|
|
21
|
+
6. 决策日志约束(当提供 decisionLogFile 时强制生效):
|
|
22
|
+
- Fixed 不再重复提出。
|
|
23
|
+
- Rejected 除非优先级升级 >=2,否则不再提出。
|
|
24
|
+
- 匹配时必须 file 一致,不做跨文件/重命名追踪。
|
|
25
|
+
- 不质疑已修复问题的实现方式(除非发现修复引入新 bug)。
|
|
26
|
+
7. 缓存约定:统一使用 `./.cache/`,交接路径必须是 repo 相对路径(`./.cache/<file>`),禁止 basename-only。
|
|
27
|
+
8. 输出文件命名:`./.cache/review-<ROLE_CODE>-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`。
|
|
28
|
+
- `<ROLE_CODE>` 由 `reviewerPromptFile` 提供(例如 SEC / LOG / STY)。
|
|
29
|
+
- findings 的 `id` 前缀必须是 `<ROLE_CODE>-`。
|
|
30
|
+
9. reviewFile 内容必须是以下结构(字段名不可改):
|
|
31
|
+
- `# Review (<ROLE_CODE>)`
|
|
32
|
+
- `PR: <PR_NUMBER>`
|
|
33
|
+
- `Round: <ROUND>`
|
|
34
|
+
- `## Summary`,含 `P0/P1/P2/P3` 四行计数
|
|
35
|
+
- `## Findings`,每条 finding 包含:
|
|
36
|
+
`id / priority / category / file / line / title / description / suggestion`
|
|
37
|
+
10. 最终响应只输出一行:`reviewFile: ./.cache/<file>.md`。
|
|
38
|
+
|
|
39
|
+
'''
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
model = "gpt-5.3-codex"
|
|
2
|
+
model_reasoning_effort = "medium"
|
|
3
|
+
approval_policy = "never"
|
|
4
|
+
sandbox_mode = "workspace-write"
|
|
5
|
+
network_access = true
|
|
6
|
+
|
|
7
|
+
developer_instructions = '''
|
|
8
|
+
你是一个通用agent 根据输入的提示词,完整遵循他的要求执行任务
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
输入必须包含:
|
|
12
|
+
--prompt 提示词字符串,要求严格按照提示词执行
|
|
13
|
+
--others 其他参数根据提示词要求
|
|
14
|
+
|
|
15
|
+
强制规则:
|
|
16
|
+
如果传入的prompt是一个文件路径,必须读取文件内容作为提示词,否则直接使用传入的字符串作为提示词。
|
|
17
|
+
prompt是一个文件路径并不存在时,必须返回结构化错误:{"error":"PROMPT_FILE_NOT_FOUND","detail":"<file_path>"}
|
|
18
|
+
|
|
19
|
+
'''
|
|
20
|
+
|
|
21
|
+
|
|
@@ -33,11 +33,18 @@ bash "$CODEX_HOME/skills/doctor/scripts/doctor.sh" --max-rounds 3
|
|
|
33
33
|
|
|
34
34
|
## 脚本职责
|
|
35
35
|
|
|
36
|
-
- 并行检测:`python3`、`python` 别名、`pnpm`、`dx`、`agent-browser`、`rg`、`multi_agent
|
|
36
|
+
- 并行检测:`python3`、`python` 别名、`pnpm`、`dx`、`agent-browser`、`rg`、`multi_agent`、`~/.codex/config.toml` 关键配置。
|
|
37
37
|
- 自动修复:按平台选择安装器修复缺失项。
|
|
38
|
+
- 自动修复:确保 `~/.codex/config.toml` 含以下目标值(缺失补齐、值不符覆盖):
|
|
39
|
+
- `[features] multi_agent = true`
|
|
40
|
+
- `[agents] max_threads = 15`
|
|
41
|
+
- `[agents.fixer] description/model_reasoning_effort/config_file`
|
|
42
|
+
- `[agents.orchestrator] description/config_file`
|
|
43
|
+
- `[agents.reviewer] description/config_file`
|
|
44
|
+
- `[agents.spark] description/config_file`
|
|
38
45
|
- 强制执行:每轮都运行 `pnpm add -g @ranger1/dx@latest && dx initial`。
|
|
39
46
|
- agent-browser:安装/升级并执行 Chromium 安装。
|
|
40
|
-
-
|
|
47
|
+
- 结果输出:展示每项状态、版本、关键信息(含 `codex_config` 检测项);全部通过则退出 0,否则最多三轮后退出 1。
|
|
41
48
|
|
|
42
49
|
## 注意
|
|
43
50
|
|
|
@@ -21,6 +21,7 @@ fi
|
|
|
21
21
|
|
|
22
22
|
CACHE_ROOT="${PWD}/.cache/doctor"
|
|
23
23
|
mkdir -p "$CACHE_ROOT"
|
|
24
|
+
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
|
|
24
25
|
|
|
25
26
|
LAST_CHECK_DIR=""
|
|
26
27
|
DX_FORCE_OK=0
|
|
@@ -177,6 +178,251 @@ ensure_multi_agent() {
|
|
|
177
178
|
echo "$line" | grep -E "experimental[[:space:]]+true" >/dev/null 2>&1
|
|
178
179
|
}
|
|
179
180
|
|
|
181
|
+
ensure_codex_config() {
|
|
182
|
+
local cfg_dir cfg_file tmp_file
|
|
183
|
+
cfg_dir="$CODEX_HOME"
|
|
184
|
+
cfg_file="$cfg_dir/config.toml"
|
|
185
|
+
tmp_file="$cfg_file.tmp.$$"
|
|
186
|
+
|
|
187
|
+
mkdir -p "$cfg_dir"
|
|
188
|
+
if [[ ! -f "$cfg_file" ]]; then
|
|
189
|
+
: >"$cfg_file"
|
|
190
|
+
fi
|
|
191
|
+
|
|
192
|
+
awk '
|
|
193
|
+
BEGIN {
|
|
194
|
+
in_features=0; in_agents=0; in_fixer=0; in_orch=0; in_reviewer=0; in_spark=0;
|
|
195
|
+
features_emitted=0; agents_emitted=0; fixer_emitted=0; orch_emitted=0; reviewer_emitted=0; spark_emitted=0;
|
|
196
|
+
features_multi_written=0; agents_max_threads_written=0;
|
|
197
|
+
fixer_desc_written=0; fixer_reasoning_written=0; fixer_cfg_written=0;
|
|
198
|
+
orch_desc_written=0; orch_cfg_written=0;
|
|
199
|
+
reviewer_desc_written=0; reviewer_cfg_written=0;
|
|
200
|
+
spark_desc_written=0; spark_cfg_written=0;
|
|
201
|
+
}
|
|
202
|
+
function trim(s) { gsub(/^[[:space:]]+|[[:space:]]+$/, "", s); return s }
|
|
203
|
+
function reset_sections() {
|
|
204
|
+
in_features=0; in_agents=0; in_fixer=0; in_orch=0; in_reviewer=0; in_spark=0;
|
|
205
|
+
}
|
|
206
|
+
function flush_features() {
|
|
207
|
+
if (!features_emitted) return;
|
|
208
|
+
if (!features_multi_written) print "multi_agent = true";
|
|
209
|
+
}
|
|
210
|
+
function flush_agents() {
|
|
211
|
+
if (!agents_emitted) return;
|
|
212
|
+
if (!agents_max_threads_written) print "max_threads = 15";
|
|
213
|
+
}
|
|
214
|
+
function flush_fixer() {
|
|
215
|
+
if (!fixer_emitted) return;
|
|
216
|
+
if (!fixer_desc_written) print "description = \"bug fixer\"";
|
|
217
|
+
if (!fixer_reasoning_written) print "model_reasoning_effort = \"medium\"";
|
|
218
|
+
if (!fixer_cfg_written) print "config_file = \"~/.codex/agents/fixer.toml\"";
|
|
219
|
+
}
|
|
220
|
+
function flush_orch() {
|
|
221
|
+
if (!orch_emitted) return;
|
|
222
|
+
if (!orch_desc_written) print "description = \"orchestrator\"";
|
|
223
|
+
if (!orch_cfg_written) print "config_file = \"~/.codex/agents/orchestrator.toml\"";
|
|
224
|
+
}
|
|
225
|
+
function flush_reviewer() {
|
|
226
|
+
if (!reviewer_emitted) return;
|
|
227
|
+
if (!reviewer_desc_written) print "description = \"reviewer\"";
|
|
228
|
+
if (!reviewer_cfg_written) print "config_file = \"~/.codex/agents/reviewer.toml\"";
|
|
229
|
+
}
|
|
230
|
+
function flush_spark() {
|
|
231
|
+
if (!spark_emitted) return;
|
|
232
|
+
if (!spark_desc_written) print "description = \"spark\"";
|
|
233
|
+
if (!spark_cfg_written) print "config_file = \"~/.codex/agents/spark.toml\"";
|
|
234
|
+
}
|
|
235
|
+
function flush_active_section() {
|
|
236
|
+
if (in_features) flush_features();
|
|
237
|
+
if (in_agents) flush_agents();
|
|
238
|
+
if (in_fixer) flush_fixer();
|
|
239
|
+
if (in_orch) flush_orch();
|
|
240
|
+
if (in_reviewer) flush_reviewer();
|
|
241
|
+
if (in_spark) flush_spark();
|
|
242
|
+
}
|
|
243
|
+
function enter_section(line) {
|
|
244
|
+
if (line == "[features]") {
|
|
245
|
+
in_features=1; features_emitted=1;
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (line == "[agents]") {
|
|
249
|
+
in_agents=1; agents_emitted=1;
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (line == "[agents.fixer]") {
|
|
253
|
+
in_fixer=1; fixer_emitted=1;
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (line == "[agents.orchestrator]") {
|
|
257
|
+
in_orch=1; orch_emitted=1;
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (line == "[agents.reviewer]") {
|
|
261
|
+
in_reviewer=1; reviewer_emitted=1;
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (line == "[agents.spark]") {
|
|
265
|
+
in_spark=1; spark_emitted=1;
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
{
|
|
270
|
+
line=$0;
|
|
271
|
+
t=trim(line);
|
|
272
|
+
if (match(t, /^\[[^]]+\]$/)) {
|
|
273
|
+
flush_active_section();
|
|
274
|
+
reset_sections();
|
|
275
|
+
enter_section(t);
|
|
276
|
+
print line;
|
|
277
|
+
next;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (in_features && match(t, /^multi_agent[[:space:]]*=/)) {
|
|
281
|
+
if (!features_multi_written) {
|
|
282
|
+
print "multi_agent = true";
|
|
283
|
+
features_multi_written=1;
|
|
284
|
+
}
|
|
285
|
+
next;
|
|
286
|
+
}
|
|
287
|
+
if (in_agents && match(t, /^max_threads[[:space:]]*=/)) {
|
|
288
|
+
if (!agents_max_threads_written) {
|
|
289
|
+
print "max_threads = 15";
|
|
290
|
+
agents_max_threads_written=1;
|
|
291
|
+
}
|
|
292
|
+
next;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (in_fixer && match(t, /^description[[:space:]]*=/)) {
|
|
296
|
+
if (!fixer_desc_written) { print "description = \"bug fixer\""; fixer_desc_written=1; }
|
|
297
|
+
next;
|
|
298
|
+
}
|
|
299
|
+
if (in_fixer && match(t, /^model_reasoning_effort[[:space:]]*=/)) {
|
|
300
|
+
if (!fixer_reasoning_written) { print "model_reasoning_effort = \"medium\""; fixer_reasoning_written=1; }
|
|
301
|
+
next;
|
|
302
|
+
}
|
|
303
|
+
if (in_fixer && match(t, /^config_file[[:space:]]*=/)) {
|
|
304
|
+
if (!fixer_cfg_written) { print "config_file = \"~/.codex/agents/fixer.toml\""; fixer_cfg_written=1; }
|
|
305
|
+
next;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (in_orch && match(t, /^description[[:space:]]*=/)) {
|
|
309
|
+
if (!orch_desc_written) { print "description = \"orchestrator\""; orch_desc_written=1; }
|
|
310
|
+
next;
|
|
311
|
+
}
|
|
312
|
+
if (in_orch && match(t, /^config_file[[:space:]]*=/)) {
|
|
313
|
+
if (!orch_cfg_written) { print "config_file = \"~/.codex/agents/orchestrator.toml\""; orch_cfg_written=1; }
|
|
314
|
+
next;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (in_reviewer && match(t, /^description[[:space:]]*=/)) {
|
|
318
|
+
if (!reviewer_desc_written) { print "description = \"reviewer\""; reviewer_desc_written=1; }
|
|
319
|
+
next;
|
|
320
|
+
}
|
|
321
|
+
if (in_reviewer && match(t, /^config_file[[:space:]]*=/)) {
|
|
322
|
+
if (!reviewer_cfg_written) { print "config_file = \"~/.codex/agents/reviewer.toml\""; reviewer_cfg_written=1; }
|
|
323
|
+
next;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (in_spark && match(t, /^description[[:space:]]*=/)) {
|
|
327
|
+
if (!spark_desc_written) { print "description = \"spark\""; spark_desc_written=1; }
|
|
328
|
+
next;
|
|
329
|
+
}
|
|
330
|
+
if (in_spark && match(t, /^config_file[[:space:]]*=/)) {
|
|
331
|
+
if (!spark_cfg_written) { print "config_file = \"~/.codex/agents/spark.toml\""; spark_cfg_written=1; }
|
|
332
|
+
next;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
print line;
|
|
336
|
+
}
|
|
337
|
+
END {
|
|
338
|
+
flush_active_section();
|
|
339
|
+
|
|
340
|
+
if (!features_emitted) {
|
|
341
|
+
print "";
|
|
342
|
+
print "[features]";
|
|
343
|
+
print "multi_agent = true";
|
|
344
|
+
}
|
|
345
|
+
if (!agents_emitted) {
|
|
346
|
+
print "";
|
|
347
|
+
print "[agents]";
|
|
348
|
+
print "max_threads = 15";
|
|
349
|
+
}
|
|
350
|
+
if (!fixer_emitted) {
|
|
351
|
+
print "";
|
|
352
|
+
print "[agents.fixer]";
|
|
353
|
+
print "description = \"bug fixer\"";
|
|
354
|
+
print "model_reasoning_effort = \"medium\"";
|
|
355
|
+
print "config_file = \"~/.codex/agents/fixer.toml\"";
|
|
356
|
+
}
|
|
357
|
+
if (!orch_emitted) {
|
|
358
|
+
print "";
|
|
359
|
+
print "[agents.orchestrator]";
|
|
360
|
+
print "description = \"orchestrator\"";
|
|
361
|
+
print "config_file = \"~/.codex/agents/orchestrator.toml\"";
|
|
362
|
+
}
|
|
363
|
+
if (!reviewer_emitted) {
|
|
364
|
+
print "";
|
|
365
|
+
print "[agents.reviewer]";
|
|
366
|
+
print "description = \"reviewer\"";
|
|
367
|
+
print "config_file = \"~/.codex/agents/reviewer.toml\"";
|
|
368
|
+
}
|
|
369
|
+
if (!spark_emitted) {
|
|
370
|
+
print "";
|
|
371
|
+
print "[agents.spark]";
|
|
372
|
+
print "description = \"spark\"";
|
|
373
|
+
print "config_file = \"~/.codex/agents/spark.toml\"";
|
|
374
|
+
}
|
|
375
|
+
}' "$cfg_file" >"$tmp_file"
|
|
376
|
+
|
|
377
|
+
mv "$tmp_file" "$cfg_file"
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
check_codex_config() {
|
|
381
|
+
local cfg_file
|
|
382
|
+
cfg_file="$CODEX_HOME/config.toml"
|
|
383
|
+
[[ -f "$cfg_file" ]] || return 1
|
|
384
|
+
|
|
385
|
+
awk '
|
|
386
|
+
BEGIN {
|
|
387
|
+
in_features=0; in_agents=0; in_fixer=0; in_orch=0; in_reviewer=0; in_spark=0;
|
|
388
|
+
ok_features=0; ok_threads=0; ok_fixer_desc=0; ok_fixer_reason=0; ok_fixer_cfg=0;
|
|
389
|
+
ok_orch_desc=0; ok_orch_cfg=0; ok_reviewer_desc=0; ok_reviewer_cfg=0; ok_spark_desc=0; ok_spark_cfg=0;
|
|
390
|
+
}
|
|
391
|
+
function trim(s) { gsub(/^[[:space:]]+|[[:space:]]+$/, "", s); return s }
|
|
392
|
+
{
|
|
393
|
+
line=trim($0);
|
|
394
|
+
sub(/[[:space:]]+#.*$/, "", line);
|
|
395
|
+
if (line ~ /^\[[^]]+\]$/) {
|
|
396
|
+
in_features=(line=="[features]");
|
|
397
|
+
in_agents=(line=="[agents]");
|
|
398
|
+
in_fixer=(line=="[agents.fixer]");
|
|
399
|
+
in_orch=(line=="[agents.orchestrator]");
|
|
400
|
+
in_reviewer=(line=="[agents.reviewer]");
|
|
401
|
+
in_spark=(line=="[agents.spark]");
|
|
402
|
+
next;
|
|
403
|
+
}
|
|
404
|
+
if (in_features && line ~ /^multi_agent[[:space:]]*=[[:space:]]*true$/) ok_features=1;
|
|
405
|
+
if (in_agents && line ~ /^max_threads[[:space:]]*=[[:space:]]*15$/) ok_threads=1;
|
|
406
|
+
if (in_fixer && line ~ /^description[[:space:]]*=[[:space:]]*"bug fixer"$/) ok_fixer_desc=1;
|
|
407
|
+
if (in_fixer && line ~ /^model_reasoning_effort[[:space:]]*=[[:space:]]*"medium"$/) ok_fixer_reason=1;
|
|
408
|
+
if (in_fixer && line ~ /^config_file[[:space:]]*=[[:space:]]*"~\/\.codex\/agents\/fixer\.toml"$/) ok_fixer_cfg=1;
|
|
409
|
+
if (in_orch && line ~ /^description[[:space:]]*=[[:space:]]*"orchestrator"$/) ok_orch_desc=1;
|
|
410
|
+
if (in_orch && line ~ /^config_file[[:space:]]*=[[:space:]]*"~\/\.codex\/agents\/orchestrator\.toml"$/) ok_orch_cfg=1;
|
|
411
|
+
if (in_reviewer && line ~ /^description[[:space:]]*=[[:space:]]*"reviewer"$/) ok_reviewer_desc=1;
|
|
412
|
+
if (in_reviewer && line ~ /^config_file[[:space:]]*=[[:space:]]*"~\/\.codex\/agents\/reviewer\.toml"$/) ok_reviewer_cfg=1;
|
|
413
|
+
if (in_spark && line ~ /^description[[:space:]]*=[[:space:]]*"spark"$/) ok_spark_desc=1;
|
|
414
|
+
if (in_spark && line ~ /^config_file[[:space:]]*=[[:space:]]*"~\/\.codex\/agents\/spark\.toml"$/) ok_spark_cfg=1;
|
|
415
|
+
}
|
|
416
|
+
END {
|
|
417
|
+
ok = ok_features && ok_threads &&
|
|
418
|
+
ok_fixer_desc && ok_fixer_reason && ok_fixer_cfg &&
|
|
419
|
+
ok_orch_desc && ok_orch_cfg &&
|
|
420
|
+
ok_reviewer_desc && ok_reviewer_cfg &&
|
|
421
|
+
ok_spark_desc && ok_spark_cfg;
|
|
422
|
+
exit(ok ? 0 : 1);
|
|
423
|
+
}' "$cfg_file"
|
|
424
|
+
}
|
|
425
|
+
|
|
180
426
|
force_dx() {
|
|
181
427
|
if ! install_pnpm; then
|
|
182
428
|
DX_FORCE_OK=0
|
|
@@ -287,12 +533,20 @@ run_parallel_checks() {
|
|
|
287
533
|
fi
|
|
288
534
|
) &
|
|
289
535
|
|
|
536
|
+
(
|
|
537
|
+
if check_codex_config; then
|
|
538
|
+
write_check_file "codex_config" "1" "$CODEX_HOME/config.toml" "ok" "$dir/codex_config.res"
|
|
539
|
+
else
|
|
540
|
+
write_check_file "codex_config" "0" "$CODEX_HOME/config.toml" "config.toml 缺失或配置不符合要求" "$dir/codex_config.res"
|
|
541
|
+
fi
|
|
542
|
+
) &
|
|
543
|
+
|
|
290
544
|
wait
|
|
291
545
|
LAST_CHECK_DIR="$dir"
|
|
292
546
|
}
|
|
293
547
|
|
|
294
548
|
all_good() {
|
|
295
|
-
local keys="python3 python_alias pnpm dx agent_browser rg multi_agent"
|
|
549
|
+
local keys="python3 python_alias pnpm dx agent_browser rg multi_agent codex_config"
|
|
296
550
|
local k
|
|
297
551
|
for k in $keys; do
|
|
298
552
|
if ! check_ok "$k"; then
|
|
@@ -309,7 +563,7 @@ print_report() {
|
|
|
309
563
|
printf '%-14s | %-4s | %-40s | %s\n' "检查项" "状态" "版本" "说明"
|
|
310
564
|
printf '%-14s-+-%-4s-+-%-40s-+-%s\n' "--------------" "----" "----------------------------------------" "------------------------------"
|
|
311
565
|
|
|
312
|
-
local keys="python3 python_alias pnpm dx agent_browser rg multi_agent"
|
|
566
|
+
local keys="python3 python_alias pnpm dx agent_browser rg multi_agent codex_config"
|
|
313
567
|
local k ok txt ver msg
|
|
314
568
|
for k in $keys; do
|
|
315
569
|
ok="$(read_field "$k" 2)"
|
|
@@ -368,6 +622,11 @@ for round in $(seq 1 "$MAX_ROUNDS"); do
|
|
|
368
622
|
ensure_multi_agent || true
|
|
369
623
|
fi
|
|
370
624
|
|
|
625
|
+
if ! check_ok "codex_config"; then
|
|
626
|
+
echo "[doctor] 修正 ~/.codex/config.toml 关键配置"
|
|
627
|
+
ensure_codex_config || true
|
|
628
|
+
fi
|
|
629
|
+
|
|
371
630
|
echo "[doctor] 强制执行 dx 安装与初始化"
|
|
372
631
|
force_dx || true
|
|
373
632
|
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pr-review-loop
|
|
3
|
+
description: pr 审查
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## 输入
|
|
7
|
+
|
|
8
|
+
- `PR_NUMBER`
|
|
9
|
+
- `round`(默认 1,由编排器控制)
|
|
10
|
+
# PR 审核闭环(技能主编排)
|
|
11
|
+
|
|
12
|
+
本文件是该技能的**唯一编排真值源**。不要再依赖独立的编排 md 文件。
|
|
13
|
+
|
|
14
|
+
## 适用场景
|
|
15
|
+
|
|
16
|
+
- 用户要求对某个 GitHub PR 执行“审核 -> 修复 -> 再审核”的循环。
|
|
17
|
+
- 需要严格执行 `runId` 透传、`./.cache` 交接、Decision Log 持久化。
|
|
18
|
+
- 需要确保修复动作由专职 `fixer` 角色执行,而不是编排器直接改代码。
|
|
19
|
+
|
|
20
|
+
## 目录约定
|
|
21
|
+
|
|
22
|
+
- 子代理说明:`${CODEX_HOME:-$HOME/.codex}/skills/pr-review-loop/references/agents/*.md`
|
|
23
|
+
- 确定性脚本:`${CODEX_HOME:-$HOME/.codex}/skills/pr-review-loop/scripts/*.py`
|
|
24
|
+
- 缓存目录:`./.cache/`
|
|
25
|
+
- 网络要求:`reviewer / aggregator / fixer ` 角色应启用 `network_access = true`。
|
|
26
|
+
|
|
27
|
+
## 输入
|
|
28
|
+
|
|
29
|
+
- `PR_NUMBER`
|
|
30
|
+
- `round`(默认 1,由编排器控制)
|
|
31
|
+
|
|
32
|
+
## runId(强制)
|
|
33
|
+
|
|
34
|
+
- 格式:`<PR>-<ROUND>-<HEAD_SHORT>`
|
|
35
|
+
- 生成者:`pr_context.py`(或 precheck 输出中的同格式值)
|
|
36
|
+
- 后续所有阶段仅允许透传,禁止重算或篡改。
|
|
37
|
+
|
|
38
|
+
## 角色分工(强制)
|
|
39
|
+
|
|
40
|
+
- `reviewer`:并行执行审查,产出 reviewFile。
|
|
41
|
+
- `fixer`:执行修复、提交推送、维护 decision-log、产出 fixReportFile。
|
|
42
|
+
- `spark`:通用agent 根据提示词执行通用任务。
|
|
43
|
+
|
|
44
|
+
## 阶段 0:预检 gate(必须先通过)
|
|
45
|
+
|
|
46
|
+
调用 `spark`,输入:
|
|
47
|
+
--prompt: `${CODEX_HOME:-$HOME/.codex}/skills/pr-review-loop/references/agents/pr-precheck.md`
|
|
48
|
+
--others: `PR #<PR_NUMBER> - round <1>`
|
|
49
|
+
|
|
50
|
+
处理规则:
|
|
51
|
+
|
|
52
|
+
- 若返回 `{"ok":true}`:进入下一阶段。
|
|
53
|
+
- 若返回 `{"error":"..."}`:立即终止流程,不重试;直接透传 precheck 的简短失败原因(可附带 `fixFile`)。
|
|
54
|
+
|
|
55
|
+
说明:阶段 0 是强 gate,不允许修复后重跑,也不进入“错误分级与重试”。
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
## Step 1: 生成上下文(串行)
|
|
59
|
+
|
|
60
|
+
调用 `spark`,输入:
|
|
61
|
+
--prompt: `${CODEX_HOME:-$HOME/.codex}/skills/pr-review-loop/references/agents/pr-context.md`
|
|
62
|
+
--others: `PR #<PR_NUMBER> - round <1>`
|
|
63
|
+
|
|
64
|
+
- 等待 spark 返回,输出必须包含 `contextFile`、`runId`、`headOid`。
|
|
65
|
+
- 检查 `./.cache/decision-log-pr<PR_NUMBER>.md` 是否存在,存在则后续传给 reviewer 以供决策过滤。
|
|
66
|
+
- 若失败:按“错误分级与重试”处理。
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
## 阶段 2~N:最多 3 轮循环
|
|
70
|
+
### Step 2: reviewers 并行(唯一允许并行阶段)
|
|
71
|
+
|
|
72
|
+
并行调用同一个 `reviewer` 角色的多个实例(提示词驱动实体),至少包含以下 3 个提示词:
|
|
73
|
+
|
|
74
|
+
- `${CODEX_HOME:-$HOME/.codex}/skills/pr-review-loop/references/agents/security-reviewer.md`
|
|
75
|
+
- `${CODEX_HOME:-$HOME/.codex}/skills/pr-review-loop/references/agents/logic-reviewer.md`
|
|
76
|
+
- `${CODEX_HOME:-$HOME/.codex}/skills/pr-review-loop/references/agents/style-reviewer.md`
|
|
77
|
+
|
|
78
|
+
每个 reviewer 实例输入至少包含:
|
|
79
|
+
|
|
80
|
+
- `PR #<PR_NUMBER>`
|
|
81
|
+
- `round: <ROUND>`
|
|
82
|
+
- `runId: <RUN_ID>` 来自 Step 1 的输出,必须透传,禁止自行生成)
|
|
83
|
+
- `contextFile: ./.cache/<file>.md`
|
|
84
|
+
- `reviewerPromptFile: <上述 md 之一>`
|
|
85
|
+
- `reviewerPromptContent: <reviewerPromptFile 的全文>`
|
|
86
|
+
- `decisionLogFile: ./.cache/decision-log-pr<PR_NUMBER>.md`(若存在)
|
|
87
|
+
|
|
88
|
+
执行要求:
|
|
89
|
+
|
|
90
|
+
- reviewer 必须先读取 `reviewerPromptFile`,并严格按其中规则执行。
|
|
91
|
+
- 若调用框架不自动注入文件内容,orchestrator 必须显式把 `reviewerPromptContent` 一并传给 reviewer。
|
|
92
|
+
- 当通用 reviewer 约束与 `reviewerPromptFile` 冲突时,以 `reviewerPromptFile` 为准。
|
|
93
|
+
- `security-reviewer` 专注安全与权限边界;`logic-reviewer` 专注逻辑正确性与边界条件;`style-reviewer` 专注可维护性、可读性与工程规范;`gh-thread-reviewer` 专注外部评论采集与归一化。
|
|
94
|
+
|
|
95
|
+
每个 reviewer 实例输出:
|
|
96
|
+
|
|
97
|
+
- `reviewFile: ./.cache/<file>.md`
|
|
98
|
+
|
|
99
|
+
### Step 3: 聚合(模式 A)
|
|
100
|
+
|
|
101
|
+
调用 `spark`,输入:
|
|
102
|
+
--prompt: `${CODEX_HOME:-$HOME/.codex}/skills/pr-review-loop/references/agents/pr-review-aggregate.md`
|
|
103
|
+
--others: `contextFile + 1..n reviewFile + runId (+ decisionLogFile) + 模式 A`。
|
|
104
|
+
|
|
105
|
+
- 输出 `{"stop":true}`:本轮结束并退出循环。
|
|
106
|
+
- 输出 `{"stop":false,"fixFile":"..."}`:进入 Step 4。
|
|
107
|
+
- 输出 `{"error":"GH_PR_COMMENT_FAILED"}`:按可重试错误处理,优先重试本步骤(脚本幂等,重复调用安全)。
|
|
108
|
+
- 其他 `{"error":"..."}`:按“错误分级与重试”处理。
|
|
109
|
+
|
|
110
|
+
### Step 4: 修复(必须委托 fixer)
|
|
111
|
+
|
|
112
|
+
**此步禁止 orchestrator 直接修复代码。**
|
|
113
|
+
|
|
114
|
+
必须调用 `fixer`,输入:
|
|
115
|
+
|
|
116
|
+
- `PR #<PR_NUMBER>`
|
|
117
|
+
- `round: <ROUND>`
|
|
118
|
+
- `runId: <RUN_ID>`
|
|
119
|
+
- `fixFile: ./.cache/<file>.md`
|
|
120
|
+
|
|
121
|
+
期望输出:
|
|
122
|
+
|
|
123
|
+
- `fixReportFile: ./.cache/<file>.md`
|
|
124
|
+
|
|
125
|
+
### Step 5: 发布修复报告(模式 B)
|
|
126
|
+
|
|
127
|
+
调用 `spark`,输入:
|
|
128
|
+
--prompt: `${CODEX_HOME:-$HOME/.codex}/skills/pr-review-loop/references/agents/pr-review-aggregate.md`
|
|
129
|
+
--others: `fixReportFile + runId + round` + 模式 B`。
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
- 期望输出:`{"ok":true}`
|
|
133
|
+
- 若返回 `{"error":"GH_PR_COMMENT_FAILED"}`:按可重试错误处理后再判定终止。
|
|
134
|
+
|
|
135
|
+
### Step 6: 下一轮
|
|
136
|
+
|
|
137
|
+
- 轮次 round +1,回到 Step 2。
|
|
138
|
+
- 总轮次上限 3。
|
|
139
|
+
|
|
140
|
+
## Step 7 收尾(强制)
|
|
141
|
+
|
|
142
|
+
- 若某轮 Step 3 返回 `stop=true`:发布 final-report = `RESOLVED`。
|
|
143
|
+
- 若达到 3 轮仍未 stop:发布 final-report = `MAX_ROUNDS_REACHED`。
|
|
144
|
+
|
|
145
|
+
final-report 由 `aggregator` 调用脚本发布,且幂等。
|
|
146
|
+
|
|
147
|
+
## 失败防护建议
|
|
148
|
+
|
|
149
|
+
- 如果日志显示“编排器直接改代码而非调用 fixer”,视为流程违约。
|
|
150
|
+
- 直接回滚该轮并重新执行,确保 Step 4 通过 `fixer` 完成。
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
## 编排硬规则(强制)
|
|
154
|
+
|
|
155
|
+
1. 除 reviewer 阶段外,其余步骤必须串行且 await 返回。
|
|
156
|
+
2. 每轮最多发布一次 Review Summary / Fix Report(由脚本幂等保证)。
|
|
157
|
+
3. `orchestrator` **禁止直接修改业务代码**。
|
|
158
|
+
4. 当 aggregate 返回 `stop=false` 时,`orchestrator` **必须调用 `fixer` 角色**处理 `fixFile`。
|
|
159
|
+
5. 如果无法调用 `fixer`,必须终止并返回 `{"error":"FIXER_NOT_INVOKED"}`(或等价错误),禁止降级为 orchestrator 自修。
|
|
160
|
+
|
|
161
|
+
## 错误分级与重试(强制)
|
|
162
|
+
|
|
163
|
+
默认策略不是“见 error 立刻终止”,而是先分级:
|
|
164
|
+
|
|
165
|
+
1. 可重试错误:先重试再决定终止。
|
|
166
|
+
2. 不可恢复错误:立即终止。
|
|
167
|
+
|
|
168
|
+
例外:阶段 0(预检 gate)不适用本节重试策略;除 `ok:true` 外,其余返回(含任意 `error`)都必须立即终止。
|
|
169
|
+
|
|
170
|
+
### 可重试错误(建议最多 2 次重试,合计最多 3 次尝试)
|
|
171
|
+
|
|
172
|
+
- `GH_PR_COMMENT_FAILED`
|
|
173
|
+
- `HARVEST_FAILED`
|
|
174
|
+
- `AGGREGATE_SCRIPT_FAILED`
|
|
175
|
+
- `PR_CONTEXT_SCRIPT_FAILED`
|
|
176
|
+
- `GIT_PUSH_FAILED_NETWORK`
|
|
177
|
+
- 其他明显网络抖动/平台瞬时错误(例如 gh API 超时)
|
|
178
|
+
|
|
179
|
+
重试要求:
|
|
180
|
+
|
|
181
|
+
- 使用**同一组输入**重试同一步骤(保持 `runId` 不变)。
|
|
182
|
+
- 使用退避:第 1 次重试前等待 2 秒,第 2 次重试前等待 5 秒。
|
|
183
|
+
- 可先做轻量自愈检查:`gh auth status`、`git remote get-url origin`、必要时重跑本步骤脚本。
|
|
184
|
+
- 若是 `fixer` 推送失败,错误码统一为 `GIT_PUSH_FAILED_NETWORK`,由编排器按可重试错误处理。
|
|
185
|
+
- 若重试后成功,继续流程,不得误判为终止。
|
|
186
|
+
- 若重试仍失败,才终止当前轮。
|
|
187
|
+
|
|
188
|
+
### 不可恢复错误(立即终止)
|
|
189
|
+
|
|
190
|
+
- 参数/协议错误:`INVALID_ARGS`、`MISSING_*`
|
|
191
|
+
- 环境错误:`NOT_A_GIT_REPO`、`GH_CLI_NOT_FOUND`
|
|
192
|
+
- 权限/资源错误:`PR_NOT_FOUND_OR_NO_ACCESS`
|
|
193
|
+
- 流程违约错误:`FIXER_NOT_INVOKED`
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
执行原则:
|
|
197
|
+
- 优先调用 `${CODEX_HOME:-$HOME/.codex}/skills/pr-review-loop/scripts/*.py` 作为确定性真值源。
|
|
198
|
+
- 不在编排层做“聪明猜测”来替代脚本返回。
|
|
199
|
+
- 对外输出只保留关键状态、产物路径与下一步动作。
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# PR Reviewer (Logic)
|
|
2
|
+
|
|
3
|
+
## 角色码(强制)
|
|
4
|
+
|
|
5
|
+
- `ROLE_CODE = LOG`
|
|
6
|
+
- `reviewFile`: `./.cache/review-LOG-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
|
|
7
|
+
- findings id 前缀:`LOG-`
|
|
8
|
+
|
|
9
|
+
## 专责范围(强制)
|
|
10
|
+
|
|
11
|
+
- 仅关注逻辑正确性:业务分支正确性、边界条件、空值/异常路径、状态机转换、并发一致性、事务边界、幂等性、回滚与补偿
|
|
12
|
+
- 非逻辑类建议(纯命名/排版/文风)默认不提
|
|
13
|
+
|
|
14
|
+
## 逻辑审核词(执行清单)
|
|
15
|
+
|
|
16
|
+
1. 状态迁移:状态机是否存在非法跳转、漏转移、重复转移。
|
|
17
|
+
2. 分支完备性:主流程、异常流程、空值分支、极端边界是否闭合。
|
|
18
|
+
3. 一致性与幂等:重复请求、重试、并发写入是否导致脏数据或重复副作用。
|
|
19
|
+
4. 事务与补偿:事务边界是否正确,失败后是否可回滚或补偿。
|
|
20
|
+
5. 时序与竞态:异步回调/事件驱动下是否存在先后顺序依赖漏洞。
|
|
21
|
+
6. 数据约束:校验、默认值、转换规则是否与业务语义一致。
|
|
22
|
+
7. 高风险优先级:会造成业务错误结论、资金/库存/配额错误的,优先判为 P0/P1。
|