gemini
1 #!/usr/bin/env python3 2 """ 3 Gemini - Sovereign OS AI Assistant 4 5 Invokes Gemini with full mesh integration, just like Claude. 6 7 Usage: 8 gemini # Interactive mode 9 gemini "your question" # One-shot query 10 gemini -f file.py "explain" # With file context 11 """ 12 13 import sys 14 import os 15 import json 16 from pathlib import Path 17 18 # Add project root to path 19 SOVEREIGN_ROOT = Path(__file__).parent.parent 20 sys.path.insert(0, str(SOVEREIGN_ROOT)) 21 22 # Suppress deprecation warnings 23 import warnings 24 warnings.filterwarnings("ignore", category=FutureWarning) 25 26 TRUST_FILE = Path.home() / ".sovereign" / "trusted_dirs.json" 27 28 29 def check_trust() -> bool: 30 """Check if current directory is trusted. Prompt if not.""" 31 cwd = Path.cwd().resolve() 32 33 # Load trusted directories 34 trusted = set() 35 if TRUST_FILE.exists(): 36 try: 37 trusted = set(json.loads(TRUST_FILE.read_text())) 38 except: 39 pass 40 41 # Check if cwd or any parent is trusted 42 for path in [cwd] + list(cwd.parents): 43 if str(path) in trusted: 44 return True 45 46 # Prompt for trust 47 print() 48 print("─" * 70) 49 print(" Do you trust the files in this folder?") 50 print() 51 print(f" {cwd}") 52 print() 53 print(" Gemini may read files and execute commands in this directory.") 54 print(" This can pose security risks with untrusted sources.") 55 print("─" * 70) 56 print() 57 print(" ❯ 1. Yes, proceed") 58 print(" 2. No, exit") 59 print() 60 print(" Enter to confirm · Esc to cancel") 61 print() 62 63 try: 64 choice = input(" ❯ ").strip() 65 except (KeyboardInterrupt, EOFError): 66 print("\nExiting.") 67 return False 68 69 # Default to "1" if just Enter pressed 70 if choice in ("", "1"): 71 # Save trust 72 trusted.add(str(cwd)) 73 TRUST_FILE.parent.mkdir(parents=True, exist_ok=True) 74 TRUST_FILE.write_text(json.dumps(list(trusted))) 75 print(f" ✓ Trusted: {cwd}\n") 76 return True 77 else: 78 print(" Exiting.") 79 return False 80 81 82 def main(): 83 # Trust check first 84 if not check_trust(): 85 sys.exit(1) 86 87 # Check for API key 88 if not os.environ.get("GEMINI_API_KEY"): 89 print("Error: GEMINI_API_KEY not set") 90 print("Add to ~/.zshrc: export GEMINI_API_KEY='your-key'") 91 sys.exit(1) 92 93 from core.mesh import ModelRouter, TaskType 94 95 router = ModelRouter() 96 available = router.available_models() 97 98 if not any('gemini' in m for m in available): 99 print("Error: No Gemini models available") 100 print("Check your API key and network connection") 101 sys.exit(1) 102 103 args = sys.argv[1:] 104 105 # No args = interactive mode 106 if not args: 107 interactive_mode(router) 108 return 109 110 # Parse flags 111 file_content = None 112 prompt_start = 0 113 114 if args[0] in ("-f", "--file"): 115 if len(args) < 3: 116 print("Usage: gemini -f <file> <prompt>") 117 sys.exit(1) 118 filepath = Path(args[1]) 119 if filepath.exists(): 120 file_content = filepath.read_text() 121 else: 122 print(f"File not found: {filepath}") 123 sys.exit(1) 124 prompt_start = 2 125 126 prompt = " ".join(args[prompt_start:]) 127 128 if not prompt: 129 interactive_mode(router) 130 return 131 132 # One-shot query 133 mesh_status = "●" if router._mesh else "○" 134 print(f"[Mesh: {mesh_status}] Routing to Gemini...\n") 135 136 response = router.query( 137 prompt, 138 model="gemini-flash", 139 file_content=file_content, 140 ) 141 print(response.content) 142 143 144 def print_banner(router): 145 """Print startup banner like Claude Code.""" 146 cwd = Path.cwd() 147 home = Path.home() 148 username = os.environ.get("USER", "User") 149 150 # Shorten path if in home 151 if str(cwd).startswith(str(home)): 152 display_path = "~" + str(cwd)[len(str(home)):] 153 else: 154 display_path = str(cwd) 155 156 mesh_icon = "\033[32m●\033[0m" if router._mesh else "\033[31m○\033[0m" 157 mesh_text = "connected" if router._mesh else "offline" 158 159 # Get terminal width, default to 100 160 try: 161 import shutil 162 width = shutil.get_terminal_size().columns 163 except: 164 width = 100 165 166 width = min(width, 110) 167 left_width = 40 168 right_width = width - left_width - 3 169 170 print() 171 print(f"╭─── Gemini Agent v1.0 {'─' * (width - 24)}╮") 172 173 # Row 1 174 left = "│" + "Welcome back {}!".format(username).center(left_width) 175 right = "│ Mesh Status".ljust(right_width) + "│" 176 print(left + right) 177 178 # Row 2 - empty / mesh status 179 left = "│" + "".center(left_width) 180 right = f"│ {mesh_icon} {mesh_text}".ljust(right_width) + "│" 181 print(left + right) 182 183 # Row 3 - Gemini symbol ♊ (twin pillars) line 1 184 left = "│" + "\033[36m ╔═══╦═══╗\033[0m".center(left_width + 9) 185 right = "│ " + "─" * (right_width - 3) + " │" 186 print(left + right) 187 188 # Row 4 - Gemini symbol line 2 189 left = "│" + "\033[36m ║ ║\033[0m".center(left_width + 9) 190 right = "│ Protocols".ljust(right_width) + "│" 191 print(left + right) 192 193 # Row 5 - Gemini symbol line 3 194 left = "│" + "\033[36m ╚═══╩═══╝\033[0m".center(left_width + 9) 195 right = "│ /preflight Run pre-flight checks".ljust(right_width) + "│" 196 print(left + right) 197 198 # Row 6 - model info 199 left = "│" + "Flash · Sovereign OS".center(left_width) 200 right = "│ /help Show all commands".ljust(right_width) + "│" 201 print(left + right) 202 203 # Row 7 - path 204 left = "│" + display_path.center(left_width) 205 right = "│ /status Check mesh & model".ljust(right_width) + "│" 206 print(left + right) 207 208 print(f"╰{'─' * (width - 2)}╯") 209 print() 210 print("─" * width) 211 212 213 def interactive_mode(router): 214 """Interactive chat mode - like Claude Code.""" 215 print_banner(router) 216 217 # Get the Gemini adapter for chat mode 218 from core.mesh.gemini_adapter import GeminiFlashAdapter 219 adapter = GeminiFlashAdapter() 220 221 if not adapter.is_available(): 222 print("Error: Gemini not available") 223 return 224 225 adapter.start_chat() 226 227 # Get terminal width 228 try: 229 import shutil 230 width = min(shutil.get_terminal_size().columns, 110) 231 except: 232 width = 100 233 234 first_prompt = True 235 while True: 236 try: 237 if first_prompt: 238 # Show hint on first prompt, then prompt, then bottom line 239 hint = "\033[90mTry \"what are the four axioms?\" · ? for help\033[0m" 240 print(hint) 241 user_input = input("\033[36m❯\033[0m ").strip() 242 if first_prompt: 243 print("─" * width) 244 first_prompt = False 245 except (KeyboardInterrupt, EOFError): 246 print("\nGoodbye!") 247 break 248 249 if not user_input: 250 continue 251 252 # Commands 253 if user_input == "/exit": 254 print("Goodbye!") 255 break 256 257 if user_input in ("/help", "?"): 258 print() 259 print(" \033[1mCommands:\033[0m") 260 print(" /exit Exit Gemini") 261 print(" /status Show model and mesh status") 262 print(" /file <path> Load a file for discussion") 263 print(" /clear Clear conversation history") 264 print(" /preflight Run pre-flight checklist") 265 print(" /debrief Generate session debrief") 266 print() 267 print(" \033[1mShortcuts:\033[0m") 268 print(" ? Show this help") 269 print(" Ctrl+C Exit") 270 print() 271 continue 272 273 if user_input == "/status": 274 print(f"\n Model: {adapter.name}") 275 print(f" Mesh: {'connected' if router._mesh else 'offline'}") 276 print() 277 continue 278 279 if user_input == "/clear": 280 adapter.start_chat() # Reset chat history 281 print(" ✓ Conversation cleared\n") 282 continue 283 284 if user_input == "/preflight": 285 print("\n Running pre-flight checks...") 286 os.system("python3 /Users/rcerf/repos/Sovereign_OS/scripts/claude_bootstrap.py") 287 print() 288 continue 289 290 if user_input == "/debrief": 291 print("\n Generating session debrief...") 292 os.system("python3 /Users/rcerf/repos/Sovereign_OS/scripts/session_report.py") 293 print() 294 continue 295 296 if user_input.startswith("/file "): 297 filepath = Path(user_input[6:].strip()) 298 if filepath.exists(): 299 content = filepath.read_text() 300 print(f" Loaded {filepath} ({len(content)} chars)") 301 user_input = input(" Question about this file: ").strip() 302 if user_input: 303 user_input = f"File content:\n```\n{content}\n```\n\n{user_input}" 304 else: 305 print(f" File not found: {filepath}") 306 continue 307 308 # Send to Gemini 309 try: 310 response = adapter.chat(user_input) 311 print() 312 print(response) 313 print() 314 except Exception as e: 315 print(f"\n Error: {e}\n") 316 317 318 if __name__ == "__main__": 319 main()