✨ feat(plan_progress): auto-detect env for blocked plans
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user