Initial commit
This commit is contained in:
122
skills/project-status-report/scripts/work_items.py
Executable file
122
skills/project-status-report/scripts/work_items.py
Executable file
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Work Items Scanner
|
||||
|
||||
Scans code for TODOs, FIXMEs, and loads session objectives.
|
||||
"""
|
||||
|
||||
import re
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
class WorkItemsScanner:
|
||||
"""Scan project for work items"""
|
||||
|
||||
def __init__(self, project_path: str = "."):
|
||||
"""Initialize scanner with project path"""
|
||||
self.project_path = Path(project_path)
|
||||
|
||||
def scan_code_markers(self, patterns: List[str] = None) -> Dict[str, List[Dict]]:
|
||||
"""Scan code files for TODO/FIXME markers"""
|
||||
if patterns is None:
|
||||
patterns = ["*.py", "*.js", "*.ts", "*.tsx", "*.java", "*.go", "*.rs"]
|
||||
|
||||
todos = []
|
||||
fixmes = []
|
||||
|
||||
for pattern in patterns:
|
||||
for file_path in self.project_path.rglob(pattern):
|
||||
# Skip test files and node_modules
|
||||
if "test" in str(file_path) or "node_modules" in str(file_path):
|
||||
continue
|
||||
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
for line_num, line in enumerate(f, 1):
|
||||
# Match TODO: or TODO -
|
||||
if re.search(r'TODO[:\-]', line, re.IGNORECASE):
|
||||
comment = line.strip()
|
||||
todos.append({
|
||||
"file": str(file_path.relative_to(self.project_path)),
|
||||
"line": line_num,
|
||||
"text": comment
|
||||
})
|
||||
# Match FIXME: or FIXME -
|
||||
if re.search(r'FIXME[:\-]', line, re.IGNORECASE):
|
||||
comment = line.strip()
|
||||
fixmes.append({
|
||||
"file": str(file_path.relative_to(self.project_path)),
|
||||
"line": line_num,
|
||||
"text": comment
|
||||
})
|
||||
except (IOError, UnicodeDecodeError):
|
||||
# Skip files we can't read
|
||||
continue
|
||||
|
||||
return {
|
||||
"todos": todos[:20], # Limit to first 20
|
||||
"fixmes": fixmes[:20]
|
||||
}
|
||||
|
||||
def load_session_objectives(self) -> List[Dict]:
|
||||
"""Load objectives from .sessions/state.json"""
|
||||
state_file = self.project_path / ".sessions" / "state.json"
|
||||
|
||||
if not state_file.exists():
|
||||
return []
|
||||
|
||||
try:
|
||||
with open(state_file) as f:
|
||||
state = json.load(f)
|
||||
|
||||
objectives = state.get("objectives", [])
|
||||
# Convert to list of dicts if it's a simple list
|
||||
if objectives and isinstance(objectives[0], str):
|
||||
return [{"text": obj, "completed": False} for obj in objectives]
|
||||
|
||||
return objectives
|
||||
except (json.JSONDecodeError, IOError, KeyError):
|
||||
return []
|
||||
|
||||
def generate_report(self) -> str:
|
||||
"""Generate work items section"""
|
||||
lines = []
|
||||
lines.append("## 📋 Open Work Items")
|
||||
lines.append("")
|
||||
|
||||
# Session objectives
|
||||
objectives = self.load_session_objectives()
|
||||
if objectives:
|
||||
lines.append("**Session Objectives**:")
|
||||
for obj in objectives:
|
||||
status = "[x]" if obj.get("completed") else "[ ]"
|
||||
text = obj.get("text", "Unknown objective")
|
||||
lines.append(f"- {status} {text}")
|
||||
lines.append("")
|
||||
|
||||
# Code markers
|
||||
markers = self.scan_code_markers()
|
||||
todos = markers["todos"]
|
||||
fixmes = markers["fixmes"]
|
||||
|
||||
if todos:
|
||||
lines.append("**TODOs in Code**:")
|
||||
for todo in todos[:5]: # Show first 5
|
||||
lines.append(f"- {todo['file']}:{todo['line']} - {todo['text'][:80]}")
|
||||
if len(todos) > 5:
|
||||
lines.append(f"- ... and {len(todos) - 5} more")
|
||||
lines.append("")
|
||||
|
||||
if fixmes:
|
||||
lines.append("**FIXMEs in Code**:")
|
||||
for fixme in fixmes[:5]:
|
||||
lines.append(f"- {fixme['file']}:{fixme['line']} - {fixme['text'][:80]}")
|
||||
if len(fixmes) > 5:
|
||||
lines.append(f"- ... and {len(fixmes) - 5} more")
|
||||
lines.append("")
|
||||
|
||||
lines.append(f"**Summary**: {len(todos)} TODOs, {len(fixmes)} FIXMEs")
|
||||
|
||||
return "\n".join(lines)
|
||||
Reference in New Issue
Block a user