@weotro/dx 0.1.9 → 0.1.10

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 CHANGED
@@ -307,6 +307,10 @@ dx cache clear -Y
307
307
  - `~/.codex/skills` 中包内管理的同名非软链接 skill 会被清理;已有软链接不会按旧副本删除。
308
308
  - 从包内移除的历史 skill 会在 `~/.agents/skills`、`~/.claude/skills`、`~/.codex/skills` 中一并清理。
309
309
  - 不属于包内管理的其他用户自有 skill 目录会保留。
310
+ - 新增的 `ask_cc`(兼容咨询入口)和 `delegate-cc`(Claude Code 咨询与任务委派)会自动一同安装,无需修改初始化名单;保留 `ask_cc` 的旧名称及脚本入口。
311
+ - 这两个 skill 仅在显式调用时启用。运行脚本需要本机的 `python3` 和已认证的 `claude` CLI;`dx initial` 不安装这些依赖或修改认证配置。
312
+
313
+ 安装或升级 dx 后,运行 `dx initial` 即可同步。默认脚本位置为 `~/.agents/skills/delegate-cc/scripts/delegate_cc.py`;旧咨询入口为 `~/.agents/skills/ask_cc/scripts/ask_cc.py`。
310
314
 
311
315
  关于 `help`:
312
316
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weotro/dx",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: ask_cc
3
+ description: 仅在用户显式调用 $ask_cc 或明确要求使用 ask_cc 技能,或用户已显式调用的 oo-ship-issue-pr 流程要求咨询时使用;不要通过关键词、任务类型或上下文自动触发。
4
+ ---
5
+
6
+ # ask_cc
7
+
8
+ 使用 [delegate-cc](../delegate-cc/SKILL.md) 的咨询流程获取答案或第二意见,默认不授予工具权限。
9
+
10
+ 若用户明确要求修改代码或执行开发任务,转入该技能的 `task` 模式,按任务范围完成实现和验收。
11
+
12
+ 旧命令 `scripts/ask_cc.py --prompt-file /绝对路径/问题.txt` 继续可用,仅运行咨询模式;模型顺序和降级逻辑复用 `delegate-cc`。
13
+
14
+ `dx initial` 会同时安装这两个技能。使用当前 `SKILL.md` 所在目录下的脚本;默认安装后的调用方式:
15
+
16
+ ```bash
17
+ python3 "$HOME/.agents/skills/ask_cc/scripts/ask_cc.py" --prompt-file /绝对路径/问题.txt
18
+ ```
@@ -0,0 +1,2 @@
1
+ policy:
2
+ allow_implicit_invocation: false
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env python3
2
+ """兼容旧咨询入口;统一实现位于 delegate-cc。"""
3
+
4
+ from pathlib import Path
5
+ import sys
6
+
7
+ sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "delegate-cc" / "scripts"))
8
+ from delegate_cc import ask, main
9
+
10
+
11
+ if __name__ == "__main__":
12
+ main([*sys.argv[1:], "--mode", "consult"])
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: delegate-cc
3
+ description: 仅在用户显式调用 $delegate-cc 或明确要求使用 delegate-cc 技能,或已获用户调用授权的 ask_cc、oo-ship-issue-pr 流程要求委派时使用;不要通过关键词、任务类型或上下文自动触发。
4
+ ---
5
+
6
+ # delegate-cc
7
+
8
+ 把 Claude Code 当作协作者。模型顺序固定为 Fable 5.1(`claude-fable-5-1`)→ Opus 5(`claude-opus-5`)→ 当前 sub-agent。保留本机现有认证。
9
+
10
+ ## 1. 准备交接
11
+
12
+ - **咨询 `consult`**:用户需要答案、决策建议或评审意见。提供问题、相关代码或事实、约束;CLI 无工具权限,只返回建议。
13
+ - **执行 `task`**:用户要求实现、修复或把开发任务交给 CC。任务书必须包含工作目录、目标、可修改文件或模块、适用的项目指令、验收标准和仓库实际验证命令。明确告知 CC 还有其他协作者,保留他人的改动,只负责分配范围。
14
+
15
+ 执行前检查 `git status --short` 和已有 diff,记录涉及文件的初始状态及未跟踪文件。分配互不重叠的文件;需要并发改同一文件时,先拆分责任或使用独立 worktree,指定由谁整合。worktree 不会自动携带未提交改动,必须提供任务所需的当前版本。CC 执行期间,主 agent 不同时修改其负责的文件。
16
+
17
+ 将任务书写入临时 UTF-8 文件,只传必要上下文,排除凭据。完成条件:CC 无需当前对话即可理解目标、权限范围与完成标准。
18
+
19
+ ## 2. 调用 CLI
20
+
21
+ 需要本机提供 `python3` 和已完成认证的 `claude` CLI;`dx initial` 只同步技能文件,不安装运行时或配置认证。
22
+
23
+ 运行当前 `SKILL.md` 所在目录下的 `scripts/delegate_cc.py`。以下路径适用于 `dx initial` 的默认安装位置;通过其他方式安装时使用实际技能目录:
24
+
25
+ ```bash
26
+ # 咨询:默认模式,单个模型默认超时 120 秒。
27
+ python3 "$HOME/.agents/skills/delegate-cc/scripts/delegate_cc.py" --prompt-file /绝对路径/问题.txt
28
+
29
+ # 执行:必须指定工作目录,单次默认超时 900 秒。
30
+ python3 "$HOME/.agents/skills/delegate-cc/scripts/delegate_cc.py" --mode task --cwd /绝对路径/工作区 --prompt-file /绝对路径/任务.txt
31
+ ```
32
+
33
+ 需要 CC 自行运行验证命令时,按本次授权追加 `--allow-command '仓库实际验证命令'`,可重复传入。使用具体命令而非开放全部 Bash;检查命令本身及其调用脚本的作用。现有 Claude Code 权限规则仍生效,`--allow-command` 只是增加允许规则,不是独立沙箱。文件责任范围通过任务书和验收控制,需要隔离时使用 worktree 或已有沙箱。
34
+
35
+ 执行模式保留默认编码系统提示,开放 `Read,Glob,Grep,Edit,Write,Bash`,使用 `acceptEdits`,不能获批的工具请求自动拒绝。两种模式都关闭 hooks、MCP 和技能,避免额外任务链;主 agent 应把所需项目规则及技能要求写入任务书。本技能不使用 `bypassPermissions`,也不扩大任务至提交、推送、部署或对外发送消息。
36
+
37
+ 通过工具异步运行,至少每 60 秒向用户汇报进度。`--timeout` 可按任务规模调整;超时会终止执行任务的进程组。完成条件:获得 JSON 结果或明确的执行失败,旧进程已结束。
38
+
39
+ ## 3. 验收或接续
40
+
41
+ - `ok`:咨询获得答案;检查是否回应原问题,不能把空泛回复当作完成。
42
+ - `review_required`:执行已返回报告,尚待主 agent 验收。对照初始状态检查 diff、新文件及责任范围,运行必要的格式化、检查和测试。发现越界修改时先核对归属,只修正本次 CC 引入的变化。`permission_denials` 非零时核实未执行工作,使用已获授权的宿主工具完成,或准确报告实际权限阻塞。
43
+ - `continuation_required`:任务可能已部分执行。先检查现有 diff、测试状态及外部副作用;更新任务书为“保留已完成部分,仅完成剩余工作”。若 `next_model` 有值,以 `--start-model` 传入该值接续;每个模型最多尝试一次。禁止将同一任务从头盲目重放。
44
+ - `fallback_required`、第二模型也失败或 CLI 无法运行:当前执行者已经是 sub-agent 时直接接手;主 agent 则委派一个继承当前模型的 sub-agent,环境不支持委派时自己接手。提供原任务、已完成改动和剩余工作,明确跳过本技能及 CLI,避免递归。权限拒绝不能靠切换模型绕过。
45
+
46
+ 咨询模式由脚本自动依次尝试两个模型;执行模式每次只运行一个模型,由主 agent 检查现场后接续。认证、额度、模型不可用和超时都不等于任务完成。
47
+
48
+ 完成条件:咨询得到实质答案,或执行任务的代码和验证满足验收标准;仍有缺口时明确列出,而非仅转述 CC 的成功声明。
49
+
50
+ ## 4. 交付
51
+
52
+ 用简体中文说明答案或实际改动、验证结果、未完成项,简短注明使用的模型及降级情况。`requested_model` 是请求 ID,不保证服务端未自动切换模型。清理本次临时任务文件;保留仍有成果待整合的 worktree。
53
+
54
+ 参数或模型出错时核对本机 `claude --help`、[CLI 参考](https://code.claude.com/docs/en/cli-reference)和[模型 ID](https://platform.claude.com/docs/en/models/overview)。用 `claude auth status` 区分认证状态,`unrecognized_model` 不表示未登录。
@@ -0,0 +1,2 @@
1
+ policy:
2
+ allow_implicit_invocation: false
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env python3
2
+ """向 Claude Code 委派咨询或代码任务,并报告降级与接续状态。"""
3
+
4
+ import argparse
5
+ import json
6
+ import math
7
+ import os
8
+ from pathlib import Path
9
+ import shutil
10
+ import signal
11
+ import subprocess
12
+ import sys
13
+
14
+
15
+ MODELS = ("claude-fable-5-1", "claude-opus-5")
16
+
17
+
18
+ def run_task(command, prompt, timeout, cwd):
19
+ """超时或中断时终止整个任务进程组,避免后台写入与接续任务竞争。"""
20
+ with subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
21
+ stderr=subprocess.PIPE, text=True, encoding="utf-8",
22
+ errors="replace", cwd=cwd, start_new_session=True) as process:
23
+ try:
24
+ stdout, stderr = process.communicate(prompt, timeout=timeout)
25
+ except BaseException:
26
+ try:
27
+ os.killpg(process.pid, signal.SIGKILL)
28
+ except ProcessLookupError:
29
+ pass
30
+ process.communicate()
31
+ raise
32
+ return subprocess.CompletedProcess(command, process.returncode, stdout, stderr)
33
+
34
+
35
+ def ask(prompt, timeout=120, *, mode="consult", cwd=None, start_model=None, allow_commands=()):
36
+ if mode not in ("consult", "task"):
37
+ raise ValueError("未知模式")
38
+ if mode == "task" and (cwd is None or not Path(cwd).is_dir()):
39
+ raise ValueError("代码任务必须指定存在的工作目录")
40
+ if mode == "consult" and (start_model is not None or allow_commands):
41
+ raise ValueError("咨询模式不接受任务专用参数")
42
+ attempts = []
43
+ cli = shutil.which("claude")
44
+ if not cli:
45
+ return {"status": "fallback_required", "attempts": [], "reason": "未找到 claude 命令"}
46
+ models = MODELS if mode == "consult" else (start_model or MODELS[0],)
47
+ for model in models:
48
+ if model not in MODELS:
49
+ raise ValueError("模型不在委派链中")
50
+ print(f"正在调用 {model}({mode})…", file=sys.stderr, flush=True)
51
+ command = [
52
+ cli, "--print", "--model", model, "--output-format", "json",
53
+ "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}',
54
+ "--disable-slash-commands", "--no-session-persistence",
55
+ "--settings", '{"disableAllHooks":true}',
56
+ ]
57
+ if mode == "consult":
58
+ command += ["--tools", "", "--system-prompt", "你是决策与问答顾问。根据提供的事实用简体中文回答,说明关键理由和不确定性。仅提供建议。"]
59
+ else:
60
+ command += [
61
+ "--tools", "Read,Glob,Grep,Edit,Write,Bash",
62
+ "--permission-mode", "acceptEdits", "--permission-prompts", "none",
63
+ "--append-system-prompt",
64
+ "你是协作开发者。按任务书实际修改代码并验证。你不是唯一的执行者,保留其他人的改动,遵守分配的文件范围。"
65
+ "先阅读适用的 AGENTS.md 和 CLAUDE.md;接续任务先检查当前状态,仅完成剩余工作。"
66
+ "仅执行本地开发和验证,不提交、推送、发布或对外发消息。权限被拒绝时报告具体阻塞,不尝试绕过。"
67
+ "完成后用简体中文报告变更文件、验证命令及结果、未完成项。",
68
+ ]
69
+ if allow_commands:
70
+ command += ["--allowedTools", *[f"Bash({item})" for item in allow_commands]]
71
+ try:
72
+ process = run_task(command, prompt, timeout, cwd) if mode == "task" else subprocess.run(
73
+ command, input=prompt, text=True, encoding="utf-8",
74
+ errors="replace", capture_output=True, timeout=timeout, cwd=cwd,
75
+ )
76
+ except subprocess.TimeoutExpired:
77
+ attempts.append({"requested_model": model, "reason": "调用超时"})
78
+ continue
79
+ except OSError as error:
80
+ attempts.append({"requested_model": model, "reason": f"无法运行 CLI:{type(error).__name__}"})
81
+ break
82
+ try:
83
+ payload = json.loads(process.stdout)
84
+ except (ValueError, TypeError):
85
+ payload = {}
86
+ if not isinstance(payload, dict):
87
+ payload = {}
88
+ answer = payload.get("result")
89
+ if (process.returncode == 0 and payload.get("type") == "result"
90
+ and payload.get("subtype") == "success"
91
+ and payload.get("is_error") is not True
92
+ and isinstance(answer, str) and answer.strip()):
93
+ return {"status": "review_required" if mode == "task" else "ok", "requested_model": model,
94
+ "permission_denials": len(payload.get("permission_denials") or []),
95
+ "answer": answer.strip(), "attempts": attempts}
96
+ # 原始错误可能包含账户信息,仅输出有界的分类原因。
97
+ diagnostic = (process.stderr + process.stdout).lower()
98
+ if any(word in diagnostic for word in ("quota", "credit", "usage limit", "rate_limit", "rate limit", "429")):
99
+ reason = "额度不足或限流"
100
+ elif any(word in diagnostic for word in ("authentication", "unauthorized", "not logged in", "401")):
101
+ reason = "认证不可用"
102
+ elif any(word in diagnostic for word in ("unrecognized_model", "issue with the selected model", "model_not_found")):
103
+ reason = "模型名称无效或当前账户无权访问"
104
+ else:
105
+ reason = f"CLI 未返回有效答案(退出码 {process.returncode})"
106
+ attempts.append({"requested_model": model, "reason": reason})
107
+ result = {"status": "fallback_required", "attempts": attempts}
108
+ if mode == "task":
109
+ result.update(status="continuation_required", reason="检查现有改动后接续,前次任务可能已经部分执行",
110
+ next_model=MODELS[1] if models[0] == MODELS[0] else None)
111
+ return result
112
+
113
+
114
+ def main(argv=None):
115
+ parser = argparse.ArgumentParser(description=__doc__)
116
+ parser.add_argument("--prompt-file", required=True, type=Path)
117
+ parser.add_argument("--mode", choices=("consult", "task"), default="consult")
118
+ parser.add_argument("--cwd", type=Path)
119
+ parser.add_argument("--start-model", choices=MODELS)
120
+ parser.add_argument("--allow-command", action="append", default=[])
121
+ parser.add_argument("--timeout", type=float)
122
+ args = parser.parse_args(argv)
123
+ if args.timeout is None:
124
+ args.timeout = 900 if args.mode == "task" else 120
125
+ if not math.isfinite(args.timeout) or args.timeout <= 0:
126
+ parser.error("--timeout 必须为有限正数")
127
+ if args.mode == "task" and (args.cwd is None or not args.cwd.is_dir()):
128
+ parser.error("任务模式必须通过 --cwd 指定存在的工作目录")
129
+ if args.mode == "consult" and (args.start_model or args.allow_command):
130
+ parser.error("--start-model 和 --allow-command 仅用于任务模式")
131
+ try:
132
+ prompt = args.prompt_file.read_text(encoding="utf-8")
133
+ if not prompt.strip():
134
+ raise ValueError("问题为空")
135
+ except (OSError, UnicodeError, ValueError):
136
+ result = {"status": "fallback_required", "attempts": [], "reason": "问题文件不可读或为空,请宿主使用原问题兜底"}
137
+ else:
138
+ result = ask(prompt, args.timeout, mode=args.mode, cwd=args.cwd,
139
+ start_model=args.start_model, allow_commands=args.allow_command)
140
+ print(json.dumps(result, ensure_ascii=False))
141
+
142
+
143
+ if __name__ == "__main__":
144
+ main()
@@ -0,0 +1,90 @@
1
+ """复现 CLI 模型校验失败,覆盖模型顺序及错误分类。"""
2
+
3
+ import json
4
+ import subprocess
5
+ import unittest
6
+ import tempfile
7
+ from pathlib import Path
8
+ from unittest.mock import patch
9
+
10
+ import delegate_cc as ask_cc
11
+
12
+
13
+ def result(answer, failed=False):
14
+ return subprocess.CompletedProcess([], int(failed), json.dumps({
15
+ "type": "result", "subtype": "success", "is_error": failed,
16
+ "result": answer,
17
+ }), "")
18
+
19
+
20
+ class AskCCTest(unittest.TestCase):
21
+ @patch.object(ask_cc.shutil, "which", return_value="/fake/claude")
22
+ def test_task_permissions_and_review(self, _which):
23
+ with tempfile.TemporaryDirectory() as cwd, patch.object(ask_cc, "run_task", return_value=result("完成")) as run:
24
+ response = ask_cc.ask("修改代码", mode="task", cwd=cwd, allow_commands=["python3 -m unittest"])
25
+ command = run.call_args.args[0]
26
+ self.assertEqual(response["status"], "review_required")
27
+ self.assertEqual(command[command.index("--permission-mode") + 1], "acceptEdits")
28
+ self.assertIn("Bash(python3 -m unittest)", command)
29
+ self.assertNotIn("bypassPermissions", command)
30
+
31
+ @patch.object(ask_cc.shutil, "which", return_value="/fake/claude")
32
+ def test_partial_task_is_not_blindly_retried(self, _which):
33
+ with tempfile.TemporaryDirectory() as cwd:
34
+ def partial(*args):
35
+ Path(cwd, "partial.txt").write_text("已完成部分")
36
+ raise subprocess.TimeoutExpired("claude", 1)
37
+ with patch.object(ask_cc, "run_task", side_effect=partial) as run:
38
+ response = ask_cc.ask("修改代码", mode="task", cwd=cwd)
39
+ self.assertEqual(run.call_count, 1)
40
+ self.assertEqual(Path(cwd, "partial.txt").read_text(), "已完成部分")
41
+ self.assertEqual(response["status"], "continuation_required")
42
+ self.assertEqual(response["next_model"], "claude-opus-5")
43
+
44
+ @patch.object(ask_cc.shutil, "which", return_value="/fake/claude")
45
+ def test_second_task_failure_requests_local_fallback(self, _which):
46
+ with tempfile.TemporaryDirectory() as cwd, patch.object(ask_cc, "run_task", return_value=result("usage limit", True)):
47
+ response = ask_cc.ask("接续", mode="task", cwd=cwd, start_model="claude-opus-5")
48
+ self.assertEqual(response["status"], "continuation_required")
49
+ self.assertIsNone(response["next_model"])
50
+
51
+ def test_task_requires_explicit_directory(self):
52
+ with self.assertRaises(ValueError):
53
+ ask_cc.ask("修改代码", mode="task")
54
+
55
+ @patch.object(ask_cc.shutil, "which", return_value="/fake/claude")
56
+ def test_supported_model_ids_and_fallback(self, _which):
57
+ seen = []
58
+
59
+ def cli(command, **kwargs):
60
+ model = command[command.index("--model") + 1]
61
+ seen.append(model)
62
+ if model == "claude-fable-5-1":
63
+ return result("usage limit exceeded", True)
64
+ if model == "claude-opus-5":
65
+ return result("OK")
66
+ return result(f"There's an issue with the selected model ({model}).", True)
67
+
68
+ with patch.object(ask_cc.subprocess, "run", side_effect=cli):
69
+ response = ask_cc.ask("仅回答 OK")
70
+ self.assertEqual(response["status"], "ok")
71
+ self.assertEqual(seen, ["claude-fable-5-1", "claude-opus-5"])
72
+ self.assertEqual(response["answer"], "OK")
73
+
74
+ @patch.object(ask_cc.shutil, "which", return_value="/fake/claude")
75
+ def test_unrecognized_model_is_reported(self, _which):
76
+ failure = result("There's an issue with the selected model (fable-5.1). It may not exist or you may not have access to it.", True)
77
+ with patch.object(ask_cc.subprocess, "run", return_value=failure):
78
+ response = ask_cc.ask("问题")
79
+ self.assertEqual(response["status"], "fallback_required")
80
+ self.assertEqual(response["attempts"][0]["reason"], "模型名称无效或当前账户无权访问")
81
+
82
+ @patch.object(ask_cc.shutil, "which", return_value="/fake/claude")
83
+ def test_login_error_is_distinct(self, _which):
84
+ with patch.object(ask_cc.subprocess, "run", return_value=result("Not logged in · Please run /login", True)):
85
+ response = ask_cc.ask("问题")
86
+ self.assertEqual(response["attempts"][0]["reason"], "认证不可用")
87
+
88
+
89
+ if __name__ == "__main__":
90
+ unittest.main()
@@ -5,7 +5,7 @@ description: 仅在用户显式调用 $oo-ship-issue-pr、/oo-ship-issue-pr 或
5
5
 
