Hermes plugin that rewrites terminal commands through RTK for lower-context tool output
Project description
RTK Plugin for Hermes
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,grepand file inspection- test runners such as
pytestandcargo 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:
rtk --versionworks in the same environment that starts Hermes.rtk rewrite "git status"returns a rewritten command.plugins.enabledcontainsrtk-rewrite.- Hermes was restarted after the config change.
RTK_HERMES_MODEis not set toofforsuggest.RTK_HERMES_BACKENDSincludes the active terminal backend. By default, onlylocalis 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b4e9638db21749921d6ee35f91be00907b59c3710a8c46afabb042b38fef14af
|
|
| MD5 |
dbe59483e9a3d1e1494d50e6538fa0a2
|
|
| BLAKE2b-256 |
5c2422bcf6fc7561e8a456b04992c3521b33825d36449f6a2819a14ebc3420d5
|
Provenance
The following attestation bundles were made for rtk_hermes-1.2.3.tar.gz:
Publisher:
publish.yml on ogallotti/rtk-hermes
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rtk_hermes-1.2.3.tar.gz -
Subject digest:
b4e9638db21749921d6ee35f91be00907b59c3710a8c46afabb042b38fef14af - Sigstore transparency entry: 1437407413
- Sigstore integration time:
-
Permalink:
ogallotti/rtk-hermes@da69176dc0543b8934998ba74f3403cd28f57764 -
Branch / Tag:
refs/tags/v1.2.3 - Owner: https://github.com/ogallotti
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@da69176dc0543b8934998ba74f3403cd28f57764 -
Trigger Event:
release
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9da353304d817e8303e0f892283b22be79a65cffe9054c9b302fcd4d2aa79be8
|
|
| MD5 |
f33e30acbc35024fe14701004475b2f4
|
|
| BLAKE2b-256 |
80fbc10245d4100ca75913653332c44db970a9e482371c479604aba4d6397728
|
Provenance
The following attestation bundles were made for rtk_hermes-1.2.3-py3-none-any.whl:
Publisher:
publish.yml on ogallotti/rtk-hermes
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rtk_hermes-1.2.3-py3-none-any.whl -
Subject digest:
9da353304d817e8303e0f892283b22be79a65cffe9054c9b302fcd4d2aa79be8 - Sigstore transparency entry: 1437407424
- Sigstore integration time:
-
Permalink:
ogallotti/rtk-hermes@da69176dc0543b8934998ba74f3403cd28f57764 -
Branch / Tag:
refs/tags/v1.2.3 - Owner: https://github.com/ogallotti
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@da69176dc0543b8934998ba74f3403cd28f57764 -
Trigger Event:
release
-
Statement type: