Hooks that rewrite bash pretool ask every time and allowing it auto-allows everything

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

Allow/Deny now operates on a hooks rewritten command. Adding the base rewritten command to the allowlist allows everything. Not adding it “asks” everything.

Steps to Reproduce

Create a simple hook that rewrites the Bash() call like sh -c ‘inner cmd’

Expected Behavior

Allowing the outer rewritten command should not auto-allow every command it wraps.

Operating System

Linux

Version Information

Version: 3.15.6
VS Code Extension API: 1.128.0
Commit: a1f686545fd0ce8917bbd2449f733551a9bce420
Date: 2026-08-06T01:41:03.876Z
Layout: IDE
Build Type: Stable
Release Track: Default
Electron: 40.10.3
Chromium: 144.0.7559.236
Node.js: 24.15.0
V8: 14.4.258.32-electron.0
xterm.js: 6.1.0-beta.291
OS: Linux x64 6.17.0-124040-tuxedo

Does this stop you from using Cursor

Yes - Cursor is unusable

I cannot stress how broken this is for me: I auto-allow many read-only and safe commands, but I do not run in YOLO mode. This leaves me choosing between allowing everything, which is dangerous, or manually approving nearly every command, which completely breaks my workflow.

Hey! Thanks for the detailed write-up — you’re right on both counts, and neither is something you misconfigured. Definitely don’t add sh to the allowlist.

What should unblock you today: emit the rewritten command directly instead of wrapping it, so the first token stays the real executable. pytest tests/ --color=no keeps matching your existing pytest entry; sh -c 'pytest tests/ --color=no' doesn’t. Compound commands are fine unwrapped too - each part is matched separately, so a && b still hits your entries for a and b. And if the wrapper is only there to change directory or extend the timeout, updated_input takes cwd and timeout alongside command, so you can drop sh -c entirely.

If your rewrite needs something you genuinely can’t express without a wrapper, what’s it doing? That’d help me pass along the right shape.

We’ve let the team know and this is an issue we’re tracking - same underlying gap as your earlier request for authoritative hook verdicts. I’ll post here when there’s an update.

Most of these hooks work around Cursor bugs or missing capabilities. Cursor gives every preToolUse hook the original input, so rewrites do not compose and the last rewrite wins. h.pt-chain turns them into a real pipeline.

Tools

  • h.pt-chain

    • Runs hooks sequentially.
    • Passes each rewrite into the next hook plus original hook input.
    • Merges their responses.
    • Applies my actual hooks h.pt-rc and h.pt-outlim.
  • cursor-pretool-wrap

    • Thin wrapper around h.pt-chain.
    • Waits 50 ms after writing the response because Cursor can lose output from fast-exiting hooks.
  • h.pt-rc

    • Restores direnv and the required PATH.
    • Works around Cursor’s incomplete shell initialization.
  • h.pt-outlim

    • Limits output sent into the agent context.
    • Saves the complete output separately.
  • agent-tool-call-audit-hook

    • Records tool timing, results, failures, and permission activity.

I do not think it makes sense to require separate approval for rewritten commands. Cursor already executes the hook itself without approval, and that hook can execute arbitrary code. Its rewrites should be treated as trusted hook output.

I did a quick investigation into refactoring my hooks to emit plain Bash joined with &&, ;, and ||. That would not work well for the output limiter, and it would make the rest of the implementation awkward. My hook wrappers are essentially a sh -c but they are my own golang binary so h.pt-chain -c ''

It would also be helpful to get advance notice of breaking changes like this.