[
  {
    "path": ".gitignore",
    "content": "uploads/\n*.pyc\n.DS_Store\n.env\n.venv/\n__pycache__/\n.ruff_cache/\n.pytest_cache/"
  },
  {
    "path": "Claude-Eng-v2/main.py",
    "content": "import os\nfrom dotenv import load_dotenv\nimport json\nfrom tavily import TavilyClient\nimport base64\nfrom PIL import Image\nimport io\nimport re\nfrom anthropic import Anthropic, APIStatusError, APIError\nimport difflib\nimport time\nfrom rich.console import Console\nfrom rich.panel import Panel\nfrom rich.syntax import Syntax\nfrom rich.markdown import Markdown\nimport asyncio\nfrom prompt_toolkit import PromptSession\nfrom prompt_toolkit.styles import Style\nimport glob\nimport speech_recognition as sr\nimport websockets\nfrom pydub import AudioSegment\nfrom pydub.playback import play\nimport datetime\nimport venv\nimport sys\nimport signal\nimport logging\nfrom typing import Tuple, Optional, Dict, Any\nimport mimetypes\nfrom rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn\nimport subprocess\nimport shutil\nfrom typing import AsyncIterable\n\n# Configure logging\nlogging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')\n\n# Load environment variables from .env file\nload_dotenv()\n\n# Define a list of voice commands\nVOICE_COMMANDS = {\n    \"exit voice mode\": \"exit_voice_mode\",\n    \"save chat\": \"save_chat\",\n    \"reset conversation\": \"reset_conversation\"\n}\n\n# Initialize recognizer and microphone as None\nrecognizer = None\nmicrophone = None\n\n# 11 Labs TTS\ntts_enabled = True\nuse_tts = False\nELEVEN_LABS_API_KEY = os.getenv('ELEVEN_LABS_API_KEY')\nVOICE_ID = 'YOUR VOICE ID'\nMODEL_ID = 'eleven_turbo_v2_5'\n\ndef is_installed(lib_name):\n    return shutil.which(lib_name) is not None\n\nasync def text_chunker(text: str) -> AsyncIterable[str]:\n    \"\"\"Split text into chunks, ensuring to not break sentences.\"\"\"\n    splitters = (\".\", \",\", \"?\", \"!\", \";\", \":\", \"—\", \"-\", \"(\", \")\", \"[\", \"]\", \"}\", \" \")\n    buffer = \"\"\n    \n    for char in text:\n        if buffer.endswith(splitters):\n            yield buffer + \" \"\n            buffer = char\n        elif char in splitters:\n            yield buffer + char + \" \"\n            buffer = \"\"\n        else:\n            buffer += char\n\n    if buffer:\n        yield buffer + \" \"\n\nasync def stream_audio(audio_stream):\n    \"\"\"Stream audio data using mpv player.\"\"\"\n    if not is_installed(\"mpv\"):\n        console.print(\"mpv not found. Installing alternative audio playback...\", style=\"bold yellow\")\n        # Fall back to pydub playback if mpv is not available\n        audio_data = b''.join([chunk async for chunk in audio_stream])\n        audio = AudioSegment.from_mp3(io.BytesIO(audio_data))\n        play(audio)\n        return\n\n    mpv_process = subprocess.Popen(\n        [\"mpv\", \"--no-cache\", \"--no-terminal\", \"--\", \"fd://0\"],\n        stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,\n    )\n\n    console.print(\"Started streaming audio\", style=\"bold green\")\n    try:\n        async for chunk in audio_stream:\n            if chunk:\n                mpv_process.stdin.write(chunk)\n                mpv_process.stdin.flush()\n    except Exception as e:\n        console.print(f\"Error during audio streaming: {str(e)}\", style=\"bold red\")\n    finally:\n        if mpv_process.stdin:\n            mpv_process.stdin.close()\n        mpv_process.wait()\n\nasync def text_to_speech(text):\n    if not ELEVEN_LABS_API_KEY:\n        console.print(\"ElevenLabs API key not found. Text-to-speech is disabled.\", style=\"bold yellow\")\n        console.print(text)\n        return\n\n    uri = f\"wss://api.elevenlabs.io/v1/text-to-speech/{VOICE_ID}/stream-input?model_id={MODEL_ID}\"\n    \n    try:\n        async with websockets.connect(uri, extra_headers={'xi-api-key': ELEVEN_LABS_API_KEY}) as websocket:\n            # Send initial message\n            await websocket.send(json.dumps({\n                \"text\": \" \",\n                \"voice_settings\": {\"stability\": 0.5, \"similarity_boost\": 0.75},\n                \"xi_api_key\": ELEVEN_LABS_API_KEY,\n            }))\n\n            # Set up listener for audio chunks\n            async def listen():\n                while True:\n                    try:\n                        message = await websocket.recv()\n                        data = json.loads(message)\n                        if data.get(\"audio\"):\n                            yield base64.b64decode(data[\"audio\"])\n                        elif data.get('isFinal'):\n                            break\n                    except websockets.exceptions.ConnectionClosed:\n                        logging.error(\"WebSocket connection closed unexpectedly\")\n                        break\n                    except Exception as e:\n                        logging.error(f\"Error processing audio message: {str(e)}\")\n                        break\n\n            # Start audio streaming task\n            stream_task = asyncio.create_task(stream_audio(listen()))\n\n            # Send text in chunks\n            async for chunk in text_chunker(text):\n                try:\n                    await websocket.send(json.dumps({\"text\": chunk, \"try_trigger_generation\": True}))\n                except Exception as e:\n                    logging.error(f\"Error sending text chunk: {str(e)}\")\n                    break\n\n            # Send closing message\n            await websocket.send(json.dumps({\"text\": \"\"}))\n\n            # Wait for streaming to complete\n            await stream_task\n\n    except websockets.exceptions.InvalidStatusCode as e:\n        logging.error(f\"Failed to connect to ElevenLabs API: {e}\")\n        console.print(f\"Failed to connect to ElevenLabs API: {e}\", style=\"bold red\")\n        console.print(\"Fallback: Printing the text instead.\", style=\"bold yellow\")\n        console.print(text)\n    except Exception as e:\n        logging.error(f\"Error in text-to-speech: {str(e)}\")\n        console.print(f\"Error in text-to-speech: {str(e)}\", style=\"bold red\")\n        console.print(\"Fallback: Printing the text instead.\", style=\"bold yellow\")\n        console.print(text)\n\ndef initialize_speech_recognition():\n    global recognizer, microphone\n    recognizer = sr.Recognizer()\n    microphone = sr.Microphone()\n    \n    # Adjust for ambient noise\n    with microphone as source:\n        recognizer.adjust_for_ambient_noise(source, duration=1)\n    \n    logging.info(\"Speech recognition initialized\")\n\nasync def voice_input(max_retries=3):\n    global recognizer, microphone\n\n    for attempt in range(max_retries):\n        # Reinitialize speech recognition objects before each attempt\n        initialize_speech_recognition()\n\n        try:\n            with microphone as source:\n                console.print(\"Listening... Speak now.\", style=\"bold green\")\n                audio = recognizer.listen(source, timeout=5)\n                \n            console.print(\"Processing speech...\", style=\"bold yellow\")\n            text = recognizer.recognize_google(audio)\n            console.print(f\"You said: {text}\", style=\"cyan\")\n            return text.lower()\n        except sr.WaitTimeoutError:\n            console.print(f\"No speech detected. Attempt {attempt + 1} of {max_retries}.\", style=\"bold red\")\n            logging.warning(f\"No speech detected. Attempt {attempt + 1} of {max_retries}\")\n        except sr.UnknownValueError:\n            console.print(f\"Speech was unintelligible. Attempt {attempt + 1} of {max_retries}.\", style=\"bold red\")\n            logging.warning(f\"Speech was unintelligible. Attempt {attempt + 1} of {max_retries}\")\n        except sr.RequestError as e:\n            console.print(f\"Could not request results from speech recognition service; {e}\", style=\"bold red\")\n            logging.error(f\"Could not request results from speech recognition service; {e}\")\n            return None\n        except Exception as e:\n            console.print(f\"Unexpected error in voice input: {str(e)}\", style=\"bold red\")\n            logging.error(f\"Unexpected error in voice input: {str(e)}\")\n            return None\n        \n        # Add a short delay between attempts\n        await asyncio.sleep(1)\n    \n    console.print(\"Max retries reached. Returning to text input mode.\", style=\"bold red\")\n    logging.info(\"Max retries reached in voice input. Returning to text input mode.\")\n    return None\n\ndef cleanup_speech_recognition():\n    global recognizer, microphone\n    recognizer = None\n    microphone = None\n    logging.info('Speech recognition objects cleaned up')\n\ndef process_voice_command(command):\n    if command in VOICE_COMMANDS:\n        action = VOICE_COMMANDS[command]\n        if action == \"exit_voice_mode\":\n            return False, \"Exiting voice mode.\"\n        elif action == \"save_chat\":\n            filename = save_chat()\n            return True, f\"Chat saved to {filename}\"\n        elif action == \"reset_conversation\":\n            reset_conversation()\n            return True, \"Conversation has been reset.\"\n    return True, None\n\nasync def get_user_input(prompt=\"You: \"):\n    style = Style.from_dict({\n        'prompt': 'cyan bold',\n    })\n    session = PromptSession(style=style)\n    return await session.prompt_async(prompt, multiline=False)\n\ndef setup_virtual_environment() -> Tuple[str, str]:\n    venv_name = \"code_execution_env\"\n    venv_path = os.path.join(os.getcwd(), venv_name)\n    try:\n        if not os.path.exists(venv_path):\n            venv.create(venv_path, with_pip=True)\n\n        # Activate the virtual environment\n        if sys.platform == \"win32\":\n            activate_script = os.path.join(venv_path, \"Scripts\", \"activate.bat\")\n        else:\n            activate_script = os.path.join(venv_path, \"bin\", \"activate\")\n\n        return venv_path, activate_script\n    except Exception as e:\n        logging.error(f\"Error setting up virtual environment: {str(e)}\")\n        raise\n\n# Initialize the Anthropic client\nanthropic_api_key = os.getenv(\"ANTHROPIC_API_KEY\")\nif not anthropic_api_key:\n    raise ValueError(\"ANTHROPIC_API_KEY not found in environment variables\")\nclient = Anthropic(api_key=anthropic_api_key)\n\n# Initialize the Tavily client\ntavily_api_key = os.getenv(\"TAVILY_API_KEY\")\nif not tavily_api_key:\n    raise ValueError(\"TAVILY_API_KEY not found in environment variables\")\ntavily = TavilyClient(api_key=tavily_api_key)\n\nconsole = Console()\n\n# Token tracking variables\nmain_model_tokens = {'input': 0, 'output': 0, 'cache_write': 0, 'cache_read': 0}\ntool_checker_tokens = {'input': 0, 'output': 0, 'cache_write': 0, 'cache_read': 0}\ncode_editor_tokens = {'input': 0, 'output': 0, 'cache_write': 0, 'cache_read': 0}\ncode_execution_tokens = {'input': 0, 'output': 0, 'cache_write': 0, 'cache_read': 0}\n\nUSE_FUZZY_SEARCH = True\n\n# Set up the conversation memory (maintains context for MAINMODEL)\nconversation_history = []\n\n# Store file contents (part of the context for MAINMODEL)\nfile_contents = {}\n\n# Code editor memory (maintains some context for CODEEDITORMODEL between calls)\ncode_editor_memory = []\n\n# Files already present in code editor's context\ncode_editor_files = set()\n\n# automode flag\nautomode = False\n\n# Global dictionary to store running processes\nrunning_processes = {}\n\n# Constants\nCONTINUATION_EXIT_PHRASE = \"AUTOMODE_COMPLETE\"\nMAX_CONTINUATION_ITERATIONS = 25\nMAX_CONTEXT_TOKENS = 200000  # Reduced to 200k tokens for context window\n\nMAINMODEL = \"claude-3-5-sonnet-20241022\"\nTOOLCHECKERMODEL = \"claude-3-5-sonnet-20241022\"\nCODEEDITORMODEL = \"claude-3-5-sonnet-20241022\"\nCODEEXECUTIONMODEL = \"claude-3-5-sonnet-20241022\"\n\n# System prompts\nBASE_SYSTEM_PROMPT = \"\"\"\nYou are Claude, an AI assistant powered by Anthropic's Claude-3.5-Sonnet model, specialized in software development with access to a variety of tools and the ability to instruct and direct a coding agent and a code execution one. Your capabilities include:\n\n<capabilities>\n1. Creating and managing project structures\n2. Writing, debugging, and improving code across multiple languages\n3. Providing architectural insights and applying design patterns\n4. Staying current with the latest technologies and best practices\n5. Analyzing and manipulating files within the project directory\n6. Performing web searches for up-to-date information\n7. Executing code and analyzing its output within an isolated 'code_execution_env' virtual environment\n8. Managing and stopping running processes started within the 'code_execution_env'\n9. Running shell commands.\n</capabilities>\n\nAvailable tools and their optimal use cases:\n\n<tools>\n1. create_folders: Create new folders at the specified paths, including nested directories. Use this to create one or more directories in the project structure, even complex nested structures in a single operation.\n2. create_files: Generate one or more new files with specified content. Strive to make the files as complete and useful as possible.\n3. edit_and_apply_multiple: Examine and modify one or more existing files by instructing a separate AI coding agent. You are responsible for providing clear, detailed instructions for each file. When using this tool:\n   - Provide comprehensive context about the project, including recent changes, new variables or functions, and how files are interconnected.\n   - Clearly state the specific changes or improvements needed for each file, explaining the reasoning behind each modification.\n   - Include ALL the snippets of code to change, along with the desired modifications.\n   - Specify coding standards, naming conventions, or architectural patterns to be followed.\n   - Anticipate potential issues or conflicts that might arise from the changes and provide guidance on how to handle them.\n   - IMPORTANT: Always provide the input in the following format:\n     {\n    \"files\": [\n        {\n            \"path\": \"app/templates/base.html\",\n            \"instructions\": \"Update the navigation bar for better UX.\"\n        },\n        {\n            \"path\": \"app/routes.py\",\n            \"instructions\": \"Refactor the route handling for scalability.\"\n        }\n    ],\n    \"project_context\": \"Overall context about the project and desired changes.\"\n}\n\n   - Ensure that the \"files\" key contains a list of dictionaries, even if you're only editing one file.\n   - Always include the \"project_context\" key with relevant information.\n4. execute_code: Run Python code exclusively in the 'code_execution_env' virtual environment and analyze its output. Use this when you need to test code functionality or diagnose issues. Remember that all code execution happens in this isolated environment. This tool returns a process ID for long-running processes.\n5. stop_process: Stop a running process by its ID. Use this when you need to terminate a long-running process started by the execute_code tool.\n6. read_multiple_files: Read the contents of one or more existing files, supporting wildcards (e.g., '*.py') and recursive directory reading. This tool can handle single or multiple file paths, directory paths, and wildcard patterns. Use this when you need to examine or work with file contents, especially for multiple files or entire directories.\n IMPORTANT: Before using the read_multiple_files tool, always check if the files you need are already in your context (system prompt).\n    If the file contents are already available to you, use that information directly instead of calling the read_multiple_files tool.\n    Only use the read_multiple_files tool for files that are not already in your context.\n7. list_files: List all files and directories in a specified folder.\n8. tavily_search: Perform a web search using the Tavily API for up-to-date information.\n9. scan_folder: Scan a specified folder and create a Markdown file with the contents of all coding text files, excluding binary files and common ignored folders. Use this tool to generate comprehensive documentation of project structures.\n10. run_shell_command: Execute a shell command and return its output. Use this tool when you need to run system commands or interact with the operating system. Ensure the command is safe and appropriate for the current operating system.\nIMPORTANT: Use this tool to install dependencies in the code_execution_env when using the execute_code tool.\n</tools>\n\n<tool_usage_guidelines>\nTool Usage Guidelines:\n- Always use the most appropriate tool for the task at hand.\n- Provide detailed and clear instructions when using tools, especially for edit_and_apply_multiple.\n- After making changes, always review the output to ensure accuracy and alignment with intentions.\n- Use execute_code to run and test code within the 'code_execution_env' virtual environment, then analyze the results.\n- For long-running processes, use the process ID returned by execute_code to stop them later if needed.\n- Proactively use tavily_search when you need up-to-date information or additional context.\n- When working with files, use read_multiple_files for both single and multiple file read making sure that the files are not already in your context.\n</tool_usage_guidelines>\n\n<error_handling>\nError Handling and Recovery:\n- If a tool operation fails, carefully analyze the error message and attempt to resolve the issue.\n- For file-related errors, double-check file paths and permissions before retrying.\n- If a search fails, try rephrasing the query or breaking it into smaller, more specific searches.\n- If code execution fails, analyze the error output and suggest potential fixes, considering the isolated nature of the environment.\n- If a process fails to stop, consider potential reasons and suggest alternative approaches.\n</error_handling>\n\n<project_management>\nProject Creation and Management:\n1. Start by creating a root folder for new projects.\n2. Create necessary subdirectories and files within the root folder.\n3. Organize the project structure logically, following best practices for the specific project type.\n</project_management>\n\nAlways strive for accuracy, clarity, and efficiency in your responses and actions. Your instructions must be precise and comprehensive. If uncertain, use the tavily_search tool or admit your limitations. When executing code, always remember that it runs in the isolated 'code_execution_env' virtual environment. Be aware of any long-running processes you start and manage them appropriately, including stopping them when they are no longer needed.\n\n<tool_usage_best_practices>\nWhen using tools:\n1. Carefully consider if a tool is necessary before using it.\n2. Ensure all required parameters are provided and valid.\n3. When using edit_and_apply_multiple, always structure your input as a dictionary with \"files\" (a list of file dictionaries) and \"project_context\" keys.\n4. Handle both successful results and errors gracefully.\n5. Provide clear explanations of tool usage and results to the user.\n</tool_usage_best_practices>\n\nRemember, you are an AI assistant, and your primary goal is to help the user accomplish their tasks effectively and efficiently while maintaining the integrity and security of their development environment.\n\"\"\"\n\nAUTOMODE_SYSTEM_PROMPT = \"\"\"\nYou are currently in automode. Follow these guidelines:\n\n<goal_setting>\n1. Goal Setting:\n   - Set clear, achievable goals based on the user's request.\n   - Break down complex tasks into smaller, manageable goals.\n</goal_setting>\n\n<goal_execution>\n2. Goal Execution:\n   - Work through goals systematically, using appropriate tools for each task.\n   - Utilize file operations, code writing, and web searches as needed.\n   - Always read a file before editing and review changes after editing.\n</goal_execution>\n\n<progress_tracking>\n3. Progress Tracking:\n   - Provide regular updates on goal completion and overall progress.\n   - Use the iteration information to pace your work effectively.\n</progress_tracking>\n\n<task_breakdown>\nBreak Down Complex Tasks:\nWhen faced with a complex task or project, break it down into smaller, manageable steps. Provide a clear outline of the steps involved, potential challenges, and how to approach each part of the task.\n</task_breakdown>\n\n<explanation_preference>\nPrefer Answering Without Code:\nWhen explaining concepts or providing solutions, prioritize clear explanations and pseudocode over full code implementations. Only provide full code snippets when explicitly requested or when it's essential for understanding.\n</explanation_preference>\n\n<code_review_process>\nCode Review Process:\nWhen reviewing code, follow these steps:\n1. Understand the context and purpose of the code\n2. Check for clarity and readability\n3. Identify potential bugs or errors\n4. Suggest optimizations or improvements\n5. Ensure adherence to best practices and coding standards\n6. Consider security implications\n7. Provide constructive feedback with explanations\n</code_review_process>\n\n<project_planning>\nProject Planning:\nWhen planning a project, consider the following:\n1. Define clear project goals and objectives\n2. Break down the project into manageable tasks and subtasks\n3. Estimate time and resources required for each task\n4. Identify potential risks and mitigation strategies\n5. Suggest appropriate tools and technologies\n6. Outline a testing and quality assurance strategy\n7. Consider scalability and future maintenance\n</project_planning>\n\n<security_review>\nSecurity Review:\nWhen conducting a security review, focus on:\n1. Identifying potential vulnerabilities in the code\n2. Checking for proper input validation and sanitization\n3. Ensuring secure handling of sensitive data\n4. Reviewing authentication and authorization mechanisms\n5. Checking for secure communication protocols\n6. Identifying any use of deprecated or insecure functions\n7. Suggesting security best practices and improvements\n</security_review>\n\nRemember to apply these additional skills and processes when assisting users with their software development tasks and projects.\n\n<tool_usage>\n4. Tool Usage:\n   - Leverage all available tools to accomplish your goals efficiently.\n   - Prefer edit_and_apply_multiple for file modifications, applying changes in chunks for large edits.\n   - Use tavily_search proactively for up-to-date information.\n</tool_usage>\n\n<error_handling>\n5. Error Handling:\n   - If a tool operation fails, analyze the error and attempt to resolve the issue.\n   - For persistent errors, consider alternative approaches to achieve the goal.\n</error_handling>\n\n<automode_completion>\n6. Automode Completion:\n   - When all goals are completed, respond with \"AUTOMODE_COMPLETE\" to exit automode.\n   - Do not ask for additional tasks or modifications once goals are achieved.\n</automode_completion>\n\n<iteration_awareness>\n7. Iteration Awareness:\n   - You have access to this {iteration_info}.\n   - Use this information to prioritize tasks and manage time effectively.\n</iteration_awareness>\n\nRemember: Focus on completing the established goals efficiently and effectively. Avoid unnecessary conversations or requests for additional tasks.\n\"\"\"\n\n\ndef update_system_prompt(current_iteration: Optional[int] = None, max_iterations: Optional[int] = None) -> str:\n    global file_contents\n    chain_of_thought_prompt = \"\"\"\n    Answer the user's request using relevant tools (if they are available). Before calling a tool, do some analysis within <thinking></thinking> tags. First, think about which of the provided tools is the relevant tool to answer the user's request. Second, go through each of the required parameters of the relevant tool and determine if the user has directly provided or given enough information to infer a value. When deciding if the parameter can be inferred, carefully consider all the context to see if it supports a specific value. If all of the required parameters are present or can be reasonably inferred, close the thinking tag and proceed with the tool call. BUT, if one of the values for a required parameter is missing, DO NOT invoke the function (not even with fillers for the missing params) and instead, ask the user to provide the missing parameters. DO NOT ask for more information on optional parameters if it is not provided.\n\n    Do not reflect on the quality of the returned search results in your response.\n\n    IMPORTANT: Before using the read_multiple_files tool, always check if the files you need are already in your context (system prompt).\n    If the file contents are already available to you, use that information directly instead of calling the read_multiple_files tool.\n    Only use the read_multiple_files tool for files that are not already in your context.\n    When instructing to read a file, always use the full file path.\n    \"\"\"\n\n    files_in_context = \"\\n\".join(file_contents.keys())\n    file_contents_prompt = f\"\\n\\nFiles already in your context:\\n{files_in_context}\\n\\nFile Contents:\\n\"\n    for path, content in file_contents.items():\n        file_contents_prompt += f\"\\n--- {path} ---\\n{content}\\n\"\n\n    if automode:\n        iteration_info = \"\"\n        if current_iteration is not None and max_iterations is not None:\n            iteration_info = f\"You are currently on iteration {current_iteration} out of {max_iterations} in automode.\"\n        return BASE_SYSTEM_PROMPT + file_contents_prompt + \"\\n\\n\" + AUTOMODE_SYSTEM_PROMPT.format(iteration_info=iteration_info) + \"\\n\\n\" + chain_of_thought_prompt\n    else:\n        return BASE_SYSTEM_PROMPT + file_contents_prompt + \"\\n\\n\" + chain_of_thought_prompt\n\ndef create_folders(paths):\n    results = []\n    for path in paths:\n        try:\n            # Use os.makedirs with exist_ok=True to create nested directories\n            os.makedirs(path, exist_ok=True)\n            results.append(f\"Folder(s) created: {path}\")\n        except Exception as e:\n            results.append(f\"Error creating folder(s) {path}: {str(e)}\")\n    return \"\\n\".join(results)\n\ndef create_files(files):\n    global file_contents\n    results = []\n    \n    # Handle different input types\n    if isinstance(files, str):\n        # If a string is passed, assume it's a single file path\n        files = [{\"path\": files, \"content\": \"\"}]\n    elif isinstance(files, dict):\n        # If a single dictionary is passed, wrap it in a list\n        files = [files]\n    elif not isinstance(files, list):\n        return \"Error: Invalid input type for create_files. Expected string, dict, or list.\"\n    \n    for file in files:\n        try:\n            if not isinstance(file, dict):\n                results.append(f\"Error: Invalid file specification: {file}\")\n                continue\n            \n            path = file.get('path')\n            content = file.get('content', '')\n            \n            if path is None:\n                results.append(f\"Error: Missing 'path' for file\")\n                continue\n            \n            dir_name = os.path.dirname(path)\n            if dir_name:\n                os.makedirs(dir_name, exist_ok=True)\n            \n            with open(path, 'w') as f:\n                f.write(content)\n            \n            file_contents[path] = content\n            results.append(f\"File created and added to system prompt: {path}\")\n        except Exception as e:\n            results.append(f\"Error creating file: {str(e)}\")\n    \n    return \"\\n\".join(results)\n\n\nasync def generate_edit_instructions(file_path, file_content, instructions, project_context, full_file_contents):\n    global code_editor_tokens, code_editor_memory, code_editor_files\n    try:\n        # Prepare memory context (maintains some context between calls)\n        memory_context = \"\\n\".join([f\"Memory {i+1}:\\n{mem}\" for i, mem in enumerate(code_editor_memory)])\n    \n        # Prepare full file contents context, excluding the file being edited if it's already in code_editor_files\n        full_file_contents_context = \"\\n\\n\".join([\n            f\"--- {path} ---\\n{content}\" for path, content in full_file_contents.items()\n            if path != file_path or path not in code_editor_files\n        ])\n    \n        system_prompt = f\"\"\"\n        You are an expert coding assistant specializing in web development (CSS, JavaScript, React, Tailwind, Node.JS, Hugo/Markdown). Review the following information carefully:\n    \n        1. File Content:\n        {file_content}\n    \n        2. Edit Instructions:\n        {instructions}\n    \n        3. Project Context:\n        {project_context}\n    \n        4. Previous Edit Memory:\n        {memory_context}\n    \n        5. Full Project Files Context:\n        {full_file_contents_context}\n    \n        Follow this process to generate edit instructions:\n    \n        1. <CODE_REVIEW>\n        Analyze the existing code thoroughly. Describe how it works, identifying key components, \n        dependencies, and potential issues. Consider the broader project context and previous edits.\n        </CODE_REVIEW>\n    \n        2. <PLANNING>\n        Construct a plan to implement the requested changes. Consider:\n        - How to avoid code duplication (DRY principle)\n        - Balance between maintenance and flexibility\n        - Relevant frameworks or libraries\n        - Security implications\n        - Performance impacts\n        Outline discrete changes and suggest small tests for each stage.\n        </PLANNING>\n    \n        3. Finally, generate SEARCH/REPLACE blocks for each necessary change:\n        - Use enough context to uniquely identify the code to be changed\n        - Maintain correct indentation and formatting\n        - Focus on specific, targeted changes\n        - Ensure consistency with project context and previous edits\n    \n        USE THIS FORMAT FOR CHANGES:\n    \n        <SEARCH>\n        Code to be replaced (with sufficient context)\n        </SEARCH>\n        <REPLACE>\n        New code to insert\n        </REPLACE>\n    \n        IMPORTANT: ONLY RETURN CODE INSIDE THE <SEARCH> AND <REPLACE> TAGS. DO NOT INCLUDE ANY OTHER TEXT, COMMENTS, or Explanations. FOR EXAMPLE:\n    \n        <SEARCH>\n        def old_function():\n            pass\n        </SEARCH>\n        <REPLACE>\n        def new_function():\n            print(\"New Functionality\")\n        </REPLACE>\n        \"\"\"\n    \n        response = client.beta.prompt_caching.messages.create(\n            model=CODEEDITORMODEL,\n            max_tokens=8000,\n            system=[\n                {\n                    \"type\": \"text\",\n                    \"text\": system_prompt,\n                    \"cache_control\": {\"type\": \"ephemeral\"}\n                }\n            ],\n            messages=[\n                {\"role\": \"user\", \"content\": \"Generate SEARCH/REPLACE blocks for the necessary changes.\"}\n            ],\n            extra_headers={\"anthropic-beta\": \"prompt-caching-2024-07-31\"}\n        )\n    \n        # Update token usage for code editor\n        code_editor_tokens['input'] += response.usage.input_tokens\n        code_editor_tokens['output'] += response.usage.output_tokens\n        code_editor_tokens['cache_write'] = response.usage.cache_creation_input_tokens\n        code_editor_tokens['cache_read'] = response.usage.cache_read_input_tokens\n    \n        ai_response_text = response.content[0].text  # Extract the text\n    \n        # If ai_response_text is a list, handle it\n        if isinstance(ai_response_text, list):\n            ai_response_text = ' '.join(\n                item['text'] if isinstance(item, dict) and 'text' in item else str(item)\n                for item in ai_response_text\n            )\n        elif not isinstance(ai_response_text, str):\n            ai_response_text = str(ai_response_text)\n    \n        # Validate AI response\n        try:\n            if not validate_ai_response(ai_response_text):\n                raise ValueError(\"AI response does not contain valid SEARCH/REPLACE blocks\")\n        except ValueError as ve:\n            logging.error(f\"Validation failed: {ve}\")\n            return []  # Return empty list to indicate failure\n    \n        # Parse the response to extract SEARCH/REPLACE blocks\n        edit_instructions = parse_search_replace_blocks(ai_response_text)\n    \n        if not edit_instructions:\n            raise ValueError(\"No valid edit instructions were generated\")\n    \n        # Update code editor memory\n        code_editor_memory.append(f\"Edit Instructions for {file_path}:\\n{ai_response_text}\")\n    \n        # Add the file to code_editor_files set\n        code_editor_files.add(file_path)\n    \n        return edit_instructions\n\n    except Exception as e:\n        console.print(f\"Error in generating edit instructions: {str(e)}\", style=\"bold red\")\n        logging.error(f\"Error in generating edit instructions: {str(e)}\")\n        return []  # Return empty list if any exception occurs\n\n\ndef validate_ai_response(response_text):\n    if isinstance(response_text, list):\n        # Extract 'text' from each dictionary in the list\n        try:\n            response_text = ' '.join(\n                item['text'] if isinstance(item, dict) and 'text' in item else str(item)\n                for item in response_text\n            )\n        except Exception as e:\n            logging.error(f\"Error processing response_text list: {str(e)}\")\n            raise ValueError(\"Invalid format in response_text list.\")\n    elif not isinstance(response_text, str):\n        logging.debug(f\"validate_ai_response received type {type(response_text)}: {response_text}\")\n        raise ValueError(f\"Invalid type for response_text: {type(response_text)}. Expected string.\")\n    \n    # Log the processed response_text\n    logging.debug(f\"Processed response_text for validation: {response_text}\")\n    \n    if not re.search(r'<SEARCH>.*?</SEARCH>', response_text, re.DOTALL):\n        raise ValueError(\"AI response does not contain any <SEARCH> blocks\")\n    if not re.search(r'<REPLACE>.*?</REPLACE>', response_text, re.DOTALL):\n        raise ValueError(\"AI response does not contain any <REPLACE> blocks\")\n    return True\n\n\ndef parse_search_replace_blocks(response_text, use_fuzzy=USE_FUZZY_SEARCH):\n    \"\"\"\n    Parse the response text for SEARCH/REPLACE blocks.\n\n    Args:\n    response_text (str): The text containing SEARCH/REPLACE blocks.\n    use_fuzzy (bool): Whether to use fuzzy matching for search blocks.\n\n    Returns:\n    list: A list of dictionaries, each containing 'search', 'replace', and 'similarity' keys.\n    \"\"\"\n    blocks = []\n    pattern = r'<SEARCH>\\s*(.*?)\\s*</SEARCH>\\s*<REPLACE>\\s*(.*?)\\s*</REPLACE>'\n    matches = re.findall(pattern, response_text, re.DOTALL)\n\n    for search, replace in matches:\n        search = search.strip()\n        replace = replace.strip()\n        similarity = 1.0  # Default to exact match\n\n        if use_fuzzy and search not in response_text:\n            # Extract possible search targets from the response text\n            possible_search_targets = re.findall(r'<SEARCH>\\s*(.*?)\\s*</SEARCH>', response_text, re.DOTALL)\n            possible_search_targets = [target.strip() for target in possible_search_targets]\n            \n            best_match = difflib.get_close_matches(search, possible_search_targets, n=1, cutoff=0.6)\n            if best_match:\n                similarity = difflib.SequenceMatcher(None, search, best_match[0]).ratio()\n            else:\n                similarity = 0.0\n\n        blocks.append({\n            'search': search,\n            'replace': replace,\n            'similarity': similarity\n        })\n\n    return blocks\n\n\nasync def edit_and_apply_multiple(files, project_context, is_automode=False):\n    global file_contents\n    results = []\n    console_outputs = []\n\n    logging.debug(f\"edit_and_apply_multiple called with files: {files}\")\n    logging.debug(f\"Project context: {project_context}\")\n\n    try:\n        files = validate_files_structure(files)\n    except ValueError as ve:\n        logging.error(f\"Validation error: {ve}\")\n        return [], f\"Error: {ve}\"\n\n    logging.info(f\"Starting edit_and_apply_multiple with {len(files)} file(s)\")\n\n    for file in files:\n        path = file['path']\n        instructions = file['instructions']\n        logging.info(f\"Processing file: {path}\")\n        try:\n            original_content = file_contents.get(path, \"\")\n            if not original_content:\n                logging.info(f\"Reading content for file: {path}\")\n                with open(path, 'r') as f:\n                    original_content = f.read()\n                file_contents[path] = original_content\n\n            logging.info(f\"Generating edit instructions for file: {path}\")\n            edit_instructions = await generate_edit_instructions(path, original_content, instructions, project_context, file_contents)\n\n            logging.debug(f\"AI response for {path}: {edit_instructions}\")\n\n            if not isinstance(edit_instructions, list) or not all(isinstance(item, dict) for item in edit_instructions):\n                raise ValueError(\"Invalid edit_instructions format. Expected a list of dictionaries.\")\n\n            if edit_instructions:\n                console.print(Panel(f\"File: {path}\\nThe following SEARCH/REPLACE blocks have been generated:\", title=\"Edit Instructions\", style=\"cyan\"))\n                for i, block in enumerate(edit_instructions, 1):\n                    console.print(f\"Block {i}:\")\n                    console.print(Panel(f\"SEARCH:\\n{block['search']}\\n\\nREPLACE:\\n{block['replace']}\\nSimilarity: {block['similarity']:.2f}\", expand=False))\n\n                logging.info(f\"Applying edits to file: {path}\")\n                edited_content, changes_made, failed_edits, console_output = await apply_edits(path, edit_instructions, original_content)\n\n                console_outputs.append(console_output)\n\n                if changes_made:\n                    file_contents[path] = edited_content\n                    console.print(Panel(f\"File contents updated in system prompt: {path}\", style=\"green\"))\n                    logging.info(f\"Changes applied to file: {path}\")\n\n                    if failed_edits:\n                        logging.warning(f\"Some edits failed for file: {path}\")\n                        logging.debug(f\"Failed edits for {path}: {failed_edits}\")\n                        results.append({\n                            \"path\": path,\n                            \"status\": \"partial_success\",\n                            \"message\": f\"Some changes applied to {path}, but some edits failed.\",\n                            \"failed_edits\": failed_edits,\n                            \"edited_content\": edited_content\n                        })\n                    else:\n                        results.append({\n                            \"path\": path,\n                            \"status\": \"success\",\n                            \"message\": f\"All changes successfully applied to {path}\",\n                            \"edited_content\": edited_content\n                        })\n                else:\n                    logging.warning(f\"No changes applied to file: {path}\")\n                    results.append({\n                        \"path\": path,\n                        \"status\": \"no_changes\",\n                        \"message\": f\"No changes could be applied to {path}. Please review the edit instructions and try again.\"\n                    })\n            else:\n                logging.warning(f\"No edit instructions generated for file: {path}\")\n                results.append({\n                    \"path\": path,\n                    \"status\": \"no_instructions\",\n                    \"message\": f\"No edit instructions generated for {path}\"\n                })\n        except Exception as e:\n            logging.error(f\"Error editing/applying to file {path}: {str(e)}\")\n            logging.exception(\"Full traceback:\")\n            error_message = f\"Error editing/applying to file {path}: {str(e)}\"\n            results.append({\n                \"path\": path,\n                \"status\": \"error\",\n                \"message\": error_message\n            })\n            console_outputs.append(error_message)\n\n    logging.info(\"Completed edit_and_apply_multiple\")\n    logging.debug(f\"Results: {results}\")\n    return results, \"\\n\".join(console_outputs)\n\n\n\nasync def apply_edits(file_path, edit_instructions, original_content):\n    changes_made = False\n    edited_content = original_content\n    total_edits = len(edit_instructions)\n    failed_edits = []\n    console_output = []\n\n    with Progress(\n        SpinnerColumn(),\n        TextColumn(\"[progress.description]{task.description}\"),\n        BarColumn(),\n        TextColumn(\"[progress.percentage]{task.percentage:>3.0f}%\"),\n        console=console\n    ) as progress:\n        edit_task = progress.add_task(\"[cyan]Applying edits...\", total=total_edits)\n\n        for i, edit in enumerate(edit_instructions, 1):\n            search_content = edit['search'].strip()\n            replace_content = edit['replace'].strip()\n            similarity = edit['similarity']\n\n            # Use regex to find the content, ignoring leading/trailing whitespace\n            pattern = re.compile(re.escape(search_content), re.DOTALL)\n            match = pattern.search(edited_content)\n\n            if match or (USE_FUZZY_SEARCH and similarity >= 0.8):\n                if not match:\n                    # If using fuzzy search and no exact match, find the best match\n                    best_match = difflib.get_close_matches(search_content, [edited_content], n=1, cutoff=0.6)\n                    if best_match:\n                        match = re.search(re.escape(best_match[0]), edited_content)\n\n                if match:\n                    # Replace the content using re.sub for more robust replacement\n                    replace_content_cleaned = re.sub(r'</?SEARCH>|</?REPLACE>', '', replace_content)\n                    edited_content = pattern.sub(replace_content_cleaned, edited_content, count=1)\n                    changes_made = True\n\n                    # Display the diff for this edit\n                    diff_result = generate_diff(search_content, replace_content, file_path)\n                    console.print(Panel(diff_result, title=f\"Changes in {file_path} ({i}/{total_edits}) - Similarity: {similarity:.2f}\", style=\"cyan\"))\n                    console_output.append(f\"Edit {i}/{total_edits} applied successfully\")\n                else:\n                    message = f\"Edit {i}/{total_edits} not applied: content not found (Similarity: {similarity:.2f})\"\n                    console_output.append(message)\n                    console.print(Panel(message, style=\"yellow\"))\n                    failed_edits.append(f\"Edit {i}: {search_content}\")\n            else:\n                message = f\"Edit {i}/{total_edits} not applied: content not found (Similarity: {similarity:.2f})\"\n                console_output.append(message)\n                console.print(Panel(message, style=\"yellow\"))\n                failed_edits.append(f\"Edit {i}: {search_content}\")\n\n            progress.update(edit_task, advance=1)\n\n    if not changes_made:\n        message = \"No changes were applied. The file content already matches the desired state.\"\n        console_output.append(message)\n        console.print(Panel(message, style=\"green\"))\n    else:\n        # Write the changes to the file\n        with open(file_path, 'w') as file:\n            file.write(edited_content)\n        message = f\"Changes have been written to {file_path}\"\n        console_output.append(message)\n        console.print(Panel(message, style=\"green\"))\n\n    return edited_content, changes_made, failed_edits, \"\\n\".join(console_output)\n\n\ndef highlight_diff(diff_text):\n    return Syntax(diff_text, \"diff\", theme=\"monokai\", line_numbers=True)\n\n\ndef generate_diff(original, new, path):\n    diff = list(difflib.unified_diff(\n        original.splitlines(keepends=True),\n        new.splitlines(keepends=True),\n        fromfile=f\"a/{path}\",\n        tofile=f\"b/{path}\",\n        n=3\n    ))\n\n    diff_text = ''.join(diff)\n    highlighted_diff = highlight_diff(diff_text)\n\n    return highlighted_diff\n\n\nasync def execute_code(code, timeout=10):\n    global running_processes\n    venv_path, activate_script = setup_virtual_environment()\n\n    # Input validation\n    if not isinstance(code, str):\n        raise ValueError(\"code must be a string\")\n    if not isinstance(timeout, (int, float)):\n        raise ValueError(\"timeout must be a number\")\n\n    # Generate a unique identifier for this process\n    process_id = f\"process_{len(running_processes)}\"\n\n    # Write the code to a temporary file\n    try:\n        with open(f\"{process_id}.py\", \"w\") as f:\n            f.write(code)\n    except IOError as e:\n        return process_id, f\"Error writing code to file: {str(e)}\"\n\n    # Prepare the command to run the code\n    if sys.platform == \"win32\":\n        command = f'\"{activate_script}\" && python3 {process_id}.py'\n    else:\n        command = f'source \"{activate_script}\" && python3 {process_id}.py'\n\n    try:\n        # Create a process to run the command\n        process = await asyncio.create_subprocess_shell(\n            command,\n            stdout=asyncio.subprocess.PIPE,\n            stderr=asyncio.subprocess.PIPE,\n            shell=True,\n            preexec_fn=None if sys.platform == \"win32\" else os.setsid\n        )\n\n        # Store the process in our global dictionary\n        running_processes[process_id] = process\n\n        try:\n            # Wait for initial output or timeout\n            stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=timeout)\n            stdout = stdout.decode()\n            stderr = stderr.decode()\n            return_code = process.returncode\n        except asyncio.TimeoutError:\n            # If we timeout, it means the process is still running\n            stdout = \"Process started and running in the background.\"\n            stderr = \"\"\n            return_code = \"Running\"\n\n        execution_result = f\"Process ID: {process_id}\\n\\nStdout:\\n{stdout}\\n\\nStderr:\\n{stderr}\\n\\nReturn Code: {return_code}\"\n        return process_id, execution_result\n    except Exception as e:\n        return process_id, f\"Error executing code: {str(e)}\"\n    finally:\n        # Cleanup: remove the temporary file\n        try:\n            os.remove(f\"{process_id}.py\")\n        except OSError:\n            pass  # Ignore errors in removing the file\n\n# Update the read_multiple_files function to handle both single and multiple files\ndef read_multiple_files(paths, recursive=False):\n    global file_contents\n    results = []\n\n    if isinstance(paths, str):\n        paths = [paths]\n\n    for path in paths:\n        try:\n            abs_path = os.path.abspath(path)\n            if os.path.isdir(abs_path):\n                if recursive:\n                    file_paths = glob.glob(os.path.join(abs_path, '**', '*'), recursive=True)\n                else:\n                    file_paths = glob.glob(os.path.join(abs_path, '*'))\n                file_paths = [f for f in file_paths if os.path.isfile(f)]\n            else:\n                file_paths = glob.glob(abs_path, recursive=recursive)\n\n            for file_path in file_paths:\n                abs_file_path = os.path.abspath(file_path)\n                if os.path.isfile(abs_file_path):\n                    if abs_file_path not in file_contents:\n                        with open(abs_file_path, 'r', encoding='utf-8') as f:\n                            content = f.read()\n                        file_contents[abs_file_path] = content\n                        results.append(f\"File '{abs_file_path}' has been read and stored in the system prompt.\")\n                    else:\n                        results.append(f\"File '{abs_file_path}' is already in the system prompt. No need to read again.\")\n                else:\n                    results.append(f\"Skipped '{abs_file_path}': Not a file.\")\n        except Exception as e:\n            results.append(f\"Error reading path '{path}': {str(e)}\")\n\n    return \"\\n\".join(results)\n\ndef list_files(path=\".\"):\n    try:\n        files = os.listdir(path)\n        return \"\\n\".join(files)\n    except Exception as e:\n        return f\"Error listing files: {str(e)}\"\n\ndef tavily_search(query):\n    try:\n        response = tavily.qna_search(query=query, search_depth=\"advanced\")\n        return response\n    except Exception as e:\n        return f\"Error performing search: {str(e)}\"\n\ndef stop_process(process_id):\n    global running_processes\n    if process_id in running_processes:\n        process = running_processes[process_id]\n        if sys.platform == \"win32\":\n            process.terminate()\n        else:\n            os.killpg(os.getpgid(process.pid), signal.SIGTERM)\n        del running_processes[process_id]\n        return f\"Process {process_id} has been stopped.\"\n    else:\n        return f\"No running process found with ID {process_id}.\"\n\ndef run_shell_command(command):\n    try:\n        result = subprocess.run(command, shell=True, check=True, text=True, capture_output=True)\n        return {\n            \"stdout\": result.stdout,\n            \"stderr\": result.stderr,\n            \"return_code\": result.returncode\n        }\n    except subprocess.CalledProcessError as e:\n        return {\n            \"stdout\": e.stdout,\n            \"stderr\": e.stderr,\n            \"return_code\": e.returncode,\n            \"error\": str(e)\n        }\n    except Exception as e:\n        return {\n            \"error\": f\"An error occurred while executing the command: {str(e)}\"\n        }\n    \ndef validate_files_structure(files):\n    if not isinstance(files, (dict, list)):\n        raise ValueError(\"Invalid 'files' structure. Expected a dictionary or a list of dictionaries.\")\n    \n    if isinstance(files, dict):\n        files = [files]\n    \n    for file in files:\n        if not isinstance(file, dict):\n            raise ValueError(\"Each file must be a dictionary.\")\n        if 'path' not in file or 'instructions' not in file:\n            raise ValueError(\"Each file dictionary must contain 'path' and 'instructions' keys.\")\n        if not isinstance(file['path'], str) or not isinstance(file['instructions'], str):\n            raise ValueError(\"'path' and 'instructions' must be strings.\")\n\n    return files\n\ndef scan_folder(folder_path: str, output_file: str) -> str:\n    ignored_folders = {'.git', '__pycache__', 'node_modules', 'venv', 'env'}\n    markdown_content = f\"# Folder Scan: {folder_path}\\n\\n\"\n    total_chars = len(markdown_content)\n    max_chars = 600000  # Approximating 150,000 tokens\n\n    for root, dirs, files in os.walk(folder_path):\n        dirs[:] = [d for d in dirs if d not in ignored_folders]\n\n        for file in files:\n            file_path = os.path.join(root, file)\n            relative_path = os.path.relpath(file_path, folder_path)\n\n            mime_type, _ = mimetypes.guess_type(file_path)\n            if mime_type and mime_type.startswith('text'):\n                try:\n                    with open(file_path, 'r', encoding='utf-8') as f:\n                        content = f.read()\n\n                    file_content = f\"## {relative_path}\\n\\n```\\n{content}\\n```\\n\\n\"\n                    if total_chars + len(file_content) > max_chars:\n                        remaining_chars = max_chars - total_chars\n                        if remaining_chars > 0:\n                            truncated_content = file_content[:remaining_chars]\n                            markdown_content += truncated_content\n                            markdown_content += \"\\n\\n... Content truncated due to size limitations ...\\n\"\n                        else:\n                            markdown_content += \"\\n\\n... Additional files omitted due to size limitations ...\\n\"\n                        break\n                    else:\n                        markdown_content += file_content\n                        total_chars += len(file_content)\n                except Exception as e:\n                    error_msg = f\"## {relative_path}\\n\\nError reading file: {str(e)}\\n\\n\"\n                    if total_chars + len(error_msg) <= max_chars:\n                        markdown_content += error_msg\n                        total_chars += len(error_msg)\n\n        if total_chars >= max_chars:\n            break\n\n    with open(output_file, 'w', encoding='utf-8') as f:\n        f.write(markdown_content)\n\n    return f\"Folder scan complete. Markdown file created at: {output_file}. Total characters: {total_chars}\"\n\ndef encode_image_to_base64(image_path):\n    try:\n        with Image.open(image_path) as img:\n            max_size = (1024, 1024)\n            img.thumbnail(max_size, Image.DEFAULT_STRATEGY)\n            if img.mode != 'RGB':\n                img = img.convert('RGB')\n            img_byte_arr = io.BytesIO()\n            img.save(img_byte_arr, format='JPEG')\n            return base64.b64encode(img_byte_arr.getvalue()).decode('utf-8')\n    except Exception as e:\n        return f\"Error encoding image: {str(e)}\"\n\n\nasync def send_to_ai_for_executing(code, execution_result):\n    global code_execution_tokens\n\n    try:\n        system_prompt = f\"\"\"\n        You are an AI code execution agent. Your task is to analyze the provided code and its execution result from the 'code_execution_env' virtual environment, then provide a concise summary of what worked, what didn't work, and any important observations. Follow these steps:\n\n        1. Review the code that was executed in the 'code_execution_env' virtual environment:\n        {code}\n\n        2. Analyze the execution result from the 'code_execution_env' virtual environment:\n        {execution_result}\n\n        3. Provide a brief summary of:\n           - What parts of the code executed successfully in the virtual environment\n           - Any errors or unexpected behavior encountered in the virtual environment\n           - Potential improvements or fixes for issues, considering the isolated nature of the environment\n           - Any important observations about the code's performance or output within the virtual environment\n           - If the execution timed out, explain what this might mean (e.g., long-running process, infinite loop)\n\n        Be concise and focus on the most important aspects of the code execution within the 'code_execution_env' virtual environment.\n\n        IMPORTANT: PROVIDE ONLY YOUR ANALYSIS AND OBSERVATIONS. DO NOT INCLUDE ANY PREFACING STATEMENTS OR EXPLANATIONS OF YOUR ROLE.\n        \"\"\"\n\n        response = client.beta.prompt_caching.messages.create(\n            model=CODEEXECUTIONMODEL,\n            max_tokens=2000,\n            system=[\n                {\n                    \"type\": \"text\",\n                    \"text\": system_prompt,\n                    \"cache_control\": {\"type\": \"ephemeral\"}\n                }\n            ],\n            messages=[\n                {\"role\": \"user\", \"content\": f\"Analyze this code execution from the 'code_execution_env' virtual environment:\\n\\nCode:\\n{code}\\n\\nExecution Result:\\n{execution_result}\"}\n            ],\n            extra_headers={\"anthropic-beta\": \"prompt-caching-2024-07-31\"}\n        )\n\n        # Update token usage for code execution\n        code_execution_tokens['input'] += response.usage.input_tokens\n        code_execution_tokens['output'] += response.usage.output_tokens\n        code_execution_tokens['cache_creation'] = response.usage.cache_creation_input_tokens\n        code_execution_tokens['cache_read'] = response.usage.cache_read_input_tokens\n\n        analysis = response.content[0].text\n\n        return analysis\n\n    except Exception as e:\n        console.print(f\"Error in AI code execution analysis: {str(e)}\", style=\"bold red\")\n        return f\"Error analyzing code execution from 'code_execution_env': {str(e)}\"\n\n\ndef save_chat():\n    # Generate filename\n    now = datetime.datetime.now()\n    filename = f\"Chat_{now.strftime('%H%M')}.md\"\n\n    # Format conversation history\n    formatted_chat = \"# Claude-3-Sonnet Engineer Chat Log\\n\\n\"\n    for message in conversation_history:\n        if message['role'] == 'user':\n            formatted_chat += f\"## User\\n\\n{message['content']}\\n\\n\"\n        elif message['role'] == 'assistant':\n            if isinstance(message['content'], str):\n                formatted_chat += f\"## Claude\\n\\n{message['content']}\\n\\n\"\n            elif isinstance(message['content'], list):\n                for content in message['content']:\n                    if content['type'] == 'tool_use':\n                        formatted_chat += f\"### Tool Use: {content['name']}\\n\\n```json\\n{json.dumps(content['input'], indent=2)}\\n```\\n\\n\"\n                    elif content['type'] == 'text':\n                        formatted_chat += f\"## Claude\\n\\n{content['text']}\\n\\n\"\n        elif message['role'] == 'user' and isinstance(message['content'], list):\n            for content in message['content']:\n                if content['type'] == 'tool_result':\n                    formatted_chat += f\"### Tool Result\\n\\n```\\n{content['content']}\\n```\\n\\n\"\n\n    # Save to file\n    with open(filename, 'w', encoding='utf-8') as f:\n        f.write(formatted_chat)\n\n    return filename\n\n\ntools = [\n    {\n        \"name\": \"create_folders\",\n        \"description\": \"Create new folders at the specified paths, including nested directories. This tool should be used when you need to create one or more directories (including nested ones) in the project structure. It will create all necessary parent directories if they don't exist.\",\n        \"input_schema\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"paths\": {\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"string\"\n                    },\n                    \"description\": \"An array of absolute or relative paths where the folders should be created. Use forward slashes (/) for path separation, even on Windows systems. For nested directories, simply include the full path (e.g., 'parent/child/grandchild').\"\n                }\n            },\n            \"required\": [\"paths\"]\n        }\n    },\n    {\n        \"name\": \"scan_folder\",\n        \"description\": \"Scan a specified folder and create a Markdown file with the contents of all coding text files, excluding binary files and common ignored folders.\",\n        \"input_schema\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"folder_path\": {\n                    \"type\": \"string\",\n                    \"description\": \"The absolute or relative path of the folder to scan. Use forward slashes (/) for path separation, even on Windows systems.\"\n                },\n                \"output_file\": {\n                    \"type\": \"string\",\n                    \"description\": \"The name of the output Markdown file to create with the scanned contents.\"\n                }\n            },\n            \"required\": [\"folder_path\", \"output_file\"]\n        }\n    },\n    {\n        \"name\": \"create_files\",\n        \"description\": \"Create one or more new files with the given contents. This tool should be used when you need to create files in the project structure. It will create all necessary parent directories if they don't exist.\",\n        \"input_schema\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"files\": {\n                    \"oneOf\": [\n                        {\n                            \"type\": \"string\",\n                            \"description\": \"A single file path to create an empty file.\"\n                        },\n                        {\n                            \"type\": \"object\",\n                            \"properties\": {\n                                \"path\": {\"type\": \"string\"},\n                                \"content\": {\"type\": \"string\"}\n                            },\n                            \"required\": [\"path\"]\n                        },\n                        {\n                            \"type\": \"array\",\n                            \"items\": {\n                                \"type\": \"object\",\n                                \"properties\": {\n                                    \"path\": {\"type\": \"string\"},\n                                    \"content\": {\"type\": \"string\"}\n                                },\n                                \"required\": [\"path\"]\n                            }\n                        }\n                    ]\n                }\n            },\n            \"required\": [\"files\"]\n        }\n    },\n    {\n        \"name\": \"edit_and_apply_multiple\",\n        \"description\": \"Apply AI-powered improvements to multiple files based on specific instructions and detailed project context.\",\n        \"input_schema\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"files\": {\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"object\",\n                        \"properties\": {\n                            \"path\": {\n                                \"type\": \"string\",\n                                \"description\": \"The absolute or relative path of the file to edit.\"\n                            },\n                            \"instructions\": {\n                                \"type\": \"string\",\n                                \"description\": \"Specific instructions for editing this file.\"\n                            }\n                        },\n                        \"required\": [\"path\", \"instructions\"]\n                    }\n                },\n                \"project_context\": {\n                    \"type\": \"string\",\n                    \"description\": \"Comprehensive context about the project, including recent changes, new variables or functions, interconnections between files, coding standards, and any other relevant information that might affect the edits.\"\n                }\n            },\n            \"required\": [\"files\", \"project_context\"]\n        }\n    },\n    {\n        \"name\": \"execute_code\",\n        \"description\": \"Execute Python code in the 'code_execution_env' virtual environment and return the output. This tool should be used when you need to run code and see its output or check for errors. All code execution happens exclusively in this isolated environment. The tool will return the standard output, standard error, and return code of the executed code. Long-running processes will return a process ID for later management.\",\n        \"input_schema\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"code\": {\n                    \"type\": \"string\",\n                    \"description\": \"The Python code to execute in the 'code_execution_env' virtual environment. Include all necessary imports and ensure the code is complete and self-contained.\"\n                }\n            },\n            \"required\": [\"code\"]\n        }\n    },\n    {\n        \"name\": \"stop_process\",\n        \"description\": \"Stop a running process by its ID. This tool should be used to terminate long-running processes that were started by the execute_code tool. It will attempt to stop the process gracefully, but may force termination if necessary. The tool will return a success message if the process is stopped, and an error message if the process doesn't exist or can't be stopped.\",\n        \"input_schema\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"process_id\": {\n                    \"type\": \"string\",\n                    \"description\": \"The ID of the process to stop, as returned by the execute_code tool for long-running processes.\"\n                }\n            },\n            \"required\": [\"process_id\"]\n        }\n    },\n    {\n        \"name\": \"read_multiple_files\",\n        \"description\": \"Read the contents of one or more existing files, supporting wildcards and recursive directory reading.\",\n        \"input_schema\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"paths\": {\n                    \"oneOf\": [\n                        {\n                            \"type\": \"string\",\n                            \"description\": \"A single file path, directory path, or wildcard pattern.\"\n                        },\n                        {\n                            \"type\": \"array\",\n                            \"items\": {\n                                \"type\": \"string\"\n                            },\n                            \"description\": \"An array of file paths, directory paths, or wildcard patterns.\"\n                        }\n                    ],\n                    \"description\": \"The path(s) of the file(s) to read. Use forward slashes (/) for path separation, even on Windows systems. Supports wildcards (e.g., '*.py') and directory paths.\"\n                },\n                \"recursive\": {\n                    \"type\": \"boolean\",\n                    \"description\": \"If true, read files recursively from directories. Default is false.\",\n                    \"default\": False\n                }\n            },\n            \"required\": [\"paths\"]\n        }\n    },\n    {\n        \"name\": \"list_files\",\n        \"description\": \"List all files and directories in the specified folder. This tool should be used when you need to see the contents of a directory. It will return a list of all files and subdirectories in the specified path. If the directory doesn't exist or can't be read, an appropriate error message will be returned.\",\n        \"input_schema\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"path\": {\n                    \"type\": \"string\",\n                    \"description\": \"The absolute or relative path of the folder to list. Use forward slashes (/) for path separation, even on Windows systems. If not provided, the current working directory will be used.\"\n                }\n            }\n        }\n    },\n    {\n        \"name\": \"tavily_search\",\n        \"description\": \"Perform a web search using the Tavily API to get up-to-date information or additional context. This tool should be used when you need current information or feel a search could provide a better answer to the user's query. It will return a summary of the search results, including relevant snippets and source URLs.\",\n        \"input_schema\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"query\": {\n                    \"type\": \"string\",\n                    \"description\": \"The search query. Be as specific and detailed as possible to get the most relevant results.\"\n                }\n            },\n            \"required\": [\"query\"]\n        }\n    },\n    {\n        \"name\": \"run_shell_command\",\n        \"description\": \"Execute a shell command and return its output. This tool should be used when you need to run system commands or interact with the operating system. It will return the standard output, standard error, and return code of the executed command.\",\n        \"input_schema\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"command\": {\n                    \"type\": \"string\",\n                    \"description\": \"The shell command to execute. Ensure the command is safe and appropriate for the current operating system.\"\n                }\n            },\n            \"required\": [\"command\"]\n        }\n    }\n]\n\n\n\nasync def decide_retry(tool_checker_response, edit_results, tool_input):\n    try:\n        if not edit_results:\n            console.print(Panel(\"No edits were made or an error occurred. Skipping retry.\", title=\"Info\", style=\"bold yellow\"))\n            return {\"retry\": False, \"files_to_retry\": []}\n\n        response = client.messages.create(\n            model=TOOLCHECKERMODEL,\n            max_tokens=1000,\n            system=\"\"\"You are an AI assistant tasked with deciding whether to retry editing files based on the previous edit results and the AI's response. Respond with a JSON object containing 'retry' (boolean) and 'files_to_retry' (list of file paths).\n\nExample of the expected JSON response:\n{\n    \"retry\": true,\n    \"files_to_retry\": [\"/path/to/file1.py\", \"/path/to/file2.py\"]\n}\n\nOnly return the JSON object, nothing else. Ensure that the JSON is properly formatted with double quotes around property names and string values.\"\"\",\n            messages=[\n                {\"role\": \"user\", \"content\": f\"Previous edit results: {json.dumps(edit_results)}\\n\\nAI's response: {tool_checker_response}\\n\\nDecide whether to retry editing any files.\"}\n            ]\n        )\n        \n        response_text = response.content[0].text.strip()\n        \n        # Handle list of dicts if necessary\n        if isinstance(response_text, list):\n            response_text = ' '.join(\n                item['text'] if isinstance(item, dict) and 'text' in item else str(item)\n                for item in response_text\n            )\n        elif not isinstance(response_text, str):\n            response_text = str(response_text)\n        \n        try:\n            decision = json.loads(response_text)\n        except json.JSONDecodeError:\n            console.print(Panel(\"Failed to parse JSON from AI response. Using fallback decision.\", title=\"Warning\", style=\"bold yellow\"))\n            decision = {\n                \"retry\": \"retry\" in response_text.lower(),\n                \"files_to_retry\": []\n            }\n        \n        files = tool_input.get('files', [])\n        if isinstance(files, dict):\n            files = [files]\n        elif not isinstance(files, list):\n            console.print(Panel(\"Error: 'files' must be a dictionary or a list of dictionaries.\", title=\"Error\", style=\"bold red\"))\n            return {\"retry\": False, \"files_to_retry\": []}\n        \n        if not all(isinstance(item, dict) for item in files):\n            console.print(Panel(\"Error: Each file must be a dictionary with 'path' and 'instructions'.\", title=\"Error\", style=\"bold red\"))\n            return {\"retry\": False, \"files_to_retry\": []}\n        \n        valid_file_paths = set(file['path'] for file in files)\n        files_to_retry = [\n            file_path for file_path in decision.get(\"files_to_retry\", [])\n            if file_path in valid_file_paths\n        ]\n        \n        retry_decision = {\n            \"retry\": decision.get(\"retry\", False),\n            \"files_to_retry\": files_to_retry\n        }\n        \n        console.print(Panel(f\"Retry decision: {json.dumps(retry_decision, indent=2)}\", title=\"Retry Decision\", style=\"bold cyan\"))\n        return retry_decision\n\n    except Exception as e:\n        console.print(Panel(f\"Error in decide_retry: {str(e)}\", title=\"Error\", style=\"bold red\"))\n        return {\"retry\": False, \"files_to_retry\": []}\n\nasync def execute_tool(tool_name: str, tool_input: Dict[str, Any]) -> Dict[str, Any]:\n    try:\n        result = None\n        is_error = False\n        console_output = None\n\n        if tool_name == \"create_files\":\n            if isinstance(tool_input, dict) and 'files' in tool_input:\n                files = tool_input['files']\n            else:\n                files = tool_input\n            result = create_files(files)\n        elif tool_name == \"edit_and_apply_multiple\":\n            files = tool_input.get(\"files\")\n            if not files:\n                result = \"Error: 'files' key is missing or empty.\"\n                is_error = True\n            else:\n                # Ensure 'files' is a list of dictionaries\n                if isinstance(files, str):\n                    try:\n                        # Attempt to parse the JSON string\n                        files = json.loads(files)\n                        if isinstance(files, dict):\n                            files = [files]\n                        elif isinstance(files, list):\n                            if not all(isinstance(file, dict) for file in files):\n                                result = \"Error: Each file must be a dictionary with 'path' and 'instructions'.\"\n                                is_error = True\n                    except json.JSONDecodeError:\n                        result = \"Error: 'files' must be a dictionary or a list of dictionaries, and should not be a string.\"\n                        is_error = True\n                elif isinstance(files, dict):\n                    files = [files]\n                elif isinstance(files, list):\n                    if not all(isinstance(file, dict) for file in files):\n                        result = \"Error: Each file must be a dictionary with 'path' and 'instructions'.\"\n                        is_error = True\n                else:\n                    result = \"Error: 'files' must be a dictionary or a list of dictionaries.\"\n                    is_error = True\n\n                if not is_error:\n                    # Validate the structure of 'files'\n                    try:\n                        files = validate_files_structure(files)\n                    except ValueError as ve:\n                        result = f\"Error: {str(ve)}\"\n                        is_error = True\n\n            if not is_error:\n                result, console_output = await edit_and_apply_multiple(files, tool_input[\"project_context\"], is_automode=automode)\n        elif tool_name == \"create_folders\":\n            result = create_folders(tool_input[\"paths\"])\n        elif tool_name == \"read_multiple_files\":\n            paths = tool_input.get(\"paths\")\n            recursive = tool_input.get(\"recursive\", False)\n            if paths is None:\n                result = \"Error: No file paths provided\"\n                is_error = True\n            else:\n                files_to_read = [p for p in (paths if isinstance(paths, list) else [paths]) if p not in file_contents]\n                if not files_to_read:\n                    result = \"All requested files are already in the system prompt. No need to read from disk.\"\n                else:\n                    result = read_multiple_files(files_to_read, recursive)\n        elif tool_name == \"list_files\":\n            result = list_files(tool_input.get(\"path\", \".\"))\n        elif tool_name == \"tavily_search\":\n            result = tavily_search(tool_input[\"query\"])\n        elif tool_name == \"stop_process\":\n            result = stop_process(tool_input[\"process_id\"])\n        elif tool_name == \"execute_code\":\n            process_id, execution_result = await execute_code(tool_input[\"code\"])\n            if execution_result.startswith(\"Process started and running\"):\n                analysis = \"The process is still running in the background.\"\n            else:\n                analysis_task = asyncio.create_task(send_to_ai_for_executing(tool_input[\"code\"], execution_result))\n                analysis = await analysis_task\n            result = f\"{execution_result}\\n\\nAnalysis:\\n{analysis}\"\n            if process_id in running_processes:\n                result += \"\\n\\nNote: The process is still running in the background.\"\n        elif tool_name == \"scan_folder\":\n            result = scan_folder(tool_input[\"folder_path\"], tool_input[\"output_file\"])\n        elif tool_name == \"run_shell_command\":\n            result = run_shell_command(tool_input[\"command\"])\n        else:\n            is_error = True\n            result = f\"Unknown tool: {tool_name}\"\n\n        return {\n            \"content\": result,\n            \"is_error\": is_error,\n            \"console_output\": console_output\n        }\n    except KeyError as e:\n        logging.error(f\"Missing required parameter {str(e)} for tool {tool_name}\")\n        return {\n            \"content\": f\"Error: Missing required parameter {str(e)} for tool {tool_name}\",\n            \"is_error\": True,\n            \"console_output\": None\n        }\n    except Exception as e:\n        logging.error(f\"Error executing tool {tool_name}: {str(e)}\")\n        return {\n            \"content\": f\"Error executing tool {tool_name}: {str(e)}\",\n            \"is_error\": True,\n            \"console_output\": None\n        }\n\n\n\nasync def chat_with_claude(user_input, image_path=None, current_iteration=None, max_iterations=None):\n    global conversation_history, automode, main_model_tokens, use_tts, tts_enabled\n\n    # Input validation\n    if not isinstance(user_input, str):\n        raise ValueError(\"user_input must be a string\")\n    if image_path is not None and not isinstance(image_path, str):\n        raise ValueError(\"image_path must be a string or None\")\n    if current_iteration is not None and not isinstance(current_iteration, int):\n        raise ValueError(\"current_iteration must be an integer or None\")\n    if max_iterations is not None and not isinstance(max_iterations, int):\n        raise ValueError(\"max_iterations must be an integer or None\")\n\n    current_conversation = []\n\n    if image_path:\n        console.print(Panel(f\"Processing image at path: {image_path}\", title_align=\"left\", title=\"Image Processing\", expand=False, style=\"yellow\"))\n        image_base64 = encode_image_to_base64(image_path)\n\n        if image_base64.startswith(\"Error\"):\n            console.print(Panel(f\"Error encoding image: {image_base64}\", title=\"Error\", style=\"bold red\"))\n            return \"I'm sorry, there was an error processing the image. Please try again.\", False\n\n        image_message = {\n            \"role\": \"user\",\n            \"content\": [\n                {\n                    \"type\": \"image\",\n                    \"source\": {\n                        \"type\": \"base64\",\n                        \"media_type\": \"image/jpeg\",\n                        \"data\": image_base64\n                    }\n                },\n                {\n                    \"type\": \"text\",\n                    \"text\": f\"User input for image: {user_input}\"\n                }\n            ]\n        }\n        current_conversation.append(image_message)\n        console.print(Panel(\"Image message added to conversation history\", title_align=\"left\", title=\"Image Added\", style=\"green\"))\n    else:\n        current_conversation.append({\"role\": \"user\", \"content\": user_input})\n\n    # Filter conversation history to maintain context\n    filtered_conversation_history = []\n    for message in conversation_history:\n        if isinstance(message['content'], list):\n            filtered_content = [\n                content for content in message['content']\n                if content.get('type') != 'tool_result' or (\n                    content.get('type') == 'tool_result' and\n                    not any(keyword in content.get('output', '') for keyword in [\n                        \"File contents updated in system prompt\",\n                        \"File created and added to system prompt\",\n                        \"has been read and stored in the system prompt\"\n                    ])\n                )\n            ]\n            if filtered_content:\n                filtered_conversation_history.append({**message, 'content': filtered_content})\n        else:\n            filtered_conversation_history.append(message)\n\n    # Combine filtered history with current conversation to maintain context\n    messages = filtered_conversation_history + current_conversation\n\n    max_retries = 3\n    retry_delay = 5\n\n    for attempt in range(max_retries):\n        try:\n            # MAINMODEL call with prompt caching\n            response = client.beta.prompt_caching.messages.create(\n                model=MAINMODEL,\n                max_tokens=8000,\n                system=[\n                    {\n                        \"type\": \"text\",\n                        \"text\": update_system_prompt(current_iteration, max_iterations),\n                        \"cache_control\": {\"type\": \"ephemeral\"}\n                    },\n                    {\n                        \"type\": \"text\",\n                        \"text\": json.dumps(tools),\n                        \"cache_control\": {\"type\": \"ephemeral\"}\n                    }\n                ],\n                messages=messages,\n                tools=tools,\n                tool_choice={\"type\": \"auto\"},\n                extra_headers={\"anthropic-beta\": \"prompt-caching-2024-07-31\"}\n            )\n            # Update token usage for MAINMODEL\n            main_model_tokens['input'] += response.usage.input_tokens\n            main_model_tokens['output'] += response.usage.output_tokens\n            main_model_tokens['cache_write'] = response.usage.cache_creation_input_tokens\n            main_model_tokens['cache_read'] = response.usage.cache_read_input_tokens\n            break  # If successful, break out of the retry loop\n        except APIStatusError as e:\n            if e.status_code == 429 and attempt < max_retries - 1:\n                console.print(Panel(f\"Rate limit exceeded. Retrying in {retry_delay} seconds... (Attempt {attempt + 1}/{max_retries})\", title=\"API Error\", style=\"bold yellow\"))\n                time.sleep(retry_delay)\n                retry_delay *= 2  # Exponential backoff\n            else:\n                console.print(Panel(f\"API Error: {str(e)}\", title=\"API Error\", style=\"bold red\"))\n                return \"I'm sorry, there was an error communicating with the AI. Please try again.\", False\n        except APIError as e:\n            console.print(Panel(f\"API Error: {str(e)}\", title=\"API Error\", style=\"bold red\"))\n            return \"I'm sorry, there was an error communicating with the AI. Please try again.\", False\n    else:\n        console.print(Panel(\"Max retries reached. Unable to communicate with the AI.\", title=\"Error\", style=\"bold red\"))\n        return \"I'm sorry, there was a persistent error communicating with the AI. Please try again later.\", False\n\n    assistant_response = \"\"\n    exit_continuation = False\n    tool_uses = []\n\n    for content_block in response.content:\n        if content_block.type == \"text\":\n            assistant_response += content_block.text\n            if CONTINUATION_EXIT_PHRASE in content_block.text:\n                exit_continuation = True\n        elif content_block.type == \"tool_use\":\n            tool_uses.append(content_block)\n\n    console.print(Panel(Markdown(assistant_response), title=\"Claude's Response\", title_align=\"left\", border_style=\"blue\", expand=False))\n    \n    if tts_enabled and use_tts:\n        await text_to_speech(assistant_response)\n\n    # Display files in context\n    if file_contents:\n        files_in_context = \"\\n\".join(file_contents.keys())\n    else:\n        files_in_context = \"No files in context. Read, create, or edit files to add.\"\n    console.print(Panel(files_in_context, title=\"Files in Context\", title_align=\"left\", border_style=\"white\", expand=False))\n\n    for tool_use in tool_uses:\n        tool_name = tool_use.name\n        tool_input = tool_use.input\n        tool_use_id = tool_use.id\n\n        console.print(Panel(f\"Tool Used: {tool_name}\", style=\"green\"))\n        console.print(Panel(f\"Tool Input: {json.dumps(tool_input, indent=2)}\", style=\"green\"))\n\n        # Always use execute_tool for all tools\n        tool_result = await execute_tool(tool_name, tool_input)\n\n        if isinstance(tool_result, dict) and tool_result.get(\"is_error\"):\n            console.print(Panel(tool_result[\"content\"], title=\"Tool Execution Error\", style=\"bold red\"))\n            edit_results = []  # Assign empty list due to error\n        else:\n            # Assuming tool_result[\"content\"] is a list of results\n            edit_results = tool_result.get(\"content\", [])\n\n        # Prepare the tool_result_content for conversation history\n        tool_result_content = {\n            \"type\": \"text\",\n            \"text\": json.dumps(tool_result) if isinstance(tool_result, (dict, list)) else str(tool_result)\n        }\n\n        current_conversation.append({\n            \"role\": \"assistant\",\n            \"content\": [\n                {\n                    \"type\": \"tool_use\",\n                    \"id\": tool_use_id,\n                    \"name\": tool_name,\n                    \"input\": tool_input\n                }\n            ]\n        })\n\n        current_conversation.append({\n            \"role\": \"user\",\n            \"content\": [\n                {\n                    \"type\": \"tool_result\",\n                    \"tool_use_id\": tool_use_id,\n                    \"content\": [tool_result_content],\n                    \"is_error\": tool_result.get(\"is_error\", False) if isinstance(tool_result, dict) else False\n                }\n            ]\n        })\n\n        # Update the file_contents dictionary if applicable\n        if tool_name in ['create_files', 'edit_and_apply_multiple', 'read_multiple_files'] and not (isinstance(tool_result, dict) and tool_result.get(\"is_error\")):\n            if tool_name == 'create_files':\n                for file in tool_input['files']:\n                    if \"File created and added to system prompt\" in str(tool_result):\n                        file_contents[file['path']] = file['content']\n            elif tool_name == 'edit_and_apply_multiple':\n                edit_results = tool_result if isinstance(tool_result, list) else [tool_result]\n                for result in edit_results:\n                    if isinstance(result, dict) and result.get(\"status\") in [\"success\", \"partial_success\"]:\n                        file_contents[result[\"path\"]] = result.get(\"edited_content\", file_contents.get(result[\"path\"], \"\"))\n            elif tool_name == 'read_multiple_files':\n                # The file_contents dictionary is already updated in the read_multiple_files function\n                pass\n\n        messages = filtered_conversation_history + current_conversation\n\n        try:\n            tool_response = client.messages.create(\n                model=TOOLCHECKERMODEL,\n                max_tokens=8000,\n                system=update_system_prompt(current_iteration, max_iterations),\n                extra_headers={\"anthropic-beta\": \"max-tokens-3-5-sonnet-2024-07-15\"},\n                messages=messages,\n                tools=tools,\n                tool_choice={\"type\": \"auto\"}\n            )\n            # Update token usage for tool checker\n            tool_checker_tokens['input'] += tool_response.usage.input_tokens\n            tool_checker_tokens['output'] += tool_response.usage.output_tokens\n\n            tool_checker_response = \"\"\n            for tool_content_block in tool_response.content:\n                if tool_content_block.type == \"text\":\n                    tool_checker_response += tool_content_block.text\n            console.print(Panel(Markdown(tool_checker_response), title=\"Claude's Response to Tool Result\",  title_align=\"left\", border_style=\"blue\", expand=False))\n            if use_tts:\n                await text_to_speech(tool_checker_response)\n            assistant_response += \"\\n\\n\" + tool_checker_response\n\n            # If the tool was edit_and_apply_multiple, let the AI decide whether to retry\n            if tool_name == 'edit_and_apply_multiple':\n                retry_decision = await decide_retry(tool_checker_response, edit_results, tool_input)\n                if retry_decision[\"retry\"] and retry_decision['files_to_retry']:\n                    console.print(Panel(f\"AI has decided to retry editing for files: {', '.join(retry_decision['files_to_retry'])}\", style=\"yellow\"))\n                    retry_files = [\n                        file for file in tool_input['files'] \n                        if file['path'] in retry_decision['files_to_retry']\n                    ]\n                    \n                    # Ensure 'instructions' are present\n                    for file in retry_files:\n                        if 'instructions' not in file:\n                            file['instructions'] = \"Please reapply the previous instructions.\"\n                    \n                    if retry_files:\n                        retry_result, retry_console_output = await edit_and_apply_multiple(retry_files, tool_input['project_context'])\n                        console.print(Panel(retry_console_output, title=\"Retry Result\", style=\"cyan\"))\n                        assistant_response += f\"\\n\\nRetry result: {json.dumps(retry_result, indent=2)}\"\n                    else:\n                        console.print(Panel(\"No files to retry. Skipping retry.\", style=\"yellow\"))\n                else:\n                    console.print(Panel(\"Claude has decided not to retry editing\", style=\"green\"))\n\n        except APIError as e:\n            error_message = f\"Error in tool response: {str(e)}\"\n            console.print(Panel(error_message, title=\"Error\", style=\"bold red\"))\n            assistant_response += f\"\\n\\n{error_message}\"\n\n    if assistant_response:\n        current_conversation.append({\"role\": \"assistant\", \"content\": assistant_response})\n\n    conversation_history = messages + [{\"role\": \"assistant\", \"content\": assistant_response}]\n\n    # Display token usage at the end\n    display_token_usage()\n\n    return assistant_response, exit_continuation\n\ndef reset_code_editor_memory():\n    global code_editor_memory\n    code_editor_memory = []\n    console.print(Panel(\"Code editor memory has been reset.\", title=\"Reset\", style=\"bold green\"))\n\n\ndef reset_conversation():\n    global conversation_history, main_model_tokens, tool_checker_tokens, code_editor_tokens, code_execution_tokens, file_contents, code_editor_files\n    conversation_history = []\n    main_model_tokens = {'input': 0, 'output': 0}\n    tool_checker_tokens = {'input': 0, 'output': 0}\n    code_editor_tokens = {'input': 0, 'output': 0}\n    code_execution_tokens = {'input': 0, 'output': 0}\n    file_contents = {}\n    code_editor_files = set()\n    reset_code_editor_memory()\n    console.print(Panel(\"Conversation history, token counts, file contents, code editor memory, and code editor files have been reset.\", title=\"Reset\", style=\"bold green\"))\n    display_token_usage()\n\ndef display_token_usage():\n    from rich.table import Table\n    from rich.panel import Panel\n    from rich.box import ROUNDED\n\n    table = Table(box=ROUNDED)\n    table.add_column(\"Model\", style=\"cyan\")\n    table.add_column(\"Input\", style=\"magenta\")\n    table.add_column(\"Output\", style=\"magenta\")\n    table.add_column(\"Cache Write\", style=\"blue\")\n    table.add_column(\"Cache Read\", style=\"blue\")\n    table.add_column(\"Total\", style=\"green\")\n    table.add_column(f\"% of Context ({MAX_CONTEXT_TOKENS:,})\", style=\"yellow\")\n    table.add_column(\"Cost ($)\", style=\"red\")\n\n    model_costs = {\n        \"Main Model\": {\"input\": 3.00, \"output\": 15.00, \"cache_write\": 3.75, \"cache_read\": 0.30, \"has_context\": True},\n        \"Tool Checker\": {\"input\": 3.00, \"output\": 15.00, \"cache_write\": 3.75, \"cache_read\": 0.30, \"has_context\": False},\n        \"Code Editor\": {\"input\": 3.00, \"output\": 15.00, \"cache_write\": 3.75, \"cache_read\": 0.30, \"has_context\": True},\n        \"Code Execution\": {\"input\": 3.00, \"output\": 15.00, \"cache_write\": 3.75, \"cache_read\": 0.30, \"has_context\": False}\n    }\n\n    total_input = 0\n    total_output = 0\n    total_cache_write = 0\n    total_cache_read = 0\n    total_cost = 0\n    total_context_tokens = 0\n\n    for model, tokens in [(\"Main Model\", main_model_tokens),\n                          (\"Tool Checker\", tool_checker_tokens),\n                          (\"Code Editor\", code_editor_tokens),\n                          (\"Code Execution\", code_execution_tokens)]:\n        input_tokens = tokens['input']\n        output_tokens = tokens['output']\n        cache_write_tokens = tokens['cache_write']\n        cache_read_tokens = tokens['cache_read']\n        total_tokens = input_tokens + output_tokens + cache_write_tokens + cache_read_tokens\n\n        total_input += input_tokens\n        total_output += output_tokens\n        total_cache_write += cache_write_tokens\n        total_cache_read += cache_read_tokens\n\n        input_cost = (input_tokens / 1_000_000) * model_costs[model][\"input\"]\n        output_cost = (output_tokens / 1_000_000) * model_costs[model][\"output\"]\n        cache_write_cost = (cache_write_tokens / 1_000_000) * model_costs[model][\"cache_write\"]\n        cache_read_cost = (cache_read_tokens / 1_000_000) * model_costs[model][\"cache_read\"]\n        model_cost = input_cost + output_cost + cache_write_cost + cache_read_cost\n        total_cost += model_cost\n\n        if model_costs[model][\"has_context\"]:\n            total_context_tokens += total_tokens\n            percentage = (total_tokens / MAX_CONTEXT_TOKENS) * 100\n        else:\n            percentage = 0\n\n        table.add_row(\n            model,\n            f\"{input_tokens:,}\",\n            f\"{output_tokens:,}\",\n            f\"{cache_write_tokens:,}\",\n            f\"{cache_read_tokens:,}\",\n            f\"{total_tokens:,}\",\n            f\"{percentage:.2f}%\" if model_costs[model][\"has_context\"] else \"Doesn't save context\",\n            f\"${model_cost:.3f}\"\n        )\n\n    grand_total = total_input + total_output + total_cache_write + total_cache_read\n    total_percentage = (total_context_tokens / MAX_CONTEXT_TOKENS) * 100\n\n    table.add_row(\n        \"Total\",\n        f\"{total_input:,}\",\n        f\"{total_output:,}\",\n        f\"{total_cache_write:,}\",\n        f\"{total_cache_read:,}\",\n        f\"{grand_total:,}\",\n        f\"{total_percentage:.2f}%\",\n        f\"${total_cost:.3f}\",\n        style=\"bold\"\n    )\n\n    console.print(table)\n\n\n\nasync def test_voice_mode():\n    global voice_mode\n    voice_mode = True\n    initialize_speech_recognition()\n    console.print(Panel(\"Entering voice input test mode. Say a few phrases, then say 'exit voice mode' to end the test.\", style=\"bold green\"))\n    \n    while voice_mode:\n        user_input = await voice_input()\n        if user_input is None:\n            voice_mode = False\n            cleanup_speech_recognition()\n            console.print(Panel(\"Exited voice input test mode due to error.\", style=\"bold yellow\"))\n            break\n        \n        stay_in_voice_mode, command_result = process_voice_command(user_input)\n        if not stay_in_voice_mode:\n            voice_mode = False\n            cleanup_speech_recognition()\n            console.print(Panel(\"Exited voice input test mode.\", style=\"bold green\"))\n            break\n        elif command_result:\n            console.print(Panel(command_result, style=\"cyan\"))\n    \n    console.print(Panel(\"Voice input test completed.\", style=\"bold green\"))\n\nasync def main():\n    global automode, conversation_history, use_tts, tts_enabled\n    console.print(Panel(\"Welcome to the Claude-3-Sonnet Engineer Chat with Multi-Agent, Image, Voice, and Text-to-Speech Support!\", title=\"Welcome\", style=\"bold green\"))\n    console.print(\"Type 'exit' to end the conversation.\")\n    console.print(\"Type 'image' to include an image in your message.\")\n    console.print(\"Type 'voice' to enter voice input mode.\")\n    console.print(\"Type 'test voice' to run a voice input test.\")\n    console.print(\"Type 'automode [number]' to enter Autonomous mode with a specific number of iterations.\")\n    console.print(\"Type 'reset' to clear the conversation history.\")\n    console.print(\"Type 'save chat' to save the conversation to a Markdown file.\")\n    console.print(\"Type '11labs on' to enable text-to-speech.\")\n    console.print(\"Type '11labs off' to disable text-to-speech.\")\n    console.print(\"While in automode, press Ctrl+C at any time to exit the automode to return to regular chat.\")\n\n    voice_mode = False\n\n    while True:\n        if voice_mode:\n            user_input = await voice_input()\n            if user_input is None:\n                voice_mode = False\n                cleanup_speech_recognition()\n                console.print(Panel(\"Exited voice input mode due to error. Returning to text input.\", style=\"bold yellow\"))\n                continue\n            \n            stay_in_voice_mode, command_result = process_voice_command(user_input)\n            if not stay_in_voice_mode:\n                voice_mode = False\n                cleanup_speech_recognition()\n                console.print(Panel(\"Exited voice input mode. Returning to text input.\", style=\"bold green\"))\n                if command_result:\n                    console.print(Panel(command_result, style=\"cyan\"))\n                continue\n            elif command_result:\n                console.print(Panel(command_result, style=\"cyan\"))\n                continue\n        else:\n            user_input = await get_user_input()\n\n        if user_input.lower() == 'exit':\n            console.print(Panel(\"Thank you for chatting. Goodbye!\", title_align=\"left\", title=\"Goodbye\", style=\"bold green\"))\n            break\n\n        if user_input.lower() == 'test voice':\n            await test_voice_mode()\n            continue\n\n        if user_input.lower() == '11labs on':\n            use_tts = True\n            tts_enabled = True\n            console.print(Panel(\"Text-to-speech enabled.\", style=\"bold green\"))\n            continue\n\n        if user_input.lower() == '11labs off':\n            use_tts = False\n            tts_enabled = False\n            console.print(Panel(\"Text-to-speech disabled.\", style=\"bold yellow\"))\n            continue\n\n\n\n        if user_input.lower() == 'reset':\n            reset_conversation()\n            continue\n\n        if user_input.lower() == 'save chat':\n            filename = save_chat()\n            console.print(Panel(f\"Chat saved to {filename}\", title=\"Chat Saved\", style=\"bold green\"))\n            continue\n\n        if user_input.lower() == 'voice':\n            voice_mode = True\n            initialize_speech_recognition()\n            console.print(Panel(\"Entering voice input mode. Say 'exit voice mode' to return to text input.\", style=\"bold green\"))\n            continue\n\n        if user_input.lower() == 'image':\n            image_path = (await get_user_input(\"Drag and drop your image here, then press enter: \")).strip().replace(\"'\", \"\")\n\n            if os.path.isfile(image_path):\n                user_input = await get_user_input(\"You (prompt for image): \")\n                response, _ = await chat_with_claude(user_input, image_path)\n            else:\n                console.print(Panel(\"Invalid image path. Please try again.\", title=\"Error\", style=\"bold red\"))\n                continue\n        elif user_input.lower().startswith('automode'):\n            try:\n                parts = user_input.split()\n                if len(parts) > 1 and parts[1].isdigit():\n                    max_iterations = int(parts[1])\n                else:\n                    max_iterations = MAX_CONTINUATION_ITERATIONS\n\n                automode = True\n                console.print(Panel(f\"Entering automode with {max_iterations} iterations. Please provide the goal of the automode.\", title_align=\"left\", title=\"Automode\", style=\"bold yellow\"))\n                console.print(Panel(\"Press Ctrl+C at any time to exit the automode loop.\", style=\"bold yellow\"))\n                user_input = await get_user_input()\n\n                iteration_count = 0\n                error_count = 0\n                max_errors = 3  # Maximum number of consecutive errors before exiting automode\n                try:\n                    while automode and iteration_count < max_iterations:\n                        try:\n                            response, exit_continuation = await chat_with_claude(user_input, current_iteration=iteration_count+1, max_iterations=max_iterations)\n                            error_count = 0  # Reset error count on successful iteration\n                        except Exception as e:\n                            console.print(Panel(f\"Error in automode iteration: {str(e)}\", style=\"bold red\"))\n                            error_count += 1\n                            if error_count >= max_errors:\n                                console.print(Panel(f\"Exiting automode due to {max_errors} consecutive errors.\", style=\"bold red\"))\n                                automode = False\n                                break\n                            continue\n\n                        if exit_continuation or CONTINUATION_EXIT_PHRASE in response:\n                            console.print(Panel(\"Automode completed.\", title_align=\"left\", title=\"Automode\", style=\"green\"))\n                            automode = False\n                        else:\n                            console.print(Panel(f\"Continuation iteration {iteration_count + 1} completed. Press Ctrl+C to exit automode. \", title_align=\"left\", title=\"Automode\", style=\"yellow\"))\n                            user_input = \"Continue with the next step. Or STOP by saying 'AUTOMODE_COMPLETE' if you think you've achieved the results established in the original request.\"\n                        iteration_count += 1\n\n                        if iteration_count >= max_iterations:\n                            console.print(Panel(\"Max iterations reached. Exiting automode.\", title_align=\"left\", title=\"Automode\", style=\"bold red\"))\n                            automode = False\n                except KeyboardInterrupt:\n                    console.print(Panel(\"\\nAutomode interrupted by user. Exiting automode.\", title_align=\"left\", title=\"Automode\", style=\"bold red\"))\n                    automode = False\n                    if conversation_history and conversation_history[-1][\"role\"] == \"user\":\n                        conversation_history.append({\"role\": \"assistant\", \"content\": \"Automode interrupted. How can I assist you further?\"})\n            except KeyboardInterrupt:\n                console.print(Panel(\"\\nAutomode interrupted by user. Exiting automode.\", title_align=\"left\", title=\"Automode\", style=\"bold red\"))\n                automode = False\n                if conversation_history and conversation_history[-1][\"role\"] == \"user\":\n                    conversation_history.append({\"role\": \"assistant\", \"content\": \"Automode interrupted. How can I assist you further?\"})\n\n            console.print(Panel(\"Exited automode. Returning to regular chat.\", style=\"green\"))\n        else:\n            response, _ = await chat_with_claude(user_input)\n\n\n\n    # Add more tests for other functions as needed\n\nif __name__ == \"__main__\":\n\n\n    # Run the main program\n    try:\n        asyncio.run(main())\n    except KeyboardInterrupt:\n        console.print(\"\\nProgram interrupted by user. Exiting...\", style=\"bold red\")\n    except Exception as e:\n        console.print(f\"An unexpected error occurred: {str(e)}\", style=\"bold red\")\n        logging.error(f\"Unexpected error: {str(e)}\", exc_info=True)\n    finally:\n        console.print(\"Program finished. Goodbye!\", style=\"bold green\")\n"
  },
  {
    "path": "Claude-Eng-v2/ollama-eng.py",
    "content": "import os\nfrom dotenv import load_dotenv\nimport json\nfrom tavily import TavilyClient\nimport re\nimport ollama\nimport asyncio\nimport difflib\nimport time\nimport logging\nfrom typing import Optional, Dict, Any\nfrom rich.console import Console\nfrom rich.panel import Panel\nfrom rich.syntax import Syntax\nfrom rich.markdown import Markdown\nimport asyncio\nimport aiohttp\nfrom prompt_toolkit import PromptSession\nfrom prompt_toolkit.styles import Style\n\nasync def get_user_input(prompt=\"You: \"):\n    style = Style.from_dict({\n        'prompt': 'cyan bold',\n    })\n    session = PromptSession(style=style)\n    return await session.prompt_async(prompt, multiline=False)\nfrom rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn\nimport datetime\n# Load environment variables from .env file\nload_dotenv()\n\n# Initialize the Ollama client\nclient = ollama.AsyncClient()\n\n# Initialize the Tavily client\ntavily_api_key = os.getenv(\"TAVILY_API_KEY\")\nif not tavily_api_key:\n    raise ValueError(\"TAVILY_API_KEY not found in environment variables\")\ntavily = TavilyClient(api_key=tavily_api_key)\n\nconsole = Console()\n\n\n\n# Set up the conversation memory (maintains context for MAINMODEL)\nconversation_history = []\n\n# Store file contents (part of the context for MAINMODEL)\nfile_contents = {}\n\n# Code editor memory (maintains some context for CODEEDITORMODEL between calls)\ncode_editor_memory = []\n\n# Files already present in code editor's context\ncode_editor_files = set()\n\n# automode flag\nautomode = False\n\n# Store file contents\nfile_contents = {}\n\n# Global dictionary to store running processes\nrunning_processes = {}\n\n# Constants\nCONTINUATION_EXIT_PHRASE = \"AUTOMODE_COMPLETE\"\nMAX_CONTINUATION_ITERATIONS = 25\nMAX_CONTEXT_TOKENS = 200000  # Reduced to 200k tokens for context window\n\n# Models\n# Models that maintain context memory across interactions\nMAINMODEL = \"mistral-nemo\"  # Maintains conversation history and file contents\n\n# Models that don't maintain context (memory is reset after each call)\nTOOLCHECKERMODEL = \"mistral-nemo\"\nCODEEDITORMODEL = \"mistral-nemo\"\n\n# System prompts\nBASE_SYSTEM_PROMPT = \"\"\"\nYou are Ollama Engineer, an AI assistant powered Ollama models, specialized in software development with access to a variety of tools and the ability to instruct and direct a coding agent and a code execution one. Your capabilities include:\n\n1. Creating and managing project structures\n2. Writing, debugging, and improving code across multiple languages\n3. Providing architectural insights and applying design patterns\n4. Staying current with the latest technologies and best practices\n5. Analyzing and manipulating files within the project directory\n6. Performing web searches for up-to-date information\n7. Executing code and analyzing its output within an isolated 'code_execution_env' virtual environment\n8. Managing and stopping running processes started within the 'code_execution_env'\n\nAvailable tools and their optimal use cases:\n\n1. create_folder: Create new directories in the project structure.\n2. create_file: Generate new files with specified content. Strive to make the file as complete and useful as possible.\n3. edit_and_apply: Examine and modify existing files by instructing a separate AI coding agent. You are responsible for providing clear, detailed instructions to this agent. When using this tool:\n   - Provide comprehensive context about the project, including recent changes, new variables or functions, and how files are interconnected.\n   - Clearly state the specific changes or improvements needed, explaining the reasoning behind each modification.\n   - Include ALL the snippets of code to change, along with the desired modifications.\n   - Specify coding standards, naming conventions, or architectural patterns to be followed.\n   - Anticipate potential issues or conflicts that might arise from the changes and provide guidance on how to handle them.\n4. execute_code: Run Python code exclusively in the 'code_execution_env' virtual environment and analyze its output. Use this when you need to test code functionality or diagnose issues. Remember that all code execution happens in this isolated environment. This tool now returns a process ID for long-running processes.\n5. stop_process: Stop a running process by its ID. Use this when you need to terminate a long-running process started by the execute_code tool.\n6. read_file: Read the contents of an existing file.\n7. read_multiple_files: Read the contents of multiple existing files at once. Use this when you need to examine or work with multiple files simultaneously.\n8. list_files: List all files and directories in a specified folder.\n9. tavily_search: Perform a web search using the Tavily API for up-to-date information.\n\nTool Usage Guidelines:\n- Always use the most appropriate tool for the task at hand.\n- Provide detailed and clear instructions when using tools, especially for edit_and_apply.\n- After making changes, always review the output to ensure accuracy and alignment with intentions.\n- Use execute_code to run and test code within the 'code_execution_env' virtual environment, then analyze the results.\n- For long-running processes, use the process ID returned by execute_code to stop them later if needed.\n- Proactively use tavily_search when you need up-to-date information or additional context.\n- When working with multiple files, consider using read_multiple_files for efficiency.\n\nError Handling and Recovery:\n- If a tool operation fails, carefully analyze the error message and attempt to resolve the issue.\n- For file-related errors, double-check file paths and permissions before retrying.\n- If a search fails, try rephrasing the query or breaking it into smaller, more specific searches.\n- If code execution fails, analyze the error output and suggest potential fixes, considering the isolated nature of the environment.\n- If a process fails to stop, consider potential reasons and suggest alternative approaches.\n\nProject Creation and Management:\n1. Start by creating a root folder for new projects.\n2. Create necessary subdirectories and files within the root folder.\n3. Organize the project structure logically, following best practices for the specific project type.\n\nAlways strive for accuracy, clarity, and efficiency in your responses and actions. Your instructions must be precise and comprehensive. If uncertain, use the tavily_search tool or admit your limitations. When executing code, always remember that it runs in the isolated 'code_execution_env' virtual environment. Be aware of any long-running processes you start and manage them appropriately, including stopping them when they are no longer needed.\n\nWhen using tools:\n1. Carefully consider if a tool is necessary before using it.\n2. Ensure all required parameters are provided and valid.\n3. Handle both successful results and errors gracefully.\n4. Provide clear explanations of tool usage and results to the user.\n\nRemember, you are an AI assistant, and your primary goal is to help the user accomplish their tasks effectively and efficiently while maintaining the integrity and security of their development environment.\n\"\"\"\n\nAUTOMODE_SYSTEM_PROMPT = \"\"\"\nYou are currently in automode. Follow these guidelines:\n\n1. Goal Setting:\n   - Set clear, achievable goals based on the user's request.\n   - Break down complex tasks into smaller, manageable goals.\n\n2. Goal Execution:\n   - Work through goals systematically, using appropriate tools for each task.\n   - Utilize file operations, code writing, and web searches as needed.\n   - Always read a file before editing and review changes after editing.\n\n3. Progress Tracking:\n   - Provide regular updates on goal completion and overall progress.\n   - Use the iteration information to pace your work effectively.\n\n4. Tool Usage:\n   - Leverage all available tools to accomplish your goals efficiently.\n   - Prefer edit_and_apply for file modifications, applying changes in chunks for large edits.\n   - Use tavily_search proactively for up-to-date information.\n\n5. Error Handling:\n   - If a tool operation fails, analyze the error and attempt to resolve the issue.\n   - For persistent errors, consider alternative approaches to achieve the goal.\n\n6. Automode Completion:\n   - When all goals are completed, respond with \"AUTOMODE_COMPLETE\" to exit automode.\n   - Do not ask for additional tasks or modifications once goals are achieved.\n\n7. Iteration Awareness:\n   - You have access to this {iteration_info}.\n   - Use this information to prioritize tasks and manage time effectively.\n\nRemember: Focus on completing the established goals efficiently and effectively. Avoid unnecessary conversations or requests for additional tasks.\n\"\"\"\n\n\ndef update_system_prompt(current_iteration: Optional[int] = None, max_iterations: Optional[int] = None) -> str:\n    global file_contents\n    chain_of_thought_prompt = \"\"\"\n    Answer the user's request using relevant tools (if they are available). Before calling a tool, do some analysis within <thinking></thinking> tags. First, think about which of the provided tools is the relevant tool to answer the user's request. Second, go through each of the required parameters of the relevant tool and determine if the user has directly provided or given enough information to infer a value. When deciding if the parameter can be inferred, carefully consider all the context to see if it supports a specific value. If all of the required parameters are present or can be reasonably inferred, close the thinking tag and proceed with the tool call. BUT, if one of the values for a required parameter is missing, DO NOT invoke the function (not even with fillers for the missing params) and instead, ask the user to provide the missing parameters. DO NOT ask for more information on optional parameters if it is not provided.\n\n    Do not reflect on the quality of the returned search results in your response.\n    \"\"\"\n    \n    file_contents_prompt = \"\\n\\nFile Contents:\\n\"\n    for path, content in file_contents.items():\n        file_contents_prompt += f\"\\n--- {path} ---\\n{content}\\n\"\n    \n    if automode:\n        iteration_info = \"\"\n        if current_iteration is not None and max_iterations is not None:\n            iteration_info = f\"You are currently on iteration {current_iteration} out of {max_iterations} in automode.\"\n        return BASE_SYSTEM_PROMPT + file_contents_prompt + \"\\n\\n\" + AUTOMODE_SYSTEM_PROMPT.format(iteration_info=iteration_info) + \"\\n\\n\" + chain_of_thought_prompt\n    else:\n        return BASE_SYSTEM_PROMPT + file_contents_prompt + \"\\n\\n\" + chain_of_thought_prompt\n\ndef create_folder(path):\n    try:\n        os.makedirs(path, exist_ok=True)\n        return f\"Folder created: {path}\"\n    except Exception as e:\n        return f\"Error creating folder: {str(e)}\"\n\ndef create_file(path, content=\"\"):\n    global file_contents\n    try:\n        with open(path, 'w') as f:\n            f.write(content)\n        file_contents[path] = content\n        return f\"File created and added to system prompt: {path}\"\n    except Exception as e:\n        return f\"Error creating file: {str(e)}\"\n\ndef highlight_diff(diff_text):\n    return Syntax(diff_text, \"diff\", theme=\"monokai\", line_numbers=True)\n\ndef generate_and_apply_diff(original_content, new_content, path):\n    diff = list(difflib.unified_diff(\n        original_content.splitlines(keepends=True),\n        new_content.splitlines(keepends=True),\n        fromfile=f\"a/{path}\",\n        tofile=f\"b/{path}\",\n        n=3\n    ))\n\n    if not diff:\n        return \"No changes detected.\"\n\n    try:\n        with open(path, 'w') as f:\n            f.writelines(new_content)\n\n        diff_text = ''.join(diff)\n        highlighted_diff = highlight_diff(diff_text)\n\n        diff_panel = Panel(\n            highlighted_diff,\n            title=f\"Changes in {path}\",\n            expand=False,\n            border_style=\"cyan\"\n        )\n\n        console.print(diff_panel)\n\n        added_lines = sum(1 for line in diff if line.startswith('+') and not line.startswith('+++'))\n        removed_lines = sum(1 for line in diff if line.startswith('-') and not line.startswith('---'))\n\n        summary = f\"Changes applied to {path}:\\n\"\n        summary += f\"  Lines added: {added_lines}\\n\"\n        summary += f\"  Lines removed: {removed_lines}\\n\"\n\n        return summary\n\n    except Exception as e:\n        error_panel = Panel(\n            f\"Error: {str(e)}\",\n            title=\"Error Applying Changes\",\n            style=\"bold red\"\n        )\n        console.print(error_panel)\n        return f\"Error applying changes: {str(e)}\"\n\n\nasync def generate_edit_instructions(file_path, file_content, instructions, project_context, full_file_contents):\n    global code_editor_tokens, code_editor_memory, code_editor_files\n    try:\n        # Prepare memory context (this is the only part that maintains some context between calls)\n        memory_context = \"\\n\".join([f\"Memory {i+1}:\\n{mem}\" for i, mem in enumerate(code_editor_memory)])\n\n        # Prepare full file contents context, excluding the file being edited if it's already in code_editor_files\n        full_file_contents_context = \"\\n\\n\".join([\n            f\"--- {path} ---\\n{content}\" for path, content in full_file_contents.items()\n            if path != file_path or path not in code_editor_files\n        ])\n\n        system_prompt = f\"\"\"\n        You are an AI coding agent that generates edit instructions for code files. Your task is to analyze the provided code and generate SEARCH/REPLACE blocks for necessary changes. Follow these steps:\n\n        1. Review the entire file content to understand the context:\n        {file_content}\n\n        2. Carefully analyze the specific instructions:\n        {instructions}\n\n        3. Take into account the overall project context:\n        {project_context}\n\n        4. Consider the memory of previous edits:\n        {memory_context}\n\n        5. Consider the full context of all files in the project:\n        {full_file_contents_context}\n\n        6. Generate SEARCH/REPLACE blocks for each necessary change. Each block should:\n           - Include enough context to uniquely identify the code to be changed\n           - Provide the exact replacement code, maintaining correct indentation and formatting\n           - Focus on specific, targeted changes rather than large, sweeping modifications\n\n        7. Ensure that your SEARCH/REPLACE blocks:\n           - Address all relevant aspects of the instructions\n           - Maintain or enhance code readability and efficiency\n           - Consider the overall structure and purpose of the code\n           - Follow best practices and coding standards for the language\n           - Maintain consistency with the project context and previous edits\n           - Take into account the full context of all files in the project\n\n        IMPORTANT: RETURN ONLY THE SEARCH/REPLACE BLOCKS. NO EXPLANATIONS OR COMMENTS.\n        USE THE FOLLOWING FORMAT FOR EACH BLOCK:\n\n        <SEARCH>\n        Code to be replaced\n        </SEARCH>\n        <REPLACE>\n        New code to insert\n        </REPLACE>\n\n        If no changes are needed, return an empty list.\n        \"\"\"\n\n        # Make the API call to CODEEDITORMODEL (context is not maintained except for code_editor_memory)\n        response = client.messages.create(\n            model=CODEEDITORMODEL,\n            max_tokens=8000,\n            system=system_prompt,\n            extra_headers={\"anthropic-beta\": \"max-tokens-3-5-sonnet-2024-07-15\"},\n            messages=[\n                {\"role\": \"user\", \"content\": \"Generate SEARCH/REPLACE blocks for the necessary changes.\"}\n            ]\n        )\n        # Update token usage for code editor\n        code_editor_tokens['input'] += response.usage.input_tokens\n        code_editor_tokens['output'] += response.usage.output_tokens\n\n        # Parse the response to extract SEARCH/REPLACE blocks\n        edit_instructions = parse_search_replace_blocks(response.content[0].text)\n\n        # Update code editor memory (this is the only part that maintains some context between calls)\n        code_editor_memory.append(f\"Edit Instructions for {file_path}:\\n{response.content[0].text}\")\n\n        # Add the file to code_editor_files set\n        code_editor_files.add(file_path)\n\n        return edit_instructions\n\n    except Exception as e:\n        console.print(f\"Error in generating edit instructions: {str(e)}\", style=\"bold red\")\n        return []  # Return empty list if any exception occurs\n\n\n\ndef parse_search_replace_blocks(response_text):\n    blocks = []\n    pattern = r'<SEARCH>\\n(.*?)\\n</SEARCH>\\n<REPLACE>\\n(.*?)\\n</REPLACE>'\n    matches = re.findall(pattern, response_text, re.DOTALL)\n    \n    for search, replace in matches:\n        blocks.append({\n            'search': search.strip(),\n            'replace': replace.strip()\n        })\n    \n    return json.dumps(blocks)  # Keep returning JSON string\n\n\nasync def edit_and_apply(path, instructions, project_context, is_automode=False, max_retries=3):\n    global file_contents\n    try:\n        original_content = file_contents.get(path, \"\")\n        if not original_content:\n            with open(path, 'r') as file:\n                original_content = file.read()\n            file_contents[path] = original_content\n\n        for attempt in range(max_retries):\n            edit_instructions_json = await generate_edit_instructions(path, original_content, instructions, project_context, file_contents)\n            \n            if edit_instructions_json:\n                edit_instructions = json.loads(edit_instructions_json)  # Parse JSON here\n                console.print(Panel(f\"Attempt {attempt + 1}/{max_retries}: The following SEARCH/REPLACE blocks have been generated:\", title=\"Edit Instructions\", style=\"cyan\"))\n                for i, block in enumerate(edit_instructions, 1):\n                    console.print(f\"Block {i}:\")\n                    console.print(Panel(f\"SEARCH:\\n{block['search']}\\n\\nREPLACE:\\n{block['replace']}\", expand=False))\n\n                edited_content, changes_made, failed_edits = await apply_edits(path, edit_instructions, original_content)\n\n                if changes_made:\n                    file_contents[path] = edited_content  # Update the file_contents with the new content\n                    console.print(Panel(f\"File contents updated in system prompt: {path}\", style=\"green\"))\n                    \n                    if failed_edits:\n                        console.print(Panel(f\"Some edits could not be applied. Retrying...\", style=\"yellow\"))\n                        instructions += f\"\\n\\nPlease retry the following edits that could not be applied:\\n{failed_edits}\"\n                        original_content = edited_content\n                        continue\n                    \n                    return f\"Changes applied to {path}\"\n                elif attempt == max_retries - 1:\n                    return f\"No changes could be applied to {path} after {max_retries} attempts. Please review the edit instructions and try again.\"\n                else:\n                    console.print(Panel(f\"No changes could be applied in attempt {attempt + 1}. Retrying...\", style=\"yellow\"))\n            else:\n                return f\"No changes suggested for {path}\"\n        \n        return f\"Failed to apply changes to {path} after {max_retries} attempts.\"\n    except Exception as e:\n        return f\"Error editing/applying to file: {str(e)}\"\n\n\n\nasync def apply_edits(file_path, edit_instructions, original_content):\n    changes_made = False\n    edited_content = original_content\n    total_edits = len(edit_instructions)\n    failed_edits = []\n\n    with Progress(\n        SpinnerColumn(),\n        TextColumn(\"[progress.description]{task.description}\"),\n        BarColumn(),\n        TextColumn(\"[progress.percentage]{task.percentage:>3.0f}%\"),\n        console=console\n    ) as progress:\n        edit_task = progress.add_task(\"[cyan]Applying edits...\", total=total_edits)\n\n        for i, edit in enumerate(edit_instructions, 1):\n            search_content = edit['search'].strip()\n            replace_content = edit['replace'].strip()\n            \n            # Use regex to find the content, ignoring leading/trailing whitespace\n            pattern = re.compile(re.escape(search_content), re.DOTALL)\n            match = pattern.search(edited_content)\n            \n            if match:\n                # Replace the content, preserving the original whitespace\n                start, end = match.span()\n                # Strip <SEARCH> and <REPLACE> tags from replace_content\n                replace_content_cleaned = re.sub(r'</?SEARCH>|</?REPLACE>', '', replace_content)\n                edited_content = edited_content[:start] + replace_content_cleaned + edited_content[end:]\n                changes_made = True\n                \n                # Display the diff for this edit\n                diff_result = generate_diff(search_content, replace_content, file_path)\n                console.print(Panel(diff_result, title=f\"Changes in {file_path} ({i}/{total_edits})\", style=\"cyan\"))\n            else:\n                console.print(Panel(f\"Edit {i}/{total_edits} not applied: content not found\", style=\"yellow\"))\n                failed_edits.append(f\"Edit {i}: {search_content}\")\n\n            progress.update(edit_task, advance=1)\n\n    if not changes_made:\n        console.print(Panel(\"No changes were applied. The file content already matches the desired state.\", style=\"green\"))\n    else:\n        # Write the changes to the file\n        with open(file_path, 'w') as file:\n            file.write(edited_content)\n        console.print(Panel(f\"Changes have been written to {file_path}\", style=\"green\"))\n\n    return edited_content, changes_made, \"\\n\".join(failed_edits)\n\ndef generate_diff(original, new, path):\n    diff = list(difflib.unified_diff(\n        original.splitlines(keepends=True),\n        new.splitlines(keepends=True),\n        fromfile=f\"a/{path}\",\n        tofile=f\"b/{path}\",\n        n=3\n    ))\n\n    diff_text = ''.join(diff)\n    highlighted_diff = highlight_diff(diff_text)\n\n    return highlighted_diff\n\ndef read_file(path):\n    global file_contents\n    try:\n        with open(path, 'r') as f:\n            content = f.read()\n        file_contents[path] = content\n        return f\"File '{path}' has been read and stored in the system prompt.\"\n    except Exception as e:\n        return f\"Error reading file: {str(e)}\"\n\ndef read_multiple_files(paths):\n    global file_contents\n    results = []\n    for path in paths:\n        try:\n            with open(path, 'r') as f:\n                content = f.read()\n            file_contents[path] = content\n            results.append(f\"File '{path}' has been read and stored in the system prompt.\")\n        except Exception as e:\n            results.append(f\"Error reading file '{path}': {str(e)}\")\n    return \"\\n\".join(results)\n\ndef list_files(path=\".\"):\n    try:\n        files = os.listdir(path)\n        return \"\\n\".join(files)\n    except Exception as e:\n        return f\"Error listing files: {str(e)}\"\n\ndef tavily_search(query):\n    try:\n        response = tavily.qna_search(query=query, search_depth=\"advanced\")\n        return response\n    except Exception as e:\n        return f\"Error performing search: {str(e)}\"\n\ntools = [\n    {\n        \"type\": \"function\",\n        \"function\": {\n            \"name\": \"create_folder\",\n            \"description\": \"Create a new folder at the specified path\",\n            \"parameters\": {\n                \"type\": \"object\",\n                \"properties\": {\n                    \"path\": {\n                        \"type\": \"string\",\n                        \"description\": \"The absolute or relative path where the folder should be created\"\n                    }\n                },\n                \"required\": [\"path\"]\n            }\n        }\n    },\n    {\n        \"type\": \"function\",\n        \"function\": {\n            \"name\": \"create_file\",\n            \"description\": \"Create a new file at the specified path with the given content\",\n            \"parameters\": {\n                \"type\": \"object\",\n                \"properties\": {\n                    \"path\": {\n                        \"type\": \"string\",\n                        \"description\": \"The absolute or relative path where the file should be created\"\n                    },\n                    \"content\": {\n                        \"type\": \"string\",\n                        \"description\": \"The content of the file\"\n                    }\n                },\n                \"required\": [\"path\", \"content\"]\n            }\n        }\n    },\n    {\n        \"type\": \"function\",\n        \"function\": {\n            \"name\": \"edit_and_apply\",\n            \"description\": \"Apply AI-powered improvements to a file based on specific instructions and project context\",\n            \"parameters\": {\n                \"type\": \"object\",\n                \"properties\": {\n                    \"path\": {\n                        \"type\": \"string\",\n                        \"description\": \"The absolute or relative path of the file to edit\"\n                    },\n                    \"instructions\": {\n                        \"type\": \"string\",\n                        \"description\": \"Detailed instructions for the changes to be made\"\n                    },\n                    \"project_context\": {\n                        \"type\": \"string\",\n                        \"description\": \"Comprehensive context about the project\"\n                    }\n                },\n                \"required\": [\"path\", \"instructions\", \"project_context\"]\n            }\n        }\n    },\n    {\n        \"type\": \"function\",\n        \"function\": {\n            \"name\": \"read_file\",\n            \"description\": \"Read the contents of a file at the specified path\",\n            \"parameters\": {\n                \"type\": \"object\",\n                \"properties\": {\n                    \"path\": {\n                        \"type\": \"string\",\n                        \"description\": \"The absolute or relative path of the file to read\"\n                    }\n                },\n                \"required\": [\"path\"]\n            }\n        }\n    },\n    {\n        \"type\": \"function\",\n        \"function\": {\n            \"name\": \"read_multiple_files\",\n            \"description\": \"Read the contents of multiple files at the specified paths\",\n            \"parameters\": {\n                \"type\": \"object\",\n                \"properties\": {\n                    \"paths\": {\n                        \"type\": \"array\",\n                        \"items\": {\n                            \"type\": \"string\"\n                        },\n                        \"description\": \"An array of absolute or relative paths of the files to read\"\n                    }\n                },\n                \"required\": [\"paths\"]\n            }\n        }\n    },\n    {\n        \"type\": \"function\",\n        \"function\": {\n            \"name\": \"list_files\",\n            \"description\": \"List all files and directories in the specified folder\",\n            \"parameters\": {\n                \"type\": \"object\",\n                \"properties\": {\n                    \"path\": {\n                        \"type\": \"string\",\n                        \"description\": \"The absolute or relative path of the folder to list\"\n                    }\n                }\n            }\n        }\n    },\n    {\n        \"type\": \"function\",\n        \"function\": {\n            \"name\": \"tavily_search\",\n            \"description\": \"Perform a web search using the Tavily API\",\n            \"parameters\": {\n                \"type\": \"object\",\n                \"properties\": {\n                    \"query\": {\n                        \"type\": \"string\",\n                        \"description\": \"The search query\"\n                    }\n                },\n                \"required\": [\"query\"]\n            }\n        }\n    }\n]\n\nfrom typing import Dict, Any\n\nasync def execute_tool(tool_call: Dict[str, Any]) -> Dict[str, Any]:\n    try:\n        function_call = tool_call['function']\n        tool_name = function_call['name']\n        tool_arguments = function_call['arguments']\n        \n        # Check if tool_arguments is a string and parse it if necessary\n        if isinstance(tool_arguments, str):\n            try:\n                tool_input = json.loads(tool_arguments)\n            except json.JSONDecodeError:\n                return {\n                    \"content\": f\"Error: Failed to parse tool arguments for {tool_name}\",\n                    \"is_error\": True\n                }\n        else:\n            tool_input = tool_arguments\n\n        result = None\n        is_error = False\n\n        if tool_name == \"create_folder\":\n            if \"path\" not in tool_input:\n                raise KeyError(\"Missing 'path' parameter for create_folder\")\n            result = create_folder(tool_input[\"path\"])\n        elif tool_name == \"create_file\":\n            result = create_file(tool_input[\"path\"], tool_input.get(\"content\", \"\"))\n        elif tool_name == \"edit_and_apply\":\n            result = await edit_and_apply(\n                tool_input[\"path\"],\n                tool_input[\"instructions\"],\n                tool_input[\"project_context\"],\n                is_automode=automode\n            )\n        elif tool_name == \"read_file\":\n            result = read_file(tool_input[\"path\"])\n        elif tool_name == \"read_multiple_files\":\n            result = read_multiple_files(tool_input[\"paths\"])\n        elif tool_name == \"list_files\":\n            result = list_files(tool_input.get(\"path\", \".\"))\n        elif tool_name == \"tavily_search\":\n            result = tavily_search(tool_input[\"query\"])\n        else:\n            is_error = True\n            result = f\"Unknown tool: {tool_name}\"\n\n        return {\n            \"content\": result,\n            \"is_error\": is_error\n        }\n    except KeyError as e:\n        error_message = f\"Missing required parameter {str(e)} for tool {tool_name}\"\n        logging.error(error_message)\n        return {\n            \"content\": f\"Error: {error_message}\",\n            \"is_error\": True\n        }\n    except Exception as e:\n        error_message = f\"Error executing tool {tool_name}: {str(e)}\"\n        logging.error(error_message)\n        return {\n            \"content\": f\"Error: {error_message}\",\n            \"is_error\": True\n        }\n\n\ndef parse_goals(response):\n    goals = re.findall(r'Goal \\d+: (.+)', response)\n    return goals\n\nasync def execute_goals(goals):\n    global automode\n    for i, goal in enumerate(goals, 1):\n        console.print(Panel(f\"Executing Goal {i}: {goal}\", title=\"Goal Execution\", style=\"bold yellow\"))\n        response, _ = await chat_with_ollama(f\"Continue working on goal: {goal}\")\n        if CONTINUATION_EXIT_PHRASE in response:\n            automode = False\n            console.print(Panel(\"Exiting automode.\", title=\"Automode\", style=\"bold green\"))\n            break\n\nasync def run_goals(response):\n    goals = parse_goals(response)\n    await execute_goals(goals)\n\n\ndef save_chat():\n    # Generate filename\n    now = datetime.datetime.now()\n    filename = f\"Chat_{now.strftime('%H%M')}.md\"\n    \n    # Format conversation history\n    formatted_chat = \"# Claude-3-Sonnet Engineer Chat Log\\n\\n\"\n    for message in conversation_history:\n        if message['role'] == 'user':\n            formatted_chat += f\"## User\\n\\n{message['content']}\\n\\n\"\n        elif message['role'] == 'assistant':\n            if isinstance(message['content'], str):\n                formatted_chat += f\"## Claude\\n\\n{message['content']}\\n\\n\"\n            elif isinstance(message['content'], list):\n                for content in message['content']:\n                    if content['type'] == 'tool_use':\n                        formatted_chat += f\"### Tool Use: {content['name']}\\n\\n```json\\n{json.dumps(content['input'], indent=2)}\\n```\\n\\n\"\n                    elif content['type'] == 'text':\n                        formatted_chat += f\"## Claude\\n\\n{content['text']}\\n\\n\"\n        elif message['role'] == 'user' and isinstance(message['content'], list):\n            for content in message['content']:\n                if content['type'] == 'tool_result':\n                    formatted_chat += f\"### Tool Result\\n\\n```\\n{content['content']}\\n```\\n\\n\"\n    \n    # Save to file\n    with open(filename, 'w', encoding='utf-8') as f:\n        f.write(formatted_chat)\n    \n    return filename\n\n\n\nasync def chat_with_ollama(user_input, image_path=None, current_iteration=None, max_iterations=None):\n    global conversation_history, automode, main_model_tokens\n\n    # This function uses MAINMODEL, which maintains context across calls\n    current_conversation = []\n\n    current_conversation.append({\"role\": \"user\", \"content\": user_input})\n\n    # Filter conversation history to maintain context\n    filtered_conversation_history = []\n    for message in conversation_history:\n        if isinstance(message['content'], list):\n            filtered_content = [\n                content for content in message['content']\n                if content.get('type') != 'tool_result' or (\n                    content.get('type') == 'tool_result' and\n                    not any(keyword in content.get('output', '') for keyword in [\n                        \"File contents updated in system prompt\",\n                        \"File created and added to system prompt\",\n                        \"has been read and stored in the system prompt\"\n                    ])\n                )\n            ]\n            if filtered_content:\n                filtered_conversation_history.append({**message, 'content': filtered_content})\n        else:\n            filtered_conversation_history.append(message)\n\n    # Combine filtered history with current conversation to maintain context\n    messages = filtered_conversation_history + current_conversation\n\n    try:\n        # MAINMODEL call, which maintains context\n        # Prepend the system message to the messages list\n        system_message = {\"role\": \"system\", \"content\": update_system_prompt(current_iteration, max_iterations)}\n        messages_with_system = [system_message] + messages\n        \n        response = await client.chat(\n            model=MAINMODEL,\n            messages=messages_with_system,\n            tools=tools,\n            stream=False\n        )\n        \n        # Check if the response is a dictionary\n        if isinstance(response, dict):\n            if 'error' in response:\n                console.print(Panel(f\"Error: {response['error']}\", title=\"API Error\", style=\"bold red\"))\n                return f\"I'm sorry, but there was an error with the model response: {response['error']}\", False\n            elif 'message' in response:\n                assistant_message = response['message']\n                assistant_response = assistant_message.get('content', '')\n                exit_continuation = CONTINUATION_EXIT_PHRASE in assistant_response\n                tool_calls = assistant_message.get('tool_calls', [])\n            else:\n                # Handle unexpected dictionary response\n                console.print(Panel(\"Unexpected response format\", title=\"API Error\", style=\"bold red\"))\n                return \"I'm sorry, but there was an unexpected error in the model response.\", False\n        else:\n            # Handle unexpected non-dictionary response\n            console.print(Panel(\"Unexpected response type\", title=\"API Error\", style=\"bold red\"))\n            return \"I'm sorry, but there was an unexpected error in the model response.\", False\n    except Exception as e:\n        console.print(Panel(f\"API Error: {str(e)}\", title=\"API Error\", style=\"bold red\"))\n        return \"I'm sorry, there was an error communicating with the AI. Please try again.\", False\n\n    console.print(Panel(Markdown(assistant_response), title=\"Ollama's Response\", title_align=\"left\", border_style=\"blue\", expand=False))\n\n    if tool_calls:\n        console.print(Panel(\"Tool calls detected\", title=\"Tool Usage\", style=\"bold yellow\"))\n        console.print(Panel(json.dumps(tool_calls, indent=2), title=\"Tool Calls\", style=\"cyan\"))\n\n    # Display files in context\n    if file_contents:\n        files_in_context = \"\\n\".join(file_contents.keys())\n    else:\n        files_in_context = \"No files in context. Read, create, or edit files to add.\"\n    console.print(Panel(files_in_context, title=\"Files in Context\", title_align=\"left\", border_style=\"white\", expand=False))\n\n    for tool_call in tool_calls:\n        tool_name = tool_call['function']['name']\n        tool_arguments = tool_call['function']['arguments']\n        \n        # Check if tool_arguments is a string and parse it if necessary\n        if isinstance(tool_arguments, str):\n            try:\n                tool_input = json.loads(tool_arguments)\n            except json.JSONDecodeError:\n                tool_input = {\"error\": \"Failed to parse tool arguments\"}\n        else:\n            tool_input = tool_arguments\n\n        console.print(Panel(f\"Tool Used: {tool_name}\", style=\"green\"))\n        console.print(Panel(f\"Tool Input: {json.dumps(tool_input, indent=2)}\", style=\"green\"))\n\n        tool_result = await execute_tool(tool_call)\n        \n        if tool_result[\"is_error\"]:\n            console.print(Panel(tool_result[\"content\"], title=\"Tool Execution Error\", style=\"bold red\"))\n        else:\n            console.print(Panel(tool_result[\"content\"], title_align=\"left\", title=\"Tool Result\", style=\"green\"))\n\n        current_conversation.append({\n            \"role\": \"assistant\",\n            \"content\": None,\n            \"tool_calls\": [tool_call]\n        })\n\n        current_conversation.append({\n            \"role\": \"tool\",\n            \"content\": tool_result[\"content\"],\n            \"tool_call_id\": tool_call.get('id', 'unknown_id')  # Use 'unknown_id' if 'id' is not present\n        })\n\n        # Update the file_contents dictionary if applicable\n        if tool_name in ['create_file', 'edit_and_apply', 'read_file'] and not tool_result[\"is_error\"]:\n            if 'path' in tool_input:\n                file_path = tool_input['path']\n                if \"File contents updated in system prompt\" in tool_result[\"content\"] or \\\n                   \"File created and added to system prompt\" in tool_result[\"content\"] or \\\n                   \"has been read and stored in the system prompt\" in tool_result[\"content\"]:\n                    # The file_contents dictionary is already updated in the tool function\n                    pass\n\n        messages = filtered_conversation_history + current_conversation\n\n        try:\n            # Prepend the system message to the messages list\n            system_message = {\"role\": \"system\", \"content\": update_system_prompt(current_iteration, max_iterations)}\n            messages_with_system = [system_message] + messages\n            \n            tool_response = await client.chat(\n                model=TOOLCHECKERMODEL,\n                messages=messages_with_system,\n                tools=tools,\n                stream=False\n            )\n\n            if isinstance(tool_response, dict) and 'message' in tool_response:\n                tool_checker_response = tool_response['message'].get('content', '')\n                console.print(Panel(Markdown(tool_checker_response), title=\"Ollama's Response to Tool Result\",  title_align=\"left\", border_style=\"blue\", expand=False))\n                assistant_response += \"\\n\\n\" + tool_checker_response\n            else:\n                error_message = \"Unexpected tool response format\"\n                console.print(Panel(error_message, title=\"Error\", style=\"bold red\"))\n                assistant_response += f\"\\n\\n{error_message}\"\n        except Exception as e:\n            error_message = f\"Error in tool response: {str(e)}\"\n            console.print(Panel(error_message, title=\"Error\", style=\"bold red\"))\n            assistant_response += f\"\\n\\n{error_message}\"\n\n    if assistant_response:\n        current_conversation.append({\"role\": \"assistant\", \"content\": assistant_response})\n\n    conversation_history = messages + [{\"role\": \"assistant\", \"content\": assistant_response}]\n\n    return assistant_response, exit_continuation\n\ndef reset_code_editor_memory():\n    global code_editor_memory\n    code_editor_memory = []\n    console.print(Panel(\"Code editor memory has been reset.\", title=\"Reset\", style=\"bold green\"))\n\n\ndef reset_conversation():\n    global conversation_history, file_contents, code_editor_files\n    conversation_history = []\n    file_contents = {}\n    code_editor_files = set()\n    reset_code_editor_memory()\n    console.print(Panel(\"Conversation history, file contents, code editor memory, and code editor files have been reset.\", title=\"Reset\", style=\"bold green\"))\n\n\n\n\nasync def main():\n    global automode, conversation_history\n    console.print(Panel(\"Welcome to the Ollama Llama 3.1 Engineer Chat with Multi-Agent and Image Support!\", title=\"Welcome\", style=\"bold green\"))\n    console.print(\"Type 'exit' to end the conversation.\")\n    console.print(\"Type 'automode [number]' to enter Autonomous mode with a specific number of iterations.\")\n    console.print(\"Type 'reset' to clear the conversation history.\")\n    console.print(\"Type 'save chat' to save the conversation to a Markdown file.\")\n    console.print(\"While in automode, press Ctrl+C at any time to exit the automode to return to regular chat.\")\n\n    while True:\n        user_input = await get_user_input()\n\n        if user_input.lower() == 'exit':\n            console.print(Panel(\"Thank you for chatting. Goodbye!\", title_align=\"left\", title=\"Goodbye\", style=\"bold green\"))\n            break\n\n        if user_input.lower() == 'reset':\n            reset_conversation()\n            continue\n\n        if user_input.lower() == 'save chat':\n            filename = save_chat()\n            console.print(Panel(f\"Chat saved to {filename}\", title=\"Chat Saved\", style=\"bold green\"))\n            continue\n\n\n        if user_input.lower().startswith('automode'):\n            try:\n                parts = user_input.split()\n                if len(parts) > 1 and parts[1].isdigit():\n                    max_iterations = int(parts[1])\n                else:\n                    max_iterations = MAX_CONTINUATION_ITERATIONS\n\n                automode = True\n                console.print(Panel(f\"Entering automode with {max_iterations} iterations. Please provide the goal of the automode.\", title_align=\"left\", title=\"Automode\", style=\"bold yellow\"))\n                console.print(Panel(\"Press Ctrl+C at any time to exit the automode loop.\", style=\"bold yellow\"))\n                user_input = await get_user_input()\n\n                iteration_count = 0\n                try:\n                    while automode and iteration_count < max_iterations:\n                        response, exit_continuation = await chat_with_ollama(user_input, current_iteration=iteration_count+1, max_iterations=max_iterations)\n\n                        if exit_continuation or CONTINUATION_EXIT_PHRASE in response:\n                            console.print(Panel(\"Automode completed.\", title_align=\"left\", title=\"Automode\", style=\"green\"))\n                            automode = False\n                        else:\n                            console.print(Panel(f\"Continuation iteration {iteration_count + 1} completed. Press Ctrl+C to exit automode. \", title_align=\"left\", title=\"Automode\", style=\"yellow\"))\n                            user_input = \"Continue with the next step. Or STOP by saying 'AUTOMODE_COMPLETE' if you think you've achieved the results established in the original request.\"\n                        iteration_count += 1\n\n                        if iteration_count >= max_iterations:\n                            console.print(Panel(\"Max iterations reached. Exiting automode.\", title_align=\"left\", title=\"Automode\", style=\"bold red\"))\n                            automode = False\n                except KeyboardInterrupt:\n                    console.print(Panel(\"\\nAutomode interrupted by user. Exiting automode.\", title_align=\"left\", title=\"Automode\", style=\"bold red\"))\n                    automode = False\n                    if conversation_history and conversation_history[-1][\"role\"] == \"user\":\n                        conversation_history.append({\"role\": \"assistant\", \"content\": \"Automode interrupted. How can I assist you further?\"})\n            except KeyboardInterrupt:\n                console.print(Panel(\"\\nAutomode interrupted by user. Exiting automode.\", title_align=\"left\", title=\"Automode\", style=\"bold red\"))\n                automode = False\n                if conversation_history and conversation_history[-1][\"role\"] == \"user\":\n                    conversation_history.append({\"role\": \"assistant\", \"content\": \"Automode interrupted. How can I assist you further?\"})\n\n            console.print(Panel(\"Exited automode. Returning to regular chat.\", style=\"green\"))\n        else:\n            response, _ = await chat_with_ollama(user_input)\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n"
  },
  {
    "path": "Claude-Eng-v2/readme.md",
    "content": "# 🤖 Claude Engineer\n\nClaude Engineer is an advanced interactive command-line interface (CLI) that harnesses the power of Anthropic's Claude 3 and Claude 3.5 models to assist with a wide range of software development tasks. This tool seamlessly combines the capabilities of state-of-the-art large language models with practical file system operations, web search functionality, intelligent code analysis, and execution capabilities.\n\n## NEW\n\nTTS using 11labs WebSockets and audio streaming.\nType\n```\n11labs on\n```\nto use TTS and 11labs off to return to regualr mode.\n\nVoice mode 🗣️: Now you can talk to the Engineer directly without even touching your keyboard.\n\nType\n```\nvoice\n```\nto enter voice mode.\n\nSay \"exit voice mode\" to return to regular text.\n\nIf you want to use your voice and 11 labs at the same time, first activate 11labs then type voice to use your voice. \n\nPrompt caching. Make sure you udpate your Anthropic python package before running the script.\n```\npip install --upgrade anthropic\n```\n\n## ✨ Features\n\n- 💬 Interactive chat interface with Claude 3 and Claude 3.5 models\n- 📁 Comprehensive file system operations (create folders, files, read/write files)\n- 🔍 Web search capabilities using Tavily API for up-to-date information\n- 🌈 Enhanced syntax highlighting for code snippets\n- 🏗️ Intelligent project structure creation and management\n- 🧐 Advanced code analysis and improvement suggestions\n- 🖼️ Image analysis capabilities with support for drag and drop in the terminal\n- 🚀 Improved automode for efficient autonomous task completion\n- 🔄 Robust iteration tracking and management in automode\n- 📊 Precise diff-based file editing for controlled code modifications\n- 🛡️ Enhanced error handling and detailed output for tool usage\n- 🎨 Color-coded terminal output using Rich library for improved readability\n- 🔧 Detailed logging of tool usage and results\n- 🔁 Improved file editing workflow with separate read and apply steps\n- 🧠 Dynamic system prompt updates based on automode status\n- 🔍 TOOLCHECKERMODEL for validating tool usage and outputs\n- 📝 CODEEDITORMODEL for specialized code editing tasks\n- 🖥️ CODEEXECUTIONMODEL for analyzing code execution results\n- 📊 Token usage tracking (input, output, and total) for each model, with improved visualization using tables\n- 🪟 Remaining context window display\n- 💾 Chat log saving capability\n- 🔒 Enhanced code execution capabilities with isolated virtual environment\n- 🔄 Process management for long-running code executions\n- 📚 Multi-file reading capability for efficient handling of multiple files simultaneously\n\n## 🛠️ Installation\n\n1. Clone this repository:\n   ```\n   git clone https://github.com/doriandarko/claude-engineer.git\n   cd claude-engineer\n   ```\n\n2. Install the required dependencies:\n   ```\n   pip install -r requirements.txt\n   ```\n\n3. Set up your environment variables:\n   - Create a `.env` file in the project root directory\n   - Add the following environment variables:\n     ```\n     ANTHROPIC_API_KEY=your_anthropic_api_key\n     TAVILY_API_KEY=your_tavily_api_key\n     ```\n\n4. Set up the virtual environment for code execution:\n   Engineer will create a virtual environment to run code the first time it executes a piece of code.\n   This is just for you if you want to run the main script in a virtual environment rather than in your default one.\n   ```\n   python -m venv code_execution_env\n   source code_execution_env/bin/activate  # On Windows, use: code_execution_env\\Scripts\\activate\n   pip install -r requirements.txt\n   deactivate\n   ```\n\n## 🔧 Virtual Environment Setup\n\nClaude Engineer uses a dedicated virtual environment for code execution to ensure isolation and security. The virtual environment is automatically created the first time you run a piece of code. However, if you want to set it up manually or customize it, follow these steps:\n\n1. Create the virtual environment:\n   ```\n   python -m venv code_execution_env\n   ```\n\n2. Activate the virtual environment:\n   - On Windows:\n     ```\n     code_execution_env\\Scripts\\activate\n     ```\n   - On macOS and Linux:\n     ```\n     source code_execution_env/bin/activate\n     ```\n\n3. Install the required dependencies:\n   ```\n   pip install -r requirements.txt\n   ```\n\n4. Deactivate the virtual environment when you're done:\n   ```\n   deactivate\n   ```\n\nThe code_execution_env virtual environment will be used for all code execution tasks, ensuring a consistent and isolated environment for running user code.\n\n## 🚀 Usage\n\nRun the main script to start the Claude Engineer interface:\n\n```\npython main.py\n```\n\nOnce started, you can interact with Claude Engineer by typing your queries or commands. Some example interactions:\n\n- \"Create a new Python project structure for a web application\"\n- \"Explain the code in file.py and suggest improvements\"\n- \"Search for the latest best practices in React development\"\n- \"Help me debug this error: [paste your error message]\"\n- \"Analyze this image and describe its contents\"\n- \"Execute this Python code and analyze the results\"\n- \"Read multiple files: file1.py, file2.py, file3.py\"\n\nSpecial commands:\n- Type 'exit' to end the conversation and close the application.\n- Type 'image' to include an image in your message for analysis.\n- Type 'reset' to reset the entire conversation without restarting the script.\n- Type 'automode number' to enter Autonomous mode with a specific number of iterations.\n- Type 'save chat' to save the current chat log.\n- Press Ctrl+C at any time to exit the automode and return to regular chat.\n\nAfter each interaction, Claude Engineer will display:\n- Token usage (input, output, and total) for the current model\n- Remaining context window size\n\n### Code Execution and Process Management\n\nClaude Engineer now supports executing code in an isolated 'code_execution_env' virtual environment:\n\n1. Use the `execute_code` tool to run Python code safely in the isolated environment.\n2. Long-running processes can be managed using the process ID returned by `execute_code`.\n3. The CODEEXECUTIONMODEL analyzes execution results and provides insights.\n\n### Using Different AI Models\n\nClaude Engineer utilizes multiple specialized AI models:\n\n- MAINMODEL: Claude 3 or Claude 3.5 for general interactions\n- TOOLCHECKERMODEL: Validates tool usage and outputs\n- CODEEDITORMODEL: Performs specialized code editing tasks\n- CODEEXECUTIONMODEL: Analyzes code execution results\n\nThe script automatically selects the appropriate model based on the task.\n\n### 🤖 Improved Automode\n\nThe enhanced automode allows Claude to work autonomously on complex tasks with greater efficiency and control. When in automode:\n\n1. Claude sets clear, achievable goals based on your request.\n2. It works through these goals one by one, using available tools as needed.\n3. Claude provides regular updates on its progress, including the current iteration count.\n4. Automode continues until goals are completed or the maximum number of iterations is reached.\n5. You can specify the maximum number of iterations when entering automode (default is 25).\n6. Claude dynamically adjusts its approach based on progress and obstacles encountered.\n7. The TOOLCHECKERMODEL validates tool usage and outputs for increased reliability.\n\nTo use automode:\n1. Type 'automode number' when prompted for input, where number is the maximum number of iterations.\n2. Provide your request when prompted.\n3. Claude will work autonomously, providing updates after each iteration.\n4. Automode exits when the task is completed, after reaching the maximum number of iterations, or when you press Ctrl+C.\n\n### 📊 Enhanced Diff-based File Editing\n\nClaude Engineer now supports an improved diff-based file editing system, allowing for more precise and controlled modifications to existing files. The new workflow includes:\n\n1. Reading the entire content of a file using the `edit_and_apply` function without providing new content.\n2. Applying changes to the file using the `edit_and_apply` function with new content, which shows a detailed diff of the proposed changes.\n3. Utilizing the CODEEDITORMODEL for specialized code editing tasks, ensuring high-quality modifications.\n\nWhen editing files, Claude will:\n\n1. Show a detailed diff of the proposed changes, highlighting additions, removals, and unchanged lines with color coding using the Rich library.\n2. Focus on adding new code or modifying existing code without unnecessarily removing functionality.\n3. Provide a summary of lines added and removed.\n4. Apply changes carefully to avoid duplicates and unwanted replacements.\n5. Support various editing scenarios, including targeted changes, appending content, inserting at the beginning, and replacing entire file contents.\n6. Use the CODEEDITORMODEL to ensure code changes adhere to best practices and maintain consistency.\n\nThis feature enhances Claude's ability to make targeted improvements to your codebase while maintaining the integrity of existing functionality.\n\n### 🧠 Dynamic System Prompt\n\nThe system prompt is now dynamically updated based on whether the script is in automode or not. This allows for more tailored instructions and behavior depending on the current operating mode:\n\n1. In regular mode, Claude focuses on providing helpful responses and using tools as needed.\n2. In automode, Claude is instructed to work autonomously, set goals, and provide regular updates on progress.\n3. The system prompt adapts to the specific task at hand, optimizing Claude's performance for each scenario.\n4. The system prompt now includes file context for enhanced token management.\n\nThe dynamic system prompt enhances Claude's ability to adapt to different scenarios and provide more relevant assistance.\n\n### 📊 Token Management and Visualization\n\nClaude Engineer now features improved token management and visualization:\n\n1. Enhanced token management using file context in the system prompt.\n2. Improved token visualization using a table format.\n3. Display of input, output, and total token usage for each model interaction.\n4. Visualization of remaining context window size.\n\nThese improvements provide better insights into token usage and help manage conversations more effectively.\n\n### 🔧 Available Tools\n\nClaude Engineer comes with a set of powerful tools to assist with various tasks:\n\n1. create_folder: Create a new folder at a specified path.\n2. create_file: Create a new file at a specified path with content.\n3. edit_and_apply: Read the contents of a file, and optionally apply changes.\n4. read_file: Read the contents of a file at the specified path.\n5. read_multiple_files: Read the contents of multiple files at specified paths.\n6. list_files: List all files and directories in the specified folder.\n7. tavily_search: Perform a web search using Tavily API to get up-to-date information.\n8. execute_code: Run Python code in an isolated virtual environment.\n9. stop_process: Manage and stop long-running code executions.\n10. TOOLCHECKERMODEL: Validate tool usage and outputs for increased reliability.\n11. CODEEDITORMODEL: Perform specialized code editing tasks with high precision.\n12. CODEEXECUTIONMODEL: Analyze code execution results and provide insights.\n\nThese tools allow Claude to interact with the file system, manage project structures, gather information from the web, perform advanced code editing, and execute code safely.\n\n### 🖼️ Image Analysis\n\nClaude Engineer now supports image analysis capabilities. To use this feature:\n\n1. Type 'image' when prompted for input.\n2. Drag and drop your image file into the terminal or provide the file path.\n3. Provide a prompt or question about the image.\n4. Claude will analyze the image and respond to your query.\n\nThis feature enables Claude to assist with tasks involving visual data, such as analyzing diagrams, screenshots, or any other images relevant to your development work.\n\n### 🛡️ Error Handling and Recovery\n\nClaude Engineer implements robust error handling and recovery mechanisms:\n\n1. Graceful handling of API errors and network issues.\n2. Automatic retries for transient failures.\n3. Clear error messages and suggestions for user action when needed.\n4. Logging of errors for debugging and improvement purposes.\n5. Ability to recover and continue operation after non-critical errors.\n6. Safe termination of long-running processes when needed.\n\nThese features ensure a smooth and reliable user experience, even in the face of unexpected issues or complex code executions.\n\n### 💾 Chat Log Saving\n\nYou can save the current chat log at any time during your interaction with Claude Engineer:\n\n1. Type 'save' when prompted for input.\n2. The chat log will be saved to a file in the current directory with a timestamp in the filename.\n3. You can review these logs later for reference or to continue previous conversations.\n\n## 🧠 AI Models and Specialized Agents\n\nClaude Engineer utilizes multiple AI models to provide specialized functionality:\n\n1. MAINMODEL (Claude 3 or Claude 3.5): Handles general interactions and task processing.\n2. TOOLCHECKERMODEL: Validates the usage and outputs of various tools to ensure reliability.\n3. CODEEDITORMODEL: Specializes in code editing tasks, ensuring high-quality modifications.\n4. CODEEXECUTIONMODEL: Analyzes code execution results and provides insights.\n\nThese models work together to provide a comprehensive and intelligent development assistance experience.\n\n## Workflow Diagram\n\n```mermaid\ngraph TD\n    A[Start] --> B[Initialize]\n    B --> C{User Input}\n    \n    C -->|\"exit\"| D[End]\n    C -->|\"reset\"| E[Reset Conversation]\n    C -->|\"save chat\"| F[Save Chat to Markdown]\n    C -->|\"image\"| G[Process Image]\n    C -->|\"automode\"| H[Enter Automode]\n    C -->|Other| I[Regular Chat]\n    \n    E --> C\n    F --> C\n    G --> J[chat_with_claude]\n    H --> K[Automode Loop]\n    I --> J\n    \n    J --> L{Tool Use?}\n    L -->|Yes| M[Execute Tool]\n    L -->|No| N[Generate Response]\n    \n    M --> O[Tool Checker]\n    O --> N\n    \n    N --> P[Update Conversation History]\n    P --> Q[Display Token Usage]\n    Q --> C\n    \n    subgraph Memory Management\n        R[Conversation History]\n        S[File Contents]\n        T[Code Editor Memory]\n    end\n    \n    subgraph Models\n        U[MAINMODEL - Claude-3.5-Sonnet]\n        V[TOOLCHECKERMODEL - Claude-3.5-Sonnet]\n        W[CODEEDITORMODEL - Claude-3.5-Sonnet]\n        X[CODEEXECUTIONMODEL - Claude-3-Haiku]\n    end\n    \n    subgraph Tools\n        Y[create_folder]\n        Z[create_file]\n        AA[edit_and_apply]\n        AB[execute_code]\n        AC[stop_process]\n        AD[read_file]\n        AE[list_files]\n        AF[tavily_search]\n        AG[read_multiple_files]\n    end\n    \n    J --> R\n    J --> S\n    J --> T\n    J --> U\n    O --> V\n    AA --> W\n    AB --> X\n    M --> Y\n    M --> Z\n    M --> AA\n    M --> AB\n    M --> AC\n    M --> AD\n    M --> AE\n    M --> AF\n    M --> AG\n```\n\n\n## 👥 Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.\n\n## 🦙 Ollama eng is here\n\nYou can now have the power of this script, completely locally using Ollama and any of the supported function calling models:\nLlama 3.1\nMistral Nemo\nFirefunction v2\nCommand-R +\n\nBefore running make sure you install the latest version of the Ollama app and \n\n```\npip install ollama\n```\n\nThen\n\n```\npython ollama-eng.py\n```\n\n### 🚨Important note on safety when using Ollama Engineer!\n\nBe extra careful if you ever let these local models run code on your machine, especially using the executing code tool. It may brick your machine. I disabled the tool execution completely for OLLAMA engineer but if you want to implement it again based on the original script use at your own discretion.\n\n## Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=Doriandarko/claude-engineer&type=Date)](https://star-history.com/#Doriandarko/claude-engineer&Date)\n"
  },
  {
    "path": "Claude-Eng-v2/requirements.txt",
    "content": "anthropic\npython-dotenv\ntavily-python\nPillow\nanthropic\nrich\nprompt_toolkit\npydub\nwebsockets\nSpeechRecognition\n"
  },
  {
    "path": "app.py",
    "content": "from flask import Flask, render_template, request, jsonify, url_for\nfrom ce3 import Assistant\nimport os\nfrom werkzeug.utils import secure_filename\nimport base64\nfrom config import Config\n\napp = Flask(__name__, static_folder='static')\napp.config['UPLOAD_FOLDER'] = 'uploads'\napp.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024  # 16MB max file size\n\n# Ensure upload directory exists\nos.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)\n\n# Initialize the assistant\nassistant = Assistant()\n\n@app.route('/')\ndef home():\n    return render_template('index.html')\n\n@app.route('/chat', methods=['POST'])\ndef chat():\n    data = request.json\n    message = data.get('message', '')\n    image_data = data.get('image')  # Get the base64 image data\n    \n    # Prepare the message content\n    if image_data:\n        # Create a message with both text and image in correct order\n        message_content = [\n            {\n                \"type\": \"image\",\n                \"source\": {\n                    \"type\": \"base64\",\n                    \"media_type\": \"image/jpeg\",  # We should detect this from the image\n                    \"data\": image_data.split(',')[1] if ',' in image_data else image_data  # Remove data URL prefix if present\n                }\n            }\n        ]\n        \n        # Only add text message if there is actual text\n        if message.strip():\n            message_content.append({\n                \"type\": \"text\",\n                \"text\": message\n            })\n    else:\n        # Text-only message\n        message_content = message\n    \n    try:\n        # Handle the chat message with the appropriate content\n        response = assistant.chat(message_content)\n        \n        # Get token usage from assistant\n        token_usage = {\n            'total_tokens': assistant.total_tokens_used,\n            'max_tokens': Config.MAX_CONVERSATION_TOKENS\n        }\n        \n        # Get the last used tool from the conversation history\n        tool_name = None\n        if assistant.conversation_history:\n            for msg in reversed(assistant.conversation_history):\n                if msg.get('role') == 'assistant' and msg.get('content'):\n                    content = msg['content']\n                    if isinstance(content, list):\n                        for block in content:\n                            if isinstance(block, dict) and block.get('type') == 'tool_use':\n                                tool_name = block.get('name')\n                                break\n                    if tool_name:\n                        break\n        \n        return jsonify({\n            'response': response,\n            'thinking': False,\n            'tool_name': tool_name,\n            'token_usage': token_usage\n        })\n        \n    except Exception as e:\n        return jsonify({\n            'response': f\"Error: {str(e)}\",\n            'thinking': False,\n            'tool_name': None,\n            'token_usage': None\n        }), 200  # Return 200 even for errors to handle them gracefully in frontend\n\n@app.route('/upload', methods=['POST'])\ndef upload_file():\n    if 'file' not in request.files:\n        return jsonify({'error': 'No file part'}), 400\n    \n    file = request.files['file']\n    if file.filename == '':\n        return jsonify({'error': 'No selected file'}), 400\n    \n    if file and file.filename.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.webp')):\n        filename = secure_filename(file.filename)\n        filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)\n        file.save(filepath)\n        \n        # Get the actual media type\n        media_type = file.content_type or 'image/jpeg'  # Default to jpeg if not detected\n        \n        # Convert image to base64\n        with open(filepath, \"rb\") as image_file:\n            encoded_string = base64.b64encode(image_file.read()).decode('utf-8')\n        \n        # Clean up the file\n        os.remove(filepath)\n        \n        return jsonify({\n            'success': True,\n            'image_data': encoded_string,\n            'media_type': media_type\n        })\n    \n    return jsonify({'error': 'Invalid file type'}), 400\n\n@app.route('/reset', methods=['POST'])\ndef reset():\n    # Reset the assistant's conversation history\n    assistant.reset()\n    return jsonify({'status': 'success'})\n\nif __name__ == '__main__':\n    app.run(debug=False) "
  },
  {
    "path": "ce3.py",
    "content": "# ce3.py\nimport anthropic\nfrom rich.console import Console\nfrom rich.markdown import Markdown\nfrom rich.live import Live\nfrom rich.spinner import Spinner\nfrom rich.panel import Panel\nfrom typing import List, Dict, Any\nimport importlib\nimport inspect\nimport pkgutil\nimport os\nimport json\nimport sys\nimport logging\n\nfrom config import Config\nfrom tools.base import BaseTool\nfrom prompt_toolkit import prompt\nfrom prompt_toolkit.styles import Style\nfrom prompts.system_prompts import SystemPrompts\n\n# Configure logging to only show ERROR level and above\nlogging.basicConfig(\n    level=logging.ERROR,\n    format='%(levelname)s: %(message)s'\n)\n\nclass Assistant:\n    \"\"\"\n    The Assistant class manages:\n    - Loading of tools from a specified directory.\n    - Interaction with the Anthropics API (message completion).\n    - Handling user commands such as 'refresh' and 'reset'.\n    - Token usage tracking and display.\n    - Tool execution upon request from model responses.\n    \"\"\"\n\n    def __init__(self):\n        if not getattr(Config, 'ANTHROPIC_API_KEY', None):\n            raise ValueError(\"No ANTHROPIC_API_KEY found in environment variables\")\n\n        # Initialize Anthropics client\n        self.client = anthropic.Anthropic(api_key=Config.ANTHROPIC_API_KEY)\n\n        self.conversation_history: List[Dict[str, Any]] = []\n        self.console = Console()\n\n        self.thinking_enabled = getattr(Config, 'ENABLE_THINKING', False)\n        self.temperature = getattr(Config, 'DEFAULT_TEMPERATURE', 0.7)\n        self.total_tokens_used = 0\n\n        self.tools = self._load_tools()\n\n    def _execute_uv_install(self, package_name: str) -> bool:\n        \"\"\"\n        Execute the uvpackagemanager tool directly to install the missing package.\n        Returns True if installation seems successful (no errors in output), otherwise False.\n        \"\"\"\n        class ToolUseMock:\n            name = \"uvpackagemanager\"\n            input = {\n                \"command\": \"install\",\n                \"packages\": [package_name]\n            }\n\n        result = self._execute_tool(ToolUseMock())\n        if \"Error\" not in result and \"failed\" not in result.lower():\n            self.console.print(\"[green]The package was installed successfully.[/green]\")\n            return True\n        else:\n            self.console.print(f\"[red]Failed to install {package_name}. Output:[/red] {result}\")\n            return False\n\n    def _load_tools(self) -> List[Dict[str, Any]]:\n        \"\"\"\n        Dynamically load all tool classes from the tools directory.\n        If a dependency is missing, prompt the user to install it via uvpackagemanager.\n        \n        Returns:\n            A list of tools (dicts) containing their 'name', 'description', and 'input_schema'.\n        \"\"\"\n        tools = []\n        tools_path = getattr(Config, 'TOOLS_DIR', None)\n\n        if tools_path is None:\n            self.console.print(\"[red]TOOLS_DIR not set in Config[/red]\")\n            return tools\n\n        # Clear cached tool modules for fresh import\n        for module_name in list(sys.modules.keys()):\n            if module_name.startswith('tools.') and module_name != 'tools.base':\n                del sys.modules[module_name]\n\n        try:\n            for module_info in pkgutil.iter_modules([str(tools_path)]):\n                if module_info.name == 'base':\n                    continue\n\n                # Attempt loading the tool module\n                try:\n                    module = importlib.import_module(f'tools.{module_info.name}')\n                    self._extract_tools_from_module(module, tools)\n                except ImportError as e:\n                    # Handle missing dependencies\n                    missing_module = self._parse_missing_dependency(str(e))\n                    self.console.print(f\"\\n[yellow]Missing dependency:[/yellow] {missing_module} for tool {module_info.name}\")\n                    user_response = input(f\"Would you like to install {missing_module}? (y/n): \").lower()\n\n                    if user_response == 'y':\n                        success = self._execute_uv_install(missing_module)\n                        if success:\n                            # Retry loading the module after installation\n                            try:\n                                module = importlib.import_module(f'tools.{module_info.name}')\n                                self._extract_tools_from_module(module, tools)\n                            except Exception as retry_err:\n                                self.console.print(f\"[red]Failed to load tool after installation: {str(retry_err)}[/red]\")\n                        else:\n                            self.console.print(f\"[red]Installation of {missing_module} failed. Skipping this tool.[/red]\")\n                    else:\n                        self.console.print(f\"[yellow]Skipping tool {module_info.name} due to missing dependency[/yellow]\")\n                except Exception as mod_err:\n                    self.console.print(f\"[red]Error loading module {module_info.name}:[/red] {str(mod_err)}\")\n        except Exception as overall_err:\n            self.console.print(f\"[red]Error in tool loading process:[/red] {str(overall_err)}\")\n\n        return tools\n\n    def _parse_missing_dependency(self, error_str: str) -> str:\n        \"\"\"\n        Parse the missing dependency name from an ImportError string.\n        \"\"\"\n        if \"No module named\" in error_str:\n            parts = error_str.split(\"No module named\")\n            missing_module = parts[-1].strip(\" '\\\"\")\n        else:\n            missing_module = error_str\n        return missing_module\n\n    def _extract_tools_from_module(self, module, tools: List[Dict[str, Any]]) -> None:\n        \"\"\"\n        Given a tool module, find and instantiate all tool classes (subclasses of BaseTool).\n        Append them to the 'tools' list.\n        \"\"\"\n        for name, obj in inspect.getmembers(module):\n            if (inspect.isclass(obj) and issubclass(obj, BaseTool) and obj != BaseTool):\n                try:\n                    tool_instance = obj()\n                    tools.append({\n                        \"name\": tool_instance.name,\n                        \"description\": tool_instance.description,\n                        \"input_schema\": tool_instance.input_schema\n                    })\n                    self.console.print(f\"[green]Loaded tool:[/green] {tool_instance.name}\")\n                except Exception as tool_init_err:\n                    self.console.print(f\"[red]Error initializing tool {name}:[/red] {str(tool_init_err)}\")\n\n    def refresh_tools(self):\n        \"\"\"\n        Refresh the list of tools and show newly discovered tools.\n        \"\"\"\n        current_tool_names = {tool['name'] for tool in self.tools}\n        self.tools = self._load_tools()\n        new_tool_names = {tool['name'] for tool in self.tools}\n        new_tools = new_tool_names - current_tool_names\n\n        if new_tools:\n            self.console.print(\"\\n\")\n            for tool_name in new_tools:\n                tool_info = next((t for t in self.tools if t['name'] == tool_name), None)\n                if tool_info:\n                    description_lines = tool_info['description'].strip().split('\\n')\n                    formatted_description = '\\n    '.join(line.strip() for line in description_lines)\n                    self.console.print(f\"[bold green]NEW[/bold green] 🔧 [cyan]{tool_name}[/cyan]:\\n    {formatted_description}\")\n        else:\n            self.console.print(\"\\n[yellow]No new tools found[/yellow]\")\n\n    def display_available_tools(self):\n        \"\"\"\n        Print a list of currently loaded tools.\n        \"\"\"\n        self.console.print(\"\\n[bold cyan]Available tools:[/bold cyan]\")\n        tool_names = [tool['name'] for tool in self.tools]\n        if tool_names:\n            formatted_tools = \", \".join([f\"🔧 [cyan]{name}[/cyan]\" for name in tool_names])\n        else:\n            formatted_tools = \"No tools available.\"\n        self.console.print(formatted_tools)\n        self.console.print(\"\\n---\")\n\n    def _display_tool_usage(self, tool_name: str, input_data: Dict, result: str):\n        \"\"\"\n        If SHOW_TOOL_USAGE is enabled, display the input and result of a tool execution.\n        Handles special cases like image data and large outputs for cleaner display.\n        \"\"\"\n        if not getattr(Config, 'SHOW_TOOL_USAGE', False):\n            return\n\n        # Clean up input data by removing any large binary/base64 content\n        cleaned_input = self._clean_data_for_display(input_data)\n        \n        # Clean up result data\n        cleaned_result = self._clean_data_for_display(result)\n\n        tool_info = f\"\"\"[cyan]📥 Input:[/cyan] {json.dumps(cleaned_input, indent=2)}\n[cyan]📤 Result:[/cyan] {cleaned_result}\"\"\"\n        \n        panel = Panel(\n            tool_info,\n            title=f\"Tool used: {tool_name}\",\n            title_align=\"left\",\n            border_style=\"cyan\",\n            padding=(1, 2)\n        )\n        self.console.print(panel)\n\n    def _clean_data_for_display(self, data):\n        \"\"\"\n        Helper method to clean data for display by handling various data types\n        and removing/replacing large content like base64 strings.\n        \"\"\"\n        if isinstance(data, str):\n            try:\n                # Try to parse as JSON first\n                parsed_data = json.loads(data)\n                return self._clean_parsed_data(parsed_data)\n            except json.JSONDecodeError:\n                # If it's a long string, check for base64 patterns\n                if len(data) > 1000 and ';base64,' in data:\n                    return \"[base64 data omitted]\"\n                return data\n        elif isinstance(data, dict):\n            return self._clean_parsed_data(data)\n        else:\n            return data\n\n    def _clean_parsed_data(self, data):\n        \"\"\"\n        Recursively clean parsed JSON/dict data, handling nested structures\n        and replacing large data with placeholders.\n        \"\"\"\n        if isinstance(data, dict):\n            cleaned = {}\n            for key, value in data.items():\n                # Handle image data in various formats\n                if key in ['data', 'image', 'source'] and isinstance(value, str):\n                    if len(value) > 1000 and (';base64,' in value or value.startswith('data:')):\n                        cleaned[key] = \"[base64 data omitted]\"\n                    else:\n                        cleaned[key] = value\n                else:\n                    cleaned[key] = self._clean_parsed_data(value)\n            return cleaned\n        elif isinstance(data, list):\n            return [self._clean_parsed_data(item) for item in data]\n        elif isinstance(data, str) and len(data) > 1000 and ';base64,' in data:\n            return \"[base64 data omitted]\"\n        return data\n\n    def _execute_tool(self, tool_use):\n        \"\"\"\n        Given a tool usage request (with tool name and inputs),\n        dynamically load and execute the corresponding tool.\n        \"\"\"\n        tool_name = tool_use.name\n        tool_input = tool_use.input or {}\n        tool_result = None\n\n        try:\n            module = importlib.import_module(f'tools.{tool_name}')\n            tool_instance = self._find_tool_instance_in_module(module, tool_name)\n\n            if not tool_instance:\n                tool_result = f\"Tool not found: {tool_name}\"\n            else:\n                # Execute the tool with the provided input\n                try:\n                    result = tool_instance.execute(**tool_input)\n                    # Keep structured data intact\n                    tool_result = result\n                except Exception as exec_err:\n                    tool_result = f\"Error executing tool '{tool_name}': {str(exec_err)}\"\n        except ImportError:\n            tool_result = f\"Failed to import tool: {tool_name}\"\n        except Exception as e:\n            tool_result = f\"Error executing tool: {str(e)}\"\n\n        # Display tool usage with proper handling of structured data\n        self._display_tool_usage(tool_name, tool_input, \n            json.dumps(tool_result) if not isinstance(tool_result, str) else tool_result)\n        return tool_result\n\n    def _find_tool_instance_in_module(self, module, tool_name: str):\n        \"\"\"\n        Search a given module for a tool class matching tool_name and return an instance of it.\n        \"\"\"\n        for name, obj in inspect.getmembers(module):\n            if (inspect.isclass(obj) and issubclass(obj, BaseTool) and obj != BaseTool):\n                candidate_tool = obj()\n                if candidate_tool.name == tool_name:\n                    return candidate_tool\n        return None\n\n    def _display_token_usage(self, usage):\n        \"\"\"\n        Display a visual representation of token usage and remaining tokens.\n        Uses only the tracked total_tokens_used.\n        \"\"\"\n        used_percentage = (self.total_tokens_used / Config.MAX_CONVERSATION_TOKENS) * 100\n        remaining_tokens = max(0, Config.MAX_CONVERSATION_TOKENS - self.total_tokens_used)\n\n        self.console.print(f\"\\nTotal used: {self.total_tokens_used:,} / {Config.MAX_CONVERSATION_TOKENS:,}\")\n\n        bar_width = 40\n        filled = int(used_percentage / 100 * bar_width)\n        bar = \"█\" * filled + \"░\" * (bar_width - filled)\n\n        color = \"green\"\n        if used_percentage > 75:\n            color = \"yellow\"\n        if used_percentage > 90:\n            color = \"red\"\n\n        self.console.print(f\"[{color}][{bar}] {used_percentage:.1f}%[/{color}]\")\n\n        if remaining_tokens < 20000:\n            self.console.print(f\"[bold red]Warning: Only {remaining_tokens:,} tokens remaining![/bold red]\")\n\n        self.console.print(\"---\")\n\n    def _get_completion(self):\n        \"\"\"\n        Get a completion from the Anthropic API.\n        Handles both text-only and multimodal messages.\n        \"\"\"\n        try:\n            response = self.client.messages.create(\n                model=Config.MODEL,\n                max_tokens=min(\n                    Config.MAX_TOKENS,\n                    Config.MAX_CONVERSATION_TOKENS - self.total_tokens_used\n                ),\n                temperature=self.temperature,\n                tools=self.tools,\n                messages=self.conversation_history,\n                system=f\"{SystemPrompts.DEFAULT}\\n\\n{SystemPrompts.TOOL_USAGE}\"\n            )\n\n            # Update token usage based on response usage\n            if hasattr(response, 'usage') and response.usage:\n                message_tokens = response.usage.input_tokens + response.usage.output_tokens\n                self.total_tokens_used += message_tokens\n                self._display_token_usage(response.usage)\n\n            if self.total_tokens_used >= Config.MAX_CONVERSATION_TOKENS:\n                self.console.print(\"\\n[bold red]Token limit reached! Please reset the conversation.[/bold red]\")\n                return \"Token limit reached! Please type 'reset' to start a new conversation.\"\n\n            if response.stop_reason == \"tool_use\":\n                self.console.print(\"\\n[bold yellow]  Handling Tool Use...[/bold yellow]\\n\")\n\n                tool_results = []\n                if getattr(response, 'content', None) and isinstance(response.content, list):\n                    # Execute each tool in the response content\n                    for content_block in response.content:\n                        if content_block.type == \"tool_use\":\n                            result = self._execute_tool(content_block)\n                            \n                            # Handle structured data (like image blocks) vs text\n                            if isinstance(result, (list, dict)):\n                                tool_results.append({\n                                    \"type\": \"tool_result\",\n                                    \"tool_use_id\": content_block.id,\n                                    \"content\": result  # Keep structured data intact\n                                })\n                            else:\n                                # Convert text results to proper content blocks\n                                tool_results.append({\n                                    \"type\": \"tool_result\",\n                                    \"tool_use_id\": content_block.id,\n                                    \"content\": [{\"type\": \"text\", \"text\": str(result)}]\n                                })\n\n                    # Append tool usage to conversation and continue\n                    self.conversation_history.append({\n                        \"role\": \"assistant\",\n                        \"content\": response.content\n                    })\n                    self.conversation_history.append({\n                        \"role\": \"user\",\n                        \"content\": tool_results\n                    })\n                    return self._get_completion()  # Recursive call to continue the conversation\n\n                else:\n                    self.console.print(\"[red]No tool content received despite 'tool_use' stop reason.[/red]\")\n                    return \"Error: No tool content received\"\n\n            # Final assistant response\n            if (getattr(response, 'content', None) and \n                isinstance(response.content, list) and \n                response.content):\n                final_content = response.content[0].text\n                self.conversation_history.append({\n                    \"role\": \"assistant\",\n                    \"content\": response.content\n                })\n                return final_content\n            else:\n                self.console.print(\"[red]No content in final response.[/red]\")\n                return \"No response content available.\"\n\n        except Exception as e:\n            logging.error(f\"Error in _get_completion: {str(e)}\")\n            return f\"Error: {str(e)}\"\n\n    def chat(self, user_input):\n        \"\"\"\n        Process a chat message from the user.\n        user_input can be either a string (text-only) or a list (multimodal message)\n        \"\"\"\n        # Handle special commands only for text-only messages\n        if isinstance(user_input, str):\n            if user_input.lower() == 'refresh':\n                self.refresh_tools()\n                return \"Tools refreshed successfully!\"\n            elif user_input.lower() == 'reset':\n                self.reset()\n                return \"Conversation reset!\"\n            elif user_input.lower() == 'quit':\n                return \"Goodbye!\"\n\n        try:\n            # Add user message to conversation history\n            self.conversation_history.append({\n                \"role\": \"user\",\n                \"content\": user_input  # This can be either string or list\n            })\n\n            # Show thinking indicator if enabled\n            if self.thinking_enabled:\n                with Live(Spinner('dots', text='Thinking...', style=\"cyan\"), \n                         refresh_per_second=10, transient=True):\n                    response = self._get_completion()\n            else:\n                response = self._get_completion()\n\n            return response\n\n        except Exception as e:\n            logging.error(f\"Error in chat: {str(e)}\")\n            return f\"Error: {str(e)}\"\n\n    def reset(self):\n        \"\"\"\n        Reset the assistant's memory and token usage.\n        \"\"\"\n        self.conversation_history = []\n        self.total_tokens_used = 0\n        self.console.print(\"\\n[bold green]🔄 Assistant memory has been reset![/bold green]\")\n\n        welcome_text = \"\"\"\n# Claude Engineer v3. A self-improving assistant framework with tool creation\n\nType 'refresh' to reload available tools\nType 'reset' to clear conversation history\nType 'quit' to exit\n\nAvailable tools:\n\"\"\"\n        self.console.print(Markdown(welcome_text))\n        self.display_available_tools()\n\n\ndef main():\n    \"\"\"\n    Entry point for the assistant CLI loop.\n    Provides a prompt for user input and handles 'quit' and 'reset' commands.\n    \"\"\"\n    console = Console()\n    style = Style.from_dict({'prompt': 'orange'})\n\n    try:\n        assistant = Assistant()\n    except ValueError as e:\n        console.print(f\"[bold red]Error:[/bold red] {str(e)}\")\n        console.print(\"Please ensure ANTHROPIC_API_KEY is set correctly.\")\n        return\n\n    welcome_text = \"\"\"\n# Claude Engineer v3. A self-improving assistant framework with tool creation\n\nType 'refresh' to reload available tools\nType 'reset' to clear conversation history\nType 'quit' to exit\n\nAvailable tools:\n\"\"\"\n    console.print(Markdown(welcome_text))\n    assistant.display_available_tools()\n\n    while True:\n        try:\n            user_input = prompt(\"You: \", style=style).strip()\n\n            if user_input.lower() == 'quit':\n                console.print(\"\\n[bold blue]👋 Goodbye![/bold blue]\")\n                break\n            elif user_input.lower() == 'reset':\n                assistant.reset()\n                continue\n\n            response = assistant.chat(user_input)\n            console.print(\"\\n[bold purple]Claude Engineer:[/bold purple]\")\n            if isinstance(response, str):\n                safe_response = response.replace('[', '\\\\[').replace(']', '\\\\]')\n                console.print(safe_response)\n            else:\n                console.print(str(response))\n\n        except KeyboardInterrupt:\n            continue\n        except EOFError:\n            break\n\n\nif __name__ == \"__main__\":\n    main()"
  },
  {
    "path": "config.py",
    "content": "from pathlib import Path\nimport os\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nclass Config:\n    ANTHROPIC_API_KEY = os.getenv('ANTHROPIC_API_KEY')\n    MODEL = \"claude-3-5-sonnet-20241022\"\n    MAX_TOKENS = 8000\n    MAX_CONVERSATION_TOKENS = 200000  # Maximum tokens per conversation\n\n    # Paths\n    BASE_DIR = Path(__file__).parent\n    TOOLS_DIR = BASE_DIR / \"tools\"\n    PROMPTS_DIR = BASE_DIR / \"prompts\"\n\n    # Assistant Configuration\n    ENABLE_THINKING = True\n    SHOW_TOOL_USAGE = True\n    DEFAULT_TEMPERATURE = 0.7\n"
  },
  {
    "path": "prompts/system_prompts.py",
    "content": "class SystemPrompts:\n    TOOL_USAGE = \"\"\"\n    When using tools, please follow these guidelines:\n    1. Think carefully about which tool is appropriate for the task\n    2. Only use tools when necessary\n    3. Ask for clarification if required parameters are missing\n    4. Explain your choices and results in a natural way\n    5. Available tools and their use cases\n    6. Chain multiple tools together to achieve complex goals:\n       - Break down the goal into logical steps\n       - Use tools sequentially to complete each step\n       - Pass outputs from one tool as inputs to the next\n       - Continue running tools until the full goal is achieved\n       - Provide clear updates on progress through the chain\n    7. Available tools and their use cases\n       - BrowserTool: Opens URLs in system's default browser\n       - CreateFoldersTool: Creates new folders and nested directories\n       - DiffEditorTool: Performs precise text replacements in files\n       - DuckDuckGoTool: Performs web searches using DuckDuckGo\n       - Explorer: Enhanced file/directory management (list, create, delete, move, search)\n       - FileContentReaderTool: Reads content from multiple files\\\n       - FileCreatorTool: Creates new files with specified content\n       - FileEditTool: Edits existing file contents\n       - GitOperationsTool: Handles Git operations (clone, commit, push, etc.)\n       - LintingTool: Lints Python code using Ruff\n       - SequentialThinkingTool: Helps break down complex problems into steps\n       - ShellTool: Executes shell commands securely\n       - ToolCreatorTool: Creates new tool classes based on descriptions\n       - UVPackageManager: Manages Python packages using UV\n       - WebScraperTool: Extracts content from web pages\n\n    6. Consider creating new tools only when:\n       - The requested capability is completely outside existing tools\n       - The functionality can't be achieved by combining existing tools\n       - The new tool would serve a distinct and reusable purpose\n       Do not create new tools if:\n       - An existing tool can handle the task, even partially\n       - The functionality is too similar to existing tools\n       - The tool would be too specific or single-use\n    \"\"\"\n\n    DEFAULT = \"\"\"\n    I am Claude Engineer v3, a powerful AI assistant specialized in software development.\n    I have access to various tools for file management, code execution, web interactions,\n    and development workflows.\n\n    My capabilities include:\n    1. File Operations:\n       - Creating/editing files and folders\n       - Reading file contents\n       - Managing file systems\n    \n    2. Development Tools:\n       - Package management with UV\n    \n    3. Web Interactions:\n       - Web scraping\n       - DuckDuckGo searches\n       - URL handling\n    \n    4. Problem Solving:\n       - Sequential thinking for complex problems\n       - Tool creation for new capabilities\n       - Secure command execution\n    \n    I will:\n    - Think through problems carefully\n    - Show my reasoning clearly\n    - Ask for clarification when needed\n    - Use the most appropriate tools for each task\n    - Explain my choices and results\n    - Handle errors gracefully\n    \n    I can help with various development tasks while maintaining\n    security and following best practices.\n    \"\"\"\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n\n[project]\nname = \"ce3\"\nversion = \"0.1.0\"\ndescription = \"A self-improving assistant framework with tool creation capabilities\"\nreadme = \"README.md\"\nrequires-python = \">=3.9\"\nlicense = { text = \"MIT\" }\nauthors = [{ name = \"Your Name\", email = \"your.email@example.com\" }]\nclassifiers = [\n    \"Development Status :: 3 - Alpha\",\n    \"Intended Audience :: Developers\",\n    \"License :: OSI Approved :: MIT License\",\n    \"Programming Language :: Python :: 3\",\n    \"Programming Language :: Python :: 3.9\",\n    \"Programming Language :: Python :: 3.10\",\n    \"Programming Language :: Python :: 3.11\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\",\n]\ndependencies = [\n    \"anthropic\",\n    \"python-dotenv\",\n    \"rich\",\n    \"requests\",\n    \"beautifulsoup4\",\n    \"validators>=0.34.0\",\n    \"PyAutoGUI\",\n    \"Pillow\",\n    \"prompt-toolkit\",\n    \"matplotlib>=3.9.2\",\n    \"flask>=3.0.3\",\n    \"werkzeug>=3.1.2\",\n    \"markdownify>=0.14.1\",\n    \"protego>=0.3.1\",\n    \"readability-lxml>=0.8.1\",\n    \"e2b-code-interpreter>=1.0.3\",\n]\n\n[project.optional-dependencies]\ndev = [\n    \"pytest\",\n    \"pytest-cov\",\n    \"black\",\n    \"ruff\",\n    \"mypy\",\n    \"pre-commit\",\n    \"types-requests\",\n]\n\n[project.urls]\nHomepage = \"https://github.com/yourusername/cev3\"\nRepository = \"https://github.com/yourusername/cev3\"\nDocumentation = \"https://github.com/yourusername/cev3#readme\"\n\"Bug Tracker\" = \"https://github.com/yourusername/cev3/issues\"\n\n[tool.hatch]\nversion.path = \"cev3/__init__.py\"\n\n[tool.ruff]\nline-length = 88\ntarget-version = \"py39\"\nselect = [\n    \"E\",   # pycodestyle errors\n    \"F\",   # pyflakes\n    \"I\",   # isort\n    \"B\",   # flake8-bugbear\n    \"C4\",  # flake8-comprehensions\n    \"UP\",  # pyupgrade\n    \"RUF\", # ruff-specific rules\n]\nignore = []\n\n[tool.ruff.isort]\nknown-first-party = [\"cev3\"]\n\n[tool.ruff.flake8-tidy-imports]\nban-relative-imports = \"all\"\n\n[tool.black]\nline-length = 88\ntarget-version = [\"py39\"]\ninclude = '\\.pyi?$'\n\n[tool.mypy]\npython_version = \"3.9\"\nwarn_return_any = true\nwarn_unused_configs = true\ndisallow_untyped_defs = true\ncheck_untyped_defs = true\ndisallow_any_generics = true\ndisallow_incomplete_defs = true\ndisallow_untyped_decorators = true\nno_implicit_optional = true\nwarn_redundant_casts = true\nwarn_unused_ignores = true\nwarn_no_return = true\nwarn_unreachable = true\n\n[[tool.mypy.overrides]]\nmodule = [\"wikipedia.*\", \"pyautogui.*\"]\nignore_missing_imports = true\n\n[tool.pytest.ini_options]\nminversion = \"6.0\"\naddopts = \"-ra -q --cov=cev3 --cov-report=term-missing\"\ntestpaths = [\"tests\"]\n\n[tool.uv.workspace]\nmembers = [\"testfolder\"]\n\n[project.scripts]\ncev3 = \"cev3.cev3:main\"\n"
  },
  {
    "path": "readme.md",
    "content": "# Claude Engineer v3 🤖\n\nA powerful self-improving AI Assistant designed for creating and managing AI tools with Claude 3.5. This framework enables Claude to generate and manage its own tools, continuously expanding its capabilities through conversation. Available both as a CLI and a modern web interface!\n\n## History and Evolution\nThis project represents the third major iteration of Claude Engineer, building upon the success of Claude Engineer v2. Key improvements from previous versions include:\n- Upgraded to Claude 3.5 Sonnet model\n- Enhanced token management with Anthropic's new token counting API\n- Self-improving tool creation system\n- Streamlined conversation handling\n- More precise token usage tracking and visualization\n- Autonomous tool generation capabilities\n- No need for automode since Claude can intelligently decide when to run tools automatically and sequentially.\n\n## Description\nClaude Engineer v3 is a sophisticated framework that allows Claude to expand its own capabilities through dynamic tool creation. During conversations, Claude can identify needs for new tools, design them, and implement them automatically. This self-improving architecture means the framework becomes more powerful the more you use it.\n\n\n## Installation\n\nFor the best possible experience install uv\n\n### macOS and Linux\n```bash\n# Install uv\ncurl -LsSf https://astral.sh/uv/install.sh | sh\n# Or using wget if curl is not available:\n# wget -qO- https://astral.sh/uv/install.sh | sh\n\n# Clone and setup\ngit clone https://github.com/Doriandarko/claude-engineer.git\ncd claude-engineer\nuv venv\nsource .venv/bin/activate\n\n# Run web interface\nuv run app.py\n\n# Or run CLI\nuv run ce3.py\n```\n\n### Windows\n```powershell\n# Install uv\npowershell -ExecutionPolicy ByPass -c \"irm https://astral.sh/uv/install.ps1 | iex\"\n\n# Clone and setup\ngit clone https://github.com/Doriandarko/claude-engineer.git\ncd claude-engineer\nuv venv\n.venv\\Scripts\\activate\n\n\n# Run web interface\nuv run app.py\n\n# Or run CLI\nuv run ce3.py\n```\n\n\n## Interface Options\n\n### 1. Web Interface 🌐\nA sleek, modern web UI with features like:\n- Real-time token usage visualization\n- Image upload and analysis capabilities\n- Markdown rendering with syntax highlighting\n- Responsive design for all devices\n- Tool usage indicators\n- Clean, minimal interface\n\n![Claude Engineer v3 Web Interface](ui.png)\n\nTo run the web interface:\n```bash\n# Using uv (recommended)\nuv run app.py\n\n# Or using traditional Python\npython app.py\n\n# Then open your browser to:\nhttp://localhost:5000\n```\n\n### 2. Command Line Interface (CLI) 💻\nA powerful terminal-based interface with:\n- Rich text formatting\n- Progress indicators\n- Token usage visualization\n- Direct tool interaction\n- Detailed debugging output\n\nTo run the CLI:\n```bash\n# Using uv (recommended)\nuv run ce3.py\n\n# Or using traditional Python\npython ce3.py\n```\n\nChoose the interface that best suits your workflow:\n- Web UI: Great for visual work, image analysis, and a more modern experience\n- CLI: Perfect for developers, system integration, and terminal workflows\n\n\n## Self-Improvement Features\n- 🧠 Autonomous tool identification and creation\n- 🔄 Dynamic capability expansion during conversations\n- 🎯 Smart tool dependency management\n- 📈 Learning from tool usage patterns\n- 🔍 Automatic identification of capability gaps\n- 🛠️ Self-optimization of existing tools\n\n## Core Features\n- 🔨 Dynamic tool creation and loading\n- 🔄 Hot-reload capability for new tools\n- 🎨 Rich console interface with progress indicators\n- 🧩 Tool abstraction framework with clean interfaces\n- 📝 Automated tool code generation\n- 🔌 Easy integration with Claude 3.5 AI\n- 💬 Persistent conversation history with token management\n- 🛠️ Real-time tool usage display\n- 🔄 Automatic tool chaining support\n- ⚡ Dynamic module importing system\n- 📊 Advanced token tracking with Anthropic's token counting API\n- 🎯 Precise context window management\n- 🔍 Enhanced error handling and debugging\n- 💾 Conversation state management\n\n## Project Structure\n```\nclaude-engineer/\n├── app.py             # Web interface server\n├── ce3.py            # CLI interface\n├── config.py         # Configuration settings\n├── static/           # Web assets\n│   ├── css/         # Stylesheets\n│   └── js/          # JavaScript files\n├── templates/        # HTML templates\n├── tools/           # Tool implementations\n│   ├── base.py      # Base tool class\n│   └── ...         # Generated and custom tools\n└── prompts/         # System prompts\n    └── system_prompts.py\n```\n\n## Features by Interface\n\n### Web Interface Features\n- 🖼️ Image upload and analysis with Claude Vision\n- 📊 Visual token usage progress bar\n- 🎨 Clean, modern design with Tailwind CSS\n- 📝 Markdown rendering with syntax highlighting\n- 🔄 Real-time updates\n- 📱 Responsive design for all devices\n- 🖥️ Tool usage indicators\n- ⌨️ Command/Ctrl + Enter to send messages\n\n### CLI Features\n- 🎨 Rich text formatting\n- 📊 ASCII token usage bar\n- 🔄 Live progress indicators\n- 🛠️ Direct tool interaction\n- 📝 Detailed debugging output\n- 💻 Terminal-optimized interface\n\nChoose the interface that best matches your workflow and preferences. Both interfaces provide access to the same powerful Claude Engineer capabilities, just presented in different ways.\n\n## Key Components\n\n### Assistant Class\nThe core Assistant class provides:\n- Dynamic tool loading and management\n- Real-time conversation handling with token tracking\n- Automatic tool creation and validation\n- Tool execution and chaining\n- Rich console output with progress indicators\n- Token usage optimization\n\n### Configuration Options\nThe assistant supports various configuration options through the Config class:\n- MODEL: Claude 3.5 Sonnet model specification\n- MAX_TOKENS: Maximum tokens for individual responses\n- MAX_CONVERSATION_TOKENS: Total token limit for conversations\n- TOOLS_DIR: Directory for tool storage\n- SHOW_TOOL_USAGE: Toggle tool usage display\n- ENABLE_THINKING: Toggle thinking indicator\n- DEFAULT_TEMPERATURE: Model temperature setting\n\n## Requirements\n- Python 3.8+\n- Anthropic API Key (Claude 3.5 access)\n- Required packages in `requirements.txt`\n- Rich terminal support\n\n## Contributing\nContributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.\n\n## License\nMIT\n\n## Acknowledgments\nThis project builds upon the foundations of Claude Engineer v2, enhancing its capabilities with self-improving tool generation and advanced token management.\n\n## Built-in Tools\nClaude Engineer v3 comes with a comprehensive set of pre-built tools:\n\n### Core Tools\n- 🛠️ **Tool Creator** (`toolcreator`): Creates new tools based on natural language descriptions, enabling the framework's self-improvement capabilities.\n\n### Development Tools\n- 📦 **UV Package Manager** (`uvpackagemanager`): Interface to the UV package manager for Python dependency management, supporting package installation, removal, updates, and virtual environment management.\n- 🐍 **E2B Code Executor** (`e2bcodetool`): Securely executes Python code in a sandboxed environment powered by E2B. This tool enables Claude to write and run Python code directly, making it capable of data analysis, visualization, and complex computations. Requires an E2B API key available at [e2b.dev](https://e2b.dev/).\n- 🔍 **Linting Tool** (`lintingtool`): Runs the Ruff linter on Python files to detect and fix coding style or syntax issues, with support for automatic fixes and customizable rules.\n\n### File System Tools\n- 📂 **Create Folders Tool** (`createfolderstool`): Creates new directories and nested directory structures with proper error handling and path validation.\n- 📝 **File Creator** (`filecreatortool`): Creates new files with specified content, supporting both text and binary files.\n- 📖 **File Content Reader** (`filecontentreadertool`): Reads content from multiple files simultaneously, with smart filtering of binary and system files.\n- ✏️ **File Edit** (`fileedittool`): Advanced file editing with support for full content replacement and partial edits.\n- 🔄 **Diff Editor** (`diffeditortool`): Performs precise text replacements in files by matching exact substrings.\n\n### Web Tools\n- 🔍 **DuckDuckGo** (`duckduckgotool`): Performs web searches using DuckDuckGo.\n- 🌐 **Web Scraper** (`webscrapertool`): Intelligently extracts readable content from web pages while removing unnecessary elements.\n- 🌍 **Browser** (`browsertool`): Opens URLs in the system's default web browser.\n\n### Utility Tools\n- 📸 **Screenshot Tool** (`screenshottool`): Captures screenshots of the entire screen or specific regions, returning base64-encoded images ready for Claude's vision capabilities.\n\nEach tool is designed to be:\n- Self-documenting with detailed descriptions\n- Error-resistant with comprehensive error handling\n- Composable for complex operations\n- Secure with proper input validation\n- Cross-platform compatible where applicable\n\nThe tools are dynamically loaded and can be extended during runtime through the Tool Creator, allowing the assistant to continuously expand its capabilities based on user needs.\n\n## API Keys Required\n1. **Anthropic API Key**: Required for Claude 3.5 access\n2. **E2B API Key**: Required for Python code execution capabilities. Get your key at [e2b.dev](https://e2b.dev/)\n\nAdd these to your `.env` file:\n\n```bash\nANTHROPIC_API_KEY=your_anthropic_key\nE2B_API_KEY=your_e2b_key\n```\n\n## Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=Doriandarko/claude-engineer&type=Date)](https://star-history.com/#Doriandarko/claude-engineer&Date)\n"
  },
  {
    "path": "requirements.txt",
    "content": "flask==3.0.0\nanthropic>=0.18.1\nbeautifulsoup4>=4.12.3\nmarkdownify>=0.14.1\npillow>=10.2.0\nprotego>=0.3.1\npyautogui>=0.9.54\npython-dotenv>=1.0.1\nreadability-lxml>=0.8.1\nrequests>=2.31.0\nrich>=13.7.0\nvalidators>=0.22.0\nwerkzeug>=3.0.1\nprompt-toolkit>=3.0.43\nmatplotlib>=3.9.2"
  },
  {
    "path": "static/css/style.css",
    "content": "/* Custom scrollbar */\n::-webkit-scrollbar {\n    width: 8px;\n}\n::-webkit-scrollbar-track {\n    background: transparent;\n}\n::-webkit-scrollbar-thumb {\n    background: #cbd5e1;\n    border-radius: 4px;\n}\n::-webkit-scrollbar-thumb:hover {\n    background: #94a3b8;\n}\n\n/* Code block styling */\npre {\n    background: #f8fafc;\n    border-radius: 6px;\n    padding: 1rem;\n    margin: 0.5rem 0;\n    overflow-x: auto;\n}\ncode {\n    font-family: ui-monospace, monospace;\n    font-size: 0.9em;\n}\n\n/* Chat container styles */\n.chat-container {\n    display: flex;\n    flex-direction: column;\n    height: 100vh;\n    overflow: hidden;\n}\n\n.messages-container {\n    flex: 1;\n    overflow-y: auto;\n    padding: 1rem;\n    padding-bottom: 2rem;\n    max-width: 5xl;\n    margin: 0 auto;\n    width: 100%;\n}\n\n.input-container {\n    position: sticky;\n    bottom: 0;\n    background-color: white;\n    border-top: 1px solid #e5e7eb;\n    padding: 1rem 0;\n    width: 100%;\n    box-shadow: 0 -4px 6px -1px rgb(0 0 0 / 0.05);\n}\n\n@keyframes pulse {\n    0%, 100% { opacity: 1; }\n    50% { opacity: 0.5; }\n}\n\n.thinking {\n    display: flex;\n    align-items: center;\n    gap: 0.5rem;\n    color: #6b7280;\n    font-size: 0.875rem;\n}\n\n.thinking::before {\n    content: '';\n    width: 0.5rem;\n    height: 0.5rem;\n    background-color: currentColor;\n    border-radius: 50%;\n    animation: pulse 1.5s ease-in-out infinite;\n}\n\n.thinking-dots span {\n    animation: pulse 1.5s ease-in-out infinite;\n    display: inline-block;\n    margin-right: 2px;\n}\n\n.thinking-dots span:nth-child(2) {\n    animation-delay: 0.2s;\n}\n\n.thinking-dots span:nth-child(3) {\n    animation-delay: 0.4s;\n}\n\n/* Update the messages spacing */\n.message-wrapper {\n    margin: 0 auto 1.5rem;\n    max-width: 5xl;\n    padding: 0 1rem;\n}\n\n.message-wrapper:last-child {\n    margin-bottom: 2rem;\n}\n\n/* Special spacing for consecutive messages */\n.message-wrapper + .message-wrapper {\n    margin-top: 1.5rem;\n}\n\n/* Update the primary color to Tailwind black */\n.ai-avatar {\n    background-color: #111827; /* Tailwind black-900 */\n}\n\n/* Add to your existing CSS */\n.tool-usage {\n    display: flex;\n    align-items: center;\n    gap: 0.5rem;\n    color: #6b7280;\n    font-size: 0.875rem;\n    padding: 0.5rem 0.75rem;\n    background-color: #f8fafc;\n    border-radius: 0.5rem;\n}\n\n.tool-usage::before {\n    content: '🔧';\n    font-size: 1rem;\n}\n\n/* Add responsive container styles */\n@media (max-width: 640px) {\n    .input-container {\n        padding: 0.75rem 0;\n    }\n    \n    .messages-container {\n        padding: 0.75rem;\n    }\n}\n\n/* Add these styles to your existing CSS */\n.token-usage-container {\n    position: sticky;\n    bottom: 80px;\n    background-color: white;\n    padding: 0.5rem 0;\n    border-top: 1px solid #e5e7eb;\n    z-index: 10;\n}\n\n.token-bar-container {\n    flex: 1;\n    height: 8px;\n    background-color: #f3f4f6;\n    border-radius: 4px;\n    overflow: hidden;\n}\n\n.token-bar {\n    height: 100%;\n    background-color: #10b981; /* Green by default */\n    border-radius: 4px;\n    transition: width 0.3s ease, background-color 0.3s ease;\n}\n\n.token-bar.warning {\n    background-color: #f59e0b; /* Yellow for warning */\n}\n\n.token-bar.danger {\n    background-color: #ef4444; /* Red for danger */\n}\n\n.token-count {\n    min-width: 160px;\n}\n\n.token-percentage {\n    min-width: 48px;\n    text-align: right;\n}\n\n/* Add these styles to your existing CSS */\n.message-wrapper .prose p {\n    color: #18181B;\n    font-size: 14px;\n    line-height: 1.5;\n}\n\n/* For user messages */\n.message-wrapper .prose {\n    color: #18181B;\n    font-size: 14px;\n    line-height: 1.5;\n}\n\n.command-code {\n    background-color: #e7f3ed;\n    color: #0a3622;\n    padding: 2px 6px;\n    border-radius: 4px;\n    font-family: monospace;\n}\n  "
  },
  {
    "path": "static/js/chat.js",
    "content": "let currentImageData = null;\nlet currentMediaType = null;\n\n// Auto-resize textarea\nconst textarea = document.getElementById('message-input');\ntextarea.addEventListener('input', function() {\n    this.style.height = '28px';\n    this.style.height = (this.scrollHeight) + 'px';\n});\n\nfunction appendMessage(content, isUser = false) {\n    const messagesDiv = document.getElementById('chat-messages');\n    const messageWrapper = document.createElement('div');\n    messageWrapper.className = 'message-wrapper';\n    \n    const messageDiv = document.createElement('div');\n    messageDiv.className = 'flex items-start space-x-4 space-y-1';\n    \n    // Avatar\n    const avatarDiv = document.createElement('div');\n    if (isUser) {\n        avatarDiv.className = 'w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center text-gray-600 font-bold text-xs';\n        avatarDiv.textContent = 'You';\n    } else {\n        avatarDiv.className = 'w-8 h-8 rounded-full ai-avatar flex items-center justify-center text-white font-bold text-xs';\n        avatarDiv.textContent = 'CE';\n    }\n    \n    // Message content\n    const contentDiv = document.createElement('div');\n    contentDiv.className = 'flex-1';\n    \n    const innerDiv = document.createElement('div');\n    innerDiv.className = 'prose prose-slate max-w-none';\n    \n    if (!isUser && content) {\n        try {\n            innerDiv.innerHTML = marked.parse(content);\n        } catch (e) {\n            console.error('Error parsing markdown:', e);\n            innerDiv.textContent = content;\n        }\n    } else {\n        innerDiv.textContent = content || '';\n    }\n    \n    contentDiv.appendChild(innerDiv);\n    messageDiv.appendChild(avatarDiv);\n    messageDiv.appendChild(contentDiv);\n    messageWrapper.appendChild(messageDiv);\n    messagesDiv.appendChild(messageWrapper);\n    messagesDiv.scrollTop = messagesDiv.scrollHeight;\n}\n\n// Event Listeners\ndocument.getElementById('upload-btn').addEventListener('click', () => {\n    document.getElementById('file-input').click();\n});\n\ndocument.getElementById('file-input').addEventListener('change', async (e) => {\n    const file = e.target.files[0];\n    if (file) {\n        const formData = new FormData();\n        formData.append('file', file);\n\n        try {\n            const response = await fetch('/upload', {\n                method: 'POST',\n                body: formData\n            });\n            const data = await response.json();\n            \n            if (data.success) {\n                currentImageData = data.image_data;\n                currentMediaType = data.media_type;\n                document.getElementById('preview-img').src = `data:${data.media_type};base64,${data.image_data}`;\n                document.getElementById('image-preview').classList.remove('hidden');\n            }\n        } catch (error) {\n            console.error('Error uploading image:', error);\n        }\n    }\n});\n\ndocument.getElementById('remove-image').addEventListener('click', () => {\n    currentImageData = null;\n    document.getElementById('image-preview').classList.add('hidden');\n    document.getElementById('file-input').value = '';\n});\n\nfunction appendThinkingIndicator() {\n    const messagesDiv = document.getElementById('chat-messages');\n    const messageWrapper = document.createElement('div');\n    messageWrapper.className = 'message-wrapper thinking-message';\n    \n    const messageDiv = document.createElement('div');\n    messageDiv.className = 'flex items-start space-x-4';\n    \n    // AI Avatar\n    const avatarDiv = document.createElement('div');\n    avatarDiv.className = 'w-8 h-8 rounded-full ai-avatar flex items-center justify-center text-white font-bold text-sm';\n    avatarDiv.textContent = 'CE';\n    \n    // Thinking content\n    const contentDiv = document.createElement('div');\n    contentDiv.className = 'flex-1';\n    \n    const thinkingDiv = document.createElement('div');\n    thinkingDiv.className = 'thinking';\n    thinkingDiv.innerHTML = '<div style=\"margin-top: 6px; margin-bottom: 4px;\">Thinking<span class=\"thinking-dots\"><span>.</span><span>.</span><span>.</span></span></div>';\n    \n    contentDiv.appendChild(thinkingDiv);\n    messageDiv.appendChild(avatarDiv);\n    messageDiv.appendChild(contentDiv);\n    messageWrapper.appendChild(messageDiv);\n    messagesDiv.appendChild(messageWrapper);\n    messagesDiv.scrollTop = messagesDiv.scrollHeight;\n    \n    return messageWrapper;\n}\n\n// Add command+enter handler\ndocument.getElementById('message-input').addEventListener('keydown', (e) => {\n    if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n        e.preventDefault();\n        document.getElementById('chat-form').dispatchEvent(new Event('submit'));\n    }\n});\n\n// Add function to show tool usage\nfunction appendToolUsage(toolName) {\n    const messagesDiv = document.getElementById('chat-messages');\n    const messageWrapper = document.createElement('div');\n    messageWrapper.className = 'message-wrapper';\n    \n    const messageDiv = document.createElement('div');\n    messageDiv.className = 'flex items-start space-x-4';\n    \n    // AI Avatar\n    const avatarDiv = document.createElement('div');\n    avatarDiv.className = 'w-8 h-8 rounded-full ai-avatar flex items-center justify-center text-white font-bold text-sm';\n    avatarDiv.textContent = 'CE';\n    \n    // Tool usage content\n    const contentDiv = document.createElement('div');\n    contentDiv.className = 'flex-1';\n    \n    const toolDiv = document.createElement('div');\n    toolDiv.className = 'tool-usage';\n    toolDiv.textContent = `Using tool: ${toolName}`;\n    \n    contentDiv.appendChild(toolDiv);\n    messageDiv.appendChild(avatarDiv);\n    messageDiv.appendChild(contentDiv);\n    messageWrapper.appendChild(messageDiv);\n    messagesDiv.appendChild(messageWrapper);\n    messagesDiv.scrollTop = messagesDiv.scrollHeight;\n}\n\n// Add this function near the top of your file\nfunction updateTokenUsage(usedTokens, maxTokens) {\n    const percentage = (usedTokens / maxTokens) * 100;\n    const tokenBar = document.getElementById('token-bar');\n    const tokensUsed = document.getElementById('tokens-used');\n    const tokenPercentage = document.getElementById('token-percentage');\n    \n    // Update the numbers\n    tokensUsed.textContent = usedTokens.toLocaleString();\n    tokenPercentage.textContent = `${percentage.toFixed(1)}%`;\n    \n    // Update the bar\n    tokenBar.style.width = `${percentage}%`;\n    \n    // Update colors based on usage\n    tokenBar.classList.remove('warning', 'danger');\n    if (percentage > 90) {\n        tokenBar.classList.add('danger');\n    } else if (percentage > 75) {\n        tokenBar.classList.add('warning');\n    }\n}\n\n// Update the chat form submit handler\ndocument.getElementById('chat-form').addEventListener('submit', async (e) => {\n    e.preventDefault();\n    \n    const messageInput = document.getElementById('message-input');\n    const message = messageInput.value.trim();\n    \n    if (!message && !currentImageData) return;\n    \n    // Append user message (and image if present)\n    appendMessage(message, true);\n    if (currentImageData) {\n        // Optionally show the image in the chat\n        const imagePreview = document.createElement('img');\n        imagePreview.src = `data:image/jpeg;base64,${currentImageData}`;\n        imagePreview.className = 'max-h-48 rounded-lg mt-2';\n        document.querySelector('.message-wrapper:last-child .prose').appendChild(imagePreview);\n    }\n    \n    // Clear input and reset height\n    messageInput.value = '';\n    resetTextarea();\n    \n    try {\n        // Add thinking indicator\n        const thinkingMessage = appendThinkingIndicator();\n        \n        const response = await fetch('/chat', {\n            method: 'POST',\n            headers: {\n                'Content-Type': 'application/json'\n            },\n            body: JSON.stringify({\n                message: message,\n                image: currentImageData  // This will be null if no image is selected\n            })\n        });\n        \n        const data = await response.json();\n        \n        // Update token usage if provided in response\n        if (data.token_usage) {\n            updateTokenUsage(data.token_usage.total_tokens, data.token_usage.max_tokens);\n        }\n        \n        // Remove thinking indicator\n        if (thinkingMessage) {\n            thinkingMessage.remove();\n        }\n        \n        // Show tool usage if present\n        if (data.tool_name) {\n            appendToolUsage(data.tool_name);\n        }\n        \n        // Show response if we have one\n        if (data && data.response) {\n            appendMessage(data.response);\n        } else {\n            appendMessage('Error: No response received');\n        }\n        \n        // Clear image after sending\n        currentImageData = null;\n        document.getElementById('image-preview').classList.add('hidden');\n        document.getElementById('file-input').value = '';\n        \n    } catch (error) {\n        console.error('Error sending message:', error);\n        document.querySelector('.thinking-message')?.remove();\n        appendMessage('Error: Failed to send message');\n    }\n});\n\nfunction resetTextarea() {\n    const textarea = document.getElementById('message-input');\n    textarea.style.height = '28px';\n}\n\ndocument.getElementById('chat-form').addEventListener('reset', () => {\n    resetTextarea();\n});\n\n// Add at the top of the file\nwindow.addEventListener('load', async () => {\n    try {\n        // Reset the conversation when page loads\n        const response = await fetch('/reset', {\n            method: 'POST',\n            headers: {\n                'Content-Type': 'application/json'\n            }\n        });\n        \n        if (!response.ok) {\n            console.error('Failed to reset conversation');\n        }\n        \n        // Clear any existing messages except the first one\n        const messagesDiv = document.getElementById('chat-messages');\n        const messages = messagesDiv.getElementsByClassName('message-wrapper');\n        while (messages.length > 1) {\n            messages[1].remove();\n        }\n        \n        // Reset any other state\n        currentImageData = null;\n        document.getElementById('image-preview')?.classList.add('hidden');\n        document.getElementById('file-input').value = '';\n        document.getElementById('message-input').value = '';\n        resetTextarea();\n        \n        // Reset token usage display\n        updateTokenUsage(0, 200000);\n    } catch (error) {\n        console.error('Error resetting conversation:', error);\n    }\n}); "
  },
  {
    "path": "templates/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Claude Engineer v3</title>\n    <script src=\"https://cdn.tailwindcss.com\"></script>\n    <script src=\"https://unpkg.com/marked@4.0.0/marked.min.js\"></script>\n    <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='css/style.css') }}\">\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/github.min.css\">\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js\"></script>\n    <script>\n        // Configure marked to use highlight.js\n        marked.setOptions({\n            highlight: function(code, lang) {\n                if (lang && hljs.getLanguage(lang)) {\n                    return hljs.highlight(code, { language: lang }).value;\n                }\n                return hljs.highlightAuto(code).value;\n            }\n        });\n    </script>\n</head>\n<body class=\"bg-white\">\n    <div class=\"chat-container\">\n        <!-- Messages area -->\n        <div class=\" max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 messages-container\" id=\"chat-messages\">\n            <!-- Initial message -->\n            <div class=\"message-wrapper initial-message\">\n                <div class=\"flex items-start space-x-4 space-y-1\">\n                    <div class=\"w-8 h-8 rounded-full ai-avatar flex items-center justify-center text-white font-bold text-xs\">\n                        CE\n                    </div>\n                    <div class=\"flex-1\">\n                        <div class=\"prose prose-slate max-w-none\">\n                            <p>Welcome to Claude Engineer v3! I'm here to help with programming and development tasks. I can create custom tools on demand to help with any task you need - just ask!</p>\n                            \n                            <p>Available commands:</p>\n                            <p>\n                                <span class=\"command-code\">refresh</span> - Reload available tools<br>\n                                <span class=\"command-code\">reset</span> - Clear conversation history<br>\n                                <span class=\"command-code\">quit</span> - Exit the conversation\n                            </p>\n                            \n                            \n                            \n                            <p>How can I assist you today?</p>\n                        </div>\n                    </div>\n                </div>\n            </div>\n        </div>\n        \n        <!-- Add this right before the input-container div -->\n        <div class=\"token-usage-container\">\n            <div class=\"max-w-3xl mx-auto px-4 sm:px-6 lg:px-8\">\n                <div class=\"flex items-center space-x-4 text-sm text-gray-500\">\n                    <div class=\"token-count\">Total used: <span id=\"tokens-used\">0</span> / <span id=\"max-tokens\">200,000</span></div>\n                    <div class=\"token-bar-container\">\n                        <div id=\"token-bar\" class=\"token-bar\" style=\"width: 0%\"></div>\n                    </div>\n                    <div id=\"token-percentage\" class=\"token-percentage\">0%</div>\n                </div>\n            </div>\n        </div>\n        \n        <!-- Fixed input area -->\n        <div class=\"input-container\">\n            <div class=\"max-w-5xl mx-auto px-4 sm:px-6 lg:px-8\">\n                <form id=\"chat-form\" class=\"relative\">\n                    <div id=\"image-preview\" class=\"hidden mb-2\">\n                        <div class=\"relative inline-block\">\n                            <img id=\"preview-img\" class=\"max-h-32 rounded-lg border border-gray-200\" src=\"\" alt=\"Preview\">\n                            <button type=\"button\" id=\"remove-image\" class=\"absolute -top-2 -right-2 bg-white rounded-full p-1 shadow-sm hover:bg-gray-100\">\n                                <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4 text-gray-500\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                                    <path fill-rule=\"evenodd\" d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\" clip-rule=\"evenodd\" />\n                                </svg>\n                            </button>\n                        </div>\n                    </div>\n                    <div class=\"flex items-end space-x-2 bg-white rounded-xl border border-gray-200 p-3 mx-2\">\n                        <button type=\"button\" id=\"upload-btn\" class=\"p-2 text-gray-400 hover:text-gray-600\">\n                            <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n                                <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z\" />\n                            </svg>\n                        </button>\n                        <input type=\"file\" id=\"file-input\" class=\"hidden\" accept=\"image/*\">\n                        <textarea id=\"message-input\" \n                            class=\"flex-1 border-0 bg-transparent p-2 focus:ring-0 focus:outline-none resize-none max-h-32 overflow-y-auto min-h-[2.5rem]\"\n                            rows=\"1\"\n                            placeholder=\"Type something... (⌘ + Enter to send)\"\n                            style=\"height: 40px; max-height: 200px;\"\n                        ></textarea>\n                        <button type=\"submit\" class=\"p-2 text-gray-400 hover:text-gray-600\">\n                            <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                                <path d=\"M10.894 2.553a1 1 0 00-1.788 0l-7 14a1 1 0 001.169 1.409l5-1.429A1 1 0 009 15.571V11a1 1 0 112 0v4.571a1 1 0 00.725.962l5 1.428a1 1 0 001.17-1.408l-7-14z\" />\n                            </svg>\n                        </button>\n                    </div>\n                </form>\n            </div>\n        </div>\n    </div>\n\n    <script src=\"{{ url_for('static', filename='js/chat.js') }}\"></script>\n</body>\n</html> "
  },
  {
    "path": "test.py",
    "content": "from typing import List\nimport unittest\n\ndef calculate_sum(numbers: List[float]) -> float:\n    \"\"\"\n    Calculate the sum of a list of numbers.\n    \n    Args:\n        numbers (List[float]): A list of numbers to sum\n        \n    Returns:\n        float: The sum of all numbers in the list\n        \n    Raises:\n        ValueError: If the input list is empty\n    \"\"\"\n    if not numbers:\n        raise ValueError(\"Cannot calculate sum of an empty list\")\n    return sum(numbers)\n\ndef calculate_median(numbers: List[float]) -> float:\n    \"\"\"\n    Calculate the median of a list of numbers.\n    \n    Args:\n        numbers (List[float]): A list of numbers to find the median of\n        \n    Returns:\n        float: The median value of the list\n        \n    Raises:\n        ValueError: If the input list is empty\n    \"\"\"\n    if not numbers:\n        raise ValueError(\"Cannot calculate median of an empty list\")\n    \n    sorted_numbers = sorted(numbers)\n    n = len(sorted_numbers)\n    mid = n // 2\n    \n    if n % 2 == 0:\n        return (sorted_numbers[mid-1] + sorted_numbers[mid]) / 2\n    else:\n        return sorted_numbers[mid]\n\ndef main() -> None:\n    \"\"\"Main function to demonstrate the usage of calculate_sum and calculate_median.\"\"\"\n    try:\n        numbers = [1, 2, 3, 4, 5]\n        total = calculate_sum(numbers)\n        median = calculate_median(numbers)\n        print(f\"The sum is: {total}\")\n        print(f\"The median is: {median}\")\n    except ValueError as e:\n        print(f\"Error: {e}\")\n\nclass TestCalculateSum(unittest.TestCase):\n    \"\"\"Test cases for the calculate_sum function.\"\"\"\n    \n    def test_normal_list(self):\n        \"\"\"Test with a normal list of numbers.\"\"\"\n        self.assertEqual(calculate_sum([1, 2, 3, 4, 5]), 15)\n        \n    def test_float_numbers(self):\n        \"\"\"Test with floating point numbers.\"\"\"\n        self.assertAlmostEqual(calculate_sum([1.5, 2.5, 3.5]), 7.5)\n        \n    def test_empty_list(self):\n        \"\"\"Test that empty list raises ValueError.\"\"\"\n        with self.assertRaises(ValueError):\n            calculate_sum([])\n            \n    def test_negative_numbers(self):\n        \"\"\"Test with negative numbers.\"\"\"\n        self.assertEqual(calculate_sum([-1, -2, -3]), -6)\n\nclass TestCalculateMedian(unittest.TestCase):\n    \"\"\"Test cases for the calculate_median function.\"\"\"\n    \n    def test_odd_length_list(self):\n        \"\"\"Test median with odd number of elements.\"\"\"\n        self.assertEqual(calculate_median([1, 2, 3, 4, 5]), 3)\n        \n    def test_even_length_list(self):\n        \"\"\"Test median with even number of elements.\"\"\"\n        self.assertEqual(calculate_median([1, 2, 3, 4]), 2.5)\n        \n    def test_unordered_list(self):\n        \"\"\"Test median with unordered list.\"\"\"\n        self.assertEqual(calculate_median([5, 2, 1, 4, 3]), 3)\n        \n    def test_empty_list(self):\n        \"\"\"Test that empty list raises ValueError.\"\"\"\n        with self.assertRaises(ValueError):\n            calculate_median([])\n            \n    def test_negative_numbers(self):\n        \"\"\"Test with negative numbers.\"\"\"\n        self.assertEqual(calculate_median([-1, -2, -3]), -2)\n\nif __name__ == \"__main__\":\n    main()\n    # Run the tests\n    unittest.main(argv=[''], exit=False)\n"
  },
  {
    "path": "tools/base.py",
    "content": "from abc import ABC, abstractmethod\nfrom typing import Dict\n\nclass BaseTool(ABC):\n    @property\n    @abstractmethod\n    def name(self) -> str:\n        \"\"\"Tool name that matches the regex ^[a-zA-Z0-9_-]{1,64}$\"\"\"\n        pass\n\n    @property\n    @abstractmethod\n    def description(self) -> str:\n        \"\"\"Detailed description of what the tool does\"\"\"\n        pass\n\n    @property\n    @abstractmethod\n    def input_schema(self) -> Dict:\n        \"\"\"JSON Schema defining the expected parameters\"\"\"\n        pass\n\n    @abstractmethod\n    def execute(self, **kwargs) -> str:\n        \"\"\"Execute the tool with given parameters\"\"\"\n        pass\n"
  },
  {
    "path": "tools/browsertool.py",
    "content": "from tools.base import BaseTool\nimport webbrowser\nimport validators\nfrom typing import Union, List\nfrom urllib.parse import urlparse\n\nclass BrowserTool(BaseTool):\n    name = \"browsertool\"\n    description = '''\n    Opens URLs in the system's default web browser.\n    Accepts a single URL or a list of URLs.\n    Validates URL format and supports http/https protocols.\n    Returns feedback on which URLs were successfully opened.\n    '''\n    input_schema = {\n        \"type\": \"object\",\n        \"properties\": {\n            \"urls\": {\n                \"type\": [\"string\", \"array\"],\n                \"items\": {\"type\": \"string\"},\n                \"description\": \"Single URL or list of URLs to open\"\n            }\n        },\n        \"required\": [\"urls\"]\n    }\n\n    def _validate_url(self, url: str) -> bool:\n        if not isinstance(url, str):\n            return False\n        if not validators.url(url):\n            return False\n        parsed = urlparse(url)\n        return parsed.scheme in ['http', 'https']\n\n    def execute(self, **kwargs) -> str:\n        urls = kwargs.get('urls', [])\n        if isinstance(urls, str):\n            urls = [urls]\n\n        results = []\n        for url in urls:\n            try:\n                if not self._validate_url(url):\n                    results.append(f\"Failed to open {url}: Invalid URL format\")\n                    continue\n                \n                webbrowser.open(url)\n                results.append(f\"Successfully opened {url}\")\n            except Exception as e:\n                results.append(f\"Error opening {url}: {str(e)}\")\n\n        return \"\\n\".join(results)"
  },
  {
    "path": "tools/createfolderstool.py",
    "content": "from tools.base import BaseTool\nimport os\nimport pathlib\nfrom typing import List\n\nclass CreateFoldersTool(BaseTool):\n    name = \"createfolderstool\"\n    description = '''\n    Creates new folders at specified paths, including nested directories if needed.\n    Accepts a list of folder paths and creates each folder along with any necessary parent directories.\n    Supports both absolute and relative paths.\n    Returns status messages for each folder creation attempt.\n    '''\n    input_schema = {\n        \"type\": \"object\",\n        \"properties\": {\n            \"folder_paths\": {\n                \"type\": \"array\",\n                \"items\": {\n                    \"type\": \"string\"\n                },\n                \"description\": \"List of folder paths to create\"\n            }\n        },\n        \"required\": [\"folder_paths\"]\n    }\n\n    def execute(self, **kwargs) -> str:\n        folder_paths: List[str] = kwargs.get(\"folder_paths\", [])\n        if not folder_paths:\n            return \"No folder paths provided\"\n\n        results = []\n        for path in folder_paths:\n            try:\n                # Normalize path\n                normalized_path = os.path.normpath(path)\n                absolute_path = os.path.abspath(normalized_path)\n                \n                # Validate path\n                if not all(c not in '<>:\"|?*' for c in absolute_path):\n                    results.append(f\"Invalid characters in path: {path}\")\n                    continue\n\n                # Create directory\n                os.makedirs(absolute_path, exist_ok=True)\n                results.append(f\"Successfully created folder: {path}\")\n\n            except PermissionError:\n                results.append(f\"Permission denied: Unable to create folder {path}\")\n            except OSError as e:\n                results.append(f\"Error creating folder {path}: {str(e)}\")\n            except Exception as e:\n                results.append(f\"Unexpected error creating folder {path}: {str(e)}\")\n\n        return \"\\n\".join(results)"
  },
  {
    "path": "tools/diffeditortool.py",
    "content": "from tools.base import BaseTool\nimport os\nfrom typing import Dict\n\nclass DiffEditorTool(BaseTool):\n    name = \"diffeditortool\"\n    description = '''\n    Performs a precise replacement of a given text snippet in a specified file.\n    It takes the following inputs:\n    - path: The path to the target file.\n    - old_text: The exact substring that should be replaced.\n    - new_text: The new substring that replaces the old one.\n\n    The tool will:\n    1. Read the file contents.\n    2. Search for `old_text` within the file.\n    3. If found, replace the first occurrence of `old_text` with `new_text`.\n    4. Write the modified content back to the file.\n    5. Return a success message if successful, or indicate that the old_text was not found.\n    '''\n\n    input_schema = {\n        \"type\": \"object\",\n        \"properties\": {\n            \"path\": {\n                \"type\": \"string\",\n                \"description\": \"Path to the file to be edited.\"\n            },\n            \"old_text\": {\n                \"type\": \"string\",\n                \"description\": \"Exact substring in the file to replace.\"\n            },\n            \"new_text\": {\n                \"type\": \"string\",\n                \"description\": \"New substring that will replace old_text.\"\n            }\n        },\n        \"required\": [\"path\", \"old_text\", \"new_text\"]\n    }\n\n    def execute(self, **kwargs) -> str:\n        path = kwargs.get(\"path\")\n        old_text = kwargs.get(\"old_text\")\n        new_text = kwargs.get(\"new_text\")\n\n        # Check if file exists\n        if not os.path.isfile(path):\n            return f\"Error: File does not exist at path: {path}\"\n\n        # Read the file content\n        try:\n            with open(path, 'r', encoding='utf-8') as f:\n                content = f.read()\n        except Exception as e:\n            return f\"Error reading file {path}: {str(e)}\"\n\n        # Locate the old_text in the file\n        index = content.find(old_text)\n        if index == -1:\n            return f\"'{old_text}' not found in the file. No changes made.\"\n\n        # Replace the first occurrence of old_text with new_text\n        # Since find gave us the exact start, we can do a direct substring replacement:\n        new_content = content[:index] + new_text + content[index+len(old_text):]\n\n        # Write the updated content back to the file\n        try:\n            with open(path, 'w', encoding='utf-8') as f:\n                f.write(new_content)\n        except Exception as e:\n            return f\"Error writing updated content to file {path}: {str(e)}\"\n\n        return f\"Successfully replaced '{old_text}' with '{new_text}' in {path}.\"\n"
  },
  {
    "path": "tools/duckduckgotool.py",
    "content": "from tools.base import BaseTool\nimport requests\nfrom bs4 import BeautifulSoup\nfrom urllib.parse import quote_plus\n\nclass DuckduckgoTool(BaseTool):\n    name = \"duckduckgotool\"\n    description = '''\n    Performs a search using DuckDuckGo and returns the top search results.\n    Returns titles, snippets, and URLs of the search results.\n    Use this tool when you need to search for current information on the internet.\n    '''\n    input_schema = {\n        \"type\": \"object\",\n        \"properties\": {\n            \"query\": {\n                \"type\": \"string\",\n                \"description\": \"The search query to look up\"\n            },\n            \"num_results\": {\n                \"type\": \"integer\",\n                \"description\": \"Number of results to return (default: 8)\", \n                \"default\": 8\n            }\n        },\n        \"required\": [\"query\"]\n    }\n\n    def execute(self, **kwargs) -> str:\n        query = kwargs.get(\"query\")\n        num_results = kwargs.get(\"num_results\", 8)\n\n        url = f\"https://html.duckduckgo.com/html/?q={quote_plus(query)}\"\n        headers = {\n            \"User-Agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36\"\n        }\n\n        try:\n            response = requests.get(url, headers=headers)\n            response.raise_for_status()\n            soup = BeautifulSoup(response.text, 'html.parser')\n            \n            results = []\n            for result in soup.select('.result')[:num_results]:\n                title_elem = result.select_one('.result__title')\n                snippet_elem = result.select_one('.result__snippet')\n                url_elem = result.select_one('.result__url')\n                \n                if title_elem and snippet_elem:\n                    title = title_elem.get_text(strip=True)\n                    snippet = snippet_elem.get_text(strip=True)\n                    url = url_elem.get('href') if url_elem else None\n                    \n                    results.append(f\"Title: {title}\\nSnippet: {snippet}\\nURL: {url}\\n\")\n\n            if not results:\n                return \"No results found.\"\n                \n            return \"\\n\".join(results)\n\n        except requests.RequestException as e:\n            return f\"Error performing search: {str(e)}\""
  },
  {
    "path": "tools/e2bcodetool.py",
    "content": "from tools.base import BaseTool\nfrom e2b_code_interpreter import Sandbox\nfrom dotenv import load_dotenv\nimport os\nimport time\nimport json\nimport base64\n\nclass E2bCodeTool(BaseTool):\n    name = \"e2bcodetool\"\n    description = '''\n    Executes Python code in a sandboxed environment using e2b-code-interpreter.\n    Features:\n    - Execute Python code safely in isolation\n    - Upload files to sandbox\n    - Download files from sandbox\n    - Support for environment variables\n    Returns execution results including stdout, stderr, and file contents.\n    '''\n    input_schema = {\n        \"type\": \"object\",\n        \"properties\": {\n            \"code\": {\n                \"type\": \"string\",\n                \"description\": \"Python code to execute\"\n            },\n            \"env_vars\": {\n                \"type\": \"object\",\n                \"description\": \"Dictionary of environment variables\",\n                \"additionalProperties\": {\"type\": \"string\"}\n            },\n            \"upload_files\": {\n                \"type\": \"array\",\n                \"description\": \"List of files to upload to sandbox\",\n                \"items\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"local_path\": {\"type\": \"string\"},\n                        \"sandbox_path\": {\"type\": \"string\"},\n                        \"content\": {\"type\": \"string\"}\n                    },\n                    \"required\": [\"sandbox_path\", \"content\"]\n                }\n            },\n            \"download_paths\": {\n                \"type\": \"array\",\n                \"description\": \"List of file paths to download from sandbox\",\n                \"items\": {\"type\": \"string\"}\n            }\n        },\n        \"required\": [\"code\"]\n    }\n\n    def execute(self, **kwargs) -> str:\n        try:\n            load_dotenv()\n            \n            code = kwargs.get(\"code\")\n            upload_files = kwargs.get(\"upload_files\", [])\n            download_paths = kwargs.get(\"download_paths\", [])\n            \n            # Create sandbox instance\n            sandbox = Sandbox()\n            \n            # Upload files if specified\n            uploaded_files = []\n            for file_spec in upload_files:\n                try:\n                    sandbox_path = file_spec[\"sandbox_path\"]\n                    content = file_spec[\"content\"]\n                    \n                    # Handle both text and base64 content\n                    if \";base64,\" in content:\n                        # Extract base64 data\n                        content = content.split(\";base64,\")[1]\n                        file_content = base64.b64decode(content)\n                    else:\n                        file_content = content.encode('utf-8')\n                        \n                    sandbox.files.write(sandbox_path, file_content)\n                    uploaded_files.append(sandbox_path)\n                except Exception as e:\n                    return json.dumps({\n                        \"success\": False,\n                        \"error\": f\"Failed to upload file {sandbox_path}: {str(e)}\",\n                        \"stdout\": \"\",\n                        \"stderr\": \"\"\n                    }, indent=2)\n\n            # Execute code\n            result = sandbox.run_code(code)\n            \n            # Download requested files\n            downloaded_files = {}\n            for file_path in download_paths:\n                try:\n                    content = sandbox.files.read(file_path)\n                    # Convert binary content to base64\n                    if isinstance(content, bytes):\n                        content = base64.b64encode(content).decode('utf-8')\n                        content = f\"data:application/octet-stream;base64,{content}\"\n                    downloaded_files[file_path] = content\n                except Exception as e:\n                    downloaded_files[file_path] = f\"Error downloading: {str(e)}\"\n            \n            response = {\n                \"stdout\": result.logs.stdout,\n                \"stderr\": result.logs.stderr,\n                \"success\": True,\n                \"error\": None,\n                \"uploaded_files\": uploaded_files,\n                \"downloaded_files\": downloaded_files\n            }\n            \n            return json.dumps(response, indent=2)\n            \n        except Exception as e:\n            return json.dumps({\n                \"success\": False,\n                \"error\": f\"Tool execution failed: {str(e)}\",\n                \"stdout\": \"\",\n                \"stderr\": \"\",\n                \"uploaded_files\": [],\n                \"downloaded_files\": {}\n            }, indent=2)"
  },
  {
    "path": "tools/filecontentreadertool.py",
    "content": "from tools.base import BaseTool\nimport os\nimport json\nimport mimetypes\n\nclass FileContentReaderTool(BaseTool):\n    name = \"filecontentreadertool\"\n    description = '''\n    Reads content from multiple files and returns their contents.\n    Accepts a list of file paths and returns a dictionary with file paths as keys\n    and their content as values.\n    Handles file reading errors gracefully with built-in Python exceptions.\n    When given a directory, recursively reads all text files while skipping binaries and common ignore patterns.\n    '''\n    \n    # Files and directories to ignore\n    IGNORE_PATTERNS = {\n        # Hidden files and directories\n        '.git', '.svn', '.hg', '.DS_Store', '.env', '.idea', '.vscode', '.settings',\n        # Build directories\n        'node_modules', '__pycache__', 'build', 'dist', 'venv', 'env', 'bin', 'obj',\n        'target', 'out', 'Debug', 'Release', 'x64', 'x86', 'builds', 'coverage',\n        # Binary file extensions\n        '.pyc', '.pyo', '.so', '.dll', '.dylib', '.pdb', '.ilk', '.exp', '.map',\n        '.exe', '.bin', '.dat', '.db', '.sqlite', '.sqlite3', '.o', '.cache',\n        '.lib', '.a', '.sys', '.ko', '.obj', '.iso', '.msi', '.msp', '.msm',\n        '.img', '.dmg', '.class', '.jar', '.war', '.ear', '.aar', '.apk',\n        # Media files\n        '.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.psd', '.ai', '.eps',\n        '.mp3', '.mp4', '.avi', '.mov', '.wav', '.aac', '.m4a', '.wma', '.midi',\n        '.flv', '.mkv', '.wmv', '.m4v', '.webm', '.3gp', '.mpg', '.mpeg', '.m2v',\n        '.ogg', '.ogv', '.webp', '.heic', '.raw', '.svg', '.ico', '.icns',\n        # Archive files\n        '.zip', '.tar', '.gz', '.rar', '.7z', '.pkg', '.deb', '.rpm', '.snap',\n        '.bz2', '.xz', '.cab', '.iso', '.tgz', '.tbz2', '.lz', '.lzma', '.tlz',\n        # IDE and editor files\n        '.sln', '.suo', '.user', '.workspace', '.project', '.classpath', '.iml',\n        # Log and temp files\n        '.log', '.tmp', '.temp', '.swp', '.bak', '.old', '.orig', '.pid'\n    }\n\n    input_schema = {\n        \"type\": \"object\",\n        \"properties\": {\n            \"file_paths\": {\n                \"type\": \"array\",\n                \"items\": {\n                    \"type\": \"string\"\n                },\n                \"description\": \"List of file paths to read\"\n            }\n        },\n        \"required\": [\"file_paths\"]\n    }\n\n    def _should_skip(self, path: str) -> bool:\n        \"\"\"Determine if a file or directory should be skipped.\"\"\"\n        name = os.path.basename(path)\n        ext = os.path.splitext(name)[1].lower()\n\n        # Skip if name or extension matches ignore patterns\n        if name in self.IGNORE_PATTERNS or ext in self.IGNORE_PATTERNS:\n            return True\n\n        # Skip hidden files/directories (starting with .)\n        if name.startswith('.'):\n            return True\n\n        # If it's a file, check if it's binary using mimetype\n        if os.path.isfile(path):\n            mime_type, _ = mimetypes.guess_type(path)\n            if mime_type and not mime_type.startswith('text/'):\n                return True\n\n        return False\n\n    def _read_file(self, file_path: str) -> str:\n        \"\"\"Safely read a file and handle errors.\"\"\"\n        try:\n            if not os.path.exists(file_path):\n                return \"Error: File not found\"\n\n            if self._should_skip(file_path):\n                return \"Skipped: Binary or ignored file type\"\n\n            with open(file_path, 'r', encoding='utf-8') as file:\n                return file.read()\n\n        except PermissionError:\n            return \"Error: Permission denied\"\n        except IsADirectoryError:\n            return \"Error: Path is a directory\"\n        except UnicodeDecodeError:\n            return \"Error: Unable to decode file (likely binary)\"\n        except Exception as e:\n            return f\"Error: {str(e)}\"\n\n    def _read_directory(self, dir_path: str) -> dict:\n        \"\"\"Recursively read all files in a directory.\"\"\"\n        results = {}\n\n        try:\n            for root, dirs, files in os.walk(dir_path):\n                # Filter out directories to skip\n                dirs[:] = [d for d in dirs if not self._should_skip(os.path.join(root, d))]\n\n                # Process files\n                for file in files:\n                    file_path = os.path.join(root, file)\n                    if not self._should_skip(file_path):\n                        content = self._read_file(file_path)\n                        results[file_path] = content\n\n        except Exception as e:\n            results[dir_path] = f\"Error reading directory: {str(e)}\"\n\n        return results\n\n    def execute(self, **kwargs) -> str:\n        file_paths = kwargs.get('file_paths', [])\n        results = {}\n\n        try:\n            for path in file_paths:\n                if os.path.isdir(path):\n                    # If it's a directory, read it recursively\n                    dir_results = self._read_directory(path)\n                    results.update(dir_results)\n                else:\n                    # If it's a file, read it directly\n                    content = self._read_file(path)\n                    results[path] = content\n\n            return json.dumps(results, indent=2)\n\n        except Exception as e:\n            return json.dumps({\"error\": str(e)}, indent=2)"
  },
  {
    "path": "tools/filecreatortool.py",
    "content": "from tools.base import BaseTool\nimport os\nimport json\nfrom typing import Union, List, Dict\nfrom pathlib import Path\n\nclass FileCreatorTool(BaseTool):\n    name = \"filecreatortool\"\n    description = '''\n    Creates new files with specified content.\n    \n    IMPORTANT: The input must follow this exact structure:\n    1. For a single file:\n       {\n           \"files\": {\n               \"path\": \"path/to/file.txt\",\n               \"content\": \"file content here\"\n           }\n       }\n    \n    2. For multiple files:\n       {\n           \"files\": [\n               {\n                   \"path\": \"path/to/file1.txt\",\n                   \"content\": \"content for file 1\"\n               },\n               {\n                   \"path\": \"path/to/file2.txt\",\n                   \"content\": \"content for file 2\"\n               }\n           ]\n       }\n    \n    Features:\n    - Creates parent directories automatically if they don't exist\n    - Supports both text and binary content\n    - Can create multiple files in one call\n    - Handles JSON content automatically\n    \n    Optional parameters:\n    - binary: boolean (default: false) - Set to true for binary files\n    - encoding: string (default: \"utf-8\") - Specify file encoding\n    \n    Example usage:\n    1. Create a Python file:\n       {\n           \"files\": {\n               \"path\": \"test.py\",\n               \"content\": \"def hello():\\\\n    print('Hello, World!')\"\n           }\n       }\n    \n    2. Create multiple files:\n       {\n           \"files\": [\n               {\n                   \"path\": \"src/main.py\",\n                   \"content\": \"# Main file content\"\n               },\n               {\n                   \"path\": \"src/utils.py\",\n                   \"content\": \"# Utils file content\"\n               }\n           ]\n       }\n    '''\n    input_schema = {\n        \"type\": \"object\",\n        \"properties\": {\n            \"files\": {\n                \"oneOf\": [\n                    {\n                        \"type\": \"object\",\n                        \"properties\": {\n                            \"path\": {\"type\": \"string\"},\n                            \"content\": {\"oneOf\": [{\"type\": \"string\"}, {\"type\": \"object\"}]},\n                            \"binary\": {\"type\": \"boolean\", \"default\": False},\n                            \"encoding\": {\"type\": \"string\", \"default\": \"utf-8\"}\n                        },\n                        \"required\": [\"path\", \"content\"]\n                    },\n                    {\n                        \"type\": \"array\",\n                        \"items\": {\n                            \"type\": \"object\",\n                            \"properties\": {\n                                \"path\": {\"type\": \"string\"},\n                                \"content\": {\"oneOf\": [{\"type\": \"string\"}, {\"type\": \"object\"}]},\n                                \"binary\": {\"type\": \"boolean\", \"default\": False},\n                                \"encoding\": {\"type\": \"string\", \"default\": \"utf-8\"}\n                            },\n                            \"required\": [\"path\", \"content\"]\n                        }\n                    }\n                ]\n            }\n        },\n        \"required\": [\"files\"]\n    }\n\n    def execute(self, **kwargs) -> str:\n        \"\"\"\n        Execute the file creation process.\n        \n        Args:\n            **kwargs: Must contain 'files' key with either a dict or list of dicts\n                     Each dict must have 'path' and 'content' keys\n        \n        Returns:\n            str: JSON string containing results of file creation operations\n        \"\"\"\n        files = kwargs.get('files', [])\n        if isinstance(files, dict):\n            files = [files]\n\n        results = []\n        for file_spec in files:\n            try:\n                path = Path(file_spec['path'])\n                content = file_spec['content']\n                binary = file_spec.get('binary', False)\n                encoding = file_spec.get('encoding', 'utf-8')\n\n                # Create parent directories\n                path.parent.mkdir(parents=True, exist_ok=True)\n\n                # Handle content\n                if isinstance(content, dict):\n                    content = json.dumps(content, indent=2)\n\n                # Write file\n                mode = 'wb' if binary else 'w'\n                if binary:\n                    if isinstance(content, str):\n                        content = content.encode(encoding)\n                    with open(path, mode) as f:\n                        f.write(content)\n                else:\n                    with open(path, mode, encoding=encoding, newline='') as f:\n                        f.write(content)\n\n                results.append({\n                    'path': str(path),\n                    'success': True,\n                    'size': path.stat().st_size\n                })\n\n            except Exception as e:\n                results.append({\n                    'path': str(path) if 'path' in locals() else None,\n                    'success': False,\n                    'error': str(e)\n                })\n\n        return json.dumps({\n            'created_files': len([r for r in results if r['success']]),\n            'failed_files': len([r for r in results if not r['success']]),\n            'results': results\n        }, indent=2)"
  },
  {
    "path": "tools/fileedittool.py",
    "content": "from tools.base import BaseTool\nimport os\nimport re\n\nclass FileEditTool(BaseTool):\n    name = \"fileedittool\"\n    description = '''\n    A tool for editing file contents with support for:\n    - Full file content replacement\n    - Partial content editing by line numbers\n    - Pattern-based text search and replace\n    - Multiple file type support\n    - Error handling for file operations\n    '''\n    input_schema = {\n        \"type\": \"object\",\n        \"properties\": {\n            \"file_path\": {\"type\": \"string\", \"description\": \"Path to the file to edit\"},\n            \"edit_type\": {\"type\": \"string\", \"enum\": [\"full\", \"partial\"], \"description\": \"Type of edit operation\"},\n            \"new_content\": {\"type\": \"string\", \"description\": \"New content to write\"},\n            \"start_line\": {\"type\": \"integer\", \"description\": \"Starting line number for partial edits\"},\n            \"end_line\": {\"type\": \"integer\", \"description\": \"Ending line number for partial edits\"},\n            \"search_pattern\": {\"type\": \"string\", \"description\": \"Pattern to search for in partial edits\"},\n            \"replacement_text\": {\"type\": \"string\", \"description\": \"Text to replace matched patterns\"}\n        },\n        \"required\": [\"file_path\", \"edit_type\", \"new_content\"]\n    }\n\n    def execute(self, **kwargs) -> str:\n        file_path = kwargs.get('file_path')\n        edit_type = kwargs.get('edit_type')\n        new_content = kwargs.get('new_content')\n        \n        try:\n            if not os.path.exists(file_path):\n                raise FileNotFoundError(f\"File not found: {file_path}\")\n\n            with open(file_path, 'r', encoding='utf-8') as file:\n                original_content = file.read()\n                lines = original_content.splitlines()\n\n            if edit_type == \"full\":\n                updated_content = new_content\n            else:\n                start_line = kwargs.get('start_line')\n                end_line = kwargs.get('end_line')\n                search_pattern = kwargs.get('search_pattern')\n                replacement_text = kwargs.get('replacement_text')\n\n                if start_line is not None and end_line is not None:\n                    updated_content = self._edit_by_lines(lines, start_line, end_line, new_content)\n                elif search_pattern and replacement_text:\n                    updated_content = self._find_and_replace(original_content, search_pattern, replacement_text)\n                else:\n                    raise ValueError(\"Invalid partial edit parameters\")\n\n            with open(file_path, 'w', encoding='utf-8') as file:\n                file.write(updated_content)\n\n            return f\"File successfully updated: {file_path}\\n{updated_content}\"\n\n        except Exception as e:\n            return f\"Error editing file: {str(e)}\"\n\n    def _edit_by_lines(self, lines: list, start_line: int, end_line: int, new_content: str) -> str:\n        if start_line < 1 or end_line > len(lines) or start_line > end_line:\n            raise ValueError(\"Invalid line numbers\")\n\n        lines[start_line-1:end_line] = new_content.splitlines()\n        return '\\n'.join(lines)\n\n    def _find_and_replace(self, content: str, pattern: str, replacement: str) -> str:\n        try:\n            return re.sub(pattern, replacement, content)\n        except re.error as e:\n            raise ValueError(f\"Invalid regular expression pattern: {str(e)}\")"
  },
  {
    "path": "tools/lintingtool.py",
    "content": "from tools.base import BaseTool\nimport subprocess\nfrom typing import List\nimport json\n\nclass LintingTool(BaseTool):\n    name = \"lintingtool\"\n    description = '''\n    Runs the Ruff linter on the given Python files or directories to detect and fix coding style or syntax issues.\n    Supports configurable rule selection, automatic fixes, unsafe fixes, adding noqa directives, and watch mode.\n    Returns the linter output as a string.\n    '''\n\n    input_schema = {\n        \"type\": \"object\",\n        \"properties\": {\n            \"paths\": {\n                \"type\": \"array\",\n                \"items\": {\"type\": \"string\"},\n                \"description\": \"List of file or directory paths to lint. Defaults to current directory if none provided.\"\n            },\n            \"fix\": {\n                \"type\": \"boolean\",\n                \"default\": False,\n                \"description\": \"Whether to automatically fix fixable issues.\"\n            },\n            \"unsafe_fixes\": {\n                \"type\": \"boolean\",\n                \"default\": False,\n                \"description\": \"Enable unsafe fixes.\"\n            },\n            \"add_noqa\": {\n                \"type\": \"boolean\",\n                \"default\": False,\n                \"description\": \"Add noqa directives to all lines with violations.\"\n            },\n            \"select\": {\n                \"type\": \"array\",\n                \"items\": {\"type\": \"string\"},\n                \"description\": \"List of rule codes to exclusively enforce.\"\n            },\n            \"extend_select\": {\n                \"type\": \"array\",\n                \"items\": {\"type\": \"string\"},\n                \"description\": \"List of additional rule codes to enforce alongside the default selection.\"\n            },\n            \"watch\": {\n                \"type\": \"boolean\",\n                \"default\": False,\n                \"description\": \"Watch for file changes and re-run linting on change.\"\n            },\n            \"exit_zero\": {\n                \"type\": \"boolean\",\n                \"default\": False,\n                \"description\": \"Exit with code 0 even if violations are found.\"\n            },\n            \"exit_non_zero_on_fix\": {\n                \"type\": \"boolean\",\n                \"default\": False,\n                \"description\": \"Exit with non-zero even if all violations were fixed automatically.\"\n            }\n        },\n        \"required\": []\n    }\n\n    def execute(self, **kwargs) -> str:\n        paths = kwargs.get(\"paths\", [])\n        fix = kwargs.get(\"fix\", False)\n        unsafe_fixes = kwargs.get(\"unsafe_fixes\", False)\n        add_noqa = kwargs.get(\"add_noqa\", False)\n        select = kwargs.get(\"select\", [])\n        extend_select = kwargs.get(\"extend_select\", [])\n        watch = kwargs.get(\"watch\", False)\n        exit_zero = kwargs.get(\"exit_zero\", False)\n        exit_non_zero_on_fix = kwargs.get(\"exit_non_zero_on_fix\", False)\n\n        cmd = [\"uv\", \"run\", \"ruff\", \"check\"]\n\n        if fix:\n            cmd.append(\"--fix\")\n        if unsafe_fixes:\n            cmd.append(\"--unsafe-fixes\")\n        if add_noqa:\n            cmd.append(\"--add-noqa\")\n        if watch:\n            cmd.append(\"--watch\")\n        if exit_zero:\n            cmd.append(\"--exit-zero\")\n        if exit_non_zero_on_fix:\n            cmd.append(\"--exit-non-zero-on-fix\")\n\n        for rule in select:\n            cmd.extend([\"--select\", rule])\n        for rule in extend_select:\n            cmd.extend([\"--extend-select\", rule])\n\n        if not paths:\n            paths = [\".\"]\n        cmd.extend(paths)\n\n        try:\n            result = subprocess.run(\n                cmd,\n                text=True,\n                capture_output=True,\n                check=False\n            )\n            return result.stdout + result.stderr\n        except Exception as e:\n            return f\"Error running ruff check: {str(e)}\"\n"
  },
  {
    "path": "tools/screenshottool.py",
    "content": "from tools.base import BaseTool\nfrom typing import List, Dict, Any, Optional\nimport base64\nimport io\nimport json  # Add this import\n\ntry:\n    import pyautogui\n    from PIL import Image\nexcept ImportError as e:\n    # If pyautogui or PIL is missing, you may need to rely on the uvpackagemanager tool\n    # or instruct the user to install them. For now, just raise an error.\n    raise ImportError(\"The ScreenshotTool requires 'pyautogui' and 'Pillow' to be installed.\")\n\nclass ScreenshotTool(BaseTool):\n    name = \"screenshottool\"\n    description = '''\n    Captures a screenshot of the current screen and returns an image block ready to be sent to Claude.\n    Optionally, a specific region of the screen can be captured by providing coordinates.\n\n    Inputs:\n    - region (optional): A list of four integers [x, y, width, height] specifying the region of the screen to capture.\n      If omitted, captures the entire screen.\n\n    The output is a JSON-formatted string that can be included directly as part of the conversation content:\n    [\n      {\n        \"type\": \"image\",\n        \"source\": {\n          \"type\": \"base64\",\n          \"media_type\": \"image/png\",\n          \"data\": \"<base64-encoded png>\"\n        }\n      }\n    ]\n\n    This block can be inserted into the messages array sent to Claude via the Messages API.\n    '''\n    input_schema = {\n        \"type\": \"object\",\n        \"properties\": {\n            \"region\": {\n                \"type\": \"array\",\n                \"items\": {\"type\": \"integer\"},\n                \"description\": \"Optional region [x, y, width, height] to capture\",\n                \"minItems\": 4,\n                \"maxItems\": 4\n            }\n        },\n        \"required\": []\n    }\n\n    def execute(self, **kwargs) -> Any:\n        region = kwargs.get(\"region\", None)\n        if region is not None and len(region) != 4:\n            return \"Invalid region specified. Must be a list of four integers: [x, y, width, height].\"\n\n        try:\n            # Take screenshot (full screen or specified region)\n            screenshot: Image.Image = pyautogui.screenshot(region=region)\n\n            # Convert to base64\n            with io.BytesIO() as buffer:\n                screenshot.save(buffer, format=\"PNG\")\n                encoded_data = base64.b64encode(buffer.getvalue()).decode(\"utf-8\")\n\n            # Return the image block as a Python list/dict (not as JSON string)\n            return [\n                {\n                    \"type\": \"image\",\n                    \"source\": {\n                        \"type\": \"base64\",\n                        \"media_type\": \"image/png\",\n                        \"data\": encoded_data,\n                    }\n                }\n            ]\n\n        except Exception as e:\n            return f\"Error capturing screenshot: {str(e)}\"\n"
  },
  {
    "path": "tools/toolcreator.py",
    "content": "from tools.base import BaseTool\nfrom rich.console import Console\nfrom rich.panel import Panel\nfrom pathlib import Path\nimport os\nfrom dotenv import load_dotenv\nimport re\nimport anthropic\n\nload_dotenv()\n\nclass ToolCreatorTool(BaseTool):\n    name = \"toolcreator\"\n    description = '''\n    Creates a new tool based on a natural language description.\n    Use this when you need a new capability that isn't available in current tools.\n    The tool will be automatically generated and saved to the tools directory.\n    Returns the generated tool code and creation status.\n    '''\n    input_schema = {\n        \"type\": \"object\",\n        \"properties\": {\n            \"description\": {\n                \"type\": \"string\",\n                \"description\": \"Natural language description of what the tool should do\"\n            }\n        },\n        \"required\": [\"description\"]\n    }\n\n    def __init__(self):\n        self.client = anthropic.Anthropic(api_key=os.getenv('ANTHROPIC_API_KEY'))\n        self.console = Console()\n        self.tools_dir = Path(__file__).parent.parent / \"tools\"  # Fixed path\n\n    def _sanitize_filename(self, name: str) -> str:\n        \"\"\"Convert tool name to valid Python filename\"\"\"\n        return name + '.py'  # Keep exact name, just add .py\n\n    def _validate_tool_name(self, name: str) -> bool:\n        \"\"\"Validate tool name matches required pattern\"\"\"\n        return bool(re.match(r'^[a-zA-Z0-9_-]{1,64}$', name))\n\n    def execute(self, **kwargs) -> str:\n        description = kwargs.get(\"description\")\n\n        # Create exact same prompt as the original\n        prompt = f\"\"\"Create a Python tool class that follows our BaseTool interface. The tool should:\n\n1. {description}\n\nImportant:\n- The filename MUST EXACTLY match the tool name used in the class\n- The name property MUST EXACTLY match the class name in lowercase\n- For example, if the class is `WeatherTool`, then:\n  - name property must be \"weathertool\"\n  - file must be weathertool.py\n\nHere's the required structure (including imports and format):\n\n```python\nfrom tools.base import BaseTool  # This import must be present\nimport requests  # Add any other required imports\n\nclass ToolName(BaseTool):  # Class name must match name property in uppercase first letter\n    name = \"toolname\"  # Must match class name in lowercase\n    description = '''\n    Detailed description here.\n    Multiple lines for clarity.\n    '''\n    input_schema = {{\n        \"type\": \"object\",\n        \"properties\": {{\n            # Required input parameters\n        }},\n        \"required\": []  # List required parameters\n    }}\n\n    def execute(self, **kwargs) -> str:\n        # Implementation here\n        pass\n```\n\nGenerate the complete tool implementation following this exact structure.\nReturn ONLY the Python code without any explanation or markdown formatting.\n\"\"\"\n\n        try:\n            # Get tool implementation from Claude with animation\n            response = self.client.messages.create(\n                model=\"claude-3-5-sonnet-20241022\",\n                max_tokens=4000,\n                temperature=0,\n                messages=[\n                    {\"role\": \"user\", \"content\": prompt}\n                ]\n            )\n\n            tool_code = response.content[0].text.strip()\n\n            # Extract tool name from the generated code\n            name_match = re.search(r'name\\s*=\\s*[\"\\']([a-zA-Z0-9_-]+)[\"\\']', tool_code)\n            if not name_match:\n                return \"Error: Could not extract tool name from generated code\"\n\n            tool_name = name_match.group(1)\n            filename = self._sanitize_filename(tool_name)\n\n            # Ensure the tools directory exists\n            self.tools_dir.mkdir(exist_ok=True)\n\n            # Save tool to file\n            file_path = self.tools_dir / filename\n            with open(file_path, 'w') as f:\n                f.write(tool_code)\n\n            # Format the response using Panel like the original\n            result = f\"\"\"[bold green]✅ Tool created successfully![/bold green]\nTool name: [cyan]{tool_name}[/cyan]\nFile created: [cyan]{filename}[/cyan]\n\n[bold]Generated Tool Code:[/bold]\n{Panel(tool_code, border_style=\"green\")}\n\n[bold green]✨ Tool is ready to use![/bold green]\nType 'refresh' to load your new tool.\"\"\"\n\n            return result\n\n        except Exception as e:\n            return f\"[bold red]Error creating tool:[/bold red] {str(e)}\"\n"
  },
  {
    "path": "tools/uvpackagemanager.py",
    "content": "import logging\nimport subprocess\nfrom typing import List, Optional\n\nfrom tools.base import BaseTool\n\n\nclass UVPackageManager(BaseTool):\n    name = \"uvpackagemanager\"\n    description = '''\n    Comprehensive interface to the uv package manager providing package management,\n    project management, Python version management, tool management, and script support.\n    Supports all major platforms with pip compatibility.\n    '''\n    input_schema = {\n        \"type\": \"object\",\n        \"properties\": {\n            \"command\": {\n                \"type\": \"string\",\n                \"description\": \"Primary command (install, remove, update, init, venv, etc.)\"\n            },\n            \"packages\": {\n                \"type\": \"array\",\n                \"items\": {\"type\": \"string\"},\n                \"description\": \"List of packages to operate on\"\n            },\n            \"python_version\": {\n                \"type\": \"string\",\n                \"description\": \"Python version for operations that require it\"\n            },\n            \"project_path\": {\n                \"type\": \"string\",\n                \"description\": \"Path to project directory\"\n            },\n            \"requirements_file\": {\n                \"type\": \"string\",\n                \"description\": \"Path to requirements file\"\n            },\n            \"global_install\": {\n                \"type\": \"boolean\",\n                \"description\": \"Whether to install packages globally\"\n            }\n        },\n        \"required\": [\"command\"]\n    }\n\n    def execute(self, **kwargs) -> str:\n        command = kwargs.get(\"command\")\n        packages = kwargs.get(\"packages\", [])\n        python_version = kwargs.get(\"python_version\")\n        project_path = kwargs.get(\"project_path\", \".\")\n        requirements_file = kwargs.get(\"requirements_file\")\n        global_install = kwargs.get(\"global_install\", False)\n\n        try:\n            if command == \"install\":\n                return self._install_packages(packages, requirements_file, global_install)\n            elif command == \"remove\":\n                return self._remove_packages(packages)\n            elif command == \"update\":\n                return self._update_packages(packages)\n            elif command == \"list\":\n                return self._list_packages()\n            elif command == \"init\":\n                return self._init_project(project_path)\n            elif command == \"venv\":\n                return self._create_venv(project_path, python_version)\n            elif command == \"python\":\n                return self._manage_python(python_version)\n            elif command == \"compile\":\n                return self._compile_requirements()\n            elif command == \"run\":\n                return self._run_script(kwargs.get(\"script\"), packages)\n            else:\n                return f\"Unknown command: {command}\"\n        except Exception as e:\n            logging.error(f\"Error executing UV command: {e!s}\")\n            return f\"Error: {e!s}\"\n\n    def _run_uv_command(self, args: List[str]) -> str:\n        try:\n            result = subprocess.run(\n                [\"uv\"] + args,\n                capture_output=True,\n                text=True,\n                check=True\n            )\n            return result.stdout\n        except subprocess.CalledProcessError as e:\n            raise Exception(f\"UV command failed: {e.stderr}\")\n\n    def _install_packages(self, packages: List[str], requirements_file: Optional[str], global_install: bool) -> str:\n        args = [\"pip\", \"install\"]\n        if global_install:\n            args.append(\"--global\")\n        if requirements_file:\n            args.extend([\"-r\", requirements_file])\n        if packages:\n            args.extend(packages)\n        return self._run_uv_command(args)\n\n    def _remove_packages(self, packages: List[str]) -> str:\n        return self._run_uv_command([\"pip\", \"uninstall\", \"-y\"] + packages)\n\n    def _update_packages(self, packages: List[str]) -> str:\n        args = [\"pip\", \"install\", \"--upgrade\"]\n        if packages:\n            args.extend(packages)\n        return self._run_uv_command(args)\n\n    def _list_packages(self) -> str:\n        return self._run_uv_command([\"pip\", \"list\"])\n\n    def _init_project(self, project_path: str) -> str:\n        return self._run_uv_command([\"init\", project_path])\n\n    def _create_venv(self, path: str, python_version: Optional[str]) -> str:\n        args = [\"venv\"]\n        if python_version:\n            args.extend([\"--python\", python_version])\n        args.append(path)\n        return self._run_uv_command(args)\n\n    def _manage_python(self, version: Optional[str]) -> str:\n        if not version:\n            return self._run_uv_command([\"python\", \"list\"])\n        return self._run_uv_command([\"python\", \"install\", version])\n\n    def _compile_requirements(self) -> str:\n        return self._run_uv_command([\"pip\", \"compile\", \"requirements.in\"])\n\n    def _run_script(self, script: str, packages: List[str]) -> str:\n        args = [\"run\"]\n        if packages:\n            args.extend([\"--with\"] + packages)\n        args.extend([\"--\", \"python\", script])\n        return self._run_uv_command(args)"
  },
  {
    "path": "tools/webscrapertool.py",
    "content": "from tools.base import BaseTool\nimport requests\nfrom bs4 import BeautifulSoup, Comment\nimport re\n\nclass WebScraperTool(BaseTool):\n    name = \"webscrapertool\"\n    description = '''\n    An enhanced web scraper that fetches a web page, extracts and returns its main textual content,\n    along with the page title and meta description if available. It attempts to identify the main\n    article content more intelligently, remove navigational and advertising elements, and preserve\n    heading structure for context. Useful for obtaining cleaner, more relevant textual information.\n    '''\n\n    input_schema = {\n        \"type\": \"object\",\n        \"properties\": {\n            \"url\": {\n                \"type\": \"string\",\n                \"description\": \"The URL of the webpage to scrape\"\n            }\n        },\n        \"required\": [\"url\"]\n    }\n\n    def execute(self, **kwargs) -> str:\n        url = kwargs.get(\"url\")\n\n        try:\n            headers = {\n                'User-Agent': ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) '\n                               'AppleWebKit/537.36 (KHTML, like Gecko) '\n                               'Chrome/91.0.4472.124 Safari/537.36')\n            }\n            response = requests.get(url, headers=headers, timeout=10)\n            response.raise_for_status()\n\n            soup = BeautifulSoup(response.text, 'html.parser')\n\n            # Remove script, style, and other irrelevant elements\n            for elem in soup([\"script\", \"style\", \"noscript\", \"iframe\", \"svg\", \"canvas\", \"object\"]):\n                elem.decompose()\n\n            # Remove comments\n            for comment in soup.find_all(text=lambda text: isinstance(text, Comment)):\n                comment.extract()\n\n            # Identify main content container\n            main_container = (\n                soup.find('main') or\n                soup.find('article') or\n                soup.find(attrs={'id': re.compile(r'(main|content|article)', re.I)}) or\n                soup.find(attrs={'class': re.compile(r'(main|content|article)', re.I)})\n            )\n\n            # If no main-like container found, fallback to body or entire doc\n            if not main_container:\n                main_container = soup.find('body')\n            if not main_container:\n                main_container = soup\n\n            # Remove elements likely not part of main content\n            # including nav, footer, aside, forms, and common ad-based sections\n            for tag_name in [\"nav\", \"footer\", \"aside\", \"form\", \"header\"]:\n                for elem in main_container.find_all(tag_name):\n                    elem.decompose()\n\n            # Remove known ad or irrelevant containers by class or id hints\n            # E.g., \"sidebar\", \"ad\", \"advertisement\"\n            for elem in main_container.find_all(attrs={'class': re.compile(r'(sidebar|nav|menu|ad|advert)', re.I)}):\n                elem.decompose()\n            for elem in main_container.find_all(attrs={'id': re.compile(r'(sidebar|nav|menu|ad|advert)', re.I)}):\n                elem.decompose()\n\n            # Remove empty elements that are not headings or block-level content\n            for elem in main_container.find_all(lambda e: (e.name not in ['h1','h2','h3','h4','h5','h6','p','div','ul','ol','li','section','article','main'] and not e.get_text(strip=True))):\n                elem.decompose()\n\n            # Extract page title\n            title_elem = soup.find('title')\n            page_title = title_elem.get_text(strip=True) if title_elem else ''\n\n            # Extract meta description\n            meta_desc = ''\n            desc_tag = soup.find('meta', attrs={\"name\": \"description\"})\n            if desc_tag and desc_tag.get('content'):\n                meta_desc = desc_tag['content'].strip()\n\n            # Convert main content to text\n            # We'll use get_text with a separator to maintain some structure\n            # but we need to carefully handle headings.\n            # Let's extract text in a structured way:\n            # We'll join block-level elements with newlines, and strip excess whitespace.\n            block_elements = ['p','h1','h2','h3','h4','h5','h6','li','section','article','main','div']\n            text_chunks = []\n            for elem in main_container.find_all(block_elements):\n                # Get the text, strip whitespace\n                block_text = elem.get_text(\" \", strip=True)\n                if block_text:\n                    text_chunks.append(block_text)\n\n            cleaned_text = \"\\n\\n\".join(text_chunks)\n\n            if not cleaned_text.strip():\n                # If no text found, return a default message\n                return \"No readable content found on the webpage.\"\n\n            # Construct final output\n            output_parts = []\n            if page_title:\n                output_parts.append(f\"Title: {page_title}\")\n            if meta_desc:\n                output_parts.append(f\"Description: {meta_desc}\")\n            output_parts.append(\"Content:\")\n            output_parts.append(cleaned_text)\n\n            final_output = \"\\n\\n\".join(output_parts)\n\n            return final_output\n\n        except requests.RequestException as e:\n            return f\"Error scraping the webpage: {str(e)}\"\n        except Exception as e:\n            return f\"An unexpected error occurred: {str(e)}\"\n"
  }
]