scripts-repair-skill-frontmatter-v3
#!/usr/bin/env python3 """ Repair corrupted skill frontmatter v3 - handles missing tags list.
The issue: tags: is followed immediately by cef_track without tag items in between.
Usage: python3 scripts/repair-skill-frontmatter-v3.py [--dry-run] """
import argparse import sys from pathlib import Path
def repair_skill_file(skill_path: Path, dry_run: bool = False) -> bool: """Repair a single skill file.""" content = skill_path.read_text()
# Check if file has cef_track
if 'cef_track:' not in content:
return False
lines = content.split('\n')
# Check for corruption: tags: followed by cef_track: without list items in between
tags_idx = None
cef_idx = None
loose_tags = []
for i, line in enumerate(lines):
stripped = line.strip()
if stripped == 'tags:':
tags_idx = i
elif stripped.startswith('cef_track:'):
cef_idx = i
# Check if there's a loose tag on the next line (wrong indentation)
if i + 1 < len(lines) and lines[i + 1].strip().startswith('- ') and not lines[i + 1].startswith(' '):
j = i + 1
while j < len(lines) and lines[j].strip().startswith('- '):
loose_tags.append((j, ' ' + lines[j].strip()))
j += 1
break
if tags_idx is None or cef_idx is None:
return False
# Check if there are tag items between tags: and cef_track:
has_tag_items = False
for i in range(tags_idx + 1, cef_idx):
if lines[i].strip().startswith('- '):
has_tag_items = True
break
if has_tag_items and not loose_tags:
# Already OK
return False
if not has_tag_items and not loose_tags:
# No tag items at all, just needs empty line
pass
# Repair: insert tag items after tags: and before cef_track:
new_lines = []
for i, line in enumerate(lines):
if i == tags_idx + 1 and loose_tags:
# Insert loose tags here with proper indentation
new_lines.append(line) # The original line (might be cef_track)
for _, tag in loose_tags:
new_lines.append(tag)
elif i in [idx for idx, _ in loose_tags]:
# Skip loose tags at wrong position
continue
else:
new_lines.append(line)
new_content = '\n'.join(new_lines)
if dry_run:
print(f"Would repair: {skill_path}")
return True
skill_path.write_text(new_content)
print(f"✅ Repaired: {skill_path}")
return True
def main(): parser = argparse.ArgumentParser(description="Repair corrupted skill frontmatter v3") parser.add_argument("--dry-run", action="store_true", help="Preview changes without applying") args = parser.parse_args()
skills_dir = Path("skills")
skill_files = list(skills_dir.glob("*/SKILL.md"))
print(f"Checking {len(skill_files)} skill files...")
print(f"Mode: {'DRY RUN' if args.dry_run else 'LIVE'}")
print()
repaired = 0
checked = 0
for skill_path in skill_files:
checked += 1
if repair_skill_file(skill_path, args.dry_run):
repaired += 1
print()
print(f"Checked: {checked} files")
print(f"Repaired: {repaired} files")
if args.dry_run:
print("\nRun without --dry-run to apply repairs")
return 0
if name == "main": sys.exit(main())