All plugins

hermes-wsl-terminal

Run Hermes' terminal tools inside an existing WSL distro on Windows: one bash -c per command as the distro's default user, over the same files Windows sees at /mnt/<drive>, with no Docker Desktop, no image and no container to start or tear down. A host cwd is translated (C:\Users\me\proj becomes /mnt/c/Users/me/proj, \\wsl.localhost paths become the distro's own paths) and the mount root is asked once with wslpath, so a distro that moved its automount point still gets correct paths. cwd persists across commands, stdin and exit codes ride through wsl.exe, and hermes doctor / setup and the dashboard picker get real rows (wsl.exe path, WSL version, distros present, selected distro). HERMES_WSL_DISTRO and HERMES_WSL_USER select the distro and the user. Disclosure — this is a full-access backend, not a sandbox: when it is the selected backend, everything the terminal tool runs executes as your user inside your distro, with that distro's files, permissions and network access, so dangerous-command approval stays on and Hermes never stops the distro. The plugin itself reads no credentials, stores nothing and makes no network calls; it spawns wsl.exe per command and wslpath once per distro. It rewrites one core-produced string, the trailing cwd marker, because under WSL a command substitution is handed the directory wsl.exe started in rather than the shell's current one (measurements in the repo's VALIDATION.md); nothing in Hermes is patched or rebound at runtime.

Yicheng-MarkToolsv0.1.0Updated 12 days ago

About

Run Hermes' terminal tools inside an existing WSL distro on Windows: one bash -c per command as the distro's default user, over the same files Windows sees at /mnt/<drive>, with no Docker Desktop, no image and no container to start or tear down. A host cwd is translated (C:\Users\me\proj becomes /mnt/c/Users/me/proj, \\wsl.localhost paths become the distro's own paths) and the mount root is asked once with wslpath, so a distro that moved its automount point still gets correct paths. cwd persists across commands, stdin and exit codes ride through wsl.exe, and hermes doctor / setup and the dashboard picker get real rows (wsl.exe path, WSL version, distros present, selected distro). HERMES_WSL_DISTRO and HERMES_WSL_USER select the distro and the user. Disclosure — this is a full-access backend, not a sandbox: when it is the selected backend, everything the terminal tool runs executes as your user inside your distro, with that distro's files, permissions and network access, so dangerous-command approval stays on and Hermes never stops the distro. The plugin itself reads no credentials, stores nothing and makes no network calls; it spawns wsl.exe per command and wslpath once per distro. It rewrites one core-produced string, the trailing cwd marker, because under WSL a command substitution is handed the directory wsl.exe started in rather than the shell's current one (measurements in the repo's VALIDATION.md); nothing in Hermes is patched or rebound at runtime.

hermes-wsl-terminal

Run Hermes' terminal tools inside a WSL distro you already have, instead of a Windows shell.

# config.yaml
terminal:
  backend: wsl

One bash -c per command through wsl.exe -d <distro>, as the distro's normal user. No
container, no image, no Docker Desktop, nothing to start or tear down — the distro is already
installed and wsl.exe starts it on demand.

Why this exists

Hermes on Windows runs its shell commands through Git Bash on the Windows filesystem. That is
the wrong environment for anyone whose toolchain lives in Linux: apt, systemd, a Linux
node/python/git, POSIX permissions and paths, a project checked out under ~/.

The existing ways out are heavier than they need to be. The Docker backend needs Docker
Desktop and an image. The SSH backend needs an sshd inside the distro and a key. WSL Containers
(wslc) is a sandbox runtime that not every Windows build has. A WSL distro needs none of
that: it is already there, it boots on first use, and Windows drives are mounted inside it at
/mnt/<drive>, so the project you opened in Hermes is the same directory the commands run in.

Requirements

  • Windows 10/11 with WSL, and at least one distro (wsl -l -q lists one).
  • Hermes 0.20.6 or newer.
  • Nothing else. The plugin has no Python dependencies and makes no network calls.

Install

hermes plugins install Yicheng-Mark/hermes-wsl-terminal
hermes plugins enable hermes-wsl-terminal
hermes config set terminal.backend wsl

Or clone it into ~/.hermes/plugins/hermes-wsl-terminal and enable it.

Configuration

Optional, both read at environment-creation time:

| Variable | Default | Meaning |
|---|---|---|
| HERMES_WSL_DISTRO | the distro wsl starts with | Which distro commands run in. |
| HERMES_WSL_USER | the distro's default user | Run as another user (wsl -u). |

