borisxdave 0.3.3__tar.gz → 0.3.5__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: borisxdave
3
- Version: 0.3.3
3
+ Version: 0.3.5
4
4
  Summary: Boris - Autonomous Project Orchestrator
5
5
  Requires-Python: >=3.8
6
6
  Dynamic: requires-python
@@ -14,6 +14,8 @@ if hasattr(sys.stdout, "reconfigure"):
14
14
  pass
15
15
  os.environ["PYTHONUNBUFFERED"] = "1"
16
16
 
17
+ import threading
18
+ import time as _time
17
19
  import engine
18
20
  import git_manager
19
21
  import prompts
@@ -811,30 +813,39 @@ def _print_swarm_dashboard(project_dir: str, batch_num: int, batch_milestones: l
811
813
  return
812
814
 
813
815
  active = sum(1 for s in statuses.values() if s.get("state") in ("starting", "working"))
816
+ done = sum(1 for s in statuses.values() if s.get("state") == "done")
814
817
  total_actions = sum(s.get("actions", 0) for s in statuses.values())
815
818
 
819
+ elapsed = ""
820
+ started_times = [s.get("started_at", 0) for s in statuses.values() if s.get("started_at")]
821
+ if started_times:
822
+ elapsed_sec = int(_time.time() - min(started_times))
823
+ elapsed = f" | Elapsed: {elapsed_sec // 60}m {elapsed_sec % 60}s"
824
+
816
825
  print(flush=True)
817
- print("=== BORIS SWARM STATUS ===", flush=True)
818
- print(f"Batch {batch_num} | Workers: {active} active / {len(statuses)} total | Actions: {total_actions}", flush=True)
819
- print(flush=True)
826
+ print("╔══════════════════════════════════════════════════════════════════╗", flush=True)
827
+ print(f"║ BORIS SWARM DASHBOARD │ Batch {batch_num} │ {active} active / {done} done / {len(statuses)} total{elapsed}", flush=True)
828
+ print("╠══════════════════════════════════════════════════════════════════╣", flush=True)
820
829
 
821
830
  for m in batch_milestones:
822
831
  status = statuses.get(m.id, {})
823
- state = status.get("state", "unknown")
832
+ state = status.get("state", "waiting")
824
833
  actions = status.get("actions", 0)
825
834
  reasoning = status.get("reasoning_blocks", 0)
826
835
  interrupts = status.get("interrupts", 0)
827
836
  last = status.get("last_action", "")
828
837
 
829
838
  # Progress indicator based on reasoning blocks (rough proxy)
830
- bar_len = min(reasoning, 10)
831
- bar = "#" * bar_len + "-" * (10 - bar_len)
839
+ bar_len = min(reasoning, 20)
840
+ bar = "" * bar_len + "" * (20 - bar_len)
832
841
 
842
+ state_icons = {"starting": "⏳", "working": "⚙️", "done": "✅", "failed": "❌"}
843
+ icon = state_icons.get(state, "⏸️")
833
844
  state_str = state.upper()
834
- interrupt_str = f" | {interrupts} interrupts" if interrupts > 0 else ""
835
- print(f"[{m.id}] {m.title[:30]:<30} [{bar}] {state_str} | {actions} actions{interrupt_str}", flush=True)
845
+ interrupt_str = f" {interrupts} interrupts" if interrupts > 0 else ""
846
+ print(f"║ {icon} [{m.id}] {m.title[:28]:<28} [{bar}] {state_str:>8} {actions:>3} actions{interrupt_str}", flush=True)
836
847
  if last:
837
- print(f" Last: {last}", flush=True)
848
+ print(f"║ └─ {last[:60]}", flush=True)
838
849
 
839
850
  # Show file locks if file_lock.py is available
840
851
  try:
@@ -843,14 +854,29 @@ def _print_swarm_dashboard(project_dir: str, batch_num: int, batch_milestones: l
843
854
  locks = flm.get_locked_files()
844
855
  if locks:
845
856
  lock_strs = [f"{os.path.basename(f)} ({owner})" for f, owner in locks.items()]
846
- print(f"\nFile locks: {', '.join(lock_strs)}", flush=True)
857
+ print(f"║ 🔒 Locks: {', '.join(lock_strs)}", flush=True)
847
858
  except ImportError:
848
859
  pass
849
860
 
850
- print("===========================", flush=True)
861
+ print("╚══════════════════════════════════════════════════════════════════╝", flush=True)
851
862
  print(flush=True)
852
863
 
853
864
 
865
+ def _start_live_dashboard(project_dir: str, batch_num: int, batch_milestones: list, interval: float = 10.0):
866
+ """Start a background thread that prints the swarm dashboard periodically."""
867
+ stop_event = threading.Event()
868
+
869
+ def _loop():
870
+ while not stop_event.is_set():
871
+ stop_event.wait(interval)
872
+ if not stop_event.is_set():
873
+ _print_swarm_dashboard(project_dir, batch_num, batch_milestones)
874
+
875
+ t = threading.Thread(target=_loop, daemon=True)
876
+ t.start()
877
+ return stop_event
878
+
879
+
854
880
  def _apply_preset(args):
855
881
  """Apply a swarm preset to args if specified. Preset implies --swarm."""
856
882
  if not args.preset:
@@ -907,15 +933,29 @@ def main():
907
933
  print("[Boris] Error: No saved state found. Cannot resume.", flush=True)
908
934
  sys.exit(1)
909
935
  plan = st.plan
910
- start_index = st.current_milestone_index
936
+
937
+ # Reset skipped milestones to pending so they get retried
938
+ retried = []
939
+ for m in plan.milestones:
940
+ if m.status == "skipped":
941
+ m.status = "pending"
942
+ retried.append(m.id)
943
+ if retried:
944
+ print(f"[Boris] Retrying previously skipped milestones: {', '.join(retried)}", flush=True)
945
+ logger.info("Reset skipped milestones to pending: %s", retried)
946
+
947
+ # Start from 0 so we scan all milestones (completed ones are skipped automatically)
948
+ start_index = 0
949
+ st.current_milestone_index = 0
950
+ state_module.save(st)
911
951
 
912
952
  # If already in UI testing phase, skip straight to UI loop
913
953
  if st.phase == "ui_testing":
914
954
  print(f"[Boris] Resuming UI Testing phase...", flush=True)
915
955
  logger.info("Resuming UI testing phase")
916
956
  else:
917
- print(f"[Boris] Resuming from milestone {start_index + 1}...", flush=True)
918
- logger.info("Resuming from milestone %d", start_index + 1)
957
+ print(f"[Boris] Resuming - {len(retried)} skipped milestone(s) queued for retry...", flush=True)
958
+ logger.info("Resuming with %d milestones to retry", len(retried))
919
959
  elif args.exec_only:
920
960
  # Exec-only mode - load an existing plan file and execute it
921
961
  plan_file = os.path.abspath(args.exec_only)
@@ -1127,11 +1167,14 @@ def main():
1127
1167
  tasks.append((prompt, m))
1128
1168
  prompt_map[m.id] = prompt
1129
1169
 
1130
- # Run all DaveLoops in parallel
1170
+ # Run all DaveLoops in parallel with live dashboard
1131
1171
  isolation = getattr(args, 'isolation', 'none') if getattr(args, 'swarm', False) else 'none'
1172
+ _print_swarm_dashboard(project_dir, batch_num, ready)
1173
+ dashboard_stop = _start_live_dashboard(project_dir, batch_num, ready, interval=15.0)
1132
1174
  parallel_results = engine.run_parallel(tasks, project_dir, args.max_iter, isolation=isolation)
1175
+ dashboard_stop.set()
1133
1176
 
1134
- # Print swarm dashboard after parallel run completes (B7)
1177
+ # Print final swarm dashboard after parallel run completes (B7)
1135
1178
  _print_swarm_dashboard(project_dir, batch_num, ready)
1136
1179
  engine.clear_worker_statuses(project_dir)
1137
1180
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: borisxdave
3
- Version: 0.3.3
3
+ Version: 0.3.5
4
4
  Summary: Boris - Autonomous Project Orchestrator
5
5
  Requires-Python: >=3.8
6
6
  Dynamic: requires-python
@@ -2,7 +2,7 @@ from setuptools import setup
2
2
 
3
3
  setup(
4
4
  name="borisxdave",
5
- version="0.3.3",
5
+ version="0.3.5",
6
6
  description="Boris - Autonomous Project Orchestrator",
7
7
  py_modules=["boris", "engine", "git_manager", "prompts", "state", "planner", "config", "file_lock", "boris_prompt_data"],
8
8
  python_requires=">=3.8",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes