Custom hooks for Charm Crush
| notifications | ||
| .gitattributes | ||
| no-rm-rf.py | ||
| orchestrator.py | ||
| README.md | ||
| rtk-rewrite.py | ||
| TMUX-WATCHER.md | ||
| tmux-watcher.py | ||
Crush PreToolUse Hooks
These hooks are registered in crush.json under options.hooks.pretooluse. They run before every eligible tool call — they can inject context, modify the tool input, or allow the call to pass through unchanged.
All hooks use Crush's exit-code-based protocol (decision: "allow" or silent pass) — they never block.
Line Endings
This repo includes a .gitattributes file forcing *.sh text eol=lf to prevent CRLF issues on Windows (core.autocrlf=true would otherwise convert LF→CRLF and break bash scripts with set: pipefail\r errors).
rtk-rewrite.sh — Bash Rewriting
| Trigger | Before every bash tool call |
|---|---|
| What it does | Pipes the bash command to rtk rewrite, which replaces commands with RTK-native alternatives where possible (token savings). |
| Exit 0 (or 3) | Rewrite found — emits decision: "allow" with updated_input: {command: "..."} so Crush uses the rewritten command. |
| Exit 1 | No RTK equivalent — passes through unchanged. |
| Exit 2 | Deny rule matched — passes through unchanged. |
| Silent skip | If jq or rtk is missing, or rtk < 0.23.0, exits 0 with no output. |
Source of truth: All rewrite logic lives in the rtk Rust CLI. Edit the registry at https://github.com/rtk-ai/rtk, not this hook.
commit-reminder.sh — Config Commit Reminder
| Trigger | Before every edit or write tool call |
|---|---|
| What it does | Checks if the target file is inside the main repo or any submodule. If so, injects context reminding the agent to update CHANGELOG.md and README.md as needed, then git add, commit, and push. |
| Pass-through | Any other file — exits 0 silently. |
| Silent skip | If jq is missing, exits 0 with no output. |
submodule-watch.sh — Submodule Commit Reminder
| Trigger | Before every edit or write tool call |
|---|---|
| What it does | Resolves the target file path against the superproject's registered submodules. If the file lives inside a submodule, injects context reminding the agent to commit/push the submodule, then update and commit the superproject pointer. |
| Pass-through | Files outside any submodule — exits 0 silently. |
| No deps | Uses only git submodule foreach and jq; no silent-failure path beyond jq (uses same guard as other hooks). |
forgejo-push-reminder.sh — Push Retry Reminder
| Trigger | Before every bash tool call |
|---|---|
| What it does | Checks if the command contains git push. If so, injects context reminding the agent that Forgejo auth occasionally flakes on the first attempt and a retry usually succeeds. |
| Pass-through | Any command without git push — exits 0 silently. |
| No deps | Uses only jq; minimal footprint. |
|---
skill-context-injector.sh — Skill Context Injector
| Trigger | Before every view tool call |
|---|---|
| What it does | Checks if the viewed path is crush://skills/crush-config/SKILL.md. If so, injects context pointing the agent to AGENTS.md, README.md, and CHANGELOG.md in the config repo so the full setup is referenced. |
| Pass-through | Any other path — exits 0 silently. |
| Silent skip | If jq is missing, exits 0 with no output. |
Common Pattern
All hooks follow this shape:
- Read the relevant
CRUSH_TOOL_INPUT_*environment variable. - If the condition doesn't match →
exit 0(pass through, no output). - If it matches → emit a JSON
{decision: "allow", context: "..."}(and optionallyupdated_input) viajq. - If a required tool (
jq,rtk) is missing →exit 0(safe pass-through, warning to stderr).