Skip to content

Fix: keep the node runtime probe clear of cmd.exe metacharacters - #458

Merged
pbakaus merged 1 commit into
pbakaus:mainfrom
rlorenzo:fix/node-probe-volta-windows
Aug 3, 2026
Merged

Fix: keep the node runtime probe clear of cmd.exe metacharacters#458
pbakaus merged 1 commit into
pbakaus:mainfrom
rlorenzo:fix/node-probe-volta-windows

Conversation

@rlorenzo

@rlorenzo rlorenzo commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Adjacent to but distinct from #452 and #453: that pair fixes the POSIX [ ! -f ... ] guard the installer writes in cli/bin/commands/skills.mjs. This fixes the >= in the node -e probe emitted by scripts/lib/transformers/hooks.js. No shared code, no overlapping files.

Summary

The Node guard from #423 probes with >=:

node -e "process.exit(parseInt(process.versions.node,10)>=22?0:1)"

Volta's Windows shims exec through cmd /C, which claims the > as redirection. The probe fails with The filename, directory name, or volume label syntax is incorrect and exits 1 before node runs, so the guard reports no usable Node and hook.mjs never runs. 2>/dev/null hides the error and the node-unsupported marker shows the notice only once, so the hook is silently dead on every PostToolUse and Stop. Upstream: volta-cli/volta#1791.

Fix. Clamp instead of compare. Same floor test, same ES5-only syntax, no character cmd.exe can claim:

node -e "process.exit(Math.min(parseInt(process.versions.node,10),22)===22?0:1)"

Verification. Each version pinned with volta pin, so node resolves through the shim:

Node old >= new clamp
24.16.0 exit 1, cmd.exe parse error exit 0
24.14.0 exit 1, cmd.exe parse error exit 0
22.18.0 exit 1, cmd.exe parse error exit 0
20.6.1 exit 1, cmd.exe parse error exit 1, silent

The 20.6.1 row shows the old probe returned the right answer for the wrong reason: it never evaluated the version at all.

tests/hook-build.test.mjs gains one assertion that the probe carries no character cmd.exe re-parses. The suite already asserts every manifest command contains NODE_PROBE, so that covers all seven builders.

The six regenerated manifests are included because the generated command string is the artifact being fixed, and hook-build.test.mjs asserts committed manifests match the builders.

Type of change

  • New command
  • New / updated skill reference
  • New anti-pattern guidance
  • Bug fix
  • Documentation update
  • Build system / tooling
  • Other:

Checklist

  • Source files updated in source/ (no source/ dir in the repo today; the source change is scripts/lib/transformers/hooks.js)
  • bun run build ran successfully
  • bun test passes (green in CI on Node 22.12.0 and 24; a local Windows run has pre-existing symlink-EPERM and path-separator failures that reproduce identically on unmodified main)
  • Tested with at least one provider (Cursor / Claude Code / Gemini CLI / Codex / Copilot / Grok Build / Kiro / OpenCode / Qoder / Mistral Vibe) (Claude Code: ran the generated PostToolUse command end-to-end with CLAUDE_PROJECT_DIR set; probe passed and hook.mjs executed)
  • README / DEVELOP.md updated if needed
  • I reviewed the full diff myself before requesting human review
  • I disclosed any AI assistance in this PR and related commits/comments, or no AI assistance was used

Note

Low Risk
Localized change to hook shell command strings and build output; same version floor semantics, with lower risk on Windows Volta/cmd paths.

Overview
Fixes silent hook failure on Windows when Node is launched through Volta shims (and similar cmd /C paths): the runtime guard’s node -e probe used >=, which cmd.exe treats as redirection, so the probe exited before Node ran and manifests behaved as if no supported Node was on PATH.

The probe in scripts/lib/transformers/hooks.js now uses a clamp (Math.min(...)===22) with the same Node 22 floor and ES5-only syntax, but without <, >, or newlines in the one-line -e payload. All six regenerated hook manifests (Claude, Codex, Cursor, GitHub, Grok, plugin) pick up the new command string.

tests/hook-build.test.mjs updates the shared NODE_PROBE expectation and adds a test that the probe contains no cmd.exe-unsafe characters; existing checks still require every manifest command to include that probe.

Reviewed by Cursor Bugbot for commit 9c5e38b. Bugbot is set up for automated code reviews on this repo. Configure here.

