@ranger1/dx 0.1.98 → 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/lib/validate-env.js +21 -5
- package/package.json +1 -1
- package/skills/doctor/SKILL.md +44 -41
- package/skills/doctor/agents/openai.yaml +5 -2
- package/skills/git-pr-ship/SKILL.md +7 -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/doctor/scripts/doctor.sh +0 -430
|
@@ -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
|