The mount root is detected per distro with wslpath, so a distro that moved its automount
point in /etc/wsl.conf still gets correct paths.

What the backend does

  • Path translation. A Windows cwd is translated before the command runs: C:\Users\me\proj
    becomes /mnt/c/Users/me/proj, and \\wsl.localhost\Ubuntu-24.04\home\me becomes
    /home/me. Paths already written the Linux way pass through untouched.
  • cwd persistence. cd in one command is the starting directory of the next, the way it
    works in a terminal.
  • stdin, timeouts and background processes come from Hermes' shared environment plumbing,
    so they behave like the other spawn-per-call backends.
  • hermes doctor, hermes setup and the dashboard picker get real rows from the backend:
    the wsl.exe path, the WSL version, the distros present, and which one is selected.

What it does not do

  • No isolation. Commands run as your user in your distro, with your files and your network.
    Destructive-command approval stays on; this is explicitly not a sandbox.
  • No container-style resource limits. container_* settings do not apply to a distro.
  • It never stops the distro. Teardown releases nothing on purpose: your other shells and
    services share it.
  • Files inside the distro are addressed by their UNC path from the Windows side. File
    tools work on \\wsl.localhost\<distro>\home\me\...; the common case — a project on a
    Windows drive — needs no translation at all because /mnt/<drive> is the same directory.

Disclosure

Installing this plugin adds a terminal backend. When it is the selected backend, everything
the model asks the terminal tool to run executes as your user inside your WSL distro, with
that distro's network access and file permissions. The plugin itself reads no credentials,
stores none, and makes no network calls; it spawns wsl.exe and, once per distro, wslpath.
It does not change any Hermes core behaviour: it registers a provider and returns an
environment object.

Development

python -m pytest tests/test_config.py tests/test_wsl_cli.py   # no WSL or Hermes needed
python -m pytest tests/test_live_wsl.py                       # HERMES_WSL_LIVE=1 HERMES_SOURCE=/path/to/hermes-agent
python scripts/check_hermes_contract.py --hermes-source /path/to/hermes-agent

Layout

hermes_wsl_terminal/config.py      path translation and CLI-output parsing (stdlib only)
hermes_wsl_terminal/wsl.py         wsl.exe calls (stdlib only)
hermes_wsl_terminal/environment.py BaseEnvironment: one bash -c per command
hermes_wsl_terminal/provider.py    registration, classification, probe, doctor rows
tests/                             stdlib tests, CLI-shape tests, live distro tests
scripts/check_hermes_contract.py   the plugin against a real Hermes checkout

The two stdlib modules carry the rules a reviewer needs to check — what a Windows path becomes
inside the distro, and how wsl.exe's UTF-16 CLI output is decoded — and they are testable
without a Hermes install or a distro.

README from the reviewed commit 5bad3fb; it updates when the author re-pins.

More by Yicheng-Mark

15 plugins by Yicheng-Mark in the catalog.

