feat(plan_progress): auto-detect env for blocked plans

This commit is contained in:
csh
2026-01-29 14:54:12 +08:00
parent 0d9a8ec465
commit a85453439f
3 changed files with 135 additions and 22 deletions
+40 -6
View File
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
import platform
import re
import sys
from pathlib import Path
@@ -8,9 +9,9 @@ PLAN_STATUS_START = "<!-- plan-status:start -->"
PLAN_STATUS_END = "<!-- plan-status:end -->"
PLAN_FILE_RE = re.compile(r"^(\d{4}-\d{2}-\d{2})-.+\.md$")
PLAN_LINE_RE = re.compile(
r"^- \[(?P<check>[ xX])\] `(?P<plan>[^`]+)` (?P<status>done|blocked|pending)(?:: (?P<note>.*))?$"
r"^- \[(?P<check>[ xX])\] `(?P<plan>[^`]+)` (?P<status>done|blocked|pending|in-progress|skipped)(?:: (?P<note>.*))?$"
)
VALID_STATUSES = {"done", "blocked", "pending"}
VALID_STATUSES = {"done", "blocked", "pending", "in-progress", "skipped"}
def usage() -> str:
@@ -22,7 +23,7 @@ def usage() -> str:
"Options:\n"
" -plans DIR\n"
" -plan PATH\n"
" -status done|blocked|pending\n"
" -status done|blocked|pending|in-progress|skipped\n"
" -progress FILE\n"
" -note TEXT\n"
" -h, -help Show this help.\n"
@@ -64,6 +65,12 @@ def render_plan_line(plan_key: str, status: str, note: Optional[str]) -> str:
suffix += f": {note}"
elif status == "pending":
suffix = "pending"
elif status == "in-progress":
suffix = "in-progress"
elif status == "skipped":
suffix = "skipped"
if note:
suffix += f": {note}"
else:
suffix = "done"
return f"- [{checked}] `{plan_key}` {suffix}"
@@ -125,6 +132,24 @@ def render_progress_lines(plans: list[str]) -> list[str]:
return lines
ENV_BLOCKED_RE = re.compile(r"^env:([^:]+):(.+)$")
def parse_env_blocked_note(note: Optional[str]) -> Optional[tuple[str, str]]:
"""Parse 'env:windows:Task2,Task4' format. Returns (env, tasks) or None."""
if not note:
return None
match = ENV_BLOCKED_RE.match(note)
if match:
return match.group(1), match.group(2)
return None
def detect_env() -> Optional[str]:
mapping = {"windows": "windows", "linux": "linux", "darwin": "darwin"}
return mapping.get(platform.system().lower())
def select_plan(plans_dir: Path, progress_path: Path) -> tuple[int, str]:
if not plans_dir.is_dir():
return 2, f"ERROR: plans dir not found: {plans_dir}"
@@ -155,10 +180,19 @@ def select_plan(plans_dir: Path, progress_path: Path) -> tuple[int, str]:
progress_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
entries = parse_entries(lines, start_idx, end_idx)
for plan_key, status, _, _ in entries:
if status == "pending":
for plan_key, status, note, _ in entries:
if status in ("pending", "in-progress"):
return 0, (plans_dir / plan_key).as_posix()
# Check for env-blocked Plans if current environment is detected
current_env = detect_env()
if current_env:
for plan_key, status, note, _ in entries:
if status == "blocked":
env_info = parse_env_blocked_note(note)
if env_info and env_info[0] == current_env:
return 0, (plans_dir / plan_key).as_posix()
return 2, "ERROR: no pending plans"
@@ -186,7 +220,7 @@ def record_status(plan: str, status: str, progress_path: Path, note: Optional[st
entries = parse_entries(lines, start_idx, end_idx)
rendered_note = None
if status == "blocked" and note:
if status in ("blocked", "skipped") and note:
rendered_note = normalize_note(note)
updated_line = render_plan_line(plan_key, status, rendered_note)