126 lines
4.3 KiB
Python
126 lines
4.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Generate comprehensive Hermes Skills Inventory
|
|
Scans profile-specific skills directory and produces accurate inventory with linked files.
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
|
|
def parse_yaml_description(content: str, filename: str = "") -> str:
|
|
"""Extract description from YAML frontmatter or body."""
|
|
lines = content.split('\n')
|
|
for line in lines:
|
|
if line.startswith('description:'):
|
|
# Handle quoted and unquoted values
|
|
match = re.match(r'description:\s*["\']?(.*)["\']?$', line)
|
|
if match:
|
|
return match.group(1).strip().strip('"\'')
|
|
return ""
|
|
|
|
def get_linked_files(skill_path: Path) -> dict:
|
|
"""Get linked files for a skill."""
|
|
link_dirs = ["references", "templates", "scripts", "assets"]
|
|
linked_files = {}
|
|
|
|
for link_dir in link_dirs:
|
|
link_path = skill_path / link_dir
|
|
if link_path.exists() and link_path.is_dir():
|
|
files = [f.name for f in link_path.iterdir() if f.is_file()]
|
|
if files:
|
|
linked_files[link_dir] = files
|
|
|
|
return linked_files
|
|
|
|
def get_skills(profile="spark"):
|
|
"""Get comprehensive list of skills from profile-specific skills directory."""
|
|
skills_dir = Path(f"/root/.hermes/profiles/{profile}/skills")
|
|
if not skills_dir.exists():
|
|
print(f"Skills directory not found: {skills_dir}")
|
|
return []
|
|
|
|
skills = []
|
|
category_names = {
|
|
'apple', 'autonomous-ai-agents', 'creative', 'devops',
|
|
'email', 'media', 'note-taking', 'productivity',
|
|
'research', 'social-media', 'software-development', 'web'
|
|
}
|
|
|
|
for item in skills_dir.iterdir():
|
|
if not item.is_dir():
|
|
continue
|
|
|
|
# Case 1: Category directory (has skill subdirs)
|
|
if item.name in category_names:
|
|
for subdir in item.iterdir():
|
|
if subdir.is_dir():
|
|
skill_md = subdir / 'SKILL.md'
|
|
if skill_md.exists():
|
|
with open(skill_md, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
|
|
description = parse_yaml_description(content)
|
|
linked_files = get_linked_files(subdir)
|
|
|
|
skills.append({
|
|
"name": subdir.name,
|
|
"description": description,
|
|
"has_skill_md": True,
|
|
"linked_files": linked_files,
|
|
"category": item.name
|
|
})
|
|
|
|
# Case 2: Direct skill directory (not in category)
|
|
else:
|
|
skill_md = item / 'SKILL.md'
|
|
if skill_md.exists():
|
|
with open(skill_md, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
|
|
description = parse_yaml_description(content)
|
|
linked_files = get_linked_files(item)
|
|
|
|
skills.append({
|
|
"name": item.name,
|
|
"description": description,
|
|
"has_skill_md": True,
|
|
"linked_files": linked_files,
|
|
"category": None
|
|
})
|
|
|
|
return sorted(skills, key=lambda x: x['name'])
|
|
|
|
def main():
|
|
skills = get_skills("spark")
|
|
|
|
# Build inventory
|
|
inventory = {
|
|
"profile": "spark",
|
|
"generated_at": datetime.utcnow().isoformat() + "Z",
|
|
"total_skills": len(skills),
|
|
"skills": skills
|
|
}
|
|
|
|
# Count totals by category
|
|
categories = {}
|
|
for skill in skills:
|
|
cat = skill.get('category') or 'uncategorized'
|
|
categories[cat] = categories.get(cat, 0) + 1
|
|
|
|
inventory['categories'] = categories
|
|
|
|
# Write output
|
|
output_path = Path("/root/.hermes/wizard-outputs/spark/skills_inventory.json")
|
|
with open(output_path, 'w', encoding='utf-8') as f:
|
|
json.dump(inventory, f, indent=2, ensure_ascii=False)
|
|
|
|
print(f"Generated skills inventory: {output_path}")
|
|
print(f"Total skills: {len(skills)}")
|
|
print(f"Categories: {categories}")
|
|
return 0
|
|
|
|
if __name__ == "__main__":
|
|
exit(main()) |