/ scripts / gemini
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()