Hard rules for AI coding agents - one rulefile, compiled to native hooks, zero daemons.
Project description
picket
Hard rules for AI coding agents — one rulefile, compiled to native hooks, zero daemons.
Your agents ignore CLAUDE.md instructions some fraction of the time, because prompt
instructions are probabilistic. Their hooks fire deterministically — no probabilistic
instruction-following. picket compiles one
picket.toml (protected branches, protected paths, secret patterns, command deny-list)
into your agent's native hook configuration, then exits. No daemon, no proxy, no LLM
calls, no network. Blocked attempts land in a local JSONL audit log.
demo GIF placeholder — split terminal: left, agent runs
git push origin main→ ✗ BLOCKED[protect-branch:main]; right, agent runscat .env→ ✗ BLOCKED[protect-path:.env*]; bottom,picket logshowing both attempts.
Quickstart
uvx picket init # detect agents, write picket.toml with safe defaults
uvx picket apply # compile + install native hooks (preview first: --dry-run)
# your agent is now fenced - restart any session already running in this repo
uvx picket log # see everything that got blocked
uvx picket unapply # restore your config byte-for-byte
Not on PyPI yet? Run straight from git:
uvx --from git+https://github.com/KaiC5504/picket picket init
The defaults are useful with zero editing: protect this repo's default branch (push,
force-push, deletion), deny reading/editing existing .env* files and anything under
~/.ssh/**, deny recursive deletes that resolve outside the repo, deny force-pushes, and
deny writing well-known secret formats (AWS/GitHub/Slack/Google/Anthropic keys,
private-key blocks) into files or commands.
v0.1 targets Claude Code. Codex, Cursor, and Copilot CLI targets are next (the compiler was built around a target interface for exactly that).
Reversibility is the product
picket apply --dry-runprints the exact unified diff of every file it would touch, and writes nothing.applyis idempotent — running it twice changes nothing.- Your existing hooks are never modified: picket appends one clearly recognizable entry
to the project-scope
.claude/settings.local.json(hooks merge across scopes in Claude Code, so user-level and plugin hooks keep running untouched). - Before the first modifying write, the prior file is backed up;
picket unapplyrestores it byte-for-byte. If you edited the file after apply, unapply surgically removes only picket's entries and tells you why a byte-identical restore wasn't possible. - If your settings file is malformed JSON, picket aborts and touches nothing.
What it is (and is not)
picket is a footgun guard, not a sandbox. It deterministically blocks the accidents
agents actually commit — pushing to main, reading .env, rm -rf outside the repo,
pasting a key into a file — and writes an audit trail. It does not defend against
malicious code, and its shell-command scanning is honest best-effort (e.g. cat .env is
caught; an obfuscated cat $(echo .env) is not). Hooks fail open by design: if the hook
itself breaks, your agent keeps working and the error is logged. For hostile-code
scenarios, pair picket with OS-level sandboxing (e.g. Anthropic's sandboxing features) —
they solve different problems and compose well.
How it compares
| picket | prempti | claude-code-guardrails | claude-guardrails | |
|---|---|---|---|---|
| Architecture | compiles to native hooks, then exits | Falco-engine interceptor in the loop | PreToolUse hook config | deny rules + hooks dotfiles |
| Runtime footprint | none | daemon/interceptor | none | none |
| Rule format | plain TOML | Falco YAML dialect | JSON/scripts | JSON/scripts |
| Agents | Claude Code (Codex/Cursor/Copilot planned) | Claude Code + Codex (exp.) | Claude Code | Claude Code |
| Audit log | local JSONL + picket log |
yes (its strength) | no | no |
| Reversibility | dry-run diff + byte-exact unapply | n/a (runtime component) | manual | manual |
| Windows | first-class (no bash-isms; PowerShell dialect parsed) | POSIX-first | bash hooks | bash hooks |
All of these are good projects — prempti in particular validates the category and is the right choice if you want a security-team-grade Falco pipeline. picket's bet is the dev-first corner: one TOML file, native hooks, nothing running afterwards, works on the machine you actually develop on (including Windows), and trivially reversible.
Rule reference
version = 1
[branches]
protected = ["main"] # push, force-push, and deletion denied
deny_force_push = true # force-push to ANY branch denied
[paths]
protected = [".env*", "~/.ssh/**"] # Read/Write/Edit denied + best-effort shell scan
allow = ["*.example", "*.sample", "*.template"] # exceptions: templates stay usable
# no "/" -> basename match at any depth (like .gitignore); with "/" -> full glob, ** ok
# basename patterns guard existing files only, so `cp .env.example .env` still works;
# full-path patterns like ~/.ssh/** are absolute (nothing may be added there either)
# tradeoff, stated honestly: an agent MAY create a brand-new .env - only the secret
# scan applies to freshly created files; once the file exists it is protected
[secrets]
builtin_patterns = true # AWS, GitHub, Slack, Google, Anthropic keys, private-key blocks
extra = [] # your own regexes
[commands]
deny = [] # glob on the full command string, e.g. "*npm publish*"
deny_recursive_delete_outside_project = true
Every blocked attempt appends one JSONL line to .picket/audit.jsonl:
{"ts": "2026-07-21T14:55:23Z", "agent": "claude-code", "session_id": "...",
"rule": "protect-branch:main", "tool": "PowerShell",
"action": {"command": "git push origin main"}, "decision": "deny", "message": "..."}
Written content is never logged (it may contain the secret being blocked).
Development
uv sync && uv run pytest # 100 tests, run on ubuntu + windows in CI
This repo is fenced with picket itself (picket.toml at the root).
MIT 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 picket-0.1.0.tar.gz.
File metadata
- Download URL: picket-0.1.0.tar.gz
- Upload date:
- Size: 42.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc7875712bcfe50aa6953138b22d220707c9f571fe6e89611c49fc12f42f30f8
|
|
| MD5 |
7959a409c6c80422ee8c85741b121ca7
|
|
| BLAKE2b-256 |
938307795989a950686c8ad78908199f79f74549561b19c0e11afbfd371971a2
|
File details
Details for the file picket-0.1.0-py3-none-any.whl.
File metadata
- Download URL: picket-0.1.0-py3-none-any.whl
- Upload date:
- Size: 23.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bc5ca7f99db247d9975303d6f2e8805c9d57fe63dcb17fa03723db4ac0a6d94a
|
|
| MD5 |
4e8c2435ff2be33ddda58e8d30dfe6bf
|
|
| BLAKE2b-256 |
f9632e93b444d5fc8be19927dc496f813278530cfcb45d866b9b3489c72168d7
|