- Added `date_added` field to all skills in frontmatter. - Updated Home page to display skill addition date alongside risk level. - Enhanced Skill Detail page to show date added in a badge format. - Created scripts for managing skill dates, including adding missing dates and generating reports. - Updated validators to enforce date format compliance. - Added comprehensive documentation on date tracking implementation and usage. - Introduced a new skill template including the `date_added` field.
128 lines
4.4 KiB
Python
128 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Generate a report of skills with their date_added metadata in JSON format.
|
|
|
|
Usage:
|
|
python generate_skills_report.py [--output report.json] [--sort date|name]
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import json
|
|
import sys
|
|
import argparse
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
def get_project_root():
|
|
"""Get the project root directory."""
|
|
return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
def parse_frontmatter(content):
|
|
"""Parse frontmatter from SKILL.md content."""
|
|
fm_match = re.search(r'^---\s*\n(.*?)\n---', content, re.DOTALL)
|
|
if not fm_match:
|
|
return None
|
|
|
|
fm_text = fm_match.group(1)
|
|
metadata = {}
|
|
for line in fm_text.split('\n'):
|
|
if ':' in line and not line.strip().startswith('#'):
|
|
key, val = line.split(':', 1)
|
|
metadata[key.strip()] = val.strip().strip('"').strip("'")
|
|
|
|
return metadata
|
|
|
|
def generate_skills_report(output_file=None, sort_by='date'):
|
|
"""Generate a report of all skills with their metadata."""
|
|
skills_dir = os.path.join(get_project_root(), 'skills')
|
|
skills_data = []
|
|
|
|
for root, dirs, files in os.walk(skills_dir):
|
|
# Skip hidden/disabled directories
|
|
dirs[:] = [d for d in dirs if not d.startswith('.')]
|
|
|
|
if "SKILL.md" in files:
|
|
skill_name = os.path.basename(root)
|
|
skill_path = os.path.join(root, "SKILL.md")
|
|
|
|
try:
|
|
with open(skill_path, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
|
|
metadata = parse_frontmatter(content)
|
|
if metadata is None:
|
|
continue
|
|
|
|
skill_info = {
|
|
'id': metadata.get('id', skill_name),
|
|
'name': metadata.get('name', skill_name),
|
|
'description': metadata.get('description', ''),
|
|
'date_added': metadata.get('date_added', None),
|
|
'source': metadata.get('source', 'unknown'),
|
|
'risk': metadata.get('risk', 'unknown'),
|
|
'category': metadata.get('category', metadata.get('id', '').split('-')[0] if '-' in metadata.get('id', '') else 'other'),
|
|
}
|
|
|
|
skills_data.append(skill_info)
|
|
except Exception as e:
|
|
print(f"⚠️ Error reading {skill_path}: {str(e)}", file=sys.stderr)
|
|
|
|
# Sort data
|
|
if sort_by == 'date':
|
|
# Sort by date_added (newest first), then by name
|
|
skills_data.sort(key=lambda x: (x['date_added'] or '0000-00-00', x['name']), reverse=True)
|
|
elif sort_by == 'name':
|
|
skills_data.sort(key=lambda x: x['name'])
|
|
|
|
# Prepare report
|
|
report = {
|
|
'generated_at': datetime.now().isoformat(),
|
|
'total_skills': len(skills_data),
|
|
'skills_with_dates': sum(1 for s in skills_data if s['date_added']),
|
|
'skills_without_dates': sum(1 for s in skills_data if not s['date_added']),
|
|
'coverage_percentage': round(
|
|
sum(1 for s in skills_data if s['date_added']) / len(skills_data) * 100 if skills_data else 0,
|
|
1
|
|
),
|
|
'sorted_by': sort_by,
|
|
'skills': skills_data
|
|
}
|
|
|
|
# Output
|
|
if output_file:
|
|
try:
|
|
with open(output_file, 'w', encoding='utf-8') as f:
|
|
json.dump(report, f, indent=2, ensure_ascii=False)
|
|
print(f"✅ Report saved to: {output_file}")
|
|
except Exception as e:
|
|
print(f"❌ Error saving report: {str(e)}")
|
|
return None
|
|
else:
|
|
# Print to stdout
|
|
print(json.dumps(report, indent=2, ensure_ascii=False))
|
|
|
|
return report
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Generate a skills report with date_added metadata",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Examples:
|
|
python generate_skills_report.py
|
|
python generate_skills_report.py --output skills_report.json
|
|
python generate_skills_report.py --sort name --output sorted_skills.json
|
|
"""
|
|
)
|
|
|
|
parser.add_argument('--output', '-o', help='Output file (JSON). If not specified, prints to stdout')
|
|
parser.add_argument('--sort', choices=['date', 'name'], default='date', help='Sort order (default: date)')
|
|
|
|
args = parser.parse_args()
|
|
|
|
generate_skills_report(output_file=args.output, sort_by=args.sort)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|