if field in hooks does not match file-extension patterns — Edit(*.ts) never fires even for TypeScript files
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
The docs state:
"Edit(*.ts)" runs only for TypeScript files
In practice, a hook with if: "Edit(*.ts)" never fires, including when Claude edits a .ts file. Removing the if field causes the hook to fire correctly. The if condition appears to always evaluate to false for file-path patterns.
Environment
- Claude Code version: 2.1.110
- OS: macOS (Darwin 25.3.0)
- Shell: zsh
Minimal Reproduction
1. Add to ~/.claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"if": "Edit(*.ts)|Write(*.ts)",
"command": "echo $(date) >> /tmp/if-hook-test.log"
}
]
}
]
}
}
2. In a Claude Code session, ask Claude to edit any .ts file.
3. Check the log:
cat /tmp/if-hook-test.log
Expected: A timestamp appears — the hook ran for a TypeScript file.
Actual: The file is never created. The hook never fires.
4. Confirm the hook system works without if:
Remove the if line and repeat step 2. The timestamp appears in the log. This rules out any issue with the hook command itself.
Root Cause Hypothesis
Claude Code's Edit and Write tools always pass file_path as a full absolute path, e.g.:
/Users/alice/project/src/app.ts
The permission rule Edit(*.ts) uses a glob where * does not match path separators (/), so it never matches an absolute path. The if condition always evaluates to false, and the hook is silently skipped.
This is consistent with the rest of the permission rule system, where absolute paths require a // prefix and recursive matching requires **:
"Read(//Users/alice/project/**)"
If Edit(*.ts) is intended as a suffix/basename check (matching any file ending in .ts regardless of path), the glob matching logic needs to be updated to handle it that way.
Expected Behaviour
Per the docs, "Edit(*.ts)" should match any Edit tool call where the target file ends in .ts, at any path depth.
Either:
- Fix glob matching so
*.tsis a suffix check against the filename or full path, or - Clarify in the docs that
Edit(**/*.ts)is required for matching files in subdirectories
Workaround
Filter by file extension inside the hook script instead:
file_path=$(cat | jq -r '.tool_input.file_path // empty')
[[ "$file_path" == *.ts ]] || exit 0
# ... rest of hook
Note: bash's [[ == *.ts ]] correctly matches an absolute path suffix, unlike the if field's glob.
Additional Context
- The
iffield was added in v2.1.85. This may be a regression, or the file-path case may never have been implemented correctly. - The one confirmed fix (v2.1.88) was specific to compound Bash commands (
ls && git push) and env-var prefixes — not file-path patterns. - #36332: A feature request to add argument-pattern filtering to
matcher— the community workaround suggested was script-level filtering, notif. This suggestsifwith file patterns is not well-known as a working solution.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