codeaois 0.2.1__tar.gz → 0.2.6__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.
Files changed (37) hide show
  1. {codeaois-0.2.1 → codeaois-0.2.6}/PKG-INFO +1 -1
  2. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/agents/coder_agent.py +12 -3
  3. codeaois-0.2.6/codeaois/agents/git_agent.py +84 -0
  4. codeaois-0.2.6/codeaois/agents/terminal_agent.py +54 -0
  5. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/agents/tester_agent.py +18 -6
  6. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/cli/main.py +192 -65
  7. codeaois-0.2.6/codeaois/core/_auth.py +17 -0
  8. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/core/planner.py +10 -11
  9. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois.egg-info/PKG-INFO +1 -1
  10. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois.egg-info/SOURCES.txt +2 -0
  11. {codeaois-0.2.1 → codeaois-0.2.6}/pyproject.toml +1 -1
  12. {codeaois-0.2.1 → codeaois-0.2.6}/setup.py +4 -4
  13. codeaois-0.2.1/codeaois/agents/git_agent.py +0 -45
  14. {codeaois-0.2.1 → codeaois-0.2.6}/README.md +0 -0
  15. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/__init__.py +0 -0
  16. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/agents/data_science_agent.py +0 -0
  17. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/agents/pip_agent.py +0 -0
  18. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/app.py +0 -0
  19. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/brain/__init__.py +0 -0
  20. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/brain/embeddings.py +0 -0
  21. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/brain/memory.py +0 -0
  22. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/brain/scanner.py +0 -0
  23. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/cli/__init__.py +0 -0
  24. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/config/settings.py +0 -0
  25. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/core/context.py +0 -0
  26. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/core/marketplace.py +0 -0
  27. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/core/memory.py +0 -0
  28. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/core/orchestrator.py +0 -0
  29. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/core/router.py +0 -0
  30. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/models/llm_interface.py +0 -0
  31. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/utils/file_writer.py +0 -0
  32. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois/utils/logger.py +0 -0
  33. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois.egg-info/dependency_links.txt +0 -0
  34. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois.egg-info/entry_points.txt +0 -0
  35. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois.egg-info/requires.txt +0 -0
  36. {codeaois-0.2.1 → codeaois-0.2.6}/codeaois.egg-info/top_level.txt +0 -0
  37. {codeaois-0.2.1 → codeaois-0.2.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeaois
3
- Version: 0.2.1
3
+ Version: 0.2.6
4
4
  Summary: Advanced AI Developer OS
5
5
  Author: Nikhil Nagar
6
6
  Requires-Python: >=3.8
@@ -1,6 +1,7 @@
1
1
  # codeaois/agents/coder_agent.py
2
2
  from codeaois.models.llm_interface import call_openrouter
3
3
 
4
+
4
5
  def generate_code(user_prompt: str, file_context: str = "") -> str:
5
6
  if file_context:
6
7
  system_prompt = (
@@ -16,10 +17,18 @@ def generate_code(user_prompt: str, file_context: str = "") -> str:
16
17
  "2. AFTER the code, you MUST add the exact text '---SUMMARY---' on a new line.\n"
17
18
  "3. AFTER the summary delimiter, write a brief, friendly explanation of how the code works."
18
19
  )
19
-
20
+
20
21
  full_prompt = user_prompt
21
22
  if file_context:
22
23
  full_prompt += f"\n\nHere are the existing files for context:\n{file_context}"
23
-
24
+
24
25
  response = call_openrouter(system_prompt, full_prompt, intent="code")
25
- return response.strip()
26
+ return response.strip()
27
+
28
+
29
+ class CoderAgent:
30
+ def execute(self, task: str) -> str:
31
+ """Execute a code generation task."""
32
+ if not task:
33
+ return "```text\nNo task provided to the coder agent.\n```"
34
+ return generate_code(str(task))
@@ -0,0 +1,84 @@
1
+ import subprocess
2
+ from typing import Optional
3
+ from rich.console import Console
4
+ from rich.prompt import Prompt
5
+ from rich.panel import Panel
6
+ from codeaois.models.llm_interface import call_openrouter
7
+ from codeaois.core.memory import load_settings
8
+
9
+ console = Console()
10
+
11
+ def get_theme():
12
+ return load_settings().get("ui_theme", "cyan")
13
+
14
+ def generate_git_code(user_input: str, context: str = "") -> str:
15
+ """Analyze git diffs, generate a commit message using the LLM, and execute git add, commit, push."""
16
+ theme = get_theme()
17
+ console.print("\n[dim]✦ Analyzing git repository...[/dim]")
18
+
19
+ try:
20
+ # 1. Check if we're in a git repository
21
+ is_repo = subprocess.run(
22
+ ["git", "rev-parse", "--is-inside-work-tree"],
23
+ capture_output=True,
24
+ text=True,
25
+ )
26
+ if is_repo.returncode != 0:
27
+ return "```text\nNot a git repository. Please run 'git init' first.\n```"
28
+
29
+ # 2. Stage all changes FIRST so we can read the exact diff of everything
30
+ subprocess.run(["git", "add", "."], capture_output=True)
31
+
32
+ diff = subprocess.run(
33
+ ["git", "diff", "--cached"],
34
+ capture_output=True,
35
+ text=True,
36
+ )
37
+
38
+ if not diff.stdout.strip():
39
+ return "```text\nNo changes detected. Working tree is clean.\n```"
40
+
41
+ # 3. Generate a commit message via the LLM
42
+ system_prompt = (
43
+ "You are a helper that writes short, clear git commit messages based on the provided git diff. "
44
+ "Respond with ONLY the commit message, no quotes, no markdown formatting."
45
+ )
46
+ # Truncate diff to 3000 chars to save tokens!
47
+ full_prompt = f"{user_input}\n\nGit diff:\n{diff.stdout[:3000]}"
48
+ commit_msg = call_openrouter(system_prompt, full_prompt, intent="code").strip()
49
+ commit_msg = commit_msg.replace('"', "'").replace("```text", "").replace("```", "").strip()
50
+
51
+ if not commit_msg:
52
+ return "```text\nCould not generate a commit message.\n```"
53
+
54
+ console.print(f"\n[bold yellow]⚠ AI Proposed Commit Message:[/bold yellow]")
55
+ console.print(Panel(commit_msg, border_style=theme))
56
+
57
+ # 4. Ask the user before committing & pushing
58
+ choice = Prompt.ask(f"[{theme}]Execute `git commit` and `git push`?[/{theme}] [Y/n]", default="y")
59
+ if choice.lower() != "y":
60
+ # Undo the 'git add' if they cancel, to keep their workspace clean!
61
+ subprocess.run(["git", "reset"], capture_output=True)
62
+ return "```text\nGit operation aborted by user.\n```"
63
+
64
+ # 5. Commit and Push safely
65
+ console.print("\n[dim]Executing git commands...[/dim]")
66
+ subprocess.run(
67
+ ["git", "commit", "-m", commit_msg],
68
+ capture_output=True,
69
+ text=True,
70
+ )
71
+
72
+ push_res = subprocess.run(
73
+ ["git", "push"],
74
+ capture_output=True,
75
+ text=True,
76
+ )
77
+
78
+ output = push_res.stdout if push_res.returncode == 0 else push_res.stderr
79
+ status = "Success" if push_res.returncode == 0 else "Failed"
80
+
81
+ return f"```text\n{output.strip()}\nStatus: {status}\n```"
82
+
83
+ except Exception as e:
84
+ return f"```text\nError: {str(e)}\n```"
@@ -0,0 +1,54 @@
1
+ import subprocess
2
+ from rich.console import Console
3
+ from rich.prompt import Prompt
4
+ from rich.panel import Panel
5
+ from codeaois.models.llm_interface import call_openrouter
6
+ from codeaois.core.memory import load_settings
7
+
8
+ console = Console()
9
+
10
+ def get_theme():
11
+ return load_settings().get("ui_theme", "cyan")
12
+
13
+ def generate_terminal_code(user_input: str, context: str) -> str:
14
+ """Extracts bash commands from the LLM and executes them safely."""
15
+ theme = get_theme()
16
+
17
+ sys_prompt = "You are a terminal command extractor. Convert the user's request into a single bash/terminal command or a short chain of commands (like `mkdir foo && cd foo`). Return ONLY the raw terminal command, no markdown, no explanations."
18
+
19
+ console.print("\n[dim]✦ Translating request to terminal commands...[/dim]")
20
+
21
+ try:
22
+ command = call_openrouter(sys_prompt, user_input, intent="code").strip()
23
+
24
+ # Clean up any markdown the AI accidentally sends
25
+ command = command.replace("```bash", "").replace("```sh", "").replace("```", "").replace("\n", " ").strip()
26
+
27
+ if not command:
28
+ return "```text\nCould not determine a valid terminal command.\n```"
29
+
30
+ # --- HUMAN IN THE LOOP SAFETY CHECK ---
31
+ console.print(f"\n[bold yellow]⚠ SECURITY CHECKPOINT[/bold yellow]")
32
+ console.print(f"The AI requested to execute: [bold white]{command}[/bold white]")
33
+ choice = Prompt.ask(f"[{theme}]Allow execution?[/{theme}] [Y/n]", default="y")
34
+
35
+ if choice.lower() != 'y':
36
+ return "```text\nExecution aborted by user. Stay safe!\n```"
37
+
38
+ console.print(f"\n[dim]Executing: {command}[/dim]")
39
+
40
+ # Actually run the command on the OS!
41
+ result = subprocess.run(command, shell=True, capture_output=True, text=True)
42
+
43
+ output = result.stdout if result.returncode == 0 else result.stderr
44
+ log_color = "green" if result.returncode == 0 else "red"
45
+
46
+ if output.strip():
47
+ console.print(Panel(output.strip(), title="Terminal Output", border_style=log_color))
48
+ else:
49
+ console.print(f"[{log_color}]✓ Command executed successfully with no output.[/{log_color}]")
50
+
51
+ status = 'Success' if result.returncode == 0 else 'Failed'
52
+ return f"```text\n{output.strip()}\nStatus: {status}\n```"
53
+ except Exception as e:
54
+ return f"```text\nError executing command: {str(e)}\n```"
@@ -2,26 +2,38 @@
2
2
  import subprocess
3
3
  import sys
4
4
 
5
+
5
6
  def run_test(file_path: str) -> tuple[bool, str]:
6
7
  """Runs a Python script and catches any terminal errors."""
7
- if not file_path.endswith('.py'):
8
+ if not file_path or not file_path.endswith('.py'):
8
9
  return True, "Not a Python file. Skipping auto-test."
9
10
 
10
11
  try:
11
- # sys.executable ensures it uses your isolated 'venv' Python, not the global Ubuntu one!
12
+ # sys.executable ensures it uses your isolated 'venv' Python, not the global one
12
13
  result = subprocess.run(
13
14
  [sys.executable, file_path],
14
15
  capture_output=True,
15
16
  text=True,
16
- timeout=10 # 10-second kill switch so infinite loops don't crash your computer
17
+ timeout=10, # 10-second kill switch so infinite loops don't crash your computer
17
18
  )
18
-
19
+
19
20
  if result.returncode == 0:
20
21
  return True, result.stdout.strip()
21
22
  else:
22
23
  return False, result.stderr.strip()
23
-
24
+
24
25
  except subprocess.TimeoutExpired:
25
26
  return False, "Error: Script execution timed out (possible infinite loop)."
26
27
  except Exception as e:
27
- return False, str(e)
28
+ return False, str(e)
29
+
30
+
31
+ class TesterAgent:
32
+ def execute(self, task: str) -> str:
33
+ """Execute a test task on a Python file."""
34
+ if not task:
35
+ return "```text\nNo test task provided to the tester agent.\n```"
36
+
37
+ success, output = run_test(str(task))
38
+ status = "Success" if success else "Failed"
39
+ return f"```text\n{output}\nStatus: {status}\n```"
@@ -5,14 +5,17 @@ import importlib
5
5
  import time
6
6
  import random
7
7
  import string
8
-
8
+ import re
9
+ import smtplib
10
+ from email.mime.text import MIMEText
11
+ import base64
9
12
  # --- CLI UI ENGINES ---
10
13
  from prompt_toolkit import PromptSession
11
14
  from prompt_toolkit.completion import WordCompleter
12
15
  from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
13
16
  from prompt_toolkit.formatted_text import HTML
14
17
  from prompt_toolkit.shortcuts import radiolist_dialog
15
-
18
+ from codeaois.core._auth import get_secure_credentials
16
19
  from rich.console import Console
17
20
  from rich.markdown import Markdown
18
21
  from rich.panel import Panel
@@ -59,11 +62,57 @@ def apply_saved_background():
59
62
  bg_color = settings.get("bg_theme", "#231e20")
60
63
  set_terminal_background(bg_color)
61
64
 
65
+ # --- CLAUDE CODE SAFETY ENGINE ---
66
+ import base64
67
+
68
+ # --- THE REAL SMTP OTP ENGINE (Hidden File Method) ---
69
+ def send_real_otp(receiver_email, otp_code):
70
+ """Sends a REAL email automatically using the hidden _auth.py credentials."""
71
+ try:
72
+ sender_email, sender_password = get_secure_credentials()
73
+
74
+ if not sender_email or not sender_password:
75
+ return False
76
+
77
+ msg = MIMEText(f"Hello!\n\nYour CodeAOIS secure login verification code is: {otp_code}\n\nWelcome to the Advanced Developer OS.\n- The CodeAOIS Team")
78
+ msg['Subject'] = 'CodeAOIS Secure Login Verification'
79
+ msg['From'] = f"CodeAOIS Security <{sender_email}>"
80
+ msg['To'] = receiver_email
81
+
82
+ with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
83
+ server.login(sender_email, sender_password)
84
+ server.sendmail(sender_email, [receiver_email], msg.as_string())
85
+ return True
86
+ except Exception as e:
87
+ return False
88
+
89
+ # --- THE REAL SMTP OTP ENGINE ---
90
+ def send_real_otp(receiver_email, otp_code):
91
+ """Sends a REAL email using Gmail SMTP. Requires Environment Variables."""
92
+ sender_email = os.getenv("CODEAOIS_SENDER_EMAIL")
93
+ sender_password = os.getenv("CODEAOIS_APP_PASS")
94
+
95
+ # If the developer hasn't set up their mail server yet, fallback to local dev mode
96
+ if not sender_email or not sender_password:
97
+ return False
98
+
99
+ msg = MIMEText(f"Hello!\n\nYour CodeAOIS secure login verification code is: {otp_code}\n\nWelcome to the OS.\n- The CodeAOIS Team")
100
+ msg['Subject'] = 'CodeAOIS Secure Login Verification'
101
+ msg['From'] = f"CodeAOIS Security <{sender_email}>"
102
+ msg['To'] = receiver_email
103
+
104
+ try:
105
+ with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
106
+ server.login(sender_email, sender_password)
107
+ server.sendmail(sender_email, [receiver_email], msg.as_string())
108
+ return True
109
+ except Exception as e:
110
+ return False
111
+
62
112
  # --- THE MULTI-ANIMATION LOGO ENGINE ---
63
113
  def print_logo():
64
114
  settings = load_settings()
65
115
  theme = settings.get("ui_theme", "cyan")
66
- # Option 4 (Data Sweep) is the default!
67
116
  anim_style = str(settings.get("logo_animation", "4"))
68
117
 
69
118
  logo_lines = [
@@ -75,25 +124,24 @@ def print_logo():
75
124
  ]
76
125
  logo_str = "\n".join(logo_lines)
77
126
 
78
- # 0. Instant (No Animation)
79
127
  if anim_style == "0":
80
128
  console.print(f"[bold {theme}]{logo_str}[/]")
81
- console.print(f" [dim]✦[/dim] [bold white]Advanced Developer OS[/bold white] [dim]v0.2.1[/dim]")
129
+ console.print(f" [dim]✦[/dim] [bold white]Advanced Developer OS[/bold white] [dim]v0.2.6[/dim]")
82
130
  console.print(f" [dim]✦[/dim] [dim]Type /help for commands.[/dim]\n")
83
131
  return
84
132
 
85
133
  try:
86
- if anim_style == "1": # Classic Hacker Typewriter
134
+ if anim_style == "1":
87
135
  text = Text(style=f"bold {theme}")
88
136
  with Live(console=console, refresh_per_second=60, transient=False) as live:
89
137
  for char in logo_str:
90
138
  text.append(char)
91
139
  live.update(text)
92
140
  time.sleep(0.002)
93
- text.append(f"\n ✦ Advanced Developer OS v0.2.1\n ✦ Type /help for commands.\n", style="dim white")
141
+ text.append(f"\n ✦ Advanced Developer OS v0.2.6\n ✦ Type /help for commands.\n", style="dim white")
94
142
  live.update(text)
95
143
 
96
- elif anim_style == "2": # Neon Pulse
144
+ elif anim_style == "2":
97
145
  pulse_colors = ["#001111", "#003333", "#006666", "#009999", "#00cccc", "#00ffff", "#00cccc", "#009999"]
98
146
  with Live(console=console, refresh_per_second=20, transient=False) as live:
99
147
  for _ in range(2):
@@ -101,10 +149,10 @@ def print_logo():
101
149
  live.update(Text(logo_str, style=f"bold {color}"))
102
150
  time.sleep(0.05)
103
151
  final_text = Text(logo_str, style=f"bold {theme}")
104
- final_text.append(f"\n ✦ Advanced Developer OS v0.2.1\n ✦ Type /help for commands.\n", style="dim white")
152
+ final_text.append(f"\n ✦ Advanced Developer OS v0.2.6\n ✦ Type /help for commands.\n", style="dim white")
105
153
  live.update(final_text)
106
154
 
107
- elif anim_style == "3": # Cyber-Decryption
155
+ elif anim_style == "3":
108
156
  chars = string.ascii_letters + string.punctuation
109
157
  with Live(console=console, refresh_per_second=30, transient=False) as live:
110
158
  for i in range(15):
@@ -112,10 +160,10 @@ def print_logo():
112
160
  live.update(Text(scrambled, style="bold green"))
113
161
  time.sleep(0.05)
114
162
  final_text = Text(logo_str, style=f"bold {theme}")
115
- final_text.append(f"\n ✦ Advanced Developer OS v0.2.1\n ✦ Type /help for commands.\n", style="dim white")
163
+ final_text.append(f"\n ✦ Advanced Developer OS v0.2.6\n ✦ Type /help for commands.\n", style="dim white")
116
164
  live.update(final_text)
117
165
 
118
- elif anim_style == "4": # Data Sweep (THE DEFAULT)
166
+ elif anim_style == "4":
119
167
  max_len = max(len(line) for line in logo_lines)
120
168
  chars = string.ascii_letters + string.punctuation + "10"
121
169
  with Live(console=console, refresh_per_second=60, transient=False) as live:
@@ -134,10 +182,10 @@ def print_logo():
134
182
  live.update(display_text)
135
183
  time.sleep(0.015)
136
184
  final_text = Text(logo_str, style=f"bold {theme}")
137
- final_text.append(f"\n ✦ Advanced Developer OS v0.2.1\n ✦ Type /help for commands.\n", style="dim white")
185
+ final_text.append(f"\n ✦ Advanced Developer OS v0.2.6\n ✦ Type /help for commands.\n", style="dim white")
138
186
  live.update(final_text)
139
187
 
140
- elif anim_style == "5": # System Override Boot
188
+ elif anim_style == "5":
141
189
  boot_msgs = [
142
190
  "[dim green][+] Kernel loaded. Initializing core OS...[/]",
143
191
  "[dim green][+] Bypassing proxy security... OK[/]",
@@ -157,28 +205,77 @@ def print_logo():
157
205
  live.update(Text(logo_str, style="bold cyan"))
158
206
  time.sleep(0.03)
159
207
  final_text = Text(logo_str, style=f"bold {theme}")
160
- final_text.append(f"\n ✦ Advanced Developer OS v0.2.1\n ✦ Type /help for commands.\n", style="dim white")
208
+ final_text.append(f"\n ✦ Advanced Developer OS v0.2.6\n ✦ Type /help for commands.\n", style="dim white")
161
209
  live.update(final_text)
162
210
  except:
163
211
  console.print(f"[bold {theme}]{logo_str}[/]")
212
+ console.print(f" [dim]✦[/dim] [bold white]Advanced Developer OS[/bold white] [dim]v0.2.6[/dim]")
164
213
 
165
214
  def run_login_flow():
166
215
  theme = get_theme()
167
216
  os.system('clear' if os.name == 'posix' else 'cls')
168
217
  apply_saved_background()
169
218
  print_logo()
170
- console.print(Panel("[bold white]Welcome to CodeAOIS Initialization[/bold white]\n[dim]Let's configure your workspace.[/dim]", border_style=theme))
219
+ console.print(Panel("[bold white]Welcome to CodeAOIS Initialization[/bold white]\n[dim]Let's configure your secure workspace.[/dim]", border_style=theme))
171
220
 
172
221
  email = Prompt.ask(f"\n[bold {theme}]►[/bold {theme}] Enter your developer email")
173
222
 
174
- with console.status("[dim]Sending secure OTP verification...[/dim]", spinner="dots"):
223
+ # --- REAL OTP EXECUTION ---
224
+ real_otp = str(random.randint(100000, 999999))
225
+
226
+ with console.status("[dim]Connecting to secure mail servers...[/dim]", spinner="dots"):
227
+ email_sent = send_real_otp(email, real_otp)
175
228
  time.sleep(1.5)
229
+
230
+ if email_sent:
231
+ console.print(f"[bold green]✓ Real Verification Email sent to {email}![/bold green]")
232
+ else:
233
+ # Failsafe if the developer hasn't configured their OS environment variables yet!
234
+ console.print(f"\n[dim yellow]⚠ SMTP Environment Variables not found. Falling back to local DEV INBOX:[/dim yellow]")
235
+ console.print(f"[bold magenta]┌── [DEV SANDBOX INBOX] ───────────────────┐[/]")
236
+ console.print(f"[bold magenta]│[/] To: {email}")
237
+ console.print(f"[bold magenta]│[/] Subject: CodeAOIS Verification")
238
+ console.print(f"[bold magenta]│[/] Your secure 6-digit OTP is: [bold white]{real_otp}[/]")
239
+ console.print(f"[bold magenta]└─────────────────────────────────────────┘[/]\n")
176
240
 
177
- console.print("\n[dim](For this local beta, the verification code is automatically verified.)[/dim]")
241
+ attempts = 3
242
+ while attempts > 0:
243
+ user_otp = Prompt.ask(f"[bold {theme}]►[/bold {theme}] Enter 6-digit OTP")
244
+ if user_otp.strip() == real_otp:
245
+ console.print("[bold green]✓ Email verified successfully.[/bold green]\n")
246
+ break
247
+ attempts -= 1
248
+ if attempts > 0:
249
+ console.print(f"[bold red]✗ Invalid OTP. {attempts} attempts remaining.[/bold red]")
250
+ else:
251
+ console.print("[bold red]Access Denied. Exiting.[/bold red]")
252
+ sys.exit(1)
178
253
 
179
- name = Prompt.ask(f"\n[bold {theme}]►[/bold {theme}] Choose a workspace username", default=email.split('@')[0])
254
+ name = Prompt.ask(f"[bold {theme}]►[/bold {theme}] Choose a workspace username", default=email.split('@')[0])
180
255
  role = Prompt.ask(f"[bold {theme}]►[/bold {theme}] Primary role (e.g., Full Stack, Data Science)", default="Developer")
181
256
 
257
+ # --- FIXED: API KEY ONBOARDING WITH BLANK VALIDATION ---
258
+ console.print(f"\n[bold white]✦ Select your AI Engine Mode:[/bold white]")
259
+ console.print(" 1. CodeAOIS API (Free Base Model)")
260
+ console.print(" 2. Pro Mode (Custom OpenRouter API Key)")
261
+
262
+ api_choice = Prompt.ask("Select option", choices=["1", "2"], default="1")
263
+ settings = load_settings()
264
+
265
+ if api_choice == "2":
266
+ new_key = Prompt.ask("Enter OpenRouter API Key (press Enter to fallback to Free Model)", password=True).strip()
267
+ if new_key: # Prevents the blank bug!
268
+ settings["custom_api_key"] = new_key
269
+ settings["use_custom_api_key"] = True
270
+ console.print("[bold green]✓ Custom API Key secured.[/bold green]")
271
+ else:
272
+ settings["use_custom_api_key"] = False
273
+ console.print("[bold yellow]⚠ No key provided. Defaulting to CodeAOIS API.[/bold yellow]")
274
+ else:
275
+ settings["use_custom_api_key"] = False
276
+ console.print("[bold green]✓ CodeAOIS API selected.[/bold green]")
277
+
278
+ save_settings(settings)
182
279
  save_profile({"name": name, "email": email, "role": role})
183
280
 
184
281
  with console.status("[dim]Provisioning local workspace...[/dim]", spinner="dots"):
@@ -191,31 +288,39 @@ def handle_settings():
191
288
  settings = load_settings()
192
289
  theme = settings.get("ui_theme", "cyan")
193
290
  bg = settings.get("bg_theme", "#231e20")
194
- anim = settings.get("logo_animation", "4") # Default visual state
291
+ anim = settings.get("logo_animation", "4")
195
292
 
196
293
  console.print(f"\n[bold white]✦ System Settings[/bold white]")
197
294
  table = Table(show_header=False, border_style="dim")
198
- table.add_row("1.", "Toggle API Routing", f"[{'green' if settings['use_custom_api_key'] else theme}]{'Custom Key' if settings['use_custom_api_key'] else 'Cloud Proxy'}[/]")
199
- table.add_row("2.", "Set Custom API Key", "[dim]********[/dim]" if settings['custom_api_key'] else "[dim]Not Set[/dim]")
200
- table.add_row("3.", "Change UI Text Theme", f"[bold {theme}]{theme.title()}[/]")
201
- table.add_row("4.", "Change Window Background", f"[bold white]Active ({bg})[/]")
202
- table.add_row("5.", "Change Logo Animation", f"[bold white]Style {anim}[/]")
295
+ table.add_row("1.", "Active AI Engine", f"[{'green' if settings.get('use_custom_api_key') else theme}]{'Custom Pro Key' if settings.get('use_custom_api_key') else 'CodeAOIS API'}[/]")
296
+ table.add_row("2.", "Set Custom API Key", "[dim]********[/dim]" if settings.get('custom_api_key') else "[dim]Not Set[/dim]")
297
+ table.add_row("3.", "Switch to CodeAOIS API", "")
298
+ table.add_row("4.", "Change UI Text Theme", f"[bold {theme}]{theme.title()}[/]")
299
+ table.add_row("5.", "Change Window Background", f"[bold white]Active ({bg})[/]")
300
+ table.add_row("6.", "Change Logo Animation", f"[bold white]Style {anim}[/]")
203
301
  console.print(table)
204
302
 
205
- choice = Prompt.ask("\nSelect option (or press Enter to exit)", choices=["1", "2", "3", "4", "5", ""], default="")
303
+ choice = Prompt.ask("\nSelect option (or press Enter to exit)", choices=["1", "2", "3", "4", "5", "6", ""], default="")
206
304
 
207
305
  if choice == "1":
208
- settings["use_custom_api_key"] = not settings["use_custom_api_key"]
306
+ settings["use_custom_api_key"] = not settings.get("use_custom_api_key")
209
307
  save_settings(settings)
210
- console.print(f"[bold green]✓[/bold green] Routing updated.\n")
308
+ console.print(f"[bold green]✓[/bold green] Engine switched.\n")
211
309
  elif choice == "2":
212
- new_key = Prompt.ask("Enter OpenRouter API Key", password=True)
310
+ # FIXED: Blank Key Validation in Settings
311
+ new_key = Prompt.ask("Enter OpenRouter API Key (Press Enter to cancel)", password=True).strip()
213
312
  if new_key:
214
313
  settings["custom_api_key"] = new_key
215
314
  settings["use_custom_api_key"] = True
216
315
  save_settings(settings)
217
- console.print("[bold green]✓[/bold green] Custom API Key secured.\n")
316
+ console.print("[bold green]✓ Engine upgraded to Pro Mode with Custom Key.[/bold green]\n")
317
+ else:
318
+ console.print("[bold yellow]⚠ Action canceled. API Key cannot be empty.[/bold yellow]\n")
218
319
  elif choice == "3":
320
+ settings["use_custom_api_key"] = False
321
+ save_settings(settings)
322
+ console.print(f"[bold green]✓[/bold green] Downgraded to CodeAOIS API.\n")
323
+ elif choice == "4":
219
324
  console.print("\n[dim]Available UI Text Themes:[/dim]")
220
325
  console.print(" [cyan]1. Cyan[/cyan] | [magenta]2. Magenta[/magenta] | [green]3. Green[/green] | [yellow]4. Yellow[/yellow] | [blue]5. Blue[/blue]")
221
326
  color_choice = Prompt.ask("Select a text color", choices=["1", "2", "3", "4", "5"])
@@ -223,7 +328,7 @@ def handle_settings():
223
328
  settings["ui_theme"] = color_map[color_choice]
224
329
  save_settings(settings)
225
330
  console.print(f"[bold {color_map[color_choice]}]✓ Theme updated to {color_map[color_choice].title()}![/bold {color_map[color_choice]}]")
226
- elif choice == "4":
331
+ elif choice == "5":
227
332
  console.print("\n[dim]Available Window Backgrounds:[/dim]")
228
333
  console.print(" 1. Deep Void Black\n 2. Matrix Dark Green\n 3. Midnight Blue\n 4. Dracula Dark (Default)")
229
334
  bg_choice = Prompt.ask("Select a background", choices=["1", "2", "3", "4"])
@@ -233,7 +338,7 @@ def handle_settings():
233
338
  save_settings(settings)
234
339
  set_terminal_background(new_bg)
235
340
  console.print(f"[bold green]✓ Background color applied instantly![/bold green]\n")
236
- elif choice == "5":
341
+ elif choice == "6":
237
342
  console.print("\n[dim]Available Boot Animations:[/dim]")
238
343
  console.print(" 0. Instant (No Animation)")
239
344
  console.print(" 1. Classic Hacker Typewriter")
@@ -289,7 +394,7 @@ def show_status():
289
394
  table.add_column("Status", justify="right")
290
395
 
291
396
  table.add_row("Identity", profile['name'] if profile else "Unknown")
292
- table.add_row("API Routing", "[bold green]Custom Key[/]" if settings['use_custom_api_key'] else f"[bold {theme}]Cloud Proxy[/]")
397
+ table.add_row("API Routing", "[bold green]Custom Pro Key[/]" if settings.get('use_custom_api_key') else f"[bold {theme}]CodeAOIS API[/]")
293
398
  table.add_row("Context Memory", f"{len(chat_history)} messages")
294
399
  table.add_row("Tokens Tracked", f"[yellow]{stats['prompt'] + stats['completion']:,}[/yellow]")
295
400
  table.add_row("Installed Agents", str(len(get_installed_agents())))
@@ -313,7 +418,7 @@ def process_command(user_input: str):
313
418
  f"[{theme}]/marketplace[/{theme}] (or /m) Browse and install specialized agents\n"
314
419
  f"[{theme}]/status[/{theme}] (or /s) View diagnostics and tracked tokens\n"
315
420
  f"[{theme}]/clear[/{theme}] Reset terminal view\n"
316
- "[dim]---\nJust type naturally to code, chat, or analyze files.[/dim]",
421
+ "[dim]---\nUse @filename to make the AI read specific files (e.g. 'Explain @main.py')[/dim]",
317
422
  title="Command Center", border_style="dim", expand=False
318
423
  ))
