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.
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
- with open(_BORIS_PROMPT_PATH, "r", encoding="utf-8") as f:
38
- _boris_prompt_cache = f.read().strip()
39
- logger.debug("Loaded Boris prompt: %d chars", len(_boris_prompt_cache))
40
- except FileNotFoundError:
41
- logger.warning("boris_prompt.md not found at %s", _BORIS_PROMPT_PATH)
42
- _boris_prompt_cache = ""
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,,