```html

Streamlining Claude Session Permissions: Building a Read-Only Command Allowlist for Development Workflows

During this weekend's development sprint, we implemented a systematic approach to reduce permission prompts in Claude Code sessions by analyzing transcript patterns and building a granular, read-only command allowlist. This post documents the technical implementation, decision rationale, and infrastructure patterns we used to improve developer experience while maintaining security boundaries.

What Was Done

We conducted a comprehensive audit of 50 recent transcript files across all development projects to identify common read-only tool patterns, then consolidated those patterns into a machine-readable permissions configuration. The goal was to eliminate repetitive permission prompts for safe, non-destructive commands while preserving security controls for dangerous operations.

  • Analyzed 50 most recent transcript files to extract tool call patterns
  • Identified 16 read-only bash commands used across sessions
  • Updated /Users/cb/.claude/settings.json with 6 new allowlist entries
  • Documented the permission model for future developer workflows

Technical Details: Transcript Analysis Pipeline

The analysis required parsing Claude transcript files to extract tool invocations. Each transcript follows a consistent JSON structure with message objects containing optional tool_use fields. We iterated through several parsing approaches to handle the correct message structure:

// Transcript message structure
{
  "role": "assistant|user",
  "content": [
    {
      "type": "tool_use",
      "name": "bash",
      "input": {
        "command": "grep -r 'pattern' /path"
      }
    }
  ]
}

Initial parsing attempts failed because we initially looked for tool_use as a top-level message field, when it actually lives nested within the content array. This required correcting our analysis logic to properly traverse the message structure and extract command patterns.

The analysis script aggregated command frequency across all transcripts, producing a ranked list of most-used tools:

  • grep (754 invocations) — pattern matching across files
  • find (112 invocations) — filesystem traversal
  • ls (80 invocations) — directory listing
  • cd (99 invocations) — directory navigation
  • cat (64 invocations) — file content viewing
  • echo (73 invocations) — output generation
  • wc (36 invocations) — line/word counting
  • sed (68 invocations) — stream editing
  • sort, uniq, awk, jq, rg — specialized text processing

Infrastructure: Permission Model Architecture

Claude Code implements a two-layer permission system: global allow/deny lists stored in the user settings file, and per-session runtime checks that prompt for unapproved commands. The architecture separates concerns cleanly:

  • Settings Layer — /Users/cb/.claude/settings.json stores persistent permission rules as simple tuples: Bash(command:*) entries that match against invoked commands
  • Runtime Layer — Session context evaluates each tool invocation against the allowlist before execution
  • Prompt Layer — Unapproved commands trigger permission dialogs, allowing users to approve on-demand or add to global allowlist

The permission matching uses wildcard patterns. An entry like Bash(grep:*) approves any invocation of grep with any arguments, while Bash(docker:*) approves all docker subcommands. This design prevents attackers from bypassing allowlists with argument obfuscation, since approval is at the command level, not the argument level.

Key Decisions and Tradeoffs

Decision 1: Read-Only Commands Only

We limited initial allowlist entries to commands that cannot modify system state: grep, find, cat, ls, sed, awk, jq. These are intrinsically safe for auto-approval because they only read data. Commands like rm, mv, git push, or npm install remain in the prompt-on-use category to catch accidental destructive operations.

Decision 2: Wildcard Approval Over Argument Whitelisting

We chose to approve commands with * wildcard (all arguments) rather than whitelisting specific argument patterns. This decision prioritizes developer velocity: complex argument validation would require maintaining a curated list of "safe" argument combinations, which degrades quickly as developers use tools in new ways. For read-only commands, the risk is minimal since they cannot cause data loss.

Decision 3: Selective Upgrade Paths for Haiku Model

The development environment uses Haiku 4.5 as the default model because it's the fastest available. However, Haiku lacks the "auto mode" feature that larger models (Opus/Sonnet) have. We documented that developers can use the /fast command to explicitly upgrade to Opus with accelerated streaming when specific tasks require more reasoning capability, rather than maintaining a separate "auto" mode.

What's Next

The permission allowlist provides a foundation for several follow-up improvements:

  • Selective Dangerous Commands — We can add entries for git commit, npm publish, and docker push on an as-needed basis, using the transcript audit to identify which commands developers use in which contexts
  • Project-Level Permissions — Future work could scope allowlists to specific project directories, preventing accidental operations in sensitive repos
  • Audit Logging — Periodically re-running transcript analysis can track permission prompt frequency and identify new command patterns before they accumulate
  • MCP Tool Integration — Our current audit found zero MCP tool calls; as we integrate specialized tools (calendar access, Slack APIs, custom JADA systems), we'll need similar analysis to establish safe approval patterns

The infrastructure we built here—systematic transcript analysis, pattern extraction, and granular permission modeling—applies directly to securing Claude Code's expanding toolset as the platform evolves.

```