borisxdave 0.3.0__py3-none-any.whl → 0.3.2__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.
- boris.py +435 -6
- boris_prompt_data.py +194 -0
- {borisxdave-0.3.0.dist-info → borisxdave-0.3.2.dist-info}/METADATA +1 -1
- borisxdave-0.3.2.dist-info/RECORD +14 -0
- {borisxdave-0.3.0.dist-info → borisxdave-0.3.2.dist-info}/top_level.txt +2 -0
- engine.py +212 -14
- file_lock.py +123 -0
- prompts.py +31 -7
- state.py +80 -1
- borisxdave-0.3.0.dist-info/RECORD +0 -12
- {borisxdave-0.3.0.dist-info → borisxdave-0.3.2.dist-info}/WHEEL +0 -0
- {borisxdave-0.3.0.dist-info → borisxdave-0.3.2.dist-info}/entry_points.txt +0 -0
prompts.py
CHANGED
|
@@ -29,17 +29,41 @@ _boris_prompt_cache = None
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def _load_boris_prompt() -> str:
|
|
32
|
-
"""Load Boris's management prompt from boris_prompt.md. Cached after first load.
|
|
32
|
+
"""Load Boris's management prompt from boris_prompt.md. Cached after first load.
|
|
33
|
+
|
|
34
|
+
Searches multiple locations to handle both editable installs (source dir)
|
|
35
|
+
and regular pip installs (site-packages).
|
|
36
|
+
"""
|
|
33
37
|
global _boris_prompt_cache
|
|
34
38
|
if _boris_prompt_cache is not None:
|
|
35
39
|
return _boris_prompt_cache
|
|
40
|
+
|
|
41
|
+
search_paths = [
|
|
42
|
+
_BORIS_PROMPT_PATH, # Next to this .py file (editable install / source)
|
|
43
|
+
os.path.join(sys.prefix, "boris_prompt.md"), # sys.prefix (some data_files installs)
|
|
44
|
+
os.path.join(os.path.dirname(shutil.which("boris") or ""), "boris_prompt.md"), # Next to boris script
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
for path in search_paths:
|
|
48
|
+
try:
|
|
49
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
50
|
+
_boris_prompt_cache = f.read().strip()
|
|
51
|
+
logger.debug("Loaded Boris prompt from %s (%d chars)", path, len(_boris_prompt_cache))
|
|
52
|
+
return _boris_prompt_cache
|
|
53
|
+
except (FileNotFoundError, OSError, TypeError):
|
|
54
|
+
continue
|
|
55
|
+
|
|
56
|
+
# Fallback: use embedded prompt from boris_prompt_data.py (always available after pip install)
|
|
36
57
|
try:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
logger.debug("Loaded Boris prompt
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
58
|
+
from boris_prompt_data import BORIS_PROMPT
|
|
59
|
+
_boris_prompt_cache = BORIS_PROMPT.strip()
|
|
60
|
+
logger.debug("Loaded Boris prompt from embedded boris_prompt_data.py (%d chars)", len(_boris_prompt_cache))
|
|
61
|
+
return _boris_prompt_cache
|
|
62
|
+
except ImportError:
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
logger.warning("boris_prompt.md not found in any search path: %s", search_paths)
|
|
66
|
+
_boris_prompt_cache = ""
|
|
43
67
|
return _boris_prompt_cache
|
|
44
68
|
|
|
45
69
|
# Regex to strip ANSI escape codes
|
state.py
CHANGED
|
@@ -67,13 +67,92 @@ class State:
|
|
|
67
67
|
|
|
68
68
|
|
|
69
69
|
def save(state: State) -> None:
|
|
70
|
-
"""Save state to {project_dir}/.boris/state.json."""
|
|
70
|
+
"""Save state to {project_dir}/.boris/state.json and plan.md."""
|
|
71
71
|
state_dir = os.path.join(state.project_dir, config.STATE_DIR)
|
|
72
72
|
os.makedirs(state_dir, exist_ok=True)
|
|
73
73
|
state_path = os.path.join(state_dir, config.STATE_FILE)
|
|
74
74
|
state.updated_at = datetime.now().isoformat()
|
|
75
75
|
with open(state_path, "w", encoding="utf-8") as f:
|
|
76
76
|
json.dump(asdict(state), f, indent=2, ensure_ascii=False)
|
|
77
|
+
# Also export a human-readable markdown plan
|
|
78
|
+
_save_plan_md(state)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _save_plan_md(state: State) -> None:
|
|
82
|
+
"""Export the plan as a readable markdown file in the project directory."""
|
|
83
|
+
plan = state.plan
|
|
84
|
+
if not plan or not plan.milestones:
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
status_icon = {
|
|
88
|
+
"completed": "[x]",
|
|
89
|
+
"in_progress": "[-]",
|
|
90
|
+
"skipped": "[~]",
|
|
91
|
+
"pending": "[ ]",
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
lines = []
|
|
95
|
+
lines.append(f"# Boris Plan")
|
|
96
|
+
lines.append("")
|
|
97
|
+
lines.append(f"**Task:** {plan.task}")
|
|
98
|
+
lines.append(f"**Created:** {plan.created_at}")
|
|
99
|
+
lines.append(f"**Updated:** {state.updated_at}")
|
|
100
|
+
lines.append(f"**Mode:** {'Turbo (parallel)' if state.turbo else 'Sequential'}")
|
|
101
|
+
lines.append("")
|
|
102
|
+
|
|
103
|
+
# Summary counts
|
|
104
|
+
counts = {}
|
|
105
|
+
for m in plan.milestones:
|
|
106
|
+
counts[m.status] = counts.get(m.status, 0) + 1
|
|
107
|
+
total = len(plan.milestones)
|
|
108
|
+
done = counts.get("completed", 0)
|
|
109
|
+
lines.append(f"**Progress:** {done}/{total} milestones completed")
|
|
110
|
+
if counts.get("skipped", 0):
|
|
111
|
+
lines.append(f"**Skipped:** {counts['skipped']}")
|
|
112
|
+
lines.append("")
|
|
113
|
+
lines.append("---")
|
|
114
|
+
lines.append("")
|
|
115
|
+
|
|
116
|
+
# Milestone list
|
|
117
|
+
lines.append("## Milestones")
|
|
118
|
+
lines.append("")
|
|
119
|
+
for m in plan.milestones:
|
|
120
|
+
icon = status_icon.get(m.status, "[ ]")
|
|
121
|
+
lines.append(f"### {icon} {m.id}: {m.title}")
|
|
122
|
+
lines.append("")
|
|
123
|
+
if m.depends_on:
|
|
124
|
+
lines.append(f"**Depends on:** {', '.join(m.depends_on)}")
|
|
125
|
+
lines.append(f"**Status:** {m.status}")
|
|
126
|
+
if m.completed_at:
|
|
127
|
+
lines.append(f"**Completed:** {m.completed_at}")
|
|
128
|
+
lines.append("")
|
|
129
|
+
lines.append(m.description)
|
|
130
|
+
lines.append("")
|
|
131
|
+
|
|
132
|
+
if m.acceptance_criteria:
|
|
133
|
+
lines.append("**Acceptance Criteria:**")
|
|
134
|
+
for ac in m.acceptance_criteria:
|
|
135
|
+
lines.append(f"- {ac}")
|
|
136
|
+
lines.append("")
|
|
137
|
+
|
|
138
|
+
if m.files_to_create:
|
|
139
|
+
lines.append(f"**Files to create:** {len(m.files_to_create)}")
|
|
140
|
+
for f_path in m.files_to_create:
|
|
141
|
+
lines.append(f"- `{f_path}`")
|
|
142
|
+
lines.append("")
|
|
143
|
+
|
|
144
|
+
if m.files_to_modify:
|
|
145
|
+
lines.append(f"**Files to modify:** {len(m.files_to_modify)}")
|
|
146
|
+
for f_path in m.files_to_modify:
|
|
147
|
+
lines.append(f"- `{f_path}`")
|
|
148
|
+
lines.append("")
|
|
149
|
+
|
|
150
|
+
lines.append("---")
|
|
151
|
+
lines.append("")
|
|
152
|
+
|
|
153
|
+
md_path = os.path.join(state.project_dir, config.STATE_DIR, "plan.md")
|
|
154
|
+
with open(md_path, "w", encoding="utf-8") as f:
|
|
155
|
+
f.write("\n".join(lines))
|
|
77
156
|
|
|
78
157
|
|
|
79
158
|
def load(project_dir: str) -> Optional[State]:
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
boris.py,sha256=xStvDOxKqbvQiNsQORr4qcD5wEiruNIcm7v_fFPjwZ8,44667
|
|
2
|
-
config.py,sha256=KfFKyCGasdm1yBvIRFv-ykzA_oRo-zu1Euu9YC7V1Cg,324
|
|
3
|
-
engine.py,sha256=LuVUwtlE1j8K3MOIH_pozB6Vy04_kPJs7Q0l9g-ePa8,27875
|
|
4
|
-
git_manager.py,sha256=BuuTT4naPb5-jLhOik1xHM2ztzuKvJ_bnecZmlYgwFs,8493
|
|
5
|
-
planner.py,sha256=UrU--kBvzvyD1gOVxIn-kdbJiu8tt4rcowsln66WkGw,5670
|
|
6
|
-
prompts.py,sha256=oBVDZ0dG50FcSBa4F9GTkCPxmMB653BsK3RShoGcWUM,34182
|
|
7
|
-
state.py,sha256=8MeUiDwL9ecgD9N6RhNUQf_P-qE7HeG3WlgL_SbRQic,3160
|
|
8
|
-
borisxdave-0.3.0.dist-info/METADATA,sha256=qP3P-mpjm-BaQuJBXasveJ4L_wGNH9S-pIhNLAdNVd4,133
|
|
9
|
-
borisxdave-0.3.0.dist-info/WHEEL,sha256=hPN0AlP2dZM_3ZJZWP4WooepkmU9wzjGgCLCeFjkHLA,92
|
|
10
|
-
borisxdave-0.3.0.dist-info/entry_points.txt,sha256=a6FLWgxiQjGMJIRSV5sDxaaaaQchunm04ZuzX8N7-6I,61
|
|
11
|
-
borisxdave-0.3.0.dist-info/top_level.txt,sha256=zNzzkbLJWzWpTjJTQsnbdOBypcA4XpioE1dEgWZVBx4,54
|
|
12
|
-
borisxdave-0.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|