Skip to content

Commit cbeb748

Browse files
orybandclaude
andcommitted
docs: add design decisions section to README
Explain why this hook exists (upstream compound command evaluation is still unresolved), why bash + shfmt + jq was chosen (plugin ecosystem transparency requirements), why shfmt over alternatives (parser maturity), and why not a compiled binary (plugin binary policy). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b323600 commit cbeb748

1 file changed

Lines changed: 10 additions & 0 deletions

File tree

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,16 @@ bats test/
8585

8686
**`bash -c` on simple path**: `bash -c 'echo hello'` has no shell metacharacters, so it takes the fast path and matches against the prefix list as-is without recursing into the inner command. Don't add `bash`, `sh`, or `zsh` to your allow list.
8787

88+
## Design decisions
89+
90+
**Why this hook exists.** Claude Code evaluates `Bash(cmd *)` permissions against the full command string. Compound commands like `ls | grep foo` or `nvm use && yarn test` don't match individual prefix rules, so users get prompted even when every sub-command is already allowed. As of March 2026, this remains an [open](https://github.com/anthropics/claude-code/issues/29491) [issue](https://github.com/anthropics/claude-code/issues/4236) with no native fix.
91+
92+
**Why bash + shfmt + jq.** Claude Code plugins are expected to be [transparent and auditable](https://code.claude.com/docs/en/discover-plugins) — compiled binaries and obfuscated code are explicitly discouraged. A bash script with well-known dependencies meets this standard. shfmt and jq are both small, fast, and available via standard package managers.
93+
94+
**Why shfmt for parsing.** [shfmt](https://github.com/mvdan/sh) (`mvdan.cc/sh`) is the most complete and battle-tested bash parser available. Its JSON AST output covers all compound constructs: pipes, chains, subshells, command/process substitution, control flow, and declarations. Alternatives like [tree-sitter-bash](https://github.com/tree-sitter/tree-sitter-bash) are designed for editor highlighting rather than semantic analysis, and hand-written parsers (as used by [Dippy](https://github.com/ldayton/Dippy)) trade external dependencies for ongoing maintenance burden and potential correctness gaps.
95+
96+
**Why not a compiled binary.** A Go rewrite using `mvdan.cc/sh` as a library would eliminate the shfmt and jq subprocesses, but would produce an opaque binary that conflicts with the plugin ecosystem's source-readability expectations. The current approach adds ~100–150ms of subprocess overhead per compound command, well within Claude Code's hook timeout defaults.
97+
8898
## Credits
8999

90100
Based on [claude-code-plus](https://github.com/AbdelrahmanHafez/claude-code-plus) (MIT). Key differences: deny list support, active deny for compounds, fast path for simple commands, falls through on empty parse (the original approves), settings layer support, env var stripping, and a test suite.

0 commit comments

Comments
 (0)