fix(packaging): ship bundled skills in wheel

Salvages #23738 by @LeonSGP43. Wheel installs were missing skills/ and
optional-skills/ because pyproject's [tool.setuptools.packages.find]
only includes Python packages — the skills directories don't have
__init__.py so they were silently dropped from the wheel.

Adds setup.py with data_files spec emitting skills/* and optional-skills/*
under hermes_agent-<v>.data/data/, and a get_bundled_skills_dir() helper
in hermes_constants that discovers the wheel-installed location via
sysconfig before falling back to a source-checkout path. tools/skills_sync
uses the helper so 'hermes update' works for pip-installed users.
This commit is contained in:
LeonSGP43
2026-05-18 20:52:29 -07:00
committed by Teknium
parent 5fdcfd851f
commit 3a7ed7be08
3 changed files with 73 additions and 6 deletions

View File

@@ -5,6 +5,7 @@ without risk of circular imports.
"""
import os
import sysconfig
from contextvars import ContextVar, Token
from pathlib import Path
@@ -139,6 +140,23 @@ def get_default_hermes_root() -> Path:
return env_path
def _get_packaged_data_dir(name: str) -> Path | None:
"""Return an installed data-files directory if one exists.
Used to discover bundled skills/optional-skills when Hermes is installed
from a wheel that emitted them via setuptools data_files.
"""
candidates = []
for scheme in ("data", "purelib", "platlib"):
raw = sysconfig.get_path(scheme)
if raw:
candidates.append(Path(raw) / name)
for candidate in candidates:
if candidate.exists():
return candidate
return None
def get_optional_skills_dir(default: Path | None = None) -> Path:
"""Return the optional-skills directory, honoring package-manager wrappers.
@@ -148,11 +166,34 @@ def get_optional_skills_dir(default: Path | None = None) -> Path:
override = os.getenv("HERMES_OPTIONAL_SKILLS", "").strip()
if override:
return Path(override)
packaged = _get_packaged_data_dir("optional-skills")
if packaged is not None:
return packaged
if default is not None:
return default
return get_hermes_home() / "optional-skills"
def get_bundled_skills_dir(default: Path | None = None) -> Path:
"""Return the bundled skills directory for source and packaged installs.
Resolution order:
1. ``HERMES_BUNDLED_SKILLS`` env var (Nix wrapper / explicit override)
2. Wheel-installed ``<sysconfig data>/skills`` (pip install path)
3. Caller-supplied ``default`` (typically the source-checkout path)
4. ``<HERMES_HOME>/skills`` last-resort
"""
override = os.getenv("HERMES_BUNDLED_SKILLS", "").strip()
if override:
return Path(override)
packaged = _get_packaged_data_dir("skills")
if packaged is not None:
return packaged
if default is not None:
return default
return get_hermes_home() / "skills"
def get_hermes_dir(new_subpath: str, old_name: str) -> Path:
"""Resolve a Hermes subdirectory with backward compatibility.