Coverage for src/workstack/status/collectors/plan.py: 100%
30 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-19 09:31 -0400
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-19 09:31 -0400
1"""Plan file collector."""
3from pathlib import Path
5from workstack.core.context import WorkstackContext
6from workstack.status.collectors.base import StatusCollector
7from workstack.status.models.status_data import PlanStatus
10class PlanFileCollector(StatusCollector):
11 """Collects information about .PLAN.md file."""
13 @property
14 def name(self) -> str:
15 """Name identifier for this collector."""
16 return "plan"
18 def is_available(self, ctx: WorkstackContext, worktree_path: Path) -> bool:
19 """Check if .PLAN.md exists.
21 Args:
22 ctx: Workstack context
23 worktree_path: Path to worktree
25 Returns:
26 True if .PLAN.md exists
27 """
28 plan_path = worktree_path / ".PLAN.md"
29 return plan_path.exists()
31 def collect(
32 self, ctx: WorkstackContext, worktree_path: Path, repo_root: Path
33 ) -> PlanStatus | None:
34 """Collect plan file information.
36 Args:
37 ctx: Workstack context
38 worktree_path: Path to worktree
39 repo_root: Repository root path
41 Returns:
42 PlanStatus with file information or None if collection fails
43 """
44 plan_path = worktree_path / ".PLAN.md"
46 if not plan_path.exists():
47 return PlanStatus(
48 exists=False,
49 path=None,
50 summary=None,
51 line_count=0,
52 first_lines=[],
53 )
55 # Read the file
56 content = plan_path.read_text(encoding="utf-8")
57 lines = content.splitlines()
58 line_count = len(lines)
60 # Get first 5 lines
61 first_lines = lines[:5] if len(lines) >= 5 else lines
63 # Extract summary from first few non-empty lines
64 summary_lines = []
65 for line in lines[:10]: # Look at first 10 lines
66 stripped = line.strip()
67 if stripped and not stripped.startswith("#"):
68 summary_lines.append(stripped)
69 if len(summary_lines) >= 2:
70 break
72 summary = " ".join(summary_lines) if summary_lines else None
74 # Truncate summary if too long
75 if summary and len(summary) > 100:
76 summary = summary[:97] + "..."
78 return PlanStatus(
79 exists=True,
80 path=plan_path,
81 summary=summary,
82 line_count=line_count,
83 first_lines=first_lines,
84 )