Hermes-agent

This commit is contained in:
Zakaria
2026-06-14 14:30:48 -04:00
commit dac4b88b94
5058 changed files with 1884848 additions and 0 deletions
View File
+116
View File
@@ -0,0 +1,116 @@
"""Tests for website/scripts/extract-skills.py helpers.
Covers the two behavioral contracts added when the Skills Hub page gained
per-skill source links and a cleaned-up category sidebar:
1. ``_source_url`` — every community skill must resolve to a clickable
origin URL (explicit ``extra`` URL preferred, else synthesized from the
identifier shape). Built-in/optional skills intentionally return ""
they have a generated docs page (docsPath) instead.
2. ``_guess_category`` — tags only map to a curated category bucket;
unknown tags fall to ``uncategorized`` (folded into "Other" later) so the
sidebar doesn't fill with one-off junk like version strings or brand
names.
"""
from __future__ import annotations
import importlib.util
from pathlib import Path
import pytest
REPO_ROOT = Path(__file__).resolve().parents[2]
EXTRACT = REPO_ROOT / "website" / "scripts" / "extract-skills.py"
@pytest.fixture(scope="module")
def mod():
spec = importlib.util.spec_from_file_location("extract_skills", EXTRACT)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
# --------------------------------------------------------------------------
# _source_url
# --------------------------------------------------------------------------
def test_source_url_prefers_explicit_detail_url(mod):
extra = {"detail_url": "https://skills.sh/owner/repo/skill"}
assert (
mod._source_url("skills.sh", "skills-sh/owner/repo/skill", extra)
== "https://skills.sh/owner/repo/skill"
)
def test_source_url_prefers_browse_sh_source_url(mod):
# browse.sh adapter carries its origin under extra["source_url"].
extra = {"source_url": "https://airbnb.com/host"}
assert (
mod._source_url("browse-sh", "browse-sh/airbnb.com/login-abc", extra)
== "https://airbnb.com/host"
)
def test_source_url_synthesizes_github_tree_url(mod):
url = mod._source_url("github", "anthropics/skills/skills/algorithmic-art", {})
assert url == "https://github.com/anthropics/skills/tree/main/skills/algorithmic-art"
def test_source_url_synthesizes_github_root_when_no_subpath(mod):
assert mod._source_url("github", "owner/repo", {}) == "https://github.com/owner/repo"
def test_source_url_synthesizes_clawhub(mod):
assert mod._source_url("clawhub", "go-music-skill", {}) == "https://clawhub.ai/skills/go-music-skill"
def test_source_url_synthesizes_clawhub_strips_prefix(mod):
# identifier may arrive already prefixed; we must not double-prefix.
assert (
mod._source_url("clawhub", "clawhub/go-music-skill", {})
== "https://clawhub.ai/skills/go-music-skill"
)
def test_source_url_synthesizes_lobehub(mod):
assert mod._source_url("lobehub", "lobehub/chinese-paper", {}) == "https://lobehub.com/agent/chinese-paper"
def test_source_url_empty_for_unknown_source_without_identifier(mod):
assert mod._source_url("mystery", "", {}) == ""
# --------------------------------------------------------------------------
# _guess_category
# --------------------------------------------------------------------------
def test_guess_category_maps_known_tag(mod):
assert mod._guess_category(["security"]) == "security"
assert mod._guess_category(["machine-learning"]) == "mlops"
assert mod._guess_category(["crypto"]) == "blockchain"
def test_guess_category_accepts_literal_curated_key(mod):
# A skill tagged literally with a curated category key should route there.
assert mod._guess_category(["devops"]) == "devops"
def test_guess_category_rejects_junk_tag(mod):
# This is the whole point: version strings / brand names must NOT become
# their own sidebar category. They land in "uncategorized" → "Other".
assert mod._guess_category(["0.10.7 Dev"]) == "uncategorized"
assert mod._guess_category(["Doramagic Crystal"]) == "uncategorized"
assert mod._guess_category(["Ap2"]) == "uncategorized"
def test_guess_category_empty_tags(mod):
assert mod._guess_category([]) == "uncategorized"
def test_guess_category_skips_first_junk_tag_for_later_known_tag(mod):
# First tag is junk, second is curated — we should still find the curated one.
assert mod._guess_category(["Some Brand", "security"]) == "security"
+116
View File
@@ -0,0 +1,116 @@
"""Tests for website/scripts/generate-skill-docs.py.
The generator turns every `skills/**/SKILL.md` into a Docusaurus page before
the `docs-site-checks` CI workflow runs `ascii-guard lint` on the result. If
a SKILL.md contains ASCII diagrams (box-drawing chars in a fenced code block)
without its own `<!-- ascii-guard-ignore -->` markers, the generator must
add them defensively — otherwise every PR touching `website/**` fails lint
on unrelated skill content.
Regression for issue #15305.
"""
from __future__ import annotations
import importlib.util
from pathlib import Path
import pytest
REPO_ROOT = Path(__file__).resolve().parents[2]
GENERATOR = REPO_ROOT / "website" / "scripts" / "generate-skill-docs.py"
@pytest.fixture(scope="module")
def gen_module():
"""Load generate-skill-docs.py as a module (hyphenated filename, not importable via normal import)."""
spec = importlib.util.spec_from_file_location("generate_skill_docs", GENERATOR)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def test_code_block_without_box_chars_is_not_wrapped(gen_module):
"""Plain bash/python code blocks should stay uncluttered."""
body = "Intro.\n\n```bash\npip install foo\nfoo --run\n```\n\nOutro."
result = gen_module.mdx_escape_body(body)
assert "ascii-guard-ignore" not in result
assert "pip install foo" in result
def test_code_block_with_box_chars_gets_wrapped(gen_module):
"""A code fence containing Unicode box-drawing chars must be wrapped in
ascii-guard-ignore comments so the docs-site-checks lint can't fail on
a skill's own diagram (issue #15305)."""
body = (
"Some text.\n\n"
"```\n"
"┌─────────┐\n"
"│ diagram │\n"
"└─────────┘\n"
"```\n\n"
"More text."
)
result = gen_module.mdx_escape_body(body)
assert "<!-- ascii-guard-ignore -->" in result
assert "<!-- ascii-guard-ignore-end -->" in result
# The wrapper must sit OUTSIDE the fence, not inside.
wrap_open = result.index("<!-- ascii-guard-ignore -->")
fence_open = result.index("```\n")
assert wrap_open < fence_open
def test_multiple_code_blocks_only_box_ones_wrapped(gen_module):
"""Mixed body: plain code stays plain, box code gets wrapped."""
body = (
"```bash\necho hi\n```\n\n"
"```\n┌──┐\n│ │\n└──┘\n```\n\n"
"```python\nprint('ok')\n```"
)
result = gen_module.mdx_escape_body(body)
# exactly one wrap pair
assert result.count("<!-- ascii-guard-ignore -->") == 1
assert result.count("<!-- ascii-guard-ignore-end -->") == 1
# plain blocks untouched
assert "echo hi" in result
assert "print('ok')" in result
def test_tilde_fenced_box_is_wrapped(gen_module):
"""The generator supports both ``` and ~~~ fences — both must be covered."""
body = "~~~\n│ box │\n~~~"
result = gen_module.mdx_escape_body(body)
assert "<!-- ascii-guard-ignore -->" in result
def test_already_wrapped_source_double_wraps_harmlessly(gen_module):
"""If the SKILL.md already has ascii-guard-ignore markers, the generator's
extra wrap is harmless (ascii-guard tolerates adjacent duplicate markers).
The test just verifies we don't crash and the content survives."""
body = (
"<!-- ascii-guard-ignore -->\n"
"```\n┌─┐\n└─┘\n```\n"
"<!-- ascii-guard-ignore-end -->"
)
result = gen_module.mdx_escape_body(body)
assert "┌─┐" in result
# At least one marker pair survives
assert "<!-- ascii-guard-ignore -->" in result
assert "<!-- ascii-guard-ignore-end -->" in result
def test_box_drawing_detection_covers_common_chars(gen_module):
"""Smoke-test that the char set covers box-drawing ranges actually used
in skill diagrams."""
# Sample from real SKILL.md diagrams (segment-anything, research-paper-writing, etc.)
for ch in "┌┐└┘─│├┤┬┴┼═║╔╗╚╝╭╮╯╰▶◀▲▼":
assert ch in gen_module._BOX_DRAWING_CHARS, f"missing: {ch!r}"
def test_bundled_catalog_explains_missing_local_skills(gen_module):
"""The bundled catalog should explain how to restore a listed skill that
was removed from the local profile's skills tree."""
result = gen_module.build_catalog_md_bundled([])
assert "respects local deletions and user edits" in result
assert "hermes skills reset <name> --restore" in result