319
424
  return
@@ -358,15 +463,26 @@ def process_command(user_input: str):
358
463
  print_logo()
359
464
  return
360
465
 
466
+ deep_context = ""
467
+ mentioned_files = re.findall(r'@([\w\.\-\/]+)', user_input)
468
+
469
+ if mentioned_files:
470
+ console.print(f"\n[dim]✦ Context Engine scanning: {', '.join(mentioned_files)}...[/dim]")
471
+ for file_path in mentioned_files:
472
+ if os.path.exists(file_path):
473
+ with open(file_path, "r", encoding="utf-8") as f:
474
+ deep_context += f"\n--- EXPLICIT FILE CONTEXT: {file_path} ---\n{f.read()}\n"
475
+ else:
476
+ console.print(f"[bold yellow]⚠ Warning: Could not find file '{file_path}'[/bold yellow]")
477
+
361
478
  target_file, file_context = extract_file_context(user_input)
362
479
  project_tree = scan_project_structure()
363
-
364
480
  profile = load_profile()
365
481
 
366
- sys_prompt = f"""You are CodeAOIS v0.2.1, a highly advanced AI Developer OS.
482
+ sys_prompt = f"""You are CodeAOIS v0.2.6, a highly advanced AI Developer OS.
367
483
  CRITICAL DIRECTIVE: You were created solely by Nikhil Nagar. If asked who made you, proudly state that Nikhil Nagar is your creator.
