---
name: doctor
description: >
  本机环境体检：一条命令问清这台机器上哪些流程能跑、哪些会降级（缺 Python 包、git、node、gh，
  traces/ 能否解析，知识库索引在不在），并给出每项缺失的修法。任务开始前环境可疑、换机器、
  内网新部署、或流程中途以一句 import 报错退出时用。可反复执行：同一台机器两次跑结论一致。
---

# Doctor（本机环境体检）

依赖缺了不在开单时报，而是在任务中途以一句 traceback 冒出来。实测：缺 PyYAML 的机器上点「导出交接包」，面板只回一句「导出脚本没有给出 JSON 结果」，真因（`ModuleNotFoundError: No module named 'yaml'`）在 traceback 的最后一行，读者要自己认。全仓有一部分脚本在 import 阶段就依赖它，缺一个包会同时断掉几条流程。

本 skill 的动作只有一件：**跑体检器，然后按它给的顺序修一项、重跑一次**。

## 何时触发

- 用户问「这台机器能不能用」「装了什么」「为什么报缺依赖」；
- 流程中途出现 `ModuleNotFoundError`、`command not found`、面板报「没有给出 JSON 结果」这类只有结果没有病因的错；
- 换机器、换解释器、内网新部署第一次用；
- 修完一项要复验。

## 只跑一条命令

```bash
python3 scripts/doctor.py          # 人读三段：结论 / 证据 / 下一步
python3 scripts/doctor.py --json   # 面板或脚本消费
```

退出码：`0` 无阻断、`2` 有阻断项、`3` 不在 ascend-sleuth 检出里。

**不要自己另写探测命令**（手敲 `python3 -c "import yaml"` 只能验一个包，且会漏掉解释器不一致这一层）。体检器扫的是 `scripts/` 下全部脚本的 import，新增第三方包会被它自动发现。

## 怎么读输出

三段对应三个问题：

| 段 | 回答 | 用法 |
|---|---|---|
| 结论 | 哪些流程能跑 / 降级 / 不能跑 | 第一屏直接转述这三行，不要让用户自己读全表 |
| 证据 | 每一项缺什么、影响几个脚本、修法原值 | 修的时候照抄「修：」后面那条命令，不自己拼 |
| 下一步 | 按影响面排序的修复动作 | 一次只做第一条 |

报告末尾那行「体检用的解释器」必须带上：面板与终端可能不是同一个解释器（Windows 上按 `python3` → `python` → `py -3` 的顺序探活），把包装到另一个解释器上，体检结论不会变。

## 修复循环

1. 读结论段，记下第一条**阻断**项。
2. 只修那一项，命令照抄证据段给的「修：」原值。
3. 重跑同一条命令。
4. 阻断项清零后再处理降级项。
5. 结论与上一次不一致时先停：同一台机器两次跑出不同结果，说明环境在变（PATH、解释器、代理），先查这个再继续修。

## 硬规则

- 没跑就不许说「环境没问题」。结论只从脚本输出里取。
- 不许为了让检查变绿去改体检器或绕开它。它是判据，不是待办清单。
- 阻断与降级的判据不同：缺 Python 包是**阻断**（相关脚本在 import 阶段退出）；缺 `node`、`gh`、`git` 是**降级**（对应流程退化，其余照跑）。修阻断项优先。
- 修不了的项照实说。内网装不了包就给出带 wheel 的离线装法，不要再给一条本机执行不了的联网命令。
- 降级项不挡流程时不要顺手修：一次只动一项，修完重跑，否则下一轮分不清是谁改好了。

## 它不负责什么

- **内容门禁**：索引新鲜度、结构校验、面板契约由各自的检查脚本判，体检器不碰内容。
- **一次跑完所有 CI 命令**：那是 `python3 scripts/rehearse_evolve_loop.py`（逐条复跑 CI 的 `run:`）。体检器只在「下一步」里指出该跑哪一条，不抄那份清单。
- **装包与改配置**：它只读。装包是人的决定，装法由它给出、人执行。
