-
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathhud_state.py
More file actions
131 lines (113 loc) · 3.64 KB
/
Copy pathhud_state.py
File metadata and controls
131 lines (113 loc) · 3.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
"""HUD state management for CodingBuddy statusLine (#1087, #1326).
Manages ~/.codingbuddy/hud-state.json shared between hooks.
Uses fcntl.flock() for file-level locking on every IO operation.
"""
import json
import os
from datetime import datetime, timezone
from typing import Any, Dict
# Default values for extended schema fields (#1326, #1364).
_EXTENDED_DEFAULTS: Dict[str, Any] = {
"phase": "ready",
"focus": None,
"executionStrategy": None,
"councilStatus": None,
"blockerCount": 0,
"lastHandoff": None,
# Council UX fields (#1364)
"councilActive": False,
"councilStage": "",
"councilCast": [],
}
try:
import fcntl
HAS_FCNTL = True
except ImportError:
HAS_FCNTL = False
DEFAULT_STATE_FILE = os.path.join(
os.environ.get(
"CLAUDE_PLUGIN_DATA",
os.path.join(os.path.expanduser("~"), ".codingbuddy"),
),
"hud-state.json",
)
def read_hud_state(
state_file: str = DEFAULT_STATE_FILE,
*,
fill_defaults: bool = False,
) -> Dict[str, Any]:
"""Read HUD state from JSON file with shared lock.
Args:
state_file: Path to the state JSON file.
fill_defaults: When True, back-fill missing extended-schema keys
with their defaults so callers always see the full schema.
Returns empty dict on any error (missing file, parse error).
"""
try:
with open(state_file, "r", encoding="utf-8") as f:
if HAS_FCNTL:
fcntl.flock(f.fileno(), fcntl.LOCK_SH)
data: Dict[str, Any] = json.load(f)
except (json.JSONDecodeError, OSError):
return {}
if fill_defaults:
for key, default in _EXTENDED_DEFAULTS.items():
data.setdefault(key, default)
return data
def init_hud_state(
session_id: str,
version: str,
state_file: str = DEFAULT_STATE_FILE,
) -> None:
"""Initialize HUD state for a new session.
Creates parent directory if needed. Overwrites existing state.
"""
now = datetime.now(timezone.utc).isoformat()
data: Dict[str, Any] = {
"sessionStartTimestamp": now,
"sessionId": session_id,
"version": version,
"currentMode": None,
"activeAgent": None,
# Extended schema (#1326)
"phase": "ready",
"focus": None,
"executionStrategy": None,
"councilStatus": None,
"blockerCount": 0,
"lastHandoff": None,
# Council UX fields (#1364)
"councilActive": False,
"councilStage": "",
"councilCast": [],
"updatedAt": now,
}
_locked_write(state_file, data)
def update_hud_state(
state_file: str = DEFAULT_STATE_FILE,
**kwargs: Any,
) -> None:
"""Update HUD state by merging kwargs into existing state.
Atomic read-modify-write under a single exclusive lock.
Silently no-ops on error.
"""
try:
os.makedirs(os.path.dirname(state_file), mode=0o700, exist_ok=True)
with open(state_file, "r+", encoding="utf-8") as f:
if HAS_FCNTL:
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
data = json.load(f)
data.update(kwargs)
data["updatedAt"] = datetime.now(timezone.utc).isoformat()
f.seek(0)
f.truncate()
json.dump(data, f)
except (OSError, json.JSONDecodeError):
pass
def _locked_write(state_file: str, data: Dict[str, Any]) -> None:
"""Write state file with exclusive lock."""
os.makedirs(os.path.dirname(state_file), mode=0o700, exist_ok=True)
with open(state_file, "w", encoding="utf-8") as f:
if HAS_FCNTL:
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
json.dump(data, f)