Summary
Add support for a {"action": "rewrite", "args": {...}} return value to pre_tool_call hook callbacks, allowing plugins to rewrite tool arguments before execution. This would bring parity with Claude Code's PreToolUse permissionDecision: "allow" + updatedInput mechanism, and unblock cleaner integrations for tools like RTK that ship native hook support for other agents.
Problem
Currently pre_tool_call callbacks support two control-flow actions:
- Implicit allow (return
None)
- Block (return
{"action": "block", "message": "..."} — handled by get_pre_tool_call_block_message at hermes_cli/plugins.py:1172-1208)
There's no way for a plugin to inspect tool args and rewrite them before execution. This forces output-side workarounds (using transform_terminal_output post-hoc) for use cases that would be cleaner — and slightly more efficient — at the input side.
Concrete use case
RTK is a CLI proxy that filters shell command output for token efficiency (60–90% savings on git status, cargo test, pytest, etc.). It ships native hooks for Claude Code, Cursor, Gemini CLI, and Copilot — every platform whose PreToolUse-equivalent supports args rewriting. The integration pattern is uniform: git status → rtk git status so the model sees the filtered output without any plugin-side filtering logic.
Hermes is the only major agent platform missing from RTK's hook lineup, specifically because pre_tool_call cannot rewrite. As a workaround I shipped a Hermes plugin (ta3pks/hermes-rtk-hook) that hooks transform_terminal_output and pipes raw output through rtk pipe -f <filter> post-hoc. End-to-end measurements: 98.1% reduction on cargo test (50 tests), 90.2% on git log --oneline (50 commits), 33.4% on find. So the savings target reaches the model — but the original command still runs to completion before the hook fires, so wallclock cost is unchanged. With pre_tool_call rewrite support, the plugin could pivot to args-rewriting and gain wallclock parity with RTK's other integrations, plus lose its own command→filter mapping (RTK would do it natively).
Proposed shape
def my_rewrite_hook(tool_name, args, **_):
if tool_name == "terminal" and args.get("command", "").startswith("git status"):
return {"action": "rewrite", "args": {"command": f"rtk {args['command']}"}}
return None
The existing get_pre_tool_call_block_message could be extended (or renamed to get_pre_tool_call_directive) to also surface a rewrite directive back to the dispatcher in model_tools.py. Strictly additive — plugins not returning "action": "rewrite" are unaffected.
Related
Notes
- The
pre_gateway_dispatch hook already supports {"action": "rewrite", "text": "..."} (hermes_cli/plugins.py:92-99), so the dispatch shape is precedented.
- Backwards compatibility: strictly additive.
Summary
Add support for a
{"action": "rewrite", "args": {...}}return value topre_tool_callhook callbacks, allowing plugins to rewrite tool arguments before execution. This would bring parity with Claude Code's PreToolUsepermissionDecision: "allow"+updatedInputmechanism, and unblock cleaner integrations for tools like RTK that ship native hook support for other agents.Problem
Currently
pre_tool_callcallbacks support two control-flow actions:None){"action": "block", "message": "..."}— handled byget_pre_tool_call_block_messageathermes_cli/plugins.py:1172-1208)There's no way for a plugin to inspect tool args and rewrite them before execution. This forces output-side workarounds (using
transform_terminal_outputpost-hoc) for use cases that would be cleaner — and slightly more efficient — at the input side.Concrete use case
RTK is a CLI proxy that filters shell command output for token efficiency (60–90% savings on
git status,cargo test,pytest, etc.). It ships native hooks for Claude Code, Cursor, Gemini CLI, and Copilot — every platform whose PreToolUse-equivalent supports args rewriting. The integration pattern is uniform:git status→rtk git statusso the model sees the filtered output without any plugin-side filtering logic.Hermes is the only major agent platform missing from RTK's hook lineup, specifically because
pre_tool_callcannot rewrite. As a workaround I shipped a Hermes plugin (ta3pks/hermes-rtk-hook) that hookstransform_terminal_outputand pipes raw output throughrtk pipe -f <filter>post-hoc. End-to-end measurements: 98.1% reduction oncargo test(50 tests), 90.2% ongit log --oneline(50 commits), 33.4% onfind. So the savings target reaches the model — but the original command still runs to completion before the hook fires, so wallclock cost is unchanged. Withpre_tool_callrewrite support, the plugin could pivot to args-rewriting and gain wallclock parity with RTK's other integrations, plus lose its own command→filter mapping (RTK would do it natively).Proposed shape
The existing
get_pre_tool_call_block_messagecould be extended (or renamed toget_pre_tool_call_directive) to also surface a rewrite directive back to the dispatcher inmodel_tools.py. Strictly additive — plugins not returning"action": "rewrite"are unaffected.Related
transform_user_input/before_llm_call/intercept_tool_call. This request is a narrower, focused ask: add only therewritedirective to the existingpre_tool_callsurface, parallel to howblockalready works. Either issue can subsume this; I'm filing focused so the use case is concrete.Notes
pre_gateway_dispatchhook already supports{"action": "rewrite", "text": "..."}(hermes_cli/plugins.py:92-99), so the dispatch shape is precedented.