From 6ee4a8ded5a2a59d2914fbf0d5fcda609a1eeac4 Mon Sep 17 00:00:00 2001 From: kriptoburak Date: Tue, 30 Jun 2026 04:58:49 +0300 Subject: [PATCH 1/2] Add Hermes Agent discovery support --- backend/.env.example | 2 +- backend/app/core/config.py | 3 +- .../services/repository_inspection_service.py | 10 ++++- .../test_repository_inspection_service.py | 37 +++++++++++++++++++ docker/.env.example | 2 +- docs/operations.md | 2 +- 6 files changed, 51 insertions(+), 5 deletions(-) diff --git a/backend/.env.example b/backend/.env.example index 11b8d14..6f42a95 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -2,7 +2,7 @@ APP_ENV=development DATABASE_URL=mysql+pymysql://agentskill:agentskill@localhost:3306/agentskill REDIS_URL=redis://localhost:6379/0 GITHUB_TOKEN= -GITHUB_SEARCH_QUERY=claude skill in:name,description,topics +GITHUB_SEARCH_QUERY=("claude skill" OR "agent skill" OR openclaw OR "hermes agent" OR "hermes plugin") in:name,description,topics GITHUB_SEARCH_PER_PAGE=30 GITHUB_MAX_PAGES=5 GITHUB_MAX_RESULTS=300 diff --git a/backend/app/core/config.py b/backend/app/core/config.py index a77cfb8..c669b63 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -20,7 +20,8 @@ class Settings(BaseSettings): github_token: str | None = None github_search_query: str = ( - '"claude skill" OR "agent skill" OR openclaw in:name,description,topics' + '("claude skill" OR "agent skill" OR openclaw OR "hermes agent" ' + 'OR "hermes plugin") in:name,description,topics' ) github_search_per_page: int = 30 github_max_pages: int = 5 diff --git a/backend/app/services/repository_inspection_service.py b/backend/app/services/repository_inspection_service.py index 1f3ce68..287478f 100644 --- a/backend/app/services/repository_inspection_service.py +++ b/backend/app/services/repository_inspection_service.py @@ -31,6 +31,7 @@ ("pdf", ("pdf", "document")), ("memory", ("memory", "vector", "embedding", "lancedb", "rag")), ("search", ("search", "retrieval", "web search")), + ("social", ("social", "twitter", "x/twitter", "tweet")), ("image", ("image", "vision", "screenshot")), ("video", ("video", "ffmpeg", "transcript")), ("terminal", ("terminal", "shell", "cli")), @@ -40,6 +41,7 @@ PLATFORM_KEYWORDS: tuple[tuple[str, tuple[str, ...]], ...] = ( ("Claude", ("claude", "claude skill", "anthropic")), ("MCP", ("mcp", "model context protocol")), + ("Hermes Agent", ("hermes agent", "hermes-agent", "hermes plugin")), ("OpenClaw", ("openclaw",)), ("Codex", ("codex",)), ("Cursor", ("cursor",)), @@ -142,6 +144,12 @@ def _fetch_root_files( def _detect_skill_type(text: str, topics: list[str], source_files: list[str]) -> str: haystack = " ".join([text, " ".join(topics), " ".join(source_files)]).lower() + if ( + "hermes agent" in haystack + or "hermes-agent" in haystack + or "hermes plugin" in haystack + ): + return "hermes_agent_plugin" if "mcp" in haystack or "model context protocol" in haystack: return "mcp_server" if "openclaw" in haystack or "SKILL.md" in source_files: @@ -188,7 +196,7 @@ def _detect_config_keys(readme: str | None, source_files: list[str]) -> list[str filtered = [ key for key in keys - if key.endswith(("KEY", "TOKEN", "SECRET", "URL", "ID")) + if key.endswith(("KEY", "TOKEN", "SECRET", "URL", "ID", "ACTIONS")) and key not in {"README", "HTTP", "HTTPS", "JSON"} ] if "package.json" in source_files: diff --git a/backend/tests/test_repository_inspection_service.py b/backend/tests/test_repository_inspection_service.py index 4232ac1..6eaa8cd 100644 --- a/backend/tests/test_repository_inspection_service.py +++ b/backend/tests/test_repository_inspection_service.py @@ -73,3 +73,40 @@ def test_inspect_skill_repository_extracts_registry_metadata(): assert result.source_files == ["README.md", "mcp.json", "package.json", "tests"] assert result.quality_score >= 60 assert result.verification_status == "readme_parsed" + + +def test_inspect_skill_repository_detects_hermes_agent_plugins(): + skill = Skill( + repo_id=2, + name="hermes-tweet", + full_name="Xquik-dev/hermes-tweet", + description="Hermes Agent plugin for X/Twitter exploration and guarded actions", + html_url="https://github.com/Xquik-dev/hermes-tweet", + stars=10, + forks=1, + language="Python", + topics="hermes-agent,twitter,social", + topics_json=["hermes-agent", "twitter", "social"], + ) + readme = """ + # Hermes Tweet + + Hermes Agent plugin for X/Twitter exploration, timeline reading, and + action-gated posting workflows. + + Configure XQUIK_API_KEY for read tools. Set HERMES_TWEET_ENABLE_ACTIONS=true + only when action tools should be available. + """ + result = inspect_skill_repository( + skill, + Settings(), + client=FakeClient(readme, ["README.md", ".claude-plugin", "pyproject.toml"]), + ) + + assert result is not None + assert result.skill_type == "hermes_agent_plugin" + assert "Hermes Agent" in result.platforms + assert "social" in result.capabilities + assert "XQUIK_API_KEY" in result.config_keys + assert "HERMES_TWEET_ENABLE_ACTIONS" in result.config_keys + assert result.verification_status == "readme_parsed" diff --git a/docker/.env.example b/docker/.env.example index a72fa6f..fd2e5d0 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -4,7 +4,7 @@ REDIS_URL=redis://agentskill-redis:6379/0 CELERY_BROKER_URL= CELERY_RESULT_BACKEND= GITHUB_TOKEN= -GITHUB_SEARCH_QUERY=("claude skill" OR "agent skill") in:name,description,topics +GITHUB_SEARCH_QUERY=("claude skill" OR "agent skill" OR openclaw OR "hermes agent" OR "hermes plugin") in:name,description,topics GITHUB_SEARCH_PER_PAGE=30 GITHUB_MAX_PAGES=5 GITHUB_MAX_RESULTS=300 diff --git a/docs/operations.md b/docs/operations.md index 58caf69..1824d59 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -24,7 +24,7 @@ cd docker && docker compose up -d - Enrichment task: `tasks.skill_enrich` (interval from `ENRICH_INTERVAL_MINUTES`, enabled by `ENABLE_ENRICHMENT=true`) - Repository inspection task: `tasks.repository_inspect` (interval from `INSPECT_INTERVAL_MINUTES`, enabled by `ENABLE_REPOSITORY_INSPECTION=true`) - Immediate sync on beat start (if `SYNC_ON_START=true`) -- GitHub search query: `GITHUB_SEARCH_QUERY` (defaults to `("claude skill" OR "agent skill") in:name,description,topics`) +- GitHub search query: `GITHUB_SEARCH_QUERY` (defaults to `("claude skill" OR "agent skill" OR openclaw OR "hermes agent" OR "hermes plugin") in:name,description,topics`) - Latest discovery (new repos) search window: - `GITHUB_NEWEST_WINDOW_DAYS` (default: 7) - `GITHUB_NEWEST_MAX_PAGES` (default: 2) From bb188817ee003a4cfe9c4f6052f55e29b638f8ec Mon Sep 17 00:00:00 2001 From: kriptoburak Date: Tue, 30 Jun 2026 05:15:12 +0300 Subject: [PATCH 2/2] Preserve plugin manifest detection --- .../services/repository_inspection_service.py | 2 ++ .../test_repository_inspection_service.py | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/backend/app/services/repository_inspection_service.py b/backend/app/services/repository_inspection_service.py index 287478f..a80ca50 100644 --- a/backend/app/services/repository_inspection_service.py +++ b/backend/app/services/repository_inspection_service.py @@ -49,6 +49,7 @@ ) KEY_FILES = { + ".claude-plugin", "README.md", "README.zh-CN.md", "README_EN.md", @@ -148,6 +149,7 @@ def _detect_skill_type(text: str, topics: list[str], source_files: list[str]) -> "hermes agent" in haystack or "hermes-agent" in haystack or "hermes plugin" in haystack + or ".claude-plugin" in source_files ): return "hermes_agent_plugin" if "mcp" in haystack or "model context protocol" in haystack: diff --git a/backend/tests/test_repository_inspection_service.py b/backend/tests/test_repository_inspection_service.py index 6eaa8cd..e8b4cfc 100644 --- a/backend/tests/test_repository_inspection_service.py +++ b/backend/tests/test_repository_inspection_service.py @@ -110,3 +110,29 @@ def test_inspect_skill_repository_detects_hermes_agent_plugins(): assert "XQUIK_API_KEY" in result.config_keys assert "HERMES_TWEET_ENABLE_ACTIONS" in result.config_keys assert result.verification_status == "readme_parsed" + + +def test_inspect_skill_repository_keeps_manifest_only_hermes_marker(): + skill = Skill( + repo_id=3, + name="social-actions", + full_name="example/social-actions", + description="Social automation plugin", + html_url="https://github.com/example/social-actions", + stars=5, + forks=0, + language="Python", + topics="social", + topics_json=["social"], + ) + + result = inspect_skill_repository( + skill, + Settings(), + client=FakeClient("", [".claude-plugin", "pyproject.toml"]), + ) + + assert result is not None + assert result.source_files == [".claude-plugin", "pyproject.toml"] + assert result.skill_type == "hermes_agent_plugin" + assert result.verification_status == "root_files_parsed"