6
6
  # OO Ship Issue PR
7
7
 
8
- Codex 入口:主进程执行轻改动,直接派出 fresh Codex subagent 承接重改动的方案讨论、实现与独立审查;subagent 不可用时由主进程降级接管并如实记录。
8
+ Codex 入口:主进程执行轻改动;Claude 咨询与独立审查调用 [ask_cc](../ask_cc/SKILL.md),重改动实现与修复调用 [delegate-cc](../delegate-cc/SKILL.md) `task` 模式。调用、模型选择和降级均复用这两个技能。
9
9
 
10
10
  ## 步骤一:加载共享流程
11
11
 
@@ -18,7 +18,7 @@ test -f "$SHIP_CORE"
18
18
  完整读取 `SHIP_CORE` 后执行全部阶段。运行时映射如下:
19
19
 
20
20
  - 主进程:当前 Codex。
21
- - 外部专家:fresh Codex subagent;每个专家阶段新建独立上下文,不复用方案讨论、实现或修复 agent 执行审查。
21
+ - 外部专家:通过 `ask_cc` `delegate-cc` 调用的 Claude Code;审查使用独立上下文,不复用实现或修复会话。
22
22
  - 入口显示名:`OO Ship Issue PR`。
23
23
  - Follow-up:Codex `spawn_agent` 的 `worker` 角色。
