Plugin PostToolUse hook relative command resolves from workspace cwd

Open 💬 1 comment Opened Jun 5, 2026 by igorshos

Summary

Plugin-defined PostToolUse hooks can fail with exit 127 when their command is a relative path, because Codex appears to run the hook from the edited workspace cwd instead of the plugin root / hooks.json directory.

Environment

  • Codex CLI: codex-cli 0.136.0
  • Codex surface: Desktop app / local Codex runtime on macOS
  • Platform: macOS arm64
  • Plugin hook source observed locally:
  • <home>/.codex/.tmp/plugins/plugins/<plugin-name>/hooks.json

Observed behavior

After a normal file edit in an unrelated repository, Codex reported:

PostToolUse hook (failed)
error: hook exited with code 127

The edited repository was:

<workspace>/<repo-worktree>

The repository does have Git hooks under .githooks, but it does not define PostToolUse, .claude, or hooks.json. Searching the repo for PostToolUse returned no repo-local hook config.

The only matching PostToolUse hook was plugin-local:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "./scripts/post_write_figma_parity_check.sh"
          }
        ]
      }
    ]
  }
}

The script exists and is executable under the plugin:

<home>/.codex/.tmp/plugins/plugins/<plugin-name>/scripts/post_write_figma_parity_check.sh

But the edited workspace has no ./scripts directory. If Codex executes the hook from the workspace cwd, the relative command resolves to:

<workspace>/<repo-worktree>/scripts/post_write_figma_parity_check.sh

That path does not exist, which explains exit 127.

Expected behavior

For plugin-defined hooks, relative hook commands should resolve relative to the plugin root or the directory containing the plugin hooks.json, not the edited workspace cwd.

For the example above, Codex should effectively run the hook with one of these safe behaviors:

  • set hook execution cwd to <home>/.codex/.tmp/plugins/plugins/<plugin-name>, then run ./scripts/post_write_figma_parity_check.sh
  • or resolve the command to <home>/.codex/.tmp/plugins/plugins/<plugin-name>/scripts/post_write_figma_parity_check.sh

Codex should still expose the edited workspace cwd separately to the hook, for example:

CODEX_TOOL_CWD=<workspace>/<repo-worktree>
CODEX_HOOK_ROOT=<home>/.codex/.tmp/plugins/plugins/<plugin-name>

Why this matters

Plugin hooks are packaged with plugin-local scripts. If relative commands are interpreted from the active workspace, a valid plugin hook can fail in unrelated repos or accidentally run a same-named workspace-local script.

This is both a reliability issue and a hook provenance/safety issue.

Suggested fix

  • Track hook source metadata when loading plugin hook configs.
  • For plugin hooks, resolve relative command paths against the plugin root / hook config directory.
  • Preserve the tool invocation cwd as explicit payload/env metadata rather than overloading process cwd.
  • Include hook source path and resolved command in hook failure output.

Suggested regression test

  1. Create a fake plugin with:
  • hooks.json
  • scripts/hook.sh
  1. Configure hooks.json with a relative command:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "./scripts/hook.sh"
          }
        ]
      }
    ]
  }
}
  1. Run an edit/write tool from a different workspace cwd that has no ./scripts.
  2. Assert the hook succeeds and the invoked script path is the plugin-local script.
  3. Assert hook failure output includes the hook config source path and resolved command.

Related issues

This is related to plugin/hook runtime behavior, but appears distinct from:

  • #17331, which covered plugin hook loading and plugin hook path resolution at the manifest/config level.
  • #24211, which covers user-level PostToolUse hooks being ignored while plugin PostToolUse hooks run.

This report is specifically about command execution cwd / relative command resolution for a plugin-defined hook that is already being executed.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