Support argument patterns in hook matchers (consistent with permissions syntax)

Resolved 💬 3 comments Opened Mar 19, 2026 by TyceHerrman Closed Apr 25, 2026

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

Hook matchers only support tool names (e.g. "Bash", "Write|Edit"), meaning a hook registered on Bash fires on every Bash tool call. There's no way to narrow a hook to specific commands.

The permissions system already supports argument patterns — Bash(git:*), Bash(npm run test), Edit(.claude). Hook matchers don't, despite sharing the same conceptual role of "match a tool call."

This forces hook authors to handle filtering inside the hook itself. For example, an agent hook that should only run before git commit still spins up on every Bash call, wastes tokens determining the command isn't a commit, and bails out.

Proposed Solution

Extend hook matchers to support the same argument pattern syntax as permissions:

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash(git commit:*)",
      "hooks": [{
        "type": "agent",
        "prompt": "Run /simplify on all changed files before this commit proceeds.",
        "timeout": 120
      }]
    }]
  }
}

Pattern syntax (same as permissions):

  • Bash(git commit:*) — matches any Bash command starting with git commit
  • Bash(npm run test) — exact match
  • Write(src/*.ts) — matches writes to TypeScript files in src/
  • Edit(.claude) — matches edits to .claude files

This is already implemented for permissions. Reusing the same matching logic for hook matchers would be a small, consistent change.

Alternative Solutions

  • Filter inside the hook — current workaround. Works, but agent/prompt hooks still spin up on every non-matching call, wasting tokens and adding latency.
  • condition field on matchers — a shell command evaluated at session start to gate registration. More complex, and doesn't handle cases where the working directory changes mid-session.
  • Separate argumentMatcher field — more explicit but introduces a new concept when the permissions syntax already exists.

Priority

Medium - Nice to have improvement

Feature Category

Other

Use Case Example

  1. I want an agent hook that runs /simplify before git commits
  2. Currently: "matcher": "Bash" — hook fires on every Bash call, agent spins up, checks if it's a commit, bails 99% of the time
  3. With this change: "matcher": "Bash(git commit:*)" — hook only fires on git commit commands, zero overhead on all other Bash calls

Other useful patterns:

  • Bash(docker build:*) — lint Dockerfiles before builds
  • Bash(terraform apply:*) — require plan review before apply
  • Write(*.sql) — validate SQL files after writing
  • Edit(src/api/*:*) — run API tests after editing API files

Additional Context

Related: #35564 proposes matching skill names in hook matchers — a different matcher property but the same underlying idea of making hook matchers more expressive beyond just tool names.

The permissions system's argument matching is already well-understood by users and implemented in the codebase. Extending it to hook matchers creates a consistent experience and avoids the need for more complex gating mechanisms.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