24
24
 
@@ -2,50 +2,47 @@
2
2
 
3
3
  仅在共享流程进入外部专家或 follow-up 分支时读取。
4
4
 
5
- ## Fresh Codex subagent 专家通道
5
+ ## Claude 技能路由
6
6
 
7
- 共享流程中的“外部专家”在本入口指独立上下文的 Codex subagent,不要求跨模型或第三方 CLI。每个专家阶段直接调用 Codex `spawn_agent` 创建全新的 `worker`,不复用此前 agent,尤其不让实现或修复 agent 审查自己的改动。使用 `fork_turns: "none"`;若当前工具不提供该参数,使用其支持的新会话方式且不传已有会话 ID,在 prompt 中完整提供任务上下文。
7
+ 用户显式调用 `oo-ship-issue-pr` 后,按下表调用其依赖技能。先完整读取对应 `SKILL.md`,再执行其中的流程;路径相对于本文件,`dx initial` 会将这些技能安装在同一个 skills 根目录下。
8
8
 
9
- ### 派发上下文与权限
9
+ | 共享流程阶段 | 调用技能 | 本阶段目标 |
10
+ |---|---|---|
11
+ | 阶段四方案讨论 | [ask_cc](../../ask_cc/SKILL.md) | 只读咨询,裁决初步方案、备选与取舍 |
12
+ | 阶段 7.3 review | [ask_cc](../../ask_cc/SKILL.md) | 对照 Issue 与当前 diff 返回独立 findings |
13
+ | 阶段 7.3 adversarial review | [ask_cc](../../ask_cc/SKILL.md) | 挑战假设、不变量、反例与失败路径 |
14
+ | 阶段四重改动实现、阶段 7.4 重修复 | [delegate-cc](../../delegate-cc/SKILL.md) 的 `task` 模式 | 按最终方案修改代码并提供验证结果 |
10
15
 
