#!/usr/bin/env python3 # /// script # requires-python = ">=3.9" # /// """ Generate an interactive HTML skill conversion comparison report. Measures original and rebuilt skill directories, combines with LLM-generated analysis (cuts, retained content, verdict), and renders a self-contained HTML report showing the stark before/after comparison. Usage: python3 generate-convert-report.py [-o output.html] [--open] """ from __future__ import annotations import argparse import html as html_lib import json import platform import subprocess import sys from datetime import datetime, timezone from pathlib import Path def measure_skill(skill_path: Path) -> dict: """Measure a skill directory or single file for lines, words, chars, sections, files.""" total_lines = 0 total_words = 0 total_chars = 0 total_sections = 0 md_file_count = 0 non_md_file_count = 0 if skill_path.is_file(): md_files = [skill_path] else: md_files = sorted(skill_path.rglob('*.md')) for f in md_files: content = f.read_text(encoding='utf-8') lines = content.splitlines() total_lines += len(lines) total_words += sum(len(line.split()) for line in lines) total_chars += len(content) total_sections += sum(1 for line in lines if line.startswith('## ')) md_file_count += 1 if skill_path.is_dir(): for f in skill_path.rglob('*'): if f.is_file() and f.suffix != '.md': non_md_file_count += 1 return { 'lines': total_lines, 'words': total_words, 'chars': total_chars, 'sections': total_sections, 'files': md_file_count + non_md_file_count, 'estimated_tokens': int(total_words * 1.3), } def calculate_reductions(original: dict, rebuilt: dict) -> dict: """Calculate percentage reductions for each metric.""" reductions = {} for key in ('lines', 'words', 'chars', 'sections', 'estimated_tokens'): orig_val = original.get(key, 0) new_val = rebuilt.get(key, 0) if orig_val > 0: reductions[key] = f'{round((1 - new_val / orig_val) * 100)}%' else: reductions[key] = 'N/A' return reductions def build_report_data(original_metrics: dict, rebuilt_metrics: dict, analysis: dict, reductions: dict) -> dict: """Assemble the full report data structure.""" return { 'meta': { 'skill_name': analysis.get('skill_name', 'Unknown'), 'original_source': analysis.get('original_source', ''), 'timestamp': datetime.now(timezone.utc).isoformat(), }, 'metrics': { 'original': original_metrics, 'rebuilt': rebuilt_metrics, }, 'reductions': reductions, 'cuts': analysis.get('cuts', []), 'retained': analysis.get('retained', []), 'verdict': analysis.get('verdict', ''), } # ── HTML Template ────────────────────────────────────────────────────────────── HTML_TEMPLATE = r""" BMad Method · Skill Conversion: SKILL_NAME
BMad Method

Skill Conversion:

Metric Original Rebuilt Reduction Comparison
""" def generate_html(report_data: dict) -> str: """Inject report data into the HTML template.""" data_json = json.dumps(report_data, indent=None, ensure_ascii=False) data_tag = f'' html = HTML_TEMPLATE.replace( '