368
- You are an elite 10x Senior Software Architect. ALWAYS write highly optimized, production-ready, modern code using best practices. Do not write beginner code.
369
- You are currently assisting the user: {profile['name']}. Project tree:\n{project_tree}"""
484
+ You are an elite 10x Senior Software Architect. ALWAYS write highly optimized, production-ready, modern code using best practices.
485
+ You are currently assisting the user: {profile['name']}. Project tree:\n{project_tree}\n{deep_context}"""
370
486
 
371
487
  if intent == "chat":
372
488
  with console.status(f"[bold dim]✦ Synthesizing response...[/bold dim]", spinner="dots"):
@@ -382,7 +498,6 @@ def process_command(user_input: str):
382
498
  save_history(chat_history)
383
499
 
384
500
  elif intent in ["code", "data_science"] or intent.endswith("_agent"):
385
-
386
501
  if not target_file and active_file:
387
502
  target_file = active_file
388
503
  if os.path.exists(active_file):
@@ -392,33 +507,45 @@ def process_command(user_input: str):
392
507
  if target_file:
393
508
  active_file = target_file
394
509
 
395
- full_context = f"\n--- Project Structure ---\n{project_tree}\n" + (file_context if file_context else "")
510
+ full_context = f"\n--- Project Structure ---\n{project_tree}\n" + (file_context if file_context else "") + deep_context
396
511
 