Volta's Windows shims exec through `cmd /C`, which re-parses the argument
list, so the `>=` inside the probe's `node -e` payload was read as output
redirection. The command died with "The filename, directory name, or volume
label syntax is incorrect" before node started, the guard read that as a
missing runtime, and the hook it exists to protect was disabled on every
PostToolUse and Stop. A user on a supported Node 24 got a one-time notice
telling them to install Node 22, then silence.

Clamping with Math.min is the same floor test in the same ES5-only syntax,
with no character cmd.exe can claim. Verified through the Volta shim on Node
24.16.0 and 22.18.0 (exit 0) and against a real Node 20.6.1 binary (exit 1),
so the floor is unchanged. Adds a regression test asserting no `<`, `>`, or
newline reaches any generated `node -e` payload.

Upstream cause: volta-cli/volta#1791.

Prepared with AI assistance (Claude Code).
@rlorenzo
rlorenzo requested a review from pbakaus as a code owner July 31, 2026 03:16
Copilot AI review requested due to automatic review settings July 31, 2026 03:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a Windows-specific failure mode for the design hook runtime guard when node resolves through Volta shims (which execute via cmd /C). The previous node -e probe used >=, which cmd.exe can misinterpret as redirection, causing the probe to fail before Node runs and silently disabling the hook.

Changes:

  • Replace the >= version-floor check in the node -e probe with a clamp-based check (Math.min(... ) === floor) that avoids cmd.exe-claimed characters.
  • Update hook build tests to expect the new probe and assert the probe payload contains no <, >, or newline characters.
  • Regenerate and commit the affected hook manifest artifacts so they reflect the corrected probe.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.

Show a summary per file
File Description
scripts/lib/transformers/hooks.js Updates the shared NODE_PROBE string used to guard hook execution so it avoids cmd.exe redirection metacharacters.
tests/hook-build.test.mjs Updates the expected probe expression and adds an assertion ensuring the probe payload contains no <, >, or newlines.
plugin/hooks/hooks.json Regenerated plugin hook manifest with the updated probe string.
.claude/settings.json Regenerated Claude settings hooks with the updated probe string.
.codex/hooks.json Regenerated Codex hooks with the updated probe string.
.cursor/hooks.json Regenerated Cursor hooks with the updated probe string.
.github/hooks/impeccable.json Regenerated GitHub hooks with the updated probe string.
.grok/hooks/impeccable.json Regenerated Grok hooks with the updated probe string.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@greptile-apps

greptile-apps Bot commented Jul 31, 2026

Copy link
Copy Markdown

Greptile Summary

This change makes generated hook commands safe for Volta’s Windows cmd.exe shim by replacing the Node-version comparison with a metacharacter-free equivalent. The emitted probes were checked across all hook builders and committed manifests: Node 20 is rejected, Node 22 and 24 are accepted, and no probe contains angle brackets or line breaks. The focused hook-manifest builder tests passed. No defects were found; this is safe to merge.

Confidence Score: 5/5

T-Rex T-Rex Logs

What T-Rex did

  • Ran an authored validator against the parent revision and this revision, loading eight hook-manifest builder variants and six committed hook manifests, and exercised the emitted Node probe with mocked Node 20.6.1, 22.0.0, and 24.16.0.
  • Observed that the parent revision emitted cmd.exe-reparsed angle brackets across all 24 inspected payloads while the new revision produced zero unsafe payloads, and the focused hook-manifest builder suite passed with 9 tests and 0 failures.
  • After changes, the validator run produced a controlled exit path via the post-change exit expression with the expected mocked exit codes and zero unsafe payloads, and the authored validator source was included as an artifact with repository changes avoided.

View all artifacts

T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "Fix: keep the node runtime probe clear o..." | Re-trigger Greptile

@github-actions github-actions Bot added the ready to merge Passing, resolved, and ready for a maintainer merge decision label Jul 31, 2026
@github-actions github-actions Bot added needs maintainer review Ready for a maintainer to review or decide and removed ready to merge Passing, resolved, and ready for a maintainer merge decision labels Aug 3, 2026

@pbakaus pbakaus left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@pbakaus
pbakaus merged commit 14d2641 into pbakaus:main Aug 3, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs maintainer review Ready for a maintainer to review or decide policy: approved

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants