Skip to content

Index

sources

Skill source resolvers — Hermes, OpenClaw, generic GitHub.

Classes

ResolvedSkill dataclass

ResolvedSkill(name: str, source: str, path: Path, category: str, description: str, commit: str, sidecar_data: Dict[str, Any] = dict())

A skill found in an upstream source, ready to import.

Lightweight — does not include the full SKILL.md body. The importer reads the file from path when actually installing.

SourceResolver

Bases: ABC

Abstract base for a skill source resolver.

Implementations clone or pull an upstream repo into a cache directory, walk the cache to find SKILL.md files, and return ResolvedSkill objects that the importer can install.

Functions
cache_dir abstractmethod
cache_dir() -> Path

Where this source clones its repo.

Source code in src/openjarvis/skills/sources/base.py
@abstractmethod
def cache_dir(self) -> Path:
    """Where this source clones its repo."""
sync abstractmethod
sync() -> None

Clone or pull the upstream repo into the cache.

Source code in src/openjarvis/skills/sources/base.py
@abstractmethod
def sync(self) -> None:
    """Clone or pull the upstream repo into the cache."""
list_skills abstractmethod
list_skills() -> List[ResolvedSkill]

Walk the cache directory and return all discoverable skills.

Source code in src/openjarvis/skills/sources/base.py
@abstractmethod
def list_skills(self) -> List[ResolvedSkill]:
    """Walk the cache directory and return all discoverable skills."""
resolve
resolve(query: str) -> List[ResolvedSkill]

Filter list_skills() by name (substring match).

Empty query returns all skills.

Source code in src/openjarvis/skills/sources/base.py
def resolve(self, query: str) -> List[ResolvedSkill]:
    """Filter list_skills() by name (substring match).

    Empty *query* returns all skills.
    """
    all_skills = self.list_skills()
    if not query:
        return all_skills
    q = query.lower()
    return [s for s in all_skills if q in s.name.lower()]
filter_by_category
filter_by_category(category: str) -> List[ResolvedSkill]

Return skills whose category exactly matches category.

Source code in src/openjarvis/skills/sources/base.py
def filter_by_category(self, category: str) -> List[ResolvedSkill]:
    """Return skills whose category exactly matches *category*."""
    return [s for s in self.list_skills() if s.category == category]

GitHubResolver

GitHubResolver(cache_root: Path, repo_url: str)

Bases: SourceResolver

Generic resolver for any GitHub repo containing SKILL.md files.

Source code in src/openjarvis/skills/sources/github.py
def __init__(self, cache_root: Path, repo_url: str) -> None:
    self._cache_root = Path(cache_root)
    self._repo_url = repo_url

HermesResolver

HermesResolver(cache_root: Path | None = None)

Bases: SourceResolver

Resolves skills from the Hermes Agent repository.

Source code in src/openjarvis/skills/sources/hermes.py
def __init__(self, cache_root: Path | None = None) -> None:
    if cache_root is None:
        cache_root = Path("~/.openjarvis/skill-cache/hermes/").expanduser()
    self._cache_root = Path(cache_root)
Functions
sync
sync() -> None

Clone or pull the Hermes repo into the cache directory.

Source code in src/openjarvis/skills/sources/hermes.py
def sync(self) -> None:
    """Clone or pull the Hermes repo into the cache directory."""
    if self._cache_root.exists() and (self._cache_root / ".git").exists():
        subprocess.run(
            ["git", "-C", str(self._cache_root), "pull", "--ff-only"],
            check=True,
        )
    else:
        self._cache_root.parent.mkdir(parents=True, exist_ok=True)
        subprocess.run(
            ["git", "clone", HERMES_REPO_URL, str(self._cache_root)],
            check=True,
        )
list_skills
list_skills() -> List[ResolvedSkill]

Walk skills///SKILL.md.

Source code in src/openjarvis/skills/sources/hermes.py
def list_skills(self) -> List[ResolvedSkill]:
    """Walk skills/<category>/<skill>/SKILL.md."""
    skills_root = self._cache_root / "skills"
    if not skills_root.exists():
        return []

    results: List[ResolvedSkill] = []
    commit = self._read_commit()

    for category_dir in sorted(skills_root.iterdir()):
        if not category_dir.is_dir():
            continue
        category = category_dir.name
        for skill_dir in sorted(category_dir.iterdir()):
            if not skill_dir.is_dir():
                continue  # skip DESCRIPTION.md and other files
            skill_md = skill_dir / "SKILL.md"
            if not skill_md.exists():
                continue

            # Read minimal frontmatter for the preview
            name, description = self._read_preview(
                skill_md, default_name=skill_dir.name
            )
            results.append(
                ResolvedSkill(
                    name=name,
                    source=self.name,
                    path=skill_dir,
                    category=category,
                    description=description,
                    commit=commit,
                )
            )

    return results

OpenClawResolver

OpenClawResolver(cache_root: Path | None = None)

Bases: SourceResolver

Resolves skills from the OpenClaw skill index.

Source code in src/openjarvis/skills/sources/openclaw.py
def __init__(self, cache_root: Path | None = None) -> None:
    if cache_root is None:
        cache_root = Path("~/.openjarvis/skill-cache/openclaw/").expanduser()
    self._cache_root = Path(cache_root)