aizen-ai-cli 2.2.2__tar.gz → 2.2.4__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 (34) hide show
  1. {aizen_ai_cli-2.2.2/aizen_ai_cli.egg-info → aizen_ai_cli-2.2.4}/PKG-INFO +1 -1
  2. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen/config.py +40 -23
  3. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen/main.py +30 -37
  4. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen/tools.py +22 -13
  5. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4/aizen_ai_cli.egg-info}/PKG-INFO +1 -1
  6. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/pyproject.toml +1 -1
  7. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/setup.py +1 -1
  8. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/MANIFEST.in +0 -0
  9. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/README.md +0 -0
  10. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen/__init__.py +0 -0
  11. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen/commands.py +0 -0
  12. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen/context.py +0 -0
  13. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen/exceptions.py +0 -0
  14. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen/logging_config.py +0 -0
  15. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen/mcp.py +0 -0
  16. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen/plugins.py +0 -0
  17. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen/retry.py +0 -0
  18. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen/session.py +0 -0
  19. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen/utils.py +0 -0
  20. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen_ai_cli.egg-info/SOURCES.txt +0 -0
  21. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen_ai_cli.egg-info/dependency_links.txt +0 -0
  22. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen_ai_cli.egg-info/entry_points.txt +0 -0
  23. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen_ai_cli.egg-info/requires.txt +0 -0
  24. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/aizen_ai_cli.egg-info/top_level.txt +0 -0
  25. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/requirements.txt +0 -0
  26. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/setup.cfg +0 -0
  27. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/tests/test_commands.py +0 -0
  28. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/tests/test_config.py +0 -0
  29. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/tests/test_context.py +0 -0
  30. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/tests/test_main.py +0 -0
  31. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/tests/test_mcp.py +0 -0
  32. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/tests/test_session.py +0 -0
  33. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/tests/test_tools.py +0 -0
  34. {aizen_ai_cli-2.2.2 → aizen_ai_cli-2.2.4}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aizen-ai-cli
3
- Version: 2.2.2
3
+ Version: 2.2.4
4
4
  Summary: Aizen AI Agent — A professional-grade AI coding assistant for your terminal.
5
5
  Author: Irtaza Malik
6
6
  License: MIT
@@ -3,12 +3,12 @@ import json
3
3
  import logging
4
4
  import os
5
5
  import shutil
6
+ import ssl
6
7
  import sys
7
8
  import threading
8
9
  import time
9
- import urllib.request
10
10
  import urllib.error
11
- import ssl
11
+ import urllib.request
12
12
  from importlib.metadata import PackageNotFoundError
13
13
  from importlib.metadata import version as _pkg_version
14
14
 
@@ -21,7 +21,7 @@ logger = logging.getLogger("aizen")
21
21
 
22
22
  # Read version from installed package metadata (stays in sync with pyproject.toml).
23
23
  # Falls back to a hardcoded value only when running from source without installing.
24
- _FALLBACK_VERSION = "2.2.2"
24
+ _FALLBACK_VERSION = "2.2.4"
25
25
  try:
26
26
  VERSION = _pkg_version("aizen-ai-cli")
27
27
  except PackageNotFoundError:
@@ -29,16 +29,16 @@ except PackageNotFoundError:
29
29
  CONFIG_PATH = os.path.expanduser("~/.aizen_config.json")
30
30
  SESSIONS_DIR = os.path.expanduser("~/.aizen_sessions")
31
31
  BACKUPS_DIR = os.path.expanduser("~/.aizen_backups")
