Skip to main content

Hermes plugin that rewrites terminal commands through RTK for lower-context tool output

Project description

RTK Plugin for Hermes

GitHub release CI PyPI License: MIT

rtk-hermes is a small Hermes Agent plugin that rewrites terminal commands through RTK before execution.

Instead of letting Hermes run verbose shell commands directly, the plugin asks rtk rewrite for a lower-context equivalent:

Hermes wants to run:  git status
Plugin rewrites to:   : RTK && rtk git status
Hermes executes:      rtk git status

RTK then returns filtered output to the LLM, which usually means fewer tokens in the context window.

Status

  • Hermes plugin entry point: rtk-rewrite = "rtk_hermes"
  • Hermes hook used: pre_tool_call
  • Default mode: rewrite terminal commands in place
  • Failure mode: fail open; original command runs unchanged
  • Current PyPI/GitHub release: v1.2.3
  • PyPI publishing: automated through GitHub Actions Trusted Publishing; no long-lived PyPI token is required.

Installation

1. Install RTK

brew install rtk

Alternative installer:

curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads/master/install.sh | sh

Verify:

rtk --version
rtk rewrite "git status"

2. Install the plugin into Hermes' Python environment

Install into the same Python environment that runs hermes. Installing into system Python, conda, or a random virtualenv will not make the plugin visible to Hermes.

For the standard Hermes source install, use the bundled virtualenv directly:

HERMES_PY="$HOME/.hermes/hermes-agent/venv/bin/python"
"$HERMES_PY" -m pip install --upgrade rtk-hermes

If hermes is installed through a symlinked shim and the standard path does not exist, derive the Python executable from the real hermes path:

HERMES_BIN="$(command -v hermes)"
HERMES_REAL="$(python3 -c 'import os, sys; print(os.path.realpath(sys.argv[1]))' "$HERMES_BIN")"
HERMES_PY="$(dirname "$HERMES_REAL")/python"
"$HERMES_PY" -m pip install --upgrade rtk-hermes

If that fails with No module named pip, the Hermes virtualenv exists but was created without pip. Use uv to install into that virtualenv without touching system Python:

curl -LsSf https://astral.sh/uv/install.sh | sh
~/.local/bin/uv pip install --python "$HOME/.hermes/hermes-agent/venv/bin/python" --upgrade rtk-hermes

On Debian/Ubuntu, if you see This environment is externally managed, you are using system pip, not Hermes' virtualenv. Do not use --break-system-packages; point the install at Hermes' Python or use the uv pip install --python ... command above.

Pinned GitHub release wheel, if you need it:

HERMES_PY="$HOME/.hermes/hermes-agent/venv/bin/python"
"$HERMES_PY" -m pip install \
  "https://github.com/ogallotti/rtk-hermes/releases/download/v1.2.3/rtk_hermes-1.2.3-py3-none-any.whl"

3. Enable the plugin in Hermes

Hermes v0.11+ treats pip-distributed plugins as opt-in. Add rtk-rewrite to ~/.hermes/config.yaml:

plugins:
  enabled:
    - rtk-rewrite

Restart Hermes or start a new session after changing plugin config.

Current Hermes CLI caveat: hermes plugins enable rtk-rewrite may not recognize pip-only entry points even when the plugin is installed correctly. Editing plugins.enabled directly is the reliable path.

How it works

Agent calls terminal(command="cargo test --nocapture")
  -> rtk-hermes receives Hermes' pre_tool_call hook
  -> plugin calls: rtk rewrite "cargo test --nocapture"
  -> RTK returns: rtk cargo test --nocapture
  -> plugin mutates args["command"] in place
  -> Hermes executes the rewritten command
  -> RTK-filtered output reaches the model

The plugin does not implement command rules itself. All rewrite logic lives in RTK, so new RTK rules become available without a plugin release.

What gets rewritten

Anything supported by rtk rewrite, including common commands around:

  • Git status, logs and diffs
  • ls, find, grep and file inspection
  • test runners such as pytest and cargo test
  • package managers and build tools
  • Docker and Kubernetes commands

Check RTK's own docs for the current command list: https://github.com/rtk-ai/rtk#commands

Runtime configuration