Yicheng-Markhermes-dpapi-secretsResolve Hermes environment variables at startup from a single secret file encrypted with the Windows Data Protection API, instead of keeping provider keys in plaintext in .env or auth.json. Config is secrets.dpapi.map, an ENV_VAR -> store-name mapping (bare names or dpapi:// names) plus an optional file path (default <HERMES_HOME>/secrets.dpapi). The file is one DPAPI blob keyed by the current Windows user profile, so it is unreadable by another Windows account and worthless off the machine; it is written by the bundled CLI (python -m hermes_dpapi_secrets set NAME, value read from a prompt or stdin, never from argv) and only read at runtime. Mapped names that are missing or empty are skipped with a warning; a file that is not ours, a blob this user cannot decrypt, or a payload that is not a flat string map is reported as an error with nothing injected. Disclosure - this source decrypts a file for every Hermes process that enables it: resolved values become environment variables of the Hermes process and its children, exactly like .env entries, so anything that can read that environment can see them. User-scope encryption only (the machine-wide flag is never used and is not exposed), and the at-rest protection is a bar for backups, cloud sync and dotfiles, not a boundary against code already running as the same user. Stdlib-only (ctypes): no Python dependencies, no subprocesses, no network calls, no self-update. Windows only. Applying fetched values stays the orchestrator's job; the plugin never writes os.environ itself. Disclosure — decrypts a DPAPI-encrypted secret file (default <HERMES_HOME>/secrets.dpapi) at startup for every Hermes process that enables it, and the resolved values become environment variables of Hermes and its child processes like `.env` entries; the encryption is per Windows user and protects backups, cloud sync and copied files, not against other code running as the same user. Local only: no network calls, no subprocesses.CommunitytoolsYicheng-Markhermes-wincredResolve Hermes environment variables from the current user's Windows Credential Manager at startup, instead of keeping provider keys in plaintext in .env or auth.json. Config is secrets.wincred.map, an ENV_VAR -> generic-credential-target mapping (bare names or wincred:// names); each value is read with CredReadW in the current user's vault and handed to the secret-source orchestrator, which applies it with the usual precedence, provenance and conflict rules. Values may be UTF-8 or UTF-16LE (cmdkey and the Credential UI write UTF-16); anything that does not decode to text, or that contains interior NULs, is skipped with a warning instead of being injected as a mangled key. Disclosure - this source reads your Windows Credential Manager: only the generic credentials named in the map, with no enumeration of the vault. Resolved values become environment variables of the Hermes process and its children, exactly like .env entries, so anything that can read that environment can see them. The package is read-only and stdlib-only (ctypes): it never creates, updates or deletes credentials, writes no files, starts no subprocesses, makes no network calls and does not self-update. Windows only. Credential Manager is user-scoped, so this raises the at-rest bar for backups, cloud sync and dotfiles; it is not a boundary against code already running as the same user. Applying fetched values stays the orchestrator's job; the plugin never writes os.environ itself. Disclosure — when enabled, reads only the Windows Credential Manager generic credentials named in secrets.wincred.map (no vault enumeration) and injects them as environment variables of the Hermes process and its children, exactly like .env entries; read-only, stdlib ctypes, no subprocesses, no network, no file writes; user-scoped at-rest protection, not a boundary against code already running as the same user.CommunitytoolsYicheng-Markhermes-windows-appsAnswer "what is installed on this Windows machine": the software inventory behind Apps & Features, swept straight from the four Uninstall registry keys (the machine's 64-bit and 32-bit views plus the user's), so a program is found even when the Settings page hides it - a per-user install that never shows on the machine page came from the user view on the writing machine. Each row carries version, publisher, size in MB, install date and which view it lives in; the same DisplayName in two views is a 32/64-bit dual install, marked instead of listed twice. Entries the publisher asked to hide (runtime packages, update rollups) are filtered out unless asked for. Details one program with every field its publisher wrote and a reading of the uninstall command (MSI product code, setup executable, appx, script) so the next step is known before anything is run; the install folder is checked against disk. Use it for "what did that installer actually register", for inventory before a cleanup, and for "which of the two installs does uninstalling remove". Read-only: registry keys are enumerated and uninstall strings are reported, never run; no network, no file writes, no elevation. The sweep is pure registry - 102 entries in seven milliseconds on the writing machine, the whole table in ten, no PowerShell, no WMI. The plugin declares no Python dependencies. Windows only. Disclosure - the tools report program names, versions, publishers, sizes, install folders and uninstall commands, and what is hidden from Apps & Features, to the model; treat the output like the machine's own Apps & Features page.CommunitytoolsYicheng-Markhermes-windows-devicesAnswer "which device on this Windows machine is unhappy, and what does it need" on Windows: win_device_table lists every present PnP device with its class (System, Net, HIDClass, Display), its status, its manufacturer, its stable device id, and its problem code in words - 28 is one with no driver installed, 10 one that cannot start, 43 one Windows stopped after it reported a failure, 22 one the person switched off on purpose and is not broken - filtering by name or id substring, by class, or to the devices that need attention; devices no longer plugged in are history, left out unless asked for. win_device_detail takes one device by id or name substring and adds the driver behind it from the signed-driver table - name, version, date, provider, signer, INF file, signed or not - or the absence of one, which for code 28 is the same fact said twice; a disabled device gets a note saying it is on purpose, not an alarm. Use it for the Device Manager yellow-mark question without opening Device Manager, for finding what an uninstalled program left behind, and for what changed after an update. Read-only: nothing is enabled, disabled, updated or reinstalled. Both answers come from CIM (Win32_PnPEntity and Win32_PnPSignedDriver, joined by device id), whose property names, enum values and ids do not change with the display language; the human-facing device names localize and are carried through as data, never parsed. The plugin declares no Python dependencies; there is no network call and no file write. Windows only. Disclosure — the tools report device names, manufacturers, device ids and driver versions to the model, and those reveal the machine's hardware inventory and what is plugged into it; treat the output like a Device Manager window opened on their machine.Communitytools

Developer Mode

Explore built-in Hermes toolsets and actions.

HermesAgent

Developer?

Create a Hermes extension for Desktop or agent workflows.

Developer Section