Root-caused in the plugin-quality audit applied in #2695 (markdown-format 0.11.17): hook processes inherit Claude Code's own process environment ("Handlers run in the current directory with Claude Code's environment" — hooks reference, fetched 2026-08-15), not the interactive shell's profile. In cloud sessions Claude Code is spawned by a supervisor without profile sourcing, so its PATH lacks the nvm layer where the SessionStart bootstrap installs tools globally — while the Bash tool (profile-initialized, sources /root/.bashrc → nvm) sees them fine. Result: command -v <tool> fails in every hook, formatting/linting silently dead for the whole session class while the environment reports the tool present. Reproduced byte-for-byte under each PATH (audit packet markdown-format/20260815T104849Z, session a86db903). Related closed precedent: #811 (node not found in scheduled-task sessions — same environment class).
#2695 fixed markdown-format specifically (accurate notice + markdownlint-cli2 as an exact repo devDependency, since its node_modules/.bin probe is filesystem-based and PATH-independent). Remaining fleet-wide work:
- Sweep every sibling formatter/lint hook that probes
command -v (bash-format/shfmt, typos-format, biome-format, go-format, powershell-format, ruff-format, eol-normalizer, actionlint, …) for the same failure mode; give each an accurate degraded-mode notice and, where the tool has an npm/pip-installable form, a repo-local filesystem probe.
- markdown-format monorepo residual (recorded in its CHANGELOG):
resolve_repo_markdownlint checks only the repo root's node_modules/.bin; workspace-level installs (packages/x/node_modules/.bin) are invisible. Walk from the edited file's directory up to $REPO_ROOT, keeping the per-directory symlink-containment check.
- Bootstrap/environment hardening: install fleet tools into a directory on the harness process PATH (or
npm i -D into the repo) rather than npm i -g under nvm, so hooks and Bash agree.
- Optional per-plugin
<tool>_binary userConfig pin (absolute path, validated -x, never shell-interpolated) for consumers who cannot do a repo-local install.
Root-caused in the plugin-quality audit applied in #2695 (markdown-format 0.11.17): hook processes inherit Claude Code's own process environment ("Handlers run in the current directory with Claude Code's environment" — hooks reference, fetched 2026-08-15), not the interactive shell's profile. In cloud sessions Claude Code is spawned by a supervisor without profile sourcing, so its PATH lacks the nvm layer where the SessionStart bootstrap installs tools globally — while the Bash tool (profile-initialized, sources
/root/.bashrc→ nvm) sees them fine. Result:command -v <tool>fails in every hook, formatting/linting silently dead for the whole session class while the environment reports the tool present. Reproduced byte-for-byte under each PATH (audit packetmarkdown-format/20260815T104849Z, session a86db903). Related closed precedent: #811 (node not found in scheduled-task sessions — same environment class).#2695 fixed markdown-format specifically (accurate notice +
markdownlint-cli2as an exact repo devDependency, since itsnode_modules/.binprobe is filesystem-based and PATH-independent). Remaining fleet-wide work:command -v(bash-format/shfmt, typos-format, biome-format, go-format, powershell-format, ruff-format, eol-normalizer, actionlint, …) for the same failure mode; give each an accurate degraded-mode notice and, where the tool has an npm/pip-installable form, a repo-local filesystem probe.resolve_repo_markdownlintchecks only the repo root'snode_modules/.bin; workspace-level installs (packages/x/node_modules/.bin) are invisible. Walk from the edited file's directory up to$REPO_ROOT, keeping the per-directory symlink-containment check.npm i -Dinto the repo) rather thannpm i -gunder nvm, so hooks and Bash agree.<tool>_binaryuserConfig pin (absolute path, validated-x, never shell-interpolated) for consumers who cannot do a repo-local install.