rtk-hermes intentionally avoids adding extra Hermes tools or MCP servers. Runtime behavior is controlled through environment variables so the plugin stays lightweight.

Variable Default Values Behavior
RTK_HERMES_MODE rewrite rewrite, suggest, off rewrite mutates terminal commands; suggest logs suggestions without changing execution; off disables the plugin at register time.
RTK_HERMES_TIMEOUT_MS 2000 positive integer Max time spent in rtk rewrite per command.
RTK_HERMES_PREVIEW_MARKER true true, false Prefixes rewritten shell commands with : RTK && so Hermes previews clearly show RTK is active.
RTK_HERMES_BACKENDS local comma-separated backend names, or all Terminal backends where rewrites are allowed. Defaults to local only because SSH, Docker and remote sandboxes also need rtk installed inside the execution backend.

Example:

export RTK_HERMES_MODE=suggest
export RTK_HERMES_TIMEOUT_MS=500
export RTK_HERMES_PREVIEW_MARKER=false
export RTK_HERMES_BACKENDS=local
hermes

For SSH or another remote backend, only opt in if rtk is installed in that execution environment too:

export RTK_HERMES_BACKENDS=local,ssh
# or, if every configured backend has rtk available:
export RTK_HERMES_BACKENDS=all

Slash command

When the running Hermes version supports plugin slash commands, the plugin registers:

/rtk status
/rtk stats
/rtk reset-stats
/rtk config

The command returns JSON so it is easy to inspect or paste into an issue.

The metrics are process-local counters only. Commands are never stored in metrics to avoid leaking secrets or private shell input.

RTK rewrite exit codes

rtk rewrite uses exit codes to describe its decision:

Code Meaning Plugin behavior
0 Rewrite allowed Apply rewrite when stdout contains a different command.
1 No equivalent Pass through the original command.
2 Deny rule matched Pass through the original command.
3 Ask/confirm verdict; rewritten command exists on stdout Apply rewrite when stdout contains a different command.

Exit code 3 is common for valid rewrites such as git status -> rtk git status and cat file -> rtk read file, so the plugin treats both 0 and 3 as successful rewrites.

Graceful degradation

The plugin should never block command execution.

Condition Behavior
RTK binary not found Plugin does not register the rewrite hook.
Terminal backend is not enabled by RTK_HERMES_BACKENDS Original command runs unchanged.
rtk rewrite times out Original command runs unchanged.
rtk rewrite crashes Original command runs unchanged.
No RTK equivalent Original command runs unchanged.
Unexpected RTK exit code Warning is logged; original command runs unchanged.

MCP and context mode

This plugin is not an MCP server and does not need to be one.

MCP exposes additional tools to Hermes. RTK rewriting needs to intercept Hermes' existing terminal tool before it executes. That belongs in the pre_tool_call plugin hook, not in MCP.

A pure pre_tool_call rewrite hook also avoids changing the tool schema sent to the model. That is safer for prompt caching than adding or removing tools mid-session.

Why output compaction is not enabled here

Hermes also has hooks such as transform_terminal_output and transform_tool_result. They can compact output after tools run, but they are riskier because they can hide debugging evidence or alter structured tool results.

rtk-hermes stays conservative by default:

  • rewrite before execution;
  • let RTK filter command output;
  • do not mutate read_file, search_files, process, or other non-terminal tool results.

If output compaction is added later, it should be opt-in and heavily tested.

Verification

Check the installed entry point:

HERMES_PY="$HOME/.hermes/hermes-agent/venv/bin/python"
"$HERMES_PY" - <<'PY'
import importlib.metadata as md
for ep in md.entry_points().select(group="hermes_agent.plugins"):
    if ep.name == "rtk-rewrite":
        module = ep.load()
        print(ep.name, ep.value, ep.dist.metadata["Version"], hasattr(module, "register"))
PY

Expected shape:

rtk-rewrite rtk_hermes 1.2.3 True

Check Hermes config:

python - <<'PY'
from pathlib import Path
import yaml
cfg = yaml.safe_load((Path.home() / ".hermes/config.yaml").read_text()) or {}
print(cfg.get("plugins", {}).get("enabled", []))
PY

Check RTK behavior:

