From efe73ed333b4eb3a3f0f50975eee0c12360164cc Mon Sep 17 00:00:00 2001 From: "claude-bot-maiixu[bot]" <275422726+claude-bot-maiixu[bot]@users.noreply.github.com> Date: Sun, 12 Apr 2026 12:02:23 -0700 Subject: [PATCH] feat(skills): add skills.create_dir config to separate creation from sync By default both skills_sync (upstream bundled) and skill_manager (agent- created) write to the same ~/.hermes/skills/. This makes it impossible to keep upstream bundled skills and agent/user-created skills in separate directories without inspecting .bundled_manifest. Add skills.create_dir config key (default: empty = ~/.hermes/skills/). When set, new skills created via skill_manage() go there instead. This lets users configure: skills: create_dir: ~/personal-intelligence/hermes/skills # agent-created here external_dirs: - ~/personal-intelligence/hermes/bundled-skills # upstream bundled here Also update get_all_skills_dirs() to include create_dir in the read path so created skills are discoverable alongside bundled and external ones. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- agent/skill_utils.py | 19 +++++++++++++++++-- hermes_cli/config.py | 3 ++- tools/skill_manager_tool.py | 23 +++++++++++++++++++++-- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/agent/skill_utils.py b/agent/skill_utils.py index 97ba92b735a8..bb5c46957e87 100644 --- a/agent/skill_utils.py +++ b/agent/skill_utils.py @@ -228,9 +228,24 @@ def get_all_skills_dirs() -> List[Path]: """Return all skill directories: local ``~/.hermes/skills/`` first, then external. The local dir is always first (and always included even if it doesn't exist - yet — callers handle that). External dirs follow in config order. + yet — callers handle that). If ``skills.create_dir`` is configured and + differs from the default, it is inserted second so created skills are found. + External dirs follow in config order. """ - dirs = [get_skills_dir()] + default = get_skills_dir() + dirs: List[Path] = [default] + + # Include create_dir in the read path if it differs from the default + try: + from hermes_cli.config import load_config + raw = load_config().get("skills", {}).get("create_dir", "") + if raw: + create_dir = Path(raw).expanduser().resolve() + if create_dir != default.resolve() and create_dir not in dirs: + dirs.insert(1, create_dir) + except Exception: + pass + dirs.extend(get_external_skills_dirs()) return dirs diff --git a/hermes_cli/config.py b/hermes_cli/config.py index f4064ad5e741..5548e5c9fbd3 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -560,9 +560,10 @@ def _ensure_hermes_home_managed(home: Path): # Skills — external skill directories for sharing skills across tools/agents. # Each path is expanded (~, ${VAR}) and resolved. Read-only — skill creation - # always goes to ~/.hermes/skills/. + # goes to ``skills.create_dir`` (default: ``~/.hermes/skills/``). "skills": { "external_dirs": [], # e.g. ["~/.agents/skills", "/shared/team-skills"] + "create_dir": "", # where new skills are written; empty = ~/.hermes/skills/ }, # Honcho AI-native memory -- reads ~/.honcho/config.json as single source of truth. diff --git a/tools/skill_manager_tool.py b/tools/skill_manager_tool.py index 2b2625fa0d4c..25a547e1bc4a 100644 --- a/tools/skill_manager_tool.py +++ b/tools/skill_manager_tool.py @@ -76,9 +76,28 @@ def _security_scan_skill(skill_dir: Path) -> Optional[str]: import yaml -# All skills live in ~/.hermes/skills/ (single source of truth) +# Default write location for new skills. Override via skills.create_dir in config.yaml. HERMES_HOME = get_hermes_home() -SKILLS_DIR = HERMES_HOME / "skills" +_DEFAULT_SKILLS_DIR = HERMES_HOME / "skills" + + +def _get_skills_create_dir() -> Path: + """Return the directory where new skills are written. + + Reads ``skills.create_dir`` from config.yaml. Falls back to the default + ``~/.hermes/skills/`` when unset, so existing setups are unaffected. + """ + try: + from hermes_cli.config import load_config + raw = load_config().get("skills", {}).get("create_dir", "") + if raw: + return Path(raw).expanduser().resolve() + except Exception: + pass + return _DEFAULT_SKILLS_DIR + + +SKILLS_DIR = _get_skills_create_dir() MAX_NAME_LENGTH = 64 MAX_DESCRIPTION_LENGTH = 1024