Skip to main content

Claude Code PreToolUse hook that auto-approves known-safe read-only shell commands.

Project description

saferead

A Claude Code PreToolUse hook that auto-approves known-safe, read-only shell commands — so routine reads (ls, cat, git status, grep, …) don't interrupt you with permission prompts.

PyPI Python License

What it does

saferead inspects each Bash command Claude Code is about to run and returns one of three verdicts:

  • allow — every part of the command is a recognized read-only operation.
  • ask — a gated git write (add / commit / stash) on a protected branch (master / main); you get a confirmation prompt.
  • abstain — anything it doesn't recognize: it stays silent and lets Claude Code's normal permission flow proceed.

It never denies a command. It only ever removes friction (allow) or adds a confirmation (ask), so it composes safely alongside any separate command-blocking layer you run.

Compound commands are decomposed at top-level &&, ||, ;, |, and newlines (quote / backtick / $(…)-aware). A command is allowed only when every segment is a recognized safe read.

Design guarantee

The cardinal rule is zero false-allows: a command that mutates state must never be silently approved. Coverage is secondary — when in doubt, it abstains. The hook is also built to never crash or block Claude Code: any internal error is caught and the hook abstains.

Examples

Command Verdict Why
ls -la, cat README.md, grep -rn foo src allow read-only
git status, git log --oneline, git diff allow read-only git
cat a.txt && wc -l a.txt allow every segment is a safe read
git commit -m "wip" on main ask gated write on a protected branch
git add . on a feature branch allow gated write off a protected branch
rm -rf build abstain not recognized → normal prompt
npm install abstain not a read
cat a.txt && rm b.txt abstain one segment mutates state

Only the Bash tool is inspected; every other tool is left untouched.

Install

One command — with uv installed — sets up saferead and registers it as a Claude Code hook:

uvx saferead install

This installs saferead permanently (via uv tool install) and writes a PreToolUse entry (matcher Bash) whose command is the full path to the installed saferead binary — a direct exec on the hot path, with no launcher overhead. The target settings.json is backed up first and any existing hook entries are left untouched; re-running is idempotent.

It prompts whether to install to the global ~/.claude/settings.json or the current project's .claude/settings.json. Pass a modifier to skip the prompt (for scripted / non-interactive installs):

uvx saferead install --project               # install into ./.claude/settings.json
uvx saferead install /path/to/settings.json  # install into an explicit path

The entry looks like:

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Bash", "hooks": [ { "type": "command", "command": "/home/you/.local/bin/saferead" } ] }
    ]
  }
}

Configuration

Configuration is optional — saferead ships with safe built-in defaults. Two TOML files are read, both optional:

File Trust Purpose
~/.config/saferead/config.toml trusted your global settings
$CLAUDE_PROJECT_DIR/.claude/saferead.toml untrusted per-project overrides

The project file can only narrow trust (add protected branches, disable recognizers) — it can never broaden the allow-set for git writes, by design.

Every key is optional; the values below are the built-in defaults:

[git]
protected_branches = ["master", "main"]           # ask on a gated git write on these branches
gated_subcommands  = ["add", "commit", "stash"]    # which git writes are gated

[recognizers]
disabled = []   # recognizer tags to turn off (those commands then abstain)

[python]
allowed_modules = ["math", "datetime", "json"]  # modules the `python -c` analyzer admits
allowed_methods = []                            # extra read-only methods to admit

[read]
local_allowed_roots = []   # empty = allow any path; non-empty restricts file reads to these roots
ssh_allowed_roots   = []

[logging]
enabled = true                       # audit log of every allow/ask decision (ON by default)
path = "/tmp/claude-hook-audit.log"  # where that audit log is written

Logs

saferead writes two files under /tmp:

  • Audit log (/tmp/claude-hook-audit.log) — one JSON line per allow/ask decision. On by default; configure or disable it via the [logging] table above.
  • Error log (/tmp/claude-hook.log) — internal diagnostics if the hook ever hits an error (it always abstains rather than crashing). Fixed path, not configurable.

Updating

Re-run the same command:

uvx saferead install

This upgrades saferead to the latest version and refreshes the hook entry.

Changelog

See CHANGELOG.md or the GitHub Releases page.

License

GPL-3.0-or-later

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

saferead-2026.6.tar.gz (164.1 kB view details)

Uploaded Source

Built Distribution

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

saferead-2026.6-py3-none-any.whl (111.6 kB view details)

Uploaded Python 3

File details

Details for the file saferead-2026.6.tar.gz.

File metadata

  • Download URL: saferead-2026.6.tar.gz
  • Upload date:
  • Size: 164.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for saferead-2026.6.tar.gz
Algorithm Hash digest
SHA256 0300350c4374dc7b1ba80651d3813f3259be03305def5beb04a1814db9ae08b0
MD5 17b3564436d950378f084423f1daff41
BLAKE2b-256 aef05e81943626905d996498fd0498d38458143ce641b1c4a05cc83db16d88cf

See more details on using hashes here.

Provenance

The following attestation bundles were made for saferead-2026.6.tar.gz:

Publisher: release.yml on tovmeod/saferead

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

File details

Details for the file saferead-2026.6-py3-none-any.whl.

File metadata

  • Download URL: saferead-2026.6-py3-none-any.whl
  • Upload date:
  • Size: 111.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for saferead-2026.6-py3-none-any.whl
Algorithm Hash digest
SHA256 a5afb157a1cec48b4c474bacb4898e0487076eda6a67150b500fd92e1505b9a2
MD5 557952fc37f2188a3998af01772397ac
BLAKE2b-256 85fd8d420042d91fab06e38f248ead6f0ac4e194e9731f4f3b49f3e053a11e7a

See more details on using hashes here.

Provenance

The following attestation bundles were made for saferead-2026.6-py3-none-any.whl:

Publisher: release.yml on tovmeod/saferead

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