GitHub - agentbellnorm/kjell: Pure Go CLI tool to let agents rip safely

4 min read Original article ↗

AI coding agents execute shell commands but can't tell which ones are safe. kjell solves this — it parses shell commands and classifies them as safe, write, or unknown so agents can auto-approve safe commands and only prompt for writes.

# Simple commands
$ kjell check "grep -r TODO src/"                              # SAFE
$ kjell check "rm -rf /tmp/junk"                               # WRITE

# Pipes — worst-case semantics
$ kjell check "cat logs/*.txt | grep error | sort"             # SAFE
$ kjell check "cat /etc/hosts | sed 's/old/new/' | tee hosts"  # WRITE (tee writes)

# Chained commands
$ kjell check "cd src && grep -r TODO ."                       # SAFE
$ kjell check "git add -A && git commit -m 'update'"           # WRITE

# Redirects
$ kjell check "ls -la 2>/dev/null"                             # SAFE (/dev/null is harmless)
$ kjell check "echo 'data' > output.txt"                       # WRITE (redirect to file)

# Flag-dependent classification
$ kjell check "curl -s https://api.example.com/status"         # SAFE (GET)
$ kjell check "curl -X POST https://api.example.com/deploy"    # WRITE (POST)

# Recursive wrappers — evaluates the inner command
$ kjell check "sudo cat /etc/hosts"                            # SAFE
$ kjell check "sudo rm -rf /var/cache"                         # WRITE

# kubectl exec — evaluates the command inside the container
$ kjell check "kubectl exec my-pod -- cat /etc/hosts"          # SAFE
$ kjell check "kubectl exec my-pod -- rm -rf /tmp/cache"       # WRITE

# Shell loops
$ kjell check 'for f in *.log; do grep error "$f"; done'       # SAFE
$ kjell check 'for f in *.log; do rm "$f"; done'               # WRITE

# Deep nesting — sudo → env → bash -c → actual command
$ kjell check "sudo env CI=1 bash -c 'grep -r TODO src/'"     # SAFE
$ kjell check "sudo env CI=1 bash -c 'find /tmp -name *.cache -delete'" # WRITE

# Python -c — analyzes the Python code
$ kjell check "python3 -c 'import json; json.loads(\"{}\")'"  # SAFE (pure computation)
$ kjell check "python3 -c 'import os; os.remove(\"foo\")'"    # WRITE (file deletion)
$ kjell check "python3 -c 'import os; os.system(\"ls -la\")'" # SAFE (shell recursion)
$ kjell check "python3 -c 'open(\"f.txt\", \"w\").write(\"x\")'" # WRITE (file write)
$ kjell check "python3 script.py"                              # UNKNOWN (can't analyze)

It handles pipes, redirects, &&/||, command substitution, loops, conditionals, recursive wrappers (sudo, env, xargs, sh -c, find -exec, docker exec --), and python -c code analysis. 100+ commands with flag-level granularity. Proper bash parser, not regex.

Claude Code

Add to ~/.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "kjell check --format claude-code"
          }
        ]
      }
    ]
  }
}

Safe commands auto-approve. Writes and unknowns pass through to Claude Code's normal permission system — your "always allow" rules still work.

Install

brew install agentbellnorm/tap/kjell

Or with Go:

go install github.com/agentbellnorm/kjell/cmd/kjell@latest

Or grab a binary from GitHub Releases.

CLI

kjell check "grep -r TODO src/"         # SAFE
kjell check --json "rm -rf /tmp/junk"   # JSON output
kjell check --log "kubectl get pods"    # classify and log to ~/.kjell/log
kjell db stats                           # show DB size
kjell db lookup git                      # show a command's entry

Use --log to write a classification trace to ~/.kjell/log — useful for reviewing what kjell approved during an agent session:

time=2026-04-05T14:22:01Z level=INFO msg=classified command="cat /etc/hosts" classification=safe reason="cat: default safe"
time=2026-04-05T14:22:01Z level=INFO msg=classified command=sudo classification=safe reason="sudo wraps: cat /etc/hosts"

Adding commands

Create a TOML file in db/:

command = "mycommand"
default = "read"

[[flags]]
flag = ["-w", "--write"]
effect = "write"
reason = "Modifies files"

Tests go in tests/commands/ in the same format. Run with go test ./... (319 tests).

Dependencies

Both dependencies are pure Go — no CGo, no C toolchain required:

  • mvdan.cc/sh — bash parser for shell command analysis
  • gotreesitter — tree-sitter runtime for Python code analysis

Local overrides

Drop TOML files in ~/.kjell/db/ to add or override command definitions without modifying the built-in database. Files use the same format as above and are merged by command name — your local definitions take precedence.