Context: Working on dotfiles (~/.config), investigating Claude Code permission denials

Claude Code Bash(jq *) allowlist not matching

Bash(jq *) is in ~/.claude/settings.json under permissions.allow, yet running jq '.mcpServers.playwright' ~/.claude.json still triggers a PermissionRequest. The PermissionRequest hook (permission-suggestion) then auto-denies it.

Root cause (from source code analysis)

Examined the Claude Code source via chauncygu/collection-claude-code-source-code (source map leak from npm package, March 31 2026).

Key files:

  • src/tools/BashTool/bashPermissions.ts - filterRulesByContentsMatchingInput()
  • src/utils/permissions/shellRuleMatching.ts - matchWildcardPattern()
  • src/utils/bash/ast.ts - parseForSecurity()

The permission matching flow:

  1. parseForSecurity() parses the command via tree-sitter
  2. If feature flag TREE_SITTER_BASH_SHADOW is on, the AST result is thrown away and forced to parse-unavailable, falling back to legacy splitCommand_DEPRECATED
  3. The legacy parser splits subcommands, then filterRulesByContentsMatchingInput() checks each subcommand against rules
  4. Before wildcard matching, a compound command check runs: if (isCompoundCommand.get(cmdToMatch)) { return false } — this skips wildcard matching entirely for compound commands
  5. Hypothesis: splitCommand_DEPRECATED misparses the single-quoted jq filter argument (e.g., '.mcpServers.playwright'), causing the compound command check to return true, which skips the wildcard jq * rule

The matchWildcardPattern("jq *", ...) function itself works correctly — it builds regex ^jq( .*)?$ which would match. The issue is upstream: the command never reaches the wildcard matcher.

Next steps

  • Clone at /Users/achhina/projects/claude-code-source for further investigation
  • Could write a test to confirm the splitCommand_DEPRECATED misparsing hypothesis
  • Consider filing a bug on anthropics/claude-code