Skip to content
Open
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
2 changes: 1 addition & 1 deletion backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion backend/app/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 11 additions & 1 deletion backend/app/services/repository_inspection_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")),
Expand All @@ -40,13 +41,15 @@
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",)),
("OpenAI Agents", ("openai agents", "openai agent")),
)

KEY_FILES = {
".claude-plugin",
"README.md",
"README.zh-CN.md",
"README_EN.md",
Expand Down Expand Up @@ -142,6 +145,13 @@ 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
Comment on lines +149 to +151

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve manifest-only Hermes plugin detection

For Hermes plugins whose only repository marker is the root .claude-plugin manifest, this phrase-only check never matches: source_files is built after filtering root files through KEY_FILES, which does not include .claude-plugin, so the manifest shown in the new test is dropped before detection. Those repos fall through to another type (often claude_skill) unless their README/topics also contain the exact Hermes phrases, so include/preserve the manifest in the inspection path and use it for classification.

Useful? React with 👍 / 👎.

or ".claude-plugin" in source_files
):
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:
Expand Down Expand Up @@ -188,7 +198,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:
Expand Down
63 changes: 63 additions & 0 deletions backend/tests/test_repository_inspection_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,66 @@ 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"


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"
2 changes: 1 addition & 1 deletion docker/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down