11
- 每次 prompt 必须包含:
16
+ 本入口只补充交付上下文与验收要求。CLI 参数、模型顺序、权限配置、等待与超时、状态解析、接续及降级统一按依赖技能执行,不在这里另写调用脚本或重试链。依赖文件缺失时先运行 `dx initial` 恢复;仍无法读取时报告缺失路径,不临时仿写该技能。
12
17
 
13
- - 当前 worktree 的绝对路径,以及只在该 worktree 工作。
14
- - 当前阶段是只读方案讨论、只读 review、adversarial review、实现还是修复;只承接这一阶段,不运行完整 `oo-ship-issue-pr`。
15
- - Issue 原文、逐条验收标准、base、主进程初步方案或最终方案,以及该阶段所需的 diff、已确认问题和验证上下文。
16
- - 先读取根与涉及目录的 `AGENTS.md`;它不是代码库中唯一执行者,保护已有未提交改动,不回退其他人的修改。
17
- - 只读阶段保持工作树不变;实现或修复阶段的所有权限定为 prompt 列出的验收范围和涉及文件,改动留在工作树。
18
- - 不 commit、不 push、不操作 GitHub、不递归委派;最后返回证据、结论、修改路径和未运行验证。
18
+ ## 交接上下文
19
19
 
20
- ### 只读讨论、review adversarial review
20
+ 每次交接包含当前 worktree 绝对路径、阶段、Issue 原文与逐条验收标准、base,以及该阶段所需的方案、代码和验证证据。任务仅覆盖当前阶段,不运行完整 `oo-ship-issue-pr`;Git 历史、GitHub 操作、权威验证和最终事实核验由 Codex 主进程负责。
21
21
 