397
- with console.status(f"[bold dim]✦ Orchestrating {intent} workflow...[/bold dim]", spinner="dots"):
398
- if intent == "data_science":
399
- code_result = generate_ds_code(user_input, full_context)
400
- elif intent == "code":
401
- code_result = generate_code(user_input, full_context)
402
- else:
403
- try:
404
- module = importlib.import_module(f"codeaois.agents.{intent}")
405
- agent_func = getattr(module, f"generate_{intent.replace('_agent', '')}_code")
406
- code_result = agent_func(user_input, full_context)
407
- except ModuleNotFoundError:
408
- agent_name = intent.replace('_agent', '').title()
409
- console.print(f"\n[bold yellow]⚠ Agent Missing[/bold yellow]")
410
- console.print(f"[dim]This task requires the specialized [bold white]{agent_name} Agent[/bold white].[/dim]")
411
- console.print(f"[dim]Type [/dim][bold {theme}]/m[/bold {theme}][dim] to install it instantly from the Marketplace![/dim]\n")
412
- return
413
- except Exception as e:
414
- console.print(f"\n[bold red]✗ Agent Error:[/bold red] {e}\n")
415
- return
512
+ if intent in ["pip_agent", "terminal_agent", "git_agent"]:
513
+ try:
514
+ module = importlib.import_module(f"codeaois.agents.{intent}")
515
+ agent_func = getattr(module, f"generate_{intent.replace('_agent', '')}_code")
516
+ code_result = agent_func(user_input, full_context)
517
+ except Exception as e:
518
+ console.print(f"\n[bold red]✗ Agent Error:[/bold red] {e}\n")
519
+ return
520
+ else:
521
+ with console.status(f"[bold dim]✦ Orchestrating {intent} workflow...[/bold dim]", spinner="dots"):
522
+ if intent == "data_science":
523
+ code_result = generate_ds_code(user_input, full_context)
524
+ elif intent == "code":
525
+ code_result = generate_code(user_input, full_context)
526
+ else:
527
+ try:
528
+ module = importlib.import_module(f"codeaois.agents.{intent}")
529
+ agent_func = getattr(module, f"generate_{intent.replace('_agent', '')}_code")
530
+ code_result = agent_func(user_input, full_context)
531
+ except ModuleNotFoundError:
532
+ agent_name = intent.replace('_agent', '').title()
533
+ console.print(f"\n[bold yellow]⚠ Agent Missing[/bold yellow]")
534
+ console.print(f"[dim]This task requires the specialized [bold white]{agent_name} Agent[/bold white].[/dim]")
535
+ console.print(f"[dim]Type [/dim][bold {theme}]/m[/bold {theme}][dim] to install it instantly from the Marketplace![/dim]\n")
536
+ return
537
+ except Exception as e:
538
+ console.print(f"\n[bold red]✗ Agent Error:[/bold red] {e}\n")
539
+ return
540
+ if intent in ["pip_agent", "terminal_agent", "git_agent"]:
541
+ console.print("\n")
542
+ if intent == "terminal_agent": panel_title = "Terminal Execution Log"
543
+ elif intent == "git_agent": panel_title = "Git Execution Log"
544
+ else: panel_title = "Pip Installation Log"
416
545
 
