Read-only health inspection of the local development environment. Diagnoses "can it run", never tunes performance, never scans for threats, and never executes a fix — every finding carries a fix command for the user to run themselves.
Quick Guide (Chinese overview)
- What it does: Performs a read-only health check on the development environment (six dimensions: toolchain / environment variables / WSL interop / disk and inodes / port usage (specified via --ports) / dependency integrity), producing a
[级别] 标题+证据+修复命令report and a--jsonmachine-readable version. - When to use: For environment-related problems such as "it won't run on my machine / I clearly installed it but the command isn't found", "the proxy is set but packages won't install", "inside WSL it hits a Windows-side binary", etc.
- Core steps: Run
scripts/env-check.py(self-test must pass first with--selftest) → read each FAIL/WARN one by one → hand the commands on thefix:linesto the user as-is for confirmation and execution(this skill never executes them on the user's behalf) → for platform details, seereferences/。 - China accessibility boundaries: All checks are performed locally with zero dependencies and zero network (the proxy connectivity probe is a local port probe against the user's already-configured proxy); no external links, no downloads.
When to Use
- A build or install fails with environment-flavored symptoms:
command not foundfor an installed tool, wrong binary version, proxy timeouts. - User asks for an environment checkup / "体检" / "why won't it run on my machine".
- On WSL: suspicious Windows-side shadowing (
/mnt/c/...binaries winning), CRLF churn.
NOT for: performance tuning (CPU/memory/disk speed), security scanning of code or dependencies, project config review, AI session health.
Hard Constraints
- Read-only — probes are
which/--version/env parsing/socket connect to already-configured proxy ports/file reads. Nothing is written, nothing is installed, no service is started. - Never execute fixes — output fix commands as text. The user runs them. No
--fixmode exists (deliberately: repair actions are system-state changes). - Evidence, not vibes — every finding carries machine-collected evidence lines; if a signal can't be read, report it as such (SKIP/INFO), never guess.
- Criteria live in the script — PASS/WARN/FAIL thresholds are pinned in
scripts/env-check.py, not re-judged ad hoc. Run--selftestfirst if the script was just modified.
Workflow
- Run the checker:
Exit codes:python3 scripts/env-check.py # human report python3 scripts/env-check.py --json # machine-readable (schema env-check/1) python3 scripts/env-check.py --tools node,deno,go # extend the probe list python3 scripts/env-check.py --timeout 15 # per-probe timeout seconds (default 5) python3 scripts/env-check.py --ports 3000,8080 # check dev-port occupancy0= no FAIL (WARN allowed),1= at least one FAIL,2= usage error. - Read findings worst-first (FAIL → WARN). For each, relay the
fix:command to the user and wait for them to run it. - For platform-specific detail (what each check reads, where wsl.conf/.wslconfig live, macOS notes), consult
references/checks-linux-wsl.mdorreferences/checks-macos.md. - Re-run after fixes to confirm the FAIL cleared.
Check Matrix
| Dimension | Checks | Level logic |
|---|---|---|
| Toolchain | presence on PATH, --version runs, shadowing (distinct realpaths from which -a) |
missing/broken = WARN (may be intentional); shadowed = WARN |
| Env vars | PATH hygiene (missing / duplicate / empty entries), proxy reachability (socket probe), LANG | dead proxy = FAIL; PATH/locale = WARN |
| WSL interop | Windows PATH injection into WSL, /etc/wsl.conf disabling flags, core.autocrlf=true |
injection/autocrlf/disabled-flags = WARN |
| Disk & inodes | free space + inode usage for cwd's filesystem and / (deduped) |
free <512MB or <1% = FAIL; <2GB or <5% = WARN; inodes <1% = FAIL, <5% = WARN |
| Ports | occupancy of user-specified ports (--ports), owner via lsof/ss when available |
in use = WARN (killing is the user's call); none requested = SKIP |
| Dependency integrity | orphan node_modules, broken .bin symlinks, missing lockfile, broken venvs (pyvenv.cfg home + bin/python3) |
orphan/broken venv/broken bins = WARN; no lockfile = INFO |
Platform coverage: the matrix runs on WSL2/Linux, macOS (tested — see Platform Test Matrix), and plain Linux (container-tested); platform-specific reading detail lives in references/checks-linux-wsl.md (Linux/WSL) and references/checks-macos.md.
Deferred (do not improvise): version-manager conflict resolution beyond shadow reporting; a --fix whitelist mode (explicitly declined for now — user ruling: fixes are command suggestions only).
Output Contract
Human report: one line per finding — [LEVEL] title + evidence lines + fix: command. JSON: {"schema": "env-check/1", "platform": {...}, "findings": [{id, level, title, evidence[], hint}]} — the JSON field is hint; the human report renders the same value as the fix: line. Levels: PASS / INFO / WARN / FAIL / SKIP. Downgrade, don't fabricate: an unreadable signal becomes INFO/SKIP with the reason.
Platform Test Matrix (evidence-pinned, rule-12 style)
| Platform | Status | Evidence |
|---|---|---|
| WSL2 / Linux (this host) | tested 2026-10-01 | normal run exit=0 (PASS=6/WARN=2, live findings); FAIL path exit=1 (HTTPS_PROXY=http://127.0.0.1:9 → ConnectionRefusedError); non-WSL SKIP branch returns wsl.not-wsl without touching /etc/wsl.conf; selftest 34/34 (v1.1.0); 1.1 dims (disk/ports/dep-integrity) re-tested live |
macOS 15.8.1 (x86_64, ssh host mac) |
tested 2026-10-01 | selftest 34/34 (v1.1.0); normal run exit=0 with real findings (MacPorts git shadowing /opt/local/bin/git vs /usr/bin/git = true positive; node/npm absent = WARN; WSL section SKIP); FAIL path exit=1; --json valid (schema env-check/1). macOS run exposed a selftest platform assumption (an assertion read live /proc/version) — fixed to injected fixture in 1.0.2; second platform assumption (live /run/WSL check inside the WSL-branch assertion) fixed likewise in 1.0.3; 1.1 dims re-tested live (disk PASS, port probe, orphan-node_modules fixture) |
| Windows native (no WSL) | out of design scope, untested | host has only the Microsoft Store python stub (no real interpreter); probes assume POSIX PATH//proc. Windows coverage = the WSL2 row above (t000016 first-driver scenario) |
Plain Linux (non-WSL, docker ubuntu:24.04 container) |
tested 2026-10-01 | selftest 34/34 (v1.1.0); normal run exit=0 (true-positive WARNs re-confirmed); FAIL path exit=1; wsl=False in JSON; orphan-node_modules fixture fires. Container test caught a real bug: /proc/version shows the WSL2 host kernel inside containers → is_wsl now requires /run/WSL to confirm kernel-string signal (v1.0.3) |
Re-run on new hardware, then update this table with date + findings summary. Absence of a row entry means untested.
Completion Criteria
- Checker ran with exit code reported; every FAIL/WARN has been relayed to the user as commands, not executed.
- No fix was executed by the agent; no file outside the working scratch was written.
- If the user wants a fix applied, they run the command; a follow-up
env-checkrun confirms.