All plugins

habit

Learns which commands you already approve, proposes an allowlist entry after a few matching approvals (the exact command, or any-container docker stop|restart), and only writes it to Hermes's command_allowlist after you run /habit accept. Wipes, download-and-execute pipes, git push and secret paths are never proposed, and /habit undo or /habit uninstall take every entry back. Disclosure — learns from your approval answers in a local plugin database; in enforce mode (off by default) it proposes command_allowlist entries (the exact approved command, or `docker stop|restart *` / `docker compose stop|restart *` for any container or service) and writes them to this profile's config.yaml through Hermes's config load/save only when you run /habit accept (owner_ids-locked on messaging gateways); an accepted entry waves through every command it matches, including ones Hermes flags as dangerous; /habit undo and /habit uninstall remove only the entries Habit added; no network calls, no credentials, no background process.

scottleimrothAutomationv0.1.3Updated 3 hours ago

About

Learns which commands you already approve, proposes an allowlist entry after a few matching approvals (the exact command, or any-container docker stop|restart), and only writes it to Hermes's command_allowlist after you run /habit accept. Wipes, download-and-execute pipes, git push and secret paths are never proposed, and /habit undo or /habit uninstall take every entry back. Disclosure — learns from your approval answers in a local plugin database; in enforce mode (off by default) it proposes command_allowlist entries (the exact approved command, or `docker stop|restart *` / `docker compose stop|restart *` for any container or service) and writes them to this profile's config.yaml through Hermes's config load/save only when you run /habit accept (owner_ids-locked on messaging gateways); an accepted entry waves through every command it matches, including ones Hermes flags as dangerous; /habit undo and /habit uninstall remove only the entries Habit added; no network calls, no credentials, no background process.

Habit

Learns which commands you already approve, proposes a safe allowlist pattern, and only writes it after you confirm — dangerous shapes never auto-allow.

Habit

Demo video (57 seconds): https://x.com/ScottLeimroth/status/2119407230940488173

Why a plugin (not a skill / prompt)

A skill can only remind the model. Habit uses Hermes’s real approval hooks (pre_approval_request / post_approval_response), stores your decisions locally, and — in enforce mode — proposes a safe generalised pattern after a threshold of matching approvals. A person must run /habit accept (CLI or messaging gateway) before anything is written to Hermes’s command_allowlist. That keeps catalog rule 12: Habit never auto-approves and never disables guards.

No extra LLM. Deterministic.

Modes

| mode | behaviour |
|------|-----------|
| off | do nothing |
| observe (default) | record decisions; /habit why works; no proposals, no allowlist writes |
| enforce | after threshold matching approvals, create a pending proposal; still no write until /habit accept |

Confirm step (required)

/habit proposals          list pending
/habit accept <id>        write that pattern to command_allowlist
/habit accept all         accept every pending proposal
/habit reject <id>|all    drop proposal(s)

Unattended runs (cron, gateway with no person, YOLO/oneshot) never call accept — so they never add allowlist entries.

Messaging gateway (Telegram / Discord / …)

/habit is registered via ctx.register_command, so the same slash commands work on the gateway. On Telegram, type /habit accept 1 (or /habit_accept autocomplete forms Hermes may expose). The reply text comes back in the chat. Gateway approvals count only when Hermes supplies an approver identity. Unidentified chat approvals (including Hermes 0.23 Matrix/Telegram events) are ignored. Local keyboard approvals still count. Only explicit accept writes the allowlist.

Owner-lock (gateway)

Mutating commands — accept, reject, undo, forget, uninstall — are owner-locked on the messaging gateway. Only user ids listed in plugins.entries.habit.settings.owner_ids may run them. Everyone else gets a refusal (logged). If Hermes does not expose a sender id, Habit fails closed (refuses). Local CLI / TUI / desktop are exempt (you are at the keyboard). Read-only why / status / proposals / help stay open.

plugins:
  entries:
    habit:
      settings:
        mode: enforce
        threshold: 2
        owner_ids: ["123456789"]   # Telegram / Discord / … user id(s)

What it may propose vs never

| May propose | Never proposes |
|-------------|----------------|
| docker stop *, docker restart *: stop or restart any container, with any stop/restart options | Home / recursive wipe shapes |
| docker compose stop *, docker compose restart *: any service, with any options | Download-and-execute pipes |
| Any other command you approved: that exact command only | git push (incl. force); git branch force delete or force reset |
| | Secret paths; shell chains; docker run/exec/rm/kill, compose down, remote daemons |

