Because ls shouldn't need approval
🍴 Fork Enhancements (vs upstream)
- File Edit/Read Approval —
allow-edit/read,ask-edit/read,deny-edit/readrules - Include directive —
include <path-or-glob>for composable config files - Context-aware rules —
[flags]syntax with@subshell,@compound, negation (!) - Custom wrappers —
wrapper <name>for project-specific tools (ssh, docker exec, etc.) - Option rules —
allow-opt,ask-opt,deny-optfor subcommand/flag control - WebSearch support — auto-approval for WebSearch tool (by tony)
- Gemini CLI support — integrated hook support for Gemini CLI tools
- Structured JSON output — for PostToolUse hooks (by tony)
- SSH/sudo handlers — remote context support for ssh and sudo commands
- Log rotation —
set log-rotate-max-days Nfor automatic cleanup - Notifier (Sidekick) —
set notifier-command "CMD"for external notifications (mail check). Supports long-polling via--idlein stop hooks. Useset notifier-include "tool1, tool2"to limit when it triggers. - Idle Prompt Notifications —
set idle-notifier-command "notify-send {title} {message}"for notifications when Claude is waiting for input - Hook approvals log control —
set log-hook-approvals offto disable hook-approvals.log - Hybrid mode —
set default passto let Claude decide unmatched commands - Audit log —
cwdandagentfields added for better context - CLI mode — standalone command validation with
--cmd,--stdin,--json,--remote - Multi-Agent Support — dedicated modes for Claude, Gemini, pi-mono, Moltbot?
- pi-mono extension — TypeScript extension for pi-mono AI assistant
- CLI management —
dippy hooks install/uninstall/listanddippy doctordiagnostics
Stop the permission fatigue. Claude Code asks for approval on every
ls,git status, andcat- destroying your flow state. You check Slack, come back, and your assistant's just sitting there waiting.
Dippy is a shell command hook that auto-approves safe commands while still prompting for anything destructive. When it blocks, your custom deny messages can steer Claude back on track—no wasted turns. Get up to 40% faster development without disabling permissions entirely.
Built on Parable, our own hand-written bash parser—no external dependencies, just pure Python. 14,000+ tests between the two.
Example: rejecting unsafe operation in a chain
Example: rejecting a command with advice, so Claude can keep going
- Complex pipelines:
ps aux | grep python | awk '{print $2}' | head -10 - Chained reads:
git status && git log --oneline -5 && git diff --stat - Cloud inspection:
aws ec2 describe-instances --filters "Name=tag:Environment,Values=prod" - Container debugging:
docker logs --tail 100 api-server 2>&1 | grep ERROR - Safe redirects:
grep -r "TODO" src/ 2>/dev/null,ls &>/dev/null - Command substitution:
ls $(pwd),git diff foo-$(date).txt
- Subshell injection:
git $(echo rm) foo.txt,echo $(rm -rf /) - Subtle file writes:
curl https://example.com > script.sh,tee output.log - Hidden mutations:
git stash drop,npm unpublish,brew unlink - Cloud danger:
aws s3 rm s3://bucket --recursive,kubectl delete pod - Destructive chains:
rm -rf node_modules && npm install(blocks the whole thing)
Subagents ignore PreToolUse hook decisions - Claude Code subagents (spawned via Task tool) do not respect allow/deny decisions from PreToolUse hooks. Even when Dippy returns "permissionDecision": "allow", subagents will still prompt for approval.
- Cause: Known bug in Claude Code (#4740, #4669)
- Status: Closed as "not planned" by Anthropic (January 2026)
- Impact: Hooks work correctly in main sessions but are ignored in subagents
- Workaround: Use explicit config rules instead of relying on hook decisions
See docs/subagent-hook-issues.md for detailed analysis.
# Clone the repository
git clone https://github.com/orgoj/Dippy.git
cd Dippy
# Install via uv tool (recommended)
uv tool install .
# Verify installation
dippy --versionNote: Make sure ~/.local/bin is on your PATH.
uv pip install -e .The easiest way to configure Dippy is using the CLI commands:
# Install for Claude Code (global)
dippy hooks install claude --global
# Install for Gemini CLI (global)
dippy hooks install gemini --global
# Install for Cursor IDE (global)
dippy hooks install cursor --global
# Install for Windsurf (global)
dippy hooks install windsurf --global# Check hook status
dippy hooks list
# Run diagnostics
dippy doctorThat's it! Dippy is now configured and will auto-approve safe commands.
If you prefer manual configuration or need project-specific settings:
Claude Code - add to ~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash|Write|Edit|MultiEdit|Read|LS|Glob|Grep|Search|WebSearch|mcp__.*",
"hooks": [{ "type": "command", "command": "dippy" }]
}
],
"PostToolUse": [
{
"matcher": "Bash|WebSearch|mcp__.*",
"hooks": [{ "type": "command", "command": "dippy" }]
}
]
}
}Gemini CLI - add to ~/.gemini/settings.json:
{
"hooks": {
"BeforeTool": [
{
"matcher": "run_shell_command|write_file|replace|read_file|google_web_search",
"hooks": [{ "type": "command", "command": "dippy --gemini" }]
}
],
"AfterTool": [
{
"matcher": "run_shell_command|google_web_search",
"hooks": [{ "type": "command", "command": "dippy --gemini" }]
}
]
}
}Hooks installed:
- PreToolUse: Validates tools BEFORE execution (Bash, file ops, WebSearch, MCP)
- PostToolUse: Shows feedback messages AFTER execution (for
afterdirective)
dippy hooks list # Show hook status for all agents
dippy hooks install <agent> # Install hooks (project-local)
dippy hooks install <agent> --global # Install hooks (global)
dippy hooks uninstall <agent> # Remove hooks (project-local)
dippy hooks uninstall <agent> --global # Remove hooks (global)Supported agents: claude, gemini, cursor, windsurf
Status indicators:
+= installed?= legacy (olddippy-hookdetected)= not installed
Scopes:
- Project-local (default):
.claude/settings.json,.cursor/hooks.json - Global (
--global):~/.claude/settings.json,~/.cursor/hooks.json
dippy doctor # Run all health checks
dippy doctor --agent claude # Check specific agent
dippy doctor --verbose # Show detailed diagnosticsHealth checks:
- ✓ Installation (on PATH, version check)
- ✓ Hook status per agent (Claude, Gemini, Cursor, Windsurf, pi-mono)
- ✓ Legacy hook detection with full path
- ✓ pi_wrapper check for pi-mono/moltbot
- ✓ Configuration validation (syntax errors)
- ✓ Log health (writable directories, file size warnings)
Exit codes: 0 (OK), 1 (warnings), 2 (critical issues)
Validate commands without running as a hook:
dippy --cmd 'rm -rf /' # validate a command
dippy --cmd 'ls -la' --json # JSON output
dippy --cmd 'git status' --cwd /path
echo 'ls -la' | dippy --stdin # read command from stdinOptions:
--cmd COMMAND— command to validate--stdin— read command from stdin--cwd PATH— working directory--json— output as JSON--config PATH— custom config file--agent NAME— force agent name in audit log--remote— skip local path checks--version— show version
Dippy adapts its output format and behavior based on the agent:
| Agent | Flag | Env Var | Hook Support |
|---|---|---|---|
| Claude Code | --claude |
DIPPY_CLAUDE=1 |
✅ |
| Gemini CLI | --gemini |
DIPPY_GEMINI=1 |
✅ |
| Cursor IDE | --cursor |
DIPPY_CURSOR=1 |
✅ |
| Windsurf | --windsurf |
DIPPY_WINDSURF=1 |
✅ |
| pi-mono | --pi |
DIPPY_PI=1 |
extension |
| Moltbot | --moltbot |
DIPPY_MOLTBOT=1 |
extension |
| OpenAI Codex | --codex |
DIPPY_CODEX=1 |
partial |
| PearAI | --pearai |
DIPPY_PEARAI=1 |
partial |
Each agent mode maintains its own approval log (e.g., ~/.claude/hook-approvals.log).
Dippy reads config from ~/.dippy/config (global) and .dippy (project).
# Allow safe commands
allow git status
allow ls *
allow cat *
# Block dangerous commands
deny rm -rf *
deny docker rm *
# Prompt with message
deny pip install "Use uv pip install instead"
deny python "Use uv run python"Auto-approve file operations using the same config:
allow-read src/**
allow-edit src/**
deny-read **/.env*
deny-edit **/.env*
ask-edit **/config.*# Include external config files
include ~/.dippy/shared-rules
include .dippy-local-*
# Log settings
set log ~/.dippy/audit.log
set log-full
set log-rotate-max-days 30
set log-hook-approvals off
# Default behavior
set default ask # prompt (default)
# set default pass # let Claude decide
# set default allow # auto-approve
# Context-aware rules
deny [!@subshell] cd * # deny cd outside subshell
allow [@subshell] cd * # allow (cd x && y)
# Custom wrappers
wrapper docker-exec
allow [docker-exec,prod] read *
# Option rules
allow-opt git status fetch log diff
deny-opt "git push" --force "Use --force-with-lease"
# MCP tools
allow-mcp mcp__github__get_*
deny-mcp mcp__*__delete_*
# After hook
after git commit * "Check project-tasks.md"Full documentation: docs/config.md
# Run diagnostics
dippy doctor
# Check hook status
dippy hooks list
# Reinstall hooks
dippy hooks uninstall claude --global
dippy hooks install claude --global- Claude Code:
~/.claude/hook-approvals.log - Gemini CLI:
~/.gemini/hook-approvals.log - Dippy audit:
~/.dippy/audit.log
- Hook not triggering → Run
dippy doctorto diagnose - JSON syntax errors → Check config with
dippy doctor - Legacy hook detected → Run
dippy hooks install <agent> --global
# Remove hooks
dippy hooks uninstall claude --global
dippy hooks uninstall gemini --global
# Uninstall tool
uv tool uninstall dippyDippy includes a TypeScript extension for pi-mono:
# Link the extension
ln -s /path/to/dippy/pi-extension/dippy-extension.ts \
~/.pi/agent/extensions/dippy-extension.tsUses your existing ~/.dippy/config and .dippy files.
# Install in development mode
uv pip install -e .
# Run tests
uv run python -m pytest
# Run specific test
uv run python -m pytest tests/test_agents.py -vFull documentation: docs/config.md Upstream: ldayton/Dippy