sos_common.py
1 #!/usr/bin/env python3 2 """ 3 Sovereign OS Common Utilities 4 5 Shared utilities for all Sovereign OS scripts. 6 Handles: 7 - Repo root detection 8 - Configuration loading 9 - Path resolution 10 11 This module makes all scripts portable across repos. 12 """ 13 14 import json 15 import os 16 from pathlib import Path 17 from typing import Optional, Dict, Any 18 19 20 # ============================================================================= 21 # REPO DETECTION 22 # ============================================================================= 23 24 def find_repo_root(start_path: Path = None) -> Path: 25 """ 26 Find the repository root by looking for .sovereign-os.json or .git. 27 28 Searches upward from start_path (or current script location). 29 """ 30 if start_path is None: 31 # Start from the script that called us 32 start_path = Path(__file__).parent 33 34 current = start_path.resolve() 35 36 # First, look for .sovereign-os.json (installed Sovereign OS) 37 while current != current.parent: 38 if (current / '.sovereign-os.json').exists(): 39 return current 40 current = current.parent 41 42 # Fall back to looking for .git 43 current = start_path.resolve() 44 while current != current.parent: 45 if (current / '.git').exists(): 46 return current 47 current = current.parent 48 49 # Last resort: return the start path 50 return start_path.resolve() 51 52 53 def is_sovereign_os_repo(repo_root: Path = None) -> bool: 54 """Check if this repo has Sovereign OS installed.""" 55 if repo_root is None: 56 repo_root = find_repo_root() 57 return (repo_root / '.sovereign-os.json').exists() 58 59 60 # ============================================================================= 61 # CONFIGURATION 62 # ============================================================================= 63 64 DEFAULT_CONFIG = { 65 "version": "1.0.0", 66 "name": "Sovereign OS Instance", 67 "paths": { 68 "sessions": "sessions", 69 "patterns": "patterns", 70 "scripts": "scripts", 71 }, 72 "thresholds": { 73 "staleness_warning_minutes": 60, 74 "staleness_critical_minutes": 180, 75 "confidence_collapse": 0.4, 76 "escalation_confidence": 0.4, 77 } 78 } 79 80 81 def load_config(repo_root: Path = None) -> Dict[str, Any]: 82 """ 83 Load Sovereign OS configuration. 84 85 Returns default config if not installed. 86 """ 87 if repo_root is None: 88 repo_root = find_repo_root() 89 90 config_file = repo_root / '.sovereign-os.json' 91 92 if config_file.exists(): 93 try: 94 return json.loads(config_file.read_text()) 95 except json.JSONDecodeError: 96 pass 97 98 return DEFAULT_CONFIG.copy() 99 100 101 def save_config(config: Dict[str, Any], repo_root: Path = None): 102 """Save configuration to repo.""" 103 if repo_root is None: 104 repo_root = find_repo_root() 105 106 config_file = repo_root / '.sovereign-os.json' 107 config_file.write_text(json.dumps(config, indent=2)) 108 109 110 # ============================================================================= 111 # PATH RESOLUTION 112 # ============================================================================= 113 114 def get_sessions_dir(repo_root: Path = None) -> Path: 115 """Get the sessions directory path.""" 116 if repo_root is None: 117 repo_root = find_repo_root() 118 config = load_config(repo_root) 119 return repo_root / config['paths']['sessions'] 120 121 122 def get_live_compression_path(repo_root: Path = None) -> Path: 123 """Get the LIVE-COMPRESSION.md path.""" 124 return get_sessions_dir(repo_root) / 'LIVE-COMPRESSION.md' 125 126 127 def get_insight_backlog_path(repo_root: Path = None) -> Path: 128 """Get the INSIGHT-BACKLOG.md path.""" 129 return get_sessions_dir(repo_root) / 'INSIGHT-BACKLOG.md' 130 131 132 def get_daily_synthesis_path(repo_root: Path = None) -> Path: 133 """Get the DAILY-SYNTHESIS.md path.""" 134 return get_sessions_dir(repo_root) / 'DAILY-SYNTHESIS.md' 135 136 137 def get_close_down_reports_dir(repo_root: Path = None) -> Path: 138 """Get the close-down-reports directory.""" 139 return get_sessions_dir(repo_root) / 'close-down-reports' 140 141 142 def get_patterns_dir(repo_root: Path = None) -> Path: 143 """Get the patterns directory.""" 144 if repo_root is None: 145 repo_root = find_repo_root() 146 config = load_config(repo_root) 147 return repo_root / config['paths']['patterns'] 148 149 150 # ============================================================================= 151 # THRESHOLD ACCESS 152 # ============================================================================= 153 154 def get_threshold(name: str, repo_root: Path = None) -> float: 155 """Get a threshold value from config.""" 156 config = load_config(repo_root) 157 return config.get('thresholds', {}).get(name, DEFAULT_CONFIG['thresholds'].get(name, 0.5)) 158 159 160 def get_staleness_warning_minutes(repo_root: Path = None) -> int: 161 """Get staleness warning threshold in minutes.""" 162 return int(get_threshold('staleness_warning_minutes', repo_root)) 163 164 165 def get_staleness_critical_minutes(repo_root: Path = None) -> int: 166 """Get staleness critical threshold in minutes.""" 167 return int(get_threshold('staleness_critical_minutes', repo_root)) 168 169 170 # ============================================================================= 171 # UTILITIES 172 # ============================================================================= 173 174 def ensure_sessions_structure(repo_root: Path = None): 175 """Ensure the sessions directory structure exists.""" 176 sessions_dir = get_sessions_dir(repo_root) 177 sessions_dir.mkdir(parents=True, exist_ok=True) 178 (sessions_dir / 'close-down-reports').mkdir(exist_ok=True) 179 (sessions_dir / 'synthesis').mkdir(exist_ok=True) 180 181 182 def get_repo_name(repo_root: Path = None) -> str: 183 """Get the name of this Sovereign OS instance.""" 184 config = load_config(repo_root) 185 return config.get('name', 'Unknown') 186 187 188 # ============================================================================= 189 # CLI (for testing) 190 # ============================================================================= 191 192 if __name__ == "__main__": 193 print("Sovereign OS Common Utilities") 194 print("=" * 40) 195 196 repo_root = find_repo_root() 197 print(f"Repo root: {repo_root}") 198 print(f"Is Sovereign OS: {is_sovereign_os_repo(repo_root)}") 199 200 config = load_config(repo_root) 201 print(f"Config: {json.dumps(config, indent=2)}") 202 203 print(f"\nPaths:") 204 print(f" Sessions: {get_sessions_dir(repo_root)}") 205 print(f" Live Compression: {get_live_compression_path(repo_root)}") 206 print(f" Insight Backlog: {get_insight_backlog_path(repo_root)}") 207 print(f" Close Down Reports: {get_close_down_reports_dir(repo_root)}")