diff --git a/mini_agent/agent.py b/mini_agent/agent.py index 7864b1d..43c7c8f 100644 --- a/mini_agent/agent.py +++ b/mini_agent/agent.py @@ -2,6 +2,7 @@ import json from pathlib import Path +from time import perf_counter import tiktoken @@ -285,8 +286,10 @@ async def run(self) -> str: print(f"{Colors.DIM}📝 Log file: {self.logger.get_log_file_path()}{Colors.RESET}") step = 0 + step_start_time = None while step < self.max_steps: + step_start_time = perf_counter() # Check and summarize message history to prevent context overflow await self._summarize_messages() @@ -353,6 +356,8 @@ async def run(self) -> str: # Check if task is complete (no tool calls) if not response.tool_calls: + step_elapsed = perf_counter() - step_start_time + print(f"{Colors.DIM}⏱️ Step {step + 1} completed in {step_elapsed:.2f}s{Colors.RESET}") return response.content # Execute tool calls @@ -428,6 +433,9 @@ async def run(self) -> str: ) self.messages.append(tool_msg) + step_elapsed = perf_counter() - step_start_time + print(f"{Colors.DIM}⏱️ Step {step + 1} completed in {step_elapsed:.2f}s{Colors.RESET}") + step += 1 # Max steps reached diff --git a/mini_agent/cli.py b/mini_agent/cli.py index 0280b7d..0daffd7 100644 --- a/mini_agent/cli.py +++ b/mini_agent/cli.py @@ -11,6 +11,8 @@ import argparse import asyncio +import platform +import subprocess from datetime import datetime from pathlib import Path from typing import List @@ -173,6 +175,8 @@ def print_stats(agent: Agent, session_start: datetime): print(f" - Assistant Replies: {Colors.BRIGHT_BLUE}{assistant_msgs}{Colors.RESET}") print(f" - Tool Calls: {Colors.BRIGHT_YELLOW}{tool_msgs}{Colors.RESET}") print(f" Available Tools: {len(agent.tools)}") + if hasattr(agent, 'api_total_tokens') and agent.api_total_tokens > 0: + print(f" API Tokens Used: {Colors.BRIGHT_MAGENTA}{agent.api_total_tokens:,}{Colors.RESET}") print(f"{Colors.DIM}{'─' * 40}{Colors.RESET}\n") @@ -205,6 +209,10 @@ def parse_args() -> argparse.Namespace: version="mini-agent 0.1.0", ) + subparsers = parser.add_subparsers(dest="command", help="Available commands") + + log_parser = subparsers.add_parser("log", help="Show log directory and open file manager") + return parser.parse_args() @@ -451,7 +459,7 @@ def on_retry(exception: Exception, attempt: int): # 9. Setup prompt_toolkit session # Command completer command_completer = WordCompleter( - ["/help", "/clear", "/history", "/stats", "/exit", "/quit", "/q"], + ["/help", "/clear", "/history", "/stats", "/log", "/exit", "/quit", "/q"], ignore_case=True, sentence=True, ) @@ -540,6 +548,63 @@ def _(event): print_stats(agent, session_start) continue + elif command.startswith("/log"): + log_dir = Path.home() / ".mini-agent" / "log" + + if command == "/log": + print(f"\n{Colors.BRIGHT_CYAN}📁 Log Directory: {log_dir}{Colors.RESET}") + if log_dir.exists() and log_dir.is_dir(): + log_files = list(log_dir.glob("*.log")) + if log_files: + log_files.sort(key=lambda x: x.stat().st_mtime, reverse=True) + print(f"{Colors.DIM}{'─' * 60}{Colors.RESET}") + print(f"{Colors.BOLD}{Colors.BRIGHT_YELLOW}Available Log Files:{Colors.RESET}") + for i, log_file in enumerate(log_files[:10], 1): + mtime = datetime.fromtimestamp(log_file.stat().st_mtime) + size = log_file.stat().st_size + print(f" {Colors.GREEN}{i:2d}.{Colors.RESET} {Colors.BRIGHT_WHITE}{log_file.name}{Colors.RESET}") + print(f" {Colors.DIM}Modified: {mtime.strftime('%Y-%m-%d %H:%M:%S')}, Size: {size:,} bytes{Colors.RESET}") + if len(log_files) > 10: + print(f" {Colors.DIM}... and {len(log_files) - 10} more files{Colors.RESET}") + print(f"{Colors.DIM}{'─' * 60}{Colors.RESET}") + if platform.system() == "Darwin": + subprocess.run(["open", str(log_dir)]) + elif platform.system() == "Windows": + subprocess.run(["explorer", str(log_dir)]) + elif platform.system() == "Linux": + try: + subprocess.run(["xdg-open", str(log_dir)]) + except FileNotFoundError: + print(f"{Colors.YELLOW}Could not open file manager. Please install xdg-utils or navigate manually.{Colors.RESET}") + else: + print(f"{Colors.YELLOW}No log files found in directory.{Colors.RESET}") + else: + print(f"{Colors.RED}Log directory does not exist: {log_dir}{Colors.RESET}") + print() + continue + + parts = command.split(maxsplit=1) + if len(parts) == 2: + pattern = parts[1].strip('"\'') + if pattern: + log_file = log_dir / pattern + if log_file.exists() and log_file.is_file(): + print(f"\n{Colors.BRIGHT_CYAN}📄 Reading: {log_file}{Colors.RESET}") + print(f"{Colors.DIM}{'─' * 80}{Colors.RESET}") + try: + with open(log_file, 'r', encoding='utf-8') as f: + content = f.read() + print(content) + print(f"{Colors.DIM}{'─' * 80}{Colors.RESET}") + print(f"\n{Colors.GREEN}✅ End of file{Colors.RESET}\n") + except Exception as e: + print(f"\n{Colors.RED}❌ Error reading file: {e}{Colors.RESET}\n") + else: + print(f"\n{Colors.RED}❌ Log file not found: {log_file}{Colors.RESET}\n") + else: + print(f"\n{Colors.YELLOW}Usage: /log \"filename.log\" or /log \"*.log\" pattern{Colors.RESET}\n") + continue + else: print(f"{Colors.RED}❌ Unknown command: {user_input}{Colors.RESET}") print(f"{Colors.DIM}Type /help to see available commands{Colors.RESET}\n") @@ -582,6 +647,39 @@ def main(): # Parse command line arguments args = parse_args() + # Handle log command + if args.command == "log": + log_dir = Path.home() / ".mini-agent" / "log" + print(f"{Colors.BRIGHT_CYAN}📁 Log Directory: {log_dir}{Colors.RESET}") + if log_dir.exists() and log_dir.is_dir(): + log_files = list(log_dir.glob("*.log")) + if log_files: + log_files.sort(key=lambda x: x.stat().st_mtime, reverse=True) + print(f"{Colors.DIM}{'─' * 60}{Colors.RESET}") + print(f"{Colors.BOLD}{Colors.BRIGHT_YELLOW}Available Log Files:{Colors.RESET}") + for i, log_file in enumerate(log_files[:10], 1): + mtime = datetime.fromtimestamp(log_file.stat().st_mtime) + size = log_file.stat().st_size + print(f" {Colors.GREEN}{i:2d}.{Colors.RESET} {Colors.BRIGHT_WHITE}{log_file.name}{Colors.RESET}") + print(f" {Colors.DIM}Modified: {mtime.strftime('%Y-%m-%d %H:%M:%S')}, Size: {size:,} bytes{Colors.RESET}") + if len(log_files) > 10: + print(f" {Colors.DIM}... and {len(log_files) - 10} more files{Colors.RESET}") + print(f"{Colors.DIM}{'─' * 60}{Colors.RESET}") + if platform.system() == "Darwin": + subprocess.run(["open", str(log_dir)]) + elif platform.system() == "Windows": + subprocess.run(["explorer", str(log_dir)]) + elif platform.system() == "Linux": + try: + subprocess.run(["xdg-open", str(log_dir)]) + except FileNotFoundError: + print(f"{Colors.YELLOW}Could not open file manager. Please install xdg-utils or navigate manually.{Colors.RESET}") + else: + print(f"{Colors.YELLOW}No log files found in directory.{Colors.RESET}") + else: + print(f"{Colors.RED}Log directory does not exist: {log_dir}{Colors.RESET}") + return + # Determine workspace directory if args.workspace: workspace_dir = Path(args.workspace).absolute()