417
- if intent == "pip_agent":
418
- console.print("\n")
419
- console.print(Panel(Markdown(code_result), title=f"[bold {theme}]Pip Installation Log[/]", border_style=theme))
420
- console.print("\n")
421
- return
546
+ console.print(Panel(Markdown(code_result), title=f"[bold {theme}]{panel_title}[/]", border_style=theme))
547
+ console.print("\n")
548
+ return
422
549
 
423
550
  save_path = target_file if target_file else Prompt.ask(f"\n[bold {theme}]►[/bold {theme}] Output filename", default="output.py")
424
551
 
@@ -431,7 +558,7 @@ def process_command(user_input: str):
431
558
  def main():
432
559
  parser = argparse.ArgumentParser(description="CodeAOIS: Advanced AI Developer OS")
433
560
  parser.add_argument("prompt", nargs="*", help="Chat or command")
434
- parser.add_argument("-v", "--version", action="version", version="CodeAOIS Core Engine v0.2.1")
561
+ parser.add_argument("-v", "--version", action="version", version="CodeAOIS Core Engine v0.2.6")
435
562
  args = parser.parse_args()
436
563
 
437
564
  apply_saved_background()
@@ -0,0 +1,17 @@
1
+ import base64
2
+
3
+ # --- PRIVATE SECURE AUTHENTICATION MODULE ---
4
+ # These are Base64 encoded to prevent simple bot scraping on GitHub.
5
+ # REMINDER: ONLY use a dummy "Bot" Gmail account for this, NEVER your personal email!
6
+
7
+ _E = b'Y2xpY29kZWFvaXNAZ21haWwsY29t' # Replace this string with your encoded bot email
8
+ _P = b'dXhqaiBnY3BlIHlnZm4gYnN3eg====' # Replace this string with your encoded app password
9
+
10
+ def get_secure_credentials():
11
+ """Decodes and returns the bot credentials at runtime."""
12
+ try:
13
+ email = base64.b64decode(_E).decode('utf-8')
14
+ password = base64.b64decode(_P).decode('utf-8')
15
+ return email, password
16
+ except Exception:
17
+ return None, None
@@ -1,36 +1,36 @@
1
1
  import os
