agentleFS
rodos4312rodos4312/Meu-Agent/plugins/AGENTS.md

Meu-Agent / plugins

AGENTS.md0 starsChanged 10 days agoMITView on GitHub

Applies on top of the root AGENTS.md. Authoring guide + canonical compat contract: website/docs/developer-guide/plugins/index.md. Per-kind guides: memory-provider-plugin.md, model-provider-plugin.md, context-engine-plugin.md, image-gen-provider-plugin.md, ... Plugins live in their own directory and work within the ABCs / hooks / ctx surface we provide. A plugin MUST NOT modify run_agent.py, cli.py, gateway/run.py, hermes_cli/main.py, etc. If it needs a capability the framework lacks, widen the generic plugin surface (new hook, new ctx method) and have the plugin use it — never hardcode plugin-specific logic into core…

The file

We split this AGENTS.md into the rules it gives an agent

2 rules. About 0 are common, 0 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    If it needs a capability the framework lacks, widen the generic plugin surface (new hook, new ctx method) and have the plugin use it — …

    line 11
  2. Very specific:

    Never remove or rename PluginContext methods.

    line 154

≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
AGENTS.md

plugins/ — plugin kinds, compat contract, in-tree policy

Applies on top of the root AGENTS.md. Authoring guide + canonical compat contract: website/docs/developer-guide/plugins/index.md. Per-kind guides: memory-provider-plugin.md, model-provider-plugin.md, context-engine-plugin.md, image-gen-provider-plugin.md, ...

Plugins never touch core (Teknium, May 2026)

