borisxdave 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
state.py ADDED
@@ -0,0 +1,103 @@
1
+
2
+ """Boris state management - data models and persistence."""
3
+ import json
4
+ import os
5
+ from dataclasses import dataclass, field, asdict
6
+ from datetime import datetime
7
+ from typing import Optional
8
+
9
+ import config
10
+
11
+
12
+ @dataclass
13
+ class Milestone:
14
+ id: str
15
+ title: str
16
+ description: str
17
+ depends_on: list[str]
18
+ acceptance_criteria: list[str]
19
+ files_to_create: list[str]
20
+ files_to_modify: list[str]
21
+ status: str = "pending" # pending | in_progress | completed | skipped
22
+ retry_count: int = 0
23
+ completed_at: Optional[str] = None
24
+
25
+
26
+ @dataclass
27
+ class UIMilestone:
28
+ id: str
29
+ title: str
30
+ description: str
31
+ test_tool: str
32
+ test_commands: list[str]
33
+ acceptance_criteria: list[str]
34
+ issues_found: list[str] = field(default_factory=list)
35
+ issues_fixed: list[str] = field(default_factory=list)
36
+ status: str = "pending"
37
+ retry_count: int = 0
38
+ completed_at: Optional[str] = None
39
+
40
+
41
+ @dataclass
42
+ class UIPlan:
43
+ project_type: str
44
+ test_tool: str
45
+ milestones: list[UIMilestone]
46
+
47
+
48
+ @dataclass
49
+ class Plan:
50
+ task: str
51
+ milestones: list[Milestone]
52
+ created_at: str = field(default_factory=lambda: datetime.now().isoformat())
53
+
54
+
55
+ @dataclass
56
+ class State:
57
+ plan: Plan
58
+ current_milestone_index: int
59
+ project_dir: str
60
+ git_remote: Optional[str]
61
+ no_git: bool
62
+ started_at: str = field(default_factory=lambda: datetime.now().isoformat())
63
+ updated_at: str = field(default_factory=lambda: datetime.now().isoformat())
64
+ phase: str = "structural" # structural | ui_testing
65
+ ui_plan: Optional[UIPlan] = None
66
+
67
+
68
+ def save(state: State) -> None:
69
+ """Save state to {project_dir}/.boris/state.json."""
70
+ state_dir = os.path.join(state.project_dir, config.STATE_DIR)
71
+ os.makedirs(state_dir, exist_ok=True)
72
+ state_path = os.path.join(state_dir, config.STATE_FILE)
73
+ state.updated_at = datetime.now().isoformat()
74
+ with open(state_path, "w", encoding="utf-8") as f:
75
+ json.dump(asdict(state), f, indent=2, ensure_ascii=False)
76
+
77
+
78
+ def load(project_dir: str) -> Optional[State]:
79
+ """Load state from {project_dir}/.boris/state.json. Returns None if not found."""
80
+ state_path = os.path.join(project_dir, config.STATE_DIR, config.STATE_FILE)
81
+ if not os.path.exists(state_path):
82
+ return None
83
+ try:
84
+ with open(state_path, "r", encoding="utf-8") as f:
85
+ data = json.load(f)
86
+ # Reconstruct dataclass instances from dicts
87
+ milestones = [Milestone(**m) for m in data["plan"]["milestones"]]
88
+ plan = Plan(
89
+ task=data["plan"]["task"],
90
+ milestones=milestones,
91
+ created_at=data["plan"]["created_at"],
92
+ )
93
+ return State(
94
+ plan=plan,
95
+ current_milestone_index=data["current_milestone_index"],
96
+ project_dir=data["project_dir"],
97
+ git_remote=data.get("git_remote"),
98
+ no_git=data.get("no_git", False),
99
+ started_at=data.get("started_at", ""),
100
+ updated_at=data.get("updated_at", ""),
101
+ )
102
+ except (json.JSONDecodeError, KeyError, TypeError):
103
+ return None