@ranger1/dx 0.1.99 → 0.1.100
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 +6 -47
- package/lib/codex-initial.js +68 -17
- package/package.json +1 -1
- package/skills/doctor/SKILL.md +44 -41
- package/skills/doctor/agents/openai.yaml +5 -2
- package/skills/git-release/SKILL.md +4 -2
- package/skills/git-release/agents/openai.yaml +3 -0
- package/skills/multi-pr-feature-delivery/SKILL.md +493 -0
- package/skills/multi-pr-feature-delivery/agents/openai.yaml +7 -0
- package/skills/autospec/SKILL.md +0 -203
- package/skills/doctor/scripts/doctor.sh +0 -430
package/README.md
CHANGED
|
@@ -192,53 +192,6 @@ target(端)不写死,由 `env-policy.jsonc.targets` 定义;`commands.jso
|
|
|
192
192
|
|
|
193
193
|
查看 `example/`:包含一个最小可读的 `dx/config` 配置示例,以及如何在一个 pnpm+nx monorepo 中接入 dx。
|
|
194
194
|
|
|
195
|
-
## PR Review Loop(自动评审-修复闭环)
|
|
196
|
-
|
|
197
|
-
仓库内提供了基于 Codex Skill 的 PR 评审自动化工作流:并行评审 -> 聚合结论 -> 生成修复清单 -> 自动修复 -> 再评审,最多循环 3 轮,用于让 PR 更快收敛。
|
|
198
|
-
|
|
199
|
-
### 什么时候用
|
|
200
|
-
|
|
201
|
-
- PR 变更较大、想要更系统地覆盖安全/性能/可维护性问题
|
|
202
|
-
- 希望在 CI 通过前提下,把评审建议落成可执行修复清单(fixFile)
|
|
203
|
-
- 希望避免同一个问题在不同轮次被反复提出(Decision Log)
|
|
204
|
-
|
|
205
|
-
### 如何运行
|
|
206
|
-
|
|
207
|
-
在 Codex 会话中触发该技能:
|
|
208
|
-
|
|
209
|
-
```text
|
|
210
|
-
使用 $pr-review-loop 对 PR #<PR_NUMBER> 执行审核闭环
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
技能入口与说明见:
|
|
214
|
-
|
|
215
|
-
- `skills/pr-review-loop/SKILL.md`
|
|
216
|
-
- `skills/pr-review-loop/references/agents/*.md`
|
|
217
|
-
|
|
218
|
-
### 工作流概览
|
|
219
|
-
|
|
220
|
-
- 预检(`pr-precheck`):先做编译/基础 gate,不通过则终止流程
|
|
221
|
-
- 获取上下文(`pr-context`):生成本轮上下文缓存 `contextFile` 与 `runId`
|
|
222
|
-
- 并行评审(reviewers):按 `./reviewer/*-reviewer.md` 并行审查并产出 reviewFile
|
|
223
|
-
- 聚合(`pr-review-aggregate` 模式 A):合并评审结果、去重、发布 Review Summary、生成 `fixFile`
|
|
224
|
-
- 修复(`fixer`):按 `fixFile` 执行修复并产出 `fixReportFile`
|
|
225
|
-
- 发布修复报告(`pr-review-aggregate` 模式 B)
|
|
226
|
-
|
|
227
|
-
### 缓存文件(项目内 `./.cache/`)
|
|
228
|
-
|
|
229
|
-
该流程中间产物写入 `./.cache/`,并在各阶段传递相对路径:
|
|
230
|
-
|
|
231
|
-
- `./.cache/pr-context-pr<PR>-r<ROUND>-<RUN_ID>.md`(contextFile)
|
|
232
|
-
- `./.cache/review-<ROLE_CODE>-pr<PR>-r<ROUND>-<RUN_ID>.md`(reviewFile)
|
|
233
|
-
- `./.cache/fix-pr<PR>-r<ROUND>-<RUN_ID>.md`(fixFile)
|
|
234
|
-
- `./.cache/fix-report-pr<PR>-r<ROUND>-<RUN_ID>.md`(fixReportFile)
|
|
235
|
-
|
|
236
|
-
### Decision Log(跨轮次决策日志)
|
|
237
|
-
|
|
238
|
-
- 文件:`./.cache/decision-log-pr<PR_NUMBER>.md`
|
|
239
|
-
- 作用:记录每轮 Fixed/Rejected 结论,后续轮次用于过滤重复问题
|
|
240
|
-
- 规则:默认 append-only,保留历史决策用于收敛
|
|
241
|
-
|
|
242
195
|
## 命令
|
|
243
196
|
|
|
244
197
|
dx 的命令由 `dx/config/commands.json` 驱动,并且内置了一些 internal runner(避免项目侧依赖任何 `scripts/lib/*.js`):
|
|
@@ -266,6 +219,12 @@ dx test e2e backend apps/backend/e2e/auth
|
|
|
266
219
|
dx test e2e quantify apps/quantify/e2e/health/health.e2e-spec.ts
|
|
267
220
|
```
|
|
268
221
|
|
|
222
|
+
关于 `dx initial`:
|
|
223
|
+
|
|
224
|
+
- `dx initial` 会把 npm 包内置的 `skills/` 同步到 `~/.codex/skills` 与 `~/.claude/skills`。
|
|
225
|
+
- 包内管理的同名 skill 目录会按当前包内容替换;旧版本中已删除的官方 skill 会被清理。
|
|
226
|
+
- 不属于包内管理的其他用户自有 skill 目录会保留。
|
|
227
|
+
|
|
269
228
|
关于 `help`:
|
|
270
229
|
|
|
271
230
|
- `dx --help`
|
package/lib/codex-initial.js
CHANGED
|
@@ -4,18 +4,14 @@ import os from 'node:os'
|
|
|
4
4
|
|
|
5
5
|
import { logger } from './logger.js'
|
|
6
6
|
|
|
7
|
-
const DEPRECATED_SKILL_DIRS = ['pr-review-loop', 'git-commit-and-pr']
|
|
7
|
+
const DEPRECATED_SKILL_DIRS = ['pr-review-loop', 'git-commit-and-pr', 'autospec']
|
|
8
|
+
const TEMP_DIR_PATTERN = /^\..+\.(tmp|backup)-\d+-\d+$/
|
|
8
9
|
|
|
9
10
|
async function collectAllFiles(dir) {
|
|
10
11
|
const out = []
|
|
11
12
|
|
|
12
13
|
async function walk(current) {
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
entries = await fs.readdir(current, { withFileTypes: true })
|
|
16
|
-
} catch {
|
|
17
|
-
return
|
|
18
|
-
}
|
|
14
|
+
const entries = await fs.readdir(current, { withFileTypes: true })
|
|
19
15
|
|
|
20
16
|
for (const entry of entries) {
|
|
21
17
|
const full = join(current, entry.name)
|
|
@@ -42,6 +38,15 @@ async function ensureDir(path) {
|
|
|
42
38
|
await fs.mkdir(path, { recursive: true })
|
|
43
39
|
}
|
|
44
40
|
|
|
41
|
+
async function pathExists(path) {
|
|
42
|
+
try {
|
|
43
|
+
await fs.access(path)
|
|
44
|
+
return true
|
|
45
|
+
} catch {
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
45
50
|
async function assertDirExists(path, label) {
|
|
46
51
|
try {
|
|
47
52
|
const st = await fs.stat(path)
|
|
@@ -55,18 +60,53 @@ async function assertDirExists(path, label) {
|
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
async function copyDirMerge({ srcDir, dstDir }) {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (DEPRECATED_SKILL_DIRS.includes(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
const entries = await fs.readdir(srcDir, { withFileTypes: true })
|
|
64
|
+
let fileCount = 0
|
|
65
|
+
|
|
66
|
+
for (const entry of entries) {
|
|
67
|
+
if (!entry.isDirectory()) continue
|
|
68
|
+
if (DEPRECATED_SKILL_DIRS.includes(entry.name)) continue
|
|
69
|
+
|
|
70
|
+
const srcSkillDir = join(srcDir, entry.name)
|
|
71
|
+
const dstSkillDir = join(dstDir, entry.name)
|
|
72
|
+
const token = `${process.pid}-${Date.now()}`
|
|
73
|
+
const tmpSkillDir = join(dstDir, `.${entry.name}.tmp-${token}`)
|
|
74
|
+
const backupSkillDir = join(dstDir, `.${entry.name}.backup-${token}`)
|
|
75
|
+
let hasBackup = false
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const files = await collectAllFiles(srcSkillDir)
|
|
79
|
+
fileCount += files.length
|
|
80
|
+
await ensureDir(tmpSkillDir)
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
const rel = relative(srcSkillDir, file)
|
|
83
|
+
const target = join(tmpSkillDir, rel)
|
|
84
|
+
await ensureDir(dirname(target))
|
|
85
|
+
await fs.copyFile(file, target)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (await pathExists(dstSkillDir)) {
|
|
89
|
+
await fs.rename(dstSkillDir, backupSkillDir)
|
|
90
|
+
hasBackup = true
|
|
91
|
+
}
|
|
92
|
+
await fs.rename(tmpSkillDir, dstSkillDir)
|
|
93
|
+
if (hasBackup) {
|
|
94
|
+
await fs.rm(backupSkillDir, { recursive: true, force: true })
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
await fs.rm(tmpSkillDir, { recursive: true, force: true })
|
|
98
|
+
if (hasBackup && !(await pathExists(dstSkillDir))) {
|
|
99
|
+
await fs.rename(backupSkillDir, dstSkillDir)
|
|
100
|
+
hasBackup = false
|
|
101
|
+
}
|
|
102
|
+
if (hasBackup) {
|
|
103
|
+
await fs.rm(backupSkillDir, { recursive: true, force: true })
|
|
104
|
+
}
|
|
105
|
+
throw error
|
|
106
|
+
}
|
|
67
107
|
}
|
|
68
108
|
|
|
69
|
-
return { fileCount
|
|
109
|
+
return { fileCount }
|
|
70
110
|
}
|
|
71
111
|
|
|
72
112
|
async function removeDeprecatedSkillDirs(skillsDir) {
|
|
@@ -75,6 +115,15 @@ async function removeDeprecatedSkillDirs(skillsDir) {
|
|
|
75
115
|
}
|
|
76
116
|
}
|
|
77
117
|
|
|
118
|
+
async function removeStaleTempDirs(skillsDir) {
|
|
119
|
+
const entries = await fs.readdir(skillsDir, { withFileTypes: true })
|
|
120
|
+
for (const entry of entries) {
|
|
121
|
+
if (!entry.isDirectory()) continue
|
|
122
|
+
if (!TEMP_DIR_PATTERN.test(entry.name)) continue
|
|
123
|
+
await fs.rm(join(skillsDir, entry.name), { recursive: true, force: true })
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
78
127
|
export async function runCodexInitial(options = {}) {
|
|
79
128
|
const packageRoot = options.packageRoot
|
|
80
129
|
if (!packageRoot) throw new Error('runCodexInitial: 缺少 packageRoot')
|
|
@@ -91,8 +140,10 @@ export async function runCodexInitial(options = {}) {
|
|
|
91
140
|
const stats = []
|
|
92
141
|
for (const target of targets) {
|
|
93
142
|
await ensureDir(target.dir)
|
|
143
|
+
await removeStaleTempDirs(target.dir)
|
|
94
144
|
await removeDeprecatedSkillDirs(target.dir)
|
|
95
145
|
const copyStats = await copyDirMerge({ srcDir: srcSkillsDir, dstDir: target.dir })
|
|
146
|
+
await removeStaleTempDirs(target.dir)
|
|
96
147
|
await removeDeprecatedSkillDirs(target.dir)
|
|
97
148
|
stats.push({ ...target, ...copyStats })
|
|
98
149
|
}
|
package/package.json
CHANGED
package/skills/doctor/SKILL.md
CHANGED
|
@@ -1,63 +1,66 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: doctor
|
|
3
|
-
description:
|
|
3
|
+
description: 仅在用户显式调用 $doctor 或明确要求使用 doctor 技能时使用;不要通过关键词自动触发。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Doctor
|
|
7
7
|
|
|
8
8
|
## 概览
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
并且在任何本地检测/修复动作前,必须先拉取并完整执行一次远程 INSTALL 指令(不是只下载)。
|
|
10
|
+
本技能用于把当前机器调整到可稳定运行 Codex 开发工作流的状态。
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
不要把执行路径写死。模型应先识别当前系统、shell、包管理器、已有工具版本与用户权限,再自行选择最合适的检测和修复方式。完成修复后必须复检,并给出可读报告。
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
- 发现命令缺失或版本漂移,希望一次性修复。
|
|
14
|
+
## 目标状态
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
最终环境应尽量满足:
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
- `python3` 可用。
|
|
19
|
+
- `python` 可调用 Python 3,或有清晰说明当前系统无需/不应创建该别名。
|
|
20
|
+
- `node`、`npm`、`pnpm` 可用,且满足当前工作流需要。
|
|
21
|
+
- `dx` 可用,并已完成必要初始化。
|
|
22
|
+
- `agent-browser` 可用,且 Chromium/浏览器依赖已安装到可运行状态。
|
|
23
|
+
- `rg`(ripgrep)可用。
|
|
24
|
+
- 常用 PATH 配置在当前 shell 中可生效;若需要持久化,说明写入了哪个 shell 配置文件。
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
INSTALL_URL="https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/.codex/INSTALL.md"
|
|
24
|
-
INSTALL_MD="$(mktemp -t codex-install.XXXXXX.md)"
|
|
26
|
+
## 执行原则
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
- 先诊断,再修复;不要未经确认就重复安装已经健康的工具。
|
|
29
|
+
- 优先使用系统已有的包管理器和用户态安装路径。
|
|
30
|
+
- 遇到多个可行方案时,选择对系统影响最小、最容易回滚的方案。
|
|
31
|
+
- 对需要管理员权限、网络下载或会修改 shell 配置的动作,执行前简短说明影响。
|
|
32
|
+
- 不要求固定轮次;根据实际结果迭代,直到通过验收或明确无法继续。
|
|
33
|
+
- 不要强制执行远程安装文档。只有当诊断显示确实需要外部安装指引时,才读取可信来源并按实际情况采用。
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
cat "${INSTALL_MD}"
|
|
31
|
-
```
|
|
35
|
+
## 建议工作流
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
+
1. 收集上下文:
|
|
38
|
+
- 操作系统与架构
|
|
39
|
+
- 当前 shell 与 PATH
|
|
40
|
+
- `python3`、`python`、`node`、`npm`、`pnpm`、`dx`、`agent-browser`、`rg` 的存在性与版本
|
|
41
|
+
- `CODEX_HOME` 与相关配置目录是否存在
|
|
42
|
+
2. 对照目标状态判断缺口。
|
|
43
|
+
3. 制定最小修复动作并执行。
|
|
44
|
+
4. 每次修复后重新验证相关项。
|
|
45
|
+
5. 所有项目完成后运行一次最终复检。
|
|
46
|
+
6. 输出报告。
|
|
37
47
|
|
|
38
|
-
|
|
48
|
+
## 验证要求
|
|
39
49
|
|
|
40
|
-
|
|
41
|
-
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
|
|
42
|
-
bash "$CODEX_HOME/skills/doctor/scripts/doctor.sh"
|
|
43
|
-
```
|
|
50
|
+
最终复检至少覆盖:
|
|
44
51
|
|
|
45
|
-
|
|
52
|
+
- 每个目标命令是否可被当前 shell 找到。
|
|
53
|
+
- 每个目标命令的版本或基本健康输出。
|
|
54
|
+
- `dx` 的初始化结果或当前初始化状态。
|
|
55
|
+
- `agent-browser` 是否能找到并使用已安装的浏览器依赖。
|
|
56
|
+
- 对未通过项给出失败原因、已尝试动作和下一步建议。
|
|
46
57
|
|
|
47
|
-
|
|
48
|
-
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
|
|
49
|
-
bash "$CODEX_HOME/skills/doctor/scripts/doctor.sh" --max-rounds 3
|
|
50
|
-
```
|
|
58
|
+
## 报告格式
|
|
51
59
|
|
|
52
|
-
|
|
60
|
+
最终报告使用中文,至少包含:
|
|
53
61
|
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
|
|
60
|
-
## 注意
|
|
61
|
-
|
|
62
|
-
- 某些安装步骤可能需要管理员权限(例如 `sudo` 或 Homebrew 写权限)。
|
|
63
|
-
- 若系统缺少包管理器,脚本会给出明确失败原因。
|
|
62
|
+
- 环境摘要:系统、shell、关键 PATH 变更。
|
|
63
|
+
- 检查结果表:检查项、状态、版本/证据、说明。
|
|
64
|
+
- 已执行修复:实际执行过的安装、链接、初始化或配置变更。
|
|
65
|
+
- 未完成项:若存在,说明阻塞原因和用户需要做什么。
|
|
66
|
+
- 结论:通过 / 部分通过 / 未通过。
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "Doctor"
|
|
3
|
-
short_description: "
|
|
4
|
-
default_prompt: "使用 $doctor
|
|
3
|
+
short_description: "按目标状态自主诊断、修复并验证 Codex 开发环境"
|
|
4
|
+
default_prompt: "使用 $doctor 对当前系统进行 Codex 开发环境体检;根据实际缺口自主选择修复方式,完成后复检并输出报告。"
|
|
5
|
+
|
|
6
|
+
policy:
|
|
7
|
+
allow_implicit_invocation: false
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: git-release
|
|
3
|
-
description:
|
|
3
|
+
description: 仅在用户显式调用 $git-release 或明确要求使用 git-release 技能时使用;不要通过关键词自动触发。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Git Release
|
|
@@ -15,6 +15,7 @@ description: 在 Git 仓库中执行标准化版本发布流程并自动生成
|
|
|
15
15
|
- 严格执行前置校验,任何硬性条件不满足时立即终止。
|
|
16
16
|
- 发行说明必须结构化、可读、可追溯。
|
|
17
17
|
- 命令默认在仓库根目录执行。
|
|
18
|
+
- 若能从当前 release 分支或自动建分支流程唯一推断出合法版本号,直接使用该版本继续发布,不要询问用户确认。
|
|
18
19
|
|
|
19
20
|
## 流程
|
|
20
21
|
|
|
@@ -33,7 +34,8 @@ description: 在 Git 仓库中执行标准化版本发布流程并自动生成
|
|
|
33
34
|
- `release/v1.2.3` -> `v1.2.3` -> `1.2.3`
|
|
34
35
|
- `release/v1.2.3-beta.2` -> `v1.2.3-beta.2` -> `1.2.3-beta.2`
|
|
35
36
|
7. 检查目标 tag 是否已存在:`git tag -l "v<VERSION>"`。
|
|
36
|
-
8.
|
|
37
|
+
8. 输出推断出的版本号和推断来源,直接使用该版本号继续执行;不要向用户请求确认。
|
|
38
|
+
9. 仅当无法从分支名或自动建分支流程唯一推断出合法版本号时,终止并要求用户显式指定目标版本。
|
|
37
39
|
|
|
38
40
|
### 二、更新版本号
|
|
39
41
|
|