Skip to content
Closed
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
19 changes: 13 additions & 6 deletions hermes_cli/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"post_llm_call",
"on_session_start",
"on_session_end",
"pre_context_compress", # Allow alternative context assembly before compression
}

ENTRY_POINTS_GROUP = "hermes_agent.plugins"
Expand All @@ -72,6 +73,7 @@ def _env_enabled(name: str) -> bool:
# Data classes
# ---------------------------------------------------------------------------


@dataclass
class PluginManifest:
"""Parsed representation of a plugin.yaml manifest."""
Expand All @@ -83,7 +85,7 @@ class PluginManifest:
requires_env: List[str] = field(default_factory=list)
provides_tools: List[str] = field(default_factory=list)
provides_hooks: List[str] = field(default_factory=list)
source: str = "" # "user", "project", or "entrypoint"
source: str = "" # "user", "project", or "entrypoint"
path: Optional[str] = None


Expand All @@ -103,6 +105,7 @@ class LoadedPlugin:
# PluginContext – handed to each plugin's ``register()`` function
# ---------------------------------------------------------------------------


class PluginContext:
"""Facade given to plugins so they can register tools and hooks."""

Expand Down Expand Up @@ -151,8 +154,7 @@ def register_hook(self, hook_name: str, callback: Callable) -> None:
"""
if hook_name not in VALID_HOOKS:
logger.warning(
"Plugin '%s' registered unknown hook '%s' "
"(valid: %s)",
"Plugin '%s' registered unknown hook '%s' (valid: %s)",
self.manifest.name,
hook_name,
", ".join(sorted(VALID_HOOKS)),
Expand All @@ -165,6 +167,7 @@ def register_hook(self, hook_name: str, callback: Callable) -> None:
# PluginManager
# ---------------------------------------------------------------------------


class PluginManager:
"""Central manager that discovers, loads, and invokes plugins."""

Expand Down Expand Up @@ -232,7 +235,9 @@ def _scan_directory(self, path: Path, source: str) -> List[PluginManifest]:

try:
if yaml is None:
logger.warning("PyYAML not installed – cannot load %s", manifest_file)
logger.warning(
"PyYAML not installed – cannot load %s", manifest_file
)
continue
data = yaml.safe_load(manifest_file.read_text()) or {}
manifest = PluginManifest(
Expand Down Expand Up @@ -306,8 +311,10 @@ def _load_plugin(self, manifest: PluginManifest) -> None:
ctx = PluginContext(manifest, self)
register_fn(ctx)
loaded.tools_registered = [
t for t in self._plugin_tool_names
if t not in {
t
for t in self._plugin_tool_names
if t
not in {
n
for name, p in self._plugins.items()
for n in p.tools_registered
Expand Down
Loading