22
- prompt 明确要求只读、不执行任何写操作,并直接读取源码和 `git diff <base>...HEAD` 取证。
22
+ ### 咨询与独立审查
23
23
 
24
- - 方案讨论:围绕主进程初步方案、备选与取舍、希望裁决的具体问题返回建议;最终方案由主进程拍板。
25
- - 普通 review:以 findings 为主,按严重级排序;每项包含严重级、`文件:行号`、证据、影响和具体修法。
26
- - adversarial review:先挑战方案假设、构造反例和失败路径,再输出同样格式的 findings;没有证据的问题不进入报告。
24
+ `ask_cc` 的咨询模式无工具权限。主进程先读取适用的 `AGENTS.md`、相关源码和当前 diff,将必要内容连同文件路径、行号、验证结果写进咨询材料;审查材料覆盖整个 PR diff,另附影响判断所需的上下文。仅提供仓库路径或要求 Claude 自行读文件不构成完整交接。证据不足时由主进程补充材料后继续同一审查阶段。
27
25
 
28
- ### 重改动实现与修复
26
+ - 方案讨论:附主进程初步方案、备选与取舍、希望裁决的具体问题;最终方案由主进程拍板。
27
+ - 普通 review:要求按严重级返回 findings,每项包含 `文件:行号`、证据、影响和具体修法。
28
+ - adversarial review:先挑战方案假设、构造反例和失败路径,再按同样格式返回 findings。
29
29
 
