BMAD-METHOD/scripts/lib/add-status-fields.py

113 lines
3.4 KiB
Python
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
Add Status field to story files that are missing it.
Uses sprint-status.yaml as source of truth.
"""
import re
from pathlib import Path
from typing import Dict
def load_sprint_status(path: str = "docs/sprint-artifacts/sprint-status.yaml") -> Dict[str, str]:
"""Load story statuses from sprint-status.yaml"""
with open(path) as f:
lines = f.readlines()
statuses = {}
in_dev_status = False
for line in lines:
if 'development_status:' in line:
in_dev_status = True
continue
if in_dev_status:
# Check if we've left development_status section
if line.strip() and not line.startswith(' ') and not line.startswith('#'):
break
# Parse story line: " story-id: status # comment"
match = re.match(r' ([a-z0-9-]+):\s*(\S+)', line)
if match:
story_id, status = match.groups()
statuses[story_id] = status
return statuses
def add_status_to_story(story_file: Path, status: str) -> bool:
"""Add Status field to story file if missing"""
content = story_file.read_text()
# Check if Status field already exists (handles both "Status:" and "**Status:**")
if re.search(r'^\*?\*?Status:', content, re.MULTILINE | re.IGNORECASE):
return False # Already has Status field
# Find the first section after the title (usually ## Story or ## Description)
# Insert Status field before that
lines = content.split('\n')
# Find insertion point (after title, before first ## section)
insert_idx = None
for idx, line in enumerate(lines):
if line.startswith('# ') and idx == 0:
# Title line - keep looking
continue
if line.startswith('##'):
# Found first section - insert before it
insert_idx = idx
break
if insert_idx is None:
# No ## sections found, insert after title
insert_idx = 1
# Insert blank line, Status field, blank line
lines.insert(insert_idx, '')
lines.insert(insert_idx + 1, f'**Status:** {status}')
lines.insert(insert_idx + 2, '')
# Write back
story_file.write_text('\n'.join(lines))
return True
def main():
story_dir = Path("docs/sprint-artifacts")
statuses = load_sprint_status()
added = 0
skipped = 0
missing = 0
for story_file in sorted(story_dir.glob("*.md")):
story_id = story_file.stem
# Skip special files
if (story_id.startswith('.') or
story_id.startswith('EPIC-') or
'COMPLETION' in story_id.upper() or
'SUMMARY' in story_id.upper() or
'REPORT' in story_id.upper() or
'README' in story_id.upper()):
continue
if story_id not in statuses:
print(f"⚠️ {story_id}: Not in sprint-status.yaml")
missing += 1
continue
status = statuses[story_id]
if add_status_to_story(story_file, status):
print(f"{story_id}: Added Status: {status}")
added += 1
else:
skipped += 1
print()
print(f"✅ Added Status field to {added} stories")
print(f" Skipped {skipped} stories (already have Status)")
print(f"⚠️ {missing} stories not in sprint-status.yaml")
if __name__ == '__main__':
main()