runok is a command permission tool for LLM coding agents.
📖 Documentation · 🚀 Quick Start · 🔧 Configuration · 📋 Recipes
Even with allow rules configured, Claude Code asks for confirmation in cases like these:
# Claude adds a comment before the command -- no longer matches your allow rule
⏺ Bash(# check logs
git log --oneline -5)
⎿ Running…
Command contains newlines that could separate multiple commands
Do you want to proceed?
❯ 1. Yes
2. No# Text-based parsing flags safe commands as suspicious
⏺ Bash(git log --oneline -5 && echo "---" && git status)
⎿ Running…
Command contains quoted characters in flag names
Do you want to proceed?
❯ 1. Yes
2. Norunok parses commands with tree-sitter-bash, so comments, compound commands (&&, |, ;), and wrapper commands (sudo, bash -c, xargs) are all handled correctly. Each sub-command is evaluated independently against your rules.
And that's just the start. Claude Code's built-in permissions have other limitations too:
Denied commands give no explanation. The agent has no idea why a command was blocked. With runok, deny rules include a message and a suggested fix -- the agent reads it and self-corrects:
# runok.yml
- deny: 'git push -f|--force *'
message: 'Force push is not allowed.'
fix_suggestion: 'git push --force-with-lease'Global flags break matching. Claude sometimes adds flags like -C before the subcommand. git -C /path commit does not match Bash(git commit *). runok handles this with optional groups and order-independent matching:
# runok.yml
- allow: 'git [-C *] commit *'
# matches: git commit -m "fix"
# matches: git -C /path/to/repo commit -m "fix"No recursive parsing of wrappers. Claude Code does not inspect sudo, bash -c, or $(). runok recursively unwraps them to evaluate the inner command:
# runok.yml
definitions:
wrappers:
- 'sudo <cmd>'
- 'bash -c <cmd>'
rules:
- deny: 'rm -rf /'
# "sudo bash -c 'rm -rf /'" -> unwrap sudo -> unwrap bash -c -> denyJSON only, no comments. settings.json cannot be annotated. runok uses YAML:
# runok.yml
rules:
# read-only git commands are always safe
- allow: 'git status'
- allow: 'git diff *'
# allow push, but not force push -- rewrites shared history
- deny: 'git push -f|--force *'
message: 'Use --force-with-lease instead.'
- ask: 'git push *'See Why runok? for a full comparison table.
Flexible command parsing
tree-sitter-bashAST parsing -- comments, pipes,&&,;are understood, not treated as opaque stringssudo,bash -c,xargsare recursively unwrapped so rules apply to the inner command
Flexible rule configuration
- Wildcards, flag alternation (
-f|--force), optional groups, argument-order-independent matching - Conditional
whenclauses with CEL expressions for environment-aware decisions - OS-level sandboxing (macOS Seatbelt / Linux Landlock) for file and network restrictions
And more -- rule testing, audit logging, preset sharing, denial feedback, extension protocol, Claude Code plugin for natural-language configuration assistance
Homebrew
brew install fohte/tap/runokFrom source with Cargo
cargo install --git https://github.com/fohte/runok.gitPre-built binaries are also available on GitHub Releases. See Installation for all options.
The fastest way to get started is with the interactive setup wizard:
runok initThis creates a runok.yml, and if you have Claude Code configured, migrates your Bash permissions to runok rules and registers the PreToolUse hook automatically.
You can also configure manually. Create ~/.config/runok/runok.yml:
rules:
- allow: 'git status'
- allow: 'git diff *'
- allow: 'git log *'
- ask: 'git push *'
- deny: 'git push -f|--force *'
message: 'Force push is not allowed'
defaults:
action: askAnd add runok as a PreToolUse hook in .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "runok check --input-format claude-code-hook"
}
]
}
]
}
}See Claude Code Integration for sandbox setup and advanced configuration.
runok check -- git status # => allow
runok check -- git push -f main # => deny
runok check -- git push main # => askSee runok.fohte.net
Feature requests and bug reports are welcome on GitHub Issues.