Skip to main content

agentperm

One permission policy for Claude Code, Codex CLI, OpenCode, Gemini CLI, and Kiro, with a shell parser that reads compound commands the way bash does.

The problem

Coding agents ask before running shell commands. Each has its own permission config, and none of them parse what you typed — cat foo | head -60 is one opaque string, so every agent asks about it even though both sides are read-only. You end up maintaining the same rules in three different places, and still getting prompted for things you've already allowed.

What agentperm does

It replaces those separate configs with a single .agent-permissions.jsonc that every agent consults. It parses pipes, &&/|| chains, loops, subshells, redirects, and bash -c wrappers, then decides each segment against your rules:

{
  "version": 1,
  "permissions": {
    "allow": [
      "Shell(git {status,log,diff,show,branch} !-*)",
      "Shell(sed !{-i,--in-place})",
      "Shell({cat,head,grep,rg,ls,wc})",
      "Python(readonly)",
      "Read", "Grep"
    ],
    "ask": [
      { "tool": "Bash", "command": ["sed"], "when": { "hasOption": ["-i"] },
        "reason": "sed -i edits files in place" }
    ],
    "deny": ["Shell(sudo)", "Shell(rm -r -f /*)"]
  },
  "shell": {
    "redirection": {
      "stdoutToFile": "ask",
      "allowPaths": ["/tmp"]
    }
  }
}
Command Verdict Why
cat foo 2>&1 | head -60 allow both segments allowed; 2>&1 is a safe fd dup
git status && cat README.md allow && — both sides allowed
sed -i s/old/new/ x.txt ask the ask rule on -i wins over the allow
echo hi > /tmp/out.txt allow redirect target matches allowPaths
echo hi > ~/notes.txt ask redirect to file outside allowPaths
cat foo | ./deploy.sh ask one unrecognized segment in a compound
rm -rf / deny deny always wins

No prompt on the first two. Native matchers ask about both because a pipe or && defeats string matching.

Install

uv tool install agentperm   # or: pipx install agentperm

Setup

agentperm install   # wire hooks into Claude Code / Codex / OpenCode / Gemini / Kiro
agentperm import    # pull existing native rules into ~/.agent-permissions.jsonc
agentperm edit      # open the policy in $VISUAL/$EDITOR

install writes to each agent's hook config, or merges into ~/.rulesync/hooks.json if you use Rulesync. Preview with --dry-run. Your native settings keep working underneath — nothing is taken away.

Writing rules

Rules go in allow, ask, or deny. First match wins within a list; deny beats ask beats allow across lists.

Shell patterns — the recommended form. Flags float; trailing args open by default:

"Shell(git {status,log,diff} !-*)"          // match subcommands, reject all flags
"Shell(git push !--force)"                   // allow push, forbid --force
"Shell(aws values(--region) ec2 describe-*)" // declare that --region eats the next token
"Shell(git stash {list,show} !... !-*)"      // exact subcommand, no extra args or flags

Per-rule redirect paths — allow redirects to specific directories for matching commands:

{"Shell(mise exec just synth-env)": {"allowPaths": ["/tmp"]}}

Named tools — match non-shell tools, optionally scoped by input:

"Read"                          // exact
"mcp__memory__*"                // prefix glob
"WebFetch(domain:github.com)"   // URL host scoping
"Edit(src/**)"                  // path glob scoping

Legacy positional — "Bash(git status:*)" remains supported for existing policies and imports.

Flag matching — { "tool": "Bash", "command": ["sed"], "when": { "hasOption": ["-i"] }, "reason": "..." }.

Python analysis — "Python(readonly)" shallowly inspects python -c and heredoc source with the stdlib AST. Imports, printing, and inspection run without a prompt; mutation asks.

In compounds the strictest segment decides. One unrecognized command = ask. Deny always wins. Full spec: Shell pattern DSL · Policy reference.

Global + per-project

~/.agent-permissions.jsonc sets global defaults. <repo>/.agent-permissions.jsonc adds project-specific rules. Both apply at the same time — deny wins across both.

// global — broad defaults
{ "version": 1, "permissions": {
    "allow": ["Shell({cat,ls,grep,rg})", "Shell(git {status,diff,log})", "Read"],
    "deny":  ["Shell(sudo)", "Shell(rm -r -f /*)"]
}}
// ~/work/payments/.agent-permissions.jsonc — project overrides
{ "version": 1, "permissions": {
    "allow": ["Shell(pytest)", "Shell(pnpm run {build,test,lint})"],
    "deny":  [{ "tool": "Bash", "command": ["git"],
                "when": { "hasOption": ["--force"] },
                "reason": "no force-push in this repo" }]
}}

A repo adds its own tools and clamps down on dangerous variants without touching the global file. agentperm edit --local creates or opens the project file.

Redirect allowlisting

File redirects (>, >>) default to ask. You can allowlist directories globally or per-rule:

"shell": {
  "redirection": {
    "stdoutToFile": "ask",
    "allowPaths": ["/tmp", "/private/tmp/claude-*"]
  }
}

Paths resolve through symlinks (/tmp covers /private/tmp on macOS). Globs match per component. Relative targets resolve against the working directory.

Zellij pane bypass

The bundled WASM plugin adds a per-pane toggle: ask and unmatched commands become allow in that pane only. deny rules still bite. Under Claude Code's own --dangerously-skip-permissions, agentperm steps aside completely.

What it doesn't do

  • No sandbox. It decides allow / ask / deny; it doesn't contain what runs.
  • No replacing native settings. Those keep working as a fast path.
  • No MCP server management. Use Rulesync or native config for that.

More

Policy reference · Shell pattern DSL · Architecture · Adapter notes · Changelog

License

MIT

Metadata

Release files for agentperm 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agentperm 0.3.0
File Size Uploaded Provenance
agentperm-0.3.0.tar.gz 88.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for agentperm 0.3.0
File Interpreter ABI Platform
agentperm-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 149.5 kB

Release files / agentperm-0.3.0.tar.gz

Download URL agentperm-0.3.0.tar.gz
Size 88.9 kB
Tags Source
SHA-256 checksum
How to use checksums
ba7a732de2c45c6100e7d4b65fad3babb885aff1c34d0e7a79ce4313e2beabad
BLAKE2b-256 checksum
How to use checksums
8818cad2cea66f575e1fa04e6fd39c2cd67e8d50d6a9ee305b94ca17bcdbb571
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / agentperm-0.3.0-py3-none-any.whl

Download URL agentperm-0.3.0-py3-none-any.whl
Size 60.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d3f45b3f08adf08d0d0746ca1c94cc4afdf78f7398ef594c2baf80095841cd26
BLAKE2b-256 checksum
How to use checksums
eab9930e5e688a4a9ee4fe5e18ffdbc75946aa423b15f1e22d35b8232d079653
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for agentperm 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page