11310068c6
Part A — pre_command observer hook (observer-first per #64182 ground rule 3): - New VALID_HOOKS event `pre_command`: fires when a recognized slash command is about to be dispatched, BEFORE the handler runs, on both surfaces: - CLI: cli.py process_command (right after alias resolution) - Gateway: gateway/run.py _handle_message cold-path canonical dispatch - Payload: surface ('cli'|'gateway'), command (canonical), alias_used, args_raw, session_key, platform. Return values IGNORED in v1; a plugin returning a directive-shaped dict gets a debug log so future block/rewrite adopters are discoverable (#64231 taxonomy). - Deliberately NOT fired on the gateway running-agent intercept path (/stop, /approve, busy_policy dispatch during an active run): those are control-plane escape hatches on an in-flight run and must stay outside plugin observation/veto reach. - fire_pre_command_hook() helper never raises, so broken plugin infra can never break command dispatch. Part B — ctx.call_mcp (capability-gated, default-off, ground rule 4): - PluginContext.call_mcp(server, tool, arguments, timeout=30): synchronous, callable from plugin hooks/tools, routes through the EXISTING native MCP client machinery (tools.mcp_tool._make_tool_handler: background loop, trust-tier gates, circuit breaker, reconnect) — never a parallel client. - Gate: plugins.entries.<id>.mcp_allowlist (list of server names). Absent key / unreadable config / non-list value => default-deny. Unlisted server raises PermissionError naming the exact config key. TODO seam left for the #64228 declared-capability model. - Bounded: timeout clamped to 1-600s and forwarded to the MCP loop call; results capped at 64KB with truncation marker; stable {ok, result|error, structuredContent?, truncated?} envelope. Tests (transport mocked, no live MCP servers): - tests/hermes_cli/test_pre_command_hook.py: both surfaces fire, canonical alias reporting (/exit->quit, /q->queue), hook-before-handler ordering, control-plane exclusion, hook failure non-fatal, observer-only directive handling. - tests/hermes_cli/test_plugin_call_mcp.py: default-deny (absent entry, unreadable config, non-list, '*'), allowlist enforced per-server, denied calls never touch transport, timeout forwarding/clamping, result truncation, error/structuredContent envelopes. Docs: hooks.md shipped-catalog row for pre_command; plugins.md "Calling MCP servers from plugins" section with the security note. Closes #64204
Website
This website is built using Docusaurus, a modern static website generator.
Installation
yarn
Local Development
yarn start
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
Build
yarn build
This command generates static content into the build directory and can be served using any static contents hosting service.
Deployment
Using SSH:
USE_SSH=true yarn deploy
Not using SSH:
GIT_USER=<Your GitHub username> yarn deploy
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the gh-pages branch.
Diagram Linting
CI runs ascii-guard to lint docs for ASCII box diagrams. Use Mermaid (````mermaid`) or plain lists/tables instead of ASCII boxes to avoid CI failures.