2026-01-30 07:39:55 +00:00
#!/usr/bin/env python3
"""
Skills Tool Module
This module provides tools for listing and viewing skill documents .
Skills are organized as directories containing a SKILL . md file ( the main instructions )
and optional supporting files like references , templates , and examples .
Inspired by Anthropic ' s Claude Skills system with progressive disclosure architecture:
- Metadata ( name ≤ 64 chars , description ≤ 1024 chars ) - shown in skills_list
- Full Instructions - loaded via skill_view when needed
- Linked Files ( references , templates ) - loaded on demand
Directory Structure :
skills /
├ ─ ─ my - skill /
│ ├ ─ ─ SKILL . md # Main instructions (required)
│ ├ ─ ─ references / # Supporting documentation
│ │ ├ ─ ─ api . md
│ │ └ ─ ─ examples . md
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
│ ├ ─ ─ templates / # Templates for output
│ │ └ ─ ─ template . md
│ └ ─ ─ assets / # Supplementary files (agentskills.io standard)
2026-01-30 07:39:55 +00:00
└ ─ ─ category / # Category folder for organization
└ ─ ─ another - skill /
└ ─ ─ SKILL . md
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
SKILL . md Format ( YAML Frontmatter , agentskills . io compatible ) :
2026-01-30 07:39:55 +00:00
- - -
name : skill - name # Required, max 64 chars
description : Brief description # Required, max 1024 chars
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
version : 1.0 .0 # Optional
license : MIT # Optional (agentskills.io)
2026-03-07 00:47:54 -08:00
platforms : [ macos ] # Optional — restrict to specific OS platforms
# Valid: macos, linux, windows
# Omit to load on all platforms (default)
2026-03-13 03:14:04 -07:00
prerequisites : # Optional — legacy runtime requirements
env_vars : [ API_KEY ] # Legacy env var names are normalized into
# required_environment_variables on load.
commands : [ curl , jq ] # Command checks remain advisory only.
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
compatibility : Requires X # Optional (agentskills.io)
metadata : # Optional, arbitrary key-value (agentskills.io)
hermes :
tags : [ fine - tuning , llm ]
related_skills : [ peft , lora ]
2026-01-30 07:39:55 +00:00
- - -
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
# Skill Title
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
Full instructions and content here . . .
Available tools :
- skills_list : List skills with metadata ( progressive disclosure tier 1 )
- skill_view : Load full skill content ( progressive disclosure tier 2 - 3 )
Usage :
from tools . skills_tool import skills_list , skill_view , check_skills_requirements
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
# List all skills (returns metadata only - token efficient)
result = skills_list ( )
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
# View a skill's main content (loads full instructions)
content = skill_view ( " axolotl " )
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
# View a reference file within a skill (loads linked file)
content = skill_view ( " axolotl " , " references/dataset-formats.md " )
"""
import json
2026-03-08 00:30:49 +03:00
import logging
refactor: consolidate get_hermes_home() and parse_reasoning_effort() (#3062)
Centralizes two widely-duplicated patterns into hermes_constants.py:
1. get_hermes_home() — Path resolution for ~/.hermes (HERMES_HOME env var)
- Was copy-pasted inline across 30+ files as:
Path(os.getenv("HERMES_HOME", Path.home() / ".hermes"))
- Now defined once in hermes_constants.py (zero-dependency module)
- hermes_cli/config.py re-exports it for backward compatibility
- Removed local wrapper functions in honcho_integration/client.py,
tools/website_policy.py, tools/tirith_security.py, hermes_cli/uninstall.py
2. parse_reasoning_effort() — Reasoning effort string validation
- Was copy-pasted in cli.py, gateway/run.py, cron/scheduler.py
- Same validation logic: check against (xhigh, high, medium, low, minimal, none)
- Now defined once in hermes_constants.py, called from all 3 locations
- Warning log for unknown values kept at call sites (context-specific)
31 files changed, net +31 lines (125 insertions, 94 deletions)
Full test suite: 6179 passed, 0 failed
2026-03-25 15:54:28 -07:00
from hermes_constants import get_hermes_home
2026-01-30 07:39:55 +00:00
import os
import re
2026-03-07 00:47:54 -08:00
import sys
2026-03-13 03:14:04 -07:00
from enum import Enum
2026-01-30 07:39:55 +00:00
from pathlib import Path
2026-03-11 03:06:15 -07:00
from typing import Dict , Any , List , Optional , Set , Tuple
2026-01-30 07:39:55 +00:00
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
import yaml
2026-03-13 03:14:04 -07:00
from hermes_cli . config import load_env , _ENV_VAR_NAME_RE
from tools . registry import registry
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
2026-03-31 00:37:14 +00:00
# Import skill security utilities for path traversal protection (V-011)
try :
from agent . skill_security import (
validate_skill_name ,
SkillSecurityError ,
PathTraversalError ,
)
_SECURITY_VALIDATION_AVAILABLE = True
except ImportError :
_SECURITY_VALIDATION_AVAILABLE = False
# Fallback validation if import fails
def validate_skill_name ( name : str , allow_path_separator : bool = False ) - > None :
if not name or not isinstance ( name , str ) :
raise ValueError ( " Skill name must be a non-empty string " )
if " .. " in name :
raise ValueError ( " Path traversal ( ' .. ' ) is not allowed in skill names " )
if name . startswith ( " / " ) or name . startswith ( " ~ " ) :
raise ValueError ( " Absolute paths are not allowed in skill names " )
class SkillSecurityError ( Exception ) :
pass
class PathTraversalError ( SkillSecurityError ) :
pass
2026-03-08 00:30:49 +03:00
logger = logging . getLogger ( __name__ )
2026-01-30 07:39:55 +00:00
2026-02-19 18:25:53 -08:00
# All skills live in ~/.hermes/skills/ (seeded from bundled skills/ on install).
# This is the single source of truth -- agent edits, hub installs, and bundled
# skills all coexist here without polluting the git repo.
refactor: consolidate get_hermes_home() and parse_reasoning_effort() (#3062)
Centralizes two widely-duplicated patterns into hermes_constants.py:
1. get_hermes_home() — Path resolution for ~/.hermes (HERMES_HOME env var)
- Was copy-pasted inline across 30+ files as:
Path(os.getenv("HERMES_HOME", Path.home() / ".hermes"))
- Now defined once in hermes_constants.py (zero-dependency module)
- hermes_cli/config.py re-exports it for backward compatibility
- Removed local wrapper functions in honcho_integration/client.py,
tools/website_policy.py, tools/tirith_security.py, hermes_cli/uninstall.py
2. parse_reasoning_effort() — Reasoning effort string validation
- Was copy-pasted in cli.py, gateway/run.py, cron/scheduler.py
- Same validation logic: check against (xhigh, high, medium, low, minimal, none)
- Now defined once in hermes_constants.py, called from all 3 locations
- Warning log for unknown values kept at call sites (context-specific)
31 files changed, net +31 lines (125 insertions, 94 deletions)
Full test suite: 6179 passed, 0 failed
2026-03-25 15:54:28 -07:00
HERMES_HOME = get_hermes_home ( )
2026-02-19 18:25:53 -08:00
SKILLS_DIR = HERMES_HOME / " skills "
2026-01-30 07:39:55 +00:00
# Anthropic-recommended limits for progressive disclosure efficiency
MAX_NAME_LENGTH = 64
MAX_DESCRIPTION_LENGTH = 1024
2026-03-07 00:47:54 -08:00
# Platform identifiers for the 'platforms' frontmatter field.
# Maps user-friendly names to sys.platform prefixes.
_PLATFORM_MAP = {
" macos " : " darwin " ,
" linux " : " linux " ,
" windows " : " win32 " ,
}
2026-03-13 03:14:04 -07:00
_EXCLUDED_SKILL_DIRS = frozenset ( ( " .git " , " .github " , " .hub " ) )
_REMOTE_ENV_BACKENDS = frozenset ( { " docker " , " singularity " , " modal " , " ssh " , " daytona " } )
_secret_capture_callback = None
class SkillReadinessStatus ( str , Enum ) :
AVAILABLE = " available "
SETUP_NEEDED = " setup_needed "
UNSUPPORTED = " unsupported "
def set_secret_capture_callback ( callback ) - > None :
global _secret_capture_callback
_secret_capture_callback = callback
2026-03-07 00:47:54 -08:00
def skill_matches_platform ( frontmatter : Dict [ str , Any ] ) - > bool :
""" Check if a skill is compatible with the current OS platform.
2026-03-27 10:54:02 -07:00
Delegates to ` ` agent . skill_utils . skill_matches_platform ` ` — kept here
as a public re - export so existing callers don ' t need updating.
2026-03-07 00:47:54 -08:00
"""
2026-03-27 10:54:02 -07:00
from agent . skill_utils import skill_matches_platform as _impl
return _impl ( frontmatter )
2026-03-07 00:47:54 -08:00
2026-01-30 07:39:55 +00:00
2026-03-13 03:14:04 -07:00
def _normalize_prerequisite_values ( value : Any ) - > List [ str ] :
if not value :
return [ ]
if isinstance ( value , str ) :
value = [ value ]
return [ str ( item ) for item in value if str ( item ) . strip ( ) ]
def _collect_prerequisite_values (
frontmatter : Dict [ str , Any ] ,
) - > Tuple [ List [ str ] , List [ str ] ] :
prereqs = frontmatter . get ( " prerequisites " )
if not prereqs or not isinstance ( prereqs , dict ) :
return [ ] , [ ]
return (
_normalize_prerequisite_values ( prereqs . get ( " env_vars " ) ) ,
_normalize_prerequisite_values ( prereqs . get ( " commands " ) ) ,
)
def _normalize_setup_metadata ( frontmatter : Dict [ str , Any ] ) - > Dict [ str , Any ] :
setup = frontmatter . get ( " setup " )
if not isinstance ( setup , dict ) :
return { " help " : None , " collect_secrets " : [ ] }
help_text = setup . get ( " help " )
normalized_help = (
str ( help_text ) . strip ( )
if isinstance ( help_text , str ) and help_text . strip ( )
else None
)
collect_secrets_raw = setup . get ( " collect_secrets " )
if isinstance ( collect_secrets_raw , dict ) :
collect_secrets_raw = [ collect_secrets_raw ]
if not isinstance ( collect_secrets_raw , list ) :
collect_secrets_raw = [ ]
collect_secrets : List [ Dict [ str , Any ] ] = [ ]
for item in collect_secrets_raw :
if not isinstance ( item , dict ) :
continue
env_var = str ( item . get ( " env_var " ) or " " ) . strip ( )
if not env_var :
continue
prompt = str ( item . get ( " prompt " ) or f " Enter value for { env_var } " ) . strip ( )
provider_url = str ( item . get ( " provider_url " ) or item . get ( " url " ) or " " ) . strip ( )
entry : Dict [ str , Any ] = {
" env_var " : env_var ,
" prompt " : prompt ,
" secret " : bool ( item . get ( " secret " , True ) ) ,
}
if provider_url :
entry [ " provider_url " ] = provider_url
collect_secrets . append ( entry )
return {
" help " : normalized_help ,
" collect_secrets " : collect_secrets ,
}
def _get_required_environment_variables (
frontmatter : Dict [ str , Any ] ,
legacy_env_vars : List [ str ] | None = None ,
) - > List [ Dict [ str , Any ] ] :
setup = _normalize_setup_metadata ( frontmatter )
required_raw = frontmatter . get ( " required_environment_variables " )
if isinstance ( required_raw , dict ) :
required_raw = [ required_raw ]
if not isinstance ( required_raw , list ) :
required_raw = [ ]
required : List [ Dict [ str , Any ] ] = [ ]
seen : set [ str ] = set ( )
def _append_required ( entry : Dict [ str , Any ] ) - > None :
env_name = str ( entry . get ( " name " ) or entry . get ( " env_var " ) or " " ) . strip ( )
if not env_name or env_name in seen :
return
if not _ENV_VAR_NAME_RE . match ( env_name ) :
return
normalized : Dict [ str , Any ] = {
" name " : env_name ,
" prompt " : str ( entry . get ( " prompt " ) or f " Enter value for { env_name } " ) . strip ( ) ,
}
help_text = (
entry . get ( " help " )
or entry . get ( " provider_url " )
or entry . get ( " url " )
or setup . get ( " help " )
)
if isinstance ( help_text , str ) and help_text . strip ( ) :
normalized [ " help " ] = help_text . strip ( )
required_for = entry . get ( " required_for " )
if isinstance ( required_for , str ) and required_for . strip ( ) :
normalized [ " required_for " ] = required_for . strip ( )
seen . add ( env_name )
required . append ( normalized )
for item in required_raw :
if isinstance ( item , str ) :
_append_required ( { " name " : item } )
continue
if isinstance ( item , dict ) :
_append_required ( item )
for item in setup [ " collect_secrets " ] :
_append_required (
{
" name " : item . get ( " env_var " ) ,
" prompt " : item . get ( " prompt " ) ,
" help " : item . get ( " provider_url " ) or setup . get ( " help " ) ,
}
)
if legacy_env_vars is None :
legacy_env_vars , _ = _collect_prerequisite_values ( frontmatter )
for env_var in legacy_env_vars :
_append_required ( { " name " : env_var } )
return required
def _capture_required_environment_variables (
skill_name : str ,
missing_entries : List [ Dict [ str , Any ] ] ,
) - > Dict [ str , Any ] :
if not missing_entries :
return {
" missing_names " : [ ] ,
" setup_skipped " : False ,
" gateway_setup_hint " : None ,
}
missing_names = [ entry [ " name " ] for entry in missing_entries ]
if _is_gateway_surface ( ) :
return {
" missing_names " : missing_names ,
" setup_skipped " : False ,
" gateway_setup_hint " : _gateway_setup_hint ( ) ,
}
if _secret_capture_callback is None :
return {
" missing_names " : missing_names ,
" setup_skipped " : False ,
" gateway_setup_hint " : None ,
}
setup_skipped = False
remaining_names : List [ str ] = [ ]
for entry in missing_entries :
metadata = { " skill_name " : skill_name }
if entry . get ( " help " ) :
metadata [ " help " ] = entry [ " help " ]
if entry . get ( " required_for " ) :
metadata [ " required_for " ] = entry [ " required_for " ]
try :
callback_result = _secret_capture_callback (
entry [ " name " ] ,
entry [ " prompt " ] ,
metadata ,
)
except Exception :
logger . warning (
f " Secret capture callback failed for { entry [ ' name ' ] } " , exc_info = True
)
callback_result = {
" success " : False ,
" stored_as " : entry [ " name " ] ,
" validated " : False ,
" skipped " : True ,
}
success = isinstance ( callback_result , dict ) and bool (
callback_result . get ( " success " )
)
skipped = isinstance ( callback_result , dict ) and bool (
callback_result . get ( " skipped " )
)
if success and not skipped :
continue
setup_skipped = True
remaining_names . append ( entry [ " name " ] )
return {
" missing_names " : remaining_names ,
" setup_skipped " : setup_skipped ,
" gateway_setup_hint " : None ,
}
def _is_gateway_surface ( ) - > bool :
if os . getenv ( " HERMES_GATEWAY_SESSION " ) :
return True
return bool ( os . getenv ( " HERMES_SESSION_PLATFORM " ) )
def _get_terminal_backend_name ( ) - > str :
return str ( os . getenv ( " TERMINAL_ENV " , " local " ) ) . strip ( ) . lower ( ) or " local "
def _is_env_var_persisted (
var_name : str , env_snapshot : Dict [ str , str ] | None = None
) - > bool :
if env_snapshot is None :
env_snapshot = load_env ( )
if var_name in env_snapshot :
return bool ( env_snapshot . get ( var_name ) )
return bool ( os . getenv ( var_name ) )
def _remaining_required_environment_names (
required_env_vars : List [ Dict [ str , Any ] ] ,
capture_result : Dict [ str , Any ] ,
* ,
env_snapshot : Dict [ str , str ] | None = None ,
) - > List [ str ] :
missing_names = set ( capture_result [ " missing_names " ] )
if env_snapshot is None :
env_snapshot = load_env ( )
remaining = [ ]
for entry in required_env_vars :
name = entry [ " name " ]
if name in missing_names or not _is_env_var_persisted ( name , env_snapshot ) :
remaining . append ( name )
return remaining
def _gateway_setup_hint ( ) - > str :
try :
from gateway . platforms . base import GATEWAY_SECRET_CAPTURE_UNSUPPORTED_MESSAGE
return GATEWAY_SECRET_CAPTURE_UNSUPPORTED_MESSAGE
except Exception :
2026-03-13 04:10:15 -07:00
return " Secure secret entry is not available. Load this skill in the local CLI to be prompted, or add the key to ~/.hermes/.env manually. "
2026-03-13 03:14:04 -07:00
def _build_setup_note (
readiness_status : SkillReadinessStatus ,
missing : List [ str ] ,
setup_help : str | None = None ,
) - > str | None :
if readiness_status == SkillReadinessStatus . SETUP_NEEDED :
missing_str = " , " . join ( missing ) if missing else " required prerequisites "
note = f " Setup needed before using this skill: missing { missing_str } . "
if setup_help :
return f " { note } { setup_help } "
return note
return None
2026-01-30 07:39:55 +00:00
def check_skills_requirements ( ) - > bool :
2026-02-19 18:25:53 -08:00
""" Skills are always available -- the directory is created on first use if needed. """
return True
2026-01-30 07:39:55 +00:00
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
def _parse_frontmatter ( content : str ) - > Tuple [ Dict [ str , Any ] , str ] :
2026-03-27 10:54:02 -07:00
""" Parse YAML frontmatter from markdown content.
2026-03-13 03:14:04 -07:00
2026-03-27 10:54:02 -07:00
Delegates to ` ` agent . skill_utils . parse_frontmatter ` ` — kept here
as a public re - export so existing callers don ' t need updating.
2026-01-30 07:39:55 +00:00
"""
2026-03-27 10:54:02 -07:00
from agent . skill_utils import parse_frontmatter
return parse_frontmatter ( content )
2026-01-30 07:39:55 +00:00
def _get_category_from_path ( skill_path : Path ) - > Optional [ str ] :
"""
Extract category from skill path based on directory structure .
2026-03-13 03:14:04 -07:00
2026-02-19 18:25:53 -08:00
For paths like : ~ / . hermes / skills / mlops / axolotl / SKILL . md - > " mlops "
2026-01-30 07:39:55 +00:00
"""
try :
rel_path = skill_path . relative_to ( SKILLS_DIR )
parts = rel_path . parts
if len ( parts ) > = 3 :
return parts [ 0 ]
return None
except ValueError :
return None
def _estimate_tokens ( content : str ) - > int :
"""
Rough token estimate ( 4 chars per token average ) .
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
Args :
content : Text content
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
Returns :
Estimated token count
"""
return len ( content ) / / 4
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
def _parse_tags ( tags_value ) - > List [ str ] :
2026-01-30 07:39:55 +00:00
"""
Parse tags from frontmatter value .
2026-03-13 03:14:04 -07:00
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
Handles :
- Already - parsed list ( from yaml . safe_load ) : [ tag1 , tag2 ]
- String with brackets : " [tag1, tag2] "
- Comma - separated string : " tag1, tag2 "
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
Args :
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
tags_value : Raw tags value — may be a list or string
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
Returns :
List of tag strings
"""
if not tags_value :
return [ ]
2026-03-13 03:14:04 -07:00
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
# yaml.safe_load already returns a list for [tag1, tag2]
if isinstance ( tags_value , list ) :
return [ str ( t ) . strip ( ) for t in tags_value if t ]
2026-03-13 03:14:04 -07:00
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
# String fallback — handle bracket-wrapped or comma-separated
tags_value = str ( tags_value ) . strip ( )
2026-03-13 03:14:04 -07:00
if tags_value . startswith ( " [ " ) and tags_value . endswith ( " ] " ) :
2026-01-30 07:39:55 +00:00
tags_value = tags_value [ 1 : - 1 ]
2026-03-13 03:14:04 -07:00
return [ t . strip ( ) . strip ( " \" ' " ) for t in tags_value . split ( " , " ) if t . strip ( ) ]
2026-01-30 07:39:55 +00:00
2026-03-09 07:02:06 +03:00
2026-03-11 03:06:15 -07:00
def _get_disabled_skill_names ( ) - > Set [ str ] :
2026-03-27 10:54:02 -07:00
""" Load disabled skill names from config.
2026-03-09 07:02:06 +03:00
2026-03-27 10:54:02 -07:00
Delegates to ` ` agent . skill_utils . get_disabled_skill_names ` ` — kept here
as a public re - export so existing callers don ' t need updating.
2026-03-09 07:02:06 +03:00
"""
2026-03-27 10:54:02 -07:00
from agent . skill_utils import get_disabled_skill_names
return get_disabled_skill_names ( )
2026-03-11 03:06:15 -07:00
def _is_skill_disabled ( name : str , platform : str = None ) - > bool :
""" Check if a skill is disabled in config. """
import os
try :
from hermes_cli . config import load_config
config = load_config ( )
skills_cfg = config . get ( " skills " , { } )
2026-03-09 07:02:06 +03:00
resolved_platform = platform or os . getenv ( " HERMES_PLATFORM " )
if resolved_platform :
platform_disabled = skills_cfg . get ( " platform_disabled " , { } ) . get ( resolved_platform )
if platform_disabled is not None :
return name in platform_disabled
return name in skills_cfg . get ( " disabled " , [ ] )
except Exception :
return False
2026-03-11 03:06:15 -07:00
def _find_all_skills ( * , skip_disabled : bool = False ) - > List [ Dict [ str , Any ] ] :
2026-03-29 00:33:30 -07:00
""" Recursively find all skills in ~/.hermes/skills/ and external dirs.
2026-03-11 03:06:15 -07:00
Args :
skip_disabled : If True , return ALL skills regardless of disabled
state ( used by ` ` hermes skills ` ` config UI ) . Default False
filters out disabled skills .
2026-01-30 07:39:55 +00:00
Returns :
2026-03-11 03:06:15 -07:00
List of skill metadata dicts ( name , description , category ) .
2026-01-30 07:39:55 +00:00
"""
2026-03-29 00:33:30 -07:00
from agent . skill_utils import get_external_skills_dirs
2026-03-11 03:06:15 -07:00
2026-03-29 00:33:30 -07:00
skills = [ ]
seen_names : set = set ( )
2026-03-11 03:06:15 -07:00
# Load disabled set once (not per-skill)
disabled = set ( ) if skip_disabled else _get_disabled_skill_names ( )
2026-03-29 00:33:30 -07:00
# Scan local dir first, then external dirs (local takes precedence)
dirs_to_scan = [ ]
if SKILLS_DIR . exists ( ) :
dirs_to_scan . append ( SKILLS_DIR )
dirs_to_scan . extend ( get_external_skills_dirs ( ) )
2026-03-13 03:14:04 -07:00
2026-03-29 00:33:30 -07:00
for scan_dir in dirs_to_scan :
for skill_md in scan_dir . rglob ( " SKILL.md " ) :
if any ( part in _EXCLUDED_SKILL_DIRS for part in skill_md . parts ) :
continue
2026-03-11 03:06:15 -07:00
2026-03-29 00:33:30 -07:00
skill_dir = skill_md . parent
2026-03-11 03:06:15 -07:00
2026-03-29 00:33:30 -07:00
try :
content = skill_md . read_text ( encoding = " utf-8 " ) [ : 4000 ]
frontmatter , body = _parse_frontmatter ( content )
2026-03-07 00:47:54 -08:00
2026-03-29 00:33:30 -07:00
if not skill_matches_platform ( frontmatter ) :
continue
2026-03-11 03:06:15 -07:00
2026-03-29 00:33:30 -07:00
name = frontmatter . get ( " name " , skill_dir . name ) [ : MAX_NAME_LENGTH ]
if name in seen_names :
continue
if name in disabled :
continue
2026-03-11 03:06:15 -07:00
2026-03-29 00:33:30 -07:00
description = frontmatter . get ( " description " , " " )
if not description :
for line in body . strip ( ) . split ( " \n " ) :
line = line . strip ( )
if line and not line . startswith ( " # " ) :
description = line
break
2026-03-11 03:06:15 -07:00
2026-03-29 00:33:30 -07:00
if len ( description ) > MAX_DESCRIPTION_LENGTH :
description = description [ : MAX_DESCRIPTION_LENGTH - 3 ] + " ... "
2026-03-11 03:06:15 -07:00
2026-03-29 00:33:30 -07:00
category = _get_category_from_path ( skill_md )
2026-03-11 03:06:15 -07:00
2026-03-29 00:33:30 -07:00
seen_names . add ( name )
skills . append ( {
" name " : name ,
" description " : description ,
" category " : category ,
} )
2026-03-11 03:06:15 -07:00
2026-03-29 00:33:30 -07:00
except ( UnicodeDecodeError , PermissionError ) as e :
logger . debug ( " Failed to read skill file %s : %s " , skill_md , e )
continue
except Exception as e :
logger . debug (
" Skipping skill at %s : failed to parse: %s " , skill_md , e , exc_info = True
)
continue
2026-03-11 03:06:15 -07:00
2026-01-30 07:39:55 +00:00
return skills
2026-02-01 01:32:21 -08:00
def _load_category_description ( category_dir : Path ) - > Optional [ str ] :
"""
Load category description from DESCRIPTION . md if it exists .
2026-03-13 03:14:04 -07:00
2026-02-01 01:32:21 -08:00
Args :
category_dir : Path to the category directory
2026-03-13 03:14:04 -07:00
2026-02-01 01:32:21 -08:00
Returns :
Description string or None if not found
"""
desc_file = category_dir / " DESCRIPTION.md "
if not desc_file . exists ( ) :
return None
2026-03-13 03:14:04 -07:00
2026-02-01 01:32:21 -08:00
try :
2026-03-13 03:14:04 -07:00
content = desc_file . read_text ( encoding = " utf-8 " )
2026-02-01 01:32:21 -08:00
# Parse frontmatter if present
frontmatter , body = _parse_frontmatter ( content )
2026-03-13 03:14:04 -07:00
2026-02-01 01:32:21 -08:00
# Prefer frontmatter description, fall back to first non-header line
2026-03-13 03:14:04 -07:00
description = frontmatter . get ( " description " , " " )
2026-02-01 01:32:21 -08:00
if not description :
2026-03-13 03:14:04 -07:00
for line in body . strip ( ) . split ( " \n " ) :
2026-02-01 01:32:21 -08:00
line = line . strip ( )
2026-03-13 03:14:04 -07:00
if line and not line . startswith ( " # " ) :
2026-02-01 01:32:21 -08:00
description = line
break
2026-03-13 03:14:04 -07:00
2026-02-01 01:32:21 -08:00
# Truncate to reasonable length
if len ( description ) > MAX_DESCRIPTION_LENGTH :
2026-03-13 03:14:04 -07:00
description = description [ : MAX_DESCRIPTION_LENGTH - 3 ] + " ... "
2026-02-01 01:32:21 -08:00
return description if description else None
2026-03-08 00:30:49 +03:00
except ( UnicodeDecodeError , PermissionError ) as e :
logger . debug ( " Failed to read category description %s : %s " , desc_file , e )
return None
except Exception as e :
2026-03-13 03:14:04 -07:00
logger . warning (
" Error parsing category description %s : %s " , desc_file , e , exc_info = True
)
2026-02-01 01:32:21 -08:00
return None
2026-02-03 15:26:59 -08:00
def skills_categories ( verbose : bool = False , task_id : str = None ) - > str :
2026-01-30 07:39:55 +00:00
"""
2026-02-01 01:32:21 -08:00
List available skill categories with descriptions ( progressive disclosure tier 0 ) .
2026-03-13 03:14:04 -07:00
2026-02-01 01:32:21 -08:00
Returns category names and descriptions for efficient discovery before drilling down .
Categories can have a DESCRIPTION . md file with a description frontmatter field
or first paragraph to explain what skills are in that category .
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
Args :
2026-02-03 15:26:59 -08:00
verbose : If True , include skill counts per category ( default : False , but currently always included )
2026-03-13 03:14:04 -07:00
task_id : Optional task identifier used to probe the active backend
2026-01-30 07:39:55 +00:00
Returns :
2026-02-01 01:32:21 -08:00
JSON string with list of categories and their descriptions
2026-01-30 07:39:55 +00:00
"""
try :
if not SKILLS_DIR . exists ( ) :
2026-03-13 03:14:04 -07:00
return json . dumps (
{
" success " : True ,
" categories " : [ ] ,
" message " : " No skills directory found. " ,
} ,
ensure_ascii = False ,
)
2026-02-01 01:32:21 -08:00
category_dirs = { }
2026-03-13 03:14:04 -07:00
category_counts : Dict [ str , int ] = { }
2026-01-30 07:39:55 +00:00
for skill_md in SKILLS_DIR . rglob ( " SKILL.md " ) :
2026-03-13 03:14:04 -07:00
if any ( part in _EXCLUDED_SKILL_DIRS for part in skill_md . parts ) :
continue
try :
frontmatter , _ = _parse_frontmatter (
skill_md . read_text ( encoding = " utf-8 " ) [ : 4000 ]
)
except Exception :
frontmatter = { }
if not skill_matches_platform ( frontmatter ) :
continue
2026-01-30 07:39:55 +00:00
category = _get_category_from_path ( skill_md )
if category :
2026-03-13 03:14:04 -07:00
category_counts [ category ] = category_counts . get ( category , 0 ) + 1
2026-02-01 01:32:21 -08:00
if category not in category_dirs :
2026-03-13 03:14:04 -07:00
category_dirs [ category ] = SKILLS_DIR / category
2026-02-01 01:32:21 -08:00
categories = [ ]
for name in sorted ( category_dirs . keys ( ) ) :
category_dir = category_dirs [ name ]
description = _load_category_description ( category_dir )
2026-03-13 03:14:04 -07:00
cat_entry = { " name " : name , " skill_count " : category_counts [ name ] }
2026-02-01 01:32:21 -08:00
if description :
cat_entry [ " description " ] = description
categories . append ( cat_entry )
2026-03-13 03:14:04 -07:00
return json . dumps (
{
" success " : True ,
" categories " : categories ,
" hint " : " If a category is relevant to your task, use skills_list with that category to see available skills " ,
} ,
ensure_ascii = False ,
)
2026-01-30 07:39:55 +00:00
except Exception as e :
2026-03-13 03:14:04 -07:00
return json . dumps ( { " success " : False , " error " : str ( e ) } , ensure_ascii = False )
2026-01-30 07:39:55 +00:00
def skills_list ( category : str = None , task_id : str = None ) - > str :
"""
List all available skills ( progressive disclosure tier 1 - minimal metadata ) .
2026-03-13 03:14:04 -07:00
Returns only name + description to minimize token usage . Use skill_view ( ) to
2026-01-30 07:39:55 +00:00
load full content , tags , related files , etc .
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
Args :
category : Optional category filter ( e . g . , " mlops " )
2026-03-13 03:14:04 -07:00
task_id : Optional task identifier used to probe the active backend
2026-01-30 07:39:55 +00:00
Returns :
JSON string with minimal skill info : name , description , category
"""
try :
if not SKILLS_DIR . exists ( ) :
SKILLS_DIR . mkdir ( parents = True , exist_ok = True )
2026-03-13 03:14:04 -07:00
return json . dumps (
{
" success " : True ,
" skills " : [ ] ,
" categories " : [ ] ,
" message " : " No skills found. Skills directory created at ~/.hermes/skills/ " ,
} ,
ensure_ascii = False ,
)
2026-01-30 07:39:55 +00:00
# Find all skills
all_skills = _find_all_skills ( )
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
if not all_skills :
2026-03-13 03:14:04 -07:00
return json . dumps (
{
" success " : True ,
" skills " : [ ] ,
" categories " : [ ] ,
" message " : " No skills found in skills/ directory. " ,
} ,
ensure_ascii = False ,
)
2026-01-30 07:39:55 +00:00
# Filter by category if specified
if category :
all_skills = [ s for s in all_skills if s . get ( " category " ) == category ]
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
# Sort by category then name
all_skills . sort ( key = lambda s : ( s . get ( " category " ) or " " , s [ " name " ] ) )
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
# Extract unique categories
2026-03-13 03:14:04 -07:00
categories = sorted (
set ( s . get ( " category " ) for s in all_skills if s . get ( " category " ) )
)
return json . dumps (
{
" success " : True ,
" skills " : all_skills ,
" categories " : categories ,
" count " : len ( all_skills ) ,
" hint " : " Use skill_view(name) to see full content, tags, and linked files " ,
} ,
ensure_ascii = False ,
)
2026-01-30 07:39:55 +00:00
except Exception as e :
2026-03-13 03:14:04 -07:00
return json . dumps ( { " success " : False , " error " : str ( e ) } , ensure_ascii = False )
2026-01-30 07:39:55 +00:00
def skill_view ( name : str , file_path : str = None , task_id : str = None ) - > str :
"""
View the content of a skill or a specific file within a skill directory .
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
Args :
name : Name or path of the skill ( e . g . , " axolotl " or " 03-fine-tuning/axolotl " )
file_path : Optional path to a specific file within the skill ( e . g . , " references/api.md " )
2026-03-13 03:14:04 -07:00
task_id : Optional task identifier used to probe the active backend
2026-01-30 07:39:55 +00:00
Returns :
JSON string with skill content or error message
"""
2026-03-31 00:37:14 +00:00
# Security: Validate skill name to prevent path traversal (V-011)
try :
validate_skill_name ( name , allow_path_separator = True )
except SkillSecurityError as e :
logger . warning ( " Security: Blocked skill_view attempt with invalid name ' %s ' : %s " , name , e )
return json . dumps (
{
" success " : False ,
" error " : f " Invalid skill name: { e } " ,
" security_error " : True ,
} ,
ensure_ascii = False ,
)
2026-01-30 07:39:55 +00:00
try :
2026-03-29 00:33:30 -07:00
from agent . skill_utils import get_external_skills_dirs
# Build list of all skill directories to search
all_dirs = [ ]
if SKILLS_DIR . exists ( ) :
all_dirs . append ( SKILLS_DIR )
all_dirs . extend ( get_external_skills_dirs ( ) )
if not all_dirs :
2026-03-13 03:14:04 -07:00
return json . dumps (
{
" success " : False ,
" error " : " Skills directory does not exist yet. It will be created on first install. " ,
} ,
ensure_ascii = False ,
)
2026-01-30 07:39:55 +00:00
skill_dir = None
skill_md = None
2026-03-13 03:14:04 -07:00
2026-03-29 00:33:30 -07:00
# Search all dirs: local first, then external (first match wins)
for search_dir in all_dirs :
# Try direct path first (e.g., "mlops/axolotl")
direct_path = search_dir / name
2026-03-31 00:37:14 +00:00
# Security: Verify direct_path doesn't escape search_dir (V-011)
try :
resolved_direct = direct_path . resolve ( )
resolved_search = search_dir . resolve ( )
if not resolved_direct . is_relative_to ( resolved_search ) :
logger . warning (
" Security: Skill path ' %s ' escapes directory boundary in ' %s ' " ,
name , search_dir
)
continue
except ( OSError , ValueError ) as e :
logger . warning ( " Security: Invalid skill path ' %s ' : %s " , name , e )
continue
2026-03-29 00:33:30 -07:00
if direct_path . is_dir ( ) and ( direct_path / " SKILL.md " ) . exists ( ) :
skill_dir = direct_path
skill_md = direct_path / " SKILL.md "
break
elif direct_path . with_suffix ( " .md " ) . exists ( ) :
skill_md = direct_path . with_suffix ( " .md " )
break
# Search by directory name across all dirs
2026-02-19 18:25:53 -08:00
if not skill_md :
2026-03-29 00:33:30 -07:00
for search_dir in all_dirs :
for found_skill_md in search_dir . rglob ( " SKILL.md " ) :
if found_skill_md . parent . name == name :
skill_dir = found_skill_md . parent
skill_md = found_skill_md
break
if skill_md :
2026-01-30 07:39:55 +00:00
break
2026-03-13 03:14:04 -07:00
2026-02-19 18:25:53 -08:00
# Legacy: flat .md files
if not skill_md :
2026-03-29 00:33:30 -07:00
for search_dir in all_dirs :
for found_md in search_dir . rglob ( f " { name } .md " ) :
if found_md . name != " SKILL.md " :
skill_md = found_md
break
if skill_md :
2026-02-19 18:25:53 -08:00
break
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
if not skill_md or not skill_md . exists ( ) :
2026-03-13 03:14:04 -07:00
available = [ s [ " name " ] for s in _find_all_skills ( ) [ : 20 ] ]
return json . dumps (
{
" success " : False ,
" error " : f " Skill ' { name } ' not found. " ,
" available_skills " : available ,
" hint " : " Use skills_list to see all available skills " ,
} ,
ensure_ascii = False ,
)
# Read the file once — reused for platform check and main content below
try :
content = skill_md . read_text ( encoding = " utf-8 " )
except Exception as e :
return json . dumps (
{
" success " : False ,
" error " : f " Failed to read skill ' { name } ' : { e } " ,
} ,
ensure_ascii = False ,
)
2026-03-29 00:33:30 -07:00
# Security: warn if skill is loaded from outside trusted directories
# (local skills dir + configured external_dirs are all trusted)
_outside_skills_dir = True
_trusted_dirs = [ SKILLS_DIR . resolve ( ) ]
fix: prevent infinite 400 loop on context overflow + block prompt injection via cache files (#1630, #1558)
* fix: prevent infinite 400 failure loop on context overflow (#1630)
When a gateway session exceeds the model's context window, Anthropic may
return a generic 400 invalid_request_error with just 'Error' as the
message. This bypassed the phrase-based context-length detection,
causing the agent to treat it as a non-retryable client error. Worse,
the failed user message was still persisted to the transcript, making
the session even larger on each attempt — creating an infinite loop.
Three-layer fix:
1. run_agent.py — Fallback heuristic: when a 400 error has a very short
generic message AND the session is large (>40% of context or >80
messages), treat it as a probable context overflow and trigger
compression instead of aborting.
2. run_agent.py + gateway/run.py — Don't persist failed messages:
when the agent returns failed=True before generating any response,
skip writing the user's message to the transcript/DB. This prevents
the session from growing on each failure.
3. gateway/run.py — Smarter error messages: detect context-overflow
failures and suggest /compact or /reset specifically, instead of a
generic 'try again' that will fail identically.
* fix(skills): detect prompt injection patterns and block cache file reads
Adds two security layers to prevent prompt injection via skills hub
cache files (#1558):
1. read_file: blocks direct reads of ~/.hermes/skills/.hub/ directory
(index-cache, catalog files). The 3.5MB clawhub_catalog_v1.json
was the original injection vector — untrusted skill descriptions
in the catalog contained adversarial text that the model executed.
2. skill_view: warns when skills are loaded from outside the trusted
~/.hermes/skills/ directory, and detects common injection patterns
in skill content ("ignore previous instructions", "<system>", etc.).
Cherry-picked from PR #1562 by ygd58.
---------
Co-authored-by: buray <ygd58@users.noreply.github.com>
2026-03-17 01:50:59 -07:00
try :
2026-03-29 00:33:30 -07:00
_trusted_dirs . extend ( d . resolve ( ) for d in all_dirs [ 1 : ] )
except Exception :
pass
for _td in _trusted_dirs :
try :
skill_md . resolve ( ) . relative_to ( _td )
_outside_skills_dir = False
break
except ValueError :
continue
fix: prevent infinite 400 loop on context overflow + block prompt injection via cache files (#1630, #1558)
* fix: prevent infinite 400 failure loop on context overflow (#1630)
When a gateway session exceeds the model's context window, Anthropic may
return a generic 400 invalid_request_error with just 'Error' as the
message. This bypassed the phrase-based context-length detection,
causing the agent to treat it as a non-retryable client error. Worse,
the failed user message was still persisted to the transcript, making
the session even larger on each attempt — creating an infinite loop.
Three-layer fix:
1. run_agent.py — Fallback heuristic: when a 400 error has a very short
generic message AND the session is large (>40% of context or >80
messages), treat it as a probable context overflow and trigger
compression instead of aborting.
2. run_agent.py + gateway/run.py — Don't persist failed messages:
when the agent returns failed=True before generating any response,
skip writing the user's message to the transcript/DB. This prevents
the session from growing on each failure.
3. gateway/run.py — Smarter error messages: detect context-overflow
failures and suggest /compact or /reset specifically, instead of a
generic 'try again' that will fail identically.
* fix(skills): detect prompt injection patterns and block cache file reads
Adds two security layers to prevent prompt injection via skills hub
cache files (#1558):
1. read_file: blocks direct reads of ~/.hermes/skills/.hub/ directory
(index-cache, catalog files). The 3.5MB clawhub_catalog_v1.json
was the original injection vector — untrusted skill descriptions
in the catalog contained adversarial text that the model executed.
2. skill_view: warns when skills are loaded from outside the trusted
~/.hermes/skills/ directory, and detects common injection patterns
in skill content ("ignore previous instructions", "<system>", etc.).
Cherry-picked from PR #1562 by ygd58.
---------
Co-authored-by: buray <ygd58@users.noreply.github.com>
2026-03-17 01:50:59 -07:00
# Security: detect common prompt injection patterns
_INJECTION_PATTERNS = [
" ignore previous instructions " ,
" ignore all previous " ,
" you are now " ,
" disregard your " ,
" forget your instructions " ,
" new instructions: " ,
" system prompt: " ,
" <system> " ,
" ]]> " ,
]
_content_lower = content . lower ( )
_injection_detected = any ( p in _content_lower for p in _INJECTION_PATTERNS )
if _outside_skills_dir or _injection_detected :
_warnings = [ ]
if _outside_skills_dir :
_warnings . append ( f " skill file is outside the trusted skills directory (~/.hermes/skills/): { skill_md } " )
if _injection_detected :
_warnings . append ( " skill content contains patterns that may indicate prompt injection " )
import logging as _logging
_logging . getLogger ( __name__ ) . warning ( " Skill security warning for ' %s ' : %s " , name , " ; " . join ( _warnings ) )
2026-03-13 03:14:04 -07:00
parsed_frontmatter : Dict [ str , Any ] = { }
try :
parsed_frontmatter , _ = _parse_frontmatter ( content )
except Exception :
parsed_frontmatter = { }
if not skill_matches_platform ( parsed_frontmatter ) :
return json . dumps (
{
" success " : False ,
" error " : f " Skill ' { name } ' is not supported on this platform. " ,
" readiness_status " : SkillReadinessStatus . UNSUPPORTED . value ,
} ,
ensure_ascii = False ,
)
2026-03-18 03:17:37 -07:00
# Check if the skill is disabled by the user
resolved_name = parsed_frontmatter . get ( " name " , skill_md . parent . name )
if _is_skill_disabled ( resolved_name ) :
return json . dumps (
{
" success " : False ,
" error " : (
f " Skill ' { resolved_name } ' is disabled. "
" Enable it with `hermes skills` or inspect the files directly on disk. "
) ,
} ,
ensure_ascii = False ,
)
2026-01-30 07:39:55 +00:00
# If a specific file path is requested, read that instead
if file_path and skill_dir :
2026-03-02 02:00:09 -08:00
# Security: Prevent path traversal attacks
normalized_path = Path ( file_path )
if " .. " in normalized_path . parts :
2026-03-13 03:14:04 -07:00
return json . dumps (
{
" success " : False ,
" error " : " Path traversal ( ' .. ' ) is not allowed. " ,
" hint " : " Use a relative path within the skill directory " ,
} ,
ensure_ascii = False ,
)
2026-01-30 07:39:55 +00:00
target_file = skill_dir / file_path
2026-03-13 03:14:04 -07:00
2026-03-02 02:00:09 -08:00
# Security: Verify resolved path is still within skill directory
try :
resolved = target_file . resolve ( )
skill_dir_resolved = skill_dir . resolve ( )
2026-03-04 05:30:43 -08:00
if not resolved . is_relative_to ( skill_dir_resolved ) :
2026-03-13 03:14:04 -07:00
return json . dumps (
{
" success " : False ,
" error " : " Path escapes skill directory boundary. " ,
" hint " : " Use a relative path within the skill directory " ,
} ,
ensure_ascii = False ,
)
2026-03-02 02:00:09 -08:00
except ( OSError , ValueError ) :
2026-03-13 03:14:04 -07:00
return json . dumps (
{
" success " : False ,
" error " : f " Invalid file path: ' { file_path } ' " ,
" hint " : " Use a valid relative path within the skill directory " ,
} ,
ensure_ascii = False ,
)
2026-01-30 07:39:55 +00:00
if not target_file . exists ( ) :
# List available files in the skill directory, organized by type
available_files = {
" references " : [ ] ,
" templates " : [ ] ,
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
" assets " : [ ] ,
2026-01-30 07:39:55 +00:00
" scripts " : [ ] ,
2026-03-13 03:14:04 -07:00
" other " : [ ] ,
2026-01-30 07:39:55 +00:00
}
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
# Scan for all readable files
for f in skill_dir . rglob ( " * " ) :
if f . is_file ( ) and f . name != " SKILL.md " :
rel = str ( f . relative_to ( skill_dir ) )
if rel . startswith ( " references/ " ) :
available_files [ " references " ] . append ( rel )
elif rel . startswith ( " templates/ " ) :
available_files [ " templates " ] . append ( rel )
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
elif rel . startswith ( " assets/ " ) :
available_files [ " assets " ] . append ( rel )
2026-01-30 07:39:55 +00:00
elif rel . startswith ( " scripts/ " ) :
available_files [ " scripts " ] . append ( rel )
2026-03-13 03:14:04 -07:00
elif f . suffix in [
" .md " ,
" .py " ,
" .yaml " ,
" .yml " ,
" .json " ,
" .tex " ,
" .sh " ,
] :
2026-01-30 07:39:55 +00:00
available_files [ " other " ] . append ( rel )
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
# Remove empty categories
available_files = { k : v for k , v in available_files . items ( ) if v }
2026-03-13 03:14:04 -07:00
return json . dumps (
{
" success " : False ,
" error " : f " File ' { file_path } ' not found in skill ' { name } ' . " ,
" available_files " : available_files ,
" hint " : " Use one of the available file paths listed above " ,
} ,
ensure_ascii = False ,
)
2026-01-30 07:39:55 +00:00
# Read the file content
try :
2026-03-13 03:14:04 -07:00
content = target_file . read_text ( encoding = " utf-8 " )
2026-01-30 07:39:55 +00:00
except UnicodeDecodeError :
# Binary file - return info about it instead
2026-03-13 03:14:04 -07:00
return json . dumps (
{
" success " : True ,
" name " : name ,
" file " : file_path ,
" content " : f " [Binary file: { target_file . name } , size: { target_file . stat ( ) . st_size } bytes] " ,
" is_binary " : True ,
} ,
ensure_ascii = False ,
)
return json . dumps (
{
2026-01-30 07:39:55 +00:00
" success " : True ,
" name " : name ,
" file " : file_path ,
2026-03-13 03:14:04 -07:00
" content " : content ,
" file_type " : target_file . suffix ,
} ,
ensure_ascii = False ,
)
# Reuse the parse from the platform check above
frontmatter = parsed_frontmatter
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
# Get reference, template, asset, and script files if this is a directory-based skill
2026-01-30 07:39:55 +00:00
reference_files = [ ]
template_files = [ ]
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
asset_files = [ ]
2026-01-30 07:39:55 +00:00
script_files = [ ]
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
if skill_dir :
references_dir = skill_dir / " references "
if references_dir . exists ( ) :
2026-03-13 03:14:04 -07:00
reference_files = [
str ( f . relative_to ( skill_dir ) ) for f in references_dir . glob ( " *.md " )
]
2026-01-30 07:39:55 +00:00
templates_dir = skill_dir / " templates "
if templates_dir . exists ( ) :
2026-03-13 03:14:04 -07:00
for ext in [
" *.md " ,
" *.py " ,
" *.yaml " ,
" *.yml " ,
" *.json " ,
" *.tex " ,
" *.sh " ,
] :
template_files . extend (
[
str ( f . relative_to ( skill_dir ) )
for f in templates_dir . rglob ( ext )
]
)
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
# assets/ — agentskills.io standard directory for supplementary files
assets_dir = skill_dir / " assets "
if assets_dir . exists ( ) :
for f in assets_dir . rglob ( " * " ) :
if f . is_file ( ) :
asset_files . append ( str ( f . relative_to ( skill_dir ) ) )
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
scripts_dir = skill_dir / " scripts "
if scripts_dir . exists ( ) :
2026-03-13 03:14:04 -07:00
for ext in [ " *.py " , " *.sh " , " *.bash " , " *.js " , " *.ts " , " *.rb " ] :
script_files . extend (
[ str ( f . relative_to ( skill_dir ) ) for f in scripts_dir . glob ( ext ) ]
)
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
# Read tags/related_skills with backward compat:
# Check metadata.hermes.* first (agentskills.io convention), fall back to top-level
hermes_meta = { }
2026-03-13 03:14:04 -07:00
metadata = frontmatter . get ( " metadata " )
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
if isinstance ( metadata , dict ) :
2026-03-13 03:14:04 -07:00
hermes_meta = metadata . get ( " hermes " , { } ) or { }
tags = _parse_tags ( hermes_meta . get ( " tags " ) or frontmatter . get ( " tags " , " " ) )
related_skills = _parse_tags (
hermes_meta . get ( " related_skills " ) or frontmatter . get ( " related_skills " , " " )
)
2026-01-30 07:39:55 +00:00
# Build linked files structure for clear discovery
linked_files = { }
if reference_files :
linked_files [ " references " ] = reference_files
if template_files :
linked_files [ " templates " ] = template_files
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
if asset_files :
linked_files [ " assets " ] = asset_files
2026-01-30 07:39:55 +00:00
if script_files :
linked_files [ " scripts " ] = script_files
2026-03-13 03:14:04 -07:00
2026-03-29 00:33:30 -07:00
try :
rel_path = str ( skill_md . relative_to ( SKILLS_DIR ) )
except ValueError :
# External skill — use path relative to the skill's own parent dir
rel_path = str ( skill_md . relative_to ( skill_md . parent . parent ) ) if skill_md . parent . parent else skill_md . name
2026-03-13 03:14:04 -07:00
skill_name = frontmatter . get (
" name " , skill_md . stem if not skill_dir else skill_dir . name
)
legacy_env_vars , _ = _collect_prerequisite_values ( frontmatter )
required_env_vars = _get_required_environment_variables (
frontmatter , legacy_env_vars
)
backend = _get_terminal_backend_name ( )
env_snapshot = load_env ( )
missing_required_env_vars = [
e
for e in required_env_vars
2026-03-28 17:52:32 -07:00
if not _is_env_var_persisted ( e [ " name " ] , env_snapshot )
2026-03-13 03:14:04 -07:00
]
capture_result = _capture_required_environment_variables (
skill_name ,
missing_required_env_vars ,
)
if missing_required_env_vars :
env_snapshot = load_env ( )
remaining_missing_required_envs = _remaining_required_environment_names (
required_env_vars ,
capture_result ,
env_snapshot = env_snapshot ,
)
setup_needed = bool ( remaining_missing_required_envs )
feat: env var passthrough for skills and user config (#2807)
* feat: env var passthrough for skills and user config
Skills that declare required_environment_variables now have those vars
passed through to sandboxed execution environments (execute_code and
terminal). Previously, execute_code stripped all vars containing KEY,
TOKEN, SECRET, etc. and the terminal blocklist removed Hermes
infrastructure vars — both blocked skill-declared env vars.
Two passthrough sources:
1. Skill-scoped (automatic): when a skill is loaded via skill_view and
declares required_environment_variables, vars that are present in
the environment are registered in a session-scoped passthrough set.
2. Config-based (manual): terminal.env_passthrough in config.yaml lets
users explicitly allowlist vars for non-skill use cases.
Changes:
- New module: tools/env_passthrough.py — shared passthrough registry
- hermes_cli/config.py: add terminal.env_passthrough to DEFAULT_CONFIG
- tools/skills_tool.py: register available skill env vars on load
- tools/code_execution_tool.py: check passthrough before filtering
- tools/environments/local.py: check passthrough in _sanitize_subprocess_env
and _make_run_env
- 19 new tests covering all layers
* docs: add environment variable passthrough documentation
Document the env var passthrough feature across four docs pages:
- security.md: new 'Environment Variable Passthrough' section with
full explanation, comparison table, and security considerations
- code-execution.md: update security section, add passthrough subsection,
fix comparison table
- creating-skills.md: add tip about automatic sandbox passthrough
- skills.md: add note about passthrough after secure setup docs
Live-tested: launched interactive CLI, loaded a skill with
required_environment_variables, verified TEST_SKILL_SECRET_KEY was
accessible inside execute_code sandbox (value: passthrough-test-value-42).
2026-03-24 08:19:34 -07:00
# Register available skill env vars so they pass through to sandboxed
# execution environments (execute_code, terminal). Only vars that are
# actually set get registered — missing ones are reported as setup_needed.
available_env_names = [
e [ " name " ]
for e in required_env_vars
if e [ " name " ] not in remaining_missing_required_envs
]
if available_env_names :
try :
from tools . env_passthrough import register_env_passthrough
register_env_passthrough ( available_env_names )
except Exception :
logger . debug (
" Could not register env passthrough for skill %s " ,
skill_name ,
exc_info = True ,
)
2026-03-28 23:53:40 -07:00
# Register credential files for mounting into remote sandboxes
# (Modal, Docker). Files that exist on the host are registered;
# missing ones are added to the setup_needed indicators.
required_cred_files_raw = frontmatter . get ( " required_credential_files " , [ ] )
if not isinstance ( required_cred_files_raw , list ) :
required_cred_files_raw = [ ]
missing_cred_files : list = [ ]
if required_cred_files_raw :
try :
from tools . credential_files import register_credential_files
missing_cred_files = register_credential_files ( required_cred_files_raw )
if missing_cred_files :
setup_needed = True
except Exception :
logger . debug (
" Could not register credential files for skill %s " ,
skill_name ,
exc_info = True ,
)
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
result = {
2026-01-30 07:39:55 +00:00
" success " : True ,
2026-03-13 03:14:04 -07:00
" name " : skill_name ,
" description " : frontmatter . get ( " description " , " " ) ,
2026-01-30 07:39:55 +00:00
" tags " : tags ,
" related_skills " : related_skills ,
" content " : content ,
2026-02-19 18:25:53 -08:00
" path " : rel_path ,
2026-01-30 07:39:55 +00:00
" linked_files " : linked_files if linked_files else None ,
2026-03-13 03:14:04 -07:00
" usage_hint " : " To view linked files, call skill_view(name, file_path) where file_path is e.g. ' references/api.md ' or ' assets/config.yaml ' "
if linked_files
else None ,
" required_environment_variables " : required_env_vars ,
" required_commands " : [ ] ,
" missing_required_environment_variables " : remaining_missing_required_envs ,
2026-03-28 23:53:40 -07:00
" missing_credential_files " : missing_cred_files ,
2026-03-13 03:14:04 -07:00
" missing_required_commands " : [ ] ,
" setup_needed " : setup_needed ,
" setup_skipped " : capture_result [ " setup_skipped " ] ,
" readiness_status " : SkillReadinessStatus . SETUP_NEEDED . value
if setup_needed
else SkillReadinessStatus . AVAILABLE . value ,
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
}
2026-03-13 03:14:04 -07:00
setup_help = next ( ( e [ " help " ] for e in required_env_vars if e . get ( " help " ) ) , None )
if setup_help :
result [ " setup_help " ] = setup_help
if capture_result [ " gateway_setup_hint " ] :
result [ " gateway_setup_hint " ] = capture_result [ " gateway_setup_hint " ]
if setup_needed :
missing_items = [
f " env $ { env_name } " for env_name in remaining_missing_required_envs
2026-03-28 23:53:40 -07:00
] + [
f " file { path } " for path in missing_cred_files
2026-03-13 03:14:04 -07:00
]
setup_note = _build_setup_note (
SkillReadinessStatus . SETUP_NEEDED ,
missing_items ,
setup_help ,
)
if backend in _REMOTE_ENV_BACKENDS and setup_note :
setup_note = f " { setup_note } { backend . upper ( ) } -backed skills need these requirements available inside the remote environment as well. "
if setup_note :
result [ " setup_note " ] = setup_note
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
# Surface agentskills.io optional fields when present
2026-03-13 03:14:04 -07:00
if frontmatter . get ( " compatibility " ) :
result [ " compatibility " ] = frontmatter [ " compatibility " ]
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
if isinstance ( metadata , dict ) :
result [ " metadata " ] = metadata
2026-03-13 03:14:04 -07:00
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
return json . dumps ( result , ensure_ascii = False )
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
except Exception as e :
2026-03-13 03:14:04 -07:00
return json . dumps ( { " success " : False , " error " : str ( e ) } , ensure_ascii = False )
2026-01-30 07:39:55 +00:00
# Tool description for model_tools.py
SKILLS_TOOL_DESCRIPTION = """ Access skill documents providing specialized instructions, guidelines, and executable knowledge.
Progressive disclosure workflow :
1. skills_list ( ) - Returns metadata ( name , description , tags , linked_file_count ) for all skills
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
2. skill_view ( name ) - Loads full SKILL . md content + shows available linked_files
2026-01-30 07:39:55 +00:00
3. skill_view ( name , file_path ) - Loads specific linked file ( e . g . , ' references/api.md ' , ' scripts/train.py ' )
Skills may include :
- references / : Additional documentation , API specs , examples
- templates / : Output formats , config files , boilerplate code
Add Skills Hub — universal skill search, install, and management from online registries
Implements the Hermes Skills Hub with agentskills.io spec compliance,
multi-registry skill discovery, security scanning, and user-driven
management via CLI and /skills slash command.
Core features:
- Security scanner (tools/skills_guard.py): 120 threat patterns across
12 categories, trust-aware install policy (builtin/trusted/community),
structural checks, unicode injection detection, LLM audit pass
- Hub client (tools/skills_hub.py): GitHub, ClawHub, Claude Code
marketplace, and LobeHub source adapters with shared GitHubAuth
(PAT + gh CLI + GitHub App), lock file provenance tracking, quarantine
flow, and unified search across all sources
- CLI interface (hermes_cli/skills_hub.py): search, install, inspect,
list, audit, uninstall, publish (GitHub PR), snapshot export/import,
and tap management — powers both `hermes skills` and `/skills`
Spec conformance (Phase 0):
- Upgraded frontmatter parser to yaml.safe_load with fallback
- Migrated 39 SKILL.md files: tags/related_skills to metadata.hermes.*
- Added assets/ directory support and compatibility/metadata fields
- Excluded .hub/ from skill discovery in skills_tool.py
Updated 13 config/doc files including README, AGENTS.md, .env.example,
setup wizard, doctor, status, pyproject.toml, and docs.
2026-02-18 16:09:05 -08:00
- assets / : Supplementary files ( agentskills . io standard )
2026-01-30 07:39:55 +00:00
- scripts / : Executable helpers ( Python , shell scripts ) """
if __name__ == " __main__ " :
""" Test the skills tool """
print ( " 🎯 Skills Tool Test " )
print ( " = " * 60 )
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
# Test listing skills
print ( " \n 📋 Listing all skills: " )
result = json . loads ( skills_list ( ) )
if result [ " success " ] :
2026-03-13 03:14:04 -07:00
print (
f " Found { result [ ' count ' ] } skills in { len ( result . get ( ' categories ' , [ ] ) ) } categories "
)
2026-01-30 07:39:55 +00:00
print ( f " Categories: { result . get ( ' categories ' , [ ] ) } " )
print ( " \n First 10 skills: " )
for skill in result [ " skills " ] [ : 10 ] :
2026-03-13 03:14:04 -07:00
cat = f " [ { skill [ ' category ' ] } ] " if skill . get ( " category " ) else " "
print ( f " • { cat } { skill [ ' name ' ] } : { skill [ ' description ' ] [ : 60 ] } ... " )
2026-01-30 07:39:55 +00:00
else :
print ( f " Error: { result [ ' error ' ] } " )
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
# Test viewing a skill
print ( " \n 📖 Viewing skill ' axolotl ' : " )
result = json . loads ( skill_view ( " axolotl " ) )
if result [ " success " ] :
print ( f " Name: { result [ ' name ' ] } " )
print ( f " Description: { result . get ( ' description ' , ' N/A ' ) [ : 100 ] } ... " )
print ( f " Content length: { len ( result [ ' content ' ] ) } chars " )
2026-03-13 03:14:04 -07:00
if result . get ( " linked_files " ) :
print ( f " Linked files: { result [ ' linked_files ' ] } " )
2026-01-30 07:39:55 +00:00
else :
print ( f " Error: { result [ ' error ' ] } " )
2026-03-13 03:14:04 -07:00
2026-01-30 07:39:55 +00:00
# Test viewing a reference file
print ( " \n 📄 Viewing reference file ' axolotl/references/dataset-formats.md ' : " )
result = json . loads ( skill_view ( " axolotl " , " references/dataset-formats.md " ) )
if result [ " success " ] :
print ( f " File: { result [ ' file ' ] } " )
print ( f " Content length: { len ( result [ ' content ' ] ) } chars " )
print ( f " Preview: { result [ ' content ' ] [ : 150 ] } ... " )
else :
print ( f " Error: { result [ ' error ' ] } " )
2026-02-21 20:22:33 -08:00
# ---------------------------------------------------------------------------
# Registry
# ---------------------------------------------------------------------------
SKILLS_LIST_SCHEMA = {
" name " : " skills_list " ,
" description " : " List available skills (name + description). Use skill_view(name) to load full content. " ,
" parameters " : {
" type " : " object " ,
" properties " : {
" category " : {
" type " : " string " ,
2026-03-13 03:14:04 -07:00
" description " : " Optional category filter to narrow results " ,
2026-02-21 20:22:33 -08:00
}
} ,
2026-03-13 03:14:04 -07:00
" required " : [ ] ,
} ,
2026-02-21 20:22:33 -08:00
}
SKILL_VIEW_SCHEMA = {
" name " : " skill_view " ,
" description " : " Skills allow for loading information about specific tasks and workflows, as well as scripts and templates. Load a skill ' s full content or access its linked files (references, templates, scripts). First call returns SKILL.md content plus a ' linked_files ' dict showing available references/templates/scripts. To access those, call again with file_path parameter. " ,
" parameters " : {
" type " : " object " ,
" properties " : {
" name " : {
" type " : " string " ,
2026-03-13 03:14:04 -07:00
" description " : " The skill name (use skills_list to see available skills) " ,
2026-02-21 20:22:33 -08:00
} ,
" file_path " : {
" type " : " string " ,
2026-03-13 03:14:04 -07:00
" description " : " OPTIONAL: Path to a linked file within the skill (e.g., ' references/api.md ' , ' templates/config.yaml ' , ' scripts/validate.py ' ). Omit to get the main SKILL.md content. " ,
} ,
2026-02-21 20:22:33 -08:00
} ,
2026-03-13 03:14:04 -07:00
" required " : [ " name " ] ,
} ,
2026-02-21 20:22:33 -08:00
}
registry . register (
name = " skills_list " ,
toolset = " skills " ,
schema = SKILLS_LIST_SCHEMA ,
2026-03-13 03:14:04 -07:00
handler = lambda args , * * kw : skills_list (
category = args . get ( " category " ) , task_id = kw . get ( " task_id " )
) ,
2026-02-21 20:22:33 -08:00
check_fn = check_skills_requirements ,
2026-03-15 20:21:21 -07:00
emoji = " 📚 " ,
2026-02-21 20:22:33 -08:00
)
registry . register (
name = " skill_view " ,
toolset = " skills " ,
schema = SKILL_VIEW_SCHEMA ,
2026-03-13 03:14:04 -07:00
handler = lambda args , * * kw : skill_view (
args . get ( " name " , " " ) , file_path = args . get ( " file_path " ) , task_id = kw . get ( " task_id " )
) ,
2026-02-21 20:22:33 -08:00
check_fn = check_skills_requirements ,
2026-03-15 20:21:21 -07:00
emoji = " 📚 " ,
2026-02-21 20:22:33 -08:00
)