30
- prompt 附最终方案原文,要求只改验收范围,最后报告修改路径、已运行验证和未运行验证及原因。Git 历史、GitHub、权威验证和最终事实核验始终由 Codex 主进程负责。
30
+ 审查使用全新独立上下文,不复用实现或修复会话。通过依赖技能降级后,只有未参与实现或修复的独立执行者完成的审查才计入外部专家审查;主进程自审不得冒充独立审查。
31
31
 
32
- ## 结果、等待与补跑
32
+ ### 实现与修复
33
33
 
34
- 通过当前运行时提供的等待/结果工具等待本次 agent 完成并读取完整报告;派发成功或拿到 agent ID 不算阶段完成。可写 agent 执行期间主进程不并发修改其所有权范围内的文件,返回且确认不再写入后才接管。
34
+ `delegate-cc` 的任务书要求,附最终方案原文、可修改文件或模块、适用项目指令、验收标准及仓库实际验证命令。保护已有改动,只在指定 worktree 和分配范围内工作,改动留在工作树。需要 Claude 运行验证时按该技能声明允许的具体命令。
35
35
 
36
- `spawn_agent` 不可用、派发失败、执行失败或超时时,记录 agent ID(如有)与原因;若已启动,先终止或确认 agent 已停止,检查部分改动后,再由主进程完成同一阶段并记录 `Codex 主进程降级`。无法确认可写 agent 已停止时,报告阻塞,避免并发覆盖。单纯的 subagent 不可用不应中断交付链,但主进程自审不得冒充独立审查。
36
+ ## 验收与报告
37
37
 
