--- title: "Writing Classifiers for permission-manager (agent-toolkit)" description: "Guide to adding new command classifiers to the St0nefish/agent-toolkit permission-manager plugin for Claude Code, covering project structure, conventions, testing, and PR workflow." type: guide domain: development tags: [claude-code, permissions, agent-toolkit, bash, classifier] --- # Writing Classifiers for permission-manager ## Overview The `permission-manager@agent-toolkit` plugin (St0nefish/agent-toolkit) provides a `cmd-gate` PreToolUse hook that classifies Bash commands before execution. Classifiers are bash scripts in `plugins-claude/permission-manager/scripts/classifiers/` that decide whether commands should be auto-allowed, require user approval, or be denied. ## Project Structure ``` plugins-claude/permission-manager/ scripts/ cmd-gate.sh # Hook entry point lib-classify.sh # Classification framework + dispatcher classifiers/ cargo.sh # cargo/rust docker.sh # docker/compose find.sh # find (deny -delete/-exec rm) git.sh # git (with protected branch logic) gh.sh # GitHub CLI gradle.sh # gradle/gradlew jvm-tools.sh # java/mvn npm.sh # npm/node/pnpm/yarn/npx pip.sh # pip/python/poetry/pyenv read-only-tools.sh # cat, ls, grep, jq, ps, etc. tea.sh # Gitea CLI uv.sh # uv/uvx (added 2026-03-18) tests/permission-manager/ test-classify.sh # Main test harness (618+ tests) ``` ## Classification Decisions Three possible outcomes: - **`allow`** — auto-approve, no prompt (read-only or safe local operations) - **`ask`** — prompt user for approval (destructive, remote, or elevated operations) - **`deny`** — block the command (e.g., `find -delete`) - **passthrough** — `return 0` without calling allow/ask/deny; defers to Claude Code's built-in permission system ## Classifier Conventions ### File Header ```bash # shellcheck shell=bash # shellcheck source=../lib-classify.sh ``` ### Function Naming - Entry point: `check_()` (e.g., `check_uv`, `check_docker`) - Subcommand extractor: `extract__subcommand()` (for tools with global flags) ### Entry Guard Pattern Simple tools (cargo, npm, pip) use `awk` + `case`: ```bash local first_token first_token=$(echo "$command" | awk '{print $1}') case "$first_token" in uv) ;; *) return 0 ;; esac ``` Complex tools (docker, git) use `perl` guard: ```bash echo "$command" | perl -ne '$f=1,last if /^\s*docker(\s|$)/; END{exit !$f}' || return 0 ``` ### Reason Message Style - Read-only: `" is read-only"` - Local ops: `" is a local build/dev operation"` - Ask: `" modifies "` - Deny: `" is not allowed"` ### Classification Philosophy (from existing classifiers) | Category | Decision | Examples | |----------|----------|---------| | Read-only inspection | allow | `git status`, `docker ps`, `pip list` | | Local build/install | allow | `npm install`, `pip install`, `cargo build` | | Global tool install | ask | `cargo install`, `uv tool install` | | Package uninstall | ask | `pip uninstall`, `uv pip uninstall` | | Publish/deploy | ask | `npm publish`, `cargo publish`, `uv publish` | | Execute arbitrary packages | passthrough | `npx`, `uvx`, `uv run --with` | | Destructive operations | deny | `find -delete` | ## Wiring a New Classifier In `lib-classify.sh`, add to `classify_single_command()`: ```bash check_ [[ "$CLASSIFY_MATCHED" -eq 1 ]] && return 0 ``` Place it logically near related classifiers (e.g., `check_uv` after `check_pip`). ## Testing ### Test Format ```bash run_test_both "" ["label"] ``` `run_test_both` runs the test in both Claude and Copilot modes. Expected values: `allow`, `ask`, `deny`, `none` (passthrough). ### Test Section Structure ```bash # ===== ALLOW: read-only ===== echo "── read-only ──" run_test_both allow "" # ===== ALLOW: local build/dev ===== echo "── local build/dev ──" ... ``` ### Running Tests ```bash bash tests/permission-manager/test-classify.sh # Full suite bash tests/permission-manager/test-classify.sh uv # Filter by keyword ``` ## PR Workflow 1. Fork `St0nefish/agent-toolkit` on GitHub 2. Branch from `master` (not `main`) 3. Add classifier, wire it, update tests 4. Run full test suite — zero regressions required 5. PR via `gh pr create --repo St0nefish/agent-toolkit --base master` ## Reference PR - [PR #38: feat: add uv/uvx classifier](https://github.com/St0nefish/agent-toolkit/pull/38) — full uv/uvx classifier with 102 tests, submitted 2026-03-18