Initial commit: a0_software_orchestrator v1.0

- Auto-Registration-Bug behoben (register_project/get_project_id/resolve_project Trennung)
- 25 Tests gruen (Pytest)
- block_compactor-Tool refactored (Option B: Soft-Check statt Hard-Block)
- 4 Restbaustellen gefixt
- DB-Schema: plugin_settings-Tabelle hinzugefuegt
- 3 Schattenprojekte aus DB geloescht
- Plan v3 + Refactor-Plan + Worklog dokumentiert
This commit is contained in:
Software Orchestrator
2026-06-16 22:13:06 +00:00
commit 5769c1cd22
236 changed files with 17825 additions and 0 deletions
@@ -0,0 +1,94 @@
"""Extension: Load compact project state at monologue start (DB-first, file fallback)."""
from __future__ import annotations
import os
from usr.plugins.a0_software_orchestrator.helpers.db_state_store import (
get_project_id,
get_kv,
get_status,
list_next_steps,
list_errors,
get_block_compact,
get_project_detail,
)
from usr.plugins.a0_software_orchestrator.helpers.state_store import read_json, get_project_files
def _project_name(project_root: str) -> str:
return os.path.basename((project_root or os.getcwd()).rstrip("/"))
def get_monologue_injection(project_root: str = None) -> str:
"""Return compact project state summary for the agent."""
if not project_root:
project_root = os.getcwd()
name = _project_name(project_root)
# DB is the source of truth. Files are legacy fallback only.
try:
pid = get_project_id(name)
if pid is None:
raise LookupError("project not registered")
status = get_status(pid)
detail = get_project_detail(name) or {}
phase = get_kv(pid, "phase", default=detail.get("phase", "unknown"))
mode = get_kv(pid, "orchestrator_mode", default={})
if (not isinstance(mode, dict) or not mode) and detail.get("plan_mode"):
mode = {"mode": detail.get("plan_mode")}
steps = list_next_steps(pid, status="pending", limit=5)
errors = list_errors(pid, status="open", limit=5)
bc = get_block_compact(pid)
parts = [f"Project: {name}", f"Project-ID: {pid}", f"Phase: {phase}"]
if isinstance(mode, dict):
parts.append(f"Plan Mode: {mode.get('mode', 'planning_only')}")
if status:
parts.append(f"Status: {status[:300]}")
parts.append(f"Pending next steps: {len(steps)}")
parts.append(f"Open errors: {len(errors)}")
if bc:
parts.append(f"Last block: {bc.get('last_block_id')} → Next: {bc.get('next_block')}")
return "\n".join(parts)
except Exception:
pass
# Legacy fallback: .a0 files
files = get_project_files(project_root)
parts = []
if os.path.exists(files["project_state"]):
state = read_json(files["project_state"], {})
project = state.get("project", {})
phase = state.get("phase", {})
parts.append(f"Project: {project.get('name', name)}")
parts.append(f"Phase: {phase.get('current', 'unknown')}")
parts.append(f"Blocked: {phase.get('blocked', False)}")
if os.path.exists(files["current_status"]):
with open(files["current_status"], "r", encoding="utf-8") as f:
parts.append(f"Status: {f.read()[:200]}")
if os.path.exists(files["next_steps"]):
with open(files["next_steps"], "r", encoding="utf-8") as f:
parts.append(f"Next: {f.read()[:200]}")
if os.path.exists(files["orchestrator_mode"]):
mode_data = read_json(files["orchestrator_mode"], {})
parts.append(f"Plan Mode: {mode_data.get('mode', 'unknown')}")
return "\n".join(parts) if parts else "No project state loaded."
if __name__ == "__main__":
print(get_monologue_injection(os.getcwd()) or "no state")
from helpers.extension import Extension
class LoadProjectState(Extension):
async def execute(self, loop_data=None, **kwargs):
# Keep startup light: only inject a compact DB-backed summary, never broad filesystem discovery.
if not self.agent or self.agent.config.profile != "a0_software_orchestrator":
return
try:
summary = get_monologue_injection(os.getcwd())
if summary and loop_data is not None:
loop_data.extras_temporary["orchestrator_project_state"] = summary
except Exception:
return