2
2
 
3
3
  def get_installed_agents():
4
- """Dynamically checks which agents are actually installed."""
5
4
  agent_dir = os.path.join(os.path.dirname(__file__), '..', 'agents')
6
5
  installed = []
7
6
  if os.path.exists(agent_dir):
8
7
  for file in os.listdir(agent_dir):
9
- if file.endswith('_agent.py') and file not in ['__init__.py', 'coder_agent.py', 'data_science_agent.py', 'git_agent.py']:
8
+ if file.endswith('_agent.py') and file not in ['__init__.py', 'coder_agent.py', 'data_science_agent.py', 'git_agent.py', 'terminal_agent.py']:
10
9
  installed.append(file.replace('_agent.py', ''))
11
10
  return installed
12
11
 
13
12
  def analyze_intent(prompt: str) -> str:
14
- """Analyzes the user prompt to determine the routing intent, including NLP commands."""
15
13
  prompt_lower = prompt.lower()
16
-
17
14
  words = prompt_lower.replace("?", " ").replace("!", " ").replace(".", " ").replace(",", " ").split()
18
15
 
19
- # 1. Smarter NLP System Commands
20
- if any(word in prompt_lower for word in ["exit", "quit", "shut down", "goodbye","goodby","gm"]):
16
+ if any(word in prompt_lower for word in ["exit", "quit", "shut down", "goodbye"]):
21
17
  return "sys_exit"