Hermes matches an allowlist entry against the whole command line, and * there matches /, spaces and any extra
arguments. A glob is therefore only as narrow as every option of the command behind it: cat ./* would also match
cat ./a ~/.ssh/id_rsa, git log * would also allow git log --output=<file> (writes a file), and pytest * would
also allow pytest --basetemp=<dir> (deletes that directory's contents). So Habit proposes a glob only for the
container lifecycle commands above, and proposes everything else exactly as you approved it. An exact command that
contains *, ? or [ is written with those characters bracketed (ls [*].py), so Hermes matches only that command.

Hermes 0.23 asks for approval only on its own dangerous-command list, so Habit only ever sees those commands; in
practice that is mostly container lifecycle and repeats of other flagged commands you keep approving.

Upgrading from 0.1.2: entries 0.1.2 proposed and you accepted (for example pytest *, git log *, cat ./*)
stay in command_allowlist until you remove them. /habit status lists them; /habit forget <entry> removes one
and /habit uninstall removes every entry Habit added.

Other commands

/habit why          why it asked / pending proposals
/habit status       pending + accepted
/habit forget <p>   drop accepted entry + allowlist row
/habit undo         undo last accept
/habit uninstall    remove every allowlist entry Habit added + clear pending

Install

# Requires installed Hermes 0.23.0 or newer.
hermes plugins install https://github.com/scottleimroth/habit --no-enable
hermes plugins enable habit
hermes plugins doctor habit
# leave observe until you want proposals:
hermes config set plugins.entries.habit.settings.mode observe
# when ready to propose (still needs /habit accept to write):
hermes config set plugins.entries.habit.settings.mode enforce
hermes config set plugins.entries.habit.settings.threshold 3

Uninstall (clean)

/habit uninstall

Then hermes plugins disable habit. Allowlist edits take effect on the next approval check in Hermes 0.23 and survive a restart. Removal does not stop an already-running command, revoke a separate session approval, or remove another overlapping allowlist rule.

Safety / disclosure

  • Observer hooks only — if Habit errors, Hermes asks as usual.
  • Smart/AI approver decisions are never learned. Hermes 0.23 identifies them with surface=smart, decided_by=aux_llm, and choice=smart_approve|smart_deny; any one marker is enough to ignore the event. Human decisions after escalation still count.
  • Never auto-writes command_allowlist. Human /habit accept only.
  • Never proposes the never-auto set.
  • No telemetry. Local SQLite via plugin_db. No secrets from disk.
  • Disclosure: accept and removal persist command_allowlist through hermes_cli.config.load_config / save_config. Hermes 0.23 has no public single-entry revocation helper; its bulk saver merges grants instead of replacing the list. Habit does not write private approval state or override core functions.
  • Non-plugin-API dependencies: gateway.session_context.get_session_env reads caller identity and surface fields (read-only). Habit classifies unknown surfaces as messaging using its own local allowlist of keyboard/service surfaces. This context reader and config persistence are Hermes implementation helpers, not stable plugin context APIs. Next-check synchronisation was tested on 0.23.0. No underscore-prefixed core helpers are imported.
  • Config write errors are reported, with no raw-YAML fallback. Accept stays pending if saving fails. Keep allowlist edits serial: read/modify/save is not a cross-process transaction.

Develop

python -m pytest -q                      # needs pytest
python doctor.py && python validate.py   # need PyYAML
python eval/run_eval.py
HERMES_HOME=... hermes plugins doctor habit
HERMES_HOME=... hermes plugins validate .

See installation details and change history.

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

More by scottleimroth

2 plugins by scottleimroth in the catalog.

scottleimrothsignal-dogListens through the microphone for household sounds (a door knock, an appliance beeping, a smoke alarm or other steady tone) and sends a chat message through the agent's messaging platform. A signal dog is a dog trained to alert a deaf person to sounds; this does the same job with a microphone, and it is built by a deaf developer. Off until the user turns it on with /signaldog on. Not a certified smoke alarm: it can miss sounds and raise false alerts, with measured rates and limits in the README. Disclosure: microphone: while switched on it listens to the microphone continuously. Background process: it runs one long-running ffmpeg process (two for about 30 seconds every 10 minutes while one replaces the other), stopped when the user turns it off or the plugin unloads; ffmpeg must already be installed and the plugin never downloads anything. Audio from the microphone is never kept: it is analysed in memory and dropped, with no option to keep it. /signaldog demo writes one recording made by code (no real sound) to a private temporary folder and deletes it when the demo ends. Stored locally: a size-limited numbers log in $HERMES_HOME/signal-dog/events.log (times, levels, what was detected, plus an optional note of up to 80 characters the user types after /signaldog wrong or right). The read-only tool signal_dog_recent_alerts returns alert times, kinds, pitches, the user's right and wrong marks and the listener state to the agent, so it reaches the user's model provider; it does not check who is asking, so do not use the plugin on an agent that strangers can message. Network: the plugin makes no network calls and has no telemetry; alerts are delivered by the agent through the messaging app the user already set up. No credentials are read or stored. The pre_llm_call hook only notices which chat session is live and reads no message content. While a beep keeps sounding, it edits its own first alert ("still beeping", then "no longer detected") where the messaging app allows edits, and otherwise sends periodic reminder messages (every three minutes by default, for as long as the beep continues); it never edits anyone else's messages.Communitytools

Developer Mode

Explore built-in Hermes toolsets and actions.

HermesAgent

Developer?

Create a Hermes extension for Desktop or agent workflows.

Developer Section