Inconsistent working directory for plugin hook commands

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

It appears that the actual working directory used when executing a command in a plugin’s hooks.json does not match Cursor’s documentation and error messages.

When a plugin hook executes a command, the working directory is actually the currently opened folder in Cursor. However, when a file specified in the plugin cannot be found, Cursor IDE displays the following message:

Note that paths are relative to the hooks.json file.

This statement suggests that paths are resolved relative to the location of hooks.json, which contradicts the observed behavior.

Below is a detailed explanation of how I discovered this problem, based on my experience working with three different plugins.

New users in this forum are allowed to include at most 2 links. So I basically removed all links.


Superpowers Plugin

After installing Superpowers, I encountered an issue: the plugin’s command invokes a .sh file, which cannot be executed directly on Windows. I looked into its hooks.json and related issues, and then I learned that ${CLAUDE_PLUGIN_ROOT} can be used inside command to reference the plugin’s installation directory (e.g., ~/.cursor/plugins/cache/cursor-public/superpowers/a0b9ecce2b25aa7d703138f17650540c2e8b2cde).

This is the very first time I found the use of ${CLAUDE_PLUGIN_ROOT} in configuring hooks.json. I thought this was the correct way to reference the plugin’s installation directory.

BTW, that Windows-related problem was resolved by manually modifying the installed hooks.json as described here.
And I switched to using WSL afterward.


Continual Learning Plugin

After installing Continual Learning, I found that bun could not locate the .ts file it was supposed to execute.

Because I had learned about ${CLAUDE_PLUGIN_ROOT} from Superpowers, and noticed that Continual Learning’s hooks.json uses relative paths.

I assumed the issue was caused by incorrect path usage and submitted a fix and opened a PR at here.

When I was testing this problem, I manually modified the hooks.json in order to figure out what was the working directory of command in hooks.json:

{
  "version": 1,
  "hooks": {
    "stop": [
      {
        "command": "echo '{\"pwd\": \"'$PWD'\"}'"
      }
    ]
  }
}

The result showed that the working directory of command in hooks.json is the currently opened folder in Cursor.

This discovery is critical in understanding the inconsistency.


Runlayer Plugin

After installing Runlayer, I encountered another issue: the hooks.json was missing the required version field. I immediately reported this here.

After that, I manually added the version field locally, and was going to continue using this plugin. That’s when I noticed that Runlayer’s hooks.json also uses relative paths.

Before I started looking for more documentation, I saw the Cursor IDE displaying the following notice:

Warning: File not found at path: ./scripts/runlayer-hook.sh
Note that paths are relative to the hooks.json file

This directly contradicts the actual observed behavior: the working directory is the currently opened project folder.


Core Issue

The real execution working directory of plugin hook command is the currently opened folder in Cursor.

But:

  • Cursor IDE’s error message states that paths are relative to hooks.json
  • Cursor’s documentation examples use relative paths

This inconsistency leads to confusion.

Postscript

When I initially submitted the PR for Continual Learning, Cursor did not explicitly display a clear “file not found” error for the missing .ts file. I assume that as for commands which are not direct .sh files, Cursor likely does not validate the inner file path, nor does it need to.

I thought that using ${CLAUDE_PLUGIN_ROOT} was the right and official way to locate the plugin’s installation directory. It turns out to be a workaround for this inconsistency :).

Steps to Reproduce

For Continual Learning plugin: watch its error msg inside execution log in Hooks after agent finished to output.
For Runlayer plugin: need to manually add version field in the local hooks.json first, and then restart Cursor and see the notice by Cursor IDE.

Expected Behavior

A solution to this inconsistency.

Screenshots / Screen Recordings

Operating System

Windows 10/11

Version Information

Version: 2.5.26 (user setup)
VSCode Version: 1.105.1
Commit: 7d96c2a03bb088ad367615e9da1a3fe20fbbc6a0
Date: 2026-02-26T04:57:56.825Z
Build Type: Stable
Release Track: Default
Electron: 39.4.0
Chromium: 142.0.7444.265
Node.js: 22.22.0
V8: 14.2.231.22-electron.0
OS: Windows_NT x64 10.0.22000

Does this stop you from using Cursor

No - Cursor works, but with this issue

Hey, thanks for the report.

You’re right, there is a contradiction here. According to the docs Hooks | Cursor Docs

  • User hooks ~/.cursor/hooks.json the cwd is ~/.cursor/
  • Project hooks <project>/.cursor/hooks.json the cwd is the project root

For plugin hooks (from the marketplace), the docs don’t say what the cwd is, and the error message “paths are relative to the hooks.json file” is misleading because in practice the cwd is the project root, as you found.

${CLAUDE_PLUGIN_ROOT} (and ${CURSOR_PLUGIN_ROOT}) is the right way to refer to the plugin directory from hooks.json. So your PR for Continual Learning is basically the correct approach.

I passed this to the team. We should fix the error message so it isn’t misleading. Let me know if you have any other questions.