python - <<'PY'
import subprocess
for cmd in ["ls -la", "git status", "cat /etc/hosts", "echo hello"]:
    cp = subprocess.run(["rtk", "rewrite", cmd], capture_output=True, text=True)
    print(cmd, "rc=", cp.returncode, "stdout=", cp.stdout.strip())
PY

Run tests from this repository:

python -m pip install -e '.[dev]'
python -m pytest
python -m build

Troubleshooting

Plugin installed but Hermes does not load it

Most likely cause: it was installed into the wrong Python environment.

Use the same interpreter chosen during installation:

HERMES_PY="$HOME/.hermes/hermes-agent/venv/bin/python"
"$HERMES_PY" -m pip show rtk-hermes

If that command cannot find the package, reinstall using the same interpreter or the uv pip install --python ... fallback from the installation section.

hermes plugins enable rtk-rewrite says the plugin is not installed

This is a current Hermes CLI limitation for pip-only entry points. Edit ~/.hermes/config.yaml directly:

plugins:
  enabled:
    - rtk-rewrite

Hermes shows no register() function

The installed package is old. Versions before 1.1.0 used the wrong entry point target.

Upgrade from PyPI:

HERMES_PY="$HOME/.hermes/hermes-agent/venv/bin/python"
"$HERMES_PY" -m pip install --upgrade rtk-hermes

If that Python has no pip, use:

~/.local/bin/uv pip install --python "$HOME/.hermes/hermes-agent/venv/bin/python" --upgrade rtk-hermes

Pinned GitHub release wheel:

HERMES_PY="$HOME/.hermes/hermes-agent/venv/bin/python"
"$HERMES_PY" -m pip install --force-reinstall \
  "https://github.com/ogallotti/rtk-hermes/releases/download/v1.2.3/rtk_hermes-1.2.3-py3-none-any.whl"

Rewritten commands do not appear

Check:

  1. rtk --version works in the same environment that starts Hermes.
  2. rtk rewrite "git status" returns a rewritten command.
  3. plugins.enabled contains rtk-rewrite.
  4. Hermes was restarted after the config change.
  5. RTK_HERMES_MODE is not set to off or suggest.
  6. RTK_HERMES_BACKENDS includes the active terminal backend. By default, only local is enabled.

Development

git clone https://github.com/ogallotti/rtk-hermes.git
cd rtk-hermes
python -m pip install -e '.[dev]'
python -m pytest
python -m build

Before releasing:

rm -rf dist/ build/ src/*.egg-info
python -m pytest
python -m build
python -m twine check dist/*

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rtk_hermes-1.2.3.tar.gz (19.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rtk_hermes-1.2.3-py3-none-any.whl (10.6 kB view details)

Uploaded Python 3

File details

Details for the file rtk_hermes-1.2.3.tar.gz.

File metadata

  • Download URL: rtk_hermes-1.2.3.tar.gz
  • Upload date:
  • Size: 19.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rtk_hermes-1.2.3.tar.gz
Algorithm Hash digest
SHA256 b4e9638db21749921d6ee35f91be00907b59c3710a8c46afabb042b38fef14af
MD5 dbe59483e9a3d1e1494d50e6538fa0a2
BLAKE2b-256 5c2422bcf6fc7561e8a456b04992c3521b33825d36449f6a2819a14ebc3420d5

See more details on using hashes here.

Provenance

The following attestation bundles were made for rtk_hermes-1.2.3.tar.gz:

Publisher: publish.yml on ogallotti/rtk-hermes

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rtk_hermes-1.2.3-py3-none-any.whl.

File metadata

  • Download URL: rtk_hermes-1.2.3-py3-none-any.whl
  • Upload date:
  • Size: 10.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rtk_hermes-1.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 9da353304d817e8303e0f892283b22be79a65cffe9054c9b302fcd4d2aa79be8
MD5 f33e30acbc35024fe14701004475b2f4
BLAKE2b-256 80fbc10245d4100ca75913653332c44db970a9e482371c479604aba4d6397728

See more details on using hashes here.

Provenance

The following attestation bundles were made for rtk_hermes-1.2.3-py3-none-any.whl:

Publisher: publish.yml on ogallotti/rtk-hermes

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page