@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/skills/autospec/SKILL.md
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: autospec
|
|
3
|
-
description: 设计文档到实施计划的全自动流水线。仅在用户显式调用时触发(如 /autospec),不通过关键词自动触发。将已讨论好的设计方案依次执行:写设计文档 → critic 审核修复 → 写实施计划 → critic 审核修复 → 自动开始 subagent-driven 执行 → git-pr-ship 交付 PR。
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# spec-to-plan:设计文档 → 审核 → 实施计划 → 审核 → 执行 → 交付 PR
|
|
7
|
-
|
|
8
|
-
六步全自动流水线。用户在 brainstorming 阶段讨论好目标和方案后显式调用,后续全部自动完成,无需人工干预。
|
|
9
|
-
|
|
10
|
-
## 输入
|
|
11
|
-
|
|
12
|
-
`/spec-to-plan <topic-slug>`
|
|
13
|
-
|
|
14
|
-
- `topic-slug`:文件命名用,如 `chat-instruction-admin`
|
|
15
|
-
- 当前对话中必须已包含充分的设计上下文(目标、方案、数据模型、API、技术决策等)
|
|
16
|
-
|
|
17
|
-
## 输出
|
|
18
|
-
|
|
19
|
-
- 审核后的设计文档:`docs/superpowers/specs/YYYY-MM-DD-<topic-slug>-design.md`
|
|
20
|
-
- 审核后的实施计划:`docs/superpowers/plans/YYYY-MM-DD-<topic-slug>.md`
|
|
21
|
-
- 自动进入 subagent-driven 执行模式
|
|
22
|
-
|
|
23
|
-
## 执行流程
|
|
24
|
-
|
|
25
|
-
严格按顺序执行五步,中间不暂停、不询问用户。
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
### Step 1:撰写设计文档
|
|
30
|
-
|
|
31
|
-
从当前对话上下文中提取所有已确认的设计决策,写入设计文档。不臆造未讨论过的需求。
|
|
32
|
-
|
|
33
|
-
**路径**:`docs/superpowers/specs/YYYY-MM-DD-<topic-slug>-design.md`
|
|
34
|
-
|
|
35
|
-
**结构**(按需裁剪,无内容的章节不写):
|
|
36
|
-
|
|
37
|
-
```markdown
|
|
38
|
-
# <功能名称>
|
|
39
|
-
|
|
40
|
-
## 背景
|
|
41
|
-
## 数据模型
|
|
42
|
-
## API 设计
|
|
43
|
-
## 缓存策略
|
|
44
|
-
## 前端改造
|
|
45
|
-
## 必要的集成步骤
|
|
46
|
-
## Seed 数据
|
|
47
|
-
## 模块结构
|
|
48
|
-
## 不做的事
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
**自检**:写完后扫描——无 TBD/TODO、章节不矛盾、范围聚焦、无歧义。发现问题直接修。
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
### Step 2:审核设计文档
|
|
56
|
-
|
|
57
|
-
派出 `oh-my-claudecode:critic` subagent 审核。
|
|
58
|
-
|
|
59
|
-
**Prompt**:
|
|
60
|
-
|
|
61
|
-
```
|
|
62
|
-
请以挑刺的角度审核这份设计文档,结合项目实际代码验证每一个设计决策。
|
|
63
|
-
|
|
64
|
-
文档路径:<spec-path>
|
|
65
|
-
|
|
66
|
-
审核要点:
|
|
67
|
-
1. 数据模型:字段命名、ID 策略、@map/@@map 是否匹配项目惯例
|
|
68
|
-
2. 模块结构:目录组织是否与现有模块一致
|
|
69
|
-
3. API 设计:路由前缀、鉴权、分页 DTO 继承是否符合惯例
|
|
70
|
-
4. 缓存:CacheService 方法签名是否正确
|
|
71
|
-
5. 前端:store 组织、API 调用模式、组件层级是否匹配实际代码
|
|
72
|
-
6. 集成步骤:是否遗漏 ErrorCode、Swagger、RBAC、api-contracts、菜单注册等
|
|
73
|
-
7. Seed:入口注册、幂等策略是否正确
|
|
74
|
-
|
|
75
|
-
每个发现给出具体文件路径和代码证据。
|
|
76
|
-
分为:错误(必须修复)、遗漏(建议补充)、建议(可选优化)。
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
**处理结果**:读取 critic 返回,逐一修复设计文档。错误和遗漏全部修复,建议类酌情采纳。不改变已与用户确认的设计决策,只修事实性错误和惯例偏差。
|
|
80
|
-
|
|
81
|
-
---
|
|
82
|
-
|
|
83
|
-
### Step 3:撰写实施计划
|
|
84
|
-
|
|
85
|
-
基于审核后的设计文档写实施计划。
|
|
86
|
-
|
|
87
|
-
**路径**:`docs/superpowers/plans/YYYY-MM-DD-<topic-slug>.md`
|
|
88
|
-
|
|
89
|
-
**头部**(固定格式):
|
|
90
|
-
|
|
91
|
-
```markdown
|
|
92
|
-
# <功能名称> Implementation Plan
|
|
93
|
-
|
|
94
|
-
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development to implement this plan task-by-task.
|
|
95
|
-
|
|
96
|
-
**Goal:** 一句话
|
|
97
|
-
**Architecture:** 2-3 句
|
|
98
|
-
**Tech Stack:** 关键技术
|
|
99
|
-
**Spec:** <spec-path>
|
|
100
|
-
|
|
101
|
-
---
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
**Task 结构**:
|
|
105
|
-
|
|
106
|
-
```markdown
|
|
107
|
-
### Task N: <组件名>
|
|
108
|
-
|
|
109
|
-
**Files:**
|
|
110
|
-
- Create: `exact/path`
|
|
111
|
-
- Modify: `exact/path`
|
|
112
|
-
|
|
113
|
-
- [ ] **Step 1: 描述**
|
|
114
|
-
(完整代码块)
|
|
115
|
-
|
|
116
|
-
- [ ] **Step 2: 验证**
|
|
117
|
-
Run: `命令`
|
|
118
|
-
Expected: 预期
|
|
119
|
-
|
|
120
|
-
- [ ] **Step 3: Commit**
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
**要求**:
|
|
124
|
-
- 每步完整代码,禁止 TBD/TODO/"类似 Task N"
|
|
125
|
-
- import 路径准确(基于 Step 2 审核已验证的惯例)
|
|
126
|
-
- Task 按依赖顺序排列
|
|
127
|
-
- 粒度 2-5 分钟每 Task
|
|
128
|
-
|
|
129
|
-
**自检**:spec 每个章节有对应 Task、无占位符、类型和方法名前后一致。
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
### Step 4:审核实施计划
|
|
134
|
-
|
|
135
|
-
派出 `oh-my-claudecode:critic` subagent 审核。
|
|
136
|
-
|
|
137
|
-
**Prompt**:
|
|
138
|
-
|
|
139
|
-
```
|
|
140
|
-
请以挑刺的角度审核这份实施计划,结合项目实际代码验证每一个代码片段。
|
|
141
|
-
|
|
142
|
-
计划路径:<plan-path>
|
|
143
|
-
设计文档:<spec-path>
|
|
144
|
-
|
|
145
|
-
审核要点:
|
|
146
|
-
1. Import 路径:每个 import 在项目中是否真实存在
|
|
147
|
-
2. 路由冲突:参数路由 (:id) 和固定路由的顺序
|
|
148
|
-
3. API 方法名:Zodios 等生成的方法命名是否匹配惯例
|
|
149
|
-
4. 测试基建:E2E setup 是否匹配项目 fixtures
|
|
150
|
-
5. Seed 入口:注册方式是否匹配项目结构
|
|
151
|
-
6. Store 注册:前端 store 根注册方式是否正确
|
|
152
|
-
7. 页面路由:文件路径能否自动注册路由
|
|
153
|
-
8. DTO/Exception:构造函数签名、方法名是否匹配实际 API
|
|
154
|
-
9. 遗漏:spec 中的需求是否全部覆盖
|
|
155
|
-
|
|
156
|
-
每个发现给出具体文件路径和代码证据。
|
|
157
|
-
分为:错误(必须修复)、遗漏(建议补充)、建议(可选优化)。
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
**处理结果**:读取 critic 返回,逐一修复实施计划。错误和遗漏全部修复。
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
### Step 5:自动开始执行
|
|
165
|
-
|
|
166
|
-
四步文档工作完成后,直接调用 `superpowers:subagent-driven-development` skill,传入实施计划路径,开始 Task-by-Task 的 subagent 执行。
|
|
167
|
-
|
|
168
|
-
不询问用户选择执行方式,直接以 subagent-driven 模式启动。
|
|
169
|
-
|
|
170
|
-
输出简短状态通知:
|
|
171
|
-
|
|
172
|
-
```
|
|
173
|
-
设计文档:<spec-path>(已审核修复)
|
|
174
|
-
实施计划:<plan-path>(已审核修复,共 N 个 Task)
|
|
175
|
-
正在以 subagent-driven 模式开始执行...
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
---
|
|
179
|
-
|
|
180
|
-
### Step 6:调用 git-pr-ship 交付 PR
|
|
181
|
-
|
|
182
|
-
subagent-driven 执行全部 Task 完成并通过验证后,直接调用 `git-pr-ship` skill,进入 PR 交付流程。
|
|
183
|
-
|
|
184
|
-
前置条件(执行阶段应已满足,若未满足先补齐):
|
|
185
|
-
- 全部 Task 已完成并勾选
|
|
186
|
-
- 代码变更已提交到当前功能分支
|
|
187
|
-
- lint、构建、相关测试已通过
|
|
188
|
-
|
|
189
|
-
不询问用户,直接以 `git-pr-ship` 流程收口:整理提交、推送分支、创建 PR。
|
|
190
|
-
|
|
191
|
-
输出简短状态通知:
|
|
192
|
-
|
|
193
|
-
```
|
|
194
|
-
实施已全部完成并验证通过。
|
|
195
|
-
正在调用 git-pr-ship 交付 PR...
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
## 注意事项
|
|
199
|
-
|
|
200
|
-
- 设计文档内容完全来自对话上下文,不臆造未讨论过的需求
|
|
201
|
-
- critic 修复只改事实错误和惯例偏差,不改变用户已确认的设计决策
|
|
202
|
-
- 如果 critic 发现的问题数量极多(>10 个错误级),在修复后可考虑再跑一轮 critic 确认
|
|
203
|
-
- 整个流程对用户透明——每步开始时输出一行状态(如"正在撰写设计文档..."),但不等待确认
|
|
@@ -1,430 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
MAX_ROUNDS=3
|
|
5
|
-
while [[ $# -gt 0 ]]; do
|
|
6
|
-
case "$1" in
|
|
7
|
-
--max-rounds)
|
|
8
|
-
MAX_ROUNDS="${2:-3}"
|
|
9
|
-
shift 2
|
|
10
|
-
;;
|
|
11
|
-
*)
|
|
12
|
-
shift
|
|
13
|
-
;;
|
|
14
|
-
esac
|
|
15
|
-
done
|
|
16
|
-
|
|
17
|
-
if ! [[ "$MAX_ROUNDS" =~ ^[0-9]+$ ]] || [[ "$MAX_ROUNDS" -lt 1 ]]; then
|
|
18
|
-
echo "[doctor] --max-rounds 必须是正整数" >&2
|
|
19
|
-
exit 2
|
|
20
|
-
fi
|
|
21
|
-
|
|
22
|
-
CACHE_ROOT="${PWD}/.cache/doctor"
|
|
23
|
-
mkdir -p "$CACHE_ROOT"
|
|
24
|
-
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
|
|
25
|
-
|
|
26
|
-
LAST_CHECK_DIR=""
|
|
27
|
-
DX_FORCE_OK=0
|
|
28
|
-
DX_FORCE_MSG=""
|
|
29
|
-
|
|
30
|
-
run_silent() {
|
|
31
|
-
"$@" >/dev/null 2>&1
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
detect_pkg_manager() {
|
|
35
|
-
if command -v brew >/dev/null 2>&1; then echo "brew"; return; fi
|
|
36
|
-
if command -v apt-get >/dev/null 2>&1; then echo "apt"; return; fi
|
|
37
|
-
if command -v dnf >/dev/null 2>&1; then echo "dnf"; return; fi
|
|
38
|
-
if command -v yum >/dev/null 2>&1; then echo "yum"; return; fi
|
|
39
|
-
if command -v pacman >/dev/null 2>&1; then echo "pacman"; return; fi
|
|
40
|
-
echo "none"
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
install_python3() {
|
|
44
|
-
local pm
|
|
45
|
-
pm="$(detect_pkg_manager)"
|
|
46
|
-
case "$pm" in
|
|
47
|
-
brew)
|
|
48
|
-
brew install python
|
|
49
|
-
;;
|
|
50
|
-
apt)
|
|
51
|
-
sudo apt-get update && sudo apt-get install -y python3 python3-venv python3-pip
|
|
52
|
-
;;
|
|
53
|
-
dnf)
|
|
54
|
-
sudo dnf install -y python3 python3-pip
|
|
55
|
-
;;
|
|
56
|
-
yum)
|
|
57
|
-
sudo yum install -y python3 python3-pip
|
|
58
|
-
;;
|
|
59
|
-
pacman)
|
|
60
|
-
sudo pacman -Sy --noconfirm python
|
|
61
|
-
;;
|
|
62
|
-
*)
|
|
63
|
-
echo "未检测到可用包管理器,无法安装 python3" >&2
|
|
64
|
-
return 1
|
|
65
|
-
;;
|
|
66
|
-
esac
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
ensure_python_alias() {
|
|
70
|
-
local py3 target_dir target py_major
|
|
71
|
-
py3="$(command -v python3 || true)"
|
|
72
|
-
if [[ -z "$py3" ]]; then
|
|
73
|
-
echo "python3 不存在,无法创建 python 别名" >&2
|
|
74
|
-
return 1
|
|
75
|
-
fi
|
|
76
|
-
|
|
77
|
-
if command -v python >/dev/null 2>&1; then
|
|
78
|
-
py_major="$(python -c 'import sys; print(sys.version_info[0])' 2>/dev/null || true)"
|
|
79
|
-
if [[ "$py_major" == "3" ]]; then
|
|
80
|
-
return 0
|
|
81
|
-
fi
|
|
82
|
-
fi
|
|
83
|
-
|
|
84
|
-
# 避免污染系统/工具链目录,统一落到用户态目录。
|
|
85
|
-
target_dir="$HOME/.local/bin"
|
|
86
|
-
mkdir -p "$target_dir"
|
|
87
|
-
target="$target_dir/python"
|
|
88
|
-
ln -sf "$py3" "$target"
|
|
89
|
-
|
|
90
|
-
if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then
|
|
91
|
-
export PATH="$HOME/.local/bin:$PATH"
|
|
92
|
-
fi
|
|
93
|
-
|
|
94
|
-
py_major="$(python -c 'import sys; print(sys.version_info[0])' 2>/dev/null || true)"
|
|
95
|
-
[[ "$py_major" == "3" ]]
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
install_rg() {
|
|
99
|
-
local pm
|
|
100
|
-
pm="$(detect_pkg_manager)"
|
|
101
|
-
case "$pm" in
|
|
102
|
-
brew)
|
|
103
|
-
brew install ripgrep
|
|
104
|
-
;;
|
|
105
|
-
apt)
|
|
106
|
-
sudo apt-get update && sudo apt-get install -y ripgrep
|
|
107
|
-
;;
|
|
108
|
-
dnf)
|
|
109
|
-
sudo dnf install -y ripgrep
|
|
110
|
-
;;
|
|
111
|
-
yum)
|
|
112
|
-
sudo yum install -y ripgrep
|
|
113
|
-
;;
|
|
114
|
-
pacman)
|
|
115
|
-
sudo pacman -Sy --noconfirm ripgrep
|
|
116
|
-
;;
|
|
117
|
-
*)
|
|
118
|
-
echo "未检测到可用包管理器,无法安装 rg" >&2
|
|
119
|
-
return 1
|
|
120
|
-
;;
|
|
121
|
-
esac
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
install_pnpm() {
|
|
125
|
-
if command -v pnpm >/dev/null 2>&1; then
|
|
126
|
-
return 0
|
|
127
|
-
fi
|
|
128
|
-
|
|
129
|
-
if command -v npm >/dev/null 2>&1; then
|
|
130
|
-
npm install -g pnpm@latest
|
|
131
|
-
elif command -v brew >/dev/null 2>&1; then
|
|
132
|
-
brew install pnpm
|
|
133
|
-
else
|
|
134
|
-
echo "缺少 npm/brew,无法安装 pnpm" >&2
|
|
135
|
-
return 1
|
|
136
|
-
fi
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
persist_user_local_bin_path() {
|
|
140
|
-
local line='export PATH="$HOME/.local/bin:$PATH"'
|
|
141
|
-
local rc
|
|
142
|
-
for rc in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.profile"; do
|
|
143
|
-
if [[ -f "$rc" ]]; then
|
|
144
|
-
if ! grep -F "$line" "$rc" >/dev/null 2>&1; then
|
|
145
|
-
printf '\n%s\n' "$line" >>"$rc"
|
|
146
|
-
fi
|
|
147
|
-
fi
|
|
148
|
-
done
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
expose_dx_globally() {
|
|
152
|
-
local source_dx="$1"
|
|
153
|
-
local target_dir target_path
|
|
154
|
-
|
|
155
|
-
if [[ ! -x "$source_dx" ]]; then
|
|
156
|
-
echo "dx 源可执行文件不存在:$source_dx" >&2
|
|
157
|
-
return 1
|
|
158
|
-
fi
|
|
159
|
-
|
|
160
|
-
target_dir=""
|
|
161
|
-
if [[ -w "/usr/local/bin" ]]; then
|
|
162
|
-
target_dir="/usr/local/bin"
|
|
163
|
-
elif [[ -w "/opt/homebrew/bin" ]]; then
|
|
164
|
-
target_dir="/opt/homebrew/bin"
|
|
165
|
-
else
|
|
166
|
-
target_dir="$HOME/.local/bin"
|
|
167
|
-
mkdir -p "$target_dir"
|
|
168
|
-
if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then
|
|
169
|
-
export PATH="$HOME/.local/bin:$PATH"
|
|
170
|
-
fi
|
|
171
|
-
persist_user_local_bin_path
|
|
172
|
-
fi
|
|
173
|
-
|
|
174
|
-
target_path="$target_dir/dx"
|
|
175
|
-
ln -sf "$source_dx" "$target_path"
|
|
176
|
-
command -v dx >/dev/null 2>&1
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
ensure_agent_browser() {
|
|
180
|
-
# 优先使用用户态 pnpm,减少全局权限问题。
|
|
181
|
-
if command -v pnpm >/dev/null 2>&1; then
|
|
182
|
-
pnpm add -g agent-browser@latest
|
|
183
|
-
elif command -v npm >/dev/null 2>&1; then
|
|
184
|
-
npm install -g agent-browser@latest
|
|
185
|
-
elif command -v brew >/dev/null 2>&1; then
|
|
186
|
-
brew install agent-browser
|
|
187
|
-
else
|
|
188
|
-
echo "缺少 npm/pnpm/brew,无法安装 agent-browser" >&2
|
|
189
|
-
return 1
|
|
190
|
-
fi
|
|
191
|
-
|
|
192
|
-
if ! command -v agent-browser >/dev/null 2>&1; then
|
|
193
|
-
local pnpm_bin npm_prefix
|
|
194
|
-
pnpm_bin="$(pnpm bin -g 2>/dev/null || true)"
|
|
195
|
-
npm_prefix="$(npm prefix -g 2>/dev/null || true)"
|
|
196
|
-
echo "agent-browser 安装后仍不可用,请检查 PATH(pnpm: ${pnpm_bin:-N/A},npm: ${npm_prefix:-N/A}/bin)" >&2
|
|
197
|
-
return 1
|
|
198
|
-
fi
|
|
199
|
-
|
|
200
|
-
if ! run_silent agent-browser install; then
|
|
201
|
-
run_silent agent-browser install --with-deps || {
|
|
202
|
-
echo "agent-browser Chromium 安装失败" >&2
|
|
203
|
-
return 1
|
|
204
|
-
}
|
|
205
|
-
fi
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
force_dx() {
|
|
209
|
-
local pnpm_bin dx_installed dx_cmd
|
|
210
|
-
if ! install_pnpm; then
|
|
211
|
-
DX_FORCE_OK=0
|
|
212
|
-
DX_FORCE_MSG="pnpm 不可用,无法执行 dx 强制初始化"
|
|
213
|
-
return 1
|
|
214
|
-
fi
|
|
215
|
-
|
|
216
|
-
if pnpm add -g @ranger1/dx@latest >/dev/null 2>&1; then
|
|
217
|
-
pnpm_bin="$(pnpm bin -g 2>/dev/null || true)"
|
|
218
|
-
dx_installed=""
|
|
219
|
-
if [[ -n "$pnpm_bin" && -x "$pnpm_bin/dx" ]]; then
|
|
220
|
-
dx_installed="$pnpm_bin/dx"
|
|
221
|
-
elif command -v dx >/dev/null 2>&1; then
|
|
222
|
-
dx_installed="$(command -v dx)"
|
|
223
|
-
fi
|
|
224
|
-
|
|
225
|
-
if [[ -z "$dx_installed" ]]; then
|
|
226
|
-
DX_FORCE_OK=0
|
|
227
|
-
DX_FORCE_MSG="dx 安装后未找到可执行文件(pnpm bin: ${pnpm_bin:-N/A})"
|
|
228
|
-
return 1
|
|
229
|
-
fi
|
|
230
|
-
|
|
231
|
-
if ! expose_dx_globally "$dx_installed"; then
|
|
232
|
-
DX_FORCE_OK=0
|
|
233
|
-
DX_FORCE_MSG="dx 已安装但未能暴露为全局命令(pnpm bin: ${pnpm_bin:-N/A})"
|
|
234
|
-
return 1
|
|
235
|
-
fi
|
|
236
|
-
|
|
237
|
-
dx_cmd="$(command -v dx || true)"
|
|
238
|
-
if [[ -n "$dx_cmd" ]] && dx initial >/dev/null 2>&1; then
|
|
239
|
-
DX_FORCE_OK=1
|
|
240
|
-
DX_FORCE_MSG="已安装并暴露全局 dx(${dx_cmd}),并执行 dx initial"
|
|
241
|
-
return 0
|
|
242
|
-
fi
|
|
243
|
-
|
|
244
|
-
DX_FORCE_OK=0
|
|
245
|
-
DX_FORCE_MSG="dx 已暴露为全局命令但 dx initial 执行失败(dx: ${dx_cmd:-N/A})"
|
|
246
|
-
return 1
|
|
247
|
-
fi
|
|
248
|
-
|
|
249
|
-
DX_FORCE_OK=0
|
|
250
|
-
DX_FORCE_MSG="强制命令执行失败:pnpm add -g @ranger1/dx@latest"
|
|
251
|
-
return 1
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
write_check_file() {
|
|
255
|
-
local key="$1" ok="$2" ver="$3" msg="$4" file="$5"
|
|
256
|
-
printf '%s|%s|%s|%s\n' "$key" "$ok" "$ver" "$msg" >"$file"
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
read_field() {
|
|
260
|
-
local key="$1" field="$2"
|
|
261
|
-
local f="$LAST_CHECK_DIR/${key}.res"
|
|
262
|
-
if [[ ! -f "$f" ]]; then
|
|
263
|
-
echo ""
|
|
264
|
-
return 0
|
|
265
|
-
fi
|
|
266
|
-
awk -F'|' -v idx="$field" '{print $idx}' "$f"
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
check_ok() {
|
|
270
|
-
local key="$1"
|
|
271
|
-
[[ "$(read_field "$key" 2)" == "1" ]]
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
run_parallel_checks() {
|
|
275
|
-
local round="$1"
|
|
276
|
-
local dir="$CACHE_ROOT/round-${round}"
|
|
277
|
-
mkdir -p "$dir"
|
|
278
|
-
|
|
279
|
-
(
|
|
280
|
-
if command -v python3 >/dev/null 2>&1; then
|
|
281
|
-
write_check_file "python3" "1" "$(python3 --version 2>&1 | head -n1)" "ok" "$dir/python3.res"
|
|
282
|
-
else
|
|
283
|
-
write_check_file "python3" "0" "" "python3 未安装" "$dir/python3.res"
|
|
284
|
-
fi
|
|
285
|
-
) &
|
|
286
|
-
|
|
287
|
-
(
|
|
288
|
-
if command -v python >/dev/null 2>&1; then
|
|
289
|
-
local py_ver py_major
|
|
290
|
-
py_ver="$(python --version 2>&1 | head -n1)"
|
|
291
|
-
py_major="$(python -c 'import sys; print(sys.version_info[0])' 2>/dev/null || true)"
|
|
292
|
-
if [[ "$py_major" == "3" ]]; then
|
|
293
|
-
write_check_file "python_alias" "1" "$py_ver" "ok" "$dir/python_alias.res"
|
|
294
|
-
else
|
|
295
|
-
write_check_file "python_alias" "0" "$py_ver" "python 存在但不是 Python 3" "$dir/python_alias.res"
|
|
296
|
-
fi
|
|
297
|
-
else
|
|
298
|
-
write_check_file "python_alias" "0" "" "python 别名不可用" "$dir/python_alias.res"
|
|
299
|
-
fi
|
|
300
|
-
) &
|
|
301
|
-
|
|
302
|
-
(
|
|
303
|
-
if command -v pnpm >/dev/null 2>&1; then
|
|
304
|
-
write_check_file "pnpm" "1" "pnpm $(pnpm --version 2>/dev/null | head -n1)" "ok" "$dir/pnpm.res"
|
|
305
|
-
else
|
|
306
|
-
write_check_file "pnpm" "0" "" "pnpm 未安装" "$dir/pnpm.res"
|
|
307
|
-
fi
|
|
308
|
-
) &
|
|
309
|
-
|
|
310
|
-
(
|
|
311
|
-
if command -v dx >/dev/null 2>&1; then
|
|
312
|
-
local v
|
|
313
|
-
v="$(dx --version 2>/dev/null | head -n1 || true)"
|
|
314
|
-
if [[ -z "$v" ]]; then
|
|
315
|
-
v="$(dx -v 2>/dev/null | head -n1 || true)"
|
|
316
|
-
fi
|
|
317
|
-
write_check_file "dx" "1" "${v:-dx (版本未知)}" "ok" "$dir/dx.res"
|
|
318
|
-
else
|
|
319
|
-
write_check_file "dx" "0" "" "dx 未安装" "$dir/dx.res"
|
|
320
|
-
fi
|
|
321
|
-
) &
|
|
322
|
-
|
|
323
|
-
(
|
|
324
|
-
if command -v agent-browser >/dev/null 2>&1; then
|
|
325
|
-
write_check_file "agent_browser" "1" "$(agent-browser --version 2>/dev/null | head -n1)" "ok" "$dir/agent_browser.res"
|
|
326
|
-
else
|
|
327
|
-
write_check_file "agent_browser" "0" "" "agent-browser 未安装" "$dir/agent_browser.res"
|
|
328
|
-
fi
|
|
329
|
-
) &
|
|
330
|
-
|
|
331
|
-
(
|
|
332
|
-
if command -v rg >/dev/null 2>&1; then
|
|
333
|
-
write_check_file "rg" "1" "$(rg --version 2>/dev/null | head -n1)" "ok" "$dir/rg.res"
|
|
334
|
-
else
|
|
335
|
-
write_check_file "rg" "0" "" "rg 未安装" "$dir/rg.res"
|
|
336
|
-
fi
|
|
337
|
-
) &
|
|
338
|
-
|
|
339
|
-
wait
|
|
340
|
-
LAST_CHECK_DIR="$dir"
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
all_good() {
|
|
344
|
-
local keys="python3 python_alias pnpm dx agent_browser rg"
|
|
345
|
-
local k
|
|
346
|
-
for k in $keys; do
|
|
347
|
-
if ! check_ok "$k"; then
|
|
348
|
-
return 1
|
|
349
|
-
fi
|
|
350
|
-
done
|
|
351
|
-
[[ "$DX_FORCE_OK" == "1" ]]
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
print_report() {
|
|
355
|
-
local round="$1"
|
|
356
|
-
echo
|
|
357
|
-
echo "===== Doctor 报告(第 ${round} 轮)====="
|
|
358
|
-
printf '%-14s | %-4s | %-40s | %s\n' "检查项" "状态" "版本" "说明"
|
|
359
|
-
printf '%-14s-+-%-4s-+-%-40s-+-%s\n' "--------------" "----" "----------------------------------------" "------------------------------"
|
|
360
|
-
|
|
361
|
-
local keys="python3 python_alias pnpm dx agent_browser rg"
|
|
362
|
-
local k ok txt ver msg
|
|
363
|
-
for k in $keys; do
|
|
364
|
-
ok="$(read_field "$k" 2)"
|
|
365
|
-
txt="FAIL"
|
|
366
|
-
[[ "$ok" == "1" ]] && txt="PASS"
|
|
367
|
-
ver="$(read_field "$k" 3)"
|
|
368
|
-
msg="$(read_field "$k" 4)"
|
|
369
|
-
printf '%-14s | %-4s | %-40s | %s\n' "$k" "$txt" "$ver" "$msg"
|
|
370
|
-
done
|
|
371
|
-
|
|
372
|
-
if [[ "$DX_FORCE_OK" == "1" ]]; then
|
|
373
|
-
printf '%-14s | %-4s | %-40s | %s\n' "dx_force" "PASS" "@ranger1/dx@latest" "$DX_FORCE_MSG"
|
|
374
|
-
else
|
|
375
|
-
printf '%-14s | %-4s | %-40s | %s\n' "dx_force" "FAIL" "@ranger1/dx@latest" "$DX_FORCE_MSG"
|
|
376
|
-
fi
|
|
377
|
-
|
|
378
|
-
if command -v node >/dev/null 2>&1; then
|
|
379
|
-
echo "node: $(node -v 2>/dev/null | head -n1)"
|
|
380
|
-
fi
|
|
381
|
-
if command -v npm >/dev/null 2>&1; then
|
|
382
|
-
echo "npm: $(npm -v 2>/dev/null | head -n1)"
|
|
383
|
-
fi
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
for round in $(seq 1 "$MAX_ROUNDS"); do
|
|
387
|
-
echo "[doctor] 第 ${round}/${MAX_ROUNDS} 轮:并行检测"
|
|
388
|
-
run_parallel_checks "$round"
|
|
389
|
-
|
|
390
|
-
echo "[doctor] 第 ${round}/${MAX_ROUNDS} 轮:修复阶段"
|
|
391
|
-
|
|
392
|
-
if ! check_ok "python3"; then
|
|
393
|
-
echo "[doctor] 安装 python3"
|
|
394
|
-
install_python3 || true
|
|
395
|
-
fi
|
|
396
|
-
|
|
397
|
-
if ! check_ok "python_alias"; then
|
|
398
|
-
echo "[doctor] 建立 python -> python3 调用能力"
|
|
399
|
-
ensure_python_alias || true
|
|
400
|
-
fi
|
|
401
|
-
|
|
402
|
-
if ! check_ok "rg"; then
|
|
403
|
-
echo "[doctor] 安装 rg"
|
|
404
|
-
install_rg || true
|
|
405
|
-
fi
|
|
406
|
-
|
|
407
|
-
if ! check_ok "agent_browser"; then
|
|
408
|
-
echo "[doctor] 安装/升级 agent-browser 并安装 Chromium"
|
|
409
|
-
ensure_agent_browser || true
|
|
410
|
-
else
|
|
411
|
-
echo "[doctor] agent-browser 已存在,执行升级与 Chromium 安装"
|
|
412
|
-
ensure_agent_browser || true
|
|
413
|
-
fi
|
|
414
|
-
|
|
415
|
-
echo "[doctor] 强制执行 dx 安装与初始化"
|
|
416
|
-
force_dx || true
|
|
417
|
-
|
|
418
|
-
echo "[doctor] 第 ${round}/${MAX_ROUNDS} 轮:复检"
|
|
419
|
-
run_parallel_checks "${round}-post"
|
|
420
|
-
print_report "$round"
|
|
421
|
-
|
|
422
|
-
if all_good; then
|
|
423
|
-
echo "[doctor] 全部检查通过"
|
|
424
|
-
exit 0
|
|
425
|
-
fi
|
|
426
|
-
|
|
427
|
-
done
|
|
428
|
-
|
|
429
|
-
echo "[doctor] 达到最大轮次 ${MAX_ROUNDS},仍有未通过项"
|
|
430
|
-
exit 1
|