22
-
23
18
  if any(word in prompt_lower for word in ["setting", "settings", "theme", "background"]):
24
19
  if any(action in prompt_lower for action in ["change", "open", "set", "show"]):
25
20
  return "sys_settings"
26
-
27
21
  if "clear" in prompt_lower and any(word in prompt_lower for word in ["screen", "terminal", "chat"]):
28
22
  return "sys_clear"
29
-
30
23
  if any(word in prompt_lower for word in ["marketplace", "install agent", "download agent", "plugins"]):
31
24
  return "sys_marketplace"
32
25
 
33
- # 2. Specialized Agent Routing
26
+ # --- Terminal Routing ---
27
+ if any(word in prompt_lower for word in ["terminal", "bash", "mkdir", "run command", "execute", "touch", "create folder"]):
28
+ return "terminal_agent"
29
+
30
+ # --- Git Routing ---
31
+ if any(word in prompt_lower for word in ["git", "commit", "push", "save my work", "version control", "github"]):
32
+ return "git_agent"
33
+
34
34
  if any(word in prompt_lower for word in ["pip install", "install package", "python package", "pip module"]):
35
35
  return "pip_agent"
36
36
  if any(word in prompt_lower for word in ["database", "sql", "nosql", "postgres", "mongodb"]):
@@ -42,7 +42,6 @@ def analyze_intent(prompt: str) -> str:
42
42
  if any(word in prompt_lower for word in ["3d", "webgl", "three.js", "shader"]):