Plugins live in their own directory and work within the ABCs / hooks / ctx surface we provide. A plugin MUST NOT modify run_agent.py, cli.py, gateway/run.py, hermes_cli/main.py, etc. If it needs a capability the framework lacks, widen the generic plugin surface (new hook, new ctx method) and have the plugin use it — never hardcode plugin-specific logic into core (PR #5295 removed 95 lines of hardcoded honcho argparse from main.py). Plugin setup goes through hermes memory setup → provider.post_setup(hermes_home, config), never a parallel top-level command. A hook with no concrete consumer is speculative infrastructure and is rejected (root).

What may live in this tree (policy)

  • No new in-tree memory providers (May 2026). plugins/memory/ is closed (byterover, retaindb stay until their Oct 15 2026 removal). hindsight (Sep 2026), honcho, supermemory, mem0, openviking and holographic (Oct 2026) moved to the plugin catalog — plugin-catalog/<name>.yaml, auto-installed by hermes_cli/memory_provider_migration.py for homes still configured for them. Host-side code a catalog provider still relies on (the honcho_host_block config storage kind, profile clone / rename / update-sync hooks) resolves the provider's modules through plugins.memory.import_provider_module, never a plugins.memory.<name> import. New backends ship as standalone repos implementing the same MemoryProvider ABC, discovered through the same path, integrated via hermes memory setup / post_setup().
  • Other features that leave core (Home Assistant platform + toolset, Oct 2026 → plugin-catalog/homeassistant.yaml; Spotify toolset → plugin-catalog/spotify.yaml; Singularity terminal backend → plugin-catalog/singularity.yaml) keep their names/config/env and get a row in hermes_cli/left_core_migration.py::LEFT_CORE (catalog plugin + read-only "this home uses it" predicate); hermes update and agent/gateway start install the plugin for homes that used it. Installing and converting core-era state are separate steps: the toolset-scope conversion runs once per home and row (_left_core_scoped in config.yaml) even when the plugin is already there. The automatic install also happens once per home and row (_left_core_installed, written only once the plugin dir exists): a later hermes plugins remove sticks, a failed install retries. Core special cases become PlatformEntry seams the plugin sets (trusted_inbound, display_tier, shared_env_prefixes), never a name check. A core hermes auth <name> login becomes the plugin's own hermes <name> command (row field cli; the old spelling prints the new one), and a nested core command becomes a top-level plugin command (command + old_command: hermes mcp serve → hermes mcp-serve). A feature with no in-home signal (MCP clients live outside Hermes) gets an in_use that is always False: hint only, never an automatic install. A built-in terminal backend leaves its name off BUILTIN_BACKEND_NAMES so the plugin can register it (row field terminal_backend: unknown-backend errors name the install command). A model provider that leaves core (GitHub Copilot → github-copilot) lists its ids and aliases (providers: unknown-provider errors name the install, runtime resolution installs it first for a home that uses it, models.dev never stands in), the env core kept profile-managed (profile_env), the executable/argv subset env writers refuse on every home (launch_env), and the hosts core served by host for any provider name (hosts: a route there is use; a request rejected there names the install); notice adds a per-home sentence to every outcome (a credential source the plugin no longer reads).
  • No new third-party-product plugins (June 2026). Observability/metrics backends, vendor SaaS connectors, analytics dashboards, paid-service tie-ins ship as standalone plugin repos (~/.hermes/plugins/ or pip entry point) promoted in Discord #plugins-skills-and-skins. Reason: every absorbed product is our maintenance burden against a fast-moving core for a backend we don't own. kanban/, disk-cleanup/ are precedent, not an invitation (Langfuse already moved out to the official catalog plugin). Closing such a PR is a coupling decision, not a quality judgment.
  • Reference/docs-companion plugins (example-dashboard, strike-freedom-cockpit, plugin-llm-example, plugin-llm-async-example) live in hermes-example-plugins, not here.

Plugin catalog (plugin-catalog/, Sep 2026)

The ONLY discovery system for out-of-tree plugins. One YAML per entry, 40-hex SHA pin mandatory, human-merged via PR (plugin-catalog/README.md = admission policy, mirrored word for word in website/docs/developer-guide/plugins/catalog-submission.md with the submission guide; plugin-catalog-ci.yml clones each changed entry at its pin and runs hermes plugins validate). removed.yaml is the kill list — every install path (CLI, dashboard, TUI) refuses matches (repo URLs compared by canonical host/owner/repo, so git@/ssh:///www. spellings match); only the CLI has a loud --allow-removed, which is recorded on the install record and is the only thing that exempts an installed plugin from the same check at update, enable and load (gate_manifest). Catalog provenance lives on the installer-owned .install-metadata.json record (catalog block, sha = checked-out commit), NEVER in the tree: the in-tree .hermes-catalog.json is a convenience copy the Desktop reads for "Install here"; Python never trusts it (a repo can ship a forged one). Code: hermes_cli/plugin_catalog.py (loader, live refresh from /docs/api/plugin-catalog.json published by the docs build, in-tree fallback), hermes_cli/plugins_cmd_catalog.py (resolution, .hermes-catalog.json provenance sidecar, search/info/validate, re-pin on update, dashboard/TUI payloads). Never add a second name index: bare names resolve through the catalog or error.

Plugin kinds and their discovery systems

KindWhereDiscoveryNotes
Generalplugins/<name>/, ~/.hermes/plugins/, ./.hermes/plugins/, pip entry pointsPluginManager (hermes_cli/plugins.py), later-winsregister(ctx) registers hooks (pre_tool_call, post_tool_call, pre_llm_call, post_llm_call, on_session_start, on_session_end), tools (ctx.register_tool), CLI subcommands (ctx.register_cli_command — argparse tree wired into hermes at startup, no main.py change), language packs (provides_locales in the manifest auto-registers `locales/<lang>[.tui.desktop].yamlviactx.register_locale_dir; manifest-only packs need no init.py; hermes_cli/plugin_validate_locales.py` validates them)
Memory providerplugins/memory/<name>/plugins/memory/__init__.py: bundled → $HERMES_HOME/plugins/ → ./.hermes/plugins/ (opt-in HERMES_ENABLE_PROJECT_PLUGINS) → hermes_agent.memory_providers entry points; bundled-firstActivated by name via memory.provider, so a dropped-in dir must not shadow a shipped one (reverse of general later-wins). Enumerates without importing. Implements MemoryProvider ABC (agent/memory_provider.py), orchestrated by agent/memory_manager.py: sync_turn, prefetch, shutdown, optional post_setup. cli.py with register_cli(subparser) is wired by discover_plugin_cli_commands() — only for the ACTIVE provider, so hermes --help stays clean
Model providerplugins/model-providers/<name>/providers/__init__.py._discover_providers(), lazy, on first get_provider_profile()/list_providers(); bundled → $HERMES_HOME/plugins/model-providers/ → legacy providers/<name>.py__init__.py calls providers.register_provider(ProviderProfile(...)) at load; last-writer-wins so a user plugin overrides a bundled profile. PluginManager records kind: model-provider manifests but does NOT import them (would double-instantiate); manifests without kind: are auto-coerced by source heuristic (register_provider + ProviderProfile)
Context engine / image-gen / delegation / othersplugins/context_engine/, plugins/image_gen/, plugins/delegation/, ...ABC + orchestrator + per-plugin directoryPlug into agent/context_engine.py, agent/image_gen_provider.py, agent/delegation_provider.py (plugins/delegation/default IS today's delegation routing)
Platform adaptersplugins/platforms/<name>/adapter.pygatewayToken-lock and scoped-secret rules in gateway/AGENTS.md (irc, feishu are canonical)

Discovery timing pitfall: discover_plugins() runs only as a side effect of importing model_tools.py. Code that reads plugin state without importing model_tools.py first must call discover_plugins() explicitly (idempotent). Hooks are invoked from model_tools.py (pre/post tool) and run_agent.py (lifecycle). A non-forced discover_plugins() short-circuits on _discovered: every mid-run load path (install/enable/update on any surface, reload-plugins verb) runs discover_plugins(force=True), and PluginManager.on_plugin_loaded fires from inside that sweep for the newly loaded plugins with an activation summary (hermes_cli/plugins_activation.py: handlers live now; tools/prompt next session; deferred.mcp_servers until mcp.reload). Never emit that event from an RPC. Auxiliary LLM calls (titling, compression, MoA, vision, ...) fire pre_auxiliary_call/post_auxiliary_call from agent/auxiliary_hooks.py (payload = the *_api_request shape + aux_task); they never fire the turn-scoped pre/post_api_request (#79733). When a plugin changes a default, add a migration guard keyed on an "existing config" signal (_explicitly_configured) so existing users keep the old default.

Lifecycle hooks fire under the owning profile's scope, and the caller binds it. on_session_start/on_session_end/sync_turn/shutdown are invoked from the turn (bound) AND from eviction, shutdown, tui_gateway teardown and cron completion (bound by the caller via _run_release_in_profile_scope, _session_profile_runtime_scope, _profile_cron_scope). One process serves several profiles, so a provider never caches hermes_home from initialize() as "the" home — key state by the home it is handed per call (hermes_home_key()) — and never reads os.environ for credentials (agent.secret_scope.get_secret; a check_fn too). Background work starts via agent.memory_provider.spawn_context_thread, never a bare threading.Thread, or the worker runs with no scope and fails closed (or writes into the launch profile's tenant). Platform plugins never mutate os.environ: YAML goes to PlatformConfig.extra through _shared.apply_yaml_bridge, gates through platform_gate_env (gateway/AGENTS.md).

Plugin host boundary (plugins.isolation: host)

in_process (default) imports user plugins into Hermes. host runs every non-bundled plugin in a per-profile host process (hermes_cli/plugin_host.py parent side, plugin_host_child.py child, plugin_host_wire.py framed JSON protocol). The child hands plugins a RemotePluginContext; Hermes registers proxies (tools, hooks, commands, provider objects as subclasses of the ABC it checks). Invariants:

  • Every user-code import path routes to the host or refuses — general plugins (plugins_loader), memory / context engine / cron providers (load_instance), model-provider profiles (plugin_host_profiles.py: credential-free extraction cached by file fingerprint, because discovery runs while hermes_cli.config/auth import; overrides run by name in the host), dashboard APIs (ASGI bridge). plugins/plugin_loader.load_plugin_module refuses any other synthetic-namespace import. A new user-code loader MUST do the same.
  • What cannot cross is one table: plugin_isolation.HOST_UNSUPPORTED_CTX_METHODS (live gateway / adapter objects). The runtime refusal and the static audit (plugin_isolation_audit.py, shown by hermes plugins validate/show) both read it; evals/plugin_isolation/audit_catalog.py re-measures the catalog.
  • Values keep the provider ABC's declared types across the wire: JSON carries dicts, lists and scalars, so plugin_host_wire.restore rebuilds arguments (child) and results (parent) from the ABC's annotations (FetchResult, Path, enums, a returned engine as a proxy). A value a core consumer type-checks must be a dataclass annotated on the ABC method, or it arrives as Record/Opaque.
  • A host never spawns a host (HERMES_PLUGIN_HOST_PROCESS=1 makes isolation_mode() in-process there) and must not start while a module import holds its lock (model-provider discovery is the known case).
  • The isolated party cannot opt out when an operator pins it: isolation_mode() / host_launcher() read through the managed overlay (load_config_readonly), so /etc/hermes/config.yaml beats the profile's own config. Never switch them to a raw user-config read.
  • Plugins stay tenant-unaware: no new API, same ctx. Per-profile env comes from served_profile_child_env, so a host sees only its own profile's secrets.

Native plugin compatibility contract (summary — canonical text in the docs page)

Compatibility is a behavior contract, not a monolithic PLUGIN_API_VERSION, a manifest-wide native api: match, or version literals on unrelated payloads. Documented surfaces stay additive:

  • Hook payload data is added as keyword fields; callbacks are signature-inspected so old narrow signatures receive only the fields they declare and **kwargs callbacks get the full payload.
  • Never remove or rename PluginContext methods; new parameters are optional with defaults and keyword-only where possible.
  • Unknown native manifest fields are ignored.
  • New provider methods get default implementations; optional callback kwargs are signature-inspected, not forwarded unconditionally.
  • A local schema version exists only for a capability with a wire or persisted contract, and old state/config/session replay is preserved or migrated.
  • Deprecations: once-per-process warning, documented replacement + migration note, ≥ 2 subsequent minor releases before removal.
  • Compat tests load frozen plugins through the real discovery path and assert outcomes — never exact registry/catalog counts, source-reading tests, or "a global version literal changed".

Internal import paths are not API

PR #102117 moved internals into <stem>_<topic> siblings. The temporary compat layer that kept the old import paths alive for external plugins was removed after its 2026-09-14 window; an old path now raises ImportError, surfaced as the plugin's load error in hermes plugins list. Plugins build on ctx and the documented ABCs. Never add re-export shims for an internal move. hermes_cli/plugin_compat.py survives only as three inert stubs that already-running pre-removal updaters import.

Tests

tests/plugins/. Load through real discovery with a temp HERMES_HOME; assert behaviour (tool registered, hook fired with expected kwargs), not counts. Opt-in telemetry rule applies to plugins too: no attribution tag ships by default.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.