Skip to main content

Permission evaluation agent for Claude Code

Project description

Tower CLI

Claude Code's permission system is there for a good reason. It makes sure AI can't run tools on your machine without your say-so. But in practice, you end up clicking "allow" dozens of times per session for things you'd always approve (file reads, grep, git status). The alternative, --dangerously-skip-permissions, removes the safety net entirely.

Tower gives you a middle ground. It sits between Claude Code and tool execution as a PreToolUse hook and lets you define rules for what gets auto-allowed, what gets blocked, and what still asks. You keep the protection where it matters and skip the prompts where it doesn't.

Install

pip install cc-tower
tower init

That's it. This creates ~/.claude/tower-rules.yml and wires up the hook in ~/.claude/settings.json. Works globally across all your projects.

If you want rules scoped to a single project:

cd your-project
tower init --local

If you skip tower init entirely and just start using Claude Code, Tower auto-creates a default config at ~/.claude/tower-rules.yml the first time it evaluates a tool call.

What the config looks like

version: 1
default: ask  # what happens when no rule matches: allow | deny | ask

rules:
  # Reads and searches are safe, let them through
  - tool: Read
    action: allow
  - tool: Glob
    action: allow
  - tool: Grep
    action: allow

  # Allow harmless bash commands
  - tool: Bash
    command_pattern: "^(ls|cat|git status|git diff|npm test|pytest).*"
    action: allow

  # Block destructive bash commands outright
  - tool: Bash
    command_pattern: "rm -rf|git push --force|DROP TABLE"
    action: deny
    reason: "Destructive command blocked by Tower"

  # Allow writes only to code files
  - tool: Write
    path_pattern: "**/*.{py,js,ts,json,yml,yaml,md}"
    action: allow

Rules are evaluated top-to-bottom, first match wins. If nothing matches, the default action kicks in.

Tower looks for tower-rules.yml in this order:

  1. ./tower-rules.yml (project root)
  2. ./.claude/tower-rules.yml (project-local)
  3. ~/.claude/tower-rules.yml (global)

Rule fields

Field Required Description
tool Yes Tool name (Bash, Read, Write, Edit, Glob, etc)
action Yes allow, deny, or ask
command_pattern No Regex pattern for Bash commands
path_pattern No Glob pattern for file paths
reason No Message shown when a rule triggers

Commands

Command What it does
tower init Set up global config + hook in ~/.claude/
tower init --local Set up project-local config + hook in ./.claude/
tower status Show loaded rules and whether the hook is wired up
tower config Interactive TUI for editing rules
tower evaluate Evaluate a tool call from stdin (called by hook)

Interactive config

Don't want to edit YAML by hand? tower config gives you a TUI.

You get a main menu, your current config info, and arrow-key navigation:

Tower Config main menu

All your rules in a color-coded table. Green for allow, red for deny, yellow for ask:

Rules table view

Adding a rule. Pick a tool from the fuzzy-searchable list, choose an action, done:

Add rule flow

Deleting a rule asks for confirmation so you don't accidentally remove something:

Delete confirmation

How it works under the hood

  1. Claude Code is about to run a tool (say, Bash with rm -rf /tmp/stuff)
  2. The PreToolUse hook pipes the tool call to tower evaluate via stdin
  3. Tower loads your rules, checks them top-to-bottom
  4. First matching rule decides: allow, deny, or ask
  5. If no rule matches, the default action is used
  6. Tower sends the decision back to Claude Code

Development

pip install -e ".[dev]"
pytest

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cc_tower-0.1.0.tar.gz (11.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cc_tower-0.1.0-py3-none-any.whl (12.6 kB view details)

Uploaded Python 3

File details

Details for the file cc_tower-0.1.0.tar.gz.

File metadata

  • Download URL: cc_tower-0.1.0.tar.gz
  • Upload date:
  • Size: 11.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for cc_tower-0.1.0.tar.gz
Algorithm Hash digest
SHA256 70fcf35b5e8e42e5087ebbc3f8a3daa363d4d30324ac021f7e3da1e63de429d5
MD5 206cd637b8140857fbf0a9804bc292b7
BLAKE2b-256 ff594489b8ae4e6362a99b123dd191ab8dc0ecb0c320de4d065c0d417876b37a

See more details on using hashes here.

Provenance

The following attestation bundles were made for cc_tower-0.1.0.tar.gz:

Publisher: publish.yml on thedhanawada/cc-tower

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cc_tower-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: cc_tower-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 12.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for cc_tower-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0f5cb80f089cb2a66589be776f3a93f05ccc41be938fcbcf50a69f7876a5abd8
MD5 0e0cffe40a32d9fb69b02efcc64b087a
BLAKE2b-256 2fde97c57e17311088c68766ccaaf03112cb0319fd5935cd0481d292f766fe15

See more details on using hashes here.

Provenance

The following attestation bundles were made for cc_tower-0.1.0-py3-none-any.whl:

Publisher: publish.yml on thedhanawada/cc-tower

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page