Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions website/docs/developer-guide/memory-provider-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,59 @@ mgr.on_session_end([])
mgr.shutdown_all()
```

## Adding CLI Commands

Memory provider plugins can register their own CLI subcommand tree (e.g. `hermes my-provider status`, `hermes my-provider config`). This uses a convention-based discovery system — no changes to core files needed.

### How it works

1. Add a `cli.py` file to your plugin directory
2. Define a `register_cli(subparser)` function that builds the argparse tree
3. The memory plugin system discovers it at startup via `discover_plugin_cli_commands()`
4. Your commands appear under `hermes <provider-name> <subcommand>`

**Active-provider gating:** Your CLI commands only appear when your provider is the active `memory.provider` in config. If a user hasn't configured your provider, your commands won't show in `hermes --help`.

### Example

```python
# plugins/memory/my-provider/cli.py

def my_command(args):
"""Handler dispatched by argparse."""
sub = getattr(args, "my_command", None)
if sub == "status":
print("Provider is active and connected.")
elif sub == "config":
print("Showing config...")
else:
print("Usage: hermes my-provider <status|config>")

def register_cli(subparser) -> None:
"""Build the hermes my-provider argparse tree.

Called by discover_plugin_cli_commands() at argparse setup time.
"""
subs = subparser.add_subparsers(dest="my_command")
subs.add_parser("status", help="Show provider status")
subs.add_parser("config", help="Show provider config")
subparser.set_defaults(func=my_command)
```

### Reference implementation

See `plugins/memory/honcho/cli.py` for a full example with 13 subcommands, cross-profile management (`--target-profile`), and config read/write.

### Directory structure with CLI

```
plugins/memory/my-provider/
├── __init__.py # MemoryProvider implementation + register()
├── plugin.yaml # Metadata
├── cli.py # register_cli(subparser) — CLI commands
└── README.md # Setup instructions
```

## Single Provider Rule

Only **one** external memory provider can be active at a time. If a user tries to register a second, the MemoryManager rejects it with a warning. This prevents tool schema bloat and conflicting backends.
40 changes: 39 additions & 1 deletion website/docs/guides/build-a-hermes-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ def register(ctx):
- Called exactly once at startup
- `ctx.register_tool()` puts your tool in the registry — the model sees it immediately
- `ctx.register_hook()` subscribes to lifecycle events
- `ctx.register_command()` — _planned but not yet implemented_
- `ctx.register_cli_command()` registers a CLI subcommand (e.g. `hermes my-plugin <subcommand>`)
- If this function crashes, the plugin is disabled but Hermes continues fine

## Step 6: Test it
Expand Down Expand Up @@ -481,6 +481,44 @@ def register(ctx):

When multiple plugins return context from `pre_llm_call`, their outputs are joined with double newlines and appended to the user message together. The order follows plugin discovery order (alphabetical by plugin directory name).

### Register CLI commands

Plugins can add their own `hermes <plugin>` subcommand tree:

```python
def _my_command(args):
"""Handler for hermes my-plugin <subcommand>."""
sub = getattr(args, "my_command", None)
if sub == "status":
print("All good!")
elif sub == "config":
print("Current config: ...")
else:
print("Usage: hermes my-plugin <status|config>")

def _setup_argparse(subparser):
"""Build the argparse tree for hermes my-plugin."""
subs = subparser.add_subparsers(dest="my_command")
subs.add_parser("status", help="Show plugin status")
subs.add_parser("config", help="Show plugin config")
subparser.set_defaults(func=_my_command)

def register(ctx):
ctx.register_tool(...)
ctx.register_cli_command(
name="my-plugin",
help="Manage my plugin",
setup_fn=_setup_argparse,
handler_fn=_my_command,
)
```

After registration, users can run `hermes my-plugin status`, `hermes my-plugin config`, etc.

**Memory provider plugins** use a convention-based approach instead: add a `register_cli(subparser)` function to your plugin's `cli.py` file. The memory plugin discovery system finds it automatically — no `ctx.register_cli_command()` call needed. See the [Memory Provider Plugin guide](/docs/developer-guide/memory-provider-plugin#adding-cli-commands) for details.

**Active-provider gating:** Memory plugin CLI commands only appear when their provider is the active `memory.provider` in config. If a user hasn't set up your provider, your CLI commands won't clutter the help output.

### Distribute via pip

For sharing plugins publicly, add an entry point to your Python package:
Expand Down
26 changes: 17 additions & 9 deletions website/docs/reference/cli-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -363,22 +363,30 @@ Notes:
## `hermes honcho`

```bash
hermes honcho <subcommand>
hermes honcho [--target-profile NAME] <subcommand>
```

Manage Honcho cross-session memory integration. This command is provided by the Honcho memory provider plugin and is only available when `memory.provider` is set to `honcho` in your config.

The `--target-profile` flag lets you manage another profile's Honcho config without switching to it.

Subcommands:

| Subcommand | Description |
|------------|-------------|
| `setup` | Interactive Honcho setup wizard. |
| `status` | Show current Honcho config and connection status. |
| `setup` | Redirects to `hermes memory setup` (unified setup path). |
| `status [--all]` | Show current Honcho config and connection status. `--all` shows a cross-profile overview. |
| `peers` | Show peer identities across all profiles. |
| `sessions` | List known Honcho session mappings. |
| `map` | Map the current directory to a Honcho session name. |
| `peer` | Show or update peer names and dialectic reasoning level. |
| `mode` | Show or set memory mode: `hybrid`, `honcho`, or `local`. |
| `tokens` | Show or set token budgets for context and dialectic. |
| `identity` | Seed or show the AI peer identity representation. |
| `migrate` | Migration guide from openclaw-honcho to Hermes Honcho. |
| `map [name]` | Map the current directory to a Honcho session name. Omit `name` to list current mappings. |
| `peer` | Show or update peer names and dialectic reasoning level. Options: `--user NAME`, `--ai NAME`, `--reasoning LEVEL`. |
| `mode [mode]` | Show or set recall mode: `hybrid`, `context`, or `tools`. Omit to show current. |
| `tokens` | Show or set token budgets for context and dialectic. Options: `--context N`, `--dialectic N`. |
| `identity [file] [--show]` | Seed or show the AI peer identity representation. |
| `enable` | Enable Honcho for the active profile. |
| `disable` | Disable Honcho for the active profile. |
| `sync` | Sync Honcho config to all existing profiles (creates missing host blocks). |
| `migrate` | Step-by-step migration guide from openclaw-honcho to Hermes Honcho. |

## `hermes memory`

Expand Down
1 change: 1 addition & 0 deletions website/docs/user-guide/features/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Project-local plugins under `./.hermes/plugins/` are disabled by default. Enable
|-----------|-----|
| Add tools | `ctx.register_tool(name, schema, handler)` |
| Add hooks | `ctx.register_hook("post_tool_call", callback)` |
| Add CLI commands | `ctx.register_cli_command(name, help, setup_fn, handler_fn)` — adds `hermes <plugin> <subcommand>` |
| Inject messages | `ctx.inject_message(content, role="user")` — see [Injecting Messages](#injecting-messages) |
| Ship data files | `Path(__file__).parent / "data" / "file.yaml"` |
| Bundle skills | Copy `skill.md` to `~/.hermes/skills/` at load time |
Expand Down
Loading