Classify bash commands as READONLY, WRITE, DANGEROUS, or UNKNOWN
Project description
bash-classify
Classify bash commands by their side-effect risk level.
What it does
bash-classify parses bash expressions using tree-sitter, classifies each command against a database of 150+ known commands, and outputs a structured JSON verdict. Commands are classified along two axes: classification (READONLY, LOCAL_EFFECTS, EXTERNAL_EFFECTS, DANGEROUS, UNKNOWN) describing what kind of effects a command has, and risk (LOW, MEDIUM, HIGH) describing how worried you should be.
Designed primarily as a Claude Code hook to automatically allow low-risk commands while flagging risky ones for human review.
Installation
uv tool install bash-classify
# or
pip install bash-classify
Quick start
$ echo 'kubectl get pods -n production' | bash-classify | jq '.classification'
"READONLY"
$ echo 'git push --force origin main' | bash-classify | jq '.classification'
"DANGEROUS"
$ echo 'cp file.txt /etc/config' | bash-classify | jq '.classification'
"DANGEROUS"
$ echo 'find . -name "*.pyc" -delete' | bash-classify | jq '.classification'
"DANGEROUS"
Claude Code plugin
The repo includes a Claude Code plugin that auto-allows low-risk bash commands via a PreToolUse hook.
# Install the bash-classify CLI
uv tool install bash-classify
# Add the marketplace and install the plugin
claude plugin marketplace add fprochazka/bash-classify
claude plugin install bash-classify-hook@fprochazka-bash-classify
To upgrade after a new release:
uv tool install --force bash-classify
claude plugin marketplace update fprochazka-bash-classify
claude plugin update bash-classify-hook@fprochazka-bash-classify
Once installed, any Bash tool call with risk: LOW is auto-approved — no permission prompt. This includes all READONLY commands plus safe routine operations like git add, git commit, mkdir, package installs, code formatters, and more. Commands with MEDIUM or HIGH risk still require confirmation.
Command database
bash-classify loads command definitions from two locations:
- Built-in database — 150+ command definitions bundled with the package, covering common Unix utilities, package managers, container tools, cloud CLIs, and more. Lives in
src/bash_classify/commands/*.yaml. - User database — your own command definitions at
~/.config/bash-classify/commands/*.yaml(override the location with theBASH_CLASSIFY_CONFIG_DIRenv var, which resolves to$BASH_CLASSIFY_CONFIG_DIR/commands/). User files with the same name as a built-in override it completely, so you can customize classifications for internal tools, company-specific wrappers, or personal CLIs without forking the repo.
Both directories use the same YAML format. See docs/classification-guidance.md for how to add new commands. YAML definitions are validated against a JSON Schema for IDE autocomplete and CI checks.
Classification levels
| Level | Description | Examples |
|---|---|---|
READONLY |
No side effects | ls, cat, grep, kubectl get |
LOCAL_EFFECTS |
Modifies local files or state only | git add, git commit, cp, mkdir, pytest |
EXTERNAL_EFFECTS |
Interacts with external systems | git push, kubectl apply, curl -d |
DANGEROUS |
Destructive, system-wide, or irreversible | rm -rf, git push --force, chmod |
UNKNOWN |
Command not in database | Any unrecognized command |
Risk levels
Each command also gets a risk rating, orthogonal to classification:
| Risk | Description | Examples |
|---|---|---|
LOW |
Safe, routine operation — auto-approved | ls, git add, git commit, mkdir, ruff format |
MEDIUM |
Normal caution warranted | git push, cp, npm run, git rebase |
HIGH |
Dangerous or unknown — always requires confirmation | rm -rf, git push --force, unknown commands |
Risk defaults are derived from classification (READONLY→LOW, LOCAL_EFFECTS→MEDIUM, EXTERNAL_EFFECTS→MEDIUM, DANGEROUS/UNKNOWN→HIGH) but can be overridden per command, subcommand, or option in the YAML database.
How it works
- Tree-sitter parsing -- bash expressions are parsed into an AST for accurate command extraction, handling pipes, subshells, and command substitution
- YAML command database -- each command has classification rules with subcommand and option matching
- Subcommand matching --
kubectl getandkubectl deletecan have different classifications - Multi-goal build tools --
subcommand_mode: match_allhandles commands likemvn clean installandgradle clean build testwhere multiple goals can be combined in any order - Delegation for wrappers -- commands like
xargs,sudo, andenvdelegate classification to the inner command - File path detection -- redirect operators (
>,>>,<) are parsed intowrite_paths/read_pathsin the output; writes to/tmpand/var/tmpstay at LOW risk
Python API
from bash_classify import classify_expression
result = classify_expression("kubectl get pods")
print(result.classification) # Classification.READONLY
print(result.risk) # Risk.LOW
See SPEC.md for the full specification.
Development
git clone https://github.com/fprochazka/bash-classify.git
cd bash-classify
uv sync --dev
Run tests and linting before committing:
uv run ruff format .
uv run ruff check .
uv run pytest
To add or modify command definitions, see docs/classification-guidance.md. All YAML files in src/bash_classify/commands/ are validated against a JSON Schema — your IDE will provide autocomplete if it supports the # $schema: comment.
Releasing
Version is derived automatically from git tags via hatch-vcs — no manual version bumping needed.
Before tagging, bump the version in both plugin manifest files:
coding-agent-plugins/claude-code/.claude-plugin/plugin.json.claude-plugin/marketplace.json
Wait for CI to pass on master, then tag, push, and create a GitHub release:
# Review changes since last release
git log $(git describe --tags --abbrev=0)..HEAD --oneline
git tag v<version>
git push origin v<version>
gh release create v<version> --title "v<version>" --notes "..."
The publish.yml GitHub Action builds and publishes to PyPI automatically via trusted publishing.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bash_classify-0.8.0.tar.gz.
File metadata
- Download URL: bash_classify-0.8.0.tar.gz
- Upload date:
- Size: 118.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
87ba0b67cc4fca77c61c272e6775a9a35c7d930b1c1442494e49fd84c638f91b
|
|
| MD5 |
8293c3e7996392e7cd9db5464701d06b
|
|
| BLAKE2b-256 |
4d3a92dea39bc1645e974e97cb6aaad3c2cfb3ebd19d60c9ab9898593deddef0
|
Provenance
The following attestation bundles were made for bash_classify-0.8.0.tar.gz:
Publisher:
publish.yml on fprochazka/bash-classify
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bash_classify-0.8.0.tar.gz -
Subject digest:
87ba0b67cc4fca77c61c272e6775a9a35c7d930b1c1442494e49fd84c638f91b - Sigstore transparency entry: 1361528957
- Sigstore integration time:
-
Permalink:
fprochazka/bash-classify@5b3329eb4693c567ae43fc62861970fb57604cd6 -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/fprochazka
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5b3329eb4693c567ae43fc62861970fb57604cd6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file bash_classify-0.8.0-py3-none-any.whl.
File metadata
- Download URL: bash_classify-0.8.0-py3-none-any.whl
- Upload date:
- Size: 102.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0a7af5c5f4adeb1e808846397275128a6d172b25630689169d69f4ff34a1ef12
|
|
| MD5 |
fdafac5db869d775592a10781465f6c5
|
|
| BLAKE2b-256 |
c1a6b573a527f43f6e933507913f930d5d6cd226c1b0d96510b04c46ff1d95df
|
Provenance
The following attestation bundles were made for bash_classify-0.8.0-py3-none-any.whl:
Publisher:
publish.yml on fprochazka/bash-classify
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bash_classify-0.8.0-py3-none-any.whl -
Subject digest:
0a7af5c5f4adeb1e808846397275128a6d172b25630689169d69f4ff34a1ef12 - Sigstore transparency entry: 1361528962
- Sigstore integration time:
-
Permalink:
fprochazka/bash-classify@5b3329eb4693c567ae43fc62861970fb57604cd6 -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/fprochazka
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5b3329eb4693c567ae43fc62861970fb57604cd6 -
Trigger Event:
push
-
Statement type: