@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 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`
@@ -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
- let entries
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 files = await collectAllFiles(srcDir)
59
-
60
- for (const file of files) {
61
- const rel = relative(srcDir, file)
62
- const topLevelDir = rel.split('/')[0]
63
- if (DEPRECATED_SKILL_DIRS.includes(topLevelDir)) continue
64
- const target = join(dstDir, rel)
65
- await ensureDir(dirname(target))
66
- await fs.copyFile(file, target)
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: files.length }
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ranger1/dx",
3
- "version": "0.1.99",
3
+ "version": "0.1.100",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,63 +1,66 @@
1
1
  ---
2
2
  name: doctor
3
- description: Use when 需要在本机一次性体检并修复 Codex 开发环境,包括 python3/python 别名、dx 初始化、agent-browser+Chromium 与 ripgrep。
3
+ description: 仅在用户显式调用 $doctor 或明确要求使用 doctor 技能时使用;不要通过关键词自动触发。
4
4
  ---
5
5
 
6
6
  # Doctor
7
7
 
8
8
  ## 概览
9
9
 
10
- 执行本技能时,优先运行 `scripts/doctor.sh`,由脚本完成并行检测、自动修复、最多三轮重试与最终报告。
11
- 并且在任何本地检测/修复动作前,必须先拉取并完整执行一次远程 INSTALL 指令(不是只下载)。
10
+ 本技能用于把当前机器调整到可稳定运行 Codex 开发工作流的状态。
12
11
 
13
- ## 适用场景
12
+ 不要把执行路径写死。模型应先识别当前系统、shell、包管理器、已有工具版本与用户权限,再自行选择最合适的检测和修复方式。完成修复后必须复检,并给出可读报告。
14
13
 
15
- - 新机器初始化 Codex 开发环境。
16
- - 发现命令缺失或版本漂移,希望一次性修复。
14
+ ## 目标状态
17
15
 
18
- ## 执行步骤
16
+ 最终环境应尽量满足:
19
17
 
20
- 1. 强制先执行并落地(不可跳过):
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
- ```bash
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
- echo "Fetch and follow instructions from ${INSTALL_URL}"
27
- curl -fsSL "${INSTALL_URL}" -o "${INSTALL_MD}"
28
+ - 先诊断,再修复;不要未经确认就重复安装已经健康的工具。
29
+ - 优先使用系统已有的包管理器和用户态安装路径。
30
+ - 遇到多个可行方案时,选择对系统影响最小、最容易回滚的方案。
31
+ - 对需要管理员权限、网络下载或会修改 shell 配置的动作,执行前简短说明影响。
32
+ - 不要求固定轮次;根据实际结果迭代,直到通过验收或明确无法继续。
33
+ - 不要强制执行远程安装文档。只有当诊断显示确实需要外部安装指引时,才读取可信来源并按实际情况采用。
28
34
 
29
- # 必须阅读并执行 INSTALL.md 中要求执行的命令;不可只拉取不执行
30
- cat "${INSTALL_MD}"
31
- ```
35
+ ## 建议工作流
32
36
 
33
- 执行要求(强制):
34
- - 拉取后,按 `INSTALL.md` 的步骤顺序执行一遍。
35
- - 对其中出现的安装/初始化命令,必须实际执行,不可仅展示或解释。
36
- - 若某步失败,先修复前置条件后重试该步;完成后再继续 `doctor.sh`。
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
- 2. 直接运行:
48
+ ## 验证要求
39
49
 
40
- ```bash
41
- CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
42
- bash "$CODEX_HOME/skills/doctor/scripts/doctor.sh"
43
- ```
50
+ 最终复检至少覆盖:
44
51
 
45
- 3. 若需限制轮次(默认 3):
52
+ - 每个目标命令是否可被当前 shell 找到。
53
+ - 每个目标命令的版本或基本健康输出。
54
+ - `dx` 的初始化结果或当前初始化状态。
55
+ - `agent-browser` 是否能找到并使用已安装的浏览器依赖。
56
+ - 对未通过项给出失败原因、已尝试动作和下一步建议。
46
57
 
47
- ```bash
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
- - 并行检测:`python3`、`python` 别名、`pnpm`、`dx`、`agent-browser`、`rg`。
55
- - 自动修复:按平台选择安装器修复缺失项。
56
- - 强制执行:每轮都运行 `pnpm add -g @ranger1/dx@latest && dx initial`。
57
- - agent-browser:安装/升级并执行 Chromium 安装。
58
- - 结果输出:展示每项状态、版本、关键信息;全部通过则退出 0,否则最多三轮后退出 1。
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: "并行检测并自动修复 Codex 开发环境关键依赖与特性开关"
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: Git 仓库中执行标准化版本发布流程并自动生成高质量中文发行说明。用于以下场景:需要从 release 分支发布新版本;需要从分支名提取并校验语义化版本(含 alpha/beta/rc 预发布);需要批量更新多个 package.json 的 version 字段并提交;需要基于最近 GitHub Release 汇总提交与 PR 信息、分类变更、生成发布摘要;需要创建 annotated tag、推送远端并创建 GitHub Release。
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
 
@@ -2,3 +2,6 @@ interface:
2
2
  display_name: "Git Release"
3
3
  short_description: "在 release 分支上自动完成版本发布与发行说明生成"
4
4
  default_prompt: "使用 $git-release 按规范执行一次发布并生成高质量发行说明。"
5
+
6
+ policy:
7
+ allow_implicit_invocation: false