Skip to content

Feature request: pre_tool_call rewrite action (rewrite tool args from a hook) #18988

@ta3pks

Description

@ta3pks

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 statusrtk 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low — cosmetic, nice to havecomp/pluginsPlugin system and bundled pluginstype/featureNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions