feat(doctor): 新增執行環境體檢技能

What: 新增 jsc-cli:doctor,一次體檢技能版本、Hook 接線、全域設定與自我設定,只讀不改,結果寫進 wiki CHECK_{HASH}。
Why: 安裝或更新技能組之後,沒有任何工具說得出這台機器還缺什麼。設定散在環境變數、rc 檔與專案目錄,出錯時只能一個一個猜。
How: 版本比對複用 jsc-hooks 的 version-guard.sh report,接線狀態複用新加的 wire-cli.sh status(唯讀),設定則由 tools/scan-config.sh 比對 tools/config-spec.tsv 判定必要或選擇。規格表是必要性的唯一判準,掃描只負責抓出漏登錄的變數。
Who: 體檢與修復流程,搭配 jsc-cli:setup 收尾。
This commit is contained in:
2026-08-26 10:43:37 +08:00
parent 08ad10353b
commit 2adf9a172e
5 changed files with 424 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
---
name: doctor
description: Health-check the execution environment in one pass and record the result, changing nothing. Four checks - plugin versions from jsc-hooks/hooks/version-guard.sh report, hook wiring from jsc-hooks/tools/wire-cli.sh status, global settings and current-directory settings from tools/scan-config.sh against tools/config-spec.tsv. Report one findings table per check, then write the whole run to wiki CHECK_{HASH} where HASH comes from {hostname}/{user}; the page keeps only the latest run. Use after installing or updating the skill set, when a skill fails on a settings or wiring problem, or before handing a machine over; not for applying fixes, which is jsc-cli:setup.
---
# doctor — execution environment health check
Read-only. Every command below either reads a file or asks Gitea; none of them writes a setting. That is the contract with `jsc-cli:setup`: doctor states the facts, setup changes things.
Collection (steps 1 to 4) **MUST run as a sub agent** — one sub agent for all four, returning the raw TSV lines. Only the report and the wiki write stay in the main agent.
## 1. Skill versions
Run `jsc-hooks/hooks/version-guard.sh report`. It prints `{domain}<TAB>{本機}<TAB>{遠端}<TAB>{落後|最新|超前|查詢失敗}` per plugin, then `behind<TAB>{count}`.
A report with no `{domain}` row, or one carrying `noregistry<TAB>{path}`, means this CLI has no local plugin registry. Report it as 無法驗證 — never as 最新. `behind<TAB>0` proves nothing when no domain row precedes it.
Done when every installed domain has a status literal, or the CLI is reported as unverifiable.
## 2. Hook wiring
Run `jsc-hooks/tools/wire-cli.sh status {cli}` for every CLI that `tools/detect-clis.sh` found. Use `status` and nothing else: `wire-cli.sh` without a subcommand rewires, `purge` deletes, and `smoke` executes hooks — all three break the read-only contract.
Exit codes: 0 wired, 1 degraded, 3 skipped (CLI not installed), 5 unwired. Each `item` line names one wiring point and whether it is present.
Only claude reaches `wired`. The other four have no pre-tool hook, so `degraded` is their healthy state — report the degradation reason as-is and never present it as a defect to fix.
Done when every detected CLI has a status and its missing items are listed.
## 3. Global settings
Run `tools/scan-config.sh scan global`. It checks every `scope=global` row of `tools/config-spec.tsv` and prints `item<TAB>scope<TAB>required<TAB>actual<TAB>expect<TAB>fix<TAB>verdict`, closing with `summary<TAB>{missing}<TAB>{invalid}<TAB>{unset}<TAB>{skipped}`.
Verdicts: `ok`, `default` (unset, default works), `unset` (optional, feature degrades), `missing` (required, skills break), `invalid` (set but fails verification), `skipped` (offline).
Add `-o` when Gitea is unreachable; the Gitea-dependent rows then come back `skipped`. Report those rows as 未取得結論 and never as passes.
Also run `tools/scan-config.sh orphans` — variables used in the source but absent from the spec table. They are a maintenance note for the skill set, not a fault on this machine.
Done when the summary line is read and every `missing` and `invalid` row is named.
## 4. Own settings
Run `tools/scan-config.sh scan project` from the current working directory. Same output format, `scope=project` rows only.
Say which directory was scanned in the report. A project-scope result is meaningless without it, because the answer changes with every `cd`.
When `.env` or `.envrc` exists, name the spec-table variables it overrides and state the value actually in effect. A global setting silently overridden here is the failure this check exists to catch.
Done when the scanned directory is stated and every project row has a verdict.
## 5. Report and record
Report all four tables per `templates/check-page.md`. Then build the 待修項目 table from every `missing`, `invalid` and `unwired` item, plus every domain reported 落後. Order them `missing` → `invalid` → `unwired` → `落後`. Nothing wrong → one row reading 無.
Write the page through `jsc-gitea:wiki`:
- Wiki repo: `jsc-gitea/tools/gitea.sh wiki-repo CHECK`.
- Page name: `CHECK_` plus `gitea.sh hash-id "{hostname}/{user}"` — the host and the login account, not `{owner}/{repo}`. Doctor checks a machine, and it has to work in directories that are not repositories at all.
- Overwrite the whole page. This page type keeps only the latest run.
- Update `CHECK_CONTENTS` from `templates/check-contents.md` in the same pass.
`wiki-repo` exiting 3 means no wiki repo is configured for CHECK. Print the tables, skip the wiki write, and put `JSC_WIKI_REPO_CHECK` at the top of 待修項目 — that unset variable is itself a finding, so a failed write never fails the health check.
Done when either the wiki page URL is reported, or the skipped write is reported together with the reason.
## 6. Hand off
State the counts: required items missing, settings invalid, CLIs unwired, domains behind. Recommend `/jsc-cli:setup` when any of those is above zero. Never fix anything here.
Done when the counts are stated and the recommendation is given or explicitly withheld.