Files
AirCoding/AirPlan/docs/spec/AirPlanV2/lib/air_runtime/todo_parser.py
AirCoding ae44be31d5 chore: push all design docs, V2 plan specs, and current working state
Includes AirPlan design documents, AircOding-alpha1-plan, AirPlanV2,
AirPlan-ParaV2, AirPlan-Para V1 reference docs, and all working code
changes across packages.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-12 17:12:29 +08:00

100 lines
3.4 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
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.
"""
TODO 解析器 — V2 修复 P1-8列索引从表头推导不再硬编码 cells[1]/cells[6]/cells[7]。
"""
from __future__ import annotations
import re
from dataclasses import dataclass
from pathlib import Path
@dataclass
class TodoTask:
task_id: str
task: str
files_dirs: str = ""
status: str = "TODO"
done_when: str = ""
validations: str = ""
adr: str = ""
def parse_tasks(todo_path: Path) -> list[TodoTask]:
"""从 todo.md 解析任务列表,动态检测列索引。"""
if not todo_path.exists():
return []
content = todo_path.read_text(encoding="utf-8")
lines = [l.strip() for l in content.splitlines() if l.strip()]
# 找到 Markdown 表格头
header_idx = -1
for i, line in enumerate(lines):
if line.startswith("|") and "Task" in line and "Status" in line:
header_idx = i
break
if header_idx < 0:
return []
# 解析列名
header_line = lines[header_idx]
header_cols = [c.strip() for c in header_line.split("|") if c.strip()]
# 建立列名 → 索引映射
col_map = {}
for idx, col_name in enumerate(header_cols):
col_name_lower = col_name.lower()
if "task" in col_name_lower:
col_map["task"] = idx
elif "status" in col_name_lower:
col_map["status"] = idx
elif "files" in col_name_lower or "dir" in col_name_lower:
col_map["files_dirs"] = idx
elif "done" in col_name_lower or "when" in col_name_lower:
col_map["done_when"] = idx
elif "valid" in col_name_lower:
col_map["validations"] = idx
elif "adr" in col_name_lower:
col_map["adr"] = idx
# 跳过表头和分隔符
tasks = []
for line in lines[header_idx + 2:]:
if not line.startswith("|"):
continue
cells = [c.strip() for c in line.split("|") if len(c.strip()) > 0]
if not cells:
continue
task_cell = cells[col_map.get("task", 0)] if col_map.get("task", 0) < len(cells) else ""
# 优先提取 [T-xxx] 方括号格式的 ID若没有则尝试从开头提取 G-001/H-000 类短 ID
tid_match = re.match(r"\[([A-Za-z0-9_\-\.]+)\]", task_cell)
if tid_match:
task_id = tid_match.group(1)
else:
short_match = re.match(r"^([A-Z]+-\d+[a-z]*)", task_cell)
task_id = short_match.group(1) if short_match else task_cell
task = task_cell
status = cells[col_map.get("status", 1)] if col_map.get("status", 1) < len(cells) else "TODO"
files_dirs = cells[col_map.get("files_dirs", 2)] if col_map.get("files_dirs", 2) < len(cells) else ""
done_when = cells[col_map.get("done_when", 3)] if col_map.get("done_when", 3) < len(cells) else ""
validations = cells[col_map.get("validations", 4)] if col_map.get("validations", 4) < len(cells) else ""
adr = cells[col_map.get("adr", 5)] if col_map.get("adr", 5) < len(cells) else ""
# 清理标记
task_id = re.sub(r"^\[|\]$", "", task_id).strip()
if task_id and task_id != "---":
tasks.append(TodoTask(
task_id=task_id,
task=task,
files_dirs=files_dirs,
status=status.upper() if status else "TODO",
done_when=done_when,
validations=validations,
adr=adr,
))
return tasks