Metadata-Version: 2.4
Name: hermes-bus-plugin
Version: 0.4.3
Summary: Hermes bus integration plugin — auto-start, auto-register, message injection, bus tools
Author-email: LinQuan <i@linquan.name>
License: MIT
Project-URL: homepage, https://github.com/mlinquan/hermes-bus-plugin
Project-URL: repository, https://github.com/mlinquan/hermes-bus-plugin
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: hermes-bus>=0.4.3
Requires-Dist: hermes-notify>=0.4.3
Dynamic: license-file

# hermes-bus-plugin

[English](./README.md) | [中文](./README.zh.md)

Hermes Agent plugin for message bus integration — lifecycle management, external message injection, bus tools.

A thin integration layer between Hermes Agent and the `hermes-bus` / `hermes-notify` packages.

## Install

```bash
# Via Hermes plugin manager
hermes plugins install hermes-bus-plugin

# Or copy to ~/.hermes/hermes-agent/plugins/
cp -r hermes-bus-plugin ~/.hermes/hermes-agent/plugins/
hermes plugins enable hermes-bus-plugin
```

## Session Naming

Each CLI window registers a unique bus endpoint on startup. The default endpoint is `hermes-bus` (first session), with `hermes-bus-2`, `hermes-bus-3`, etc. for additional sessions. To give your session a stable name that survives reconnection:

```bash
/title my-agent-name
```

The plugin uses the title set by `/title` as the bus endpoint name.

| Action | When | Description |
|--------|------|-------------|
| Start bus daemon | Plugin load | Ensures hermes-bus is running |
| Register listener | Plugin load | Opens a bus endpoint for incoming messages |
| Print notifications | On bus message | `print: true` → terminal (only when context is NOT true) |
| Inject context + push | On bus message | `context: true` → inject into LLM context + push to Agent via `pending_input` (**overrides print**, token-heavy — use sparingly) |
| Execute command | On bus message | `command` → async subprocess (audio, scripts, etc.) — runs inside Hermes process, no external daemon needed |

## Provided Tools

**bus_send** — send a message through the bus to any endpoint:
```
bus_send(target="notifier", type="progress", text="50% done")
```

**bus_status** — check bus health and connected endpoints:
```
bus_status()
```

**bus_info** — show current session's bus connection details:
```
bus_info()
```

## Route Rules

Messages arriving at the bus are matched against `~/.hermes/bus-rules.yaml` rules.
Each rule can trigger three independent actions:

| Field | Behavior | Default |
|-------|----------|---------|
| `print` | Print to terminal | `false` |
| `print_format` | Template or script for terminal output | `{text}` |
| `context` | Inject into LLM context + push to Agent | `false` |
| `context_format` | Template or script for context/push text | `{text}` |
| `command` | Execute shell command (audio, script, etc.) | none |

### Priority Logic

`context` and `print` are mutually exclusive:
- `context: true` → inject context + push to Agent (**print is ignored**). ⚠️ Token-heavy — each push triggers an Agent turn.
- `print: true` → terminal output only (only when context is NOT true)
- `command` → always executed if defined, independent of context/print

### Format Templates

`print_format` and `context_format` support these placeholders:

| Placeholder | Description |
|-------------|-------------|
| `{from}` | Sender endpoint name |
| `{text}` | Message body text |
| `{type}` | Message type |
| `{ts}` | Unix timestamp (raw) |
| `{ts:%Y-%m-%d %H:%M:%S}` | strftime formatted |
| `{color:cyan}` | ANSI foreground color (black/red/green/yellow/blue/magenta/cyan/white) |
| `{color:bold_green}` | Bold color variant |
| `{bold}` | Bold text |
| `{reset}` | Reset all styles |

### Script Support

If `print_format` or `context_format` starts with `~` or `/` and points to an executable file, the script is run with `FROM`, `TYPE`, `TEXT` as environment variables and its stdout is used as the rendered output (supports ANSI colors).

```bash
#!/bin/bash
# format-notify.sh — example format script
GREEN="\033[1;32m"
YELLOW="\033[1;33m"
RESET="\033[0m"

case "$TYPE" in
  task_done)  echo -e "${GREEN}✔ ${FROM}${RESET} — ${TEXT}" ;;
  task_error) echo -e "${YELLOW}✖ ${FROM} failed${RESET}\n   ${TEXT}" ;;
  *)          echo -e "${FROM}: ${TEXT}" ;;
esac
```

```yaml
# bus-rules.yaml
- match_type: task_done
  print: true
  print_format: "~/scripts/format-notify.sh"
```

### Example Rules

```yaml
callbacks:
  # Notification only (no context)
  - match_type: ack
    print: true
    print_format: "{color:cyan}📬 {from}{reset}  {text}  [{ts:%H:%M}]"
    context: false

  # Silent context injection
  - match_type: progress
    print: false
    context: true

  # Context + terminal + audio
  - match_type: task_done
    print: true
    print_format: "{color:bold_green}✔ {from}{reset} → {text}  {color:cyan}[{ts:%H:%M:%S}]{reset}"
    context: true
    command: "afplay ~/sounds/done.mp3"
```

## Requirements

- `hermes-bus` (pip)
- `hermes-notify` (pip)

Both are auto-detected — the plugin degrades gracefully if they're missing.

## Architecture

```
External process ──→ hermes-bus ──→ hermes-bus-plugin ──→ LLM context
                        (socket)        ├─ pre_llm_call hook
                                        └─ async subprocess (command: audio/script)

Hermes session ──→ bus_send tool ──→ hermes-bus ──→ target endpoint
```

`command` execution (audio playback, shell scripts) runs inside the Hermes process via `subprocess.Popen`. No external daemon (`bus-notifier`) needed — one less process to manage, no point-to-point routing issues, no silent failures.
