Skip to content

fix: use ${PLUGIN_ROOT} so the Stop hook runs on Windows - #64

Open
milanagm wants to merge 1 commit into
langfuse:mainfrom
milanagm:fix/windows-hook-plugin-root-minimal
Open

fix: use ${PLUGIN_ROOT} so the Stop hook runs on Windows#64
milanagm wants to merge 1 commit into
langfuse:mainfrom
milanagm:fix/windows-hook-plugin-root-minimal

Conversation

@milanagm

@milanagm milanagm commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Problem

Codex runs command hooks through the platform shell: $SHELL -lc on Unix, but %COMSPEC% /C, i.e. cmd.exe, on Windows (codex-rs/hooks/src/engine/command_runner.rs). cmd.exe has no POSIX parameter expansion, so the Stop hook command

node "${CODEX_HOME:-$HOME/.codex}/plugins/cache/codex-observability-plugin/tracing/0.1.0/dist/index.mjs"

reached node as a literal string, and the hook always exited 1 on Windows.

Fix

node "${PLUGIN_ROOT}/dist/index.mjs"

Codex substitutes ${PLUGIN_ROOT} in a plugin hook's command line itself, literally and on every platform, before any shell sees the line (openai/codex#19705, codex-rs/hooks/src/engine/discovery.rs). That support shipped on 2026-04-28, so it predates every Codex version in the reports (0.135.0 and newer). It also drops the hardcoded 0.1.0, so the path no longer has to be bumped in lockstep with the plugin version.

Why the test file changes too

The existing test could not have caught this, and it also blocked those two PRs:

  • it spawned the command with shell: true, so it always got a POSIX shell, and CI is ubuntu-latest only
  • it set CODEX_HOME but never PLUGIN_ROOT, so ${PLUGIN_ROOT} expanded to the empty string and the executing test exited 1
  • it asserted the version-pinned cache path, which this fix removes

Net effect is a smaller test file. stageInstalledPlugin and readPluginVersion existed only to serve the old hardcoded path, and the staged plugins/cache/.../0.1.0/ tree existed only so that path had something to find. The test now points PLUGIN_ROOT at the real plugin directory and executes the committed bundle.

The one genuinely new assertion is the regression guard:

expect(readHookCommand().replaceAll("${PLUGIN_ROOT}", "")).not.toContain("$");

${PLUGIN_ROOT} is resolved by Codex. Any other $ can only be resolved by a shell, and on Windows that shell is cmd.exe. This is the only assertion that separates "works on Linux" from "works on Windows", which matters because CI never runs Windows.

Verification

  • pnpm test 40/40 green, pnpm run lint clean
  • reverting hooks.json alone fails 2 of the 3 tests on Linux
  • a command that works on Linux but breaks on Windows, "${NODE_BINARY:-node}" "${PLUGIN_ROOT}/dist/index.mjs", fails the guard alone while the executing test stays green

Fixes #55
Fixes #32

LFE-15599

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@CodeCLS
CodeCLS requested a review from hassiebp August 28, 2026 14:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stop hook fails on Windows: hooks.json command uses bash-only ${VAR:-default} syntax, no shell interprets it Not support on windows?

1 participant