Custom hooks for Charm Crush
Find a file
2026-05-12 12:00:25 -04:00
notifications Remove accidentally committed pycache files 2026-05-12 12:00:25 -04:00
.gitattributes Enforce LF line endings for .sh files via .gitattributes; fix CRLF issue 2026-04-29 15:32:48 -04:00
no-rm-rf.py Migrate hooks from bash scripts to Python 2026-05-11 15:18:52 -04:00
orchestrator.py Add notification orchestrator and notifier modules, remove legacy hooks 2026-05-12 11:59:53 -04:00
README.md Document skill-context-injector.sh in hooks README 2026-04-29 23:13:15 -04:00
rtk-rewrite.py Migrate hooks from bash scripts to Python 2026-05-11 15:18:52 -04:00
TMUX-WATCHER.md Add tmux-watcher hook for automatic tmux session completion detection 2026-05-11 15:17:02 -04:00
tmux-watcher.py Add tmux-watcher hook for automatic tmux session completion detection 2026-05-11 15:17:02 -04:00

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:

  1. Read the relevant CRUSH_TOOL_INPUT_* environment variable.
  2. If the condition doesn't match → exit 0 (pass through, no output).
  3. If it matches → emit a JSON {decision: "allow", context: "..."} (and optionally updated_input) via jq.
  4. If a required tool (jq, rtk) is missing → exit 0 (safe pass-through, warning to stderr).