38
- subagent 执行过的验证只作参考;prompt 必须附本阶段主进程需补跑的验证清单,要求 subagent 列出因沙箱、权限或依赖限制未运行的项目。阶段五及后续轮次要求的全部权威验证由 Codex 主进程重新执行并阅读最新输出。
38
+ 取得结果、等待停止、处理部分改动、接续或降级均按依赖技能执行。主进程取得最终结果后直读源码和 diff,核验结论、合并重复项并记录拒绝依据;仅派发成功或返回成功声明不算阶段完成。
39
39
 
40
- fresh subagent 返回后,Codex 主进程逐条直读源码和 diff 复核,合并重复项并记录拒绝依据,再继续共享流程。
40
+ 外部执行者的验证仅作参考。交接材料附本阶段需补跑的清单,并要求报告未运行的验证及原因;阶段五及后续轮次的全部权威验证仍由 Codex 主进程重新执行并阅读最新输出。
41
41
 
42
- ## 阶段与报告映射
42
+ 阶段 7.3 仍服从共享流程的复杂度门禁和每 PR 最多一次外部专家审查约束。此前报告中的外部专家审查继续计数;同一阶段因证据不足补充材料或按依赖技能降级,不能被当作后续轮次重新派发审查的额度。
43
43
 
44
- - 阶段四方案讨论:只读 fresh Codex subagent。
45
- - 阶段四实现与阶段 7.4 重修复:可写 fresh Codex subagent。
46
- - 阶段 7.3:复杂度门禁命中且本 PR 尚未用过外部专家审查时执行 review;需要挑战方案时执行 adversarial review。成功返回的 fresh Codex subagent 计入本 PR 唯一一次外部专家审查,Codex 主进程降级不计入;已有报告中的历史外部专家审查仍按共享流程计数。
47
- - reviewer:`Codex 主进程自审 / fresh Codex subagent review / fresh Codex subagent adversarial review / Codex 主进程降级 review / Codex 主进程降级 adversarial review`,按本轮实际执行者填写;其他专家阶段同样记录实际使用 fresh Codex subagent 或 Codex 主进程降级。
44
+ reviewer 按实际执行者记录为 `Codex 主进程自审`、`Claude Code(ask_cc)review/adversarial review` 或实际降级执行者,并记录依赖技能返回的请求模型及降级情况。实现与修复记录使用 `delegate-cc task` 及实际执行者;自身接管的审查记为自审,不计入外部专家审查。
48
45
 
49
46
  ## Follow-up
50
47
 
51
- 使用 Codex `spawn_agent` 的 `worker` 角色,prompt 指定 worktree 绝对路径并要求调用 `oo-ship-issue-pr`。明确告知 worker 它不是代码库中唯一执行者,不回退其他人的改动,只在指定 worktree 工作。派发深度只允许一层;worker 发现的新 follow-up 只建 Issue。
48
+ 完整 follow-up 包含 Git/PR 交付,使用 Codex `spawn_agent` 的 `worker` 角色在后台承接;Claude 技能只负责上表中的专家阶段。prompt 指定 worktree 绝对路径并要求调用 `oo-ship-issue-pr`。明确告知 worker 它不是代码库中唯一执行者,不回退其他人的改动,只在指定 worktree 工作。派发深度只允许一层;worker 发现的新 follow-up 只建 Issue。