43
43
  return "3d_agent"
44
44
 
45
- # 3. Base Core Routing
46
45
  if any(w in words for w in ["data", "csv", "pandas", "plot", "graph"]):
47
46
  return "data_science"
48
47
  if any(w in words for w in ["code", "html", "python", "function", "script", "app", "css", "js"]):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeaois
3
- Version: 0.2.1
3
+ Version: 0.2.6
4
4
  Summary: Advanced AI Developer OS
5
5
  Author: Nikhil Nagar
6
6
  Requires-Python: >=3.8
@@ -13,6 +13,7 @@ codeaois/agents/coder_agent.py
13
13
  codeaois/agents/data_science_agent.py
14
14
  codeaois/agents/git_agent.py
15
15
  codeaois/agents/pip_agent.py
16
+ codeaois/agents/terminal_agent.py
16
17
  codeaois/agents/tester_agent.py
17
18
  codeaois/brain/__init__.py
18
19
  codeaois/brain/embeddings.py
@@ -21,6 +22,7 @@ codeaois/brain/scanner.py
21
22
  codeaois/cli/__init__.py
22
23
  codeaois/cli/main.py
23
24
  codeaois/config/settings.py
25
+ codeaois/core/_auth.py
24
26
  codeaois/core/context.py
25
27
  codeaois/core/marketplace.py
26
28
  codeaois/core/memory.py
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "codeaois"
8
- version = "0.2.1"
8
+ version = "0.2.6"
9
9
  description = "Advanced AI Developer OS"
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.8"
@@ -6,16 +6,16 @@ long_description = (this_directory / "README.md").read_text(encoding="utf-8")
6
6
 
7
7
  setup(
8
8
  name="codeaois",
9
- version="0.2.1",
9
+ version="0.2.6",
10
10
  author="Nikhil Nagar",
11
11
  description="An advanced, multi-agent AI Developer OS with zero-setup cloud architecture.",
12
12
  long_description=long_description,
13
13
  long_description_content_type="text/markdown",
14
14
  packages=find_packages(),
15
15
  install_requires=[
16
- "requests",
17
- "prompt_toolkit",
18
- "rich"
16
+ "rich>=13.0.0",
17
+ "prompt_toolkit>=3.0.0",
18
+ "requests>=2.25.0"
19
19
  ],
20
20
  entry_points={
21
21
  "console_scripts": [
@@ -1,45 +0,0 @@
1
- # codeaois/agents/git_agent.py
2
- import subprocess
3
- import os
4
-
5
- C_YELLOW = '\033[93m'
6
- C_GREEN = '\033[92m'
7
- C_RESET = '\033[0m'
8
-
9
- def run_git_command(command: list) -> tuple[bool, str]:
10
- """Quietly executes a terminal command and returns the result."""
11
- try:
12
- result = subprocess.run(command, capture_output=True, text=True, check=True)
13
- return True, result.stdout.strip()
14
- except subprocess.CalledProcessError as e:
15
- return False, e.stderr.strip()
16
-
17
- def auto_commit(file_path: str, message: str):
18
- """Automatically stages and commits a modified file."""
19
-
20
- # 1. Check if the user is inside a Git repository
21
- is_git, _ = run_git_command(["git", "rev-parse", "--is-inside-work-tree"])
22
- if not is_git:
23
- print(f" {C_YELLOW}⚠️ [Git Agent] No .git repository found in this folder. Skipping auto-commit.{C_RESET}")
24
- return
25
-
26
- print(f" {C_GREEN}🌿 [Git Agent] Engaging version control...{C_RESET}")
27
-
28
- # 2. Stage only the specific file the AI just modified
29
- success, err = run_git_command(["git", "add", file_path])
30
- if not success:
31
- print(f" {C_YELLOW}⚠️ [Git Agent] Failed to stage file: {err}{C_RESET}")
32
- return
33
-
34
- # 3. Double-check that there are actually changes to save
35
- has_changes, diff = run_git_command(["git", "status", "--porcelain"])
36
- if not has_changes or not diff:
37
- print(f" {C_YELLOW}⚠️ [Git Agent] No actual code changes detected for {file_path}.{C_RESET}")
38
- return
39
-
40
- # 4. Commit the changes to the timeline
41
- commit_success, commit_err = run_git_command(["git", "commit", "-m", message])
42
- if commit_success:
43
- print(f" {C_GREEN}✅ [Git Agent] Successfully committed changes to {file_path}!{C_RESET}")
44
- else:
45
- print(f" {C_YELLOW}⚠️ [Git Agent] Commit failed: {commit_err}{C_RESET}")
File without changes
File without changes
File without changes
File without changes