32
- DEFAULT_MODEL = "nvidia/nemotron-3-super-120b-a12b:free"
33
-
34
- AIZEN_ASCII = r"""[bold magenta]
35
- _ _
36
- / \ (_)_______ _ __
37
- / _ \ | |_ / _ \ '_ \
38
- / ___ \| |/ / __/ | | |
39
- /_/ \_\_/___\___|_| |_|
40
- [/bold magenta]
41
- [dim]by Irtaza Malik[/dim]
32
+ DEFAULT_MODEL = "openrouter/free"
33
+
34
+ AIZEN_ASCII = r"""[bold #ffabf3]
35
+ █████╗ ██╗███████╗███████╗███╗ ██╗
36
+ ██╔══██╗██║╚══███╔╝██╔════╝████╗ ██║
37
+ ███████║██║ ███╔╝ █████╗ ██╔██╗ ██║
38
+ ██╔══██║██║ ███╔╝ ██╔══╝ ██║╚██╗██║
39
+ ██║ ██║██║███████╗███████╗██║ ╚████║
40
+ ╚═╝ ╚═╝╚═╝╚══════╝╚══════╝╚═╝ ╚═══╝
41
+ [/bold #ffabf3]
42
42
  """
43
43
 
44
44
  # Safe commands that auto-execute without confirmation
@@ -134,7 +134,7 @@ def build_system_prompt(config: dict | None = None) -> str:
134
134
  f"The following rules are defined by the project maintainers "
135
135
  f"(from {rules_file}):\n\n{project_rules}"
136
136
  )
137
- console.print(f" [dim]📋 Loaded project rules from {rules_file}[/dim]")
137
+ console.print(f"[bold #ffabf3][SYSTEM][/bold #ffabf3] Project rules loaded from [#d3fbff]{rules_file}[/#d3fbff]")
138
138
  break # Only use the first rules file found
139
139
  except Exception as e:
140
140
  logger.debug("Failed to load project rules from %s: %s", rules_file, e)
@@ -258,7 +258,7 @@ def _do_update_check(config: dict):
258
258
  ctx.load_verify_locations(cafile=certifi.where())
259
259
  except ImportError:
260
260
  ctx = ssl._create_unverified_context()
261
-
261
+
262
262
  url = "https://pypi.org/pypi/aizen-ai-cli/json"
263
263
  req = urllib.request.Request(url, headers={"User-Agent": "aizen-ai-cli"})
264
264
  with urllib.request.urlopen(req, timeout=3, context=ctx) as response:
@@ -273,12 +273,20 @@ def _do_update_check(config: dict):
273
273
  except Exception as e:
274
274
  logger.debug("Failed to save config after update check: %s", e)
275
275
 
276
- if latest != VERSION:
276
+ try:
277
+ latest_parts = tuple(map(int, latest.split('.')))
278
+ current_parts = tuple(map(int, VERSION.split('.')))
279
+ is_newer = latest_parts > current_parts
280
+ except Exception:
281
+ is_newer = latest != VERSION
282
+
283
+ if is_newer:
277
284
  console.print(
278
285
  f"\n[bold magenta]🔔 Update available:[/bold magenta] v{VERSION} → v{latest}"
279
286
  )
280
287
  console.print("[dim]Run: pip install -U aizen-ai-cli (or brew upgrade aizen)[/dim]")
281
- console.print("[dim]Then restart Aizen to use the new version![/dim]\n")
288
+ console.print("[dim]Then restart Aizen to use the new version![/dim]\n"
289
+ )
282
290
  except Exception as e:
283
291
  logger.debug("Update check failed (network/parsing): %s", e)
284
292
 
@@ -295,11 +303,20 @@ def check_for_updates(config: dict | None = None):
295
303
  # Check if we have a cached latest version that's newer
296
304
  cached = config.get("_latest_version")
297
305
  if cached and cached != VERSION:
298
- console.print(
299
- f"\n[bold magenta]🔔 Update available:[/bold magenta] v{VERSION} → v{cached}"
300
- )
301
- console.print("[dim]Run: pip install -U aizen-ai-cli (or brew upgrade aizen)[/dim]")
302
- console.print("[dim]Then restart Aizen to use the new version![/dim]\n")
306
+ try:
307
+ cached_parts = tuple(map(int, cached.split('.')))
308
+ current_parts = tuple(map(int, VERSION.split('.')))
309
+ is_newer = cached_parts > current_parts
310
+ except Exception:
311
+ is_newer = cached != VERSION
312
+
313
+ if is_newer:
314
+ console.print(
315
+ f"\n[bold magenta]🔔 Update available:[/bold magenta] v{VERSION} → v{cached}"
316
+ )
317
+ console.print("[dim]Run: pip install -U aizen-ai-cli (or brew upgrade aizen)[/dim]")
318
+ console.print("[dim]Then restart Aizen to use the new version![/dim]\n"
319
+ )
303
320
  return
304
321
 
305
322
  thread = threading.Thread(target=_do_update_check, args=(config,), daemon=True)
@@ -329,7 +346,7 @@ def _do_fetch_models():
329
346
  ctx.load_verify_locations(cafile=certifi.where())
330
347
  except ImportError:
331
348
  ctx = ssl._create_unverified_context()
332
-
349
+
333
350
  req = urllib.request.Request("https://openrouter.ai/api/v1/models")
334
351
  with urllib.request.urlopen(req, timeout=5, context=ctx) as response:
335
352
  data = json.loads(response.read().decode())
@@ -14,15 +14,15 @@ import sys
14
14
  from typing import Any
15
15
 
16
16
  from openai import APIConnectionError as OpenAIConnectionError
17
- from openai import APITimeoutError, AsyncOpenAI, AuthenticationError
17
+ from openai import APITimeoutError, AsyncOpenAI, AuthenticationError, BadRequestError
18
18
  from openai import RateLimitError as OpenAIRateLimitError
19
19
  from prompt_toolkit import PromptSession
20
20
  from prompt_toolkit.filters import completion_is_selected, has_completions
21
- from prompt_toolkit.formatted_text import HTML
21
+ from prompt_toolkit.formatted_text import FormattedText
22
22
  from prompt_toolkit.key_binding import KeyBindings
23
23
  from rich.live import Live
24
24
  from rich.markdown import Markdown
25
- from rich.panel import Panel
25
+ from rich.spinner import Spinner
26
26
  from rich.text import Text
27
27
 
28
28
  from .commands import AizenCompleter, handle_slash_command
@@ -212,16 +212,12 @@ async def main_loop():
212
212
 
213
213
  # ── Header ──
214
214
  console.print(AIZEN_ASCII)
215
- header = Text()
216
- header.append(f"v{VERSION}", style="bold magenta")
217
- header.append(" │ ", style="dim")
218
- header.append(get_active_model(), style="cyan")
215
+ console.print(f"[bold #ffabf3][SYSTEM][/bold #ffabf3] Initializing Aizen AI v{VERSION}...")
216
+ console.print(f"[bold #ffabf3][SYSTEM][/bold #ffabf3] Model: {get_active_model()}")
219
217
  if auto_approve:
220
- header.append(" │ ", style="dim")
221
- header.append("YOLO MODE", style="bold red")
222
- console.print(header)
218
+ console.print("[bold #ffabf3][SYSTEM][/bold #ffabf3] Mode: YOLO")
223
219
  console.print(
224
- "[dim]Type /help for commands • @file to attach • exit to quit[/dim]\n"
220
+ "\n[dim]Type /help for commands • @file to attach • exit to quit[/dim]\n"
225
221
  )
226
222
 
227
223
  # ── Keybindings ──
@@ -239,10 +235,12 @@ async def main_loop():
239
235
  try:
240
236
  # ── Multi-line Input ──
241
237
  lines = []
242
- prompt_html = HTML(
243
- "<ansimagenta>╭─</ansimagenta> <ansimagenta><b>👤 You</b></ansimagenta>\n"
244
- "<ansimagenta>╰─❯</ansimagenta> "
245
- )
238
+ prompt_html = FormattedText([
239
+ ("fg:#ffabf3", "➜"),
240
+ ("", " "),
241
+ ("fg:#d3fbff", "~"),
242
+ ("", " ")
243
+ ])
246
244
  first_line = await session.prompt_async(prompt_html)
247
245
  lines.append(first_line)
248
246
 
@@ -250,7 +248,7 @@ async def main_loop():
250
248
  while lines[-1].rstrip().endswith("\\"):
251
249
  lines[-1] = lines[-1].rstrip()[:-1] # Remove trailing backslash
252
250
  continuation = await session.prompt_async(
253
- HTML("<ansimagenta> ⋮ </ansimagenta> ")
251
+ FormattedText([("", " ")])
254
252
  )
255
253
  lines.append(continuation)
256
254
 
@@ -346,9 +344,7 @@ async def main_loop():
346
344
  "Exploring...",
347
345
  ]
348
346
  )
349
- spinner_display = Text()
350
- spinner_display.append(" ✦ ", style="bold magenta")
351
- spinner_display.append(spinner_label, style="dim italic")
347
+ spinner_display = Spinner("dots", text=Text(spinner_label, style="#8e8e93 italic"), style="#ffabf3 bold")
352
348
 
353
349
  try:
354
350
  with Live(
@@ -378,23 +374,14 @@ async def main_loop():
378
374
  full_content += delta.content
379
375
  # Live-render Markdown in a panel
380
376
  try:
381
- rendered = Panel(
382
- Markdown(full_content),
383
- title="[bold magenta]✦ Aizen[/bold magenta]",
384
- border_style="magenta",
385
- padding=(1, 2),
386
- )
377
+ # Prepend AIZEN: styling before the markdown
378
+ display_content = f"**AIZEN:** {full_content}"
379
+ rendered = Markdown(display_content)
387
380
  live.update(rendered)
388
381
  except Exception:
389
382
  # Fallback for incomplete markdown
390
- live.update(
391
- Panel(
392
- Text(full_content),
393
- title="[bold magenta]✦ Aizen[/bold magenta]",
394
- border_style="magenta",
395
- padding=(1, 2),
396
- )
397
- )
383
+ display_text = Text.from_markup(f"[bold #ffabf3]AIZEN:[/bold #ffabf3] {full_content}")
384
+ live.update(display_text)
398
385
 
399
386
  # ── Tool call tokens ──
400
387
  if delta.tool_calls:
@@ -427,10 +414,9 @@ async def main_loop():
427
414
  ]
428
415
  if names and not full_content:
429
416
  tool_text = Text()
430
- tool_text.append(" ⚙️ ", style="magenta")
417
+ tool_text.append("AIZEN: ", style="bold #ffabf3")
431
418
  tool_text.append(
432
- f"Preparing: {', '.join(names)}",
433
- style="dim italic",
419
+ f"Invoking [#d3fbff]{', '.join(names)}[/#d3fbff]...",
434
420
  )
435
421
  live.update(tool_text)
436
422
 
@@ -470,8 +456,15 @@ async def main_loop():
470
456
  "[dim]Hint: Check your internet connection or API base URL.[/dim]"
471
457
  )
472
458
  break
459
+ except BadRequestError as e:
460
+ logger.error("Bad request to API: %s", e)
461
+ console.print(f"\n[bold red]Bad Request Error:[/bold red] {e}")
462
+ console.print(
463
+ "[dim]Hint: This usually means the model ID is invalid or the context length was exceeded.[/dim]"
464
+ )
465
+ break
473
466
  except Exception as e:
474
- logger.exception("Unexpected API error: %s", e)
467
+ logger.error("Unexpected API error: %s", e)
475
468
  console.print(f"\n[bold red]API Error:[/bold red] {e}")
476
469
  error_str = str(e).lower()
477
470
  if "401" in error_str or "unauthorized" in error_str:
@@ -697,19 +697,28 @@ def run_command_impl(command: str, auto_approve: bool = False, timeout: int = 12
697
697
  logger.warning("Command timed out after %ds: %s", timeout, command)
698
698
  return f"Error: Command timed out after {timeout} seconds."
699
699
 
700
- # Read available stdout non-blockingly
701
- if proc.stdout:
702
- rlist, _, _ = select.select([proc.stdout], [], [], 0.1)
703
- if rlist:
704
- line = proc.stdout.readline()
705
- if line:
706
- stdout_lines.append(line)
707
- # Show live output tail (last 15 lines)
708
- tail = "".join(stdout_lines[-15:])
709
- display = Text()
710
- display.append(f" ▶ Running ({elapsed:.0f}s)\n", style="dim italic")
711
- display.append(tail.rstrip(), style="dim")
712
- live.update(display)
700
+ # Read available stdout and stderr non-blockingly
701
+ reads = []
702
+ if proc.stdout: reads.append(proc.stdout)
703
+ if proc.stderr: reads.append(proc.stderr)
704
+
705
+ if reads:
706
+ rlist, _, _ = select.select(reads, [], [], 0.1)
707
+ for fd in rlist:
708
+ if fd == proc.stdout:
709
+ line = proc.stdout.readline()
710
+ if line:
711
+ stdout_lines.append(line)
712
+ # Show live output tail (last 15 lines)
713
+ tail = "".join(stdout_lines[-15:])
714
+ display = Text()
715
+ display.append(f" ▶ Running ({elapsed:.0f}s)\n", style="dim italic")
716
+ display.append(tail.rstrip(), style="dim")
717
+ live.update(display)
718
+ elif fd == proc.stderr:
719
+ line = proc.stderr.readline()
720
+ if line:
721
+ stderr_lines.append(line)
713
722
 
714
723
  # Read remaining output after process exits
715
724
  if proc.stdout:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aizen-ai-cli
3
- Version: 2.2.2
3
+ Version: 2.2.4
4
4
  Summary: Aizen AI Agent — A professional-grade AI coding assistant for your terminal.
5
5
  Author: Irtaza Malik
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "aizen-ai-cli"
7
- version = "2.2.2"
7
+ version = "2.2.4"
8
8
  description = "Aizen AI Agent — A professional-grade AI coding assistant for your terminal."
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -8,7 +8,7 @@ def parse_requirements(filename):
8
8
 
9
9
  setup(
10
10
  name="aizen-ai-cli",
11
- version="2.2.2",
11
+ version="2.2.4",
12
12
  description="Aizen AI Agent — A professional-grade AI coding assistant for your terminal.",
13
13
  packages=["aizen"],
14
14
  install_requires=parse_requirements("requirements.txt"),
File without changes
File without changes
File without changes
File without changes