writing-skills
writing-skills
Use when creating new skills, editing existing skills, or verifying skills work before deployment
env-doctorDiagnose and fix a broken local development environment methodically - reproduce the failure, fingerprint runtime, package manager, OS and arch, check version files and PATH shadowing, lockfile drift, missing env vars (without printing secrets), port conflicts, stale caches, native-module and Apple Silicon arch mismatches, and Docker context - then apply th…
Independent GitHub snapshot · Automated classification · Original documentation governs
"Works on my machine" failures are almost always a difference in versions, paths, or state - not in code. The costly mistake this skill prevents is the shotgun fix: deleting node_modules and the lockfile, upgrading the runtime, reinstalling the toolchain, and reaching a state that happens to work but that nobody can explain or reproduce - while silently upgrading dozens of dependencies and leaking secrets into a pasted log along the way.
Diagnose before changing anything. Each step either finds the cause or rules out a whole class.
Run the failing command yourself and save the full output, not a summary. Note the first error, not the last - later errors are usually fallout. Ask: did this ever work on this machine? What changed since (OS update, new branch, brew upgrade, new terminal app)? If it works on CI or a teammate's machine, that machine is your reference.
uname -sm # OS and hardware arch
node -v; node -p process.arch # runtime and the arch it runs as
npm -v; pnpm -v; yarn -v # whichever is relevant
python3 -V; python3 -c "import platform; print(platform.machine())"
git rev-parse --short HEAD; git status --short | head
Compare against what the project declares: .nvmrc, .node-version, .tool-versions, .python-version, engines and packageManager in package.json, requires-python in pyproject.toml, rust-toolchain.toml, go.mod. A mismatch here is the cause more often than not.
which -a node python3 pnpm # every match on PATH, in order
type node # alias/function/hashed path?
echo "$PATH" | tr ':' '\n'
Look for shadowing: a Homebrew or system binary ahead of the version manager's shim, a stale global install, a shell alias. Check that the version manager is initialized in the shell actually in use (zsh vs bash profile, IDE terminals that skip login profiles). Run hash -r (or open a new shell) after PATH changes.
package-lock.json plus pnpm-lock.yaml means two package managers fought; use the one packageManager names (Corepack, if enabled, pins that manager's version; it does not block other managers).npm ci, pnpm install --frozen-lockfile, yarn install --immutable (Yarn 1: --frozen-lockfile), uv sync --locked, poetry install after poetry check --lock. If strict install fails, the manifest and lockfile have drifted - fix that on purpose, in its own commit.pip check or npm ls surface broken or conflicting installs. In Python, confirm the active interpreter is the project venv: python -c "import sys; print(sys.prefix)".Compare required keys, never values:
comm -23 <(grep -oE '^[A-Za-z_][A-Za-z0-9_]*' .env.example | sort -u) \
<(grep -oE '^[A-Za-z_][A-Za-z0-9_]*' .env | sort -u)
Output is the list of missing keys. To test presence in the live shell, print set/MISSING per name, or a value's length - never the value. Also check for a variable set in the shell that overrides .env (many loaders do not overwrite existing variables), and for the wrong env file being loaded (.env.local vs .env.development).
lsof -nP -iTCP:3000 -sTCP:LISTEN # macOS/Linux
ss -ltnp | grep :5432 # Linux
netstat -ano | findstr :3000 # Windows
A zombie dev server or a second Postgres (Homebrew plus Docker) on the same port causes "address in use" or, worse, silent connections to the wrong database. Confirm dependent services are up and reachable at the host and port the app is configured for.
Stale output survives version changes: .next, dist, build, node_modules/.cache, .turbo, __pycache__, .pytest_cache, target/, Xcode DerivedData. Delete build artifacts first - they are always safe to regenerate. Reinstall dependencies from the lockfile second. Package-manager caches (npm cache verify, pnpm store prune) last.
Native addons are compiled for one runtime ABI and one CPU arch. Signs: NODE_MODULE_VERSION mismatch, wrong ELF class, incompatible architecture (have 'x86_64', need 'arm64'), mach-o file, but is an incompatible architecture. On Apple Silicon, a terminal running under Rosetta reports uname -m as x86_64 and installs x64 binaries. Fix the runtime/arch first, then rebuild (npm rebuild, or a clean install).
docker context show; docker context ls
echo "${DOCKER_HOST:-unset}"
docker version --format '{{.Server.Os}}/{{.Server.Arch}}'
Colima, OrbStack, Docker Desktop, and remote contexts each have their own images, volumes, and networks - "my container vanished" is often a switched context. On Apple Silicon, amd64-only images need --platform linux/amd64 and run emulated. Inside containers, localhost is the container; use the service name or host.docker.internal (on Linux Docker Engine this needs --add-host=host.docker.internal:host-gateway).
Change one thing, rerun the reproduction, confirm it passes. Then make the fix durable for the next person: pin the version file, add the missing key to .env.example, document the service, or add a preflight check script. Write the setup note with the cause, the fix, and the verification command.
$ npm run dev
Error: The module '.../node_modules/better-sqlite3/build/Release/better_sqlite3.node'
was compiled against a different Node.js version using NODE_MODULE_VERSION 115.
This version of Node.js requires NODE_MODULE_VERSION 127.
ABI 115 is Node 20; 127 is Node 22. Native module built under 20, now running 22.
$ cat .nvmrc -> 20
$ node -v -> v22.11.0
$ which -a node
/opt/homebrew/bin/node
/Users/dev/.nvm/versions/node/v20.18.0/bin/node
Cause: brew upgrade pulled in Homebrew's node as a dependency of another formula; /opt/homebrew/bin precedes the nvm path in .zshrc, so it shadows the project's pinned version.
Fix, smallest first:
$ nvm use -> Now using node v20.18.0
$ hash -r && node -v -> v20.18.0
$ npm run dev -> ready on http://localhost:3000
No rebuild needed - the addon was already built for 20. Durable fix: move nvm initialization after Homebrew's PATH setup in ~/.zshrc, add "engines": {"node": ">=20 <21"}, and add a note to the README's setup section. The lockfile and node_modules were never touched.
A short diagnosis report: the reproduction command and first error, the environment fingerprint versus the project's declared versions, the root cause with evidence, the minimal fix applied, the verification output, and a setup note (README section, version file, .env.example entry, or preflight script) that prevents recurrence.
rm -rf node_modules package-lock.json - it hides the cause and upgrades transitive dependencies.sudo npm install -g or sudo pip install to fix permission errors - fix the prefix or use a version manager or venv.