Files
troupe-standup/wizard-work/spark/generate_skills_inventory.py

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())